@reckona/mreact-router 0.0.208 → 0.0.210

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 (64) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/cloudflare.d.ts +3 -3
  3. package/dist/adapters/cloudflare.d.ts.map +1 -1
  4. package/dist/adapters/cloudflare.js +11 -4
  5. package/dist/adapters/cloudflare.js.map +1 -1
  6. package/dist/boundaries.d.ts +37 -1
  7. package/dist/boundaries.d.ts.map +1 -1
  8. package/dist/boundaries.js +125 -4
  9. package/dist/boundaries.js.map +1 -1
  10. package/dist/build.d.ts +6 -0
  11. package/dist/build.d.ts.map +1 -1
  12. package/dist/build.js +265 -24
  13. package/dist/build.js.map +1 -1
  14. package/dist/bundle-pipeline.d.ts +1 -0
  15. package/dist/bundle-pipeline.d.ts.map +1 -1
  16. package/dist/bundle-pipeline.js +4 -1
  17. package/dist/bundle-pipeline.js.map +1 -1
  18. package/dist/client.d.ts +1 -0
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +15 -11
  21. package/dist/client.js.map +1 -1
  22. package/dist/config.d.ts +7 -0
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +17 -0
  25. package/dist/config.js.map +1 -1
  26. package/dist/index.d.ts +5 -5
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/reactive-devtools-stub.d.ts +3 -0
  31. package/dist/reactive-devtools-stub.d.ts.map +1 -0
  32. package/dist/reactive-devtools-stub.js +10 -0
  33. package/dist/reactive-devtools-stub.js.map +1 -0
  34. package/dist/render.d.ts.map +1 -1
  35. package/dist/render.js +23 -23
  36. package/dist/render.js.map +1 -1
  37. package/dist/request-header-tracking.d.ts +19 -0
  38. package/dist/request-header-tracking.d.ts.map +1 -1
  39. package/dist/request-header-tracking.js +48 -0
  40. package/dist/request-header-tracking.js.map +1 -1
  41. package/dist/tailwind-source.d.ts.map +1 -1
  42. package/dist/tailwind-source.js +147 -1
  43. package/dist/tailwind-source.js.map +1 -1
  44. package/dist/types.d.ts +14 -2
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/types.js.map +1 -1
  47. package/dist/vite.d.ts +1 -0
  48. package/dist/vite.d.ts.map +1 -1
  49. package/dist/vite.js +2 -7
  50. package/dist/vite.js.map +1 -1
  51. package/package.json +11 -11
  52. package/src/adapters/cloudflare.ts +14 -5
  53. package/src/boundaries.ts +224 -4
  54. package/src/build.ts +379 -24
  55. package/src/bundle-pipeline.ts +9 -4
  56. package/src/client.ts +19 -12
  57. package/src/config.ts +32 -0
  58. package/src/index.ts +10 -0
  59. package/src/reactive-devtools-stub.ts +9 -0
  60. package/src/render.ts +30 -16
  61. package/src/request-header-tracking.ts +61 -0
  62. package/src/tailwind-source.ts +164 -1
  63. package/src/types.ts +15 -2
  64. package/src/vite.ts +3 -7
package/dist/build.js CHANGED
@@ -5,10 +5,11 @@ import { builtinModules } from "node:module";
5
5
  import { availableParallelism } from "node:os";
6
6
  import { dirname, join, relative, resolve, sep } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
+ import { gzipSync } from "node:zlib";
8
9
  import { collectStaticImportReferences, collectTopLevelValueExportNames, formatDiagnostic, hasModuleDirective, transform, } from "@reckona/mreact-compiler";
9
10
  import { transformCompilerModuleContext } from "@reckona/mreact-compiler/internal";
10
11
  import { compilerModuleContextForSource, collectClientRouteReferences, createClientRouteInferenceCache, detectAnchorElementUsage, detectClientNavigationOverride, formatClientRouteInferenceDiagnostic, inferClientRouteModule, navigationRuntimeLinkDisabledDiagnostic, resolveNavigationRuntime, } from "./client-route-inference.js";
11
- import { createBoundaryReport } from "./boundaries.js";
12
+ import { createBoundaryReport, validateBoundaryExecutionContracts, } from "./boundaries.js";
12
13
  import { buildClientRouteBatchOutput, buildNavigationRuntimeBundle, clientScriptForPath, routeIdForPath, routeMarkerParts, } from "./navigation-runtime.js";
