@reckona/mreact-router 0.0.158 → 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.158",
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": "0.0.158",
109
- "@reckona/mreact-compiler": "0.0.158",
110
- "@reckona/mreact-compat": "0.0.158",
111
- "@reckona/mreact-query": "0.0.158",
112
- "@reckona/mreact-devtools": "0.0.158",
113
- "@reckona/mreact-reactive-core": "0.0.158",
114
- "@reckona/mreact-reactive-dom": "0.0.158",
115
- "@reckona/mreact-server": "0.0.158",
116
- "@reckona/mreact-shared": "0.0.158"
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.158"
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";
@@ -485,7 +486,7 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
485
486
  .filter(([, references]) => references.length > 0)
486
487
  .map(([file]) => file),
487
488
  );
488
- const serverModules = await (
489
+ const { artifacts: serverModules, sharedChunks: serverModuleSharedChunks } = await (
489
490
  shouldTrackBuildPhases === false
490
491
  ? buildServerModuleArtifacts({
491
492
  actionRenderBundleExcludedFiles,
@@ -530,12 +531,14 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
530
531
  serverDir,
531
532
  serverModules,
532
533
  generatedImportPolicy.runtimePackages,
534
+ serverModuleSharedChunks,
533
535
  )
534
536
  : timeBuildPhase(timingSink, progressSink, "serverModuleArtifacts", () =>
535
537
  writeServerModuleArtifactFiles(
536
538
  serverDir,
537
539
  serverModules,
538
540
  generatedImportPolicy.runtimePackages,
541
+ serverModuleSharedChunks,
539
542
  ),
540
543
  ),
541
544
  shouldTrackBuildPhases === false
@@ -1054,6 +1057,7 @@ async function writeServerModuleArtifactFiles(
1054
1057
  serverDir: string,
1055
1058
  serverModules: Record<string, BuiltServerModuleArtifact>,
1056
1059
  portableRuntimePackages: readonly string[] = [],
1060
+ sharedChunks: readonly SharedServerModuleChunk[] = [],
1057
1061
  ): Promise<{
1058
1062
  files: Record<string, string>;
1059
1063
  renderFiles: Record<string, string>;
@@ -1075,6 +1079,22 @@ async function writeServerModuleArtifactFiles(
1075
1079
  mkdir(join(modulesDir, "render"), { recursive: true }),
1076
1080
  ]);
1077
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
+
1078
1098
  const usedCompatVendorEntries = collectCompatVendorEntryUsage(artifactEntries);
1079
1099
 
1080
1100
  if (usedCompatVendorEntries.size > 0) {
@@ -2018,7 +2038,10 @@ async function buildServerModuleArtifacts(options: {
2018
2038
  sourceAnalysis: BuildSourceAnalysisScope;
2019
2039
  serverTransformCache: ServerTransformCache;
2020
2040
  vitePlugins?: readonly PluginOption[] | undefined;
2021
- }): Promise<Record<string, BuiltServerModuleArtifact>> {
2041
+ }): Promise<{
2042
+ artifacts: Record<string, BuiltServerModuleArtifact>;
2043
+ sharedChunks: readonly SharedServerModuleChunk[];
2044
+ }> {
2022
2045
  const routeByFile = new Map(
2023
2046
  options.routes.map((route) => [relative(options.projectRoot, route.file), route]),
2024
2047
  );
@@ -2082,9 +2105,22 @@ async function buildServerModuleArtifacts(options: {
2082
2105
  });
2083
2106
  }
2084
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
+
2085
2122
  if (
2086
2123
  requestArtifactFiles.has(file) &&
2087
- !isMiddlewareFile(options.project.routesDir, absoluteFile) &&
2088
2124
  route?.kind !== "server" &&
2089
2125
  route?.kind !== "metadata"
2090
2126
  ) {
@@ -2097,19 +2133,20 @@ async function buildServerModuleArtifacts(options: {
2097
2133
  }
2098
2134
  }
2099
2135
 
2100
- const requestBatchOutputs =
2101
- requestBatchEntries.length >= 3
2136
+ const { codeByKey: requestBatchOutputs, sharedChunks } =
2137
+ requestBatchEntries.length >= (options.bundleRequestRuntimePackages ? 2 : 3)
2102
2138
  ? await bundleRouteRequestModuleBatchCode({
2103
2139
  appDir: options.project.routesDir,
2104
2140
  bundleCache: options.bundleCache,
2105
2141
  cacheDir: options.cacheDir,
2142
+ emitSharedChunks: options.bundleRequestRuntimePackages,
2106
2143
  entries: requestBatchEntries,
2107
2144
  externalizeAllowedPackages: !options.bundleRequestRuntimePackages,
2108
2145
  importPolicy: requestModuleImportPolicy,
2109
2146
  define: options.define,
2110
2147
  vitePlugins: options.vitePlugins,
2111
2148
  })
2112
- : new Map<string, string>();
2149
+ : { codeByKey: new Map<string, string>(), sharedChunks: [] };
2113
2150
 
2114
2151
  const artifactEntries = await mapWithBuildConcurrency(
2115
2152
  Object.entries(options.files),
@@ -2334,7 +2371,7 @@ async function buildServerModuleArtifacts(options: {
2334
2371
  }
2335
2372
  }
2336
2373
 
2337
- return artifacts;
2374
+ return { artifacts, sharedChunks };
2338
2375
  }
2339
2376
 
2340
2377
  async function buildServerComponentBundleArtifactCode(options: {
@@ -2563,38 +2600,42 @@ async function bundleRouteRequestModuleBatchCode(options: {
2563
2600
  bundleCache?: Map<string, Promise<RouterBundleOutput>> | undefined;
2564
2601
  cacheDir?: string | undefined;
2565
2602
  define?: UserConfig["define"] | undefined;
2603
+ emitSharedChunks?: boolean | undefined;
2566
2604
  entries: readonly RouteRequestModuleBatchEntry[];
2567
2605
  externalizeAllowedPackages?: boolean | undefined;
2568
2606
  importPolicy?: AppRouterImportPolicy | undefined;
2569
2607
  vitePlugins?: readonly PluginOption[] | undefined;
2570
- }): Promise<Map<string, string>> {
2608
+ }): Promise<RouteRequestModuleBatchOutput> {
2571
2609
  if (options.entries.length === 0) {
2572
- return new Map();
2610
+ return { codeByKey: new Map(), sharedChunks: [] };
2573
2611
  }
2574
2612
 
2575
2613
  if (options.entries.length === 1) {
2576
2614
  const entry = options.entries[0];
2577
2615
  if (entry === undefined) {
2578
- return new Map();
2616
+ return { codeByKey: new Map(), sharedChunks: [] };
2579
2617
  }
2580
2618
 
2581
- return new Map([
2582
- [
2583
- entry.key,
2584
- await bundleRouteRequestModuleCode({
2585
- appDir: options.appDir,
2586
- bundleCache: options.bundleCache,
2587
- cacheDir: options.cacheDir,
2588
- code: entry.code,
2589
- define: options.define,
2590
- externalizeAllowedPackages: options.externalizeAllowedPackages,
2591
- filename: entry.filename,
2592
- importPolicy: options.importPolicy,
2593
- label: entry.label,
2594
- vitePlugins: options.vitePlugins,
2595
- }),
2596
- ],
2597
- ]);
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
+ };
2598
2639
  }
2599
2640
 
2600
2641
  const namesByKey = new Map(
@@ -2605,13 +2646,15 @@ async function bundleRouteRequestModuleBatchCode(options: {
2605
2646
  );
2606
2647
  const output = await bundleRouterModules({
2607
2648
  cacheDir: options.cacheDir,
2608
- 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",
2609
2652
  entries: options.entries.map((entry) => ({
2610
2653
  code: entry.code,
2611
2654
  filename: entry.filename,
2612
2655
  name: namesByKey.get(entry.key) ?? hashText(entry.key).slice(0, 8),
2613
2656
  })),
2614
- entryFileNames: "request/[name].js",
2657
+ entryFileNames: "[name].mjs",
2615
2658
  define: options.define,
2616
2659
  platform: "node",
2617
2660
  plugins: [
@@ -2627,8 +2670,9 @@ async function bundleRouteRequestModuleBatchCode(options: {
2627
2670
  options.importPolicy?.projectRoot ?? dirname(options.entries[0]?.filename ?? options.appDir),
2628
2671
  vitePlugins: options.vitePlugins,
2629
2672
  });
2673
+ const sharedChunks = output.chunks.filter((chunk) => !chunk.isEntry);
2630
2674
 
2631
- if (output.chunks.some((chunk) => !chunk.isEntry)) {
2675
+ if (sharedChunks.length > 0 && !canEmitSharedRequestChunks(options.emitSharedChunks, output)) {
2632
2676
  const fallbackEntries = await mapWithBuildConcurrency(
2633
2677
  options.entries,
2634
2678
  async (entry) =>
@@ -2648,21 +2692,46 @@ async function bundleRouteRequestModuleBatchCode(options: {
2648
2692
  }),
2649
2693
  ] as const,
2650
2694
  );
2651
- return new Map(fallbackEntries);
2695
+ return { codeByKey: new Map(fallbackEntries), sharedChunks: [] };
2652
2696
  }
2653
2697
 
2654
2698
  const chunksByName = new Map(output.chunks.map((chunk) => [chunk.name, chunk]));
2655
- return new Map(
2656
- options.entries.map((entry) => {
2657
- const name = namesByKey.get(entry.key);
2658
- 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);
2659
2704
 
2660
- if (chunk === undefined) {
2661
- throw new Error(`Failed to compile request artifact for ${entry.filename}.`);
2662
- }
2705
+ if (chunk === undefined) {
2706
+ throw new Error(`Failed to compile request artifact for ${entry.filename}.`);
2707
+ }
2663
2708
 
2664
- return [entry.key, chunk.code] as const;
2665
- }),
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)),
2666
2735
  );
2667
2736
  }
2668
2737
 
@@ -2676,15 +2745,17 @@ export async function __bundleRouteRequestModuleBatchForTests(options: {
2676
2745
  vitePlugins?: readonly PluginOption[] | undefined;
2677
2746
  }): Promise<Record<string, string>> {
2678
2747
  return Object.fromEntries(
2679
- await bundleRouteRequestModuleBatchCode({
2680
- appDir: options.appDir,
2681
- cacheDir: options.cacheDir,
2682
- define: options.define,
2683
- entries: options.entries,
2684
- externalizeAllowedPackages: options.externalizeAllowedPackages,
2685
- importPolicy: options.importPolicy,
2686
- vitePlugins: options.vitePlugins,
2687
- }),
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,
2688
2759
  );
2689
2760
  }
2690
2761
 
@@ -2697,7 +2768,7 @@ async function bundleRouteRequestModuleCode(options: {
2697
2768
  externalizeAllowedPackages?: boolean | undefined;
2698
2769
  filename: string;
2699
2770
  importPolicy?: AppRouterImportPolicy | undefined;
2700
- label: "Loader" | "Metadata";
2771
+ label: RouteRequestModuleBundleLabel;
2701
2772
  vitePlugins?: readonly PluginOption[] | undefined;
2702
2773
  }): Promise<string> {
2703
2774
  const output = await bundleRouterModule({
@@ -2736,7 +2807,7 @@ function routeRequestBundleCacheKey(options: {
2736
2807
  externalizeAllowedPackages?: boolean | undefined;
2737
2808
  filename: string;
2738
2809
  importPolicy?: AppRouterImportPolicy | undefined;
2739
- label: "Loader" | "Metadata";
2810
+ label: RouteRequestModuleBundleLabel;
2740
2811
  vitePlugins?: readonly PluginOption[] | undefined;
2741
2812
  }): string {
2742
2813
  return stableCacheKey({
@@ -2836,7 +2907,19 @@ interface RouteRequestModuleBatchEntry {
2836
2907
  code: string;
2837
2908
  filename: string;
2838
2909
  key: string;
2839
- 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[];
2840
2923
  }
2841
2924
 
2842
2925
  async function writeCloudflareRouteModules(options: {