rsbuild-plugin-react-router 0.1.1 → 0.3.0

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.
Files changed (99) hide show
  1. package/README.md +185 -204
  2. package/dist/451.js +1087 -0
  3. package/dist/bounded-cache.d.ts +1 -0
  4. package/dist/build-manifest.d.ts +1 -1
  5. package/dist/build-output-transforms.d.ts +30 -0
  6. package/dist/concurrency.d.ts +2 -0
  7. package/dist/config-imports.d.ts +3 -0
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/dev-generation.d.ts +25 -0
  10. package/dist/dev-runtime-artifacts.d.ts +39 -0
  11. package/dist/dev-runtime-compilation.d.ts +32 -0
  12. package/dist/dev-runtime-controller.d.ts +14 -0
  13. package/dist/dev-runtime-session.d.ts +34 -0
  14. package/dist/dev-server.d.ts +11 -2
  15. package/dist/export-utils.d.ts +15 -7
  16. package/dist/index.cjs +3085 -1272
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +2039 -1299
  19. package/dist/lazy-compilation.d.ts +5 -0
  20. package/dist/manifest.d.ts +32 -9
  21. package/dist/modify-browser-manifest.d.ts +30 -13
  22. package/dist/parallel-route-transform-protocol.d.ts +25 -0
  23. package/dist/parallel-route-transform-worker.d.ts +1 -0
  24. package/dist/parallel-route-transform-worker.js +38 -0
  25. package/dist/parallel-route-transforms.d.ts +14 -0
  26. package/dist/performance.d.ts +26 -0
  27. package/dist/plugin-utils.d.ts +2 -12
  28. package/dist/prerender-build.d.ts +32 -0
  29. package/dist/prerender.d.ts +28 -2
  30. package/dist/react-router-config.d.ts +8 -1
  31. package/dist/route-artifacts.d.ts +33 -0
  32. package/dist/route-ast.d.ts +21 -0
  33. package/dist/route-chunks.d.ts +9 -1
  34. package/dist/route-component-transform.d.ts +5 -0
  35. package/dist/route-export-pruning.d.ts +6 -0
  36. package/dist/route-export-resolution.d.ts +5 -0
  37. package/dist/route-transform-tasks.d.ts +47 -0
  38. package/dist/route-watch.d.ts +27 -0
  39. package/dist/server-build-plan.d.ts +22 -0
  40. package/dist/server-utils.d.ts +3 -2
  41. package/dist/templates/entry.client.js +3 -3
  42. package/dist/templates/entry.server.cjs +11 -8
  43. package/dist/templates/entry.server.js +5 -5
  44. package/dist/typegen.d.ts +2 -0
  45. package/dist/types.d.ts +30 -12
  46. package/dist/virtual-modules.d.ts +2 -0
  47. package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
  48. package/dist/yuku.d.ts +15 -0
  49. package/package.json +26 -22
  50. package/src/bounded-cache.ts +18 -0
  51. package/src/build-manifest.ts +168 -0
  52. package/src/build-output-transforms.ts +273 -0
  53. package/src/concurrency.ts +34 -0
  54. package/src/config-imports.ts +45 -0
  55. package/src/constants.ts +91 -0
  56. package/src/dev-generation.ts +700 -0
  57. package/src/dev-runtime-artifacts.ts +207 -0
  58. package/src/dev-runtime-compilation.ts +92 -0
  59. package/src/dev-runtime-controller.ts +462 -0
  60. package/src/dev-runtime-session.ts +184 -0
  61. package/src/dev-server.ts +58 -0
  62. package/src/export-utils.ts +219 -0
  63. package/src/index.ts +1025 -0
  64. package/src/lazy-compilation.ts +94 -0
  65. package/src/manifest.ts +480 -0
  66. package/src/modify-browser-manifest.ts +245 -0
  67. package/src/parallel-route-transform-protocol.ts +35 -0
  68. package/src/parallel-route-transform-worker.ts +82 -0
  69. package/src/parallel-route-transforms.ts +332 -0
  70. package/src/performance.ts +254 -0
  71. package/src/plugin-utils.ts +82 -0
  72. package/src/prerender-build.ts +623 -0
  73. package/src/prerender.ts +367 -0
  74. package/src/react-router-config.ts +220 -0
  75. package/src/route-artifacts.ts +155 -0
  76. package/src/route-ast.ts +163 -0
  77. package/src/route-chunks.ts +857 -0
  78. package/src/route-component-transform.ts +314 -0
  79. package/src/route-config.ts +106 -0
  80. package/src/route-export-pruning.ts +668 -0
  81. package/src/route-export-resolution.ts +329 -0
  82. package/src/route-transform-tasks.ts +249 -0
  83. package/src/route-watch.ts +322 -0
  84. package/src/server-build-plan.ts +91 -0
  85. package/src/server-utils.ts +210 -0
  86. package/src/ssr-externals.ts +59 -0
  87. package/src/templates/context.ts +12 -0
  88. package/src/templates/entry.client.tsx +12 -0
  89. package/src/templates/entry.server.tsx +76 -0
  90. package/src/typegen.ts +49 -0
  91. package/src/types.ts +82 -0
  92. package/src/validation/validate-plugin-order.ts +76 -0
  93. package/src/virtual-modules.ts +30 -0
  94. package/src/warnings/warn-on-client-source-maps.ts +96 -0
  95. package/src/yuku.ts +67 -0
  96. package/dist/906.js +0 -1
  97. package/dist/946.js +0 -1
  98. package/dist/babel.d.ts +0 -8
  99. package/dist/rslib-runtime.js +0 -23
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
  const __rslib_import_meta_url__ = "u" < typeof document ? new (require('url'.replace('', ''))).URL('file:' + __filename).href : document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href;