13
14
  import { COMPAT_VENDOR_PLACEHOLDER_PREFIX, bundleAppRouterSourceModule, fileImportMetaUrlPlugin, importAppRouterSourceModule, resolveCompatVendorEntryFiles, sourceReferencesCompatVendorSpecifier, } from "./module-runner.js";
14
15
  import { dehydrateOptionsFromModule } from "./dehydrate-policy.js";
@@ -127,7 +128,7 @@ async function buildAppWithResolvedProject(options, project) {
127
128
  routes,
128
129
  vitePlugins,
129
130
  }));
130
- options.onBoundaryReport?.(sourceAnalysis.boundaryReport);
131
+ validateBoundaryExecutionContracts(sourceAnalysis.boundaryReport, project.executionContracts);
131
132
  if (shouldTrackBuildPhases === false) {
132
133
  await validateProductionRoutes({
133
134
  clientRouteInferenceCache: serverClientRouteInferenceCache,
@@ -158,6 +159,15 @@ async function buildAppWithResolvedProject(options, project) {
158
159
  fingerprint: incrementalBuildFingerprint,
159
160
  outDir: options.outDir,
160
161
  }))) {
162
+ if (options.onBoundaryReport !== undefined) {
163
+ const clientManifest = await readJsonBuildOutput(join(options.outDir, "client", "manifest.json"));
164
+ options.onBoundaryReport(await boundaryReportWithArtifactCosts({
165
+ boundaryCost: options.boundaryCost,
166
+ clientDir,
167
+ manifest: clientManifest,
168
+ report: sourceAnalysis.boundaryReport,
169
+ }));
170
+ }
161
171
  return { routes };
162
172
  }
