@reckona/mreact-router 0.0.30 → 0.0.32

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.
package/dist/render.js CHANGED
@@ -5,19 +5,20 @@ import { dirname, join, relative, sep } from "node:path";
5
5
  import { transform, } from "@reckona/mreact-compiler";
6
6
  import { createQueryClient, dehydrate, __MREACT_QUERY_STATE_SCRIPT_ID, runWithQueryClient, } from "@reckona/mreact-query";
7
7
  import { createStringSink, renderAsyncBoundary, renderOutOfOrderReorderScript, renderToReadableStream, } from "@reckona/mreact-server";
8
- import { hydrationMarkerParts, inferClientRouteModule, routeIdForPath, withHydrationMarkers, withRouteMarkers, } from "./client.js";
8
+ import { createClientRouteInferenceCache, hydrationMarkerParts, formatClientRouteInferenceDiagnostic, inferClientRouteModule, routeIdForPath, withHydrationMarkers, withRouteMarkers, } from "./client.js";
9
9
  import { assetPath } from "./assets.js";
10
10
  import { escapeHtmlAttribute, escapeHtmlText as escapeHtml, } from "@reckona/mreact-shared/html-escape";
11
11
  import { matchRoute, scanAppRoutes } from "./routes.js";
12
12
  import { dispatchServerActionRequest, prepareRouteServerActions, serverActionCookie, } from "./actions.js";
13
13
  import { beginRouteCacheContext, cachedRouteResponse, cacheRouteResponse, routeCacheKey, routeCachePolicyFromSource, } from "./cache.js";
14
14
  import { resolveRouterCacheLimit } from "./cache-config.js";
15
- import { importAppRouterFileModule, importAppRouterSourceModule } from "./module-runner.js";
15
+ import { importAppRouterBuiltFileModule, importAppRouterFileModule, importAppRouterSourceModule, } from "./module-runner.js";
16
16
  import { contentSecurityPolicy } from "./csp.js";
17
17
  import { htmlResponse } from "./http.js";
18
18
  import { isNotFoundError, isRedirectError, rewriteLocation } from "./navigation.js";
19
19
  import { createAppRouterImportPolicyPlugin } from "./import-policy.js";
20
- import { hasLoaderExport, isStreamRouteSource, stripRouteLoaderOnlyExports, stripRouteMetadataOnlyExports, stripRouteModuleExports, } from "./route-source.js";
20
+ import { existingRouteShellCandidates } from "./route-shells.js";
21
+ import { hasLoaderExport, isStreamRouteSource, stripRouteClientOnlyExports, stripRouteLoaderOnlyExports, stripRouteMetadataOnlyExports, stripRouteModuleExports, } from "./route-source.js";
21
22
  import { emitRouterLog, logDurationMs, logNow } from "./logger.js";
22
23
  import { createRouterRuntimeCacheCounters, readRouterRuntimeCacheEntry, routerRuntimeCacheStat, } from "./cache-stats.js";
23
24
  import { bundleRouterModule } from "./bundle-pipeline.js";
