vite-ssr
Version:
Vite utility for server side rendering
159 lines (158 loc) • 6.47 kB
JavaScript
;
const server_1 = require("./dev/server");
const vite_1 = require("vite");
const pluginName = 'vite-ssr';
const entryServer = '/entry-server';
const entryClient = '/entry-client';
function hasPlugin(plugins = [], name) {
return !!plugins.flat().find((plugin) => (plugin.name || '').startsWith(name));
}
function hasDependency(dependency) {
try {
require.resolve(dependency);
return true;
}
catch (error) {
return false;
}
}
function detectReactConfigFeatures(features = {}) {
const external = [];
let useApolloRenderer;
// TODO use virtual modules for feature-detection
if (hasDependency('@apollo/client/react/ssr')) {
useApolloRenderer = features.reactApolloRenderer !== false;
}
else {
external.push('@apollo/client');
}
return {
ssr: { external },
define: {
__USE_APOLLO_RENDERER__: !!useApolloRenderer,
},
};
}
// FIXME
// Vite 2.6.0 introduced a bug where `import.meta` is not populated
// correctly in optimized dependencies. At the same time, all the
// subdependencies in Style Collectors must be optimized.
function fixReactDeps(config, libPath) {
var _a;
const styleCollectorDeps = {
'styled-components': ['styled-components'],
'material-ui-core-v4': ['@material-ui/core/styles'],
emotion: ['@emotion/cache', '@emotion/react'],
};
const styleCollectors = Object.keys(styleCollectorDeps).filter((sc) => {
try {
require.resolve(sc);
return true;
}
catch (error) {
return false;
}
});
if (config.optimizeDeps) {
(_a = config.optimizeDeps.include) === null || _a === void 0 ? void 0 : _a.push(...styleCollectors.flatMap((sc) => styleCollectorDeps[sc]));
config.optimizeDeps.exclude = config.optimizeDeps.exclude || [];
config.optimizeDeps.exclude.push(...styleCollectors.map((sc) => pluginName + libPath + '/style-collectors/' + sc));
}
}
module.exports = function ViteSsrPlugin(options = {}) {
let detectedLib;
const nameToMatch = options.plugin || pluginName;
const autoEntryRE = new RegExp(`${nameToMatch}(\/core|\/vue|\/react)?$`);
const plugins = [
{
name: pluginName,
enforce: 'pre',
viteSsrOptions: options,
config(config, env) {
var _a;
const plugins = config.plugins;
const isVue = hasPlugin(plugins, 'vite:vue');
const isReact = hasPlugin(plugins, 'vite:react') ||
hasPlugin(plugins, 'react-refresh');
detectedLib = isVue ? 'vue' : isReact ? 'react' : 'core';
const detectedFeats = {
...(isReact && detectReactConfigFeatures(options.features)),
};
return {
...detectedFeats,
define: {
...detectedFeats.define,
__CONTAINER_ID__: JSON.stringify(options.containerId || 'app'),
// Vite 2.6.0 bug: use this
// instead of import.meta.env.DEV
__VITE_SSR_DEV__: env.mode !== 'production',
},
ssr: {
...detectedFeats.ssr,
noExternal: [pluginName],
},
server:
// Avoid displaying 'localhost' in terminal in MacOS:
// https://github.com/vitejs/vite/issues/5605
process.platform === 'darwin'
? {
host: ((_a = config.server) === null || _a === void 0 ? void 0 : _a.host) || '127.0.0.1',
}
: undefined,
};
},
configResolved: (config) => {
const libPath = `/${detectedLib}`;
// @ts-ignore
config.optimizeDeps = config.optimizeDeps || {};
config.optimizeDeps.include = config.optimizeDeps.include || [];
config.optimizeDeps.include.push(nameToMatch + libPath + entryClient, nameToMatch + libPath + entryServer);
if (detectedLib === 'react') {
fixReactDeps(config, libPath);
}
},
async configureServer(server) {
if (process.env.__DEV_MODE_SSR) {
const handler = (0, server_1.createSSRDevHandler)(server, options);
return () => server.middlewares.use(handler);
}
},
// Implement auto-entry using virtual modules:
resolveId(source, importer, options) {
if (source.includes(nameToMatch)) {
source = (0, vite_1.normalizePath)(source);
if (autoEntryRE.test(source)) {
return `virtual:${source}/index.js`;
}
}
},
load(id, options) {
if (id.startsWith(`virtual:${nameToMatch}`)) {
id = (0, vite_1.normalizePath)(id);
let [, lib = ''] = id.split('/');
if (lib === 'index.js') {
lib = detectedLib;
}
const libPath = `'${nameToMatch}/${lib}/entry-${(options === null || options === void 0 ? void 0 : options.ssr) ? 'server' : 'client'}'`;
return `export * from ${libPath}; export { default } from ${libPath}`;
}
},
},
];
if ((options.excludeSsrComponents || []).length > 0) {
plugins.push({
name: pluginName + '-exclude-components',
enforce: 'pre',
resolveId(source, importer, ...rest) {
var _a;
// @ts-ignore
const ssr = rest[1] || ((_a = rest[0]) === null || _a === void 0 ? void 0 : _a.ssr); // API changed in Vite 2.7 https://github.com/vitejs/vite/pull/5294
if (ssr &&
options.excludeSsrComponents.some((re) => re.test(source))) {
return this.resolve(`${pluginName}/${detectedLib}/ssr-component-mock`, importer, { skipSelf: true });
}
},
});
}
return plugins;
};