@reckona/mreact-router 0.0.181 → 0.0.183

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 (47) hide show
  1. package/dist/actions.d.ts.map +1 -1
  2. package/dist/actions.js +20 -8
  3. package/dist/actions.js.map +1 -1
  4. package/dist/build.d.ts +3 -0
  5. package/dist/build.d.ts.map +1 -1
  6. package/dist/build.js +263 -5
  7. package/dist/build.js.map +1 -1
  8. package/dist/built-assets.d.ts.map +1 -1
  9. package/dist/built-assets.js +2 -2
  10. package/dist/built-assets.js.map +1 -1
  11. package/dist/cache-config.d.ts +1 -1
  12. package/dist/cache-config.d.ts.map +1 -1
  13. package/dist/cache-config.js.map +1 -1
  14. package/dist/cache.d.ts.map +1 -1
  15. package/dist/cache.js +18 -1
  16. package/dist/cache.js.map +1 -1
  17. package/dist/client.d.ts.map +1 -1
  18. package/dist/client.js +16 -6
  19. package/dist/client.js.map +1 -1
  20. package/dist/config.d.ts +2 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +6 -0
  23. package/dist/config.js.map +1 -1
  24. package/dist/csrf.js +10 -6
  25. package/dist/csrf.js.map +1 -1
  26. package/dist/navigation.d.ts.map +1 -1
  27. package/dist/navigation.js +14 -1
  28. package/dist/navigation.js.map +1 -1
  29. package/dist/render.d.ts +4 -0
  30. package/dist/render.d.ts.map +1 -1
  31. package/dist/render.js +96 -46
  32. package/dist/render.js.map +1 -1
  33. package/dist/vite.d.ts.map +1 -1
  34. package/dist/vite.js +35 -4
  35. package/dist/vite.js.map +1 -1
  36. package/package.json +11 -11
  37. package/src/actions.ts +26 -9
  38. package/src/build.ts +398 -5
  39. package/src/built-assets.ts +2 -3
  40. package/src/cache-config.ts +1 -0
  41. package/src/cache.ts +20 -1
  42. package/src/client.ts +16 -6
  43. package/src/config.ts +8 -0
  44. package/src/csrf.ts +13 -6
  45. package/src/navigation.ts +16 -1
  46. package/src/render.ts +194 -78
  47. package/src/vite.ts +41 -4
package/src/navigation.ts CHANGED
@@ -31,11 +31,23 @@ function stripLeadingControlOrWhitespace(value: string): string {
31
31
  return value.slice(start);
32
32
  }
33
33
 
34
+ function containsControlCharacter(value: string): boolean {
35
+ for (let index = 0; index < value.length; index += 1) {
36
+ const code = value.charCodeAt(index);
37
+ if (code <= 0x1f || code === 0x7f) {
38
+ return true;
39
+ }
40
+ }
41
+
42
+ return false;
43
+ }
44
+
34
45
  // Returns true if `location` is safe to use as a same-origin Location header.
35
46
  // Allowed: path-absolute (`/foo`), query-only (`?x=1`), hash-only (`#x`),
36
47
  // relative (`foo`). Rejected: protocol-relative (`//evil`), backslash variants
37
48
  // (`/\evil`, `\\evil`), and anything with a scheme like `javascript:`.
