@reckona/mreact-router 0.0.157 → 0.0.159

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-router",
3
- "version": "0.0.157",
3
+ "version": "0.0.159",
4
4
  "description": "File-system app router, SSR, actions, and deployment adapters for mreact.",
5
5
  "keywords": [
6
6
  "jsx",
@@ -105,20 +105,20 @@
105
105
  },
106
106
  "dependencies": {
107
107
  "typescript": "^6.0.3",
108
- "@reckona/mreact-compat": "0.0.157",
109
- "@reckona/mreact-devtools": "0.0.157",
110
- "@reckona/mreact-query": "0.0.157",
111
- "@reckona/mreact-reactive-core": "0.0.157",
112
- "@reckona/mreact-compiler": "0.0.157",
113
- "@reckona/mreact": "0.0.157",
114
- "@reckona/mreact-reactive-dom": "0.0.157",
115
- "@reckona/mreact-server": "0.0.157",
116
- "@reckona/mreact-shared": "0.0.157"
108
+ "@reckona/mreact": "0.0.159",
109
+ "@reckona/mreact-compat": "0.0.159",
110
+ "@reckona/mreact-compiler": "0.0.159",
111
+ "@reckona/mreact-query": "0.0.159",
112
+ "@reckona/mreact-reactive-core": "0.0.159",
113
+ "@reckona/mreact-devtools": "0.0.159",
114
+ "@reckona/mreact-reactive-dom": "0.0.159",
115
+ "@reckona/mreact-server": "0.0.159",
116
+ "@reckona/mreact-shared": "0.0.159"
117
117
  },
118
118
  "peerDependencies": {
119
119
  "vite": ">=8 <9"
120
120
  },
121
121
  "optionalDependencies": {
122
- "@reckona/mreact-router-native": "0.0.157"
122
+ "@reckona/mreact-router-native": "0.0.159"
123
123
  }
124
124
  }
package/src/build.ts CHANGED
@@ -85,6 +85,7 @@ import {
85
85
  bundleRouterModules,
86
86
  type RouterCompatPlugin,
87
87
  type RouterBundleChunkOutput,
88
+ type RouterBundleModulesOutput,
88
89
  type RouterBundleOutput,
89
90
  } from "./bundle-pipeline.js";
90
91
  import { collectRouteCssFilesFromSources, collectSpecialBoundaryFiles } from "./route-styles.js";
@@ -448,7 +449,7 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
448
449
  );
449
450
 
450
451
  const clientRouteInferenceCache = createClientRouteInferenceCache();
