@react-router/dev 0.0.0-experimental-66d5af831 → 0.0.0-experimental-1ab091485

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/CHANGELOG.md CHANGED
@@ -1,5 +1,91 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Generate a "SPA fallback" HTML file for scenarios where applications are prerendering the `/` route with `ssr:false` ([#12948](https://github.com/remix-run/react-router/pull/12948))
8
+
9
+ - If you specify `ssr:false` without a `prerender` config, this is considered "SPA Mode" and the generated `index.html` file will only render down to the root route and will be able to hydrate for any valid application path
10
+ - If you specify `ssr:false` with a `prerender` config but _do not_ include the `/` path (i.e., `prerender: ['/blog/post']`), then we still generate a "SPA Mode" `index.html` file that can hydrate for any path in the application
11
+ - However, previously if you specified `ssr:false` and included the `/` path in your `prerender` config, we would prerender the `/` route into `index.html` as a non-SPA page
12
+ - The generated HTML would include the root index route which prevented hydration for any other paths
13
+ - With this change, we now generate a "SPA Mode" file in `__spa-fallback.html` that will allow you to hydrate for any non-prerendered paths
14
+ - You can serve this file from your static file server for any paths that would otherwise 404 if you only want to pre-render _some_ routes in your `ssr:false` app and serve the others as a SPA
15
+ - `npx sirv-cli build/client --single __spa-fallback.html`
16
+
17
+ - Allow a `loader` in the root route in SPA mode because it can be called/server-rendered at build time ([#12948](https://github.com/remix-run/react-router/pull/12948))
18
+
19
+ - `Route.HydrateFallbackProps` now also receives `loaderData`
20
+ - This will be defined so long as the `HydrateFallback` is rendering while _children_ routes are loading
21
+ - This will be `undefined` if the `HydrateFallback` is rendering because the route has it's own hydrating `clientLoader`
22
+ - In SPA mode, this will allow you to render loader root data into the SPA `index.html`
23
+
24
+ - New type-safe `href` utility that guarantees links point to actual paths in your app ([#13012](https://github.com/remix-run/react-router/pull/13012))
25
+
26
+ ```tsx
27
+ import { href } from "react-router";
28
+
29
+ export default function Component() {
30
+ const link = href("/blog/:slug", { slug: "my-first-post" });
31
+ return (
32
+ <main>
33
+ <Link to={href("/products/:id", { id: "asdf" })} />
34
+ <NavLink to={href("/:lang?/about", { lang: "en" })} />
35
+ </main>
36
+ );
37
+ }
38
+ ```
39
+
40
+ ### Patch Changes
41
+
42
+ - Handle custom `envDir` in Vite config ([#12969](https://github.com/remix-run/react-router/pull/12969))
43
+
44
+ - Fix typegen for repeated params ([#13012](https://github.com/remix-run/react-router/pull/13012))
45
+
46
+ In React Router, path parameters are keyed by their name.
47
+ So for a path pattern like `/a/:id/b/:id?/c/:id`, the last `:id` will set the value for `id` in `useParams` and the `params` prop.
48
+ For example, `/a/1/b/2/c/3` will result in the value `{ id: 3 }` at runtime.
49
+
50
+ Previously, generated types for params incorrectly modeled repeated params with an array.
51
+ So `/a/1/b/2/c/3` generated a type like `{ id: [1,2,3] }`.
52
+
53
+ To be consistent with runtime behavior, the generated types now correctly model the "last one wins" semantics of path parameters.
54
+ So `/a/1/b/2/c/3` now generates a type like `{ id: 3 }`.
55
+
56
+ - Fix CLI parsing to allow argumentless `npx react-router` usage ([#12925](https://github.com/remix-run/react-router/pull/12925))
57
+
58
+ - Fix `ArgError: unknown or unexpected option: --version` when running `react-router --version` ([#13012](https://github.com/remix-run/react-router/pull/13012))
59
+
60
+ - Skip action-only resource routes when using `prerender:true` ([#13004](https://github.com/remix-run/react-router/pull/13004))
61
+
62
+ - Enhance invalid export detection when using `ssr:false` ([#12948](https://github.com/remix-run/react-router/pull/12948))
63
+
64
+ - `headers`/`action` are prohibited in all routes with `ssr:false` because there will be no runtime server on which to run them
65
+ - `loader` functions are more nuanced and depend on whether a given route is prerendered
66
+ - When using `ssr:false` without a `prerender` config, only the `root` route can have a `loader`
67
+ - This is "SPA mode" which generates a single `index.html` file with the root route `HydrateFallback` so it is capable of hydrating for any path in your application - therefore we can only call a root route `loader` at build time
68
+ - When using `ssr:false` with a `prerender` config, you can export a `loader` from routes matched by one of the `prerender` paths because those routes will be server rendered at build time
69
+ - Exporting a `loader` from a route that is never matched by a `prerender` path will throw a build time error because there will be no runtime server to ever run the loader
70
+
71
+ - Limit prerendered resource route `.data` files to only the target route ([#13004](https://github.com/remix-run/react-router/pull/13004))
72
+
73
+ - Add unstable support for splitting route modules in framework mode via `future.unstable_splitRouteModules` ([#11871](https://github.com/remix-run/react-router/pull/11871))
74
+
75
+ - Fix prerendering of binary files ([#13039](https://github.com/remix-run/react-router/pull/13039))
76
+
77
+ - Add `future.unstable_viteEnvironmentApi` flag to enable experimental Vite Environment API support ([#12936](https://github.com/remix-run/react-router/pull/12936))
78
+
79
+ - Disable Lazy Route Discovery for all `ssr:false` apps and not just "SPA Mode" because there is no runtime server to serve the search-param-configured `__manifest` requests ([#12894](https://github.com/remix-run/react-router/pull/12894))
80
+
81
+ - We previously only disabled this for "SPA Mode" which is `ssr:false` and no `prerender` config but we realized it should apply to all `ssr:false` apps, including those prerendering multiple pages
82
+ - In those `prerender` scenarios we would prerender the `/__manifest` file assuming the static file server would serve it but that makes some unneccesary assumptions about the static file server behaviors
83
+
84
+ - Updated dependencies:
85
+ - `react-router@7.2.0`
86
+ - `@react-router/node@7.2.0`
87
+ - `@react-router/serve@7.2.0`
88
+
3
89
  ## 7.1.5
4
90
 
5
91
  ### Patch Changes
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v0.0.0-experimental-66d5af831
3
+ * @react-router/dev v0.0.0-experimental-1ab091485
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -389,7 +389,6 @@ async function resolveConfig({
389
389
  );
390
390
  }
391
391
  let future = {
392
- turboV3: reactRouterUserConfig.future?.turboV3 ?? false,
393
392
  unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
394
393
  unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
395
394
  unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
@@ -945,7 +944,6 @@ var init_styles = __esm({
945
944
  path6 = __toESM(require("path"));
946
945
  import_react_router = require("react-router");
947
946
  init_resolve_file_url();
948
- init_vite();
949
947
  cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
950
948
  cssModulesRegExp = new RegExp(`\\.module${cssFileRegExp.source}`);
951
949
  }
@@ -1364,9 +1362,26 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
1364
1362
  rollupOptions: {
1365
1363
  input: (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? viteUserConfig.environments?.ssr?.build?.rollupOptions?.input : viteUserConfig.build?.rollupOptions?.input) ?? virtual.serverBuild.id
1366
1364
  }
1367
- }
1365
+ },
1366
+ optimizeDeps: ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && viteUserConfig.environments?.ssr?.optimizeDeps?.noDiscovery === false ? {
1367
+ entries: [
1368
+ vite2.normalizePath(ctx.entryServerFilePath),
1369
+ ...Object.values(ctx.reactRouterConfig.routes).map(
1370
+ (route) => resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
1371
+ )
1372
+ ],
1373
+ include: [
1374
+ "react",
1375
+ "react/jsx-dev-runtime",
1376
+ "react-dom/server",
1377
+ "react-router"
1378
+ ]
1379
+ } : void 0
1368
1380
  });
1369
1381
  }
1382
+ if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && viteCommand === "serve") {
1383
+ environmentOptionsResolvers[CSS_DEV_HELPER_ENVIRONMENT_NAME] = () => ({});
1384
+ }
1370
1385
  return environmentOptionsResolvers;
1371
1386
  }
1372
1387
  function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
@@ -1381,7 +1396,7 @@ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1381
1396
  function isNonNullable(x) {
1382
1397
  return x != null;
1383
1398
  }
1384
- var import_node_crypto, path7, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1399
+ var import_node_crypto, path7, url, fse, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors4, import_kebabCase, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, CSS_DEV_HELPER_ENVIRONMENT_NAME, virtualHmrRuntime, virtualInjectHmrRuntime, resolveRelativeRouteFilePath, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1385
1400
  var init_plugin = __esm({
1386
1401
  "vite/plugin.ts"() {
1387
1402
  "use strict";
@@ -1411,8 +1426,15 @@ var init_plugin = __esm({
1411
1426
  init_with_props();
1412
1427
  BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1413
1428
  SSR_BUNDLE_PREFIX = "ssrBundle_";
1429
+ CSS_DEV_HELPER_ENVIRONMENT_NAME = "__react_router_css_dev_helper__";
1414
1430
  virtualHmrRuntime = create("hmr-runtime");
1415
1431
  virtualInjectHmrRuntime = create("inject-hmr-runtime");
1432
+ resolveRelativeRouteFilePath = (route, reactRouterConfig) => {
1433
+ let vite2 = getVite();
1434
+ let file = route.file;
1435
+ let fullPath = path7.resolve(reactRouterConfig.appDirectory, file);
1436
+ return vite2.normalizePath(fullPath);
1437
+ };
1416
1438
  virtual = {
1417
1439
  serverBuild: create("server-build"),
1418
1440
  serverManifest: create("server-manifest"),
package/dist/config.d.ts CHANGED
@@ -37,7 +37,6 @@ type ServerBundlesBuildManifest = BaseBuildManifest & {
37
37
  };
38
38
  type ServerModuleFormat = "esm" | "cjs";
39
39
  interface FutureConfig {
40
- turboV3: boolean;
41
40
  unstable_optimizeDeps: boolean;
42
41
  /**
43
42
  * Automatically split route modules into multiple chunks when possible.
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-66d5af831
2
+ * @react-router/dev v0.0.0-experimental-1ab091485
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-66d5af831
2
+ * @react-router/dev v0.0.0-experimental-1ab091485
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-66d5af831
2
+ * @react-router/dev v0.0.0-experimental-1ab091485
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -64,7 +64,18 @@ function invariant(value, message) {
64
64
  }
65
65
 
66
66
  // vite/node-adapter.ts
67
- function fromNodeHeaders(nodeHeaders) {
67
+ function fromNodeHeaders(nodeReq) {
68
+ let nodeHeaders = nodeReq.headers;
69
+ if (nodeReq.httpVersionMajor >= 2) {
70
+ nodeHeaders = { ...nodeHeaders };
71
+ if (nodeHeaders[":authority"]) {
72
+ nodeHeaders.host = nodeHeaders[":authority"];
73
+ }
74
+ delete nodeHeaders[":authority"];
75
+ delete nodeHeaders[":method"];
76
+ delete nodeHeaders[":path"];
77
+ delete nodeHeaders[":scheme"];
78
+ }
68
79
  let headers = new Headers();
69
80
  for (let [key, values] of Object.entries(nodeHeaders)) {
70
81
  if (values) {
@@ -89,7 +100,7 @@ function fromNodeRequest(nodeReq, nodeRes) {
89
100
  let controller = new AbortController();
90
101
  let init = {
91
102
  method: nodeReq.method,
92
- headers: fromNodeHeaders(nodeReq.headers),
103
+ headers: fromNodeHeaders(nodeReq),
93
104
  signal: controller.signal
94
105
  };
95
106
  nodeRes.on("finish", () => controller = null);
@@ -477,7 +488,6 @@ async function resolveConfig({
477
488
  );
478
489
  }
479
490
  let future = {
480
- turboV3: reactRouterUserConfig.future?.turboV3 ?? false,
481
491
  unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
482
492
  unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
483
493
  unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
package/dist/vite.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-66d5af831
2
+ * @react-router/dev v0.0.0-experimental-1ab091485
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -453,7 +453,6 @@ async function resolveConfig({
453
453
  );
454
454
  }
455
455
  let future = {
456
- turboV3: reactRouterUserConfig.future?.turboV3 ?? false,
457
456
  unstable_optimizeDeps: reactRouterUserConfig.future?.unstable_optimizeDeps ?? false,
458
457
  unstable_splitRouteModules: reactRouterUserConfig.future?.unstable_splitRouteModules ?? false,
459
458
  unstable_viteEnvironmentApi: reactRouterUserConfig.future?.unstable_viteEnvironmentApi ?? false
@@ -868,7 +867,18 @@ var import_node_events = require("events");
868
867
  var import_node_stream = require("stream");
869
868
  var import_set_cookie_parser = require("set-cookie-parser");
870
869
  var import_node = require("@react-router/node");
871
- function fromNodeHeaders(nodeHeaders) {
870
+ function fromNodeHeaders(nodeReq) {
871
+ let nodeHeaders = nodeReq.headers;
872
+ if (nodeReq.httpVersionMajor >= 2) {
873
+ nodeHeaders = { ...nodeHeaders };
874
+ if (nodeHeaders[":authority"]) {
875
+ nodeHeaders.host = nodeHeaders[":authority"];
876
+ }
877
+ delete nodeHeaders[":authority"];
878
+ delete nodeHeaders[":method"];
879
+ delete nodeHeaders[":path"];
880
+ delete nodeHeaders[":scheme"];
881
+ }
872
882
  let headers = new Headers();
873
883
  for (let [key, values] of Object.entries(nodeHeaders)) {
874
884
  if (values) {
@@ -893,7 +903,7 @@ function fromNodeRequest(nodeReq, nodeRes) {
893
903
  let controller = new AbortController();
894
904
  let init = {
895
905
  method: nodeReq.method,
896
- headers: fromNodeHeaders(nodeReq.headers),
906
+ headers: fromNodeHeaders(nodeReq),
897
907
  signal: controller.signal
898
908
  };
899
909
  nodeRes.on("finish", () => controller = null);
@@ -964,15 +974,12 @@ var isCssUrlWithoutSideEffects = (url2) => {
964
974
  }
965
975
  return false;
966
976
  };
967
- var injectQuery = (url2, query) => url2.includes("?") ? url2.replace("?", `?${query}&`) : `${url2}?${query}`;
968
977
  var getStylesForFiles = async ({
969
978
  viteDevServer,
970
979
  rootDirectory,
971
- cssModulesManifest,
980
+ loadCssContents,
972
981
  files
973
982
  }) => {
974
- let vite2 = getVite();
975
- let viteMajor = parseInt(vite2.version.split(".")[0], 10);
976
983
  let styles = {};
977
984
  let deps = /* @__PURE__ */ new Set();
978
985
  try {
@@ -1001,21 +1008,9 @@ var getStylesForFiles = async ({
1001
1008
  for (let dep of deps) {
1002
1009
  if (dep.file && isCssFile(dep.file) && !isCssUrlWithoutSideEffects(dep.url)) {
1003
1010
  try {
1004
- let css = isCssModulesFile(dep.file) ? cssModulesManifest[dep.file] : (await viteDevServer.ssrLoadModule(
1005
- // We need the ?inline query in Vite v6 when loading CSS in SSR
1006
- // since it does not expose the default export for CSS in a
1007
- // server environment. This is to align with non-SSR
1008
- // environments. For backwards compatibility with v5 we keep
1009
- // using the URL without ?inline query because the HMR code was
1010
- // relying on the implicit SSR-client module graph relationship.
1011
- viteMajor >= 6 ? injectQuery(dep.url, "inline") : dep.url
1012
- )).default;
1013
- if (css === void 0) {
1014
- throw new Error();
1015
- }
1016
- styles[dep.url] = css;
1011
+ styles[dep.url] = await loadCssContents(viteDevServer, dep);
1017
1012
  } catch {
1018
- console.warn(`Could not load ${dep.file}`);
1013
+ console.warn(`Failed to load CSS for ${dep.file}`);
1019
1014
  }
1020
1015
  }
1021
1016
  }
@@ -1074,7 +1069,7 @@ var getStylesForUrl = async ({
1074
1069
  rootDirectory,
1075
1070
  reactRouterConfig,
1076
1071
  entryClientFilePath,
1077
- cssModulesManifest,
1072
+ loadCssContents,
1078
1073
  build,
1079
1074
  url: url2
1080
1075
  }) => {
@@ -1089,7 +1084,7 @@ var getStylesForUrl = async ({
1089
1084
  let styles = await getStylesForFiles({
1090
1085
  viteDevServer,
1091
1086
  rootDirectory,
1092
- cssModulesManifest,
1087
+ loadCssContents,
1093
1088
  files: [
1094
1089
  // Always include the client entry file when crawling the module graph for CSS
1095
1090
  path5.relative(rootDirectory, entryClientFilePath),
@@ -1968,6 +1963,7 @@ var CLIENT_ROUTE_EXPORTS = [
1968
1963
  ];
1969
1964
  var BUILD_CLIENT_ROUTE_QUERY_STRING = "?__react-router-build-client-route";
1970
1965
  var SSR_BUNDLE_PREFIX = "ssrBundle_";
1966
+ var CSS_DEV_HELPER_ENVIRONMENT_NAME = "__react_router_css_dev_helper__";
1971
1967
  function isSeverBundleEnvironmentName(name) {
1972
1968
  return name.startsWith(SSR_BUNDLE_PREFIX);
1973
1969
  }
@@ -2159,6 +2155,7 @@ var getServerBuildDirectory = (ctx, { serverBundleId } = {}) => path6.join(
2159
2155
  ...serverBundleId ? [serverBundleId] : []
2160
2156
  );
2161
2157
  var getClientBuildDirectory = (reactRouterConfig) => path6.join(reactRouterConfig.buildDirectory, "client");
2158
+ var injectQuery = (url2, query) => url2.includes("?") ? url2.replace("?", `?${query}&`) : `${url2}?${query}`;
2162
2159
  var defaultEntriesDir = path6.resolve(
2163
2160
  path6.dirname(require.resolve("@react-router/dev/package.json")),
2164
2161
  "dist",
@@ -2415,6 +2412,7 @@ var reactRouterVitePlugin = () => {
2415
2412
  reactRouterServerManifest
2416
2413
  };
2417
2414
  };
2415
+ let currentReactRouterManifestForDev = null;
2418
2416
  let getReactRouterManifestForDev = async () => {
2419
2417
  let routes = {};
2420
2418
  let routeManifestExports = await getRouteManifestModuleExports(
@@ -2471,7 +2469,7 @@ var reactRouterVitePlugin = () => {
2471
2469
  imports: []
2472
2470
  };
2473
2471
  }
2474
- return {
2472
+ let reactRouterManifestForDev = {
2475
2473
  version: String(Math.random()),
2476
2474
  url: combineURLs(ctx.publicPath, virtual.browserManifest.url),
2477
2475
  hmr: {
@@ -2486,6 +2484,42 @@ var reactRouterVitePlugin = () => {
2486
2484
  },
2487
2485
  routes
2488
2486
  };
2487
+ currentReactRouterManifestForDev = reactRouterManifestForDev;
2488
+ return reactRouterManifestForDev;
2489
+ };
2490
+ const loadCssContents = async (viteDevServer, dep) => {
2491
+ invariant(
2492
+ viteCommand === "serve",
2493
+ "loadCssContents is only available in dev mode"
2494
+ );
2495
+ if (dep.file && isCssModulesFile(dep.file)) {
2496
+ return cssModulesManifest[dep.file];
2497
+ }
2498
+ const vite2 = getVite();
2499
+ const viteMajor = parseInt(vite2.version.split(".")[0], 10);
2500
+ const url2 = viteMajor >= 6 ? (
2501
+ // We need the ?inline query in Vite v6 when loading CSS in SSR
2502
+ // since it does not expose the default export for CSS in a
2503
+ // server environment. This is to align with non-SSR
2504
+ // environments. For backwards compatibility with v5 we keep
2505
+ // using the URL without ?inline query because the HMR code was
2506
+ // relying on the implicit SSR-client module graph relationship.
2507
+ injectQuery(dep.url, "inline")
2508
+ ) : dep.url;
2509
+ let cssMod;
2510
+ if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi) {
2511
+ const cssDevHelperEnvironment = viteDevServer.environments[CSS_DEV_HELPER_ENVIRONMENT_NAME];
2512
+ invariant(cssDevHelperEnvironment, "Missing CSS dev helper environment");
2513
+ invariant(vite2.isRunnableDevEnvironment(cssDevHelperEnvironment));
2514
+ cssMod = await cssDevHelperEnvironment.runner.import(url2);
2515
+ } else {
2516
+ cssMod = await viteDevServer.ssrLoadModule(url2);
2517
+ }
2518
+ invariant(
2519
+ typeof cssMod === "object" && cssMod !== null && "default" in cssMod && typeof cssMod.default === "string",
2520
+ `Failed to load CSS for ${dep.file ?? dep.url}`
2521
+ );
2522
+ return cssMod.default;
2489
2523
  };
2490
2524
  return [
2491
2525
  {
@@ -2664,6 +2698,8 @@ var reactRouterVitePlugin = () => {
2664
2698
  }
2665
2699
  viteChildCompiler = await vite2.createServer({
2666
2700
  ...viteUserConfig,
2701
+ // Ensure child compiler cannot overwrite the default cache directory
2702
+ cacheDir: "node_modules/.vite-child-compiler",
2667
2703
  mode: viteConfig.mode,
2668
2704
  server: {
2669
2705
  watch: viteConfig.command === "build" ? null : void 0,
@@ -2675,7 +2711,23 @@ var reactRouterVitePlugin = () => {
2675
2711
  plugins: [
2676
2712
  ...(childCompilerConfigFile.config.plugins ?? []).flat().filter(
2677
2713
  (plugin2) => typeof plugin2 === "object" && plugin2 !== null && "name" in plugin2 && plugin2.name !== "react-router" && plugin2.name !== "react-router:route-exports" && plugin2.name !== "react-router:hmr-updates"
2678
- )
2714
+ ),
2715
+ {
2716
+ name: "react-router:override-optimize-deps",
2717
+ config(userConfig) {
2718
+ if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && userConfig.environments) {
2719
+ for (const environmentName of Object.keys(
2720
+ userConfig.environments
2721
+ )) {
2722
+ userConfig.environments[environmentName].optimizeDeps = {
2723
+ noDiscovery: true
2724
+ };
2725
+ }
2726
+ } else {
2727
+ userConfig.optimizeDeps = { noDiscovery: true };
2728
+ }
2729
+ }
2730
+ }
2679
2731
  ]
2680
2732
  });
2681
2733
  await viteChildCompiler.pluginContainer.buildStart({});
@@ -2712,7 +2764,7 @@ var reactRouterVitePlugin = () => {
2712
2764
  entryClientFilePath: ctx.entryClientFilePath,
2713
2765
  reactRouterConfig: ctx.reactRouterConfig,
2714
2766
  viteDevServer,
2715
- cssModulesManifest,
2767
+ loadCssContents,
2716
2768
  build,
2717
2769
  url: url2
2718
2770
  });
@@ -2761,9 +2813,21 @@ var reactRouterVitePlugin = () => {
2761
2813
  if (!viteDevServer.config.server.middlewareMode) {
2762
2814
  viteDevServer.middlewares.use(async (req, res, next) => {
2763
2815
  try {
2764
- let build = await viteDevServer.ssrLoadModule(
2765
- virtual.serverBuild.id
2766
- );
2816
+ let build;
2817
+ if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi) {
2818
+ let vite2 = getVite();
2819
+ let ssrEnvironment = viteDevServer.environments.ssr;
2820
+ if (!vite2.isRunnableDevEnvironment(ssrEnvironment)) {
2821
+ return;
2822
+ }
2823
+ build = await ssrEnvironment.runner.import(
2824
+ virtual.serverBuild.id
2825
+ );
2826
+ } else {
2827
+ build = await viteDevServer.ssrLoadModule(
2828
+ virtual.serverBuild.id
2829
+ );
2830
+ }
2767
2831
  let handler = (0, import_react_router2.createRequestHandler)(build, "development");
2768
2832
  let nodeHandler = async (nodeReq, nodeRes) => {
2769
2833
  let req2 = fromNodeRequest(nodeReq, nodeRes);
@@ -3232,8 +3296,7 @@ var reactRouterVitePlugin = () => {
3232
3296
  let route = getRoute(ctx.reactRouterConfig, file);
3233
3297
  let hmrEventData = { route: null };
3234
3298
  if (route) {
3235
- let serverManifest = (await server.ssrLoadModule(virtual.serverManifest.id)).default;
3236
- let oldRouteMetadata = serverManifest.routes[route.id];
3299
+ let oldRouteMetadata = currentReactRouterManifestForDev?.routes[route.id];
3237
3300
  let newRouteMetadata = await getRouteMetadata(
3238
3301
  cache,
3239
3302
  ctx,
@@ -3633,17 +3696,17 @@ async function prerenderResourceRoute(handler, prerenderPath, clientBuildDirecto
3633
3696
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/").replace(/\/$/g, "");
3634
3697
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3635
3698
  let response = await handler(request);
3636
- let text = await response.text();
3699
+ let content = Buffer.from(await response.arrayBuffer());
3637
3700
  if (response.status !== 200) {
3638
3701
  throw new Error(
3639
3702
  `Prerender (resource): Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3640
- ${text}`
3703
+ ${content.toString("utf8")}`
3641
3704
  );
3642
3705
  }
3643
3706
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3644
3707
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
3645
3708
  await fse.ensureDir(path6.dirname(outfile));
3646
- await fse.outputFile(outfile, text);
3709
+ await fse.outputFile(outfile, content);
3647
3710
  viteConfig.logger.info(
3648
3711
  `Prerender (resource): ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3649
3712
  );
@@ -3742,13 +3805,24 @@ async function validateSsrFalsePrerenderExports(viteConfig, ctx, manifest, viteC
3742
3805
  if (exports2.includes("action")) invalidApis.push("action");
3743
3806
  if (invalidApis.length > 0) {
3744
3807
  errors.push(
3745
- `Prerender: ${invalidApis.length} invalid route export(s) in \`${route.id}\` when prerendering with \`ssr:false\`: ${invalidApis.join(", ")}. See https://reactrouter.com/how-to/pre-rendering for more information.`
3808
+ `Prerender: ${invalidApis.length} invalid route export(s) in \`${route.id}\` when pre-rendering with \`ssr:false\`: ${invalidApis.map((a) => `\`${a}\``).join(", ")}. See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information.`
3746
3809
  );
3747
3810
  }
3748
- if (exports2.includes("loader") && !prerenderedRoutes.has(routeId)) {
3749
- errors.push(
3750
- `Prerender: 1 invalid route export in \`${route.id}\` when using \`ssr:false\` with \`prerender\` because the route is never prerendered so the loader will never be called. See https://reactrouter.com/how-to/pre-rendering for more information.`
3751
- );
3811
+ if (!prerenderedRoutes.has(routeId)) {
3812
+ if (exports2.includes("loader")) {
3813
+ errors.push(
3814
+ `Prerender: 1 invalid route export in \`${route.id}\` when pre-rendering with \`ssr:false\`: \`loader\`. See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information.`
3815
+ );
3816
+ }
3817
+ let parentRoute = route.parentId ? manifest.routes[route.parentId] : null;
3818
+ while (parentRoute && parentRoute.id !== "root") {
3819
+ if (parentRoute.hasLoader && !parentRoute.hasClientLoader) {
3820
+ errors.push(
3821
+ `Prerender: 1 invalid route export in \`${parentRoute.id}\` when pre-rendering with \`ssr:false\`: \`loader\`. See https://reactrouter.com/how-to/pre-rendering#invalid-exports for more information.`
3822
+ );
3823
+ }
3824
+ parentRoute = parentRoute.parentId && parentRoute.parentId !== "root" ? manifest.routes[parentRoute.parentId] : null;
3825
+ }
3752
3826
  }
3753
3827
  }
3754
3828
  if (errors.length > 0) {
@@ -4102,9 +4176,26 @@ async function getEnvironmentOptionsResolvers(ctx, buildManifest, viteCommand) {
4102
4176
  rollupOptions: {
4103
4177
  input: (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi ? viteUserConfig.environments?.ssr?.build?.rollupOptions?.input : viteUserConfig.build?.rollupOptions?.input) ?? virtual.serverBuild.id
4104
4178
  }
4105
- }
4179
+ },
4180
+ optimizeDeps: ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && viteUserConfig.environments?.ssr?.optimizeDeps?.noDiscovery === false ? {
4181
+ entries: [
4182
+ vite2.normalizePath(ctx.entryServerFilePath),
4183
+ ...Object.values(ctx.reactRouterConfig.routes).map(
4184
+ (route) => resolveRelativeRouteFilePath(route, ctx.reactRouterConfig)
4185
+ )
4186
+ ],
4187
+ include: [
4188
+ "react",
4189
+ "react/jsx-dev-runtime",
4190
+ "react-dom/server",
4191
+ "react-router"
4192
+ ]
4193
+ } : void 0
4106
4194
  });
4107
4195
  }
4196
+ if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi && viteCommand === "serve") {
4197
+ environmentOptionsResolvers[CSS_DEV_HELPER_ENVIRONMENT_NAME] = () => ({});
4198
+ }
4108
4199
  return environmentOptionsResolvers;
4109
4200
  }
4110
4201
  function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-66d5af831",
3
+ "version": "0.0.0-experimental-1ab091485",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -88,7 +88,7 @@
88
88
  "set-cookie-parser": "^2.6.0",
89
89
  "valibot": "^0.41.0",
90
90
  "vite-node": "3.0.0-beta.2",
91
- "@react-router/node": "0.0.0-experimental-66d5af831"
91
+ "@react-router/node": "0.0.0-experimental-1ab091485"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@types/babel__core": "^7.20.5",
@@ -106,7 +106,7 @@
106
106
  "@types/prettier": "^2.7.3",
107
107
  "@types/set-cookie-parser": "^2.4.1",
108
108
  "dotenv": "^16.0.0",
109
- "esbuild-register": "^3.3.2",
109
+ "esbuild-register": "^3.6.0",
110
110
  "execa": "5.1.1",
111
111
  "express": "^4.19.2",
112
112
  "fast-glob": "3.2.11",
@@ -117,15 +117,15 @@
117
117
  "vite": "^6.0.0",
118
118
  "wireit": "0.14.9",
119
119
  "wrangler": "^3.28.2",
120
- "react-router": "^0.0.0-experimental-66d5af831",
121
- "@react-router/serve": "0.0.0-experimental-66d5af831"
120
+ "@react-router/serve": "0.0.0-experimental-1ab091485",
121
+ "react-router": "^0.0.0-experimental-1ab091485"
122
122
  },
123
123
  "peerDependencies": {
124
124
  "typescript": "^5.1.0",
125
125
  "vite": "^5.1.0 || ^6.0.0",
126
126
  "wrangler": "^3.28.2",
127
- "@react-router/serve": "^0.0.0-experimental-66d5af831",
128
- "react-router": "^0.0.0-experimental-66d5af831"
127
+ "@react-router/serve": "^0.0.0-experimental-1ab091485",
128
+ "react-router": "^0.0.0-experimental-1ab091485"
129
129
  },
130
130
  "peerDependenciesMeta": {
131
131
  "@react-router/serve": {