rsbuild-plugin-react-router 0.2.0 → 0.3.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 (105) hide show
  1. package/README.md +213 -201
  2. package/dist/451.js +1103 -0
  3. package/dist/bounded-cache.d.ts +1 -0
  4. package/dist/build-manifest.d.ts +7 -4
  5. package/dist/build-output-transforms.d.ts +30 -0
  6. package/dist/concurrency.d.ts +3 -0
  7. package/dist/config-imports.d.ts +10 -0
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/dev-background-resources.d.ts +38 -0
  10. package/dist/dev-generation.d.ts +25 -0
  11. package/dist/dev-runtime-artifacts.d.ts +47 -0
  12. package/dist/dev-runtime-compilation.d.ts +47 -0
  13. package/dist/dev-runtime-controller.d.ts +14 -0
  14. package/dist/dev-runtime-session.d.ts +34 -0
  15. package/dist/dev-server.d.ts +16 -2
  16. package/dist/effect-runtime.d.ts +18 -0
  17. package/dist/export-utils.d.ts +15 -7
  18. package/dist/index.cjs +13775 -1412
  19. package/dist/index.d.ts +8 -1
  20. package/dist/index.js +9429 -1501
  21. package/dist/lazy-compilation-prewarm.d.ts +25 -0
  22. package/dist/lazy-compilation.d.ts +6 -0
  23. package/dist/manifest.d.ts +48 -10
  24. package/dist/modify-browser-manifest.d.ts +30 -13
  25. package/dist/parallel-route-transform-protocol.d.ts +25 -0
  26. package/dist/parallel-route-transform-worker.d.ts +1 -0
  27. package/dist/parallel-route-transform-worker.js +38 -0
  28. package/dist/parallel-route-transforms.d.ts +29 -0
  29. package/dist/performance.d.ts +26 -0
  30. package/dist/plugin-utils.d.ts +2 -12
  31. package/dist/prerender-build.d.ts +36 -0
  32. package/dist/prerender.d.ts +27 -2
  33. package/dist/react-router-config.d.ts +15 -5
  34. package/dist/route-artifacts.d.ts +33 -0
  35. package/dist/route-ast.d.ts +21 -0
  36. package/dist/route-chunks.d.ts +9 -1
  37. package/dist/route-component-transform.d.ts +5 -0
  38. package/dist/route-export-pruning.d.ts +6 -0
  39. package/dist/route-export-resolution.d.ts +5 -0
  40. package/dist/route-transform-tasks.d.ts +47 -0
  41. package/dist/route-watch.d.ts +27 -0
  42. package/dist/server-build-plan.d.ts +22 -0
  43. package/dist/server-build-resolution.d.ts +3 -0
  44. package/dist/server-utils.d.ts +3 -2
  45. package/dist/ssr-externals.d.ts +1 -0
  46. package/dist/templates/entry.server.cjs +3 -3
  47. package/dist/templates/entry.server.js +3 -3
  48. package/dist/typegen.d.ts +15 -0
  49. package/dist/types.d.ts +41 -14
  50. package/dist/virtual-modules.d.ts +2 -0
  51. package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
  52. package/dist/yuku.d.ts +15 -0
  53. package/package.json +24 -19
  54. package/src/bounded-cache.ts +18 -0
  55. package/src/build-manifest.ts +205 -0
  56. package/src/build-output-transforms.ts +273 -0
  57. package/src/concurrency.ts +15 -0
  58. package/src/config-imports.ts +83 -0
  59. package/src/constants.ts +91 -0
  60. package/src/dev-background-resources.ts +255 -0
  61. package/src/dev-generation.ts +690 -0
  62. package/src/dev-runtime-artifacts.ts +239 -0
  63. package/src/dev-runtime-compilation.ts +171 -0
  64. package/src/dev-runtime-controller.ts +542 -0
  65. package/src/dev-runtime-session.ts +191 -0
  66. package/src/dev-server.ts +88 -0
  67. package/src/effect-runtime.ts +130 -0
  68. package/src/export-utils.ts +278 -0
  69. package/src/index.ts +990 -0
  70. package/src/lazy-compilation-prewarm.ts +279 -0
  71. package/src/lazy-compilation.ts +101 -0
  72. package/src/manifest.ts +591 -0
  73. package/src/modify-browser-manifest.ts +246 -0
  74. package/src/parallel-route-transform-protocol.ts +35 -0
  75. package/src/parallel-route-transform-worker.ts +82 -0
  76. package/src/parallel-route-transforms.ts +458 -0
  77. package/src/performance.ts +254 -0
  78. package/src/plugin-utils.ts +82 -0
  79. package/src/prerender-build.ts +687 -0
  80. package/src/prerender.ts +349 -0
  81. package/src/react-router-config.ts +245 -0
  82. package/src/route-artifacts.ts +155 -0
  83. package/src/route-ast.ts +163 -0
  84. package/src/route-chunks.ts +857 -0
  85. package/src/route-component-transform.ts +314 -0
  86. package/src/route-config.ts +106 -0
  87. package/src/route-export-pruning.ts +668 -0
  88. package/src/route-export-resolution.ts +329 -0
  89. package/src/route-transform-tasks.ts +249 -0
  90. package/src/route-watch.ts +357 -0
  91. package/src/server-build-plan.ts +91 -0
  92. package/src/server-build-resolution.ts +131 -0
  93. package/src/server-utils.ts +111 -0
  94. package/src/ssr-externals.ts +59 -0
  95. package/src/templates/context.ts +12 -0
  96. package/src/templates/entry.client.tsx +12 -0
  97. package/src/templates/entry.server.tsx +76 -0
  98. package/src/typegen.ts +178 -0
  99. package/src/types.ts +92 -0
  100. package/src/validation/validate-plugin-order.ts +76 -0
  101. package/src/virtual-modules.ts +30 -0
  102. package/src/warnings/warn-on-client-source-maps.ts +96 -0
  103. package/src/yuku.ts +67 -0
  104. package/dist/0~rslib-runtime.js +0 -16
  105. package/dist/babel.d.ts +0 -8