38
49
  function isSafeInternalRedirect(location: string): boolean {
50
+ if (containsControlCharacter(location)) return false;
39
51
  const trimmed = stripLeadingControlOrWhitespace(location);
40
52
  if (trimmed === "") return false;
41
53
  if (trimmed.startsWith("//")) return false;
@@ -47,6 +59,7 @@ function isSafeInternalRedirect(location: string): boolean {
47
59
  }
48
60
 
49
61
  function isSafeExternalRedirect(location: string): boolean {
62
+ if (containsControlCharacter(location)) return false;
50
63
  const trimmed = stripLeadingControlOrWhitespace(location);
51
64
  const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);
52
65
  if (match === null || match[1] === undefined) return false;
@@ -61,7 +74,9 @@ function throwUnsafeRedirect(location: string): never {
61
74
  }
62
75
 
63
76
  function throwUnsafeRewrite(location: string): never {
64
- throw new TypeError(`unsafe rewrite target: ${JSON.stringify(location)}`);
77
+ throw new TypeError(
78
+ `unsafe rewrite target: ${JSON.stringify(location)} - rewrite() only accepts same-origin paths, query strings, or hashes; external, protocol-relative, backslash, and control-character targets are rejected`,
79
+ );
65
80
  }
66
81
 
67
82
  /**
package/src/render.ts CHANGED
@@ -195,6 +195,7 @@ export interface RenderAppRequestOptions {
195
195
  instrumentation?: RouterInstrumentation | undefined;
196
196
  logger?: AppRouterLogger | undefined;
197
197
  navigationScripts?: ReadonlyMap<string, string> | undefined;
198
+ onRenderError?: ((error: unknown) => void) | undefined;
198
199
  onResponse?: AppRouterResponseHook | undefined;
199
200
  queryClient?: QueryClient | undefined;
200
201
  request: Request;
@@ -205,6 +206,7 @@ export interface RenderAppRequestOptions {
205
206
  routes?: readonly AppRoute[] | undefined;
206
207
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
207
208
  serverModuleCacheVersion?: string | undefined;
209
+ devServerModuleCacheVersion?: string | undefined;
208
210
  // True when serving through a dev server: request-time server transforms
209
211
  // are expected there and must not trigger the production prebuild warning.
210
212
  dev?: boolean | undefined;
@@ -251,10 +253,9 @@ export async function preloadBuiltRequestModules(options: {
251
253
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
252
254
  serverModuleCacheVersion: string;
253
255
  serverSourceFiles: ReadonlyMap<string, string>;
254
- serverActionReferencesByFile?: ReadonlyMap<
255
- string,
256
- readonly PreparedFormActionReference[]
257
- > | undefined;
256
+ serverActionReferencesByFile?:
257
+ | ReadonlyMap<string, readonly PreparedFormActionReference[]>
258
+ | undefined;
258
259
  vitePlugins?: readonly PluginOption[] | undefined;
259
260
  }): Promise<void> {
260
261
  const clientRouteInferenceCache = createClientRouteInferenceCache();
@@ -535,6 +536,8 @@ const routeOutOfOrderBoundaryAnalysisCache = new Map<string, Promise<boolean>>()
535
536
  const routeOutOfOrderBoundaryAnalysisCacheCounters = createRouterRuntimeCacheCounters();
536
537
  const routeLoaderModuleCache = new Map<string, Promise<RouteLoaderModule>>();
537
538
  const routeLoaderModuleCacheCounters = createRouterRuntimeCacheCounters();
539
+ const routeMetadataModuleCache = new Map<string, Promise<RouteMetadataModule>>();
540
+ const routeMetadataModuleCacheCounters = createRouterRuntimeCacheCounters();
538
541
  const middlewareModuleCache = new Map<string, Promise<MiddlewareModule>>();
539
542
  const middlewareModuleCacheCounters = createRouterRuntimeCacheCounters();
540
543
  const serverRouteModuleCache = new Map<string, Promise<Record<string, unknown>>>();
@@ -549,6 +552,7 @@ const maxRouteOutOfOrderBoundaryAnalysisCacheEntries = resolveRouterCacheLimit(
549
552
  512,
550
553
  );
551
554
  const maxRouteLoaderModuleCacheEntries = resolveRouterCacheLimit("ROUTE_LOADER_MODULE", 512);
555
+ const maxRouteMetadataModuleCacheEntries = resolveRouterCacheLimit("ROUTE_METADATA_MODULE", 512);
552
556
  const maxMiddlewareModuleCacheEntries = resolveRouterCacheLimit("MIDDLEWARE_MODULE", 64);
553
557
  const maxServerRouteModuleCacheEntries = resolveRouterCacheLimit("SERVER_ROUTE_MODULE", 512);
554
558
  const maxComposedRouteMetadataCacheEntries = resolveRouterCacheLimit(
@@ -588,6 +592,12 @@ export function routerRenderRuntimeCacheStats(): RouterRuntimeCacheStat[] {
588
592
  maxRouteLoaderModuleCacheEntries,
589
593
  routeLoaderModuleCacheCounters,
590
594
  ),
595
+ routerRuntimeCacheStat(
596
+ "route-metadata-module",
597
+ routeMetadataModuleCache,
598
+ maxRouteMetadataModuleCacheEntries,
599
+ routeMetadataModuleCacheCounters,
600
+ ),
591
601
  routerRuntimeCacheStat(
592
602
  "middleware-module",
593
603
  middlewareModuleCache,
@@ -718,9 +728,11 @@ function addRenderTimingPhaseDuration(
718
728
  }
719
729
 
720
730
  function routeLoaderMayReturnControlResponse(code: string): boolean {
721
- return /\b(?:redirect|redirectExternal|rewrite|notFound|throwNotFound)\s*\(/.test(code) ||
731
+ return (
732
+ /\b(?:redirect|redirectExternal|rewrite|notFound|throwNotFound)\s*\(/.test(code) ||
722
733
  /\bnew\s+Response\s*\(/.test(code) ||
723
- /\bResponse\.(?:redirect|json)\s*\(/.test(code);
734
+ /\bResponse\.(?:redirect|json)\s*\(/.test(code)
735
+ );
724
736
  }
725
737
 
726
738
  async function waitForRenderPreload(
@@ -783,6 +795,7 @@ export type AppRouterMiddlewareResult =
783
795
  export async function resolveAppRouterMiddleware(options: {
784
796
  appDir: string;
785
797
  define?: UserConfig["define"] | undefined;
798
+ devServerModuleCacheVersion?: string | undefined;
786
799
  importPolicy?: AppRouterImportPolicy | undefined;
787
800
  instrumentation?: RouterInstrumentation | undefined;
788
801
  middlewareControl?: RouteMiddlewareControl | undefined;
@@ -811,7 +824,9 @@ export async function resolveAppRouterMiddleware(options: {
811
824
  return { response: middlewareResponse, type: "response" };
812
825
  }
813
826
 
814
- async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions): Promise<Response> {
827
+ async function renderAppRequestInternal(
828
+ options: RenderAppRequestRuntimeOptions,
829
+ ): Promise<Response> {
815
830
  warnProductionRenderWithoutPrebuiltModules(options);
816
831
  const timing = createRenderTiming(options.logger);
817
832
  const clientRouteInferenceCache =
@@ -822,7 +837,9 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
822
837
  const url = options.requestUrl ?? new URL(options.request.url);
823
838
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
824
839
  const matched =
825
- options.matchedRoute ?? options.routeMatcher?.match(url.pathname) ?? matchRoute(routes, url.pathname);
840
+ options.matchedRoute ??
841
+ options.routeMatcher?.match(url.pathname) ??
842
+ matchRoute(routes, url.pathname);
826
843
  finishRenderTimingPhase(timing, phaseStartedAt, "routeMatchMs");
827
844
  const hasMiddleware =
828
845
  options.skipMiddleware === true
@@ -847,6 +864,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
847
864
  ? ({ request: options.request, type: "continue" } satisfies AppRouterMiddlewareResult)
848
865
  : await resolveAppRouterMiddleware({
849
866
  appDir: options.appDir,
867
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
850
868
  importPolicy: options.importPolicy,
851
869
  instrumentation: options.instrumentation,
852
870
  middlewareControl,
@@ -1082,6 +1100,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1082
1100
  filename: matched.route.file,
1083
1101
  importPolicy: options.importPolicy,
1084
1102
  instrumentation: options.instrumentation,
1103
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
1085
1104
  request: options.request,
1086
1105
  routeId: routeIdForPath(matched.route.path),
1087
1106
  routePath: matched.route.path,
@@ -1174,6 +1193,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1174
1193
  options.vitePlugins,
1175
1194
  undefined,
1176
1195
  options.importPolicy,
1196
+ options.devServerModuleCacheVersion,
1177
1197
  ),
1178
1198
  );
1179
1199
  const pageHtml = renderedPage.html;
@@ -1230,6 +1250,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1230
1250
  routes,
1231
1251
  serverModules: options.serverModules,
1232
1252
  serverModuleCacheVersion: options.serverModuleCacheVersion,
1253
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
1233
1254
  serverSourceFiles: options.serverSourceFiles,
1234
1255
  define: options.define,
1235
1256
  vitePlugins: options.vitePlugins,
@@ -1448,6 +1469,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1448
1469
  options.vitePlugins,
1449
1470
  timing,
1450
1471
  options.importPolicy,
1472
+ options.devServerModuleCacheVersion,
1451
1473
  ),
1452
1474
  );
1453
1475
  finishRenderTimingPhase(timing, phaseStartedAt, "pageRenderMs");
@@ -1510,12 +1532,13 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1510
1532
  filename: matched.route.file,
1511
1533
  importPolicy: options.importPolicy,
1512
1534
  routes,
1513
- serverModules: options.serverModules,
1514
- serverModuleCacheVersion: options.serverModuleCacheVersion,
1515
- serverSourceFiles: options.serverSourceFiles,
1516
- define: options.define,
1517
- vitePlugins: options.vitePlugins,
1518
- });
1535
+ serverModules: options.serverModules,
1536
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
1537
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
1538
+ serverSourceFiles: options.serverSourceFiles,
1539
+ define: options.define,
1540
+ vitePlugins: options.vitePlugins,
1541
+ });
1519
1542
  finishRenderTimingPhase(timing, phaseStartedAt, "metadataMs");
1520
1543
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
1521
1544
  html = injectHeadMetadata(html, metadata);
@@ -1610,6 +1633,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
1610
1633
  filename: "error.mreact.tsx",
1611
1634
  pageFile: matched.route.file,
1612
1635
  });
1636
+ options.onRenderError?.(error);
1613
1637
 
1614
1638
  const response = await renderSpecialRoute({
1615
1639
  appDir: options.appDir,
@@ -2351,6 +2375,7 @@ function devExternalSourceDirs(
2351
2375
  async function runMiddleware(options: {
2352
2376
  appDir: string;
2353
2377
  define?: UserConfig["define"] | undefined;
2378
+ devServerModuleCacheVersion?: string | undefined;
2354
2379
  importPolicy?: AppRouterImportPolicy | undefined;
2355
2380
  instrumentation?: RouterInstrumentation | undefined;
2356
2381
  middlewareControl?: RouteMiddlewareControl | undefined;
@@ -2421,6 +2446,7 @@ async function runMiddleware(options: {
2421
2446
  serverModules: options.serverModules,
2422
2447
  serverModuleCacheVersion: options.serverModuleCacheVersion,
2423
2448
  serverSourceFiles: options.serverSourceFiles,
2449
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
2424
2450
  vitePlugins: options.vitePlugins,
2425
2451
  });
2426
2452
  } finally {
@@ -2519,6 +2545,7 @@ async function loadMiddlewareModule(options: {
2519
2545
  appDir: string;
2520
2546
  code?: string | undefined;
2521
2547
  define?: UserConfig["define"] | undefined;
2548
+ devServerModuleCacheVersion?: string | undefined;
2522
2549
  file: string;
2523
2550
  importPolicy?: AppRouterImportPolicy | undefined;
2524
2551
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
@@ -2533,10 +2560,12 @@ async function loadMiddlewareModule(options: {
2533
2560
  options.serverModuleCacheVersion,
2534
2561
  options.serverSourceFiles,
2535
2562
  ));
2563
+ const moduleCacheVersion =
2564
+ options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
2536
2565
  const cacheKey =
2537
- options.serverModuleCacheVersion === undefined
2566
+ moduleCacheVersion === undefined
2538
2567
  ? undefined
2539
- : `middleware\0${options.appDir}\0${options.file}\0${options.serverModuleCacheVersion}\0${memoizedHashText(code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
2568
+ : `middleware\0${options.serverModuleCacheVersion === undefined ? "dev" : "build"}\0${options.appDir}\0${options.file}\0${moduleCacheVersion}\0${memoizedHashText(code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
2540
2569
 
2541
2570
  if (cacheKey !== undefined) {
2542
2571
  const cached = readRouterRuntimeCacheEntry(
@@ -2557,6 +2586,7 @@ async function loadMiddlewareModule(options: {
2557
2586
  file: options.file,
2558
2587
  importPolicy: options.importPolicy,
2559
2588
  prebuiltArtifact: prebuiltRequestModuleArtifact(options.serverModules, options.file, code),
2589
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
2560
2590
  serverModuleCacheVersion: options.serverModuleCacheVersion,
2561
2591
  vitePlugins: options.vitePlugins,
2562
2592
  }).catch((error) => {
@@ -2618,6 +2648,7 @@ async function loadBundledMiddlewareModule(options: {
2618
2648
  appDir: string;
2619
2649
  code: string;
2620
2650
  define?: UserConfig["define"] | undefined;
2651
+ devServerModuleCacheVersion?: string | undefined;
2621
2652
  file: string;
2622
2653
  importPolicy?: AppRouterImportPolicy | undefined;
2623
2654
  prebuiltArtifact?: BuiltServerModuleOutputLike | undefined;
@@ -2644,10 +2675,10 @@ async function loadBundledMiddlewareModule(options: {
2644
2675
  }));
2645
2676
 
2646
2677
  return importAppRouterSourceModule<MiddlewareModule>({
2647
- ...(options.serverModuleCacheVersion === undefined
2678
+ ...((options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion) === undefined
2648
2679
  ? {}
2649
2680
  : {
2650
- cacheKey: `middleware:${options.file}:${options.serverModuleCacheVersion}:${memoizedHashText(compiled)}:${viteDefineCacheKey(options.define)}:${vitePluginsCacheKey(options.vitePlugins)}`,
2681
+ cacheKey: `middleware:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.file}:${options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion}:${memoizedHashText(compiled)}:${viteDefineCacheKey(options.define)}:${vitePluginsCacheKey(options.vitePlugins)}`,
2651
2682
  }),
2652
2683
  code: compiled,
2653
2684
  define: options.define,
@@ -2969,6 +3000,7 @@ async function runServerModuleWithSlots(
2969
3000
  vitePlugins?: readonly PluginOption[] | undefined,
2970
3001
  timing?: RenderTiming | undefined,
2971
3002
  importPolicy?: AppRouterImportPolicy | undefined,
3003
+ devServerModuleCacheVersion?: string | undefined,
2972
3004
  ): Promise<{ html: string; slots: Record<string, string> }> {
2973
3005
  const moduleLoadStartedAt = renderTimingPhaseStartedAt(timing);
2974
3006
  const module = await loadServerModule(
@@ -2979,6 +3011,7 @@ async function runServerModuleWithSlots(
2979
3011
  define,
2980
3012
  vitePlugins,
2981
3013
  importPolicy,
3014
+ devServerModuleCacheVersion,
2982
3015
  );
2983
3016
  finishRenderTimingPhase(timing, moduleLoadStartedAt, "pageModuleLoadMs");
2984
3017
  const component = selectServerComponent(module);
@@ -3003,6 +3036,7 @@ async function loadServerModule(
3003
3036
  define?: UserConfig["define"] | undefined,
3004
3037
  vitePlugins?: readonly PluginOption[] | undefined,
3005
3038
  importPolicy?: AppRouterImportPolicy | undefined,
3039
+ devServerModuleCacheVersion?: string | undefined,
3006
3040
  ): Promise<ServerModuleExports> {
3007
3041
  const artifact = serverModules?.get(sourcefile)?.string;
3008
3042
  const codeHash = memoizedHashText(code);
@@ -3020,11 +3054,14 @@ async function loadServerModule(
3020
3054
  });
3021
3055
  }
3022
3056
  const moduleCode =
3023
- prebuiltCode === undefined ? code : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
3057
+ prebuiltCode === undefined
3058
+ ? code
3059
+ : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
3060
+ const sourceModuleCacheVersion = serverModuleCacheVersion ?? devServerModuleCacheVersion;
3024
3061
  const cacheKey =
3025
- serverModuleCacheVersion === undefined
3062
+ sourceModuleCacheVersion === undefined
3026
3063
  ? undefined
3027
- : `server-component:${serverModuleCacheVersion}:${sourcefile}:${
3064
+ : `server-component:${serverModuleCacheVersion === undefined ? "dev" : "build"}:${sourceModuleCacheVersion}:${sourcefile}:${
3028
3065
  moduleCode === code ? codeHash : memoizedHashText(moduleCode)
3029
3066
  }:${importPolicyCacheKey(importPolicy)}:${viteDefineCacheKey(define)}:${vitePluginsCacheKey(vitePlugins)}`;
3030
3067
  return await importAppRouterSourceModule<ServerModuleExports>({
@@ -3663,7 +3700,12 @@ async function loadServerStreamModule(
3663
3700
  ): Promise<StreamModuleExports> {
3664
3701
  const artifactCode = serverModules?.get(sourcefile)?.stream;
3665
3702
  const codeHash = memoizedHashText(code);
3666
- const prebuiltCode = prebuiltServerComponentModuleCode(artifactCode, code, codeHash, outputOptions);
3703
+ const prebuiltCode = prebuiltServerComponentModuleCode(
3704
+ artifactCode,
3705
+ code,
3706
+ codeHash,
3707
+ outputOptions,
3708
+ );
3667
3709
  const usesPrebuiltCompiledCode = artifactCode?.code === code;
3668
3710
  if (
3669
3711
  artifactCode !== undefined &&
@@ -3678,7 +3720,9 @@ async function loadServerStreamModule(
3678
3720
  });
3679
3721
  }
3680
3722
  const moduleCode =
3681
- prebuiltCode === undefined ? code : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
3723
+ prebuiltCode === undefined
3724
+ ? code
3725
+ : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
3682
3726
  const cacheKey =
3683
3727
  serverModuleCacheVersion === undefined
3684
3728
  ? undefined
@@ -3817,19 +3861,19 @@ async function layoutShellsForPage(
3817
3861
  const slotContext = createSlotRenderContext(slots);
3818
3862
  const renderShell = (shell: ShellFile) =>
3819
3863
  renderShellPrefixSuffix(
3820
- appDir,
3821
- shell,
3822
- props,
3823
- slotContext,
3824
- serverModules,
3825
- serverModuleCacheVersion,
3826
- serverSourceFiles,
3827
- clientRouteInferenceCache,
3828
- undefined,
3829
- define,
3830
- vitePlugins,
3831
- importPolicy,
3832
- );
3864
+ appDir,
3865
+ shell,
3866
+ props,
3867
+ slotContext,
3868
+ serverModules,
3869
+ serverModuleCacheVersion,
3870
+ serverSourceFiles,
3871
+ clientRouteInferenceCache,
3872
+ undefined,
3873
+ define,
3874
+ vitePlugins,
3875
+ importPolicy,
3876
+ );
3833
3877
  const shells =
3834
3878
  Object.keys(slotContext.namedSlots).length === 0
3835
3879
  ? await Promise.all(layoutFiles.map(renderShell))
@@ -3881,8 +3925,7 @@ async function renderShellPrefixSuffix(
3881
3925
  }
3882
3926
  }
3883
3927
 
3884
- const staticEntry =
3885
- cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
3928
+ const staticEntry = cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
3886
3929
  const loadedStaticEntry =
3887
3930
  staticEntry ??
3888
3931
  (await loadShellStaticRenderEntry({
@@ -4065,19 +4108,28 @@ async function renderShellStreamComponent(
4065
4108
  // next request.
4066
4109
  const shellFilesCache = new Map<string, ShellFile[]>();
4067
4110
  const MAX_SHELL_FILES_CACHE_ENTRIES = 1024;
4068
- const devShellFilesCache = new Map<string, { directoryStats: readonly FileStat[]; files: ShellFile[] }>();
4111
+ const devShellFilesCache = new Map<
4112
+ string,
4113
+ { directoryStats: readonly FileStat[]; files: ShellFile[] }
4114
+ >();
4069
4115
  const shellStaticRenderCache = new Map<string, ShellStaticRenderEntry>();
4070
4116
  const MAX_SHELL_STATIC_RENDER_CACHE_ENTRIES = 1024;
4071
4117
  const routeMiddlewareControlCache = new Map<string, Promise<RouteMiddlewareControl | undefined>>();
4072
4118
  const MAX_ROUTE_MIDDLEWARE_CONTROL_CACHE_ENTRIES = 1024;
4073
4119
  const appMiddlewareFileCache = new Map<string, Promise<boolean>>();
4074
4120
  const MAX_APP_MIDDLEWARE_FILE_CACHE_ENTRIES = 1024;
4075
- const devAppMiddlewareFileCache = new Map<string, { directoryStat: FileStat; hasMiddleware: boolean }>();
4121
+ const devAppMiddlewareFileCache = new Map<
4122
+ string,
4123
+ { directoryStat: FileStat; hasMiddleware: boolean }
4124
+ >();
4076
4125
  const routeMiddlewareControlSourceCache = new Map<
4077
4126
  string,
4078
4127
  { control: RouteMiddlewareControl | undefined; mtimeMs: number }
4079
4128
  >();
4080
- const devServerSourceFileCache = new Map<string, { mtimeMs: number; size: number; source: string }>();
4129
+ const devServerSourceFileCache = new Map<
4130
+ string,
4131
+ { mtimeMs: number; size: number; source: string }
4132
+ >();
4081
4133
  const MAX_DEV_SERVER_SOURCE_FILE_CACHE_ENTRIES = 2048;
4082
4134
 
4083
4135
  interface FileStat {
@@ -4111,10 +4163,7 @@ async function shellFilesForPage(
4111
4163
  if (devCacheKey !== undefined) {
4112
4164
  const directoryStats = await routeShellDirectoryStats(appDir, pageFile);
4113
4165
  const cached = devShellFilesCache.get(devCacheKey);
4114
- if (
4115
- cached !== undefined &&
4116
- sameFileStats(cached.directoryStats, directoryStats)
4117
- ) {
4166
+ if (cached !== undefined && sameFileStats(cached.directoryStats, directoryStats)) {
4118
4167
  return cached.files;
4119
4168
  }
4120
4169
 
@@ -4194,10 +4243,7 @@ async function hasAppMiddleware(options: {
4194
4243
  if (cacheKey === undefined) {
4195
4244
  const directoryStat = await fileStat(options.appDir);
4196
4245
  const cached = devAppMiddlewareFileCache.get(options.appDir);
4197
- if (
4198
- cached !== undefined &&
4199
- sameFileStat(cached.directoryStat, directoryStat)
4200
- ) {
4246
+ if (cached !== undefined && sameFileStat(cached.directoryStat, directoryStat)) {
4201
4247
  return cached.hasMiddleware;
4202
4248
  }
4203
4249
 
@@ -4413,6 +4459,7 @@ async function loadRouteData(options: {
4413
4459
  code: string;
4414
4460
  context: RouteDataContext;
4415
4461
  define?: UserConfig["define"] | undefined;
4462
+ devServerModuleCacheVersion?: string | undefined;
4416
4463
  filename: string;
4417
4464
  importPolicy?: AppRouterImportPolicy | undefined;
4418
4465
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
@@ -4440,6 +4487,7 @@ async function loadRouteDataWithInstrumentation(options: {
4440
4487
  code: string;
4441
4488
  context: RouteDataContext;
4442
4489
  define?: UserConfig["define"] | undefined;
4490
+ devServerModuleCacheVersion?: string | undefined;
4443
4491
  filename: string;
4444
4492
  importPolicy?: AppRouterImportPolicy | undefined;
4445
4493
  instrumentation?: RouterInstrumentation | undefined;
@@ -4480,16 +4528,19 @@ async function loadRouteLoaderModule(options: {
4480
4528
  appDir: string;
4481
4529
  code: string;
4482
4530
  define?: UserConfig["define"] | undefined;
4531
+ devServerModuleCacheVersion?: string | undefined;
4483
4532
  filename: string;
4484
4533
  importPolicy?: AppRouterImportPolicy | undefined;
4485
4534
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
4486
4535
  serverModuleCacheVersion?: string | undefined;
4487
4536
  vitePlugins?: readonly PluginOption[] | undefined;
4488
4537
  }): Promise<RouteLoaderModule> {
4538
+ const moduleCacheVersion =
4539
+ options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
4489
4540
  const cacheKey =
4490
- options.serverModuleCacheVersion === undefined
4541
+ moduleCacheVersion === undefined
4491
4542
  ? undefined
4492
- : `${options.appDir}\0${options.filename}\0${options.serverModuleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
4543
+ : `${options.serverModuleCacheVersion === undefined ? "dev" : "build"}\0${options.appDir}\0${options.filename}\0${moduleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
4493
4544
 
4494
4545
  if (cacheKey !== undefined) {
4495
4546
  const cached = readRouterRuntimeCacheEntry(
@@ -4568,6 +4619,7 @@ async function loadBundledRouteLoaderModule(options: {
4568
4619
  appDir: string;
4569
4620
  code: string;
4570
4621
  define?: UserConfig["define"] | undefined;
4622
+ devServerModuleCacheVersion?: string | undefined;
4571
4623
  filename: string;
4572
4624
  importPolicy?: AppRouterImportPolicy | undefined;
4573
4625
  prebuiltArtifact?: BuiltServerModuleOutputLike | undefined;
@@ -4598,10 +4650,10 @@ async function loadBundledRouteLoaderModule(options: {
4598
4650
  }));
4599
4651
 
4600
4652
  return await importAppRouterSourceModule<RouteLoaderModule>({
4601
- ...(options.serverModuleCacheVersion === undefined
4653
+ ...((options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion) === undefined
4602
4654
  ? {}
4603
4655
  : {
4604
- cacheKey: `loader:${options.filename}:${options.serverModuleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}`,
4656
+ cacheKey: `loader:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.filename}:${options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}:${vitePluginsCacheKey(options.vitePlugins)}`,
4605
4657
  }),
4606
4658
  code,
4607
4659
  define: options.define,
@@ -4614,6 +4666,7 @@ async function loadRouteMetadata(options: {
4614
4666
  code: string;
4615
4667
  context: RouteMetadataContext;
4616
4668
  define?: UserConfig["define"] | undefined;
4669
+ devServerModuleCacheVersion?: string | undefined;
4617
4670
  filename: string;
4618
4671
  importPolicy?: AppRouterImportPolicy | undefined;
4619
4672
  serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
@@ -4640,22 +4693,85 @@ async function loadRouteMetadata(options: {
4640
4693
  return await resolveRouteMetadataModule(module, options.context);
4641
4694
  }
4642
4695
 
4643
- const code = prebuiltArtifact?.code ?? (await bundleRouteMetadataModuleCode(options));
4644
-
4645
- const module = await importAppRouterSourceModule<RouteMetadataModule>({
4646
- ...(options.serverModuleCacheVersion === undefined
4647
- ? {}
4648
- : {
4649
- cacheKey: `metadata:${options.filename}:${options.serverModuleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}`,
4650
- }),
4651
- code,
4652
- define: options.define,
4653
- label: `metadata:${options.filename}`,
4654
- });
4696
+ const module = await loadRouteMetadataModule(options, prebuiltArtifact);
4655
4697
 
4656
4698
  return await resolveRouteMetadataModule(module, options.context);
4657
4699
  }
4658
4700
 
4701
+ async function loadRouteMetadataModule(
4702
+ options: {
4703
+ appDir: string;
4704
+ code: string;
4705
+ define?: UserConfig["define"] | undefined;
4706
+ devServerModuleCacheVersion?: string | undefined;
4707
+ filename: string;
4708
+ serverModuleCacheVersion?: string | undefined;
4709
+ vitePlugins?: readonly PluginOption[] | undefined;
4710
+ },
4711
+ prebuiltArtifact: BuiltServerModuleArtifact["routeMetadata"] | undefined,
4712
+ ): Promise<RouteMetadataModule> {
4713
+ if (prebuiltArtifact?.moduleFile !== undefined) {
4714
+ return await importBuiltServerModuleFile<RouteMetadataModule>({
4715
+ file: prebuiltArtifact.moduleFile,
4716
+ label: `metadata:${options.filename}`,
4717
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
4718
+ });
4719
+ }
4720
+
4721
+ const sourceHash = memoizedHashText(options.code);
4722
+ const moduleCacheVersion =
4723
+ options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
4724
+ const moduleCacheKey =
4725
+ moduleCacheVersion === undefined
4726
+ ? undefined
4727
+ : `metadata-module:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.filename}:${moduleCacheVersion}:${sourceHash}:${viteDefineCacheKey(options.define)}:${vitePluginsCacheKey(options.vitePlugins)}`;
4728
+
4729
+ if (moduleCacheKey !== undefined) {
4730
+ const cached = readRouterRuntimeCacheEntry(
4731
+ routeMetadataModuleCache,
4732
+ moduleCacheKey,
4733
+ routeMetadataModuleCacheCounters,
4734
+ );
4735
+ if (cached !== undefined) {
4736
+ return await cached;
4737
+ }
4738
+ }
4739
+
4740
+ const loaded = (async () => {
4741
+ const code = prebuiltArtifact?.code ?? (await bundleRouteMetadataModuleCode(options));
4742
+
4743
+ return await importAppRouterSourceModule<RouteMetadataModule>({
4744
+ ...(moduleCacheVersion === undefined
4745
+ ? {}
4746
+ : {
4747
+ cacheKey: `metadata:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.filename}:${moduleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}`,
4748
+ }),
4749
+ code,
4750
+ define: options.define,
4751
+ label: `metadata:${options.filename}`,
4752
+ });
4753
+ })();
4754
+
4755
+ if (moduleCacheKey !== undefined) {
4756
+ setBoundedCacheEntry(
4757
+ routeMetadataModuleCache,
4758
+ moduleCacheKey,
4759
+ loaded,
4760
+ maxRouteMetadataModuleCacheEntries,
4761
+ routeMetadataModuleCacheCounters,
4762
+ );
4763
+ }
4764
+
4765
+ try {
4766
+ return await loaded;
4767
+ } catch (error) {
4768
+ if (moduleCacheKey !== undefined) {
4769
+ routeMetadataModuleCache.delete(moduleCacheKey);
4770
+ }
4771
+ throw error;
4772
+ }
4773
+ }
4774
+
4659
4775
  async function resolveRouteMetadataModule(
4660
4776
  module: RouteMetadataModule,
4661
4777
  context: RouteMetadataContext,
@@ -4727,6 +4843,7 @@ async function loadComposedRouteMetadata(options: {
4727
4843
  code: string;
4728
4844
  context: RouteMetadataContext;
4729
4845
  define?: UserConfig["define"] | undefined;
4846
+ devServerModuleCacheVersion?: string | undefined;
4730
4847
  filename: string;
4731
4848
  importPolicy?: AppRouterImportPolicy | undefined;
4732
4849
  routes: readonly AppRoute[];
@@ -4772,14 +4889,18 @@ function composedRouteMetadataCacheKey(options: {
4772
4889
  appDir: string;
4773
4890
  code: string;
4774
4891
  define?: UserConfig["define"] | undefined;
4892
+ devServerModuleCacheVersion?: string | undefined;
4775
4893
  filename: string;
4776
4894
  importPolicy?: AppRouterImportPolicy | undefined;
4777
4895
  serverModuleCacheVersion?: string | undefined;
4778
4896
  vitePlugins?: readonly PluginOption[] | undefined;
4779
4897
  }): string | undefined {
4780
- return options.serverModuleCacheVersion === undefined
4898
+ const moduleCacheVersion =
4899
+ options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
4900
+
4901
+ return moduleCacheVersion === undefined
4781
4902
  ? undefined
4782
- : `${options.appDir}\0${options.filename}\0${options.serverModuleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
4903
+ : `${options.appDir}\0${options.filename}\0${options.serverModuleCacheVersion === undefined ? "dev" : "build"}\0${moduleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
4783
4904
  }
4784
4905
 
4785
4906
  export const __composedRouteMetadataCacheKeyForTesting = composedRouteMetadataCacheKey;
@@ -4789,6 +4910,7 @@ async function loadComposedRouteMetadataUncached(options: {
4789
4910
  code: string;
4790
4911
  context: RouteMetadataContext;
4791
4912
  define?: UserConfig["define"] | undefined;
4913
+ devServerModuleCacheVersion?: string | undefined;
4792
4914
  filename: string;
4793
4915
  importPolicy?: AppRouterImportPolicy | undefined;
4794
4916
  routes: readonly AppRoute[];
@@ -4822,6 +4944,7 @@ async function loadComposedRouteMetadataUncached(options: {
4822
4944
  importPolicy: options.importPolicy,
4823
4945
  serverModules: options.serverModules,
4824
4946
  serverModuleCacheVersion: options.serverModuleCacheVersion,
4947
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
4825
4948
  define: options.define,
4826
4949
  vitePlugins: options.vitePlugins,
4827
4950
  });
@@ -4836,6 +4959,7 @@ async function loadComposedRouteMetadataUncached(options: {
4836
4959
  importPolicy: options.importPolicy,
4837
4960
  serverModules: options.serverModules,
4838
4961
  serverModuleCacheVersion: options.serverModuleCacheVersion,
4962
+ devServerModuleCacheVersion: options.devServerModuleCacheVersion,
4839
4963
  define: options.define,
4840
4964
  vitePlugins: options.vitePlugins,
4841
4965
  });
@@ -5083,9 +5207,7 @@ function injectQueryState(html: string, state: DehydratedQueryClient): string {
5083
5207
  JSON.stringify(state),
5084
5208
  )}</script>`;
5085
5209
 
5086
- return /<\/body>/i.test(html)
5087
- ? replaceFinalBodyCloseTag(html, script)
5088
- : `${html}${script}`;
5210
+ return /<\/body>/i.test(html) ? replaceFinalBodyCloseTag(html, script) : `${html}${script}`;
5089
5211
  }
5090
5212
 
5091
5213
  function injectAuthSessionClaims(html: string, claims: unknown): string {
@@ -5097,9 +5219,7 @@ function injectAuthSessionClaims(html: string, claims: unknown): string {
5097
5219
  JSON.stringify(claims),
5098
5220
  )}</script>`;
5099
5221
 
5100
- return /<\/body>/i.test(html)
5101
- ? replaceFinalBodyCloseTag(html, script)
5102
- : `${html}${script}`;
5222
+ return /<\/body>/i.test(html) ? replaceFinalBodyCloseTag(html, script) : `${html}${script}`;
5103
5223
  }
5104
5224
 
5105
5225
  function replaceFinalBodyCloseTag(html: string, insertion: string): string {
@@ -5183,11 +5303,7 @@ function readServerSourceFile(
5183
5303
  async function readDevServerSourceFile(file: string): Promise<string> {
5184
5304
  const stats = await fileStat(file);
5185
5305
  const cached = devServerSourceFileCache.get(file);
5186
- if (
5187
- cached !== undefined &&
5188
- cached.mtimeMs === stats.mtimeMs &&
5189
- cached.size === stats.size
5190
- ) {
5306
+ if (cached !== undefined && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
5191
5307
  return cached.source;
5192
5308
  }
5193
5309