@react-router/dev 0.0.0-experimental-fbbd4fd81 → 0.0.0-experimental-27337b0d6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.js +2 -2
- package/dist/cli/detectPackageManager.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/run.js +1 -1
- package/dist/cli/useJavascript.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/colors.js +1 -1
- package/dist/config/defaults/entry.client.rsc.tsx +90 -0
- package/dist/config/defaults/entry.react-server.node.tsx +15 -0
- package/dist/config/defaults/entry.react-server.web.tsx +9 -0
- package/dist/config/defaults/entry.server.node.rsc.tsx +164 -0
- package/dist/config/defaults/entry.server.spa.tsx +63 -10
- package/dist/config/findConfig.js +1 -1
- package/dist/config/flatRoutes.js +1 -1
- package/dist/config/format.js +1 -1
- package/dist/config/routes.js +1 -1
- package/dist/config.d.ts +15 -1
- package/dist/config.js +35 -9
- package/dist/index.js +1 -1
- package/dist/invariant.js +1 -1
- package/dist/runtime.client.d.ts +1 -0
- package/dist/runtime.client.js +19 -0
- package/dist/vite/babel.js +1 -1
- package/dist/vite/build.js +15 -2
- package/dist/vite/cloudflare-proxy-plugin.js +1 -1
- package/dist/vite/dev.js +1 -1
- package/dist/vite/import-vite-esm-sync.js +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/node-adapter.js +1 -1
- package/dist/vite/plugin.d.ts +9 -0
- package/dist/vite/plugin.js +347 -40
- package/dist/vite/profiler.js +1 -1
- package/dist/vite/remove-exports.d.ts +3 -1
- package/dist/vite/remove-exports.js +18 -2
- package/dist/vite/resolve-file-url.js +1 -1
- package/dist/vite/styles.js +1 -1
- package/dist/vite/vmod.js +1 -1
- package/package.json +13 -7
package/dist/vite/build.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-27337b0d6
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -227,7 +227,16 @@ async function build(root, {
|
|
|
227
227
|
});
|
|
228
228
|
}
|
|
229
229
|
await cleanBuildDirectory(viteConfig, ctx);
|
|
230
|
-
//
|
|
230
|
+
// Then run React server build first
|
|
231
|
+
if (reactRouterConfig.future.unstable_serverComponents) {
|
|
232
|
+
// TODO: This will be handled by the vite env API in the future
|
|
233
|
+
process.env.REACT_SERVER_BUILD = "1";
|
|
234
|
+
await viteBuild({
|
|
235
|
+
ssr: true
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
process.env.REACT_SERVER_BUILD = "";
|
|
239
|
+
// Run the Vite client build second
|
|
231
240
|
await viteBuild({
|
|
232
241
|
ssr: false
|
|
233
242
|
});
|
|
@@ -267,6 +276,10 @@ async function build(root, {
|
|
|
267
276
|
reactRouterConfig,
|
|
268
277
|
viteConfig
|
|
269
278
|
}));
|
|
279
|
+
const {
|
|
280
|
+
serverModules
|
|
281
|
+
} = plugin.getReactServerOptions();
|
|
282
|
+
invariant["default"](!serverModules.size, "`use server` is not yet supported.");
|
|
270
283
|
}
|
|
271
284
|
|
|
272
285
|
exports.build = build;
|
package/dist/vite/dev.js
CHANGED
package/dist/vite/index.js
CHANGED
package/dist/vite/plugin.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ type ReactRouterPluginSsrBuildContext = {
|
|
|
59
59
|
export type ReactRouterPluginContext = ReactRouterPluginSsrBuildContext & {
|
|
60
60
|
rootDirectory: string;
|
|
61
61
|
entryClientFilePath: string;
|
|
62
|
+
entryReactServerFilePath?: string;
|
|
62
63
|
entryServerFilePath: string;
|
|
63
64
|
reactRouterConfig: ResolvedVitePluginConfig;
|
|
64
65
|
viteManifestEnabled: boolean;
|
|
@@ -68,4 +69,12 @@ type MaybePromise<T> = T | Promise<T>;
|
|
|
68
69
|
export declare let setReactRouterDevLoadContext: (loadContext: (request: Request) => MaybePromise<Record<string, unknown>>) => void;
|
|
69
70
|
export type ReactRouterVitePlugin = (config?: VitePluginConfig) => Vite.Plugin[];
|
|
70
71
|
export declare const reactRouterVitePlugin: ReactRouterVitePlugin;
|
|
72
|
+
declare global {
|
|
73
|
+
var __clientModules: Set<string>;
|
|
74
|
+
var __serverModules: Set<string>;
|
|
75
|
+
}
|
|
76
|
+
export declare function getReactServerOptions(): {
|
|
77
|
+
clientModules: Set<string>;
|
|
78
|
+
serverModules: Set<string>;
|
|
79
|
+
};
|
|
71
80
|
export {};
|
package/dist/vite/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-27337b0d6
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -18,9 +18,11 @@ var url = require('node:url');
|
|
|
18
18
|
var fse = require('fs-extra');
|
|
19
19
|
var babel = require('@babel/core');
|
|
20
20
|
var serverRuntime = require('@react-router/server-runtime');
|
|
21
|
+
var reactRouter = require('react-router');
|
|
21
22
|
var esModuleLexer = require('es-module-lexer');
|
|
22
23
|
var jsesc = require('jsesc');
|
|
23
24
|
var colors = require('picocolors');
|
|
25
|
+
var unpluginRsc = require('unplugin-rsc');
|
|
24
26
|
var findConfig = require('../config/findConfig.js');
|
|
25
27
|
var invariant = require('../invariant.js');
|
|
26
28
|
var nodeAdapter = require('./node-adapter.js');
|
|
@@ -107,21 +109,26 @@ async function loadPluginContext({
|
|
|
107
109
|
}
|
|
108
110
|
const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
|
|
109
111
|
const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
/** This is used to manage a build optimization to remove unused route exports
|
|
113
|
+
from the client build output. This is important in cases where custom route
|
|
114
|
+
exports are only ever used on the server. Without this optimization we can't
|
|
115
|
+
tree-shake any unused custom exports because routes are entry points. */
|
|
116
|
+
const BUILD_CLIENT_ROUTE_QUERY_STRING = "?__remix-build-client-route";
|
|
113
117
|
let serverBuildId = vmod.id("server-build");
|
|
114
118
|
let serverManifestId = vmod.id("server-manifest");
|
|
115
119
|
let browserManifestId = vmod.id("browser-manifest");
|
|
116
120
|
let hmrRuntimeId = vmod.id("hmr-runtime");
|
|
117
121
|
let injectHmrRuntimeId = vmod.id("inject-hmr-runtime");
|
|
122
|
+
let reactServerBuildId = vmod.id("react-server-build");
|
|
123
|
+
let clientReferencesId = vmod.id("client-references");
|
|
124
|
+
let serverReferencesId = vmod.id("server-references");
|
|
118
125
|
const resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
|
|
119
126
|
let vite = importViteEsmSync.importViteEsmSync();
|
|
120
127
|
let file = route.file;
|
|
121
128
|
let fullPath = path__namespace.resolve(reactRouterConfig.appDirectory, file);
|
|
122
129
|
return vite.normalizePath(fullPath);
|
|
123
130
|
};
|
|
124
|
-
let vmods = [serverBuildId, serverManifestId, browserManifestId];
|
|
131
|
+
let vmods = [serverBuildId, serverManifestId, browserManifestId, reactServerBuildId, clientReferencesId, serverReferencesId];
|
|
125
132
|
const invalidateVirtualModules = viteDevServer => {
|
|
126
133
|
vmods.forEach(vmod$1 => {
|
|
127
134
|
let mod = viteDevServer.moduleGraph.getModuleById(vmod.resolve(vmod$1));
|
|
@@ -134,13 +141,10 @@ const getHash = (source, maxLength) => {
|
|
|
134
141
|
let hash = node_crypto.createHash("sha256").update(source).digest("hex");
|
|
135
142
|
return typeof maxLength === "number" ? hash.slice(0, maxLength) : hash;
|
|
136
143
|
};
|
|
137
|
-
const isClientRoute = id => {
|
|
138
|
-
return id.endsWith(CLIENT_ROUTE_QUERY_STRING);
|
|
139
|
-
};
|
|
140
144
|
const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
|
|
141
145
|
let vite = importViteEsmSync.importViteEsmSync();
|
|
142
146
|
let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
|
|
143
|
-
let entryChunk = viteManifest[rootRelativeFilePath +
|
|
147
|
+
let entryChunk = viteManifest[rootRelativeFilePath + BUILD_CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
|
|
144
148
|
if (!entryChunk) {
|
|
145
149
|
let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
|
|
146
150
|
throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
|
|
@@ -233,6 +237,7 @@ const getServerBundleBuildConfig = viteUserConfig => {
|
|
|
233
237
|
return viteUserConfig.__reactRouterServerBundleBuildConfig;
|
|
234
238
|
};
|
|
235
239
|
let getServerBuildDirectory = ctx => path__namespace.join(ctx.reactRouterConfig.buildDirectory, "server", ...(ctx.serverBundleBuildConfig ? [ctx.serverBundleBuildConfig.serverBundleId] : []));
|
|
240
|
+
let getReactServerBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "react-server");
|
|
236
241
|
let getClientBuildDirectory = reactRouterConfig => path__namespace.join(reactRouterConfig.buildDirectory, "client");
|
|
237
242
|
let defaultEntriesDir = path__namespace.resolve(__dirname, "..", "config", "defaults");
|
|
238
243
|
let defaultEntries = fse__namespace.readdirSync(defaultEntriesDir).map(filename => path__namespace.join(defaultEntriesDir, filename));
|
|
@@ -280,6 +285,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
280
285
|
});
|
|
281
286
|
let {
|
|
282
287
|
entryClientFilePath,
|
|
288
|
+
entryReactServerFilePath,
|
|
283
289
|
entryServerFilePath
|
|
284
290
|
} = await config.resolveEntryFiles({
|
|
285
291
|
rootDirectory,
|
|
@@ -297,6 +303,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
297
303
|
reactRouterConfig,
|
|
298
304
|
rootDirectory,
|
|
299
305
|
entryClientFilePath,
|
|
306
|
+
entryReactServerFilePath,
|
|
300
307
|
entryServerFilePath,
|
|
301
308
|
viteManifestEnabled,
|
|
302
309
|
...ssrBuildCtx
|
|
@@ -314,7 +321,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
314
321
|
ctx.serverBundleBuildConfig.routes :
|
|
315
322
|
// Otherwise, all routes are imported as usual
|
|
316
323
|
ctx.reactRouterConfig.routes;
|
|
317
|
-
|
|
324
|
+
let code = `
|
|
318
325
|
import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryServerFilePath))};
|
|
319
326
|
${Object.keys(routes).map((key, index) => {
|
|
320
327
|
let route = routes[key];
|
|
@@ -324,7 +331,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
324
331
|
export const assetsBuildDirectory = ${JSON.stringify(path__namespace.relative(ctx.rootDirectory, getClientBuildDirectory(ctx.reactRouterConfig)))};
|
|
325
332
|
export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
|
|
326
333
|
export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
|
|
327
|
-
export const isSpaMode = ${!ctx.reactRouterConfig.ssr};
|
|
334
|
+
export const isSpaMode = ${!ctx.reactRouterConfig.ssr && ctx.reactRouterConfig.prerender == null};
|
|
328
335
|
export const publicPath = ${JSON.stringify(ctx.reactRouterConfig.publicPath)};
|
|
329
336
|
export const entry = { module: entryServer };
|
|
330
337
|
export const routes = {
|
|
@@ -340,6 +347,61 @@ const reactRouterVitePlugin = _config => {
|
|
|
340
347
|
}`;
|
|
341
348
|
}).join(",\n ")}
|
|
342
349
|
};`;
|
|
350
|
+
if (ctx.reactRouterConfig.future.unstable_serverComponents) {
|
|
351
|
+
code += `
|
|
352
|
+
export { default as clientReferences } from ${JSON.stringify(clientReferencesId)};`;
|
|
353
|
+
}
|
|
354
|
+
return code;
|
|
355
|
+
};
|
|
356
|
+
let {
|
|
357
|
+
clientModules,
|
|
358
|
+
serverModules
|
|
359
|
+
} = getReactServerOptions();
|
|
360
|
+
let getReactServerEntry = () => {
|
|
361
|
+
invariant["default"](viteConfig, "viteconfig required to generate the react-server entry");
|
|
362
|
+
invariant["default"](ctx.entryReactServerFilePath, "entryReactServerFilePath required to generate the react-server entry");
|
|
363
|
+
let routes = ctx.serverBundleBuildConfig ?
|
|
364
|
+
// For server bundle builds, the server build should only import the
|
|
365
|
+
// routes for this bundle rather than importing all routes
|
|
366
|
+
ctx.serverBundleBuildConfig.routes :
|
|
367
|
+
// Otherwise, all routes are imported as usual
|
|
368
|
+
ctx.reactRouterConfig.routes;
|
|
369
|
+
return `
|
|
370
|
+
import * as entryServer from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, ctx.entryReactServerFilePath))};
|
|
371
|
+
${Object.keys(routes).map((key, index) => {
|
|
372
|
+
let route = routes[key];
|
|
373
|
+
return `import * as route${index} from ${JSON.stringify(resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)))};`;
|
|
374
|
+
}).join("\n")}
|
|
375
|
+
export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
|
|
376
|
+
export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
|
|
377
|
+
export const entry = { module: entryServer };
|
|
378
|
+
export const routes = {
|
|
379
|
+
${Object.keys(routes).map((key, index) => {
|
|
380
|
+
let route = routes[key];
|
|
381
|
+
return `${JSON.stringify(key)}: {
|
|
382
|
+
id: ${JSON.stringify(route.id)},
|
|
383
|
+
parentId: ${JSON.stringify(route.parentId)},
|
|
384
|
+
path: ${JSON.stringify(route.path)},
|
|
385
|
+
index: ${JSON.stringify(route.index)},
|
|
386
|
+
caseSensitive: ${JSON.stringify(route.caseSensitive)},
|
|
387
|
+
module: route${index}
|
|
388
|
+
}`;
|
|
389
|
+
}).join(",\n ")}
|
|
390
|
+
};`;
|
|
391
|
+
};
|
|
392
|
+
let getClientReferencesEntry = () => {
|
|
393
|
+
let result = "export default {";
|
|
394
|
+
for (let clientModule of clientModules) {
|
|
395
|
+
result += `${JSON.stringify(prodHash(clientModule))}: () => import(${JSON.stringify(clientModule)}),`;
|
|
396
|
+
}
|
|
397
|
+
return `${result}};`;
|
|
398
|
+
};
|
|
399
|
+
let getServerReferencesEntry = () => {
|
|
400
|
+
let result = "export default {";
|
|
401
|
+
for (let serverModule of serverModules) {
|
|
402
|
+
result += `${JSON.stringify(prodHash(serverModule))}: () => import(${JSON.stringify(serverModule)}),`;
|
|
403
|
+
}
|
|
404
|
+
return `${result}\};`;
|
|
343
405
|
};
|
|
344
406
|
let loadViteManifest = async directory => {
|
|
345
407
|
let manifestContents = await fse__namespace.readFile(path__namespace.resolve(directory, ".vite", "manifest.json"), "utf-8");
|
|
@@ -432,7 +494,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
432
494
|
path: route.path,
|
|
433
495
|
index: route.index,
|
|
434
496
|
caseSensitive: route.caseSensitive,
|
|
435
|
-
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}
|
|
497
|
+
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`),
|
|
436
498
|
hasAction: sourceExports.includes("action"),
|
|
437
499
|
hasLoader: sourceExports.includes("loader"),
|
|
438
500
|
hasClientAction: sourceExports.includes("clientAction"),
|
|
@@ -538,9 +600,9 @@ const reactRouterVitePlugin = _config => {
|
|
|
538
600
|
rollupOptions: {
|
|
539
601
|
...baseRollupOptions,
|
|
540
602
|
preserveEntrySignatures: "exports-only",
|
|
541
|
-
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${
|
|
603
|
+
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`), ...(ctx.reactRouterConfig.future.unstable_serverComponents ? clientModules : [])]
|
|
542
604
|
}
|
|
543
|
-
} : {
|
|
605
|
+
} : !ctx.reactRouterConfig.future.unstable_serverComponents || !process.env.REACT_SERVER_BUILD ? {
|
|
544
606
|
// We move SSR-only assets to client assets. Note that the
|
|
545
607
|
// SSR build can also emit code-split JS files (e.g. by
|
|
546
608
|
// dynamic import) under the same assets directory
|
|
@@ -561,6 +623,28 @@ const reactRouterVitePlugin = _config => {
|
|
|
561
623
|
format: ctx.reactRouterConfig.serverModuleFormat
|
|
562
624
|
}
|
|
563
625
|
}
|
|
626
|
+
} : {
|
|
627
|
+
// We move SSR-only assets to client assets. Note that the
|
|
628
|
+
// SSR build can also emit code-split JS files (e.g. by
|
|
629
|
+
// dynamic import) under the same assets directory
|
|
630
|
+
// regardless of "ssrEmitAssets" option, so we also need to
|
|
631
|
+
// keep these JS files have to be kept as-is.
|
|
632
|
+
ssrEmitAssets: true,
|
|
633
|
+
copyPublicDir: false,
|
|
634
|
+
// Assets in the public directory are only used by the client
|
|
635
|
+
manifest: true,
|
|
636
|
+
// We need the manifest to detect SSR-only assets
|
|
637
|
+
outDir: getReactServerBuildDirectory(ctx.reactRouterConfig),
|
|
638
|
+
rollupOptions: {
|
|
639
|
+
...baseRollupOptions,
|
|
640
|
+
preserveEntrySignatures: "exports-only",
|
|
641
|
+
// TODO: Add server references (serverModules) to input
|
|
642
|
+
input: reactServerBuildId,
|
|
643
|
+
output: {
|
|
644
|
+
entryFileNames: ctx.reactRouterConfig.serverBuildFile,
|
|
645
|
+
format: ctx.reactRouterConfig.serverModuleFormat
|
|
646
|
+
}
|
|
647
|
+
}
|
|
564
648
|
})
|
|
565
649
|
}
|
|
566
650
|
} : undefined),
|
|
@@ -635,8 +719,8 @@ const reactRouterVitePlugin = _config => {
|
|
|
635
719
|
if (styles.isCssModulesFile(id)) {
|
|
636
720
|
cssModulesManifest[id] = code;
|
|
637
721
|
}
|
|
638
|
-
if (
|
|
639
|
-
let routeModuleId = id.replace(
|
|
722
|
+
if (id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING)) {
|
|
723
|
+
let routeModuleId = id.replace(BUILD_CLIENT_ROUTE_QUERY_STRING, "");
|
|
640
724
|
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
|
|
641
725
|
let routeFileName = path__namespace.basename(routeModuleId);
|
|
642
726
|
let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
|
|
@@ -714,7 +798,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
714
798
|
// After the SSR build is finished, we inspect the Vite manifest for
|
|
715
799
|
// the SSR build and move server-only assets to client assets directory
|
|
716
800
|
async handler() {
|
|
717
|
-
if (!ctx.isSsrBuild) {
|
|
801
|
+
if (!ctx.isSsrBuild || process.env.REACT_SERVER_BUILD) {
|
|
718
802
|
return;
|
|
719
803
|
}
|
|
720
804
|
invariant["default"](viteConfig);
|
|
@@ -748,8 +832,19 @@ const reactRouterVitePlugin = _config => {
|
|
|
748
832
|
if (movedAssetPaths.length) {
|
|
749
833
|
viteConfig.logger.info(["", `${colors__default["default"].green("✓")} ${movedAssetPaths.length} asset${movedAssetPaths.length > 1 ? "s" : ""} moved from React Router server build to client assets.`, ...movedAssetPaths.map(movedAssetPath => colors__default["default"].dim(path__namespace.relative(ctx.rootDirectory, movedAssetPath))), ""].join("\n"));
|
|
750
834
|
}
|
|
835
|
+
if (ctx.reactRouterConfig.prerender != null) {
|
|
836
|
+
// If we have prerender routes, that takes precedence over SPA mode
|
|
837
|
+
// which is ssr:false and only the rot route being rendered
|
|
838
|
+
await handlePrerender(viteConfig, ctx.reactRouterConfig, serverBuildDirectory, clientBuildDirectory);
|
|
839
|
+
} else if (!ctx.reactRouterConfig.ssr) {
|
|
840
|
+
await handleSpaMode(viteConfig, ctx.reactRouterConfig, serverBuildDirectory, clientBuildDirectory);
|
|
841
|
+
}
|
|
842
|
+
// For both SPA mode and prerendering, we can remove the server builds
|
|
843
|
+
// if ssr:false is set
|
|
751
844
|
if (!ctx.reactRouterConfig.ssr) {
|
|
752
|
-
|
|
845
|
+
// Cleanup - we no longer need the server build assets
|
|
846
|
+
viteConfig.logger.info(["Removing the server build in", colors__default["default"].green(serverBuildDirectory), "due to ssr:false"].join(" "));
|
|
847
|
+
fse__namespace.removeSync(serverBuildDirectory);
|
|
753
848
|
}
|
|
754
849
|
}
|
|
755
850
|
},
|
|
@@ -787,6 +882,18 @@ const reactRouterVitePlugin = _config => {
|
|
|
787
882
|
});
|
|
788
883
|
return `window.__remixManifest=${reactRouterManifestString};`;
|
|
789
884
|
}
|
|
885
|
+
case vmod.resolve(reactServerBuildId):
|
|
886
|
+
{
|
|
887
|
+
return getReactServerEntry();
|
|
888
|
+
}
|
|
889
|
+
case vmod.resolve(clientReferencesId):
|
|
890
|
+
{
|
|
891
|
+
return getClientReferencesEntry();
|
|
892
|
+
}
|
|
893
|
+
case vmod.resolve(serverReferencesId):
|
|
894
|
+
{
|
|
895
|
+
return getServerReferencesEntry();
|
|
896
|
+
}
|
|
790
897
|
}
|
|
791
898
|
}
|
|
792
899
|
}, {
|
|
@@ -842,9 +949,20 @@ const reactRouterVitePlugin = _config => {
|
|
|
842
949
|
}, {
|
|
843
950
|
name: "react-router-route-exports",
|
|
844
951
|
async transform(code, id, options) {
|
|
845
|
-
|
|
952
|
+
var _ctx;
|
|
953
|
+
if (options !== null && options !== void 0 && options.ssr && !((_ctx = ctx) !== null && _ctx !== void 0 && _ctx.reactRouterConfig.future.unstable_serverComponents)) {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
846
956
|
let route = getRoute(ctx.reactRouterConfig, id);
|
|
847
957
|
if (!route) return;
|
|
958
|
+
let [filepath] = id.split("?");
|
|
959
|
+
if (ctx.reactRouterConfig.future.unstable_serverComponents && process.env.REACT_SERVER_BUILD) {
|
|
960
|
+
return removeExports.removeExports(code, CLIENT_ROUTE_EXPORTS, {
|
|
961
|
+
sourceMaps: true,
|
|
962
|
+
filename: id,
|
|
963
|
+
sourceFileName: filepath
|
|
964
|
+
});
|
|
965
|
+
}
|
|
848
966
|
if (!ctx.reactRouterConfig.ssr) {
|
|
849
967
|
let serverOnlyExports = esModuleLexer.parse(code)[1].map(exp => exp.n).filter(exp => SERVER_ONLY_ROUTE_EXPORTS.includes(exp));
|
|
850
968
|
if (serverOnlyExports.length > 0) {
|
|
@@ -860,7 +978,6 @@ const reactRouterVitePlugin = _config => {
|
|
|
860
978
|
}
|
|
861
979
|
}
|
|
862
980
|
}
|
|
863
|
-
let [filepath] = id.split("?");
|
|
864
981
|
return removeExports.removeExports(code, SERVER_ONLY_ROUTE_EXPORTS, {
|
|
865
982
|
sourceMaps: true,
|
|
866
983
|
filename: id,
|
|
@@ -902,11 +1019,6 @@ const reactRouterVitePlugin = _config => {
|
|
|
902
1019
|
let isJSX = filepath.endsWith("x");
|
|
903
1020
|
let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
|
|
904
1021
|
if (!useFastRefresh) return;
|
|
905
|
-
if (isClientRoute(id)) {
|
|
906
|
-
return {
|
|
907
|
-
code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
1022
|
let result = await babel__default["default"].transformAsync(code, {
|
|
911
1023
|
babelrc: false,
|
|
912
1024
|
configFile: false,
|
|
@@ -961,6 +1073,82 @@ const reactRouterVitePlugin = _config => {
|
|
|
961
1073
|
});
|
|
962
1074
|
return modules;
|
|
963
1075
|
}
|
|
1076
|
+
}, {
|
|
1077
|
+
name: "remix:react-server",
|
|
1078
|
+
config() {
|
|
1079
|
+
const env = process.env.REACT_SERVER_BUILD ? "server" : "client";
|
|
1080
|
+
switch (env) {
|
|
1081
|
+
case "server":
|
|
1082
|
+
return {
|
|
1083
|
+
optimizeDeps: {
|
|
1084
|
+
include: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"]
|
|
1085
|
+
},
|
|
1086
|
+
resolve: {
|
|
1087
|
+
conditions: ["react-server"]
|
|
1088
|
+
},
|
|
1089
|
+
ssr: {
|
|
1090
|
+
noExternal: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"],
|
|
1091
|
+
optimizeDeps: {
|
|
1092
|
+
include: ["react", "react/jsx-runtime", "react/jsx-dev-runtime", "react-server-dom-diy/server"]
|
|
1093
|
+
},
|
|
1094
|
+
resolve: {
|
|
1095
|
+
conditions: ["react-server"],
|
|
1096
|
+
externalConditions: ["react-server"]
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
configResolved(resolvedViteConfig) {
|
|
1103
|
+
viteConfig = resolvedViteConfig;
|
|
1104
|
+
invariant["default"](viteConfig);
|
|
1105
|
+
},
|
|
1106
|
+
transform(...args) {
|
|
1107
|
+
invariant["default"](viteConfig);
|
|
1108
|
+
const env = process.env.REACT_SERVER_BUILD ? "server" : "client";
|
|
1109
|
+
let hash = viteConfig.mode !== "production" ? devHash : prodHash;
|
|
1110
|
+
switch (env) {
|
|
1111
|
+
case "client":
|
|
1112
|
+
return unpluginRsc.rscClientPlugin.vite({
|
|
1113
|
+
include: ["**/*"],
|
|
1114
|
+
transformModuleId: hash,
|
|
1115
|
+
useServerRuntime: {
|
|
1116
|
+
function: "createServerReference",
|
|
1117
|
+
module: "@react-router/dev/dist/runtime.client.js"
|
|
1118
|
+
},
|
|
1119
|
+
onModuleFound(id, type) {
|
|
1120
|
+
switch (type) {
|
|
1121
|
+
case "use server":
|
|
1122
|
+
serverModules.add(id);
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}).transform.call(this, ...args);
|
|
1127
|
+
case "server":
|
|
1128
|
+
return unpluginRsc.rscServerPlugin.vite({
|
|
1129
|
+
include: ["**/*"],
|
|
1130
|
+
transformModuleId: hash,
|
|
1131
|
+
useClientRuntime: {
|
|
1132
|
+
function: "registerClientReference",
|
|
1133
|
+
module: "react-server-dom-diy/server"
|
|
1134
|
+
},
|
|
1135
|
+
useServerRuntime: {
|
|
1136
|
+
function: "registerServerReference",
|
|
1137
|
+
module: "react-server-dom-diy/server"
|
|
1138
|
+
},
|
|
1139
|
+
onModuleFound(id, type) {
|
|
1140
|
+
switch (type) {
|
|
1141
|
+
case "use client":
|
|
1142
|
+
clientModules.add(id);
|
|
1143
|
+
break;
|
|
1144
|
+
case "use server":
|
|
1145
|
+
serverModules.add(id);
|
|
1146
|
+
break;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}).transform.call(this, ...args);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
964
1152
|
}];
|
|
965
1153
|
};
|
|
966
1154
|
function isInReactRouterMonorepo() {
|
|
@@ -975,7 +1163,7 @@ function isEqualJson(v1, v2) {
|
|
|
975
1163
|
}
|
|
976
1164
|
function addRefreshWrapper(reactRouterConfig, code, id) {
|
|
977
1165
|
let route = getRoute(reactRouterConfig, id);
|
|
978
|
-
let acceptExports = route
|
|
1166
|
+
let acceptExports = route ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
|
|
979
1167
|
return REACT_REFRESH_HEADER.replaceAll("__SOURCE__", JSON.stringify(id)) + code + REACT_REFRESH_FOOTER.replaceAll("__SOURCE__", JSON.stringify(id)).replaceAll("__ACCEPT_EXPORTS__", JSON.stringify(acceptExports)).replaceAll("__ROUTE_ID__", JSON.stringify(route === null || route === void 0 ? void 0 : route.id));
|
|
980
1168
|
}
|
|
981
1169
|
const REACT_REFRESH_HEADER = `
|
|
@@ -1039,32 +1227,151 @@ async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
|
|
|
1039
1227
|
};
|
|
1040
1228
|
return info;
|
|
1041
1229
|
}
|
|
1042
|
-
async function
|
|
1043
|
-
|
|
1044
|
-
// proper HydrateFallback ... or not! Maybe they have a static landing page
|
|
1045
|
-
// generated from routes/_index.tsx.
|
|
1046
|
-
let serverBuildPath = path__namespace.join(serverBuildDirectoryPath, serverBuildFile);
|
|
1230
|
+
async function getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory) {
|
|
1231
|
+
let serverBuildPath = path__namespace.join(serverBuildDirectory, reactRouterConfig.serverBuildFile);
|
|
1047
1232
|
let build = await import(url__namespace.pathToFileURL(serverBuildPath).toString());
|
|
1048
1233
|
let {
|
|
1049
1234
|
createRequestHandler: createHandler
|
|
1050
1235
|
} = await import('@react-router/node');
|
|
1051
|
-
|
|
1052
|
-
|
|
1236
|
+
return {
|
|
1237
|
+
build,
|
|
1238
|
+
handler: createHandler(build, viteConfig.mode)
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory, clientBuildDirectory) {
|
|
1242
|
+
let {
|
|
1243
|
+
handler
|
|
1244
|
+
} = await getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory);
|
|
1245
|
+
let request = new Request(`http://localhost${reactRouterConfig.basename}`);
|
|
1246
|
+
let response = await handler(request);
|
|
1247
|
+
let html = await response.text();
|
|
1248
|
+
validatePrerenderedResponse(response, html, "SPA Mode", "/");
|
|
1249
|
+
validatePrerenderedHtml(html, "SPA Mode");
|
|
1250
|
+
// Write out the index.html file for the SPA
|
|
1251
|
+
await fse__namespace.writeFile(path__namespace.join(clientBuildDirectory, "index.html"), html);
|
|
1252
|
+
viteConfig.logger.info("SPA Mode: index.html has been written to your " + colors__default["default"].bold(path__namespace.relative(process.cwd(), clientBuildDirectory)) + " directory");
|
|
1253
|
+
}
|
|
1254
|
+
async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirectory, clientBuildDirectory) {
|
|
1255
|
+
let {
|
|
1256
|
+
build,
|
|
1257
|
+
handler
|
|
1258
|
+
} = await getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory);
|
|
1259
|
+
let routes = createPrerenderRoutes(build.routes);
|
|
1260
|
+
let routesToPrerender = reactRouterConfig.prerender || ["/"];
|
|
1261
|
+
let requestInit = {
|
|
1262
|
+
headers: {
|
|
1263
|
+
// Header that can be used in the loader to know if you're running at
|
|
1264
|
+
// build time or runtime
|
|
1265
|
+
"X-React-Router-Prerender": "yes"
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
for (let path of routesToPrerender) {
|
|
1269
|
+
var _matchRoutes;
|
|
1270
|
+
let hasLoaders = (_matchRoutes = reactRouter.matchRoutes(routes, path)) === null || _matchRoutes === void 0 ? void 0 : _matchRoutes.some(m => m.route.loader);
|
|
1271
|
+
if (hasLoaders) {
|
|
1272
|
+
await prerenderData(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
|
|
1273
|
+
}
|
|
1274
|
+
await prerenderRoute(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
|
|
1275
|
+
}
|
|
1276
|
+
async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
|
|
1277
|
+
let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
|
|
1278
|
+
let request = new Request(`http://localhost${normalizedPath}`, requestInit);
|
|
1279
|
+
let response = await handler(request);
|
|
1280
|
+
let data = await response.text();
|
|
1281
|
+
validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
|
|
1282
|
+
// Write out the .data file
|
|
1283
|
+
let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
|
|
1284
|
+
let outfile = path__namespace.join(outdir, normalizedPath.split("/").join(path__namespace.sep));
|
|
1285
|
+
await fse__namespace.ensureDir(path__namespace.dirname(outfile));
|
|
1286
|
+
await fse__namespace.outputFile(outfile, data);
|
|
1287
|
+
viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
|
|
1291
|
+
let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/");
|
|
1292
|
+
let request = new Request(`http://localhost${normalizedPath}`, requestInit);
|
|
1293
|
+
let response = await handler(request);
|
|
1053
1294
|
let html = await response.text();
|
|
1295
|
+
validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
|
|
1296
|
+
if (!reactRouterConfig.ssr) {
|
|
1297
|
+
validatePrerenderedHtml(html, "Prerender");
|
|
1298
|
+
}
|
|
1299
|
+
// Write out the HTML file
|
|
1300
|
+
let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
|
|
1301
|
+
let outfile = path__namespace.join(outdir, ...normalizedPath.split("/"), "index.html");
|
|
1302
|
+
await fse__namespace.ensureDir(path__namespace.dirname(outfile));
|
|
1303
|
+
await fse__namespace.outputFile(outfile, html);
|
|
1304
|
+
viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
|
|
1305
|
+
}
|
|
1306
|
+
function validatePrerenderedResponse(response, html, prefix, path) {
|
|
1054
1307
|
if (response.status !== 200) {
|
|
1055
|
-
throw new Error(
|
|
1308
|
+
throw new Error(`${prefix}: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while prerendering the \`${path}\` ` + `path.\n${html}`);
|
|
1056
1309
|
}
|
|
1310
|
+
}
|
|
1311
|
+
function validatePrerenderedHtml(html, prefix) {
|
|
1057
1312
|
if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
|
|
1058
|
-
throw new Error(
|
|
1313
|
+
throw new Error(`${prefix}: Did you forget to include <Scripts/> in your root route? ` + "Your pre-rendered HTML files cannot hydrate without `<Scripts />`.");
|
|
1059
1314
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1315
|
+
}
|
|
1316
|
+
// Note: Duplicated from remix-server-runtime
|
|
1317
|
+
function groupRoutesByParentId(manifest) {
|
|
1318
|
+
let routes = {};
|
|
1319
|
+
Object.values(manifest).forEach(route => {
|
|
1320
|
+
let parentId = route.parentId || "";
|
|
1321
|
+
if (!routes[parentId]) {
|
|
1322
|
+
routes[parentId] = [];
|
|
1323
|
+
}
|
|
1324
|
+
routes[parentId].push(route);
|
|
1325
|
+
});
|
|
1326
|
+
return routes;
|
|
1327
|
+
}
|
|
1328
|
+
// Note: Duplicated from remix-server-runtime
|
|
1329
|
+
function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
|
|
1330
|
+
return (routesByParentId[parentId] || []).map(route => {
|
|
1331
|
+
let commonRoute = {
|
|
1332
|
+
// Always include root due to default boundaries
|
|
1333
|
+
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
|
|
1334
|
+
id: route.id,
|
|
1335
|
+
path: route.path,
|
|
1336
|
+
loader: route.module.loader ? () => null : undefined,
|
|
1337
|
+
action: undefined,
|
|
1338
|
+
handle: route.module.handle
|
|
1339
|
+
};
|
|
1340
|
+
return route.index ? {
|
|
1341
|
+
index: true,
|
|
1342
|
+
...commonRoute
|
|
1343
|
+
} : {
|
|
1344
|
+
caseSensitive: route.caseSensitive,
|
|
1345
|
+
children: createPrerenderRoutes(manifest, route.id, routesByParentId),
|
|
1346
|
+
...commonRoute
|
|
1347
|
+
};
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
function prodHash(str, _) {
|
|
1351
|
+
return `/${path__namespace.relative(process.cwd(), str)}`;
|
|
1352
|
+
}
|
|
1353
|
+
function devHash(str, _) {
|
|
1354
|
+
const resolved = path__namespace.resolve(str);
|
|
1355
|
+
let unixPath = resolved.replace(/\\/g, "/");
|
|
1356
|
+
if (!unixPath.startsWith("/")) {
|
|
1357
|
+
unixPath = `/${unixPath}`;
|
|
1358
|
+
}
|
|
1359
|
+
if (resolved.startsWith(process.cwd())) {
|
|
1360
|
+
return `/${path__namespace.relative(process.cwd(), unixPath)}`;
|
|
1361
|
+
}
|
|
1362
|
+
return `/@fs${unixPath}`;
|
|
1363
|
+
}
|
|
1364
|
+
global.__clientModules = global.__clientModules || new Set();
|
|
1365
|
+
global.__serverModules = global.__serverModules || new Set();
|
|
1366
|
+
function getReactServerOptions() {
|
|
1367
|
+
return {
|
|
1368
|
+
clientModules: global.__clientModules,
|
|
1369
|
+
serverModules: global.__serverModules
|
|
1370
|
+
};
|
|
1065
1371
|
}
|
|
1066
1372
|
|
|
1067
1373
|
exports.extractPluginContext = extractPluginContext;
|
|
1374
|
+
exports.getReactServerOptions = getReactServerOptions;
|
|
1068
1375
|
exports.getServerBuildDirectory = getServerBuildDirectory;
|
|
1069
1376
|
exports.loadPluginContext = loadPluginContext;
|
|
1070
1377
|
exports.reactRouterVitePlugin = reactRouterVitePlugin;
|