@c0va23/react-router-dev 7.9.5-alpha.1 → 7.9.5-patch.1

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,7 +1,17 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.9.5
4
+
3
5
  ### Patch Changes
4
- - Copy `server.watch` config options from `vite.config.(ts|js)` file to child vite server in development mode.
6
+
7
+ - Introduce a `prerender.unstable_concurrency` option, to support running the prerendering concurrently, potentially speeding up the build. ([#14380](https://github.com/remix-run/react-router/pull/14380))
8
+ - Move RSCHydratedRouter and utils to `/dom` export. ([#14457](https://github.com/remix-run/react-router/pull/14457))
9
+ - Ensure route navigation doesn't remove CSS `link` elements used by dynamic imports ([#14463](https://github.com/remix-run/react-router/pull/14463))
10
+ - Typegen: only register route module types for routes within the app directory ([#14439](https://github.com/remix-run/react-router/pull/14439))
11
+ - Updated dependencies:
12
+ - `react-router@7.9.5`
13
+ - `@react-router/node@7.9.5`
14
+ - `@react-router/serve@7.9.5`
5
15
 
6
16
  ## 7.9.4
7
17
 
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @c0va23/react-router-dev v7.9.4-patch.1
3
+ * @react-router/dev v7.9.5
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -390,11 +390,20 @@ async function resolveConfig({
390
390
  if (!ssr && serverBundles) {
391
391
  serverBundles = void 0;
392
392
  }
393
- let isValidPrerenderConfig = prerender == null || typeof prerender === "boolean" || Array.isArray(prerender) || typeof prerender === "function";
394
- if (!isValidPrerenderConfig) {
395
- return err(
396
- "The `prerender` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths"
397
- );
393
+ if (prerender) {
394
+ let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p);
395
+ let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths);
396
+ if (!isValidPrerenderConfig) {
397
+ return err(
398
+ "The `prerender`/`prerender.paths` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths."
399
+ );
400
+ }
401
+ let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0;
402
+ if (!isValidConcurrencyConfig) {
403
+ return err(
404
+ "The `prerender.unstable_concurrency` config must be a positive integer if specified."
405
+ );
406
+ }
398
407
  }
399
408
  let routeDiscovery;
400
409
  if (userRouteDiscovery == null) {
@@ -1061,13 +1070,13 @@ function routeModulesType(ctx) {
1061
1070
  (route) => t2.tsPropertySignature(
1062
1071
  t2.stringLiteral(route.id),
1063
1072
  t2.tsTypeAnnotation(
1064
- t2.tsTypeQuery(
1073
+ isInAppDirectory(ctx, route.file) ? t2.tsTypeQuery(
1065
1074
  t2.tsImportType(
1066
1075
  t2.stringLiteral(
1067
1076
  `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1068
1077
  )
1069
1078
  )
1070
- )
1079
+ ) : t2.tsUnknownKeyword()
1071
1080
  )
1072
1081
  )
1073
1082
  )
@@ -12,8 +12,8 @@ import {
12
12
  unstable_createCallServer as createCallServer,
13
13
  unstable_getRSCStream as getRSCStream,
14
14
  unstable_RSCHydratedRouter as RSCHydratedRouter,
15
- } from "react-router";
16
- import type { unstable_RSCPayload as RSCPayload } from "react-router";
15
+ type unstable_RSCPayload as RSCPayload,
16
+ } from "react-router/dom";
17
17
 
18
18
  setServerCallback(
19
19
  createCallServer({
@@ -18,6 +18,14 @@ export default function handleRequest(
18
18
  // If you have middleware enabled:
19
19
  // loadContext: RouterContextProvider
20
20
  ) {
21
+ // https://httpwg.org/specs/rfc9110.html#HEAD
22
+ if (request.method.toUpperCase() === "HEAD") {
23
+ return new Response(null, {
24
+ status: responseStatusCode,
25
+ headers: responseHeaders,
26
+ });
27
+ }
28
+
21
29
  return new Promise((resolve, reject) => {
22
30
  let shellRendered = false;
23
31
  let userAgent = request.headers.get("user-agent");
package/dist/config.d.ts CHANGED
@@ -58,6 +58,9 @@ type BuildEndHook = (args: {
58
58
  reactRouterConfig: ResolvedReactRouterConfig;
59
59
  viteConfig: Vite.ResolvedConfig;
60
60
  }) => void | Promise<void>;
61
+ type PrerenderPaths = boolean | Array<string> | ((args: {
62
+ getStaticPaths: () => string[];
63
+ }) => Array<string> | Promise<Array<string>>);
61
64
  /**
62
65
  * Config to be exported via the default export from `react-router.config.ts`.
63
66
  */
@@ -93,10 +96,17 @@ type ReactRouterConfig = {
93
96
  /**
94
97
  * An array of URLs to prerender to HTML files at build time. Can also be a
95
98
  * function returning an array to dynamically generate URLs.
96
- */
97
- prerender?: boolean | Array<string> | ((args: {
98
- getStaticPaths: () => string[];
99
- }) => Array<string> | Promise<Array<string>>);
99
+ *
100
+ * `unstable_concurrency` defaults to 1, which means "no concurrency" - fully serial execution.
101
+ * Setting it to a value more than 1 enables concurrent prerendering.
102
+ * Setting it to a value higher than one can increase the speed of the build,
103
+ * but may consume more resources, and send more concurrent requests to the
104
+ * server/CMS.
105
+ */
106
+ prerender?: PrerenderPaths | {
107
+ paths: PrerenderPaths;
108
+ unstable_concurrency?: number;
109
+ };
100
110
  /**
101
111
  * An array of React Router plugin config presets to ease integration with
102
112
  * other platforms and tools.
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @c0va23/react-router-dev v7.9.4-patch.1
2
+ * @react-router/dev v7.9.5
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @c0va23/react-router-dev v7.9.4-patch.1
2
+ * @react-router/dev v7.9.5
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @c0va23/react-router-dev v7.9.4-patch.1
2
+ * @react-router/dev v7.9.5
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -420,11 +420,20 @@ async function resolveConfig({
420
420
  if (!ssr && serverBundles) {
421
421
  serverBundles = void 0;
422
422
  }
423
- let isValidPrerenderConfig = prerender == null || typeof prerender === "boolean" || Array.isArray(prerender) || typeof prerender === "function";
424
- if (!isValidPrerenderConfig) {
425
- return err(
426
- "The `prerender` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths"
427
- );
423
+ if (prerender) {
424
+ let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p);
425
+ let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths);
426
+ if (!isValidPrerenderConfig) {
427
+ return err(
428
+ "The `prerender`/`prerender.paths` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths."
429
+ );
430
+ }
431
+ let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0;
432
+ if (!isValidConcurrencyConfig) {
433
+ return err(
434
+ "The `prerender.unstable_concurrency` config must be a positive integer if specified."
435
+ );
436
+ }
428
437
  }
429
438
  let routeDiscovery;
430
439
  if (userRouteDiscovery == null) {
package/dist/vite.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @c0va23/react-router-dev v7.9.4-patch.1
2
+ * @react-router/dev v7.9.5
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -447,11 +447,20 @@ async function resolveConfig({
447
447
  if (!ssr && serverBundles) {
448
448
  serverBundles = void 0;
449
449
  }
450
- let isValidPrerenderConfig = prerender == null || typeof prerender === "boolean" || Array.isArray(prerender) || typeof prerender === "function";
451
- if (!isValidPrerenderConfig) {
452
- return err(
453
- "The `prerender` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths"
454
- );
450
+ if (prerender) {
451
+ let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p);
452
+ let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths);
453
+ if (!isValidPrerenderConfig) {
454
+ return err(
455
+ "The `prerender`/`prerender.paths` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths."
456
+ );
457
+ }
458
+ let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0;
459
+ if (!isValidConcurrencyConfig) {
460
+ return err(
461
+ "The `prerender.unstable_concurrency` config must be a positive integer if specified."
462
+ );
463
+ }
455
464
  }
456
465
  let routeDiscovery;
457
466
  if (userRouteDiscovery == null) {
@@ -1049,13 +1058,13 @@ function routeModulesType(ctx) {
1049
1058
  (route) => t2.tsPropertySignature(
1050
1059
  t2.stringLiteral(route.id),
1051
1060
  t2.tsTypeAnnotation(
1052
- t2.tsTypeQuery(
1061
+ isInAppDirectory(ctx, route.file) ? t2.tsTypeQuery(
1053
1062
  t2.tsImportType(
1054
1063
  t2.stringLiteral(
1055
1064
  `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1056
1065
  )
1057
1066
  )
1058
- )
1067
+ ) : t2.tsUnknownKeyword()
1059
1068
  )
1060
1069
  )
1061
1070
  )
@@ -2549,7 +2558,7 @@ var getClientEntryChunk = (ctx, viteManifest) => {
2549
2558
  invariant(chunk, `Chunk not found: ${filePath}`);
2550
2559
  return chunk;
2551
2560
  };
2552
- var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, entryFilePath, route) => {
2561
+ var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, allDynamicCssFiles, entryFilePath, route) => {
2553
2562
  let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
2554
2563
  invariant(entryChunk, `Chunk not found: ${entryFilePath}`);
2555
2564
  let isRootRoute = Boolean(route && route.parentId === void 0);
@@ -2582,7 +2591,10 @@ var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, entryFil
2582
2591
  // in the manifest that isn't tied to any route file. If we want to render these
2583
2592
  // styles correctly, we need to include them in the root route.
2584
2593
  isRootRoute ? getCssCodeSplitDisabledFile(ctx, viteConfig, viteManifest) : null,
2585
- chunks.flatMap((e) => e.css ?? []).map((href) => `${ctx.publicPath}${href}`)
2594
+ chunks.flatMap((e) => e.css ?? []).map((href) => {
2595
+ let publicHref = `${ctx.publicPath}${href}`;
2596
+ return allDynamicCssFiles.has(href) ? `${publicHref}#` : publicHref;
2597
+ })
2586
2598
  ].flat(1).filter(isNonNullable)
2587
2599
  )
2588
2600
  };
@@ -2605,6 +2617,44 @@ function resolveDependantChunks(viteManifest, entryChunks) {
2605
2617
  }
2606
2618
  return Array.from(chunks);
2607
2619
  }
2620
+ function getAllDynamicCssFiles(ctx, viteManifest) {
2621
+ let allDynamicCssFiles = /* @__PURE__ */ new Set();
2622
+ for (let route of Object.values(ctx.reactRouterConfig.routes)) {
2623
+ let routeFile = path6.join(ctx.reactRouterConfig.appDirectory, route.file);
2624
+ let entryChunk = resolveChunk(
2625
+ ctx,
2626
+ viteManifest,
2627
+ `${routeFile}${BUILD_CLIENT_ROUTE_QUERY_STRING}`
2628
+ );
2629
+ if (entryChunk) {
2630
+ let walk2 = function(chunk, isDynamicImportContext) {
2631
+ if (visitedChunks.has(chunk)) {
2632
+ return;
2633
+ }
2634
+ visitedChunks.add(chunk);
2635
+ if (isDynamicImportContext && chunk.css) {
2636
+ for (let cssFile of chunk.css) {
2637
+ allDynamicCssFiles.add(cssFile);
2638
+ }
2639
+ }
2640
+ if (chunk.dynamicImports) {
2641
+ for (let dynamicImportKey of chunk.dynamicImports) {
2642
+ walk2(viteManifest[dynamicImportKey], true);
2643
+ }
2644
+ }
2645
+ if (chunk.imports) {
2646
+ for (let importKey of chunk.imports) {
2647
+ walk2(viteManifest[importKey], isDynamicImportContext);
2648
+ }
2649
+ }
2650
+ };
2651
+ var walk = walk2;
2652
+ let visitedChunks = /* @__PURE__ */ new Set();
2653
+ walk2(entryChunk, false);
2654
+ }
2655
+ }
2656
+ return allDynamicCssFiles;
2657
+ }
2608
2658
  function dedupe(array2) {
2609
2659
  return [...new Set(array2)];
2610
2660
  }
@@ -2894,10 +2944,12 @@ var reactRouterVitePlugin = () => {
2894
2944
  let viteManifest = await loadViteManifest(
2895
2945
  getClientBuildDirectory(ctx.reactRouterConfig)
2896
2946
  );
2947
+ let allDynamicCssFiles = getAllDynamicCssFiles(ctx, viteManifest);
2897
2948
  let entry = getReactRouterManifestBuildAssets(
2898
2949
  ctx,
2899
2950
  viteConfig2,
2900
2951
  viteManifest,
2952
+ allDynamicCssFiles,
2901
2953
  ctx.entryClientFilePath,
2902
2954
  null
2903
2955
  );
@@ -2949,6 +3001,7 @@ var reactRouterVitePlugin = () => {
2949
3001
  ctx,
2950
3002
  viteConfig2,
2951
3003
  viteManifest,
3004
+ allDynamicCssFiles,
2952
3005
  `${routeFile}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
2953
3006
  route
2954
3007
  ),
@@ -4202,10 +4255,10 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
4202
4255
  }
4203
4256
  }
4204
4257
  let buildRoutes = createPrerenderRoutes(build.routes);
4205
- for (let path9 of build.prerender) {
4258
+ let prerenderSinglePath = async (path9) => {
4206
4259
  let matches = (0, import_react_router2.matchRoutes)(buildRoutes, `/${path9}/`.replace(/^\/\/+/, "/"));
4207
4260
  if (!matches) {
4208
- continue;
4261
+ return;
4209
4262
  }
4210
4263
  let leafRoute = matches ? matches[matches.length - 1].route : null;
4211
4264
  let manifestRoute = leafRoute ? build.routes[leafRoute.id]?.module : null;
@@ -4262,7 +4315,14 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
4262
4315
  } : void 0
4263
4316
  );
4264
4317
  }
4318
+ };
4319
+ let concurrency = 1;
4320
+ let { prerender } = reactRouterConfig;
4321
+ if (typeof prerender === "object" && "unstable_concurrency" in prerender) {
4322
+ concurrency = prerender.unstable_concurrency ?? 1;
4265
4323
  }
4324
+ const pMap = await import("p-map");
4325
+ await pMap.default(build.prerender, prerenderSinglePath, { concurrency });
4266
4326
  }
4267
4327
  function getStaticPrerenderPaths(routes) {
4268
4328
  let paths = ["/"];
@@ -4378,31 +4438,40 @@ ${content.toString("utf8")}`
4378
4438
  );
4379
4439
  }
4380
4440
  async function getPrerenderPaths(prerender, ssr, routes, logWarning = false) {
4381
- let prerenderPaths = [];
4382
- if (prerender != null && prerender !== false) {
4383
- let prerenderRoutes = createPrerenderRoutes(routes);
4384
- if (prerender === true) {
4385
- let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
4386
- if (logWarning && !ssr && paramRoutes.length > 0) {
4387
- console.warn(
4388
- import_picocolors4.default.yellow(
4389
- [
4390
- "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`. You may want to use the `prerender()` API to prerender the following paths:",
4391
- ...paramRoutes.map((p) => " - " + p)
4392
- ].join("\n")
4393
- )
4394
- );
4395
- }
4396
- prerenderPaths = paths;
4397
- } else if (typeof prerender === "function") {
4398
- prerenderPaths = await prerender({
4399
- getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
4400
- });
4401
- } else {
4402
- prerenderPaths = prerender || ["/"];
4441
+ if (prerender == null || prerender === false) {
4442
+ return [];
4443
+ }
4444
+ let pathsConfig;
4445
+ if (typeof prerender === "object" && "paths" in prerender) {
4446
+ pathsConfig = prerender.paths;
4447
+ } else {
4448
+ pathsConfig = prerender;
4449
+ }
4450
+ if (pathsConfig === false) {
4451
+ return [];
4452
+ }
4453
+ let prerenderRoutes = createPrerenderRoutes(routes);
4454
+ if (pathsConfig === true) {
4455
+ let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
4456
+ if (logWarning && !ssr && paramRoutes.length > 0) {
4457
+ console.warn(
4458
+ import_picocolors4.default.yellow(
4459
+ [
4460
+ "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`. You may want to use the `prerender()` API to prerender the following paths:",
4461
+ ...paramRoutes.map((p) => " - " + p)
4462
+ ].join("\n")
4463
+ )
4464
+ );
4403
4465
  }
4466
+ return paths;
4467
+ }
4468
+ if (typeof pathsConfig === "function") {
4469
+ let paths = await pathsConfig({
4470
+ getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
4471
+ });
4472
+ return paths;
4404
4473
  }
4405
- return prerenderPaths;
4474
+ return pathsConfig;
4406
4475
  }
4407
4476
  function groupRoutesByParentId2(manifest) {
4408
4477
  let routes = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c0va23/react-router-dev",
3
- "version": "7.9.5-alpha.1",
3
+ "version": "7.9.5-patch.1",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -78,6 +78,7 @@
78
78
  "isbot": "^5.1.11",
79
79
  "jsesc": "3.0.2",
80
80
  "lodash": "^4.17.21",
81
+ "p-map": "^7.0.3",
81
82
  "pathe": "^1.1.2",
82
83
  "picocolors": "^1.1.1",
83
84
  "prettier": "^3.6.2",
@@ -86,7 +87,7 @@
86
87
  "tinyglobby": "^0.2.14",
87
88
  "valibot": "^1.1.0",
88
89
  "vite-node": "^3.2.2",
89
- "@react-router/node": "7.9.4"
90
+ "@react-router/node": "7.9.5"
90
91
  },
91
92
  "devDependencies": {
92
93
  "@types/babel__core": "^7.20.5",
@@ -109,16 +110,16 @@
109
110
  "vite": "^6.1.0",
110
111
  "wireit": "0.14.9",
111
112
  "wrangler": "^4.23.0",
112
- "@react-router/serve": "7.9.4",
113
- "react-router": "^7.9.4"
113
+ "@react-router/serve": "7.9.5",
114
+ "react-router": "^7.9.5"
114
115
  },
115
116
  "peerDependencies": {
116
117
  "@vitejs/plugin-rsc": "*",
117
118
  "typescript": "^5.1.0",
118
119
  "vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
119
120
  "wrangler": "^3.28.2 || ^4.0.0",
120
- "@react-router/serve": "^7.9.4",
121
- "react-router": "^7.9.4"
121
+ "@react-router/serve": "^7.9.5",
122
+ "react-router": "^7.9.5"
122
123
  },
123
124
  "peerDependenciesMeta": {
124
125
  "@vitejs/plugin-rsc": {