@react-router/dev 0.0.0-experimental-4ed3f5da9 → 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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
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 && (ctx === null || ctx === void 0 ? void 0 : ctx.reactRouterConfig.future.unstable_singleFetch) !== true ? `entry.server.spa.tsx` : `entry.server.${serverRuntime}.tsx`);
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;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli/run.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
3
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
package/dist/colors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,20 +1,73 @@
1
- import type { EntryContext } from "@react-router/node";
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 * as React from "react";
4
- import { renderToString } from "react-dom/server";
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
- let html = renderToString(
13
- <RemixServer context={remixContext} url={request.url} />
17
+ return handleBotRequest(
18
+ request,
19
+ responseStatusCode,
20
+ responseHeaders,
21
+ remixContext
14
22
  );
15
- html = "<!DOCTYPE html>\n" + html;
16
- return new Response(html, {
17
- headers: { "Content-Type": "text/html" },
18
- status: responseStatusCode,
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
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
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: {
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -187,8 +187,7 @@ async function resolveReactRouterConfig({
187
187
  let future = {
188
188
  v3_fetcherPersist: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_fetcherPersist) === true,
189
189
  v3_relativeSplatPath: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_relativeSplatPath) === true,
190
- v3_throwAbortReason: (userFuture === null || userFuture === void 0 ? void 0 : userFuture.v3_throwAbortReason) === true,
191
- 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
192
191
  };
193
192
  let reactRouterConfig = deepFreeze({
194
193
  appDirectory,
@@ -228,7 +227,7 @@ async function resolveEntryFiles({
228
227
  let entryClientFile = userEntryClientFile || "entry.client.tsx";
229
228
  let pkgJson = await PackageJson__default["default"].load(rootDirectory);
230
229
  let deps = pkgJson.content.dependencies ?? {};
231
- if (!reactRouterConfig.ssr && (future === null || future === void 0 ? void 0 : future.unstable_singleFetch) !== true) {
230
+ if (!reactRouterConfig.ssr) {
232
231
  // This is a super-simple default since we don't need streaming in SPA Mode.
233
232
  // We can include this in a remix-spa template, but right now `npx remix reveal`
234
233
  // will still expose the streaming template since that command doesn't have
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/invariant.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite/dev.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -108,9 +108,11 @@ async function loadPluginContext({
108
108
  }
109
109
  const SERVER_ONLY_ROUTE_EXPORTS = ["loader", "action", "headers"];
110
110
  const CLIENT_ROUTE_EXPORTS = ["clientAction", "clientLoader", "default", "ErrorBoundary", "handle", "HydrateFallback", "Layout", "links", "meta", "shouldRevalidate"];
111
- // The "=1" suffix ensures client route requests can be processed before hitting
112
- // the Vite plugin since "?client-route" can be serialized as "?client-route="
113
- const CLIENT_ROUTE_QUERY_STRING = "?client-route=1";
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";
114
116
  let serverBuildId = vmod.id("server-build");
115
117
  let serverManifestId = vmod.id("server-manifest");
116
118
  let browserManifestId = vmod.id("browser-manifest");
@@ -135,13 +137,10 @@ const getHash = (source, maxLength) => {
135
137
  let hash = node_crypto.createHash("sha256").update(source).digest("hex");
136
138
  return typeof maxLength === "number" ? hash.slice(0, maxLength) : hash;
137
139
  };
138
- const isClientRoute = id => {
139
- return id.endsWith(CLIENT_ROUTE_QUERY_STRING);
140
- };
141
140
  const resolveChunk = (ctx, viteManifest, absoluteFilePath) => {
142
141
  let vite = importViteEsmSync.importViteEsmSync();
143
142
  let rootRelativeFilePath = vite.normalizePath(path__namespace.relative(ctx.rootDirectory, absoluteFilePath));
144
- let entryChunk = viteManifest[rootRelativeFilePath + CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
143
+ let entryChunk = viteManifest[rootRelativeFilePath + BUILD_CLIENT_ROUTE_QUERY_STRING] ?? viteManifest[rootRelativeFilePath];
145
144
  if (!entryChunk) {
146
145
  let knownManifestKeys = Object.keys(viteManifest).map(key => '"' + key + '"').join(", ");
147
146
  throw new Error(`No manifest entry found for "${rootRelativeFilePath}". Known manifest keys: ${knownManifestKeys}`);
@@ -433,7 +432,7 @@ const reactRouterVitePlugin = _config => {
433
432
  path: route.path,
434
433
  index: route.index,
435
434
  caseSensitive: route.caseSensitive,
436
- module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}${CLIENT_ROUTE_QUERY_STRING}`),
435
+ module: path__namespace.posix.join(ctx.reactRouterConfig.publicPath, `${resolveFileUrl.resolveFileUrl(ctx, resolveRelativeRouteFilePath(route, ctx.reactRouterConfig))}`),
437
436
  hasAction: sourceExports.includes("action"),
438
437
  hasLoader: sourceExports.includes("loader"),
439
438
  hasClientAction: sourceExports.includes("clientAction"),
@@ -539,7 +538,7 @@ const reactRouterVitePlugin = _config => {
539
538
  rollupOptions: {
540
539
  ...baseRollupOptions,
541
540
  preserveEntrySignatures: "exports-only",
542
- input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${CLIENT_ROUTE_QUERY_STRING}`)]
541
+ input: [ctx.entryClientFilePath, ...Object.values(ctx.reactRouterConfig.routes).map(route => `${path__namespace.resolve(ctx.reactRouterConfig.appDirectory, route.file)}${BUILD_CLIENT_ROUTE_QUERY_STRING}`)]
543
542
  }
544
543
  } : {
545
544
  // We move SSR-only assets to client assets. Note that the
@@ -636,8 +635,8 @@ const reactRouterVitePlugin = _config => {
636
635
  if (styles.isCssModulesFile(id)) {
637
636
  cssModulesManifest[id] = code;
638
637
  }
639
- if (isClientRoute(id)) {
640
- let routeModuleId = id.replace(CLIENT_ROUTE_QUERY_STRING, "");
638
+ if (id.endsWith(BUILD_CLIENT_ROUTE_QUERY_STRING)) {
639
+ let routeModuleId = id.replace(BUILD_CLIENT_ROUTE_QUERY_STRING, "");
641
640
  let sourceExports = await getRouteModuleExports(viteChildCompiler, ctx, routeModuleId);
642
641
  let routeFileName = path__namespace.basename(routeModuleId);
643
642
  let clientExports = sourceExports.filter(exportName => CLIENT_ROUTE_EXPORTS.includes(exportName)).join(", ");
@@ -914,11 +913,6 @@ const reactRouterVitePlugin = _config => {
914
913
  let isJSX = filepath.endsWith("x");
915
914
  let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime));
916
915
  if (!useFastRefresh) return;
917
- if (isClientRoute(id)) {
918
- return {
919
- code: addRefreshWrapper(ctx.reactRouterConfig, code, id)
920
- };
921
- }
922
916
  let result = await babel__default["default"].transformAsync(code, {
923
917
  babelrc: false,
924
918
  configFile: false,
@@ -987,7 +981,7 @@ function isEqualJson(v1, v2) {
987
981
  }
988
982
  function addRefreshWrapper(reactRouterConfig, code, id) {
989
983
  let route = getRoute(reactRouterConfig, id);
990
- let acceptExports = route || isClientRoute(id) ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
984
+ let acceptExports = route ? ["clientAction", "clientLoader", "handle", "meta", "links", "shouldRevalidate"] : [];
991
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));
992
986
  }
993
987
  const REACT_REFRESH_HEADER = `
@@ -1069,12 +1063,8 @@ async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory
1069
1063
  let request = new Request(`http://localhost${reactRouterConfig.basename}`);
1070
1064
  let response = await handler(request);
1071
1065
  let html = await response.text();
1072
- if (response.status !== 200) {
1073
- throw new Error(`SPA Mode: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while generating the \`index.html\` file.\n${html}`);
1074
- }
1075
- if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
1076
- throw new Error("SPA Mode: Did you forget to include <Scripts/> in your `root.tsx` " + "`HydrateFallback` component? Your `index.html` file cannot hydrate " + "into a SPA without `<Scripts />`.");
1077
- }
1066
+ validatePrerenderedResponse(response, html, "SPA Mode", "/");
1067
+ validatePrerenderedHtml(html, "SPA Mode");
1078
1068
  // Write out the index.html file for the SPA
1079
1069
  await fse__namespace.writeFile(path__namespace.join(clientBuildDirectory, "index.html"), html);
1080
1070
  viteConfig.logger.info("SPA Mode: index.html has been written to your " + colors__default["default"].bold(path__namespace.relative(process.cwd(), clientBuildDirectory)) + " directory");
@@ -1097,18 +1087,16 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
1097
1087
  var _matchRoutes;
1098
1088
  let hasLoaders = (_matchRoutes = reactRouter.matchRoutes(routes, path)) === null || _matchRoutes === void 0 ? void 0 : _matchRoutes.some(m => m.route.loader);
1099
1089
  if (hasLoaders) {
1100
- await prerenderData(handler, reactRouterConfig.basename, path, clientBuildDirectory, viteConfig, requestInit);
1090
+ await prerenderData(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
1101
1091
  }
1102
- await prerenderRoute(handler, reactRouterConfig.basename, path, clientBuildDirectory, viteConfig, requestInit);
1092
+ await prerenderRoute(handler, path, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit);
1103
1093
  }
1104
- async function prerenderData(handler, basename, prerenderPath, clientBuildDirectory, viteConfig, requestInit) {
1105
- let normalizedPath = `${basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
1094
+ async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
1095
+ let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
1106
1096
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
1107
1097
  let response = await handler(request);
1108
1098
  let data = await response.text();
1109
- if (response.status !== 200) {
1110
- throw new Error(`Prerender: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while prerendering the \`${normalizedPath}\` ` + `path.\n${data}`);
1111
- }
1099
+ validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
1112
1100
  // Write out the .data file
1113
1101
  let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
1114
1102
  let outfile = path__namespace.join(outdir, normalizedPath.split("/").join(path__namespace.sep));
@@ -1117,16 +1105,14 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
1117
1105
  viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
1118
1106
  }
1119
1107
  }
1120
- async function prerenderRoute(handler, basename, prerenderPath, clientBuildDirectory, viteConfig, requestInit) {
1121
- let normalizedPath = `${basename}${prerenderPath}/`.replace(/\/\/+/g, "/");
1108
+ async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
1109
+ let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/");
1122
1110
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
1123
1111
  let response = await handler(request);
1124
1112
  let html = await response.text();
1125
- if (response.status !== 200) {
1126
- throw new Error(`Prerender: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while prerendering the \`${normalizedPath}\` ` + `path.\n${html}`);
1127
- }
1128
- if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
1129
- throw new Error("Prerender: Did you forget to include <Scripts/> in your `root.tsx` " + "`HydrateFallback` component? Your prerendered HTML files cannot " + "hydrate into a SPA without `<Scripts />`.");
1113
+ validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
1114
+ if (!reactRouterConfig.ssr) {
1115
+ validatePrerenderedHtml(html, "Prerender");
1130
1116
  }
1131
1117
  // Write out the HTML file
1132
1118
  let outdir = path__namespace.relative(process.cwd(), clientBuildDirectory);
@@ -1135,6 +1121,16 @@ async function prerenderRoute(handler, basename, prerenderPath, clientBuildDirec
1135
1121
  await fse__namespace.outputFile(outfile, html);
1136
1122
  viteConfig.logger.info(`Prerender: Generated ${colors__default["default"].bold(outfile)}`);
1137
1123
  }
1124
+ function validatePrerenderedResponse(response, html, prefix, path) {
1125
+ if (response.status !== 200) {
1126
+ throw new Error(`${prefix}: Received a ${response.status} status code from ` + `\`entry.server.tsx\` while prerendering the \`${path}\` ` + `path.\n${html}`);
1127
+ }
1128
+ }
1129
+ function validatePrerenderedHtml(html, prefix) {
1130
+ if (!html.includes("window.__remixContext =") || !html.includes("window.__remixRouteModules =")) {
1131
+ throw new Error(`${prefix}: Did you forget to include <Scripts/> in your root route? ` + "Your pre-rendered HTML files cannot hydrate without `<Scripts />`.");
1132
+ }
1133
+ }
1138
1134
  // Note: Duplicated from remix-server-runtime
1139
1135
  function groupRoutesByParentId(manifest) {
1140
1136
  let routes = {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite/vmod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-4ed3f5da9
2
+ * @react-router/dev v0.0.0-experimental-cb25a21e1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-4ed3f5da9",
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-4ed3f5da9",
46
- "@react-router/server-runtime": "0.0.0-experimental-4ed3f5da9",
47
- "react-router": "0.0.0-experimental-4ed3f5da9"
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-4ed3f5da9"
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/serve": "^0.0.0-experimental-4ed3f5da9",
79
- "react-router": "^0.0.0-experimental-4ed3f5da9"
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": {