@react-router/dev 0.0.0-experimental-fbbd4fd81 → 0.0.0-experimental-cb25a21e1
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.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 +13 -1
- package/dist/config.js +16 -7
- package/dist/index.js +1 -1
- package/dist/invariant.js +1 -1
- package/dist/vite/babel.js +1 -1
- package/dist/vite/build.js +1 -1
- 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.js +136 -34
- package/dist/vite/profiler.js +1 -1
- package/dist/vite/remove-exports.js +1 -1
- 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 +7 -7
package/dist/cli/commands.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-cb25a21e1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -128,7 +128,7 @@ async function generateEntry(entry, reactRouterRoot, flags = {}) {
|
|
|
128
128
|
}
|
|
129
129
|
let defaultsDirectory = path__namespace.resolve(__dirname, "..", "config", "defaults");
|
|
130
130
|
let defaultEntryClient = path__namespace.resolve(defaultsDirectory, "entry.client.tsx");
|
|
131
|
-
let defaultEntryServer = path__namespace.resolve(defaultsDirectory, (ctx === null || ctx === void 0 ? void 0 : ctx.reactRouterConfig.ssr) === false
|
|
131
|
+
let defaultEntryServer = path__namespace.resolve(defaultsDirectory, (ctx === null || ctx === void 0 ? void 0 : ctx.reactRouterConfig.ssr) === false ? `entry.server.spa.tsx` : `entry.server.${serverRuntime}.tsx`);
|
|
132
132
|
let isServerEntry = entry === "entry.server";
|
|
133
133
|
let contents = isServerEntry ? await createServerEntry(rootDirectory, appDirectory, defaultEntryServer) : await createClientEntry(rootDirectory, appDirectory, defaultEntryClient);
|
|
134
134
|
let useTypeScript = flags.typescript ?? true;
|
package/dist/cli/index.js
CHANGED
package/dist/cli/run.js
CHANGED
package/dist/cli.js
CHANGED
package/dist/colors.js
CHANGED
|
@@ -1,20 +1,73 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { PassThrough } from "node:stream";
|
|
2
|
+
|
|
3
|
+
import type { AppLoadContext, EntryContext } from "@react-router/node";
|
|
4
|
+
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
2
5
|
import { RemixServer } from "react-router";
|
|
3
|
-
import
|
|
4
|
-
|
|
6
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
7
|
+
|
|
8
|
+
const ABORT_DELAY = 5_000;
|
|
5
9
|
|
|
6
10
|
export default function handleRequest(
|
|
7
11
|
request: Request,
|
|
8
12
|
responseStatusCode: number,
|
|
9
13
|
responseHeaders: Headers,
|
|
10
|
-
remixContext: EntryContext
|
|
14
|
+
remixContext: EntryContext,
|
|
15
|
+
loadContext: AppLoadContext
|
|
11
16
|
) {
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
return handleBotRequest(
|
|
18
|
+
request,
|
|
19
|
+
responseStatusCode,
|
|
20
|
+
responseHeaders,
|
|
21
|
+
remixContext
|
|
14
22
|
);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function handleBotRequest(
|
|
26
|
+
request: Request,
|
|
27
|
+
responseStatusCode: number,
|
|
28
|
+
responseHeaders: Headers,
|
|
29
|
+
remixContext: EntryContext
|
|
30
|
+
) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let shellRendered = false;
|
|
33
|
+
const { pipe, abort } = renderToPipeableStream(
|
|
34
|
+
<RemixServer
|
|
35
|
+
context={remixContext}
|
|
36
|
+
url={request.url}
|
|
37
|
+
abortDelay={ABORT_DELAY}
|
|
38
|
+
/>,
|
|
39
|
+
{
|
|
40
|
+
onAllReady() {
|
|
41
|
+
shellRendered = true;
|
|
42
|
+
const body = new PassThrough();
|
|
43
|
+
const stream = createReadableStreamFromReadable(body);
|
|
44
|
+
|
|
45
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
46
|
+
|
|
47
|
+
resolve(
|
|
48
|
+
new Response(stream, {
|
|
49
|
+
headers: responseHeaders,
|
|
50
|
+
status: responseStatusCode,
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
pipe(body);
|
|
55
|
+
},
|
|
56
|
+
onShellError(error: unknown) {
|
|
57
|
+
reject(error);
|
|
58
|
+
},
|
|
59
|
+
onError(error: unknown) {
|
|
60
|
+
responseStatusCode = 500;
|
|
61
|
+
// Log streaming rendering errors from inside the shell. Don't log
|
|
62
|
+
// errors encountered during initial shell rendering since they'll
|
|
63
|
+
// reject and get logged in handleDocumentRequest.
|
|
64
|
+
if (shellRendered) {
|
|
65
|
+
console.error(error);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
setTimeout(abort, ABORT_DELAY);
|
|
19
72
|
});
|
|
20
73
|
}
|
package/dist/config/format.js
CHANGED
package/dist/config/routes.js
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -39,7 +39,6 @@ interface FutureConfig {
|
|
|
39
39
|
v3_fetcherPersist: boolean;
|
|
40
40
|
v3_relativeSplatPath: boolean;
|
|
41
41
|
v3_throwAbortReason: boolean;
|
|
42
|
-
unstable_singleFetch: boolean;
|
|
43
42
|
}
|
|
44
43
|
export type BuildManifest = DefaultBuildManifest | ServerBundlesBuildManifest;
|
|
45
44
|
type BuildEndHook = (args: {
|
|
@@ -93,6 +92,11 @@ export type VitePluginConfig = {
|
|
|
93
92
|
* Defaults to `false`.
|
|
94
93
|
*/
|
|
95
94
|
manifest?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* An array of URLs to prerender to HTML files at build time. Can also be a
|
|
97
|
+
* function returning an array to dynamically generate URLs.
|
|
98
|
+
*/
|
|
99
|
+
prerender?: Array<string> | (() => Array<string> | Promise<Array<string>>);
|
|
96
100
|
/**
|
|
97
101
|
* An array of React Router plugin config presets to ease integration with
|
|
98
102
|
* other platforms and tools.
|
|
@@ -144,6 +148,10 @@ export type ResolvedVitePluginConfig = Readonly<{
|
|
|
144
148
|
* Defaults to `false`.
|
|
145
149
|
*/
|
|
146
150
|
manifest: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* An array of URLs to prerender to HTML files at build time.
|
|
153
|
+
*/
|
|
154
|
+
prerender: Array<string> | null;
|
|
147
155
|
/**
|
|
148
156
|
* Derived from Vite's `base` config
|
|
149
157
|
* */
|
|
@@ -207,6 +215,10 @@ export declare function resolveReactRouterConfig({ rootDirectory, reactRouterUse
|
|
|
207
215
|
* Defaults to `false`.
|
|
208
216
|
*/
|
|
209
217
|
manifest: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* An array of URLs to prerender to HTML files at build time.
|
|
220
|
+
*/
|
|
221
|
+
prerender: string[] | null;
|
|
210
222
|
/**
|
|
211
223
|
* Derived from Vite's `base` config
|
|
212
224
|
* */
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @react-router/dev v0.0.0-experimental-
|
|
2
|
+
* @react-router/dev v0.0.0-experimental-cb25a21e1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -124,6 +124,7 @@ async function resolveReactRouterConfig({
|
|
|
124
124
|
ignoredRouteFiles,
|
|
125
125
|
manifest,
|
|
126
126
|
routes: userRoutesFunction,
|
|
127
|
+
prerender: prerenderConfig,
|
|
127
128
|
serverBuildFile,
|
|
128
129
|
serverBundles,
|
|
129
130
|
serverModuleFormat,
|
|
@@ -133,12 +134,21 @@ async function resolveReactRouterConfig({
|
|
|
133
134
|
// Default values should be completely overridden by user/preset config, not merged
|
|
134
135
|
...mergeReactRouterConfig(...presets, reactRouterUserConfig)
|
|
135
136
|
};
|
|
136
|
-
let isSpaMode = !ssr;
|
|
137
137
|
// Log warning for incompatible vite config flags
|
|
138
|
-
if (
|
|
138
|
+
if (!ssr && serverBundles) {
|
|
139
139
|
console.warn(colors__default["default"].yellow(colors__default["default"].bold("⚠️ SPA Mode: ") + "the `serverBundles` config is invalid with " + "`ssr:false` and will be ignored`"));
|
|
140
140
|
serverBundles = undefined;
|
|
141
141
|
}
|
|
142
|
+
let prerender = null;
|
|
143
|
+
if (prerenderConfig) {
|
|
144
|
+
if (Array.isArray(prerenderConfig)) {
|
|
145
|
+
prerender = prerenderConfig;
|
|
146
|
+
} else if (typeof prerenderConfig === "function") {
|
|
147
|
+
prerender = await prerenderConfig();
|
|
148
|
+
} else {
|
|
149
|
+
throw new Error("The `prerender` config must be an array of string paths, or a function " + "returning an array of string paths");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
142
152
|
let appDirectory = path__default["default"].resolve(rootDirectory, userAppDirectory || "app");
|
|
143
153
|
let buildDirectory = path__default["default"].resolve(rootDirectory, userBuildDirectory);
|
|
144
154
|
let publicPath = viteUserConfig.base ?? "/";
|
|
@@ -177,8 +187,7 @@ async function resolveReactRouterConfig({
|
|
|
177
187
|
let future = {
|
|
178
188
|
v3_fetcherPersist: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_fetcherPersist) === true,
|
|
179
189
|
v3_relativeSplatPath: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_relativeSplatPath) === true,
|
|
180
|
-
v3_throwAbortReason: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_throwAbortReason) === true
|
|
181
|
-
unstable_singleFetch: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.unstable_singleFetch) === true
|
|
190
|
+
v3_throwAbortReason: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_throwAbortReason) === true
|
|
182
191
|
};
|
|
183
192
|
let reactRouterConfig = deepFreeze({
|
|
184
193
|
appDirectory,
|
|
@@ -187,6 +196,7 @@ async function resolveReactRouterConfig({
|
|
|
187
196
|
buildEnd,
|
|
188
197
|
future,
|
|
189
198
|
manifest,
|
|
199
|
+
prerender,
|
|
190
200
|
publicPath,
|
|
191
201
|
routes: routes$1,
|
|
192
202
|
serverBuildFile,
|
|
@@ -210,7 +220,6 @@ async function resolveEntryFiles({
|
|
|
210
220
|
appDirectory,
|
|
211
221
|
future
|
|
212
222
|
} = reactRouterConfig;
|
|
213
|
-
let isSpaMode = !reactRouterConfig.ssr;
|
|
214
223
|
let defaultsDirectory = path__default["default"].resolve(__dirname, "config", "defaults");
|
|
215
224
|
let userEntryClientFile = findEntry(appDirectory, "entry.client");
|
|
216
225
|
let userEntryServerFile = findEntry(appDirectory, "entry.server");
|
|
@@ -218,7 +227,7 @@ async function resolveEntryFiles({
|
|
|
218
227
|
let entryClientFile = userEntryClientFile || "entry.client.tsx";
|
|
219
228
|
let pkgJson = await PackageJson__default["default"].load(rootDirectory);
|
|
220
229
|
let deps = pkgJson.content.dependencies ?? {};
|
|
221
|
-
if (
|
|
230
|
+
if (!reactRouterConfig.ssr) {
|
|
222
231
|
// This is a super-simple default since we don't need streaming in SPA Mode.
|
|
223
232
|
// We can include this in a remix-spa template, but right now `npx remix reveal`
|
|
224
233
|
// will still expose the streaming template since that command doesn't have
|
package/dist/index.js
CHANGED
package/dist/invariant.js
CHANGED
package/dist/vite/babel.js
CHANGED
package/dist/vite/build.js
CHANGED
package/dist/vite/dev.js
CHANGED
package/dist/vite/index.js
CHANGED
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-cb25a21e1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -18,6 +18,7 @@ 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');
|
|
@@ -107,9 +108,11 @@ async function loadPluginContext({
|
|
|
107
108
|
}
|
|
108
109
|
const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
|
|
109
110
|
const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
/** This is used to manage a build optimization to remove unused route exports
|
|
112
|
+
from the client build output. This is important in cases where custom route
|
|
113
|
+
exports are only ever used on the server. Without this optimization we can't
|
|
114
|
+
tree-shake any unused custom exports because routes are entry points. */
|
|
115
|
+
const BUILD_CLIENT_ROUTE_QUERY_STRING = "?__remix-build-client-route";
|
|
113
116
|
let serverBuildId = vmod.id("server-build");
|
|
114
117
|
let serverManifestId = vmod.id("server-manifest");
|
|
115
118
|
let browserManifestId = vmod.id("browser-manifest");
|
|
@@ -134,13 +137,10 @@ const getHash = (source, maxLength) => {
|
|
|
134
137
|
let hash = node_crypto.createHash("sha256").update(source).digest("hex");
|
|
135
138
|
return typeof maxLength === "number" ? hash.slice(0, maxLength) : hash;
|
|
136
139
|
};
|
|
137
|
-
const isClientRoute = id => {
|
|
138
|
-
return id.endsWith(CLIENT_ROUTE_QUERY_STRING);
|
|
139
|
-
};
|
|
140
140
|
const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
|
|
141
141
|
let vite = importViteEsmSync.importViteEsmSync();
|
|
142
142
|
let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
|
|
143
|
-
let entryChunk = viteManifest[rootRelativeFilePath +
|
|
143
|
+
let entryChunk = viteManifest[rootRelativeFilePath + BUILD_CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
|
|
144
144
|
if (!entryChunk) {
|
|
145
145
|
let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
|
|
146
146
|
throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
|
|
@@ -324,7 +324,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
324
324
|
export const assetsBuildDirectory = ${JSON.stringify(path__namespace.relative(ctx.rootDirectory, getClientBuildDirectory(ctx.reactRouterConfig)))};
|
|
325
325
|
export const basename = ${JSON.stringify(ctx.reactRouterConfig.basename)};
|
|
326
326
|
export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
|
|
327
|
-
export const isSpaMode = ${!ctx.reactRouterConfig.ssr};
|
|
327
|
+
export const isSpaMode = ${!ctx.reactRouterConfig.ssr && ctx.reactRouterConfig.prerender == null};
|
|
328
328
|
export const publicPath = ${JSON.stringify(ctx.reactRouterConfig.publicPath)};
|
|
329
329
|
export const entry = { module: entryServer };
|
|
330
330
|
export const routes = {
|
|
@@ -432,7 +432,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
432
432
|
path: route.path,
|
|
433
433
|
index: route.index,
|
|
434
434
|
caseSensitive: route.caseSensitive,
|
|
435
|
-
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}
|
|
435
|
+
module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`),
|
|
436
436
|
hasAction: sourceExports.includes("action"),
|
|
437
437
|
hasLoader: sourceExports.includes("loader"),
|
|
438
438
|
hasClientAction: sourceExports.includes("clientAction"),
|
|
@@ -538,7 +538,7 @@ const reactRouterVitePlugin = _config => {
|
|
|
538
538
|
rollupOptions: {
|
|
539
539
|
...baseRollupOptions,
|
|
540
540
|
preserveEntrySignatures: "exports-only",
|
|
541
|
-
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${
|
|
541
|
+
input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`)]
|
|
542
542
|
}
|
|
543
543
|
} : {
|
|
544
544
|
// We move SSR-only assets to client assets. Note that the
|
|
@@ -635,8 +635,8 @@ const reactRouterVitePlugin = _config => {
|
|
|
635
635
|
if (styles.isCssModulesFile(id)) {
|
|
636
636
|
cssModulesManifest[id] = code;
|
|
637
637
|
}
|
|
638
|
-
if (
|
|
639
|
-
let routeModuleId = id.replace(
|
|
638
|
+
if (id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING)) {
|
|
639
|
+
let routeModuleId = id.replace(BUILD_CLIENT_ROUTE_QUERY_STRING, "");
|
|
640
640
|
let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
|
|
641
641
|
let routeFileName = path__namespace.basename(routeModuleId);
|
|
642
642
|
let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
|
|
@@ -748,8 +748,19 @@ const reactRouterVitePlugin = _config => {
|
|
|
748
748
|
if (movedAssetPaths.length) {
|
|
749
749
|
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
750
|
}
|
|
751
|
+
if (ctx.reactRouterConfig.prerender != null) {
|
|
752
|
+
// If we have prerender routes, that takes precedence over SPA mode
|
|
753
|
+
// which is ssr:false and only the rot route being rendered
|
|
754
|
+
await handlePrerender(viteConfig, ctx.reactRouterConfig, serverBuildDirectory, clientBuildDirectory);
|
|
755
|
+
} else if (!ctx.reactRouterConfig.ssr) {
|
|
756
|
+
await handleSpaMode(viteConfig, ctx.reactRouterConfig, serverBuildDirectory, clientBuildDirectory);
|
|
757
|
+
}
|
|
758
|
+
// For both SPA mode and prerendering, we can remove the server builds
|
|
759
|
+
// if ssr:false is set
|
|
751
760
|
if (!ctx.reactRouterConfig.ssr) {
|
|
752
|
-
|
|
761
|
+
// Cleanup - we no longer need the server build assets
|
|
762
|
+
viteConfig.logger.info(["Removing the server build in", colors__default["default"].green(serverBuildDirectory), "due to ssr:false"].join(" "));
|
|
763
|
+
fse__namespace.removeSync(serverBuildDirectory);
|
|
753
764
|
}
|
|
754
765
|
}
|
|
755
766
|
},
|
|
@@ -902,11 +913,6 @@ const reactRouterVitePlugin = _config => {
|
|
|
902
913
|
let isJSX = filepath.endsWith("x");
|
|
903
914
|
let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
|
|
904
915
|
if (!useFastRefresh) return;
|
|
905
|
-
if (isClientRoute(id)) {
|
|
906
|
-
return {
|
|
907
|
-
code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
916
|
let result = await babel__default["default"].transformAsync(code, {
|
|
911
917
|
babelrc: false,
|
|
912
918
|
configFile: false,
|
|
@@ -975,7 +981,7 @@ function isEqualJson(v1, v2) {
|
|
|
975
981
|
}
|
|
976
982
|
function addRefreshWrapper(reactRouterConfig, code, id) {
|
|
977
983
|
let route = getRoute(reactRouterConfig, id);
|
|
978
|
-
let acceptExports = route
|
|
984
|
+
let acceptExports = route ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
|
|
979
985
|
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
986
|
}
|
|
981
987
|
const REACT_REFRESH_HEADER = `
|
|
@@ -1039,29 +1045,125 @@ async function getRouteMetadata(ctx, viteChildCompiler, route, readRouteFile) {
|
|
|
1039
1045
|
};
|
|
1040
1046
|
return info;
|
|
1041
1047
|
}
|
|
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);
|
|
1048
|
+
async function getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory) {
|
|
1049
|
+
let serverBuildPath = path__namespace.join(serverBuildDirectory, reactRouterConfig.serverBuildFile);
|
|
1047
1050
|
let build = await import(url__namespace.pathToFileURL(serverBuildPath).toString());
|
|
1048
1051
|
let {
|
|
1049
1052
|
createRequestHandler: createHandler
|
|
1050
1053
|
} = await import('@react-router/node');
|
|
1051
|
-
|
|
1052
|
-
|
|
1054
|
+
return {
|
|
1055
|
+
build,
|
|
1056
|
+
handler: createHandler(build, viteConfig.mode)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory, clientBuildDirectory) {
|
|
1060
|
+
let {
|
|
1061
|
+
handler
|
|
1062
|
+
} = await getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory);
|
|
1063
|
+
let request = new Request(`http://localhost${reactRouterConfig.basename}`);
|
|
1064
|
+
let response = await handler(request);
|
|
1053
1065
|
let html = await response.text();
|
|
1066
|
+
validatePrerenderedResponse(response, html, "SPA Mode", "/");
|
|
1067
|
+
validatePrerenderedHtml(html, "SPA Mode");
|
|
1068
|
+
// Write out the index.html file for the SPA
|
|
1069
|
+
await fse__namespace.writeFile(path__namespace.join(clientBuildDirectory, "index.html"), html);
|
|
1070
|
+
viteConfig.logger.info("SPA Mode: index.html has been written to your " + colors__default["default"].bold(path__namespace.relative(process.cwd(), clientBuildDirectory)) + " directory");
|
|
1071
|
+
}
|
|
1072
|
+
async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirectory, clientBuildDirectory) {
|
|
1073
|
+
let {
|
|
1074
|
+
build,
|
|
1075
|
+
handler
|
|
1076
|
+
} = await getPrerenderBuildAndHandler(viteConfig, reactRouterConfig, serverBuildDirectory);
|
|
1077
|
+
let routes = createPrerenderRoutes(build.routes);
|
|
1078
|
+
let routesToPrerender = reactRouterConfig.prerender || ["/"];
|
|
1079
|
+
let requestInit = {
|
|
1080
|
+
headers: {
|
|
1081
|
+
// Header that can be used in the loader to know if you're running at
|
|
1082
|
+
// build time or runtime
|
|
1083
|
+
"X-React-Router-Prerender": "yes"
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
for (let path of routesToPrerender) {
|
|
1087
|
+
var _matchRoutes;
|
|
1088
|
+
let hasLoaders = (_matchRoutes = reactRouter.matchRoutes(routes, path)) === null || _matchRoutes === void 0 ? void 0 : _matchRoutes.some(m => m.route.loader);
|
|
1089
|
+
if (hasLoaders) {
|
|
1090
|
+
await prerenderData(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
|
|
1091
|
+
}
|
|
1092
|
+
await prerenderRoute(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
|
|
1093
|
+
}
|
|
1094
|
+
async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
|
|
1095
|
+
let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
|
|
1096
|
+
let request = new Request(`http://localhost${normalizedPath}`, requestInit);
|
|
1097
|
+
let response = await handler(request);
|
|
1098
|
+
let data = await response.text();
|
|
1099
|
+
validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
|
|
1100
|
+
// Write out the .data file
|
|
1101
|
+
let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
|
|
1102
|
+
let outfile = path__namespace.join(outdir, normalizedPath.split("/").join(path__namespace.sep));
|
|
1103
|
+
await fse__namespace.ensureDir(path__namespace.dirname(outfile));
|
|
1104
|
+
await fse__namespace.outputFile(outfile, data);
|
|
1105
|
+
viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
|
|
1109
|
+
let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/");
|
|
1110
|
+
let request = new Request(`http://localhost${normalizedPath}`, requestInit);
|
|
1111
|
+
let response = await handler(request);
|
|
1112
|
+
let html = await response.text();
|
|
1113
|
+
validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
|
|
1114
|
+
if (!reactRouterConfig.ssr) {
|
|
1115
|
+
validatePrerenderedHtml(html, "Prerender");
|
|
1116
|
+
}
|
|
1117
|
+
// Write out the HTML file
|
|
1118
|
+
let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
|
|
1119
|
+
let outfile = path__namespace.join(outdir, ...normalizedPath.split("/"), "index.html");
|
|
1120
|
+
await fse__namespace.ensureDir(path__namespace.dirname(outfile));
|
|
1121
|
+
await fse__namespace.outputFile(outfile, html);
|
|
1122
|
+
viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
|
|
1123
|
+
}
|
|
1124
|
+
function validatePrerenderedResponse(response, html, prefix, path) {
|
|
1054
1125
|
if (response.status !== 200) {
|
|
1055
|
-
throw new Error(
|
|
1126
|
+
throw new Error(`${prefix}: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while prerendering the \`${path}\` ` + `path.\n${html}`);
|
|
1056
1127
|
}
|
|
1128
|
+
}
|
|
1129
|
+
function validatePrerenderedHtml(html, prefix) {
|
|
1057
1130
|
if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
|
|
1058
|
-
throw new Error(
|
|
1131
|
+
throw new Error(`${prefix}: Did you forget to include <Scripts/> in your root route? ` + "Your pre-rendered HTML files cannot hydrate without `<Scripts />`.");
|
|
1059
1132
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1133
|
+
}
|
|
1134
|
+
// Note: Duplicated from remix-server-runtime
|
|
1135
|
+
function groupRoutesByParentId(manifest) {
|
|
1136
|
+
let routes = {};
|
|
1137
|
+
Object.values(manifest).forEach(route => {
|
|
1138
|
+
let parentId = route.parentId || "";
|
|
1139
|
+
if (!routes[parentId]) {
|
|
1140
|
+
routes[parentId] = [];
|
|
1141
|
+
}
|
|
1142
|
+
routes[parentId].push(route);
|
|
1143
|
+
});
|
|
1144
|
+
return routes;
|
|
1145
|
+
}
|
|
1146
|
+
// Note: Duplicated from remix-server-runtime
|
|
1147
|
+
function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
|
|
1148
|
+
return (routesByParentId[parentId] || []).map(route => {
|
|
1149
|
+
let commonRoute = {
|
|
1150
|
+
// Always include root due to default boundaries
|
|
1151
|
+
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
|
|
1152
|
+
id: route.id,
|
|
1153
|
+
path: route.path,
|
|
1154
|
+
loader: route.module.loader ? () => null : undefined,
|
|
1155
|
+
action: undefined,
|
|
1156
|
+
handle: route.module.handle
|
|
1157
|
+
};
|
|
1158
|
+
return route.index ? {
|
|
1159
|
+
index: true,
|
|
1160
|
+
...commonRoute
|
|
1161
|
+
} : {
|
|
1162
|
+
caseSensitive: route.caseSensitive,
|
|
1163
|
+
children: createPrerenderRoutes(manifest, route.id, routesByParentId),
|
|
1164
|
+
...commonRoute
|
|
1165
|
+
};
|
|
1166
|
+
});
|
|
1065
1167
|
}
|
|
1066
1168
|
|
|
1067
1169
|
exports.extractPluginContext = extractPluginContext;
|
package/dist/vite/profiler.js
CHANGED
package/dist/vite/styles.js
CHANGED
package/dist/vite/vmod.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-router/dev",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-cb25a21e1",
|
|
4
4
|
"description": "Dev tools and CLI for React Router",
|
|
5
5
|
"homepage": "https://reactrouter.com",
|
|
6
6
|
"bugs": {
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"react-refresh": "^0.14.0",
|
|
43
43
|
"semver": "^7.3.7",
|
|
44
44
|
"set-cookie-parser": "^2.6.0",
|
|
45
|
-
"@react-router/node": "0.0.0-experimental-
|
|
46
|
-
"@react-router/server-runtime": "0.0.0-experimental-
|
|
47
|
-
"react-router": "0.0.0-experimental-
|
|
45
|
+
"@react-router/node": "0.0.0-experimental-cb25a21e1",
|
|
46
|
+
"@react-router/server-runtime": "0.0.0-experimental-cb25a21e1",
|
|
47
|
+
"react-router": "0.0.0-experimental-cb25a21e1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/babel__core": "^7.20.5",
|
|
@@ -69,14 +69,14 @@
|
|
|
69
69
|
"tiny-invariant": "^1.2.0",
|
|
70
70
|
"vite": "^5.1.0",
|
|
71
71
|
"wrangler": "^3.28.2",
|
|
72
|
-
"@react-router/serve": "0.0.0-experimental-
|
|
72
|
+
"@react-router/serve": "0.0.0-experimental-cb25a21e1"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"typescript": "^5.1.0",
|
|
76
76
|
"vite": "^5.1.0",
|
|
77
77
|
"wrangler": "^3.28.2",
|
|
78
|
-
"react-router": "^0.0.0-experimental-
|
|
79
|
-
"@react-router/serve": "^0.0.0-experimental-
|
|
78
|
+
"react-router": "^0.0.0-experimental-cb25a21e1",
|
|
79
|
+
"@react-router/serve": "^0.0.0-experimental-cb25a21e1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"@react-router/serve": {
|