@@ -29,6 +30,7 @@ const nativeEscapeTransform = {
29
30
  const authRuntimeStateKey = "__mreactAuthRuntimeState";
30
31
  const authSessionScriptId = "__mreact_auth_session";
31
32
  export async function preloadBuiltRequestModules(options) {
33
+ const clientRouteInferenceCache = createClientRouteInferenceCache();
32
34
  const middlewareFiles = [
33
35
  join(options.appDir, "middleware.ts"),
34
36
  join(options.appDir, "middleware.mreact.ts"),
@@ -58,10 +60,12 @@ export async function preloadBuiltRequestModules(options) {
58
60
  }
59
61
  if (options.includeRenderModules !== false) {
60
62
  const analysis = await analyzeRouteSource({
63
+ appDir: options.appDir,
61
64
  artifact: options.serverModules?.get(route.file)?.analysis,
62
65
  code,
63
66
  filename: route.file,
64
67
  routePath: route.path,
68
+ clientRouteInferenceCache,
65
69
  serverModuleCacheVersion: options.serverModuleCacheVersion,
66
70
  });
67
71
  await preloadBuiltPageRouteModules({
@@ -251,6 +255,19 @@ async function waitForRenderPreload(options, timing) {
251
255
  finishRenderTimingPhase(timing, phaseStartedAt, "preloadWaitMs");
252
256
  }
253
257
  }
258
+ async function loadServerRenderArtifacts(options, routeFile, timing) {
259
+ const loader = options.__mreactLoadServerRenderArtifacts;
260
+ if (loader === undefined) {
261
+ return;
262
+ }
263
+ const phaseStartedAt = renderTimingPhaseStartedAt(timing);
264
+ try {
265
+ await loader(routeFile);
266
+ }
267
+ finally {
268
+ finishRenderTimingPhase(timing, phaseStartedAt, "renderArtifactLoadMs");
269
+ }
270
+ }
254
271
  function emitRenderTiming(options, timing, status) {
255
272
  if (timing === undefined) {
256
273
  return;
@@ -279,6 +296,7 @@ export async function resolveAppRouterMiddleware(options) {
279
296
  }
280
297
  async function renderAppRequestInternal(options) {
281
298
  const timing = createRenderTiming(options.logger);
299
+ const clientRouteInferenceCache = createClientRouteInferenceCache();
282
300
  let phaseStartedAt = renderTimingPhaseStartedAt(timing);
283
301
  const routes = options.routes ?? (await scanAppRoutes({ appDir: options.appDir }));
284
302
  finishRenderTimingPhase(timing, phaseStartedAt, "routeScanMs");
@@ -400,10 +418,12 @@ async function renderAppRequestInternal(options) {
400
418
  finishRenderTimingPhase(timing, phaseStartedAt, "readSourceMs");
401
419
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
402
420
  const originalAnalysis = await analyzeRouteSource({
421
+ appDir: options.appDir,
403
422
  artifact: options.serverModules?.get(matched.route.file)?.analysis,
404
423
  code: originalCode,
405
424
  filename: matched.route.file,
406
425
  routePath: matched.route.path,
426
+ clientRouteInferenceCache,
407
427
  serverModuleCacheVersion: options.serverModuleCacheVersion,
408
428
  timing,
409
429
  });
@@ -438,9 +458,11 @@ async function renderAppRequestInternal(options) {
438
458
  const routeAnalysis = code === originalCode
439
459
  ? originalAnalysis
440
460
  : await analyzeRouteSource({
461
+ appDir: options.appDir,
441
462
  code,
442
463
  filename: matched.route.file,
443
464
  routePath: matched.route.path,
465
+ clientRouteInferenceCache,
444
466
  serverModuleCacheVersion: undefined,
445
467
  });
446
468
  finishRenderTimingPhase(timing, phaseStartedAt, "routeCodeAnalysisMs");
@@ -514,6 +536,7 @@ async function renderAppRequestInternal(options) {
514
536
  return data;
515
537
  }
516
538
  await waitForRenderPreload(options, timing);
539
+ await loadServerRenderArtifacts(options, matched.route.file, timing);
517
540
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
518
541
  const stringOutput = transformServerModule({
519
542
  code: routeCode,
@@ -570,6 +593,7 @@ async function renderAppRequestInternal(options) {
570
593
  serverModules: options.serverModules,
571
594
  serverModuleCacheVersion: options.serverModuleCacheVersion,
572
595
  serverSourceFiles: options.serverSourceFiles,
596
+ clientRouteInferenceCache,
573
597
  timing,
574
598
  }));
575
599
  const metadata = await loadComposedRouteMetadata({
@@ -612,6 +636,7 @@ async function renderAppRequestInternal(options) {
612
636
  }
613
637
  }
614
638
  await waitForRenderPreload(options, timing);
639
+ await loadServerRenderArtifacts(options, matched.route.file, timing);
615
640
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
616
641
  const output = transformServerModule({
617
642
  code: routeCode,
@@ -646,6 +671,7 @@ async function renderAppRequestInternal(options) {
646
671
  serverModules: options.serverModules,
647
672
  serverModuleCacheVersion: options.serverModuleCacheVersion,
648
673
  serverSourceFiles: options.serverSourceFiles,
674
+ clientRouteInferenceCache,
649
675
  script: clientScript,
650
676
  clientReferenceManifest: output.metadata.clientReferenceManifest,
651
677
  });
@@ -674,6 +700,7 @@ async function renderAppRequestInternal(options) {
674
700
  serverModules: options.serverModules,
675
701
  serverModuleCacheVersion: options.serverModuleCacheVersion,
676
702
  serverSourceFiles: options.serverSourceFiles,
703
+ clientRouteInferenceCache,
677
704
  clientRoute,
678
705
  script: clientScript,
679
706
  clientReferenceManifest: output.metadata.clientReferenceManifest,
@@ -698,6 +725,7 @@ async function renderAppRequestInternal(options) {
698
725
  return data;
699
726
  }
700
727
  await waitForRenderPreload(options, timing);
728
+ await loadServerRenderArtifacts(options, matched.route.file, timing);
701
729
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
702
730
  const output = transformServerModule({
703
731
  code: routeCode,
@@ -762,6 +790,7 @@ async function renderAppRequestInternal(options) {
762
790
  serverModules: options.serverModules,
763
791
  serverModuleCacheVersion: options.serverModuleCacheVersion,
764
792
  serverSourceFiles: options.serverSourceFiles,
793
+ clientRouteInferenceCache,
765
794
  timing,
766
795
  }));
767
796
  finishRenderTimingPhase(timing, phaseStartedAt, "layoutRenderMs");
@@ -1088,6 +1117,13 @@ async function loadServerRouteModule(options) {
1088
1117
  const code = await readServerSourceFile(options.file, options.serverModuleCacheVersion, options.serverSourceFiles);
1089
1118
  const artifactCode = options.serverModules?.get(options.file)?.request;
1090
1119
  const codeHash = memoizedHashText(code);
1120
+ if (artifactCode !== undefined && artifactCode.sourceHash === codeHash && artifactCode.moduleFile !== undefined) {
1121
+ return await importBuiltServerModuleFile({
1122
+ file: artifactCode.moduleFile,
1123
+ label: `server-route:${options.file}`,
1124
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
1125
+ });
1126
+ }
1091
1127
  const moduleCode = artifactCode !== undefined && artifactCode.sourceHash === codeHash ? artifactCode.code : code;
1092
1128
  const cacheKey = `server-route\0${options.file}\0${options.serverModuleCacheVersion}\0${codeHash}\0${memoizedHashText(moduleCode)}`;
1093
1129
  const cached = readRouterRuntimeCacheEntry(serverRouteModuleCache, cacheKey, serverRouteModuleCacheCounters);
@@ -1254,7 +1290,7 @@ async function loadMiddlewareModule(options) {
1254
1290
  code,
1255
1291
  file: options.file,
1256
1292
  importPolicy: options.importPolicy,
1257
- prebuiltCode: prebuiltRequestModuleCode(options.serverModules, options.file, code),
1293
+ prebuiltArtifact: prebuiltRequestModuleArtifact(options.serverModules, options.file, code),
1258
1294
  serverModuleCacheVersion: options.serverModuleCacheVersion,
1259
1295
  }).catch((error) => {
1260
1296
  if (cacheKey !== undefined) {
@@ -1287,7 +1323,14 @@ export async function bundleMiddlewareModuleCode(options) {
1287
1323
  return compiled;
1288
1324
  }
1289
1325
  async function loadBundledMiddlewareModule(options) {
1290
- const compiled = options.prebuiltCode ??
1326
+ if (options.prebuiltArtifact?.moduleFile !== undefined) {
1327
+ return await importBuiltServerModuleFile({
1328
+ file: options.prebuiltArtifact.moduleFile,
1329
+ label: `middleware:${options.file}`,
1330
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
1331
+ });
1332
+ }
1333
+ const compiled = options.prebuiltArtifact?.code ??
1291
1334
  (await bundleMiddlewareModuleCode({
1292
1335
  appDir: options.appDir,
1293
1336
  code: options.code,
@@ -1416,6 +1459,8 @@ function routeSourceAnalysisFromArtifact(artifact) {
1416
1459
  async function analyzeRouteSourceUncached(options) {
1417
1460
  const routeCode = stripRouteModuleExports(options.code);
1418
1461
  const clientInference = await inferClientRouteModule({
1462
+ appDir: options.appDir,
1463
+ cache: options.clientRouteInferenceCache,
1419
1464
  code: routeCode,
1420
1465
  filename: options.filename,
1421
1466
  routePath: options.routePath,
@@ -1476,6 +1521,15 @@ async function loadServerModule(code, sourcefile, serverModules, serverModuleCac
1476
1521
  const artifact = serverModules?.get(sourcefile)?.string;
1477
1522
  const codeHash = memoizedHashText(code);
1478
1523
  const prebuiltCode = prebuiltServerComponentModuleCode(artifact, code, codeHash);
1524
+ if (artifact !== undefined &&
1525
+ prebuiltServerModuleOutputMatches(artifact, code, codeHash) &&
1526
+ artifact.moduleFile !== undefined) {
1527
+ return await importBuiltServerModuleFile({
1528
+ file: artifact.moduleFile,
1529
+ label: `server-component:${sourcefile}`,
1530
+ serverModuleCacheVersion,
1531
+ });
1532
+ }
1479
1533
  const moduleCode = prebuiltCode ?? code;
1480
1534
  const cacheKey = serverModuleCacheVersion === undefined
1481
1535
  ? undefined
@@ -1501,11 +1555,24 @@ function prebuiltServerComponentModuleCode(artifact, code, codeHash) {
1501
1555
  if (artifact === undefined) {
1502
1556
  return undefined;
1503
1557
  }
1504
- if (artifact.sourceHash !== codeHash && artifact.code !== code) {
1558
+ if (!prebuiltServerModuleOutputMatches(artifact, code, codeHash)) {
1505
1559
  return undefined;
1506
1560
  }
1507
1561
  return artifact.bundleCode;
1508
1562
  }
1563
+ function prebuiltServerModuleOutputMatches(artifact, code, codeHash) {
1564
+ return artifact.sourceHash === codeHash || artifact.code === code;
1565
+ }
1566
+ async function importBuiltServerModuleFile(options) {
1567
+ return await importAppRouterBuiltFileModule({
1568
+ ...(options.serverModuleCacheVersion === undefined
1569
+ ? {}
1570
+ : {
1571
+ cacheKey: `${options.label}:${options.serverModuleCacheVersion}:${options.file}`,
1572
+ }),
1573
+ file: options.file,
1574
+ });
1575
+ }
1509
1576
  async function loadServerComponent(code, sourcefile, serverModules, serverModuleCacheVersion) {
1510
1577
  const module = await loadServerModule(code, sourcefile, serverModules, serverModuleCacheVersion);
1511
1578
  return selectServerComponent(module);
@@ -1535,7 +1602,7 @@ function runServerStreamModule(code, options) {
1535
1602
  serverModules: options.serverModules,
1536
1603
  serverModuleCacheVersion: options.serverModuleCacheVersion,
1537
1604
  });
1538
- const layoutShells = await layoutShellsForPage(options.appDir, options.pageFile, options.props, slots, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles);
1605
+ const layoutShells = await layoutShellsForPage(options.appDir, options.pageFile, options.props, slots, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.clientRouteInferenceCache);
1539
1606
  const marker = options.clientRoute
1540
1607
  ? hydrationMarkerParts({
1541
1608
  assetBaseUrl: options.assetBaseUrl,
@@ -1668,7 +1735,7 @@ async function runServerStreamModuleWithLoading(code, options) {
1668
1735
  queryClient: options.queryClient,
1669
1736
  request: options.request,
1670
1737
  };
1671
- const layoutShells = await layoutShellsForPage(options.appDir, options.pageFile, loadingProps, {}, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles);
1738
+ const layoutShells = await layoutShellsForPage(options.appDir, options.pageFile, loadingProps, {}, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.clientRouteInferenceCache);
1672
1739
  const loadingHtml = await renderServerFileToHtml(options.loadingFile, loadingProps, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles);
1673
1740
  const marker = options.clientRoute
1674
1741
  ? hydrationMarkerParts({
@@ -1770,6 +1837,15 @@ async function loadServerStreamModule(code, sourcefile, serverModules, serverMod
1770
1837
  const artifactCode = serverModules?.get(sourcefile)?.stream;
1771
1838
  const codeHash = memoizedHashText(code);
1772
1839
  const prebuiltCode = prebuiltServerComponentModuleCode(artifactCode, code, codeHash);
1840
+ if (artifactCode !== undefined &&
1841
+ prebuiltServerModuleOutputMatches(artifactCode, code, codeHash) &&
1842
+ artifactCode.moduleFile !== undefined) {
1843
+ return await importBuiltServerModuleFile({
1844
+ file: artifactCode.moduleFile,
1845
+ label: `server-stream-component:${sourcefile}`,
1846
+ serverModuleCacheVersion,
1847
+ });
1848
+ }
1773
1849
  const moduleCode = prebuiltCode ?? code;
1774
1850
  const cacheKey = serverModuleCacheVersion === undefined
1775
1851
  ? undefined
@@ -1803,7 +1879,7 @@ async function applyLayouts(options) {
1803
1879
  let html = options.html;
1804
1880
  const slotContext = createSlotRenderContext(options.slots);
1805
1881
  for (const shell of layoutFiles.reverse()) {
1806
- const rendered = await renderShellPrefixSuffix(options.appDir, shell, options.props, slotContext, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.timing);
1882
+ const rendered = await renderShellPrefixSuffix(options.appDir, shell, options.props, slotContext, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.clientRouteInferenceCache, options.timing);
1807
1883
  html = `${rendered.prefix}${html}${rendered.suffix}`;
1808
1884
  }
1809
1885
  warnUnconsumedRouteSlots({
@@ -1814,12 +1890,12 @@ async function applyLayouts(options) {
1814
1890
  });
1815
1891
  return html;
1816
1892
  }
1817
- async function layoutShellsForPage(appDir, pageFile, props, slots, serverModules, serverModuleCacheVersion, serverSourceFiles) {
1893
+ async function layoutShellsForPage(appDir, pageFile, props, slots, serverModules, serverModuleCacheVersion, serverSourceFiles, clientRouteInferenceCache) {
1818
1894
  const layoutFiles = await shellFilesForPage(appDir, pageFile, serverModuleCacheVersion);
1819
1895
  const shells = [];
1820
1896
  const slotContext = createSlotRenderContext(slots);
1821
1897
  for (const shell of layoutFiles) {
1822
- shells.push(await renderShellPrefixSuffix(appDir, shell, props, slotContext, serverModules, serverModuleCacheVersion, serverSourceFiles));
1898
+ shells.push(await renderShellPrefixSuffix(appDir, shell, props, slotContext, serverModules, serverModuleCacheVersion, serverSourceFiles, clientRouteInferenceCache));
1823
1899
  }
1824
1900
  warnUnconsumedRouteSlots({
1825
1901
  appDir,
@@ -1829,7 +1905,7 @@ async function layoutShellsForPage(appDir, pageFile, props, slots, serverModules
1829
1905
  });
1830
1906
  return shells;
1831
1907
  }
1832
- async function renderShellPrefixSuffix(appDir, shell, props, slotContext, serverModules, serverModuleCacheVersion, serverSourceFiles, timing) {
1908
+ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, serverModules, serverModuleCacheVersion, serverSourceFiles, clientRouteInferenceCache, timing) {
1833
1909
  const hasNamedSlots = Object.keys(slotContext.namedSlots).length > 0;
1834
1910
  const cacheKey = serverModuleCacheVersion === undefined || hasNamedSlots || shell.kind === "template"
1835
1911
  ? undefined
@@ -1844,8 +1920,20 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
1844
1920
  const code = await readServerSourceFile(shell.file, serverModuleCacheVersion, serverSourceFiles);
1845
1921
  addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutSourceReadMs");
1846
1922
  phaseStartedAt = renderTimingPhaseStartedAt(timing);
1923
+ const artifact = serverModules?.get(shell.file)?.string;
1924
+ const clientInference = artifact !== undefined && artifact.sourceHash === memoizedHashText(code)
1925
+ ? { client: false, clientBoundaryImports: [], diagnostics: [] }
1926
+ : await inferClientRouteModule({
1927
+ cache: clientRouteInferenceCache,
1928
+ code: stripRouteClientOnlyExports(code),
1929
+ filename: shell.file,
1930
+ });
1931
+ for (const diagnostic of clientInference.diagnostics) {
1932
+ console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
1933
+ }
1847
1934
  const output = transformServerModule({
1848
1935
  code,
1936
+ clientBoundaryImports: clientInference.clientBoundaryImports,
1849
1937
  filename: shell.file,
1850
1938
  serverModules,
1851
1939
  serverOutput: "string",
@@ -1930,31 +2018,20 @@ async function shellFilesForPage(appDir, pageFile, serverModuleCacheVersion) {
1930
2018
  return cached;
1931
2019
  }
1932
2020
  }
1933
- const relativeDir = relative(appDir, dirname(pageFile));
1934
- const parts = relativeDir === "" ? [] : relativeDir.split("/");
1935
- const directories = [appDir];
1936
- for (let index = 0; index < parts.length; index += 1) {
1937
- directories.push(join(appDir, ...parts.slice(0, index + 1)));
1938
- }
1939
- const files = [];
1940
- for (const directory of directories) {
1941
- const shellId = shellBoundaryId(appDir, directory);
1942
- for (const [filename, kind] of [
1943
- ["layout.tsx", "layout"],
1944
- ["layout.mreact.tsx", "layout"],
1945
- ["template.tsx", "template"],
1946
- ["template.mreact.tsx", "template"],
1947
- ]) {
1948
- const candidate = join(directory, filename);
1949
- try {
1950
- await access(candidate);
1951
- files.push({ file: candidate, id: shellId, kind });
1952
- }
1953
- catch {
1954
- // Missing shell files are allowed.
1955
- }
2021
+ const shells = await existingRouteShellCandidates(appDir, pageFile, async (file) => {
2022
+ try {
2023
+ await access(file);
2024
+ return true;
1956
2025
  }
1957
- }
2026
+ catch {
2027
+ return false;
2028
+ }
2029
+ });
2030
+ const files = shells.map((shell) => ({
2031
+ file: shell.file,
2032
+ id: shellBoundaryId(appDir, shell.directory),
2033
+ kind: shell.kind,
2034
+ }));
1958
2035
  if (cacheKey !== undefined) {
1959
2036
  if (shellFilesCache.size >= MAX_SHELL_FILES_CACHE_ENTRIES) {
1960
2037
  const oldestKey = shellFilesCache.keys().next().value;
@@ -2238,7 +2315,7 @@ async function loadRouteLoaderModule(options) {
2238
2315
  }
2239
2316
  const loaded = loadBundledRouteLoaderModule({
2240
2317
  ...options,
2241
- prebuiltCode: prebuiltRouteLoaderModuleCode(options.serverModules, options.filename, options.code),
2318
+ prebuiltArtifact: prebuiltRouteLoaderModuleArtifact(options.serverModules, options.filename, options.code),
2242
2319
  }).catch((error) => {
2243
2320
  if (cacheKey !== undefined) {
2244
2321
  routeLoaderModuleCache.delete(cacheKey);
@@ -2270,7 +2347,14 @@ export async function bundleRouteLoaderModuleCode(options) {
2270
2347
  return code;
2271
2348
  }
2272
2349
  async function loadBundledRouteLoaderModule(options) {
2273
- const code = options.prebuiltCode ??
2350
+ if (options.prebuiltArtifact?.moduleFile !== undefined) {
2351
+ return await importBuiltServerModuleFile({
2352
+ file: options.prebuiltArtifact.moduleFile,
2353
+ label: `loader:${options.filename}`,
2354
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
2355
+ });
2356
+ }
2357
+ const code = options.prebuiltArtifact?.code ??
2274
2358
  (await bundleRouteLoaderModuleCode({
2275
2359
  appDir: options.appDir,
2276
2360
  code: options.code,
@@ -2287,17 +2371,17 @@ async function loadBundledRouteLoaderModule(options) {
2287
2371
  label: `loader:${options.filename}`,
2288
2372
  });
2289
2373
  }
2290
- function prebuiltRequestModuleCode(serverModules, file, source, kind = "request") {
2374
+ function prebuiltRequestModuleArtifact(serverModules, file, source, kind = "request") {
2291
2375
  const artifact = serverModules?.get(file)?.[kind];
2292
2376
  return artifact !== undefined && artifact.sourceHash === memoizedHashText(source)
2293
- ? artifact.code
2377
+ ? artifact
2294
2378
  : undefined;
2295
2379
  }
2296
- function prebuiltRouteLoaderModuleCode(serverModules, file, source) {
2380
+ function prebuiltRouteLoaderModuleArtifact(serverModules, file, source) {
2297
2381
  const artifact = serverModules?.get(file)?.loader;
2298
2382
  return artifact !== undefined && artifact.sourceHash === memoizedHashText(source)
2299
- ? artifact.code
2300
- : prebuiltRequestModuleCode(serverModules, file, source);
2383
+ ? artifact
2384
+ : prebuiltRequestModuleArtifact(serverModules, file, source);
2301
2385
  }
2302
2386
  function importPolicyCacheKey(policy) {
2303
2387
  if (policy === undefined) {
@@ -2313,8 +2397,16 @@ async function loadRouteMetadata(options) {
2313
2397
  if (!hasMetadataExport(options.code)) {
2314
2398
  return undefined;
2315
2399
  }
2316
- const prebuiltCode = prebuiltRequestModuleCode(options.serverModules, options.filename, options.code, "routeMetadata");
2317
- const code = prebuiltCode ?? await bundleRouteMetadataModuleCode(options);
2400
+ const prebuiltArtifact = prebuiltRequestModuleArtifact(options.serverModules, options.filename, options.code, "routeMetadata");
2401
+ if (prebuiltArtifact?.moduleFile !== undefined) {
2402
+ const module = await importBuiltServerModuleFile({
2403
+ file: prebuiltArtifact.moduleFile,
2404
+ label: `metadata:${options.filename}`,
2405
+ serverModuleCacheVersion: options.serverModuleCacheVersion,
2406
+ });
2407
+ return module.metadata;
2408
+ }
2409
+ const code = prebuiltArtifact?.code ?? await bundleRouteMetadataModuleCode(options);
2318
2410
  const module = await importAppRouterSourceModule({
2319
2411
  ...(options.serverModuleCacheVersion === undefined
2320
2412
  ? {}