3
3
  var __webpack_require__ = {};
4
- __webpack_require__.d = (exports1, definition)=>{
5
- for(var key in definition)__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key) && Object.defineProperty(exports1, key, {
6
- enumerable: !0,
7
- get: definition[key]
8
- });
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key) && Object.defineProperty(exports1, key, {
7
+ enumerable: !0,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get"), define(values, "value");
9
12
  }, __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop), __webpack_require__.r = (exports1)=>{
10
13
  "u" > typeof Symbol && Symbol.toStringTag && Object.defineProperty(exports1, Symbol.toStringTag, {
11
14
  value: 'Module'
@@ -20,12 +23,12 @@ __webpack_require__.r(__webpack_exports__), __webpack_require__.d(__webpack_expo
20
23
  const external_node_stream_namespaceObject = require("node:stream"), node_namespaceObject = require("@react-router/node"), external_isbot_namespaceObject = require("isbot"), external_react_namespaceObject = require("react"), server_namespaceObject = require("react-dom/server"), external_react_router_namespaceObject = require("react-router"), ABORT_DELAY = 5000;
21
24
  function handleRequest(request, responseStatusCode, responseHeaders, routerContext) {
22
25
  return new Promise((resolve, reject)=>{
23
- let shellRendered = !1, statusCode = responseStatusCode, userAgent = request.headers.get('user-agent'), readyOption = userAgent && (0, external_isbot_namespaceObject.isbot)(userAgent) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady', { pipe, abort } = (0, server_namespaceObject.renderToPipeableStream)(external_react_namespaceObject.createElement(external_react_router_namespaceObject.ServerRouter, {
26
+ let abortDelay, shellRendered = !1, statusCode = responseStatusCode, userAgent = request.headers.get('user-agent'), readyOption = userAgent && (0, external_isbot_namespaceObject.isbot)(userAgent) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady', ready = !1, { pipe, abort } = (0, server_namespaceObject.renderToPipeableStream)(external_react_namespaceObject.createElement(external_react_router_namespaceObject.ServerRouter, {
24
27
  context: routerContext,
25
28
  url: request.url
26
29
  }), {
27
30
  [readyOption] () {
28
- shellRendered = !0;
31
+ ready = !0, 'onAllReady' === readyOption && abortDelay && clearTimeout(abortDelay), shellRendered = !0;
29
32
  let body = new external_node_stream_namespaceObject.PassThrough(), stream = (0, node_namespaceObject.createReadableStreamFromReadable)(body);
30
33
  responseHeaders.set('Content-Type', 'text/html'), resolve(new Response(stream, {
31
34
  headers: responseHeaders,
@@ -39,7 +42,7 @@ function handleRequest(request, responseStatusCode, responseHeaders, routerConte
39
42
  statusCode = 500, shellRendered && console.error(error);
40
43
  }
41
44
  });
42
- setTimeout(abort, 5000);
45
+ abortDelay = setTimeout(abort, 5000), 'onAllReady' === readyOption && ready && clearTimeout(abortDelay);
43
46
  });
44
47
  }
45
48
  for(var __rspack_i in exports.default = __webpack_exports__.default, __webpack_exports__)-1 === [
@@ -2,16 +2,16 @@ import { PassThrough } from "node:stream";
2
2
  import { createReadableStreamFromReadable } from "@react-router/node";
3
3
  import { isbot } from "isbot";
4
4
  import { renderToPipeableStream } from "react-dom/server";
5
- import { createElement } from "../946.js";
6
- import { ServerRouter } from "../906.js";
5
+ import { ServerRouter } from "react-router";
6
+ import * as __rspack_external_react from "react";
7
7
  export default function(request, responseStatusCode, responseHeaders, routerContext) {
8
8
  return new Promise((resolve, reject)=>{
9
- let shellRendered = !1, statusCode = responseStatusCode, userAgent = request.headers.get('user-agent'), readyOption = userAgent && isbot(userAgent) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady', { pipe, abort } = renderToPipeableStream(createElement(ServerRouter, {
9
+ let abortDelay, shellRendered = !1, statusCode = responseStatusCode, userAgent = request.headers.get('user-agent'), readyOption = userAgent && isbot(userAgent) || routerContext.isSpaMode ? 'onAllReady' : 'onShellReady', ready = !1, { pipe, abort } = renderToPipeableStream(__rspack_external_react.createElement(ServerRouter, {
10
10
  context: routerContext,
11
11
  url: request.url
12
12
  }), {
13
13
  [readyOption] () {
14
- shellRendered = !0;
14
+ ready = !0, 'onAllReady' === readyOption && abortDelay && clearTimeout(abortDelay), shellRendered = !0;
15
15
  let body = new PassThrough(), stream = createReadableStreamFromReadable(body);
16
16
  responseHeaders.set('Content-Type', 'text/html'), resolve(new Response(stream, {
17
17
  headers: responseHeaders,
@@ -25,6 +25,6 @@ export default function(request, responseStatusCode, responseHeaders, routerCont
25
25
  statusCode = 500, shellRendered && console.error(error);
26
26
  }
27
27
  });
28
- setTimeout(abort, 5000);
28
+ abortDelay = setTimeout(abort, 5000), 'onAllReady' === readyOption && ready && clearTimeout(abortDelay);
29
29
  });
30
30
  };
@@ -0,0 +1,2 @@
1
+ import type { RsbuildPluginAPI } from '@rsbuild/core';
2
+ export declare const registerReactRouterTypegen: (api: RsbuildPluginAPI) => void;
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { RsbuildConfig } from '@rsbuild/core';
1
2
  export type Route = {
2
3
  id: string;
3
4
  parentId?: string;
@@ -24,18 +25,35 @@ export type PluginOptions = {
24
25
  * Federation mode configuration
25
26
  */
26
27
  federation?: boolean;
27
- };
28
- /**
29
- * Arguments passed to transform functions
30
- */
31
- export type TransformArgs = {
32
- code: string;
33
- resource: string;
34
- resourcePath: string;
35
- context?: string | null;
36
- environment?: {
37
- name: string;
38
- };
28
+ /**
29
+ * Opt in to Rsbuild's dev-only lazy compilation behavior.
30
+ *
31
+ * React Router hydration modules remain eager so initial dev requests can
32
+ * load the browser manifest and route modules without lazy proxy delays.
33
+ *
34
+ * @default undefined
35
+ */
36
+ lazyCompilation?: NonNullable<RsbuildConfig['dev']>['lazyCompilation'];
37
+ /**
38
+ * Emit structured React Router plugin timing logs.
39
+ * @default false
40
+ */
41
+ logPerformance?: boolean;
42
+ /**
43
+ * Run route transforms in a worker-thread pool.
44
+ * Pass `true` to force the default worker count, a positive integer to set
45
+ * the worker count, or `false` to disable.
46
+ * @default Automatically enabled for 256+ resolved routes. The automatic
47
+ * pool uses available CPU cores minus 2.
48
+ */
49
+ parallelRouteTransform?: boolean | number;
50
+ /**
51
+ * Called when the route graph changes during development.
52
+ * Programmatic/custom servers can use this to recreate their Rsbuild server;
53
+ * the CLI uses its built-in reload-server watcher when this is omitted. This
54
+ * notification is not awaited, so it may safely close the current server.
55
+ */
56
+ onRouteTopologyChange?: () => void | Promise<void>;
39
57
  };
40
58
  export type RouteManifestItem = Omit<Route, 'file' | 'children'> & {
41
59
  module: string;
@@ -0,0 +1,2 @@
1
+ export declare const getVirtualModuleFilePath: (moduleId: string) => string;
2
+ export declare const mapVirtualModules: (modules: Record<string, string>) => Record<string, string>;
@@ -1,5 +1,6 @@
1
1
  import type { NormalizedConfig } from '@rsbuild/core';
2
2
  type Warn = (message: string) => void;
3
+ export declare function isSourceMapEnabled(value: unknown): boolean;
3
4
  export declare function getClientSourceMapSetting(normalized: NormalizedConfig, clientEnvName?: string): unknown;
4
5
  export declare function getClientDevtoolSetting(normalized: NormalizedConfig, clientEnvName?: string): unknown;
5
6
  export declare function warnOnClientSourceMaps(normalized: NormalizedConfig, warn: Warn, clientEnvName?: string): void;
package/dist/yuku.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { walk, type ParseOptions, type ParseResult } from 'yuku-parser';
2
+ import type { Rspack } from '@rsbuild/core';
3
+ export declare const parse: (code: string, options?: ParseOptions) => ParseResult;
4
+ export declare const traverse: typeof walk;
5
+ export declare const generate: (ast: ParseResult | {
6
+ type: "Program";
7
+ }, options?: {
8
+ sourceMaps?: boolean;
9
+ filename?: string;
10
+ sourceFileName?: string;
11
+ }) => {
12
+ code: string;
13
+ map: Rspack.RawSourceMap | null;
14
+ };
15
+ export type { ParseResult };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-react-router",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "React Router plugin for Rsbuild",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/rspack-contrib/rsbuild-plugin-react-router.git",
7
+ "url": "git+https://github.com/rstackjs/rsbuild-plugin-react-router.git",
8
8
  "directory": ""
9
9
  },
10
10
  "keywords": [
@@ -42,22 +42,16 @@
42
42
  "main": "./dist/index.cjs",
43
43
  "types": "./dist/index.d.ts",
44
44
  "files": [
45
- "dist"
45
+ "dist",
46
+ "src"
46
47
  ],
47
48
  "publint": {
48
49
  "ignoreMissingDts": true
49
50
  },
50
51
  "dependencies": {
51
- "@babel/core": "^7.28.6",
52
- "@babel/generator": "^7.28.6",
53
- "@babel/parser": "^7.28.6",
54
- "@babel/traverse": "^7.28.6",
55
- "@babel/types": "^7.28.6",
56
52
  "@react-router/node": "^7.13.0",
57
53
  "@remix-run/node-fetch-server": "^0.13.0",
58
- "@rspack/plugin-react-refresh": "^1.6.0",
59
- "babel-dead-code-elimination": "^1.0.12",
60
- "esbuild": "^0.27.2",
54
+ "@rspack/plugin-react-refresh": "^2.0.2",
61
55
  "execa": "^9.6.1",
62
56
  "fs-extra": "11.3.3",
63
57
  "isbot": "5.1.34",
@@ -65,19 +59,20 @@
65
59
  "jsesc": "^3.1.0",
66
60
  "pathe": "^2.0.3",
67
61
  "react-refresh": "^0.18.0",
68
- "rspack-plugin-virtual-module": "^1.0.1"
62
+ "yuku-analyzer": "0.5.39",
63
+ "yuku-codegen": "0.5.39",
64
+ "yuku-parser": "0.5.39"
69
65
  },
70
66
  "devDependencies": {
71
67
  "@changesets/cli": "^2.29.8",
72
- "@react-router/dev": "^7.13.0",
73
- "@rsbuild/core": "1.7.2",
74
- "@rslib/core": "^0.19.3",
75
- "@rspack/core": "1.7.4",
68
+ "@react-router/dev": "^8.0.1",
69
+ "@rsbuild/core": "2.1.0",
70
+ "@rsbuild/plugin-react": "2.1.0",
71
+ "@rslib/core": "^0.22.1",
72
+ "@rspack/core": "2.1.0",
76
73
  "@rstest/core": "^0.8.1",
77
74
  "@rstest/coverage-istanbul": "^0.2.0",
78
- "@types/babel__core": "^7.20.5",
79
- "@types/babel__generator": "^7.27.0",
80
- "@types/babel__traverse": "^7.28.0",
75
+ "@swc/helpers": "^0.5.23",
81
76
  "@types/fs-extra": "11.0.4",
82
77
  "@types/jsesc": "^3.0.3",
83
78
  "@types/node": "^25.0.10",
@@ -85,7 +80,8 @@
85
80
  "@types/react-dom": "^19.2.3",
86
81
  "es-module-lexer": "1.7.0",
87
82
  "kill-port": "^2.0.1",
88
- "playwright": "^1.58.0",
83
+ "pkg-pr-new": "^0.0.75",
84
+ "playwright": "1.58.0",
89
85
  "prettier": "3.8.1",
90
86
  "react": "^19.2.4",
91
87
  "react-dom": "^19.2.4",
@@ -95,7 +91,7 @@
95
91
  "@rsbuild/config": "1.0.1"
96
92
  },
97
93
  "peerDependencies": {
98
- "@rsbuild/core": "^1.7.2"
94
+ "@rsbuild/core": "^2.0.0"
99
95
  },
100
96
  "publishConfig": {
101
97
  "access": "public",
@@ -104,13 +100,21 @@
104
100
  },
105
101
  "scripts": {
106
102
  "build": "rslib build",
107
- "e2e": "pnpm build && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only}' test:e2e",
103
+ "bench": "node scripts/bench-builds.mjs",
104
+ "bench:ci-report": "node scripts/report-benchmark-ci.mjs",
105
+ "bench:compare": "node scripts/compare-benchmarks.mjs",
106
+ "bench:smoke": "node scripts/bench-builds.mjs --profile smoke --iterations 1 --warmup 0 --format both --out .benchmark/results/smoke",
107
+ "bench:baseline": "node scripts/bench-builds.mjs --profile default --iterations 5 --warmup 1 --clean build --format both --out .benchmark/results/baseline",
108
+ "bench:full": "node scripts/bench-builds.mjs --profile full --iterations 5 --warmup 1 --clean build --format both --out .benchmark/results/full",
109
+ "bench:large": "node scripts/bench-builds.mjs --profile large --iterations 1 --warmup 0 --clean cold --format both --out .benchmark/results/large",
110
+ "e2e": "pnpm build && pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only}' test:e2e",
108
111
  "dev": "rslib build --watch",
109
112
  "test": "rstest run",
110
113
  "test:watch": "rstest watch",
111
114
  "test:coverage": "rstest run --coverage",
112
115
  "test:core": "rstest run -c ./rstest.config.ts",
113
116
  "test:core:watch": "rstest watch -c ./rstest.config.ts",
117
+ "test:package-interop": "node scripts/test-package-interop.mjs",
114
118
  "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
115
119
  "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
116
120
  "changeset": "changeset",
@@ -0,0 +1,18 @@
1
+ export const setBoundedCacheEntry = <Key, Value>(
2
+ cache: Map<Key, Value>,
3
+ key: Key,
4
+ value: Value,
5
+ maxEntries: number
6
+ ): void => {
7
+ if (maxEntries <= 0) {
8
+ cache.clear();
9
+ return;
10
+ }
11
+ if (!cache.has(key) && cache.size >= maxEntries) {
12
+ const oldestEntry = cache.keys().next();
13
+ if (!oldestEntry.done) {
14
+ cache.delete(oldestEntry.value);
15
+ }
16
+ }
17
+ cache.set(key, value);
18
+ };
@@ -0,0 +1,168 @@
1
+ import { relative, resolve } from 'pathe';
2
+ import type { Config } from './react-router-config.js';
3
+ import type { Route } from './types.js';
4
+
5
+ type BuildManifest =
6
+ | {
7
+ routes: Record<string, Route>;
8
+ }
9
+ | {
10
+ routes: Record<string, Route>;
11
+ serverBundles: Record<string, { id: string; file: string }>;
12
+ routeIdToServerBundleId: Record<string, string>;
13
+ };
14
+
15
+ const normalizePath = (value: string): string => value.replace(/\\/g, '/');
16
+
17
+ const getAddressableRoutes = (routes: Record<string, Route>): Route[] => {
18
+ const nonAddressableIds = new Set<string>();
19
+ for (const id in routes) {
20
+ const route = routes[id];
21
+ if (route.index) {
22
+ if (!route.parentId) {
23
+ continue;
24
+ }
25
+ nonAddressableIds.add(route.parentId);
26
+ }
27
+ if (typeof route.path !== 'string' && !route.index) {
28
+ nonAddressableIds.add(id);
29
+ }
30
+ }
31
+ return Object.values(routes).filter(
32
+ route => !nonAddressableIds.has(route.id)
33
+ );
34
+ };
35
+
36
+ const getRouteBranch = (
37
+ routes: Record<string, Route>,
38
+ routeId: string
39
+ ): Route[] => {
40
+ const branch: Route[] = [];
41
+ let current = routeId;
42
+ while (current) {
43
+ const route = routes[current];
44
+ if (!route) {
45
+ throw new Error(`Missing route for ${current}`);
46
+ }
47
+ branch.push(route);
48
+ current = route.parentId || '';
49
+ }
50
+ return branch.reverse();
51
+ };
52
+
53
+ const configRouteToBranchRoute = (route: Route) => ({
54
+ id: route.id,
55
+ path: route.path,
56
+ file: route.file,
57
+ index: route.index,
58
+ });
59
+
60
+ export const getBuildManifest = async ({
61
+ reactRouterConfig,
62
+ routes,
63
+ rootDirectory,
64
+ }: {
65
+ reactRouterConfig: Required<
66
+ Pick<
67
+ Config,
68
+ 'appDirectory' | 'buildDirectory' | 'serverBuildFile' | 'future'
69
+ >
70
+ > &
71
+ Pick<Config, 'serverBundles'>;
72
+ routes: Record<string, Route>;
73
+ rootDirectory: string;
74
+ }): Promise<BuildManifest | undefined> => {
75
+ const {
76
+ serverBundles,
77
+ appDirectory,
78
+ buildDirectory,
79
+ serverBuildFile,
80
+ future,
81
+ } = reactRouterConfig;
82
+
83
+ if (!serverBundles) {
84
+ return { routes };
85
+ }
86
+
87
+ const rootRelativeRoutes = Object.fromEntries(
88
+ Object.entries(routes).map(([id, route]) => {
89
+ const filePath = resolve(appDirectory, route.file);
90
+ return [
91
+ id,
92
+ { ...route, file: normalizePath(relative(rootDirectory, filePath)) },
93
+ ];
94
+ })
95
+ );
96
+
97
+ const serverBuildDirectory = resolve(buildDirectory, 'server');
98
+
99
+ const buildManifest: BuildManifest = {
100
+ routes: rootRelativeRoutes,
101
+ serverBundles: {},
102
+ routeIdToServerBundleId: {},
103
+ };
104
+
105
+ await Promise.all(
106
+ getAddressableRoutes(routes).map(async route => {
107
+ const branch = getRouteBranch(routes, route.id);
108
+ const serverBundleId = await serverBundles({
109
+ branch: branch.map(branchRoute =>
110
+ configRouteToBranchRoute({
111
+ ...branchRoute,
112
+ file: resolve(appDirectory, branchRoute.file),
113
+ })
114
+ ),
115
+ });
116
+
117
+ if (typeof serverBundleId !== 'string') {
118
+ throw new Error('The "serverBundles" function must return a string');
119
+ }
120
+
121
+ if (future?.v8_viteEnvironmentApi) {
122
+ if (!/^[a-zA-Z0-9_]+$/.test(serverBundleId)) {
123
+ throw new Error(
124
+ 'The "serverBundles" function must only return strings containing alphanumeric characters and underscores.'
125
+ );
126
+ }
127
+ } else if (!/^[a-zA-Z0-9-_]+$/.test(serverBundleId)) {
128
+ throw new Error(
129
+ 'The "serverBundles" function must only return strings containing alphanumeric characters, hyphens and underscores.'
130
+ );
131
+ }
132
+
133
+ buildManifest.routeIdToServerBundleId![route.id] = serverBundleId;
134
+ buildManifest.serverBundles![serverBundleId] ??= {
135
+ id: serverBundleId,
136
+ file: normalizePath(
137
+ relative(
138
+ rootDirectory,
139
+ resolve(serverBuildDirectory, serverBundleId, serverBuildFile)
140
+ )
141
+ ),
142
+ };
143
+ })
144
+ );
145
+
146
+ return buildManifest;
147
+ };
148
+
149
+ export const getRoutesByServerBundleId = (
150
+ buildManifest: BuildManifest | undefined,
151
+ sourceRoutes: Record<string, Route>
152
+ ): Record<string, Record<string, Route>> => {
153
+ if (!buildManifest || !('routeIdToServerBundleId' in buildManifest)) {
154
+ return {};
155
+ }
156
+
157
+ const routesByServerBundleId: Record<string, Record<string, Route>> = {};
158
+ for (const [routeId, serverBundleId] of Object.entries(
159
+ buildManifest.routeIdToServerBundleId
160
+ )) {
161
+ routesByServerBundleId[serverBundleId] ??= {};
162
+ const branch = getRouteBranch(sourceRoutes, routeId);
163
+ for (const route of branch) {
164
+ routesByServerBundleId[serverBundleId][route.id] = route;
165
+ }
166
+ }
167
+ return routesByServerBundleId;
168
+ };