163
173
  if (shouldTrackBuildPhases === false) {
@@ -404,6 +414,7 @@ async function buildAppWithResolvedProject(options, project) {
404
414
  };
405
415
  const clientManifest = {
406
416
  ...(clientManifestAssets.length === 0 ? {} : { assets: clientManifestAssets }),
417
+ ...(clientBundle.chunks.length === 0 ? {} : { chunks: clientBundle.chunks }),
407
418
  ...(publicAssets.length === 0 ? {} : { publicAssets }),
408
419
  routes: clientManifestRoutes,
409
420
  ...(clientBundle.styles.length === 0 ? {} : { styles: clientBundle.styles }),
@@ -467,8 +478,216 @@ async function buildAppWithResolvedProject(options, project) {
467
478
  if (incrementalBuildFingerprint !== undefined) {
468
479
  await writeIncrementalBuildCacheManifest(options.outDir, incrementalBuildFingerprint);
469
480
  }
481
+ if (options.onBoundaryReport !== undefined) {
482
+ options.onBoundaryReport(await boundaryReportWithArtifactCosts({
483
+ boundaryCost: options.boundaryCost,
484
+ clientDir,
485
+ manifest: clientManifest,
486
+ report: sourceAnalysis.boundaryReport,
487
+ }));
488
+ }
470
489
  return { routes };
471
490
  }
491
+ async function boundaryReportWithArtifactCosts(options) {
492
+ if (options.manifest === undefined) {
493
+ return {
494
+ ...options.report,
495
+ routes: options.report.routes.map((route) => ({
496
+ ...route,
497
+ cost: {
498
+ reason: "Production client manifest is unavailable.",
499
+ status: "unavailable",
500
+ },
501
+ })),
502
+ };
503
+ }
504
+ const manifest = options.manifest;
505
+ const chunks = new Map((manifest.chunks ?? []).map((chunk) => [chunk.file, chunk]));
506
+ const routeClosures = options.report.routes.map((route) => {
507
+ const manifestRoute = manifest.routes.find((candidate) => candidate.path === route.path);
508
+ if (manifestRoute === undefined) {
509
+ return { initial: undefined, navigation: undefined, route };
510
+ }
511
+ const initial = clientArtifactStaticClosure(manifestRoute, chunks);
512
+ const navigation = new Set(initial);
513
+ addFetchedDynamicImportClosures({
514
+ chunks,
515
+ fetchedDynamicImports: options.boundaryCost?.fetchedDynamicImports?.[route.path] ?? [],
516
+ navigation,
517
+ routeDynamicImports: manifestRoute.dynamicImports ?? [],
518
+ });
519
+ return { initial, navigation, route };
520
+ });
521
+ const availableClosures = routeClosures.flatMap((entry) => entry.initial === undefined ? [] : [entry.initial]);
522
+ const sharedPaths = intersectionOfPathSets(availableClosures);
523
+ const allPaths = new Set();
524
+ for (const entry of routeClosures) {
525
+ for (const path of entry.navigation ?? []) {
526
+ allPaths.add(path);
527
+ }
528
+ }
529
+ const measurements = await measureClientArtifactPaths(options.clientDir, allPaths);
530
+ return {
531
+ ...options.report,
532
+ routes: routeClosures.map((entry) => {
533
+ if (entry.initial === undefined || entry.navigation === undefined) {
534
+ return {
535
+ ...entry.route,
536
+ cost: {
537
+ reason: "No client route artifact was generated for this route.",
538
+ status: "unavailable",
539
+ },
540
+ };
541
+ }
542
+ const routePaths = new Set(entry.navigation);
543
+ const unavailable = [...routePaths]
544
+ .filter((path) => measurements.get(path)?.available !== true)
545
+ .sort();
546
+ if (unavailable.length > 0) {
547
+ return {
548
+ ...entry.route,
549
+ cost: {
550
+ reason: `Missing client artifacts: ${unavailable.join(", ")}.`,
551
+ status: "unavailable",
552
+ },
553
+ };
554
+ }
555
+ const initial = byteCostForPaths(entry.initial, measurements);
556
+ const navigation = byteCostForPaths(new Set([...entry.navigation].filter((path) => !sharedPaths.has(path))), measurements);
557
+ const shared = byteCostForPaths(sharedPaths, measurements);
558
+ return {
559
+ ...entry.route,
560
+ cost: {
561
+ baseline: {
562
+ initialGzipDeltaBytes: initial.gzipEstimateBytes - shared.gzipEstimateBytes,
563
+ navigationGzipDeltaBytes: navigation.gzipEstimateBytes,
564
+ },
565
+ initial,
566
+ navigation,
567
+ status: "available",
568
+ },
569
+ };
570
+ }),
571
+ };
572
+ }
573
+ function clientArtifactStaticClosure(route, chunks) {
574
+ const paths = new Set();
575
+ for (const path of route.css ?? []) {
576
+ paths.add(path);
577
+ }
578
+ if (route.navigation === true && route.navigationScript !== undefined) {
579
+ paths.add(route.navigationScript);
580
+ }
581
+ for (const path of route.imports ?? []) {
582
+ addClientArtifactStaticClosure(paths, path, chunks);
583
+ }
584
+ if (route.script !== undefined) {
585
+ addClientArtifactStaticClosure(paths, route.script, chunks);
586
+ }
587
+ return paths;
588
+ }
589
+ function addFetchedDynamicImportClosures(options) {
590
+ const fetchedDynamicImports = new Set(options.fetchedDynamicImports);
591
+ const acceptedDynamicImports = new Set();
592
+ const reachableDynamicImports = new Set(options.routeDynamicImports);
593
+ for (;;) {
594
+ for (const dynamicImport of dynamicImportsReachableFromStaticClosure(options.navigation, options.chunks)) {
595
+ reachableDynamicImports.add(dynamicImport);
596
+ }
597
+ let added = false;
598
+ for (const dynamicImport of fetchedDynamicImports) {
599
+ if (acceptedDynamicImports.has(dynamicImport) ||
600
+ !reachableDynamicImports.has(dynamicImport) ||
601
+ !options.chunks.has(dynamicImport)) {
602
+ continue;
603
+ }
604
+ acceptedDynamicImports.add(dynamicImport);
605
+ addClientArtifactStaticClosure(options.navigation, dynamicImport, options.chunks);
606
+ added = true;
607
+ }
608
+ if (!added) {
609
+ return;
610
+ }
611
+ }
612
+ }
613
+ function dynamicImportsReachableFromStaticClosure(paths, chunks) {
614
+ const pending = [...paths];
615
+ const visited = new Set();
616
+ const dynamicImports = new Set();
617
+ while (pending.length > 0) {
618
+ const path = pending.pop();
619
+ if (path === undefined || visited.has(path)) {
620
+ continue;
621
+ }
622
+ visited.add(path);
623
+ const chunk = chunks.get(path);
624
+ if (chunk === undefined) {
625
+ continue;
626
+ }
627
+ for (const imported of chunk.imports ?? []) {
628
+ pending.push(imported);
629
+ }
630
+ for (const dynamicImport of chunk.dynamicImports ?? []) {
631
+ dynamicImports.add(dynamicImport);
632
+ }
633
+ }
634
+ return dynamicImports;
635
+ }
636
+ function addClientArtifactStaticClosure(paths, path, chunks) {
637
+ if (path === "" || paths.has(path)) {
638
+ return;
639
+ }
640
+ paths.add(path);
641
+ for (const imported of chunks.get(path)?.imports ?? []) {
642
+ addClientArtifactStaticClosure(paths, imported, chunks);
643
+ }
644
+ }
645
+ function intersectionOfPathSets(sets) {
646
+ const [first, ...rest] = sets;
647
+ if (first === undefined || sets.length < 2) {
648
+ return new Set();
649
+ }
650
+ return new Set([...first].filter((path) => rest.every((set) => set.has(path))));
651
+ }
652
+ async function measureClientArtifactPaths(clientDir, paths) {
653
+ const measurements = await Promise.all([...paths].map(async (path) => {
654
+ const absolutePath = safeClientArtifactPath(clientDir, path);
655
+ if (absolutePath === undefined) {
656
+ return [path, { available: false }];
657
+ }
658
+ try {
659
+ const content = await readFile(absolutePath);
660
+ return [
661
+ path,
662
+ {
663
+ available: true,
664
+ gzipEstimateBytes: gzipSync(content).byteLength,
665
+ rawBytes: content.byteLength,
666
+ },
667
+ ];
668
+ }
669
+ catch {
670
+ return [path, { available: false }];
671
+ }
672
+ }));
673
+ return new Map(measurements);
674
+ }
675
+ function safeClientArtifactPath(clientDir, path) {
676
+ const absolutePath = resolve(clientDir, path);
677
+ const relativePath = relative(resolve(clientDir), absolutePath);
678
+ return relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep}`)
679
+ ? undefined
680
+ : absolutePath;
681
+ }
682
+ function byteCostForPaths(paths, measurements) {
683
+ return [...paths].reduce((cost, path) => {
684
+ const measurement = measurements.get(path);
685
+ return {
686
+ gzipEstimateBytes: cost.gzipEstimateBytes + (measurement?.gzipEstimateBytes ?? 0),
687
+ rawBytes: cost.rawBytes + (measurement?.rawBytes ?? 0),
688
+ };
689
+ }, { gzipEstimateBytes: 0, rawBytes: 0 });
690
+ }
472
691
  const incrementalBuildCacheFilename = "build-cache.json";
473
692
  const incrementalBuildCacheVersion = 2;
474
693
  async function createIncrementalBuildCacheFingerprint(options) {
@@ -584,6 +803,11 @@ function collectClientManifestOutputFiles(manifest) {
584
803
  files.add(asset);
585
804
  files.add(`public/${asset}`);
586
805
  }
806
+ for (const chunk of manifest.chunks ?? []) {
807
+ for (const file of [chunk.file, ...(chunk.imports ?? []), ...(chunk.dynamicImports ?? [])]) {
808
+ files.add(file);
809
+ }
810
+ }
587
811
  for (const route of manifest.routes) {
588
812
  for (const file of [
589
813
  route.script,
@@ -591,6 +815,7 @@ function collectClientManifestOutputFiles(manifest) {
591
815
  route.navigationScript,
592
816
  ...(route.css ?? []),
593
817
  ...(route.imports ?? []),
818
+ ...(route.dynamicImports ?? []),
594
819
  ]) {
595
820
  if (file !== undefined) {
596
821
  files.add(file);
@@ -855,6 +1080,7 @@ async function analyzeBuildRouteSources(options) {
855
1080
  diagnostics: analysis.diagnostics,
856
1081
  entry: analysis.route.file,
857
1082
  path: analysis.route.path,
1083
+ source: analysis.source,
858
1084
  })),
859
1085
  }),
860
1086
  byFile,
@@ -2605,14 +2831,14 @@ const componentGenerateMetadata = readComponentModuleExport(componentModule, "ge
2605
2831
  const componentMetadata = readComponentModuleExport(componentModule, "metadata");
2606
2832
  const componentSecurityHeadersApplied = readComponentModuleExport(componentModule, "__mreactSecurityHeadersApplied");
2607
2833
 
2608
- export function App(props) {
2609
- return renderCloudflareRouteComponent(props);
2834
+ export function App(props, request) {
2835
+ return renderCloudflareRouteComponent(props, request);
2610
2836
  }
2611
- export default function CloudflareDefaultRouteComponent(props) {
2612
- return renderCloudflareRouteComponent(props);
2837
+ export default function CloudflareDefaultRouteComponent(props, request) {
2838
+ return renderCloudflareRouteComponent(props, request);
2613
2839
  }
2614
- export function CloudflareRouteComponent(props) {
2615
- return renderCloudflareRouteComponent(props);
2840
+ export function CloudflareRouteComponent(props, request) {
2841
+ return renderCloudflareRouteComponent(props, request);
2616
2842
  }
2617
2843
  export const slots = componentSlots === undefined ? undefined : { ...componentSlots };
2618
2844
  export const generateMetadata =
@@ -2620,13 +2846,13 @@ export const generateMetadata =
2620
2846
  export const metadata = componentMetadata;
2621
2847
  export const __mreactSecurityHeadersApplied = componentSecurityHeadersApplied === true;
2622
2848
 
2623
- function renderCloudflareRouteComponent(props) {
2849
+ function renderCloudflareRouteComponent(props, request) {
2624
2850
  const routeComponent = resolveCloudflareRouteComponent();
2625
2851
  if (routeComponent === undefined) {
2626
2852
  throw new Error("No Cloudflare component export was found for ${componentImport}.");
2627
2853
  }
2628
2854
 
2629
- return routeComponent(props);
2855
+ return routeComponent(props, request);
2630
2856
  }
2631
2857
 
2632
2858
  function resolveCloudflareRouteComponent() {
@@ -2881,14 +3107,14 @@ ${shellModules.join("\n")}
2881
3107
  const pageComponent = selectComponent(pageModule, ${JSON.stringify(options.filename)});
2882
3108
  const shells = [${shellDefinitions.join(", ")}];
2883
3109
  export const slots = pageModule.slots;
2884
- export default function CloudflareStringRouteComponent(props) {
2885
- return renderCloudflareStringRoute(props);
3110
+ export default function CloudflareStringRouteComponent(props, request) {
3111
+ return renderCloudflareStringRoute(props, request);
2886
3112
  }
2887
3113
 
2888
- async function renderCloudflareStringRoute(props) {
3114
+ async function renderCloudflareStringRoute(props, request) {
2889
3115
  const slotHtml = await renderRouteSlots(pageModule.slots, props);
2890
3116
  const layoutShells = await renderLayoutShells(shells, props, slotHtml);
2891
- const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], props);
3117
+ const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], { ...props, request });
2892
3118
  let html = "<!DOCTYPE html>";
2893
3119
  for (const shell of layoutShells) {
2894
3120
  html += shell.prefix;
@@ -2899,7 +3125,7 @@ async function renderCloudflareStringRoute(props) {
2899
3125
  }
2900
3126
  html = injectCloudflareHead(html, metadata, cloudflareRouteHeadTags(props.clientManifest, props.route.path));
2901
3127
  return new Response(html, {
2902
- headers: cloudflareMetadataHeaders(metadata, props.request, {
3128
+ headers: cloudflareMetadataHeaders(metadata, request, {
2903
3129
  "content-type": "text/html; charset=utf-8"
2904
3130
  })
2905
3131
  });
@@ -3021,14 +3247,14 @@ ${shellImports.join("\n")}
3021
3247
  const pageComponent = selectComponent(pageModule, ${JSON.stringify(options.filename)});
3022
3248
  const shells = [${shellDefinitions.join(", ")}];
3023
3249
  export const slots = pageModule.slots;
3024
- export default function CloudflareStringRouteComponent(props) {
3025
- return renderCloudflareStringRoute(props);
3250
+ export default function CloudflareStringRouteComponent(props, request) {
3251
+ return renderCloudflareStringRoute(props, request);
3026
3252
  }
3027
3253
 
3028
- async function renderCloudflareStringRoute(props) {
3254
+ async function renderCloudflareStringRoute(props, request) {
3029
3255
  const slotHtml = await renderRouteSlots(pageModule.slots, props);
3030
3256
  const layoutShells = await renderLayoutShells(shells, props, slotHtml);
3031
- const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], props);
3257
+ const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], { ...props, request });
3032
3258
  let html = "<!DOCTYPE html>";
3033
3259
  for (const shell of layoutShells) {
3034
3260
  html += shell.prefix;
@@ -3039,7 +3265,7 @@ async function renderCloudflareStringRoute(props) {
3039
3265
  }
3040
3266
  html = injectCloudflareHead(html, metadata, cloudflareRouteHeadTags(props.clientManifest, props.route.path));
3041
3267
  return new Response(html, {
3042
- headers: cloudflareMetadataHeaders(metadata, props.request, {
3268
+ headers: cloudflareMetadataHeaders(metadata, request, {
3043
3269
  "content-type": "text/html; charset=utf-8"
3044
3270
  })
3045
3271
  });
@@ -3114,8 +3340,8 @@ export const slots = pageModule.slots;
3114
3340
  export const App = renderCloudflareStreamRoute;
3115
3341
  export default renderCloudflareStreamRoute;
3116
3342
 
3117
- async function renderCloudflareStreamRoute(props) {
3118
- const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], props);
3343
+ async function renderCloudflareStreamRoute(props, request) {
3344
+ const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], { ...props, request });
3119
3345
  const body = renderToReadableStream(async ($sink) => {
3120
3346
  const slotHtml = await renderRouteSlots(pageModule.slots, props);
3121
3347
  const layoutShells = await renderLayoutShells(shells, props, slotHtml);
@@ -3138,7 +3364,7 @@ async function renderCloudflareStreamRoute(props) {
3138
3364
  }
3139
3365
  });
3140
3366
  return new Response(body, {
3141
- headers: cloudflareMetadataHeaders(metadata, props.request, {
3367
+ headers: cloudflareMetadataHeaders(metadata, request, {
3142
3368
  "content-type": "text/html; charset=utf-8",
3143
3369
  "x-mreact-stream": "1"
3144
3370
  })
@@ -3267,9 +3493,15 @@ function cloudflareHydrationMarkerParts(props) {
3267
3493
  }
3268
3494
  const routeId = route.routeId ?? cloudflareRouteIdForPath(props.route.path);
3269
3495
  const escapedRouteId = escapeHtmlAttribute(routeId);
3496
+ const requestUrl = new URL(props.request.url);
3270
3497
  const propsJson = escapeScriptJson(JSON.stringify({
3271
3498
  params: props.params,
3272
- request: { url: new URL(props.request.url).pathname },
3499
+ request: {
3500
+ hash: "",
3501
+ pathname: requestUrl.pathname,
3502
+ search: "",
3503
+ url: requestUrl.pathname,
3504
+ },
3273
3505
  data: props.data,
3274
3506
  }));
3275
3507
  const clientReferencesJson = route.clientReferenceManifest === undefined || route.clientReferenceManifest.length === 0
@@ -4549,6 +4781,7 @@ async function writeClientRouteBundles(options) {
4549
4781
  if (clientEntries.length === 0) {
4550
4782
  return {
4551
4783
  assets: [...routeCssAssets.assets, ...specialCssAssets.assets].sort(),
4784
+ chunks: [],
4552
4785
  routes: entries.flatMap((entry) => ("manifest" in entry ? [entry.manifest] : [])),
4553
4786
  styles: specialCssAssets.styles,
4554
4787
  };
@@ -4632,6 +4865,9 @@ async function writeClientRouteBundles(options) {
4632
4865
  ? {}
4633
4866
  : { clientReferenceManifest: entry.build.clientReferenceManifest }),
4634
4867
  ...(entry.css.length === 0 ? {} : { css: entry.css }),
4868
+ ...(routeOutput.chunk.dynamicImports.length === 0
4869
+ ? {}
4870
+ : { dynamicImports: routeOutput.chunk.dynamicImports }),
4635
4871
  ...(routeOutput.chunk.imports.length === 0 ? {} : { imports: routeOutput.chunk.imports }),
4636
4872
  ...(navigation ? { navigation } : {}),
4637
4873
  routeId,
@@ -4642,6 +4878,11 @@ async function writeClientRouteBundles(options) {
4642
4878
  });
4643
4879
  return {
4644
4880
  assets: Array.from(generatedAssets).sort(),
4881
+ chunks: output.chunks.map((chunk) => ({
4882
+ ...(chunk.dynamicImports.length === 0 ? {} : { dynamicImports: chunk.dynamicImports }),
4883
+ file: chunk.fileName,
4884
+ ...(chunk.imports.length === 0 ? {} : { imports: chunk.imports }),
4885
+ })),
4645
4886
  routes,
4646
4887
  styles: specialCssAssets.styles,
4647
4888
  };