rsbuild-plugin-react-router 0.5.0 → 0.6.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.
Files changed (110) hide show
  1. package/README.md +58 -208
  2. package/dist/511.js +371 -332
  3. package/dist/build-output-transforms.d.ts +30 -6
  4. package/dist/classic-mode.d.ts +56 -0
  5. package/dist/config-imports.d.ts +8 -2
  6. package/dist/constants.d.ts +3 -0
  7. package/dist/dev-background-resources.d.ts +3 -1
  8. package/dist/dev-generation.d.ts +2 -3
  9. package/dist/dev-hmr.d.ts +8 -2
  10. package/dist/dev-runtime-controller.d.ts +6 -1
  11. package/dist/dev-source-maps.d.ts +4 -0
  12. package/dist/effect-runtime.d.ts +17 -5
  13. package/dist/entry-paths.d.ts +16 -0
  14. package/dist/environment-output.d.ts +6 -0
  15. package/dist/export-utils.d.ts +2 -1
  16. package/dist/index.cjs +3964 -2217
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.js +3474 -1799
  19. package/dist/lazy-compilation-prewarm.d.ts +8 -5
  20. package/dist/manifest.d.ts +17 -5
  21. package/dist/mode-plan.d.ts +82 -0
  22. package/dist/modify-browser-manifest.d.ts +8 -4
  23. package/dist/plugin-utils.d.ts +27 -1
  24. package/dist/prerender-build.d.ts +5 -5
  25. package/dist/prerender.d.ts +1 -5
  26. package/dist/react-router-config.d.ts +11 -3
  27. package/dist/route-artifacts.d.ts +4 -2
  28. package/dist/route-chunks.d.ts +8 -2
  29. package/dist/route-component-transform.d.ts +0 -2
  30. package/dist/route-export-pruning.d.ts +4 -1
  31. package/dist/route-imports.d.ts +18 -0
  32. package/dist/route-transform-tasks.d.ts +5 -0
  33. package/dist/route-watch.d.ts +11 -6
  34. package/dist/rsc-dev-server.d.ts +26 -0
  35. package/dist/rsc-prerender.d.ts +54 -0
  36. package/dist/rsc-route-config.d.ts +6 -0
  37. package/dist/rsc-route-exports.d.ts +15 -0
  38. package/dist/rsc-route-transform-loader.cjs +43 -0
  39. package/dist/rsc-route-transform-loader.d.ts +30 -0
  40. package/dist/rsc-route-transform-loader.js +16 -0
  41. package/dist/rsc-route-transform-registration.d.ts +12 -0
  42. package/dist/rsc-route-transforms.d.ts +21 -0
  43. package/dist/rsc-support.d.ts +23 -0
  44. package/dist/rsc-virtual-modules.d.ts +19 -0
  45. package/dist/server-build-plan.d.ts +2 -1
  46. package/dist/server-build-resolution.d.ts +1 -2
  47. package/dist/server-utils.d.ts +4 -5
  48. package/dist/ssr-asset-relocation.d.ts +98 -0
  49. package/dist/templates/entry.rsc.client.d.ts +1 -0
  50. package/dist/templates/entry.rsc.client.js +61 -0
  51. package/dist/templates/entry.rsc.d.ts +9 -0
  52. package/dist/templates/entry.rsc.js +38 -0
  53. package/dist/templates/entry.rsc.ssr.d.ts +4 -0
  54. package/dist/templates/entry.rsc.ssr.js +24 -0
  55. package/dist/typegen.d.ts +4 -2
  56. package/dist/types.d.ts +30 -1
  57. package/package.json +69 -14
  58. package/src/build-output-transforms.ts +155 -21
  59. package/src/classic-mode.ts +258 -0
  60. package/src/config-imports.ts +153 -6
  61. package/src/constants.ts +6 -2
  62. package/src/dev-background-resources.ts +46 -85
  63. package/src/dev-generation.ts +52 -33
  64. package/src/dev-hmr.ts +112 -80
  65. package/src/dev-runtime-artifacts.ts +11 -12
  66. package/src/dev-runtime-controller.ts +75 -85
  67. package/src/dev-runtime-session.ts +14 -18
  68. package/src/dev-server.ts +2 -0
  69. package/src/dev-source-maps.ts +257 -0
  70. package/src/effect-runtime.ts +105 -57
  71. package/src/entry-paths.ts +80 -0
  72. package/src/environment-output.ts +55 -0
  73. package/src/export-utils.ts +15 -11
  74. package/src/index.ts +661 -496
  75. package/src/lazy-compilation-prewarm.ts +20 -4
  76. package/src/manifest.ts +186 -100
  77. package/src/mode-plan.ts +367 -0
  78. package/src/modify-browser-manifest.ts +131 -124
  79. package/src/plugin-utils.ts +103 -39
  80. package/src/prerender-build.ts +82 -104
  81. package/src/prerender.ts +23 -24
  82. package/src/react-router-config.ts +72 -26
  83. package/src/route-artifacts.ts +96 -66
  84. package/src/route-chunks.ts +196 -54
  85. package/src/route-component-transform.ts +14 -21
  86. package/src/route-export-pruning.ts +4 -3
  87. package/src/route-imports.ts +100 -0
  88. package/src/route-transform-tasks.ts +39 -25
  89. package/src/route-watch.ts +166 -188
  90. package/src/rsc-dev-server.ts +112 -0
  91. package/src/rsc-prerender.ts +362 -0
  92. package/src/rsc-route-config.ts +175 -0
  93. package/src/rsc-route-exports.ts +67 -0
  94. package/src/rsc-route-transform-loader.ts +65 -0
  95. package/src/rsc-route-transform-registration.ts +145 -0
  96. package/src/rsc-route-transforms.ts +995 -0
  97. package/src/rsc-runtime.d.ts +143 -0
  98. package/src/rsc-support.ts +116 -0
  99. package/src/rsc-virtual-modules.ts +113 -0
  100. package/src/server-build-plan.ts +14 -3
  101. package/src/server-build-resolution.ts +37 -47
  102. package/src/server-utils.ts +21 -35
  103. package/src/ssr-asset-relocation.ts +183 -0
  104. package/src/ssr-externals.ts +8 -26
  105. package/src/templates/entry.rsc.client.tsx +168 -0
  106. package/src/templates/entry.rsc.ssr.tsx +45 -0
  107. package/src/templates/entry.rsc.tsx +80 -0
  108. package/src/typegen.ts +40 -23
  109. package/src/types.ts +39 -1
  110. package/src/warnings/warn-on-client-source-maps.ts +6 -10
