vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
217 lines (214 loc) • 7.42 kB
JavaScript
/**
* vite-plugin-react-server
* Copyright (c) Nico Brinkkemper
* MIT License
*/
import { createInputNormalizer } from '../helpers/inputNormalizer.js';
import { join } from 'path';
import { globSync } from 'fs';
import { pluginRoot } from '../root.js';
let stashedUserConfig = {};
function resolveUserConfig({
isClient = false,
isStatic = false,
config,
configEnv,
userOptions,
files
}) {
if (isStatic) {
const serverConfig = stashedUserConfig[`${userOptions.build.server}-ssr`];
if (!serverConfig) {
return {
type: "error",
error: new Error("Static plugin should run after the server plugin")
};
}
return {
type: "success",
userConfig: serverConfig
};
}
const envDir = isStatic ? userOptions.build.static : isClient ? userOptions.build.client : userOptions.build.server;
const ssr = typeof config.build?.ssr === "boolean" ? config.build?.ssr : configEnv.isSsrBuild || !isClient && !isStatic;
const envId = `${envDir}${ssr ? "-ssr" : ""}`;
if (stashedUserConfig[envId]) {
console.log(`[RSC] Using cached config for ${envId}`);
return {
type: "success",
userConfig: stashedUserConfig[envId]
};
}
const root = config.root ?? userOptions.projectRoot ?? process.cwd();
const normalizer = createInputNormalizer({
root,
preserveModulesRoot: userOptions.build.preserveModulesRoot ? userOptions.moduleBase : undefined,
removeExtension: true
});
const serverEntry = userOptions.serverEntry ? Object.fromEntries([
normalizer([userOptions.serverEntry, userOptions.serverEntry])
]) : null;
const clientEntry = userOptions.clientEntry ? Object.fromEntries(
[
[userOptions.clientEntry, userOptions.clientEntry],
["index.html", "index.html"]
].map(normalizer)
) : { "index.html": "index.html" };
const autoDiscoveredClientFiles = (inputs2) => {
const allFiles = globSync(`**/*.client.*`, {
cwd: join(root, userOptions.moduleBase)
});
for (const file of allFiles) {
const [key, value] = normalizer(join(userOptions.moduleBase, file));
if (!inputs2[key]) {
inputs2[key] = value;
} else {
console.warn(`[RSC] Client file already exists: ${key}`);
}
}
return inputs2;
};
const autoDiscoveredServerFiles = (inputs2) => {
const allFiles = globSync(`${userOptions.moduleBase}/**/*.server.*`, {
cwd: join(root, userOptions.moduleBase)
});
for (const file of allFiles) {
const [key, value] = normalizer(join(userOptions.moduleBase, file));
if (!inputs2[key]) {
inputs2[key] = value;
} else {
console.warn(`[RSC] Server file already exists: ${key}`);
}
}
return inputs2;
};
const customWorkerFiles = (inputs2) => {
const customRscWorker = !userOptions.rscWorkerPath.startsWith(pluginRoot);
const customHtmlWorker = !userOptions.htmlWorkerPath.startsWith(pluginRoot);
if (customRscWorker && !inputs2["rsc-worker"]) {
inputs2["rsc-worker"] = userOptions.rscWorkerPath;
}
if (customHtmlWorker && !inputs2["html-worker"]) {
inputs2["html-worker"] = userOptions.htmlWorkerPath;
}
return inputs2;
};
const autoDiscoveredFiles = (inputs2) => {
if (!files) return inputs2;
for (const [key, value] of files.pageMap) {
if (!inputs2[key]) {
inputs2[key] = value;
} else {
console.warn(`[RSC] Page file already exists: ${key}`);
}
}
for (const [key, value] of files.propsMap) {
if (!inputs2[key]) {
inputs2[key] = value;
} else {
console.warn(`[RSC] Props file already exists: ${key}`);
}
}
return inputs2;
};
let inputs = isClient ? autoDiscoveredClientFiles(clientEntry) : customWorkerFiles(autoDiscoveredServerFiles(autoDiscoveredFiles(serverEntry ?? {})));
const pluginOutput = {
preserveModules: !isClient,
preserveModulesRoot: userOptions.build.preserveModulesRoot ? userOptions.moduleBase : undefined,
entryFileNames: userOptions.build.entryFile,
assetFileNames: userOptions.build.assetFile,
chunkFileNames: userOptions.build.chunkFile,
format: "esm",
exports: "named",
hoistTransitiveImports: false,
generatedCode: {
constBindings: true,
objectShorthand: true
},
interop: "auto"
};
let newOutput = Array.isArray(config.build?.rollupOptions?.output) ? [...config.build?.rollupOptions?.output, pluginOutput] : typeof config.build?.rollupOptions?.output === "object" && config.build?.rollupOptions?.output !== null ? [config.build?.rollupOptions?.output, pluginOutput] : pluginOutput;
if (isClient) {
stashedUserConfig[envId] = {
...config,
root,
mode: configEnv.mode ?? configEnv.command === "build" ? "production" : "development",
resolve: {
external: ["react", "react-dom"],
alias: {}
},
ssr: {
target: "node",
external: ["react", "react-dom", "react-server-dom-esm/client.browser"],
resolve: {
externalConditions: ["react-server"]
}
},
// client build options
build: {
...config.build,
emptyOutDir: config.build?.emptyOutDir ?? true,
outDir: join(userOptions.build.outDir, envDir),
assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
copyPublicDir: config.build?.copyPublicDir ?? true,
// modern browsers
target: ["esnext"],
minify: true,
ssr,
manifest: config.build?.manifest ?? `.vite/manifest.json`,
ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
rollupOptions: {
...config.build?.rollupOptions,
input: inputs,
output: newOutput,
preserveEntrySignatures: "exports-only"
}
}
};
} else {
if (configEnv.isSsrBuild === false) {
configEnv.isSsrBuild = true;
}
stashedUserConfig[envId] = {
...config,
root,
mode: configEnv.mode ?? configEnv.command === "build" ? "production" : "development",
resolve: {
externalConditions: ["react-server"]
},
// server build options
build: {
...config.build,
emptyOutDir: config.build?.emptyOutDir ?? true,
outDir: join(userOptions.build.outDir, envDir),
target: config.build?.target ?? "node18",
minify: config.build?.minify ?? true,
ssr,
manifest: config.build?.manifest ?? `.vite/manifest.json`,
ssrManifest: config.build?.ssrManifest ?? `.vite/ssr-manifest.json`,
ssrEmitAssets: config.build?.ssrEmitAssets ?? true,
copyPublicDir: config.build?.copyPublicDir ?? isStatic,
assetsDir: config.build?.assetsDir ?? userOptions.build.assetsDir,
rollupOptions: {
...config.build?.rollupOptions,
input: inputs,
preserveEntrySignatures: config.build?.rollupOptions?.preserveEntrySignatures ?? "strict",
output: newOutput
}
}
};
}
if (!stashedUserConfig[envId]) {
return {
type: "error",
error: new Error("Failed to resolve config")
};
}
return {
type: "success",
userConfig: stashedUserConfig[envId]
};
}
export { resolveUserConfig };
//# sourceMappingURL=resolveUserConfig.js.map