@react-router/dev 0.0.0-experimental-66357d4 → 0.0.0-experimental-adadca553

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,116 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.9.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Update `valibot` dependency to `^1.1.0` ([#14379](https://github.com/remix-run/react-router/pull/14379))
8
+
9
+ - New (unstable) `useRoute` hook for accessing data from specific routes ([#14407](https://github.com/remix-run/react-router/pull/14407))
10
+
11
+ For example, let's say you have an `admin` route somewhere in your app and you want any child routes of `admin` to all have access to the `loaderData` and `actionData` from `admin.`
12
+
13
+ ```tsx
14
+ // app/routes/admin.tsx
15
+ import { Outlet } from "react-router";
16
+
17
+ export const loader = () => ({ message: "Hello, loader!" });
18
+
19
+ export const action = () => ({ count: 1 });
20
+
21
+ export default function Component() {
22
+ return (
23
+ <div>
24
+ {/* ... */}
25
+ <Outlet />
26
+ {/* ... */}
27
+ </div>
28
+ );
29
+ }
30
+ ```
31
+
32
+ You might even want to create a reusable widget that all of the routes nested under `admin` could use:
33
+
34
+ ```tsx
35
+ import { unstable_useRoute as useRoute } from "react-router";
36
+
37
+ export function AdminWidget() {
38
+ // How to get `message` and `count` from `admin` route?
39
+ }
40
+ ```
41
+
42
+ In framework mode, `useRoute` knows all your app's routes and gives you TS errors when invalid route IDs are passed in:
43
+
44
+ ```tsx
45
+ export function AdminWidget() {
46
+ const admin = useRoute("routes/dmin");
47
+ // ^^^^^^^^^^^
48
+ }
49
+ ```
50
+
51
+ `useRoute` returns `undefined` if the route is not part of the current page:
52
+
53
+ ```tsx
54
+ export function AdminWidget() {
55
+ const admin = useRoute("routes/admin");
56
+ if (!admin) {
57
+ throw new Error(`AdminWidget used outside of "routes/admin"`);
58
+ }
59
+ }
60
+ ```
61
+
62
+ Note: the `root` route is the exception since it is guaranteed to be part of the current page.
63
+ As a result, `useRoute` never returns `undefined` for `root`.
64
+
65
+ `loaderData` and `actionData` are marked as optional since they could be accessed before the `action` is triggered or after the `loader` threw an error:
66
+
67
+ ```tsx
68
+ export function AdminWidget() {
69
+ const admin = useRoute("routes/admin");
70
+ if (!admin) {
71
+ throw new Error(`AdminWidget used outside of "routes/admin"`);
72
+ }
73
+ const { loaderData, actionData } = admin;
74
+ console.log(loaderData);
75
+ // ^? { message: string } | undefined
76
+ console.log(actionData);
77
+ // ^? { count: number } | undefined
78
+ }
79
+ ```
80
+
81
+ If instead of a specific route, you wanted access to the _current_ route's `loaderData` and `actionData`, you can call `useRoute` without arguments:
82
+
83
+ ```tsx
84
+ export function AdminWidget() {
85
+ const currentRoute = useRoute();
86
+ currentRoute.loaderData;
87
+ currentRoute.actionData;
88
+ }
89
+ ```
90
+
91
+ This usage is equivalent to calling `useLoaderData` and `useActionData`, but consolidates all route data access into one hook: `useRoute`.
92
+
93
+ Note: when calling `useRoute()` (without a route ID), TS has no way to know which route is the current route.
94
+ As a result, `loaderData` and `actionData` are typed as `unknown`.
95
+ If you want more type-safety, you can either narrow the type yourself with something like `zod` or you can refactor your app to pass down typed props to your `AdminWidget`:
96
+
97
+ ```tsx
98
+ export function AdminWidget({
99
+ message,
100
+ count,
101
+ }: {
102
+ message: string;
103
+ count: number;
104
+ }) {
105
+ /* ... */
106
+ }
107
+ ```
108
+
109
+ - Updated dependencies:
110
+ - `react-router@7.9.4`
111
+ - `@react-router/node@7.9.4`
112
+ - `@react-router/serve@7.9.4`
113
+
3
114
  ## 7.9.3
4
115
 
5
116
  ### 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-66357d4
3
+ * @react-router/dev v0.0.0-experimental-adadca553
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) {
@@ -980,9 +989,10 @@ function generateRoutes(ctx) {
980
989
  interface Register {
981
990
  pages: Pages
982
991
  routeFiles: RouteFiles
992
+ routeModules: RouteModules
983
993
  }
984
994
  }
985
- ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code
995
+ ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code + "\n\n" + generate(routeModulesType(ctx)).code
986
996
  };
987
997
  const allAnnotations = Array.from(fileToRoutes.entries()).filter(([file]) => isInAppDirectory(ctx, file)).map(
988
998
  ([file, routeIds]) => getRouteAnnotations({ ctx, file, routeIds, lineages })
@@ -1051,6 +1061,28 @@ function routeFilesType({
1051
1061
  )
1052
1062
  );
1053
1063
  }
1064
+ function routeModulesType(ctx) {
1065
+ return t2.tsTypeAliasDeclaration(
1066
+ t2.identifier("RouteModules"),
1067
+ null,
1068
+ t2.tsTypeLiteral(
1069
+ Object.values(ctx.config.routes).map(
1070
+ (route) => t2.tsPropertySignature(
1071
+ t2.stringLiteral(route.id),
1072
+ t2.tsTypeAnnotation(
1073
+ isInAppDirectory(ctx, route.file) ? t2.tsTypeQuery(
1074
+ t2.tsImportType(
1075
+ t2.stringLiteral(
1076
+ `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1077
+ )
1078
+ )
1079
+ ) : t2.tsUnknownKeyword()
1080
+ )
1081
+ )
1082
+ )
1083
+ )
1084
+ );
1085
+ }
1054
1086
  function isInAppDirectory(ctx, routeFile) {
1055
1087
  const path9 = Path3.resolve(ctx.config.appDirectory, routeFile);
1056
1088
  return path9.startsWith(ctx.config.appDirectory);
@@ -1748,7 +1780,7 @@ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1748
1780
  function isNonNullable(x) {
1749
1781
  return x != null;
1750
1782
  }
1751
- var import_node_crypto, import_node_fs3, import_promises2, path7, url, babel2, import_node_fetch_server2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors5, import_kebabCase, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1783
+ var import_node_crypto, import_node_fs3, import_promises2, path7, url, babel2, import_node_fetch_server2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors5, import_kebabCase, import_p_map, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1752
1784
  var init_plugin = __esm({
1753
1785
  "vite/plugin.ts"() {
1754
1786
  "use strict";
@@ -1765,6 +1797,7 @@ var init_plugin = __esm({
1765
1797
  import_jsesc = __toESM(require("jsesc"));
1766
1798
  import_picocolors5 = __toESM(require("picocolors"));
1767
1799
  import_kebabCase = __toESM(require("lodash/kebabCase"));
1800
+ import_p_map = __toESM(require("p-map"));
1768
1801
  init_typegen();
1769
1802
  init_invariant();
1770
1803
  init_babel();
@@ -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({
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
- * @react-router/dev v0.0.0-experimental-66357d4
2
+ * @react-router/dev v0.0.0-experimental-adadca553
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-66357d4
2
+ * @react-router/dev v0.0.0-experimental-adadca553
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-66357d4
2
+ * @react-router/dev v0.0.0-experimental-adadca553
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
- * @react-router/dev v0.0.0-experimental-66357d4
2
+ * @react-router/dev v0.0.0-experimental-adadca553
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -59,6 +59,7 @@ var import_pick3 = __toESM(require("lodash/pick"));
59
59
  var import_jsesc = __toESM(require("jsesc"));
60
60
  var import_picocolors4 = __toESM(require("picocolors"));
61
61
  var import_kebabCase = __toESM(require("lodash/kebabCase"));
62
+ var import_p_map = __toESM(require("p-map"));
62
63
 
63
64
  // typegen/index.ts
64
65
  var import_promises = __toESM(require("fs/promises"));
@@ -447,11 +448,20 @@ async function resolveConfig({
447
448
  if (!ssr && serverBundles) {
448
449
  serverBundles = void 0;
449
450
  }
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
- );
451
+ if (prerender) {
452
+ let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p);
453
+ let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths);
454
+ if (!isValidPrerenderConfig) {
455
+ return err(
456
+ "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."
457
+ );
458
+ }
459
+ let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0;
460
+ if (!isValidConcurrencyConfig) {
461
+ return err(
462
+ "The `prerender.unstable_concurrency` config must be a positive integer if specified."
463
+ );
464
+ }
455
465
  }
456
466
  let routeDiscovery;
457
467
  if (userRouteDiscovery == null) {
@@ -968,9 +978,10 @@ function generateRoutes(ctx) {
968
978
  interface Register {
969
979
  pages: Pages
970
980
  routeFiles: RouteFiles
981
+ routeModules: RouteModules
971
982
  }
972
983
  }
973
- ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code
984
+ ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code + "\n\n" + generate(routeModulesType(ctx)).code
974
985
  };
975
986
  const allAnnotations = Array.from(fileToRoutes.entries()).filter(([file]) => isInAppDirectory(ctx, file)).map(
976
987
  ([file, routeIds]) => getRouteAnnotations({ ctx, file, routeIds, lineages })
@@ -1039,6 +1050,28 @@ function routeFilesType({
1039
1050
  )
1040
1051
  );
1041
1052
  }
1053
+ function routeModulesType(ctx) {
1054
+ return t2.tsTypeAliasDeclaration(
1055
+ t2.identifier("RouteModules"),
1056
+ null,
1057
+ t2.tsTypeLiteral(
1058
+ Object.values(ctx.config.routes).map(
1059
+ (route) => t2.tsPropertySignature(
1060
+ t2.stringLiteral(route.id),
1061
+ t2.tsTypeAnnotation(
1062
+ isInAppDirectory(ctx, route.file) ? t2.tsTypeQuery(
1063
+ t2.tsImportType(
1064
+ t2.stringLiteral(
1065
+ `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1066
+ )
1067
+ )
1068
+ ) : t2.tsUnknownKeyword()
1069
+ )
1070
+ )
1071
+ )
1072
+ )
1073
+ );
1074
+ }
1042
1075
  function isInAppDirectory(ctx, routeFile) {
1043
1076
  const path9 = Path3.resolve(ctx.config.appDirectory, routeFile);
1044
1077
  return path9.startsWith(ctx.config.appDirectory);
@@ -2526,7 +2559,7 @@ var getClientEntryChunk = (ctx, viteManifest) => {
2526
2559
  invariant(chunk, `Chunk not found: ${filePath}`);
2527
2560
  return chunk;
2528
2561
  };
2529
- var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, entryFilePath, route) => {
2562
+ var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, allDynamicCssFiles, entryFilePath, route) => {
2530
2563
  let entryChunk = resolveChunk(ctx, viteManifest, entryFilePath);
2531
2564
  invariant(entryChunk, `Chunk not found: ${entryFilePath}`);
2532
2565
  let isRootRoute = Boolean(route && route.parentId === void 0);
@@ -2559,7 +2592,10 @@ var getReactRouterManifestBuildAssets = (ctx, viteConfig, viteManifest, entryFil
2559
2592
  // in the manifest that isn't tied to any route file. If we want to render these
2560
2593
  // styles correctly, we need to include them in the root route.
2561
2594
  isRootRoute ? getCssCodeSplitDisabledFile(ctx, viteConfig, viteManifest) : null,
2562
- chunks.flatMap((e) => e.css ?? []).map((href) => `${ctx.publicPath}${href}`)
2595
+ chunks.flatMap((e) => e.css ?? []).map((href) => {
2596
+ let publicHref = `${ctx.publicPath}${href}`;
2597
+ return allDynamicCssFiles.has(href) ? `${publicHref}#` : publicHref;
2598
+ })
2563
2599
  ].flat(1).filter(isNonNullable)
2564
2600
  )
2565
2601
  };
@@ -2582,6 +2618,44 @@ function resolveDependantChunks(viteManifest, entryChunks) {
2582
2618
  }
2583
2619
  return Array.from(chunks);
2584
2620
  }
2621
+ function getAllDynamicCssFiles(ctx, viteManifest) {
2622
+ let allDynamicCssFiles = /* @__PURE__ */ new Set();
2623
+ for (let route of Object.values(ctx.reactRouterConfig.routes)) {
2624
+ let routeFile = path6.join(ctx.reactRouterConfig.appDirectory, route.file);
2625
+ let entryChunk = resolveChunk(
2626
+ ctx,
2627
+ viteManifest,
2628
+ `${routeFile}${BUILD_CLIENT_ROUTE_QUERY_STRING}`
2629
+ );
2630
+ if (entryChunk) {
2631
+ let walk2 = function(chunk, isDynamicImportContext) {
2632
+ if (visitedChunks.has(chunk)) {
2633
+ return;
2634
+ }
2635
+ visitedChunks.add(chunk);
2636
+ if (isDynamicImportContext && chunk.css) {
2637
+ for (let cssFile of chunk.css) {
2638
+ allDynamicCssFiles.add(cssFile);
2639
+ }
2640
+ }
2641
+ if (chunk.dynamicImports) {
2642
+ for (let dynamicImportKey of chunk.dynamicImports) {
2643
+ walk2(viteManifest[dynamicImportKey], true);
2644
+ }
2645
+ }
2646
+ if (chunk.imports) {
2647
+ for (let importKey of chunk.imports) {
2648
+ walk2(viteManifest[importKey], isDynamicImportContext);
2649
+ }
2650
+ }
2651
+ };
2652
+ var walk = walk2;
2653
+ let visitedChunks = /* @__PURE__ */ new Set();
2654
+ walk2(entryChunk, false);
2655
+ }
2656
+ }
2657
+ return allDynamicCssFiles;
2658
+ }
2585
2659
  function dedupe(array2) {
2586
2660
  return [...new Set(array2)];
2587
2661
  }
@@ -2871,10 +2945,12 @@ var reactRouterVitePlugin = () => {
2871
2945
  let viteManifest = await loadViteManifest(
2872
2946
  getClientBuildDirectory(ctx.reactRouterConfig)
2873
2947
  );
2948
+ let allDynamicCssFiles = getAllDynamicCssFiles(ctx, viteManifest);
2874
2949
  let entry = getReactRouterManifestBuildAssets(
2875
2950
  ctx,
2876
2951
  viteConfig2,
2877
2952
  viteManifest,
2953
+ allDynamicCssFiles,
2878
2954
  ctx.entryClientFilePath,
2879
2955
  null
2880
2956
  );
@@ -2926,6 +3002,7 @@ var reactRouterVitePlugin = () => {
2926
3002
  ctx,
2927
3003
  viteConfig2,
2928
3004
  viteManifest,
3005
+ allDynamicCssFiles,
2929
3006
  `${routeFile}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
2930
3007
  route
2931
3008
  ),
@@ -4179,10 +4256,10 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
4179
4256
  }
4180
4257
  }
4181
4258
  let buildRoutes = createPrerenderRoutes(build.routes);
4182
- for (let path9 of build.prerender) {
4259
+ let prerenderSinglePath = async (path9) => {
4183
4260
  let matches = (0, import_react_router2.matchRoutes)(buildRoutes, `/${path9}/`.replace(/^\/\/+/, "/"));
4184
4261
  if (!matches) {
4185
- continue;
4262
+ return;
4186
4263
  }
4187
4264
  let leafRoute = matches ? matches[matches.length - 1].route : null;
4188
4265
  let manifestRoute = leafRoute ? build.routes[leafRoute.id]?.module : null;
@@ -4239,7 +4316,13 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
4239
4316
  } : void 0
4240
4317
  );
4241
4318
  }
4319
+ };
4320
+ let concurrency = 1;
4321
+ let { prerender } = reactRouterConfig;
4322
+ if (typeof prerender === "object" && "unstable_concurrency" in prerender) {
4323
+ concurrency = prerender.unstable_concurrency ?? 1;
4242
4324
  }
4325
+ await (0, import_p_map.default)(build.prerender, prerenderSinglePath, { concurrency });
4243
4326
  }
4244
4327
  function getStaticPrerenderPaths(routes) {
4245
4328
  let paths = ["/"];
@@ -4355,31 +4438,40 @@ ${content.toString("utf8")}`
4355
4438
  );
4356
4439
  }
4357
4440
  async function getPrerenderPaths(prerender, ssr, routes, logWarning = false) {
4358
- let prerenderPaths = [];
4359
- if (prerender != null && prerender !== false) {
4360
- let prerenderRoutes = createPrerenderRoutes(routes);
4361
- if (prerender === true) {
4362
- let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
4363
- if (logWarning && !ssr && paramRoutes.length > 0) {
4364
- console.warn(
4365
- import_picocolors4.default.yellow(
4366
- [
4367
- "\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:",
4368
- ...paramRoutes.map((p) => " - " + p)
4369
- ].join("\n")
4370
- )
4371
- );
4372
- }
4373
- prerenderPaths = paths;
4374
- } else if (typeof prerender === "function") {
4375
- prerenderPaths = await prerender({
4376
- getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
4377
- });
4378
- } else {
4379
- 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
+ );
4380
4465
  }
4466
+ return paths;
4467
+ }
4468
+ if (typeof pathsConfig === "function") {
4469
+ let paths = await pathsConfig({
4470
+ getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
4471
+ });
4472
+ return paths;
4381
4473
  }
4382
- return prerenderPaths;
4474
+ return pathsConfig;
4383
4475
  }
4384
4476
  function groupRoutesByParentId2(manifest) {
4385
4477
  let routes = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-66357d4",
3
+ "version": "0.0.0-experimental-adadca553",
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": "0.0.0-experimental-66357d4"
90
+ "@react-router/node": "0.0.0-experimental-adadca553"
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": "0.0.0-experimental-66357d4",
113
- "react-router": "^0.0.0-experimental-66357d4"
113
+ "@react-router/serve": "0.0.0-experimental-adadca553",
114
+ "react-router": "^0.0.0-experimental-adadca553"
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": "^0.0.0-experimental-66357d4",
121
- "react-router": "^0.0.0-experimental-66357d4"
121
+ "@react-router/serve": "^0.0.0-experimental-adadca553",
122
+ "react-router": "^0.0.0-experimental-adadca553"
122
123
  },
123
124
  "peerDependenciesMeta": {
124
125
  "@vitejs/plugin-rsc": {