@@ -1,7 +1,12 @@
1
1
  import * as Effect from 'effect/Effect';
2
+ import type * as Scope from 'effect/Scope';
2
3
  import type { ReactRouterManifestForDev } from './manifest.js';
3
4
  import type { RouteManifestItem } from './types.js';
4
- import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js';
5
+ import {
6
+ createDelayedPluginTask,
7
+ type PluginEffectRuntime,
8
+ tryPluginPromise,
9
+ } from './effect-runtime.js';
5
10
 
6
11
  const DEFAULT_LAZY_COMPILATION_TRIGGER_PREFIX = '/_rspack/lazy/trigger';
7
12
  const DEFAULT_PREWARM_DELAY_MS = 0;
@@ -9,17 +14,17 @@ const DEFAULT_ROUTE_PREWARM_LIMIT = 8;
9
14
  const PREWARM_FETCH_CONCURRENCY = 8;
10
15
  const PREWARM_TRIGGER_CANDIDATES = 4;
11
16
 
12
- type LazyCompilationPrewarmConfig = {
17
+ export type LazyCompilationPrewarmConfig = {
13
18
  entry: boolean;
14
19
  routeLimit: number;
15
20
  delayMs: number;
16
21
  };
17
22
 
18
- type LazyCompilationPrewarmController = {
23
+ export type LazyCompilationPrewarmController = {
19
24
  setServerOrigin(origin: string): void;
20
25
  setManifest(manifest: ReactRouterManifestForDev | null): void;
21
26
  schedule(): void;
22
- cancelEffect(): Effect.Effect<void, Error, never>;
27
+ cancelEffect(): Effect.Effect<void>;
23
28
  };
24
29
 
25
30
  export type RspackLazyCompilationTriggerClient = {
@@ -221,9 +226,11 @@ const prewarmLazyCompilation = ({
221
226
  });
222
227
 
223
228
  export const createLazyCompilationPrewarmController = ({
229
+ runtime,
224
230
  config,
225
231
  onError,
226
232
  }: {
233
+ runtime: PluginEffectRuntime;
227
234
  config: LazyCompilationPrewarmConfig;
228
235
  onError: (error: Error) => void;
229
236
  }): LazyCompilationPrewarmController => {
@@ -231,6 +238,7 @@ export const createLazyCompilationPrewarmController = ({
231
238
  let manifest: ReactRouterManifestForDev | null = null;
232
239
  let lastPrewarmAssetsKey: string | undefined;
233
240
  const task = createDelayedPluginTask({
241
+ runtime,
234
242
  delayMs: config.delayMs,
235
243
  run: () =>
236
244
  Effect.gen(function* () {
@@ -277,3 +285,11 @@ export const createLazyCompilationPrewarmController = ({
277
285
  },
278
286
  };
279
287
  };
288
+
289
+ export const acquireLazyCompilationPrewarm = (
290
+ options: Parameters<typeof createLazyCompilationPrewarmController>[0]
291
+ ): Effect.Effect<LazyCompilationPrewarmController, never, Scope.Scope> =>
292
+ Effect.acquireRelease(
293
+ Effect.sync(() => createLazyCompilationPrewarmController(options)),
294
+ controller => controller.cancelEffect()
295
+ );
package/src/manifest.ts CHANGED
@@ -3,18 +3,27 @@ import { dirname, isAbsolute, relative, resolve } from 'pathe';
3
3
  import * as Effect from 'effect/Effect';
4
4
  import type { Route, PluginOptions, RouteManifestItem } from './types.js';
5
5
  import { combineURLs, createRouteId } from './plugin-utils.js';
6
- import { SERVER_EXPORTS, CLIENT_EXPORTS } from './constants.js';
6
+ import {
7
+ CLIENT_EXPORTS,
8
+ DEFAULT_JS_DIST_PATH,
9
+ SERVER_EXPORTS,
10
+ } from './constants.js';
7
11
  import {
8
12
  buildManifestChunkValidity,
9
13
  createEmptyRouteChunkByExportName,
10
14
  detectRouteChunksIfEnabled,
11
15
  getRouteChunkEntryName,
16
+ getRouteEntryBaseName,
12
17
  routeChunkExportNames,
13
18
  validateRouteChunks,
14
19
  type RouteChunkCache,
15
20
  type RouteChunkConfig,
21
+ type RouteChunkExportName,
16
22
  } from './route-chunks.js';
17
- import { getRouteModuleAnalysis } from './export-utils.js';
23
+ import {
24
+ getRouteModuleAnalysis,
25
+ type RouteModuleAnalysis,
26
+ } from './export-utils.js';
18
27
  import { getCappedPluginConcurrency } from './concurrency.js';
19
28
  import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
20
29
 
@@ -87,13 +96,38 @@ export function configRoutesToRouteManifestEntries(
87
96
  return routeManifestEntries;
88
97
  }
89
98
 
90
- type RouteChunkManifestOptions = {
99
+ export type RouteChunkManifestOptions = {
91
100
  splitRouteModules?: boolean | 'enforce';
92
101
  rootRouteFile?: string;
93
102
  isBuild?: boolean;
94
103
  cache?: RouteChunkCache;
95
104
  };
96
105
 
106
+ export type RouteModuleAnalysisProvider = (
107
+ routeFilePath: string,
108
+ route: Route
109
+ ) => Promise<RouteModuleAnalysis | undefined>;
110
+
111
+ export type ReactRouterManifestOptions = RouteChunkManifestOptions & {
112
+ routeModuleAnalysis?: RouteModuleAnalysisProvider;
113
+ };
114
+
115
+ export const createReactRouterManifestOptions = ({
116
+ routeChunks,
117
+ routeModuleAnalysis,
118
+ }: {
119
+ routeChunks?: RouteChunkManifestOptions;
120
+ routeModuleAnalysis?: RouteModuleAnalysisProvider;
121
+ }): ReactRouterManifestOptions | undefined => {
122
+ if (!routeChunks && !routeModuleAnalysis) {
123
+ return undefined;
124
+ }
125
+ return {
126
+ ...routeChunks,
127
+ ...(routeModuleAnalysis ? { routeModuleAnalysis } : {}),
128
+ };
129
+ };
130
+
97
131
  export type ReactRouterManifestForDev = {
98
132
  version: string;
99
133
  url: string;
@@ -122,8 +156,10 @@ type ReactRouterManifestStatsEntrypoint = {
122
156
  getFiles?: () => Iterable<string>;
123
157
  };
124
158
 
125
- type ReactRouterManifestStatsLookup<T> = Iterable<[string, T]> & {
126
- get?: (name: string) => T | undefined;
159
+ type ReactRouterManifestStatsLookup<T> = Iterable<
160
+ [string, T | null | undefined]
161
+ > & {
162
+ get?: (name: string) => T | null | undefined;
127
163
  };
128
164
 
129
165
  type ReactRouterManifestStatsCompilation = {
@@ -153,6 +189,9 @@ const collectManifestFilesByName = <T>(
153
189
  const filesByName: Record<string, string[]> = {};
154
190
  if (!names) {
155
191
  for (const [name, item] of items) {
192
+ if (item == null) {
193
+ continue;
194
+ }
156
195
  filesByName[name] = getFiles(name, item);
157
196
  }
158
197
  return filesByName;
@@ -162,7 +201,7 @@ const collectManifestFilesByName = <T>(
162
201
  if (typeof items.get === 'function') {
163
202
  for (const name of names) {
164
203
  const item = items.get(name);
165
- if (!item) {
204
+ if (item == null) {
166
205
  continue;
167
206
  }
168
207
  filesByName[name] = getFiles(name, item);
@@ -178,6 +217,9 @@ const collectManifestFilesByName = <T>(
178
217
  if (!missingNames.has(name)) {
179
218
  continue;
180
219
  }
220
+ if (item == null) {
221
+ continue;
222
+ }
181
223
  filesByName[name] = getFiles(name, item);
182
224
  missingNames.delete(name);
183
225
  if (missingNames.size === 0) {
@@ -236,11 +278,12 @@ type RouteManifestAnalysis = {
236
278
  > | null;
237
279
  };
238
280
 
239
- const DEFAULT_MANIFEST_DIR = 'static/js';
281
+ const DEFAULT_MANIFEST_DIR = DEFAULT_JS_DIST_PATH;
240
282
  const CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/;
241
283
 
242
284
  const createChunkAssetResolver = (
243
- clientStats: ReactRouterManifestStats | undefined
285
+ clientStats: ReactRouterManifestStats | undefined,
286
+ includeEntrypointJs: boolean
244
287
  ): ((chunkName: string) => ChunkAssets) => {
245
288
  const chunkAssetsByName = new Map<string, ChunkAssets>();
246
289
 
@@ -259,24 +302,26 @@ const createChunkAssetResolver = (
259
302
  }
260
303
 
261
304
  const cssAssets = new Set<string>();
262
- const jsAssets: string[] = [];
305
+ const jsAssets = new Set<string>();
263
306
  for (const asset of assets) {
264
307
  if (asset.endsWith('.css')) {
265
308
  cssAssets.add(asset);
266
309
  } else if (asset.endsWith('.js')) {
267
- jsAssets.push(asset);
310
+ jsAssets.add(asset);
268
311
  }
269
312
  }
270
313
  for (const asset of clientStats?.entrypointFilesByName?.[chunkName] ?? []) {
271
314
  if (asset.endsWith('.css')) {
272
315
  cssAssets.add(asset);
316
+ } else if (includeEntrypointJs && asset.endsWith('.js')) {
317
+ jsAssets.add(asset);
273
318
  }
274
319
  }
275
- if (jsAssets.length === 0) {
276
- jsAssets.push(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`);
320
+ if (jsAssets.size === 0) {
321
+ jsAssets.add(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`);
277
322
  }
278
323
 
279
- const result = { js: jsAssets, css: [...cssAssets] };
324
+ const result = { js: [...jsAssets], css: [...cssAssets] };
280
325
  chunkAssetsByName.set(chunkName, result);
281
326
  return result;
282
327
  };
@@ -289,6 +334,8 @@ const analyzeRouteForManifestEffect = ({
289
334
  routeChunkConfig,
290
335
  routeEntryName,
291
336
  routeFilePath,
337
+ route,
338
+ routeModuleAnalysis,
292
339
  }: {
293
340
  discoveredCssAssets: string[];
294
341
  isBuild: boolean;
@@ -296,10 +343,13 @@ const analyzeRouteForManifestEffect = ({
296
343
  routeChunkConfig: RouteChunkConfig | null;
297
344
  routeEntryName: string;
298
345
  routeFilePath: string;
346
+ route: Route;
347
+ routeModuleAnalysis?: RouteModuleAnalysisProvider;
299
348
  }): Effect.Effect<RouteManifestAnalysis, Error, never> =>
300
349
  tryPluginPromise(async () => {
301
350
  const { code, exports: exportNames } =
302
- await getRouteModuleAnalysis(routeFilePath);
351
+ (await routeModuleAnalysis?.(routeFilePath, route)) ??
352
+ (await getRouteModuleAnalysis(routeFilePath));
303
353
  const cssAssets =
304
354
  !isBuild && discoveredCssAssets.length === 0 && CSS_IMPORT_RE.test(code)
305
355
  ? [
@@ -363,152 +413,181 @@ export const getReactRouterManifestPath = ({
363
413
  return `${dir}/manifest-${version}.js`;
364
414
  };
365
415
 
366
- const getManifestVersion = (
367
- fingerprintedValues: { entry: unknown; routes: unknown },
368
- isBuild: boolean
369
- ): string => {
370
- if (!isBuild) {
371
- return String(Math.random());
372
- }
373
- return createHash('md5')
416
+ // The version is derived from the manifest content in every mode. React
417
+ // Router's stale-client detection compares the version the browser loaded with
418
+ // the one the server build was pinned to, and answers a mismatch with a
419
+ // document reload. In development the browser manifest asset is served from
420
+ // the latest web compilation while the server build stays pinned to the
421
+ // compilation it was committed with, so a random per-compilation version made
422
+ // the two disagree whenever a web compilation completed without changing the
423
+ // manifest (for example the hot data revalidation recompile that follows a
424
+ // server change), reloading the document for no reason. Equal content now
425
+ // yields an equal version, and a real manifest change still busts the browser
426
+ // cache through the `?v=` query on the development manifest URL.
427
+ const getManifestVersion = (fingerprintedValues: {
428
+ entry: unknown;
429
+ routes: unknown;
430
+ }): string =>
431
+ createHash('md5')
374
432
  .update(JSON.stringify(fingerprintedValues))
375
433
  .digest('hex')
376
434
  .slice(0, 8);
377
- };
378
-
379
- const getRouteEntryName = (route: Route): string => {
380
- const extensionIndex = route.file.lastIndexOf('.');
381
- return extensionIndex >= 0 ? route.file.slice(0, extensionIndex) : route.file;
382
- };
383
435
 
384
436
  export const getReactRouterManifestChunkNames = (
385
437
  routes: Record<string, Route>,
438
+ appDirectory: string,
386
439
  splitRouteModules: boolean | 'enforce' = false
387
440
  ): Set<string> => {
388
441
  const chunkNames = new Set<string>(['entry.client']);
389
442
  for (const route of Object.values(routes)) {
390
- chunkNames.add(getRouteEntryName(route));
443
+ chunkNames.add(getRouteEntryBaseName(route, appDirectory));
391
444
  if (!splitRouteModules || route.id === 'root') {
392
445
  continue;
393
446
  }
394
447
  for (const exportName of routeChunkExportNames) {
395
- chunkNames.add(getRouteChunkEntryName(route.id, exportName));
448
+ chunkNames.add(getRouteChunkEntryName(route, exportName, appDirectory));
396
449
  }
397
450
  }
398
451
  return chunkNames;
399
452
  };
400
453
 
454
+ const createRouteManifestItem = ({
455
+ route,
456
+ appDirectory,
457
+ assetPrefix,
458
+ jsAssets,
459
+ routeAnalysis,
460
+ getModulePathForChunk,
461
+ getCssAssetsForChunk,
462
+ }: {
463
+ route: Route;
464
+ appDirectory: string;
465
+ assetPrefix: string;
466
+ jsAssets: string[];
467
+ routeAnalysis: RouteManifestAnalysis;
468
+ getModulePathForChunk: (chunkName: string) => string | undefined;
469
+ getCssAssetsForChunk: (chunkName: string) => string[];
470
+ }): RouteManifestItem => {
471
+ const routeChunkMap = routeAnalysis.hasRouteChunkByExportName;
472
+ const chunkModulePath = (exportName: RouteChunkExportName) =>
473
+ routeChunkMap?.[exportName]
474
+ ? getModulePathForChunk(
475
+ getRouteChunkEntryName(route, exportName, appDirectory)
476
+ )
477
+ : undefined;
478
+ const cssAssets = [
479
+ ...routeAnalysis.cssAssets,
480
+ ...routeChunkExportNames.flatMap(exportName =>
481
+ routeChunkMap?.[exportName]
482
+ ? getCssAssetsForChunk(
483
+ getRouteChunkEntryName(route, exportName, appDirectory)
484
+ )
485
+ : []
486
+ ),
487
+ ];
488
+
489
+ return {
490
+ id: route.id,
491
+ parentId: route.parentId,
492
+ path: route.path,
493
+ index: route.index,
494
+ caseSensitive: route.caseSensitive,
495
+ module: combineURLs(assetPrefix, jsAssets[0] || ''),
496
+ clientActionModule: chunkModulePath('clientAction'),
497
+ clientLoaderModule: chunkModulePath('clientLoader'),
498
+ clientMiddlewareModule: chunkModulePath('clientMiddleware'),
499
+ hydrateFallbackModule: chunkModulePath('HydrateFallback'),
500
+ hasAction: routeAnalysis.exports.has(SERVER_EXPORTS.action),
501
+ hasLoader: routeAnalysis.exports.has(SERVER_EXPORTS.loader),
502
+ hasClientAction: routeAnalysis.exports.has(CLIENT_EXPORTS.clientAction),
503
+ hasClientLoader: routeAnalysis.exports.has(CLIENT_EXPORTS.clientLoader),
504
+ hasClientMiddleware: routeAnalysis.exports.has(
505
+ CLIENT_EXPORTS.clientMiddleware
506
+ ),
507
+ hasDefaultExport: routeAnalysis.exports.has('default'),
508
+ hasErrorBoundary: routeAnalysis.exports.has(CLIENT_EXPORTS.ErrorBoundary),
509
+ // `module` is `jsAssets[0]`; exclude it from `imports` so the browser
510
+ // manifest does not list the route's own module twice (upstream excludes
511
+ // the entry chunk's own file from its imports list). Otherwise prefetch
512
+ // link computations produce duplicate modulepreload hrefs.
513
+ imports: jsAssets.slice(1).map(asset => combineURLs(assetPrefix, asset)),
514
+ css: Array.from(new Set(cssAssets)).map(asset =>
515
+ combineURLs(assetPrefix, asset)
516
+ ),
517
+ };
518
+ };
519
+
401
520
  function generateReactRouterManifestForDevEffect(
402
521
  routes: Record<string, Route>,
403
- _options: PluginOptions,
404
522
  clientStats: ReactRouterManifestStats | undefined,
405
523
  context: string,
406
524
  assetPrefix: string,
407
- routeChunkOptions?: RouteChunkManifestOptions
525
+ manifestOptions?: ReactRouterManifestOptions
408
526
  ): Effect.Effect<ReactRouterManifestGenerationResult, Error, never> {
409
527
  return Effect.gen(function* () {
410
528
  const result: Record<string, RouteManifestItem> = {};
411
- const splitRouteModules = routeChunkOptions?.splitRouteModules ?? false;
529
+ const splitRouteModules = manifestOptions?.splitRouteModules ?? false;
412
530
  const enforceSplitRouteModules = splitRouteModules === 'enforce';
413
- const isBuild = routeChunkOptions?.isBuild ?? false;
531
+ const isBuild = manifestOptions?.isBuild ?? false;
414
532
  const routeChunkConfig: RouteChunkConfig | null =
415
- splitRouteModules && routeChunkOptions?.rootRouteFile
533
+ splitRouteModules && manifestOptions?.rootRouteFile
416
534
  ? {
417
535
  splitRouteModules,
418
536
  appDirectory: context,
419
- rootRouteFile: routeChunkOptions.rootRouteFile,
537
+ rootRouteFile: manifestOptions.rootRouteFile,
420
538
  }
421
539
  : null;
422
540
 
423
- const getAssetsForChunk = createChunkAssetResolver(clientStats);
541
+ const getAssetsForChunk = createChunkAssetResolver(clientStats, isBuild);
424
542
  const getModulePathForChunk = (chunkName: string): string | undefined => {
425
543
  const { js: jsAssets } = getAssetsForChunk(chunkName);
426
544
  return jsAssets[0] ? combineURLs(assetPrefix, jsAssets[0]) : undefined;
427
545
  };
546
+ const getCssAssetsForChunk = (chunkName: string): string[] =>
547
+ getAssetsForChunk(chunkName).css;
428
548
 
429
549
  const manifestEntries = yield* Effect.forEach(
430
550
  Object.entries(routes),
431
551
  ([key, route]) =>
432
552
  Effect.gen(function* () {
433
- const routeEntryName = getRouteEntryName(route);
553
+ const routeEntryName = getRouteEntryBaseName(route, context);
434
554
  const { js: jsAssets, css: discoveredCssAssets } =
435
555
  getAssetsForChunk(routeEntryName);
436
556
  const routeFilePath = resolve(context, route.file);
437
557
  const routeAnalysis = yield* analyzeRouteForManifestEffect({
438
558
  discoveredCssAssets,
439
559
  isBuild,
440
- routeChunkCache: routeChunkOptions?.cache,
560
+ routeChunkCache: manifestOptions?.cache,
441
561
  routeChunkConfig,
442
562
  routeEntryName,
443
563
  routeFilePath,
564
+ route,
565
+ routeModuleAnalysis: manifestOptions?.routeModuleAnalysis,
444
566
  });
445
567
 
446
- const hasClientAction = routeAnalysis.exports.has(
447
- CLIENT_EXPORTS.clientAction
448
- );
449
- const hasClientLoader = routeAnalysis.exports.has(
450
- CLIENT_EXPORTS.clientLoader
451
- );
452
- const hasClientMiddleware = routeAnalysis.exports.has(
453
- CLIENT_EXPORTS.clientMiddleware
454
- );
455
- const hasDefaultExport = routeAnalysis.exports.has('default');
456
- const routeChunkMap = routeAnalysis.hasRouteChunkByExportName;
457
-
458
568
  if (isBuild && enforceSplitRouteModules && routeChunkConfig) {
459
569
  validateRouteChunks({
460
570
  config: routeChunkConfig,
461
571
  id: routeFilePath,
462
572
  valid: buildManifestChunkValidity(
463
573
  routeAnalysis.exports,
464
- routeChunkMap ?? createEmptyRouteChunkByExportName()
574
+ routeAnalysis.hasRouteChunkByExportName ??
575
+ createEmptyRouteChunkByExportName()
465
576
  ),
466
577
  });
467
578
  }
468
579
 
469
580
  return [
470
581
  key,
471
- {
472
- id: route.id,
473
- parentId: route.parentId,
474
- path: route.path,
475
- index: route.index,
476
- caseSensitive: route.caseSensitive,
477
- module: combineURLs(assetPrefix, jsAssets[0] || ''),
478
- clientActionModule: routeChunkMap?.clientAction
479
- ? getModulePathForChunk(
480
- getRouteChunkEntryName(route.id, 'clientAction')
481
- )
482
- : undefined,
483
- clientLoaderModule: routeChunkMap?.clientLoader
484
- ? getModulePathForChunk(
485
- getRouteChunkEntryName(route.id, 'clientLoader')
486
- )
487
- : undefined,
488
- clientMiddlewareModule: routeChunkMap?.clientMiddleware
489
- ? getModulePathForChunk(
490
- getRouteChunkEntryName(route.id, 'clientMiddleware')
491
- )
492
- : undefined,
493
- hydrateFallbackModule: routeChunkMap?.HydrateFallback
494
- ? getModulePathForChunk(
495
- getRouteChunkEntryName(route.id, 'HydrateFallback')
496
- )
497
- : undefined,
498
- hasAction: routeAnalysis.exports.has(SERVER_EXPORTS.action),
499
- hasLoader: routeAnalysis.exports.has(SERVER_EXPORTS.loader),
500
- hasClientAction,
501
- hasClientLoader,
502
- hasClientMiddleware,
503
- hasDefaultExport,
504
- hasErrorBoundary: routeAnalysis.exports.has(
505
- CLIENT_EXPORTS.ErrorBoundary
506
- ),
507
- imports: jsAssets.map(asset => combineURLs(assetPrefix, asset)),
508
- css: routeAnalysis.cssAssets.map(asset =>
509
- combineURLs(assetPrefix, asset)
510
- ),
511
- },
582
+ createRouteManifestItem({
583
+ route,
584
+ appDirectory: context,
585
+ assetPrefix,
586
+ jsAssets,
587
+ routeAnalysis,
588
+ getModulePathForChunk,
589
+ getCssAssetsForChunk,
590
+ }),
512
591
  routeAnalysis.routeModuleExports,
513
592
  ] as const;
514
593
  }),
@@ -536,12 +615,17 @@ function generateReactRouterManifestForDevEffect(
536
615
  const fingerprintedValues = {
537
616
  entry: {
538
617
  module: combineURLs(assetPrefix, entryJsAssets[0] || ''),
539
- imports: entryJsAssets.map(asset => combineURLs(assetPrefix, asset)),
618
+ // Exclude the entry's own module (`entryJsAssets[0]`) from imports so it
619
+ // is not listed twice, matching upstream where `imports` holds only the
620
+ // chunk's dependency imports.
621
+ imports: entryJsAssets
622
+ .slice(1)
623
+ .map(asset => combineURLs(assetPrefix, asset)),
540
624
  css: entryCssAssets.map(asset => combineURLs(assetPrefix, asset)),
541
625
  },
542
626
  routes: result,
543
627
  };
544
- const version = getManifestVersion(fingerprintedValues, isBuild);
628
+ const version = getManifestVersion(fingerprintedValues);
545
629
  const manifestPath = getReactRouterManifestPath({
546
630
  version,
547
631
  isBuild,
@@ -550,7 +634,10 @@ function generateReactRouterManifestForDevEffect(
550
634
 
551
635
  const manifest = {
552
636
  version,
553
- url: combineURLs(assetPrefix, manifestPath),
637
+ url: combineURLs(
638
+ assetPrefix,
639
+ isBuild ? manifestPath : `${manifestPath}?v=${version}`
640
+ ),
554
641
  hmr: undefined,
555
642
  entry: fingerprintedValues.entry,
556
643
  sri: undefined,
@@ -566,20 +653,19 @@ function generateReactRouterManifestForDevEffect(
566
653
 
567
654
  export async function generateReactRouterManifestForDev(
568
655
  routes: Record<string, Route>,
569
- options: PluginOptions,
656
+ _options: PluginOptions,
570
657
  clientStats: ReactRouterManifestStats | undefined,
571
658
  context: string,
572
659
  assetPrefix = '/',
573
- routeChunkOptions?: RouteChunkManifestOptions
660
+ manifestOptions?: ReactRouterManifestOptions
574
661
  ): Promise<ReactRouterManifestGenerationResult> {
575
662
  return runPluginEffect(
576
663
  generateReactRouterManifestForDevEffect(
577
664
  routes,
578
- options,
579
665
  clientStats,
580
666
  context,
581
667
  assetPrefix,
582
- routeChunkOptions
668
+ manifestOptions
583
669
  )
584
670
  );
585
671
  }