451
- const [serverActionManifest, serverModules, generatedImportPolicy] = await Promise.all([
452
+ const [serverActionManifest, generatedImportPolicy] = await Promise.all([
452
453
  shouldTrackBuildPhases === false
453
454
  ? collectBuildServerActionManifest({
454
455
  files,
@@ -464,8 +465,32 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
464
465
  routesDir: project.routesDir,
465
466
  }),
466
467
  ),
468
+ shouldTrackBuildPhases === false
469
+ ? buildGeneratedImportPolicy({
470
+ files,
471
+ projectRoot: project.projectRoot,
472
+ routes,
473
+ routesDir: project.routesDir,
474
+ })
475
+ : timeBuildPhase(timingSink, progressSink, "importPolicy", () =>
476
+ buildGeneratedImportPolicy({
477
+ files,
478
+ projectRoot: project.projectRoot,
479
+ routes,
480
+ routesDir: project.routesDir,
481
+ }),
482
+ ),
483
+ ]);
484
+ const actionRenderBundleExcludedFiles = new Set(
485
+ [...serverActionManifest.routeReferences.entries()]
486
+ .filter(([, references]) => references.length > 0)
487
+ .map(([file]) => file),
488
+ );
489
+ const { artifacts: serverModules, sharedChunks: serverModuleSharedChunks } = await (
467
490
  shouldTrackBuildPhases === false
468
491
  ? buildServerModuleArtifacts({
492
+ actionRenderBundleExcludedFiles,
493
+ bundleRequestRuntimePackages: shouldBuildAwsLambda,
469
494
  bundleCache: new Map(),
470
495
  clientRouteInferenceCache: serverClientRouteInferenceCache,
471
496
  define: viteDefine,
@@ -480,6 +505,8 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
480
505
  })
481
506
  : timeBuildPhase(timingSink, progressSink, "serverModules", () =>
482
507
  buildServerModuleArtifacts({
508
+ actionRenderBundleExcludedFiles,
509
+ bundleRequestRuntimePackages: shouldBuildAwsLambda,
483
510
  bundleCache: new Map(),
484
511
  clientRouteInferenceCache: serverClientRouteInferenceCache,
485
512
  define: viteDefine,
@@ -492,23 +519,8 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
492
519
  serverTransformCache,
493
520
  vitePlugins,
494
521
  }),
495
- ),
496
- shouldTrackBuildPhases === false
497
- ? buildGeneratedImportPolicy({
498
- files,
499
- projectRoot: project.projectRoot,
500
- routes,
501
- routesDir: project.routesDir,
502
- })
503
- : timeBuildPhase(timingSink, progressSink, "importPolicy", () =>
504
- buildGeneratedImportPolicy({
505
- files,
506
- projectRoot: project.projectRoot,
507
- routes,
508
- routesDir: project.routesDir,
509
- }),
510
- ),
511
- ]);
522
+ )
523
+ );
512
524
  const serverRoutes = routes.map((route) => ({
513
525
  ...route,
514
526
  file: relative(project.projectRoot, route.file),
@@ -519,12 +531,14 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
519
531
  serverDir,
520
532
  serverModules,
521
533
  generatedImportPolicy.runtimePackages,
534
+ serverModuleSharedChunks,
522
535
  )
523
536
  : timeBuildPhase(timingSink, progressSink, "serverModuleArtifacts", () =>
524
537
  writeServerModuleArtifactFiles(
525
538
  serverDir,
526
539
  serverModules,
527
540
  generatedImportPolicy.runtimePackages,
541
+ serverModuleSharedChunks,
528
542
  ),
529
543
  ),
530
544
  shouldTrackBuildPhases === false
@@ -1043,6 +1057,7 @@ async function writeServerModuleArtifactFiles(
1043
1057
  serverDir: string,
1044
1058
  serverModules: Record<string, BuiltServerModuleArtifact>,
1045
1059
  portableRuntimePackages: readonly string[] = [],
1060
+ sharedChunks: readonly SharedServerModuleChunk[] = [],
1046
1061
  ): Promise<{
1047
1062
  files: Record<string, string>;
1048
1063
  renderFiles: Record<string, string>;
@@ -1064,6 +1079,22 @@ async function writeServerModuleArtifactFiles(
1064
1079
  mkdir(join(modulesDir, "render"), { recursive: true }),
1065
1080
  ]);
1066
1081
 
1082
+ if (sharedChunks.length > 0) {
1083
+ // Shared request chunks are referenced from externalized module code via
1084
+ // "./chunks/..." imports, so they live next to server-modules/code files.
1085
+ await mkdir(join(modulesDir, "code", "chunks"), { recursive: true });
1086
+ await Promise.all(
1087
+ sharedChunks.map((chunk) =>
1088
+ writeFile(
1089
+ join(modulesDir, "code", chunk.fileName),
1090
+ rewriteCompatVendorPlaceholderImports(
1091
+ rewritePortableRuntimePackageImports(chunk.code, portableRuntimePackages),
1092
+ ),
1093
+ ),
1094
+ ),
1095
+ );
1096
+ }
1097
+
1067
1098
  const usedCompatVendorEntries = collectCompatVendorEntryUsage(artifactEntries);
1068
1099
 
1069
1100
  if (usedCompatVendorEntries.size > 0) {
@@ -1993,6 +2024,8 @@ function routePathFromParams(route: AppRoute, params: StaticParams): string {
1993
2024
  }
1994
2025
 
1995
2026
  async function buildServerModuleArtifacts(options: {
2027
+ actionRenderBundleExcludedFiles?: ReadonlySet<string> | undefined;
2028
+ bundleRequestRuntimePackages: boolean;
1996
2029
  bundleCache: Map<string, Promise<RouterBundleOutput>>;
1997
2030
  cacheDir?: string | undefined;
1998
2031
  clientRouteInferenceCache: ClientRouteInferenceCache;
@@ -2005,7 +2038,10 @@ async function buildServerModuleArtifacts(options: {
2005
2038
  sourceAnalysis: BuildSourceAnalysisScope;
2006
2039
  serverTransformCache: ServerTransformCache;
2007
2040
  vitePlugins?: readonly PluginOption[] | undefined;
2008
- }): Promise<Record<string, BuiltServerModuleArtifact>> {
2041
+ }): Promise<{
2042
+ artifacts: Record<string, BuiltServerModuleArtifact>;
2043
+ sharedChunks: readonly SharedServerModuleChunk[];
2044
+ }> {
2009
2045
  const routeByFile = new Map(
2010
2046
  options.routes.map((route) => [relative(options.projectRoot, route.file), route]),
2011
2047
  );
@@ -2069,9 +2105,22 @@ async function buildServerModuleArtifacts(options: {
2069
2105
  });
2070
2106
  }
2071
2107
 
2108
+ if (isMiddlewareFile(options.project.routesDir, absoluteFile)) {
2109
+ // Middleware joins the batch when runtime packages are bundled so the
2110
+ // auth/control dependency graph is shared with route loader artifacts.
2111
+ if (options.bundleRequestRuntimePackages) {
2112
+ requestBatchEntries.push({
2113
+ code: source,
2114
+ filename: absoluteFile,
2115
+ key: routeRequestArtifactBatchKey(file, "request"),
2116
+ label: "Middleware",
2117
+ });
2118
+ }
2119
+ continue;
2120
+ }
2121
+
2072
2122
  if (
2073
2123
  requestArtifactFiles.has(file) &&
2074
- !isMiddlewareFile(options.project.routesDir, absoluteFile) &&
2075
2124
  route?.kind !== "server" &&
2076
2125
  route?.kind !== "metadata"
2077
2126
  ) {
@@ -2084,18 +2133,20 @@ async function buildServerModuleArtifacts(options: {
2084
2133
  }
2085
2134
  }
2086
2135
 
2087
- const requestBatchOutputs =
2088
- requestBatchEntries.length >= 3
2136
+ const { codeByKey: requestBatchOutputs, sharedChunks } =
2137
+ requestBatchEntries.length >= (options.bundleRequestRuntimePackages ? 2 : 3)
2089
2138
  ? await bundleRouteRequestModuleBatchCode({
2090
2139
  appDir: options.project.routesDir,
2091
2140
  bundleCache: options.bundleCache,
2092
2141
  cacheDir: options.cacheDir,
2142
+ emitSharedChunks: options.bundleRequestRuntimePackages,
2093
2143
  entries: requestBatchEntries,
2144
+ externalizeAllowedPackages: !options.bundleRequestRuntimePackages,
2094
2145
  importPolicy: requestModuleImportPolicy,
2095
2146
  define: options.define,
2096
2147
  vitePlugins: options.vitePlugins,
2097
2148
  })
2098
- : new Map<string, string>();
2149
+ : { codeByKey: new Map<string, string>(), sharedChunks: [] };
2099
2150
 
2100
2151
  const artifactEntries = await mapWithBuildConcurrency(
2101
2152
  Object.entries(options.files),
@@ -2119,6 +2170,7 @@ async function buildServerModuleArtifacts(options: {
2119
2170
  cacheDir: options.cacheDir,
2120
2171
  code: stripRouteLoaderOnlyExports(source, absoluteFile),
2121
2172
  filename: absoluteFile,
2173
+ externalizeAllowedPackages: !options.bundleRequestRuntimePackages,
2122
2174
  importPolicy: requestModuleImportPolicy,
2123
2175
  define: options.define,
2124
2176
  vitePlugins: options.vitePlugins,
@@ -2138,6 +2190,7 @@ async function buildServerModuleArtifacts(options: {
2138
2190
  cacheDir: options.cacheDir,
2139
2191
  code: stripRouteMetadataOnlyExports(source, absoluteFile),
2140
2192
  filename: absoluteFile,
2193
+ externalizeAllowedPackages: !options.bundleRequestRuntimePackages,
2141
2194
  importPolicy: requestModuleImportPolicy,
2142
2195
  label: "Metadata",
2143
2196
  define: options.define,
@@ -2161,6 +2214,7 @@ async function buildServerModuleArtifacts(options: {
2161
2214
  bundleCache: options.bundleCache,
2162
2215
  cacheDir: options.cacheDir,
2163
2216
  filename: absoluteFile,
2217
+ externalizeAllowedPackages: !options.bundleRequestRuntimePackages,
2164
2218
  importPolicy: requestModuleImportPolicy,
2165
2219
  define: options.define,
2166
2220
  routeKind: route?.kind,
@@ -2273,23 +2327,26 @@ async function buildServerModuleArtifacts(options: {
2273
2327
  return undefined;
2274
2328
  }
2275
2329
 
2330
+ const shouldWriteRenderBundle =
2331
+ options.prebundleServerComponents &&
2332
+ options.actionRenderBundleExcludedFiles?.has(file) !== true;
2333
+ const bundleCode = shouldWriteRenderBundle
2334
+ ? await buildServerComponentBundleArtifactCode({
2335
+ clientRouteInferenceCache: options.clientRouteInferenceCache,
2336
+ code: output.code,
2337
+ externalizeCompatVendor,
2338
+ filename: absoluteFile,
2339
+ define: options.define,
2340
+ root: options.projectRoot,
2341
+ serverOutput,
2342
+ vitePlugins: options.vitePlugins,
2343
+ })
2344
+ : undefined;
2345
+
2276
2346
  return [
2277
2347
  serverOutput,
2278
2348
  {
2279
- ...(options.prebundleServerComponents
2280
- ? {
2281
- bundleCode: await buildServerComponentBundleArtifactCode({
2282
- clientRouteInferenceCache: options.clientRouteInferenceCache,
2283
- code: output.code,
2284
- externalizeCompatVendor,
2285
- filename: absoluteFile,
2286
- define: options.define,
2287
- root: options.projectRoot,
2288
- serverOutput,
2289
- vitePlugins: options.vitePlugins,
2290
- }),
2291
- }
2292
- : {}),
2349
+ ...(bundleCode === undefined ? {} : { bundleCode }),
2293
2350
  code: output.code,
2294
2351
  metadata: output.metadata,
2295
2352
  sourceHash: hashText(code),
@@ -2314,7 +2371,7 @@ async function buildServerModuleArtifacts(options: {
2314
2371
  }
2315
2372
  }
2316
2373
 
2317
- return artifacts;
2374
+ return { artifacts, sharedChunks };
2318
2375
  }
2319
2376
 
2320
2377
  async function buildServerComponentBundleArtifactCode(options: {
@@ -2476,6 +2533,7 @@ async function buildRequestModuleArtifactCode(options: {
2476
2533
  bundleCache: Map<string, Promise<RouterBundleOutput>>;
2477
2534
  cacheDir?: string | undefined;
2478
2535
  define?: UserConfig["define"] | undefined;
2536
+ externalizeAllowedPackages: boolean;
2479
2537
  filename: string;
2480
2538
  importPolicy: AppRouterImportPolicy;
2481
2539
  routeKind?: AppRoute["kind"] | undefined;
@@ -2488,6 +2546,7 @@ async function buildRequestModuleArtifactCode(options: {
2488
2546
  code: options.source,
2489
2547
  define: options.define,
2490
2548
  file: options.filename,
2549
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2491
2550
  importPolicy: options.importPolicy,
2492
2551
  vitePlugins: options.vitePlugins,
2493
2552
  });
@@ -2512,6 +2571,7 @@ async function buildRequestModuleArtifactCode(options: {
2512
2571
  cacheDir: options.cacheDir,
2513
2572
  code: stripRouteRequestOnlyExports(options.source, options.filename),
2514
2573
  define: options.define,
2574
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2515
2575
  filename: options.filename,
2516
2576
  importPolicy: options.importPolicy,
2517
2577
  vitePlugins: options.vitePlugins,
@@ -2524,6 +2584,7 @@ async function bundleRouteLoaderModuleCode(options: {
2524
2584
  cacheDir?: string | undefined;
2525
2585
  code: string;
2526
2586
  define?: UserConfig["define"] | undefined;
2587
+ externalizeAllowedPackages?: boolean | undefined;
2527
2588
  filename: string;
2528
2589
  importPolicy?: AppRouterImportPolicy | undefined;
2529
2590
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -2539,36 +2600,42 @@ async function bundleRouteRequestModuleBatchCode(options: {
2539
2600
  bundleCache?: Map<string, Promise<RouterBundleOutput>> | undefined;
2540
2601
  cacheDir?: string | undefined;
2541
2602
  define?: UserConfig["define"] | undefined;
2603
+ emitSharedChunks?: boolean | undefined;
2542
2604
  entries: readonly RouteRequestModuleBatchEntry[];
2605
+ externalizeAllowedPackages?: boolean | undefined;
2543
2606
  importPolicy?: AppRouterImportPolicy | undefined;
2544
2607
  vitePlugins?: readonly PluginOption[] | undefined;
2545
- }): Promise<Map<string, string>> {
2608
+ }): Promise<RouteRequestModuleBatchOutput> {
2546
2609
  if (options.entries.length === 0) {
2547
- return new Map();
2610
+ return { codeByKey: new Map(), sharedChunks: [] };
2548
2611
  }
2549
2612
 
2550
2613
  if (options.entries.length === 1) {
2551
2614
  const entry = options.entries[0];
2552
2615
  if (entry === undefined) {
2553
- return new Map();
2616
+ return { codeByKey: new Map(), sharedChunks: [] };
2554
2617
  }
2555
2618
 
2556
- return new Map([
2557
- [
2558
- entry.key,
2559
- await bundleRouteRequestModuleCode({
2560
- appDir: options.appDir,
2561
- bundleCache: options.bundleCache,
2562
- cacheDir: options.cacheDir,
2563
- code: entry.code,
2564
- define: options.define,
2565
- filename: entry.filename,
2566
- importPolicy: options.importPolicy,
2567
- label: entry.label,
2568
- vitePlugins: options.vitePlugins,
2569
- }),
2570
- ],
2571
- ]);
2619
+ return {
2620
+ codeByKey: new Map([
2621
+ [
2622
+ entry.key,
2623
+ await bundleRouteRequestModuleCode({
2624
+ appDir: options.appDir,
2625
+ bundleCache: options.bundleCache,
2626
+ cacheDir: options.cacheDir,
2627
+ code: entry.code,
2628
+ define: options.define,
2629
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2630
+ filename: entry.filename,
2631
+ importPolicy: options.importPolicy,
2632
+ label: entry.label,
2633
+ vitePlugins: options.vitePlugins,
2634
+ }),
2635
+ ],
2636
+ ]),
2637
+ sharedChunks: [],
2638
+ };
2572
2639
  }
2573
2640
 
2574
2641
  const namesByKey = new Map(
@@ -2579,13 +2646,15 @@ async function bundleRouteRequestModuleBatchCode(options: {
2579
2646
  );
2580
2647
  const output = await bundleRouterModules({
2581
2648
  cacheDir: options.cacheDir,
2582
- chunkFileNames: "request/chunks/[name].[hash].js",
2649
+ // Externalized module code lives flat in server-modules/code, so shared
2650
+ // chunks must be importable as "./chunks/..." from any entry file there.
2651
+ chunkFileNames: "chunks/request.[hash].mjs",
2583
2652
  entries: options.entries.map((entry) => ({
2584
2653
  code: entry.code,
2585
2654
  filename: entry.filename,
2586
2655
  name: namesByKey.get(entry.key) ?? hashText(entry.key).slice(0, 8),
2587
2656
  })),
2588
- entryFileNames: "request/[name].js",
2657
+ entryFileNames: "[name].mjs",
2589
2658
  define: options.define,
2590
2659
  platform: "node",
2591
2660
  plugins: [
@@ -2594,14 +2663,16 @@ async function bundleRouteRequestModuleBatchCode(options: {
2594
2663
  appDir: options.appDir,
2595
2664
  importPolicy: options.importPolicy,
2596
2665
  label: "Request artifact",
2666
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2597
2667
  }),
2598
2668
  ],
2599
2669
  root:
2600
2670
  options.importPolicy?.projectRoot ?? dirname(options.entries[0]?.filename ?? options.appDir),
2601
2671
  vitePlugins: options.vitePlugins,
2602
2672
  });
2673
+ const sharedChunks = output.chunks.filter((chunk) => !chunk.isEntry);
2603
2674
 
2604
- if (output.chunks.some((chunk) => !chunk.isEntry)) {
2675
+ if (sharedChunks.length > 0 && !canEmitSharedRequestChunks(options.emitSharedChunks, output)) {
2605
2676
  const fallbackEntries = await mapWithBuildConcurrency(
2606
2677
  options.entries,
2607
2678
  async (entry) =>
@@ -2613,6 +2684,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2613
2684
  cacheDir: options.cacheDir,
2614
2685
  code: entry.code,
2615
2686
  define: options.define,
2687
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2616
2688
  filename: entry.filename,
2617
2689
  importPolicy: options.importPolicy,
2618
2690
  label: entry.label,
@@ -2620,21 +2692,46 @@ async function bundleRouteRequestModuleBatchCode(options: {
2620
2692
  }),
2621
2693
  ] as const,
2622
2694
  );
2623
- return new Map(fallbackEntries);
2695
+ return { codeByKey: new Map(fallbackEntries), sharedChunks: [] };
2624
2696
  }
2625
2697
 
2626
2698
  const chunksByName = new Map(output.chunks.map((chunk) => [chunk.name, chunk]));
2627
- return new Map(
2628
- options.entries.map((entry) => {
2629
- const name = namesByKey.get(entry.key);
2630
- const chunk = name === undefined ? undefined : chunksByName.get(name);
2699
+ return {
2700
+ codeByKey: new Map(
2701
+ options.entries.map((entry) => {
2702
+ const name = namesByKey.get(entry.key);
2703
+ const chunk = name === undefined ? undefined : chunksByName.get(name);
2631
2704
 
2632
- if (chunk === undefined) {
2633
- throw new Error(`Failed to compile request artifact for ${entry.filename}.`);
2634
- }
2705
+ if (chunk === undefined) {
2706
+ throw new Error(`Failed to compile request artifact for ${entry.filename}.`);
2707
+ }
2635
2708
 
2636
- return [entry.key, chunk.code] as const;
2637
- }),
2709
+ return [entry.key, chunk.code] as const;
2710
+ }),
2711
+ ),
2712
+ sharedChunks: sharedChunks.map((chunk) => ({ code: chunk.code, fileName: chunk.fileName })),
2713
+ };
2714
+ }
2715
+
2716
+ function canEmitSharedRequestChunks(
2717
+ emitSharedChunks: boolean | undefined,
2718
+ output: RouterBundleModulesOutput,
2719
+ ): boolean {
2720
+ if (emitSharedChunks !== true) {
2721
+ return false;
2722
+ }
2723
+
2724
+ if (output.assets !== undefined && output.assets.length > 0) {
2725
+ return false;
2726
+ }
2727
+
2728
+ // Entry code is rewritten into content-addressed module files, so emitted
2729
+ // imports may only target shared chunk files or external specifiers.
2730
+ const entryFileNames = new Set(
2731
+ output.chunks.filter((chunk) => chunk.isEntry).map((chunk) => chunk.fileName),
2732
+ );
2733
+ return output.chunks.every((chunk) =>
2734
+ chunk.imports.every((specifier) => !entryFileNames.has(specifier)),
2638
2735
  );
2639
2736
  }
2640
2737
 
@@ -2643,18 +2740,22 @@ export async function __bundleRouteRequestModuleBatchForTests(options: {
2643
2740
  cacheDir?: string | undefined;
2644
2741
  define?: UserConfig["define"] | undefined;
2645
2742
  entries: readonly RouteRequestModuleBatchEntry[];
2743
+ externalizeAllowedPackages?: boolean | undefined;
2646
2744
  importPolicy?: AppRouterImportPolicy | undefined;
2647
2745
  vitePlugins?: readonly PluginOption[] | undefined;
2648
2746
  }): Promise<Record<string, string>> {
2649
2747
  return Object.fromEntries(
2650
- await bundleRouteRequestModuleBatchCode({
2651
- appDir: options.appDir,
2652
- cacheDir: options.cacheDir,
2653
- define: options.define,
2654
- entries: options.entries,
2655
- importPolicy: options.importPolicy,
2656
- vitePlugins: options.vitePlugins,
2657
- }),
2748
+ (
2749
+ await bundleRouteRequestModuleBatchCode({
2750
+ appDir: options.appDir,
2751
+ cacheDir: options.cacheDir,
2752
+ define: options.define,
2753
+ entries: options.entries,
2754
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2755
+ importPolicy: options.importPolicy,
2756
+ vitePlugins: options.vitePlugins,
2757
+ })
2758
+ ).codeByKey,
2658
2759
  );
2659
2760
  }
2660
2761
 
@@ -2664,9 +2765,10 @@ async function bundleRouteRequestModuleCode(options: {
2664
2765
  cacheDir?: string | undefined;
2665
2766
  code: string;
2666
2767
  define?: UserConfig["define"] | undefined;
2768
+ externalizeAllowedPackages?: boolean | undefined;
2667
2769
  filename: string;
2668
2770
  importPolicy?: AppRouterImportPolicy | undefined;
2669
- label: "Loader" | "Metadata";
2771
+ label: RouteRequestModuleBundleLabel;
2670
2772
  vitePlugins?: readonly PluginOption[] | undefined;
2671
2773
  }): Promise<string> {
2672
2774
  const output = await bundleRouterModule({
@@ -2683,6 +2785,7 @@ async function bundleRouteRequestModuleCode(options: {
2683
2785
  fileImportMetaUrlPlugin(),
2684
2786
  createAppRouterImportPolicyPlugin({
2685
2787
  appDir: options.appDir,
2788
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2686
2789
  importPolicy: options.importPolicy,
2687
2790
  label: options.label,
2688
2791
  }),
@@ -2701,9 +2804,10 @@ function routeRequestBundleCacheKey(options: {
2701
2804
  appDir: string;
2702
2805
  code: string;
2703
2806
  define?: UserConfig["define"] | undefined;
2807
+ externalizeAllowedPackages?: boolean | undefined;
2704
2808
  filename: string;
2705
2809
  importPolicy?: AppRouterImportPolicy | undefined;
2706
- label: "Loader" | "Metadata";
2810
+ label: RouteRequestModuleBundleLabel;
2707
2811
  vitePlugins?: readonly PluginOption[] | undefined;
2708
2812
  }): string {
2709
2813
  return stableCacheKey({
@@ -2723,6 +2827,7 @@ function routeRequestBundleCacheKey(options: {
2723
2827
  ? undefined
2724
2828
  : resolve(options.importPolicy.projectRoot),
2725
2829
  },
2830
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2726
2831
  label: options.label,
2727
2832
  platform: "node",
2728
2833
  target: "es2022",
@@ -2802,7 +2907,19 @@ interface RouteRequestModuleBatchEntry {
2802
2907
  code: string;
2803
2908
  filename: string;
2804
2909
  key: string;
2805
- label: "Loader" | "Metadata";
2910
+ label: RouteRequestModuleBundleLabel;
2911
+ }
2912
+
2913
+ type RouteRequestModuleBundleLabel = "Loader" | "Metadata" | "Middleware";
2914
+
2915
+ interface SharedServerModuleChunk {
2916
+ code: string;
2917
+ fileName: string;
2918
+ }
2919
+
2920
+ interface RouteRequestModuleBatchOutput {
2921
+ codeByKey: Map<string, string>;
2922
+ sharedChunks: readonly SharedServerModuleChunk[];
2806
2923
  }
2807
2924
 
2808
2925
  async function writeCloudflareRouteModules(options: {
@@ -21,6 +21,7 @@ export interface AppRouterImportPolicy {
21
21
  export interface AppRouterImportPolicyPluginOptions {
22
22
  allowedSourceDirs?: readonly string[] | undefined;
23
23
  appDir: string;
24
+ externalizeAllowedPackages?: boolean | undefined;
24
25
  importPolicy?: AppRouterImportPolicy | undefined;
25
26
  label: string;
26
27
  projectRoot?: string | undefined;
@@ -110,10 +111,12 @@ export function createAppRouterImportPolicyPlugin(options: AppRouterImportPolicy
110
111
  return undefined;
111
112
  }
112
113
 
113
- return {
114
- external: true,
115
- path: pathToFileURL(resolvedPackage).href,
116
- };
114
+ return options.externalizeAllowedPackages === false
115
+ ? undefined
116
+ : {
117
+ external: true,
118
+ path: pathToFileURL(resolvedPackage).href,
119
+ };
117
120
  });
118
121
  },
119
122
  };
package/src/render.ts CHANGED
@@ -2525,6 +2525,7 @@ export async function bundleMiddlewareModuleCode(options: {
2525
2525
  appDir: string;
2526
2526
  code: string;
2527
2527
  define?: UserConfig["define"] | undefined;
2528
+ externalizeAllowedPackages?: boolean | undefined;
2528
2529
  file: string;
2529
2530
  importPolicy?: AppRouterImportPolicy | undefined;
2530
2531
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -2539,6 +2540,7 @@ export async function bundleMiddlewareModuleCode(options: {
2539
2540
  fileImportMetaUrlPlugin(),
2540
2541
  createAppRouterImportPolicyPlugin({
2541
2542
  appDir: options.appDir,
2543
+ externalizeAllowedPackages: options.externalizeAllowedPackages,
2542
2544
  importPolicy: options.importPolicy,
2543
2545
  label: "Middleware",
2544
2546
  }),