@@ -0,0 +1,591 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { dirname, isAbsolute, relative, resolve } from 'pathe';
3
+ import * as Effect from 'effect/Effect';
4
+ import type { Route, PluginOptions, RouteManifestItem } from './types.js';
5
+ import { combineURLs, createRouteId } from './plugin-utils.js';
6
+ import { SERVER_EXPORTS, CLIENT_EXPORTS } from './constants.js';
7
+ import {
8
+ buildManifestChunkValidity,
9
+ createEmptyRouteChunkByExportName,
10
+ detectRouteChunksIfEnabled,
11
+ getRouteChunkEntryName,
12
+ routeChunkExportNames,
13
+ validateRouteChunks,
14
+ type RouteChunkCache,
15
+ type RouteChunkConfig,
16
+ } from './route-chunks.js';
17
+ import { getRouteModuleAnalysis } from './export-utils.js';
18
+ import { getCappedPluginConcurrency } from './concurrency.js';
19
+ import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
20
+
21
+ const ROUTE_ANALYSIS_CONCURRENCY = getCappedPluginConcurrency();
22
+
23
+ /**
24
+ * Structural equivalent of `RouteConfigEntry` from `@react-router/dev/routes`,
25
+ * inlined so the public declaration files do not reference a devDependency
26
+ * (which would fail to resolve for consumers with `skipLibCheck: false`).
27
+ */
28
+ export interface RouteConfigEntry {
29
+ id?: string;
30
+ path?: string;
31
+ index?: boolean;
32
+ caseSensitive?: boolean;
33
+ file: string;
34
+ children?: RouteConfigEntry[];
35
+ }
36
+
37
+ export function configRoutesToRouteManifest(
38
+ appDirectory: string,
39
+ routes: RouteConfigEntry[],
40
+ rootId = 'root'
41
+ ): Record<string, Route> {
42
+ return Object.fromEntries(
43
+ configRoutesToRouteManifestEntries(appDirectory, routes, rootId)
44
+ );
45
+ }
46
+
47
+ export function configRoutesToRouteManifestEntries(
48
+ appDirectory: string,
49
+ routes: RouteConfigEntry[],
50
+ rootId = 'root'
51
+ ): Array<[string, Route]> {
52
+ const routeManifestEntries: Array<[string, Route]> = [];
53
+ const routeIds = new Set<string>();
54
+
55
+ function walk(route: RouteConfigEntry, parentId: string) {
56
+ const id = route.id || createRouteId(route.file);
57
+ const manifestItem = {
58
+ id,
59
+ parentId,
60
+ file: isAbsolute(route.file)
61
+ ? relative(appDirectory, route.file)
62
+ : route.file,
63
+ path: route.path,
64
+ index: route.index,
65
+ caseSensitive: route.caseSensitive,
66
+ };
67
+
68
+ if (routeIds.has(id)) {
69
+ throw new Error(
70
+ `Unable to define routes with duplicate route id: "${id}"`
71
+ );
72
+ }
73
+ routeIds.add(id);
74
+ routeManifestEntries.push([id, manifestItem]);
75
+
76
+ if (route.children) {
77
+ for (const child of route.children) {
78
+ walk(child, id);
79
+ }
80
+ }
81
+ }
82
+
83
+ for (const route of routes) {
84
+ walk(route, rootId);
85
+ }
86
+
87
+ return routeManifestEntries;
88
+ }
89
+
90
+ type RouteChunkManifestOptions = {
91
+ splitRouteModules?: boolean | 'enforce';
92
+ rootRouteFile?: string;
93
+ isBuild?: boolean;
94
+ cache?: RouteChunkCache;
95
+ };
96
+
97
+ export type ReactRouterManifestForDev = {
98
+ version: string;
99
+ url: string;
100
+ hmr?: {
101
+ runtime: string;
102
+ };
103
+ entry: {
104
+ module: string;
105
+ imports: string[];
106
+ css: string[];
107
+ };
108
+ sri?: Record<string, string> | true;
109
+ routes: Record<string, RouteManifestItem>;
110
+ };
111
+
112
+ export type ReactRouterManifestStats = {
113
+ assetsByChunkName?: Record<string, string[]>;
114
+ entrypointFilesByName?: Record<string, string[]>;
115
+ };
116
+
117
+ type ReactRouterManifestStatsChunk = {
118
+ files?: Iterable<string>;
119
+ };
120
+
121
+ type ReactRouterManifestStatsEntrypoint = {
122
+ getFiles?: () => Iterable<string>;
123
+ };
124
+
125
+ type ReactRouterManifestStatsLookup<T> = Iterable<[string, T]> & {
126
+ get?: (name: string) => T | undefined;
127
+ };
128
+
129
+ type ReactRouterManifestStatsCompilation = {
130
+ namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
131
+ entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
132
+ };
133
+
134
+ const orderChunkFiles = (chunkName: string, files: string[]): string[] => {
135
+ const ownChunkAsset = `${chunkName}.js`;
136
+ const ownFileIndex = files.findIndex(file => file.endsWith(ownChunkAsset));
137
+ if (ownFileIndex <= 0) {
138
+ return files;
139
+ }
140
+
141
+ return [
142
+ files[ownFileIndex],
143
+ ...files.slice(0, ownFileIndex),
144
+ ...files.slice(ownFileIndex + 1),
145
+ ];
146
+ };
147
+
148
+ const collectManifestFilesByName = <T>(
149
+ items: ReactRouterManifestStatsLookup<T>,
150
+ names: ReadonlySet<string> | undefined,
151
+ getFiles: (name: string, item: T) => string[]
152
+ ): Record<string, string[]> => {
153
+ const filesByName: Record<string, string[]> = {};
154
+ if (!names) {
155
+ for (const [name, item] of items) {
156
+ filesByName[name] = getFiles(name, item);
157
+ }
158
+ return filesByName;
159
+ }
160
+
161
+ const missingNames = new Set(names);
162
+ if (typeof items.get === 'function') {
163
+ for (const name of names) {
164
+ const item = items.get(name);
165
+ if (!item) {
166
+ continue;
167
+ }
168
+ filesByName[name] = getFiles(name, item);
169
+ missingNames.delete(name);
170
+ }
171
+ }
172
+
173
+ if (missingNames.size === 0) {
174
+ return filesByName;
175
+ }
176
+
177
+ for (const [name, item] of items) {
178
+ if (!missingNames.has(name)) {
179
+ continue;
180
+ }
181
+ filesByName[name] = getFiles(name, item);
182
+ missingNames.delete(name);
183
+ if (missingNames.size === 0) {
184
+ break;
185
+ }
186
+ }
187
+
188
+ return filesByName;
189
+ };
190
+
191
+ export const createReactRouterManifestStats = (
192
+ compilation: ReactRouterManifestStatsCompilation | undefined,
193
+ chunkNames?: ReadonlySet<string>
194
+ ): ReactRouterManifestStats | undefined => {
195
+ if (!compilation) {
196
+ return undefined;
197
+ }
198
+
199
+ const assetsByChunkName = collectManifestFilesByName(
200
+ compilation.namedChunks,
201
+ chunkNames,
202
+ (chunkName, chunk) =>
203
+ orderChunkFiles(chunkName, Array.from(chunk.files ?? []))
204
+ );
205
+ const entrypointFilesByName = compilation.entrypoints
206
+ ? collectManifestFilesByName(
207
+ compilation.entrypoints,
208
+ chunkNames,
209
+ (_name, entrypoint) => Array.from(entrypoint.getFiles?.() ?? [])
210
+ )
211
+ : {};
212
+
213
+ return Object.keys(entrypointFilesByName).length > 0
214
+ ? { assetsByChunkName, entrypointFilesByName }
215
+ : { assetsByChunkName };
216
+ };
217
+
218
+ export type RouteManifestModuleExports = Record<string, readonly string[]>;
219
+
220
+ export type ReactRouterManifestGenerationResult = {
221
+ manifest: ReactRouterManifestForDev;
222
+ moduleExportsByRouteId: RouteManifestModuleExports;
223
+ };
224
+
225
+ type ChunkAssets = {
226
+ js: string[];
227
+ css: string[];
228
+ };
229
+
230
+ type RouteManifestAnalysis = {
231
+ cssAssets: string[];
232
+ exports: Set<string>;
233
+ routeModuleExports: readonly string[];
234
+ hasRouteChunkByExportName: ReturnType<
235
+ typeof createEmptyRouteChunkByExportName
236
+ > | null;
237
+ };
238
+
239
+ const DEFAULT_MANIFEST_DIR = 'static/js';
240
+ const CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/;
241
+
242
+ const createChunkAssetResolver = (
243
+ clientStats: ReactRouterManifestStats | undefined
244
+ ): ((chunkName: string) => ChunkAssets) => {
245
+ const chunkAssetsByName = new Map<string, ChunkAssets>();
246
+
247
+ return (chunkName: string): ChunkAssets => {
248
+ const cached = chunkAssetsByName.get(chunkName);
249
+ if (cached) {
250
+ return cached;
251
+ }
252
+
253
+ const assets = clientStats?.assetsByChunkName?.[chunkName];
254
+ if (!assets) {
255
+ const fallback = `${DEFAULT_MANIFEST_DIR}/${chunkName}.js`;
256
+ const result = { js: [fallback], css: [] };
257
+ chunkAssetsByName.set(chunkName, result);
258
+ return result;
259
+ }
260
+
261
+ const cssAssets = new Set<string>();
262
+ const jsAssets: string[] = [];
263
+ for (const asset of assets) {
264
+ if (asset.endsWith('.css')) {
265
+ cssAssets.add(asset);
266
+ } else if (asset.endsWith('.js')) {
267
+ jsAssets.push(asset);
268
+ }
269
+ }
270
+ for (const asset of clientStats?.entrypointFilesByName?.[chunkName] ?? []) {
271
+ if (asset.endsWith('.css')) {
272
+ cssAssets.add(asset);
273
+ }
274
+ }
275
+ if (jsAssets.length === 0) {
276
+ jsAssets.push(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`);
277
+ }
278
+
279
+ const result = { js: jsAssets, css: [...cssAssets] };
280
+ chunkAssetsByName.set(chunkName, result);
281
+ return result;
282
+ };
283
+ };
284
+
285
+ const analyzeRouteForManifestEffect = ({
286
+ discoveredCssAssets,
287
+ isBuild,
288
+ routeChunkCache,
289
+ routeChunkConfig,
290
+ routeEntryName,
291
+ routeFilePath,
292
+ }: {
293
+ discoveredCssAssets: string[];
294
+ isBuild: boolean;
295
+ routeChunkCache: RouteChunkCache | undefined;
296
+ routeChunkConfig: RouteChunkConfig | null;
297
+ routeEntryName: string;
298
+ routeFilePath: string;
299
+ }): Effect.Effect<RouteManifestAnalysis, Error, never> =>
300
+ tryPluginPromise(async () => {
301
+ const { code, exports: exportNames } =
302
+ await getRouteModuleAnalysis(routeFilePath);
303
+ const cssAssets =
304
+ !isBuild && discoveredCssAssets.length === 0 && CSS_IMPORT_RE.test(code)
305
+ ? [
306
+ `${DEFAULT_MANIFEST_DIR.replace('/js', '/css')}/${routeEntryName}.css`,
307
+ ]
308
+ : discoveredCssAssets;
309
+ const chunkInfo =
310
+ isBuild && routeChunkConfig
311
+ ? await detectRouteChunksIfEnabled(
312
+ routeChunkCache,
313
+ routeChunkConfig,
314
+ routeFilePath,
315
+ code
316
+ )
317
+ : null;
318
+
319
+ return {
320
+ cssAssets,
321
+ exports: new Set(exportNames),
322
+ routeModuleExports: exportNames,
323
+ hasRouteChunkByExportName: chunkInfo?.hasRouteChunkByExportName ?? null,
324
+ };
325
+ }).pipe(
326
+ Effect.catchAll(error => {
327
+ if (isBuild) {
328
+ return Effect.fail(error);
329
+ }
330
+ return Effect.sync(() => {
331
+ console.error(`Failed to analyze route file ${routeFilePath}:`, error);
332
+ return {
333
+ cssAssets: discoveredCssAssets,
334
+ exports: new Set<string>(),
335
+ routeModuleExports: [],
336
+ hasRouteChunkByExportName: null,
337
+ };
338
+ });
339
+ })
340
+ );
341
+
342
+ const getManifestDirFromEntryAsset = (entryModulePath?: string): string => {
343
+ if (!entryModulePath) {
344
+ return DEFAULT_MANIFEST_DIR;
345
+ }
346
+ const dir = dirname(entryModulePath);
347
+ return dir === '.' ? DEFAULT_MANIFEST_DIR : dir;
348
+ };
349
+
350
+ export const getReactRouterManifestPath = ({
351
+ version,
352
+ isBuild,
353
+ entryModulePath,
354
+ }: {
355
+ version: string;
356
+ isBuild: boolean;
357
+ entryModulePath?: string;
358
+ }): string => {
359
+ if (!isBuild) {
360
+ return 'static/js/virtual/react-router/browser-manifest.js';
361
+ }
362
+ const dir = getManifestDirFromEntryAsset(entryModulePath);
363
+ return `${dir}/manifest-${version}.js`;
364
+ };
365
+
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')
374
+ .update(JSON.stringify(fingerprintedValues))
375
+ .digest('hex')
376
+ .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
+
384
+ export const getReactRouterManifestChunkNames = (
385
+ routes: Record<string, Route>,
386
+ splitRouteModules: boolean | 'enforce' = false
387
+ ): Set<string> => {
388
+ const chunkNames = new Set<string>(['entry.client']);
389
+ for (const route of Object.values(routes)) {
390
+ chunkNames.add(getRouteEntryName(route));
391
+ if (!splitRouteModules || route.id === 'root') {
392
+ continue;
393
+ }
394
+ for (const exportName of routeChunkExportNames) {
395
+ chunkNames.add(getRouteChunkEntryName(route.id, exportName));
396
+ }
397
+ }
398
+ return chunkNames;
399
+ };
400
+
401
+ function generateReactRouterManifestForDevEffect(
402
+ routes: Record<string, Route>,
403
+ _options: PluginOptions,
404
+ clientStats: ReactRouterManifestStats | undefined,
405
+ context: string,
406
+ assetPrefix: string,
407
+ routeChunkOptions?: RouteChunkManifestOptions
408
+ ): Effect.Effect<ReactRouterManifestGenerationResult, Error, never> {
409
+ return Effect.gen(function* () {
410
+ const result: Record<string, RouteManifestItem> = {};
411
+ const splitRouteModules = routeChunkOptions?.splitRouteModules ?? false;
412
+ const enforceSplitRouteModules = splitRouteModules === 'enforce';
413
+ const isBuild = routeChunkOptions?.isBuild ?? false;
414
+ const routeChunkConfig: RouteChunkConfig | null =
415
+ splitRouteModules && routeChunkOptions?.rootRouteFile
416
+ ? {
417
+ splitRouteModules,
418
+ appDirectory: context,
419
+ rootRouteFile: routeChunkOptions.rootRouteFile,
420
+ }
421
+ : null;
422
+
423
+ const getAssetsForChunk = createChunkAssetResolver(clientStats);
424
+ const getModulePathForChunk = (chunkName: string): string | undefined => {
425
+ const { js: jsAssets } = getAssetsForChunk(chunkName);
426
+ return jsAssets[0] ? combineURLs(assetPrefix, jsAssets[0]) : undefined;
427
+ };
428
+
429
+ const manifestEntries = yield* Effect.forEach(
430
+ Object.entries(routes),
431
+ ([key, route]) =>
432
+ Effect.gen(function* () {
433
+ const routeEntryName = getRouteEntryName(route);
434
+ const { js: jsAssets, css: discoveredCssAssets } =
435
+ getAssetsForChunk(routeEntryName);
436
+ const routeFilePath = resolve(context, route.file);
437
+ const routeAnalysis = yield* analyzeRouteForManifestEffect({
438
+ discoveredCssAssets,
439
+ isBuild,
440
+ routeChunkCache: routeChunkOptions?.cache,
441
+ routeChunkConfig,
442
+ routeEntryName,
443
+ routeFilePath,
444
+ });
445
+
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
+ if (isBuild && enforceSplitRouteModules && routeChunkConfig) {
459
+ validateRouteChunks({
460
+ config: routeChunkConfig,
461
+ id: routeFilePath,
462
+ valid: buildManifestChunkValidity(
463
+ routeAnalysis.exports,
464
+ routeChunkMap ?? createEmptyRouteChunkByExportName()
465
+ ),
466
+ });
467
+ }
468
+
469
+ return [
470
+ 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
+ },
512
+ routeAnalysis.routeModuleExports,
513
+ ] as const;
514
+ }),
515
+ {
516
+ concurrency: Math.max(
517
+ 1,
518
+ Math.min(ROUTE_ANALYSIS_CONCURRENCY, Object.keys(routes).length)
519
+ ),
520
+ }
521
+ );
522
+
523
+ const routeModuleExportsByRouteId: RouteManifestModuleExports = {};
524
+ for (const [
525
+ key,
526
+ routeManifestItem,
527
+ routeModuleExports,
528
+ ] of manifestEntries) {
529
+ result[key] = routeManifestItem;
530
+ routeModuleExportsByRouteId[key] = routeModuleExports;
531
+ }
532
+
533
+ const { js: entryJsAssets, css: entryCssAssets } =
534
+ getAssetsForChunk('entry.client');
535
+
536
+ const fingerprintedValues = {
537
+ entry: {
538
+ module: combineURLs(assetPrefix, entryJsAssets[0] || ''),
539
+ imports: entryJsAssets.map(asset => combineURLs(assetPrefix, asset)),
540
+ css: entryCssAssets.map(asset => combineURLs(assetPrefix, asset)),
541
+ },
542
+ routes: result,
543
+ };
544
+ const version = getManifestVersion(fingerprintedValues, isBuild);
545
+ const manifestPath = getReactRouterManifestPath({
546
+ version,
547
+ isBuild,
548
+ entryModulePath: entryJsAssets[0],
549
+ });
550
+
551
+ const manifest = {
552
+ version,
553
+ url: combineURLs(assetPrefix, manifestPath),
554
+ hmr: undefined,
555
+ entry: fingerprintedValues.entry,
556
+ sri: undefined,
557
+ routes: result,
558
+ };
559
+
560
+ return {
561
+ manifest,
562
+ moduleExportsByRouteId: routeModuleExportsByRouteId,
563
+ };
564
+ });
565
+ }
566
+
567
+ export async function generateReactRouterManifestForDev(
568
+ routes: Record<string, Route>,
569
+ options: PluginOptions,
570
+ clientStats: ReactRouterManifestStats | undefined,
571
+ context: string,
572
+ assetPrefix = '/',
573
+ routeChunkOptions?: RouteChunkManifestOptions
574
+ ): Promise<ReactRouterManifestGenerationResult> {
575
+ return runPluginEffect(
576
+ generateReactRouterManifestForDevEffect(
577
+ routes,
578
+ options,
579
+ clientStats,
580
+ context,
581
+ assetPrefix,
582
+ routeChunkOptions
583
+ )
584
+ );
585
+ }
586
+
587
+ export async function getReactRouterManifestForDev(
588
+ ...args: Parameters<typeof generateReactRouterManifestForDev>
589
+ ): Promise<ReactRouterManifestForDev> {
590
+ return (await generateReactRouterManifestForDev(...args)).manifest;
591
+ }