@reckona/mreact-router 0.0.100 → 0.0.102
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/build.d.ts.map +1 -1
- package/dist/build.js +378 -50
- package/dist/build.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +517 -53
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.102",
|
|
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.
|
|
109
|
-
"@reckona/mreact-compat": "0.0.
|
|
110
|
-
"@reckona/mreact-
|
|
111
|
-
"@reckona/mreact-query": "0.0.
|
|
112
|
-
"@reckona/mreact-
|
|
113
|
-
"@reckona/mreact-reactive-core": "0.0.
|
|
114
|
-
"@reckona/mreact-reactive-dom": "0.0.
|
|
115
|
-
"@reckona/mreact-server": "0.0.
|
|
116
|
-
"@reckona/mreact-shared": "0.0.
|
|
108
|
+
"@reckona/mreact": "0.0.102",
|
|
109
|
+
"@reckona/mreact-compat": "0.0.102",
|
|
110
|
+
"@reckona/mreact-compiler": "0.0.102",
|
|
111
|
+
"@reckona/mreact-query": "0.0.102",
|
|
112
|
+
"@reckona/mreact-devtools": "0.0.102",
|
|
113
|
+
"@reckona/mreact-reactive-core": "0.0.102",
|
|
114
|
+
"@reckona/mreact-reactive-dom": "0.0.102",
|
|
115
|
+
"@reckona/mreact-server": "0.0.102",
|
|
116
|
+
"@reckona/mreact-shared": "0.0.102"
|
|
117
117
|
},
|
|
118
118
|
"peerDependencies": {
|
|
119
119
|
"vite": ">=8 <9"
|
|
120
120
|
},
|
|
121
121
|
"optionalDependencies": {
|
|
122
|
-
"@reckona/mreact-router-native": "0.0.
|
|
122
|
+
"@reckona/mreact-router-native": "0.0.102"
|
|
123
123
|
}
|
|
124
124
|
}
|
package/src/build.ts
CHANGED
|
@@ -2380,6 +2380,17 @@ interface CloudflareBatchedRouteModules {
|
|
|
2380
2380
|
entries: ReadonlyMap<string, CloudflareBatchedRouteModule>;
|
|
2381
2381
|
}
|
|
2382
2382
|
|
|
2383
|
+
interface CloudflareBatchedComponentRoute {
|
|
2384
|
+
filename: string;
|
|
2385
|
+
routeId: string;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
interface CloudflareBatchedStringRoute {
|
|
2389
|
+
filename: string;
|
|
2390
|
+
routeId: string;
|
|
2391
|
+
shellFiles: CloudflareShellFile[];
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2383
2394
|
interface RouteRequestModuleBatchEntry {
|
|
2384
2395
|
code: string;
|
|
2385
2396
|
filename: string;
|
|
@@ -2437,10 +2448,44 @@ async function writeCloudflareRouteModules(options: {
|
|
|
2437
2448
|
root: options.projectRoot,
|
|
2438
2449
|
vitePlugins: options.vitePlugins,
|
|
2439
2450
|
});
|
|
2451
|
+
const directComponentRoutes = await collectCloudflareDirectComponentRoutes({
|
|
2452
|
+
requiredRoutes,
|
|
2453
|
+
routesDir: options.routesDir,
|
|
2454
|
+
serverModules: options.serverModules,
|
|
2455
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2456
|
+
});
|
|
2457
|
+
const stringShellComponentRoutes = await collectCloudflareStringShellComponentRoutes({
|
|
2458
|
+
requiredRoutes,
|
|
2459
|
+
routesDir: options.routesDir,
|
|
2460
|
+
serverModules: options.serverModules,
|
|
2461
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2462
|
+
});
|
|
2463
|
+
const directComponentRouteIds = new Set(directComponentRoutes.map((route) => route.routeId));
|
|
2464
|
+
const stringShellComponentRouteIds = new Set(
|
|
2465
|
+
stringShellComponentRoutes.map((route) => route.routeId),
|
|
2466
|
+
);
|
|
2467
|
+
const directComponentModules = await buildCloudflareServerComponentModuleBatch({
|
|
2468
|
+
cacheDir: options.cacheDir,
|
|
2469
|
+
projectRoot: options.projectRoot,
|
|
2470
|
+
routes: directComponentRoutes,
|
|
2471
|
+
serverModules: options.serverModules,
|
|
2472
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2473
|
+
vitePlugins: options.vitePlugins,
|
|
2474
|
+
});
|
|
2475
|
+
const stringShellComponentModules = await buildCloudflareStringRouteComponentModuleBatch({
|
|
2476
|
+
cacheDir: options.cacheDir,
|
|
2477
|
+
projectRoot: options.projectRoot,
|
|
2478
|
+
routes: stringShellComponentRoutes,
|
|
2479
|
+
serverModules: options.serverModules,
|
|
2480
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2481
|
+
vitePlugins: options.vitePlugins,
|
|
2482
|
+
});
|
|
2440
2483
|
|
|
2441
2484
|
await Promise.all([
|
|
2442
2485
|
writeCloudflareBatchedRouteModuleChunks(options.cloudflareDir, serverRouteModules),
|
|
2443
2486
|
writeCloudflareBatchedRouteModuleChunks(options.cloudflareDir, loaderRouteModules),
|
|
2487
|
+
writeCloudflareBatchedRouteModuleChunks(options.cloudflareDir, directComponentModules),
|
|
2488
|
+
writeCloudflareBatchedRouteModuleChunks(options.cloudflareDir, stringShellComponentModules),
|
|
2444
2489
|
]);
|
|
2445
2490
|
|
|
2446
2491
|
const registryEntries = await mapWithBuildConcurrency(requiredRoutes, async ({ route, routeFile, routeId }) => {
|
|
@@ -2480,29 +2525,43 @@ async function writeCloudflareRouteModules(options: {
|
|
|
2480
2525
|
: "string";
|
|
2481
2526
|
|
|
2482
2527
|
try {
|
|
2483
|
-
const
|
|
2484
|
-
|
|
2485
|
-
?
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2528
|
+
const batchedComponent =
|
|
2529
|
+
stringShellComponentRouteIds.has(routeId)
|
|
2530
|
+
? stringShellComponentModules.entries.get(routeId)
|
|
2531
|
+
: directComponentRouteIds.has(routeId)
|
|
2532
|
+
? directComponentModules.entries.get(routeId)
|
|
2533
|
+
: undefined;
|
|
2534
|
+
let componentFile = batchedComponent?.fileName;
|
|
2535
|
+
|
|
2536
|
+
if (batchedComponent === undefined) {
|
|
2537
|
+
const componentOutput =
|
|
2538
|
+
serverOutput === "stream"
|
|
2539
|
+
? await buildCloudflareStreamRouteComponentModule({
|
|
2540
|
+
cacheDir: options.cacheDir,
|
|
2541
|
+
filename: route.file,
|
|
2542
|
+
projectRoot: options.projectRoot,
|
|
2543
|
+
routesDir: options.routesDir,
|
|
2544
|
+
serverModules: options.serverModules,
|
|
2545
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2546
|
+
vitePlugins: options.vitePlugins,
|
|
2547
|
+
})
|
|
2548
|
+
: await buildCloudflareStringRouteComponentModule({
|
|
2549
|
+
cacheDir: options.cacheDir,
|
|
2550
|
+
filename: route.file,
|
|
2551
|
+
projectRoot: options.projectRoot,
|
|
2552
|
+
routesDir: options.routesDir,
|
|
2553
|
+
serverModules: options.serverModules,
|
|
2554
|
+
sourceAnalysis: options.sourceAnalysis,
|
|
2555
|
+
vitePlugins: options.vitePlugins,
|
|
2556
|
+
});
|
|
2557
|
+
|
|
2558
|
+
componentFile = `routes/${routeId}.${hashText(componentOutput).slice(0, 8)}.component.mjs`;
|
|
2559
|
+
await writeFile(join(options.cloudflareDir, componentFile), componentOutput);
|
|
2560
|
+
}
|
|
2504
2561
|
|
|
2505
|
-
|
|
2562
|
+
if (componentFile === undefined) {
|
|
2563
|
+
throw new Error("Missing bundled Cloudflare component module.");
|
|
2564
|
+
}
|
|
2506
2565
|
|
|
2507
2566
|
const componentImport = `./${componentFile.split("/").pop() ?? componentFile}`;
|
|
2508
2567
|
routeModuleExports = [
|
|
@@ -2549,12 +2608,389 @@ async function writeCloudflareRouteModules(options: {
|
|
|
2549
2608
|
return { registryFile: "route-modules.mjs" };
|
|
2550
2609
|
}
|
|
2551
2610
|
|
|
2611
|
+
async function collectCloudflareDirectComponentRoutes(options: {
|
|
2612
|
+
requiredRoutes: readonly CloudflareRequiredRoute[];
|
|
2613
|
+
routesDir: string;
|
|
2614
|
+
serverModules: Record<string, BuiltServerModuleArtifact>;
|
|
2615
|
+
sourceAnalysis: BuildSourceAnalysisScope;
|
|
2616
|
+
}): Promise<CloudflareBatchedComponentRoute[]> {
|
|
2617
|
+
const routes = await Promise.all(
|
|
2618
|
+
options.requiredRoutes.map(async ({ route, routeFile, routeId }) => {
|
|
2619
|
+
if (route.kind !== "page") {
|
|
2620
|
+
return undefined;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
const serverOutput =
|
|
2624
|
+
options.serverModules[routeFile]?.analysis?.streamRoute === true ||
|
|
2625
|
+
options.sourceAnalysis.byRouteFile.get(routeFile)?.streamRoute === true
|
|
2626
|
+
? "stream"
|
|
2627
|
+
: "string";
|
|
2628
|
+
|
|
2629
|
+
if (serverOutput !== "string") {
|
|
2630
|
+
return undefined;
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
const shellFiles = await cloudflareShellFilesForPage(options.routesDir, route.file);
|
|
2634
|
+
|
|
2635
|
+
if (shellFiles.length > 0) {
|
|
2636
|
+
return undefined;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
return {
|
|
2640
|
+
filename: route.file,
|
|
2641
|
+
routeId,
|
|
2642
|
+
};
|
|
2643
|
+
}),
|
|
2644
|
+
);
|
|
2645
|
+
|
|
2646
|
+
return routes.filter((route): route is CloudflareBatchedComponentRoute => route !== undefined);
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
async function collectCloudflareStringShellComponentRoutes(options: {
|
|
2650
|
+
requiredRoutes: readonly CloudflareRequiredRoute[];
|
|
2651
|
+
routesDir: string;
|
|
2652
|
+
serverModules: Record<string, BuiltServerModuleArtifact>;
|
|
2653
|
+
sourceAnalysis: BuildSourceAnalysisScope;
|
|
2654
|
+
}): Promise<CloudflareBatchedStringRoute[]> {
|
|
2655
|
+
const routes = await Promise.all(
|
|
2656
|
+
options.requiredRoutes.map(async ({ route, routeFile, routeId }) => {
|
|
2657
|
+
if (route.kind !== "page") {
|
|
2658
|
+
return undefined;
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
const serverOutput =
|
|
2662
|
+
options.serverModules[routeFile]?.analysis?.streamRoute === true ||
|
|
2663
|
+
options.sourceAnalysis.byRouteFile.get(routeFile)?.streamRoute === true
|
|
2664
|
+
? "stream"
|
|
2665
|
+
: "string";
|
|
2666
|
+
|
|
2667
|
+
if (serverOutput !== "string") {
|
|
2668
|
+
return undefined;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
const shellFiles = await cloudflareShellFilesForPage(options.routesDir, route.file);
|
|
2672
|
+
|
|
2673
|
+
if (shellFiles.length === 0) {
|
|
2674
|
+
return undefined;
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
return {
|
|
2678
|
+
filename: route.file,
|
|
2679
|
+
routeId,
|
|
2680
|
+
shellFiles,
|
|
2681
|
+
};
|
|
2682
|
+
}),
|
|
2683
|
+
);
|
|
2684
|
+
|
|
2685
|
+
return routes.filter((route): route is CloudflareBatchedStringRoute => route !== undefined);
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2552
2688
|
interface CloudflareShellFile {
|
|
2553
2689
|
file: string;
|
|
2554
2690
|
id: string;
|
|
2555
2691
|
kind: "layout" | "template";
|
|
2556
2692
|
}
|
|
2557
2693
|
|
|
2694
|
+
async function buildCloudflareServerComponentModuleBatch(options: {
|
|
2695
|
+
cacheDir?: string | undefined;
|
|
2696
|
+
projectRoot: string;
|
|
2697
|
+
routes: readonly CloudflareBatchedComponentRoute[];
|
|
2698
|
+
serverModules: Record<string, BuiltServerModuleArtifact>;
|
|
2699
|
+
sourceAnalysis: BuildSourceAnalysisScope;
|
|
2700
|
+
vitePlugins?: readonly PluginOption[] | undefined;
|
|
2701
|
+
}): Promise<CloudflareBatchedRouteModules> {
|
|
2702
|
+
if (options.routes.length === 0) {
|
|
2703
|
+
return { chunks: [], entries: new Map() };
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
const virtualModules = new Map<string, CloudflareVirtualModule>();
|
|
2707
|
+
const bundleEntries = await Promise.all(
|
|
2708
|
+
options.routes.map(async (route) => {
|
|
2709
|
+
const metadataModule = await buildCloudflareRouteMetadataExportModule({
|
|
2710
|
+
cacheDir: options.cacheDir,
|
|
2711
|
+
filename: route.filename,
|
|
2712
|
+
hasMetadata: buildSourceAnalysisForFile(
|
|
2713
|
+
options.sourceAnalysis,
|
|
2714
|
+
options.projectRoot,
|
|
2715
|
+
route.filename,
|
|
2716
|
+
)?.hasMetadata,
|
|
2717
|
+
root: options.projectRoot,
|
|
2718
|
+
vitePlugins: options.vitePlugins,
|
|
2719
|
+
});
|
|
2720
|
+
const metadataId = `mreact:metadata:${route.routeId}`;
|
|
2721
|
+
|
|
2722
|
+
if (metadataModule !== undefined) {
|
|
2723
|
+
virtualModules.set(metadataId, {
|
|
2724
|
+
contents: metadataModule,
|
|
2725
|
+
resolveDir: dirname(route.filename),
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
return {
|
|
2730
|
+
code: cloudflareServerComponentModuleEntry(
|
|
2731
|
+
route.filename,
|
|
2732
|
+
metadataModule === undefined ? undefined : metadataId,
|
|
2733
|
+
),
|
|
2734
|
+
filename: `${route.filename}.mreact-cloudflare-component.js`,
|
|
2735
|
+
name: `${route.routeId}.component`,
|
|
2736
|
+
routeId: route.routeId,
|
|
2737
|
+
};
|
|
2738
|
+
}),
|
|
2739
|
+
);
|
|
2740
|
+
const output = await bundleRouterModules({
|
|
2741
|
+
cacheDir: options.cacheDir,
|
|
2742
|
+
chunkFileNames: "routes/chunks/[name].[hash].mjs",
|
|
2743
|
+
entries: bundleEntries,
|
|
2744
|
+
entryFileNames: "routes/[name].[hash].mjs",
|
|
2745
|
+
minify: true,
|
|
2746
|
+
platform: "node",
|
|
2747
|
+
plugins: [
|
|
2748
|
+
cloudflareVirtualModulesPlugin(virtualModules),
|
|
2749
|
+
cloudflareServerSourceTransformPlugin({
|
|
2750
|
+
projectRoot: options.projectRoot,
|
|
2751
|
+
serverOutput: "string",
|
|
2752
|
+
serverModules: options.serverModules,
|
|
2753
|
+
vitePlugins: options.vitePlugins,
|
|
2754
|
+
}),
|
|
2755
|
+
cloudflareWorkspaceRuntimePlugin(),
|
|
2756
|
+
],
|
|
2757
|
+
root: options.projectRoot,
|
|
2758
|
+
target: "es2022",
|
|
2759
|
+
vitePlugins: options.vitePlugins,
|
|
2760
|
+
});
|
|
2761
|
+
const entriesByName = new Map(bundleEntries.map((entry) => [entry.name, entry]));
|
|
2762
|
+
const entries = new Map<string, CloudflareBatchedRouteModule>();
|
|
2763
|
+
|
|
2764
|
+
for (const chunk of output.chunks) {
|
|
2765
|
+
if (!chunk.isEntry) {
|
|
2766
|
+
continue;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
const entry = entriesByName.get(chunk.name);
|
|
2770
|
+
|
|
2771
|
+
if (entry === undefined) {
|
|
2772
|
+
continue;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
entries.set(entry.routeId, {
|
|
2776
|
+
code: chunk.code,
|
|
2777
|
+
fileName: chunk.fileName,
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
return { chunks: output.chunks, entries };
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
async function buildCloudflareStringRouteComponentModuleBatch(options: {
|
|
2785
|
+
cacheDir?: string | undefined;
|
|
2786
|
+
projectRoot: string;
|
|
2787
|
+
routes: readonly CloudflareBatchedStringRoute[];
|
|
2788
|
+
serverModules: Record<string, BuiltServerModuleArtifact>;
|
|
2789
|
+
sourceAnalysis: BuildSourceAnalysisScope;
|
|
2790
|
+
vitePlugins?: readonly PluginOption[] | undefined;
|
|
2791
|
+
}): Promise<CloudflareBatchedRouteModules> {
|
|
2792
|
+
if (options.routes.length === 0) {
|
|
2793
|
+
return { chunks: [], entries: new Map() };
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
const virtualModules = new Map<string, CloudflareVirtualModule>();
|
|
2797
|
+
const bundleEntries = await Promise.all(
|
|
2798
|
+
options.routes.map(async (route) => {
|
|
2799
|
+
const metadataImports: string[] = [];
|
|
2800
|
+
const pageMetadataId = `mreact:metadata:${route.routeId}:page`;
|
|
2801
|
+
const pageMetadataModule = await buildCloudflareRouteMetadataExportModule({
|
|
2802
|
+
cacheDir: options.cacheDir,
|
|
2803
|
+
filename: route.filename,
|
|
2804
|
+
hasMetadata: buildSourceAnalysisForFile(
|
|
2805
|
+
options.sourceAnalysis,
|
|
2806
|
+
options.projectRoot,
|
|
2807
|
+
route.filename,
|
|
2808
|
+
)?.hasMetadata,
|
|
2809
|
+
root: options.projectRoot,
|
|
2810
|
+
vitePlugins: options.vitePlugins,
|
|
2811
|
+
});
|
|
2812
|
+
|
|
2813
|
+
if (pageMetadataModule === undefined) {
|
|
2814
|
+
metadataImports.push("const pageMetadataModule = {};");
|
|
2815
|
+
} else {
|
|
2816
|
+
virtualModules.set(pageMetadataId, {
|
|
2817
|
+
contents: pageMetadataModule,
|
|
2818
|
+
resolveDir: dirname(route.filename),
|
|
2819
|
+
});
|
|
2820
|
+
metadataImports.push(`import * as pageMetadataModule from ${JSON.stringify(pageMetadataId)};`);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
const shellMetadataImports = await Promise.all(
|
|
2824
|
+
route.shellFiles.map(async (shell, index) => {
|
|
2825
|
+
const shellMetadataId = `mreact:metadata:${route.routeId}:shell:${index}`;
|
|
2826
|
+
const shellMetadataModule = await buildCloudflareRouteMetadataExportModule({
|
|
2827
|
+
cacheDir: options.cacheDir,
|
|
2828
|
+
filename: shell.file,
|
|
2829
|
+
hasMetadata: buildSourceAnalysisForFile(
|
|
2830
|
+
options.sourceAnalysis,
|
|
2831
|
+
options.projectRoot,
|
|
2832
|
+
shell.file,
|
|
2833
|
+
)?.hasMetadata,
|
|
2834
|
+
root: options.projectRoot,
|
|
2835
|
+
vitePlugins: options.vitePlugins,
|
|
2836
|
+
});
|
|
2837
|
+
|
|
2838
|
+
if (shellMetadataModule === undefined) {
|
|
2839
|
+
return `const shellMetadataModule${index} = {};`;
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
virtualModules.set(shellMetadataId, {
|
|
2843
|
+
contents: shellMetadataModule,
|
|
2844
|
+
resolveDir: dirname(shell.file),
|
|
2845
|
+
});
|
|
2846
|
+
return `import * as shellMetadataModule${index} from ${JSON.stringify(shellMetadataId)};`;
|
|
2847
|
+
}),
|
|
2848
|
+
);
|
|
2849
|
+
metadataImports.push(...shellMetadataImports);
|
|
2850
|
+
|
|
2851
|
+
return {
|
|
2852
|
+
code: cloudflareStringRouteComponentModuleEntry({
|
|
2853
|
+
filename: route.filename,
|
|
2854
|
+
metadataImports,
|
|
2855
|
+
pageMetadataModuleName: "pageMetadataModule",
|
|
2856
|
+
shellFiles: route.shellFiles,
|
|
2857
|
+
shellMetadataModuleName: (index) => `shellMetadataModule${index}`,
|
|
2858
|
+
}),
|
|
2859
|
+
filename: `${route.filename}.mreact-cloudflare-string-route.js`,
|
|
2860
|
+
name: `${route.routeId}.string`,
|
|
2861
|
+
routeId: route.routeId,
|
|
2862
|
+
};
|
|
2863
|
+
}),
|
|
2864
|
+
);
|
|
2865
|
+
const output = await bundleRouterModules({
|
|
2866
|
+
cacheDir: options.cacheDir,
|
|
2867
|
+
chunkFileNames: "routes/chunks/[name].[hash].mjs",
|
|
2868
|
+
entries: bundleEntries,
|
|
2869
|
+
entryFileNames: "routes/[name].[hash].mjs",
|
|
2870
|
+
minify: true,
|
|
2871
|
+
platform: "node",
|
|
2872
|
+
plugins: [
|
|
2873
|
+
cloudflareVirtualModulesPlugin(virtualModules),
|
|
2874
|
+
cloudflareServerSourceTransformPlugin({
|
|
2875
|
+
projectRoot: options.projectRoot,
|
|
2876
|
+
serverOutput: "string",
|
|
2877
|
+
serverModules: options.serverModules,
|
|
2878
|
+
vitePlugins: options.vitePlugins,
|
|
2879
|
+
}),
|
|
2880
|
+
cloudflareWorkspaceRuntimePlugin(),
|
|
2881
|
+
],
|
|
2882
|
+
root: options.projectRoot,
|
|
2883
|
+
target: "es2022",
|
|
2884
|
+
vitePlugins: options.vitePlugins,
|
|
2885
|
+
});
|
|
2886
|
+
const entriesByName = new Map(bundleEntries.map((entry) => [entry.name, entry]));
|
|
2887
|
+
const entries = new Map<string, CloudflareBatchedRouteModule>();
|
|
2888
|
+
|
|
2889
|
+
for (const chunk of output.chunks) {
|
|
2890
|
+
if (!chunk.isEntry) {
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
const entry = entriesByName.get(chunk.name);
|
|
2895
|
+
|
|
2896
|
+
if (entry === undefined) {
|
|
2897
|
+
continue;
|
|
2898
|
+
}
|
|
2899
|
+
|
|
2900
|
+
entries.set(entry.routeId, {
|
|
2901
|
+
code: chunk.code,
|
|
2902
|
+
fileName: chunk.fileName,
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
return { chunks: output.chunks, entries };
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
function cloudflareStringRouteComponentModuleEntry(options: {
|
|
2910
|
+
filename: string;
|
|
2911
|
+
metadataImports: readonly string[];
|
|
2912
|
+
pageMetadataModuleName: string;
|
|
2913
|
+
shellFiles: readonly CloudflareShellFile[];
|
|
2914
|
+
shellMetadataModuleName: (index: number) => string;
|
|
2915
|
+
}): string {
|
|
2916
|
+
const shellImports = options.shellFiles.map(
|
|
2917
|
+
(shell, index) => `import * as shellRouteModule${index} from ${JSON.stringify(shell.file)};`,
|
|
2918
|
+
);
|
|
2919
|
+
const shellDefinitions = options.shellFiles.map(
|
|
2920
|
+
(shell, index) =>
|
|
2921
|
+
`{ component: selectComponent(shell${index}, ${JSON.stringify(shell.file)}), id: ${JSON.stringify(shell.id)}, kind: ${JSON.stringify(shell.kind)}, module: shell${index} }`,
|
|
2922
|
+
);
|
|
2923
|
+
const shellModules = options.shellFiles.map(
|
|
2924
|
+
(_, index) =>
|
|
2925
|
+
`const shell${index} = routeComponentModule(shellRouteModule${index}, ${options.shellMetadataModuleName(index)});`,
|
|
2926
|
+
);
|
|
2927
|
+
|
|
2928
|
+
return `import * as pageRouteModule from ${JSON.stringify(options.filename)};
|
|
2929
|
+
${shellImports.join("\n")}
|
|
2930
|
+
${options.metadataImports.join("\n")}
|
|
2931
|
+
|
|
2932
|
+
const pageModule = routeComponentModule(pageRouteModule, ${options.pageMetadataModuleName});
|
|
2933
|
+
${shellModules.join("\n")}
|
|
2934
|
+
const pageComponent = selectComponent(pageModule, ${JSON.stringify(options.filename)});
|
|
2935
|
+
const shells = [${shellDefinitions.join(", ")}];
|
|
2936
|
+
export const slots = pageModule.slots;
|
|
2937
|
+
export const App = renderCloudflareStringRoute;
|
|
2938
|
+
export default renderCloudflareStringRoute;
|
|
2939
|
+
|
|
2940
|
+
async function renderCloudflareStringRoute(props) {
|
|
2941
|
+
const slotHtml = await renderRouteSlots(pageModule.slots, props);
|
|
2942
|
+
const layoutShells = await renderLayoutShells(shells, props, slotHtml);
|
|
2943
|
+
const metadata = await resolveRouteMetadata([...shells.map((shell) => shell.module), pageModule], props);
|
|
2944
|
+
let html = "<!DOCTYPE html>";
|
|
2945
|
+
for (const shell of layoutShells) {
|
|
2946
|
+
html += shell.prefix;
|
|
2947
|
+
}
|
|
2948
|
+
html += String(await pageComponent(props) ?? "");
|
|
2949
|
+
for (const shell of [...layoutShells].reverse()) {
|
|
2950
|
+
html += shell.suffix;
|
|
2951
|
+
}
|
|
2952
|
+
html = injectCloudflareHead(html, metadata, cloudflareRouteHeadTags(props.clientManifest, props.route.path));
|
|
2953
|
+
return new Response(html, {
|
|
2954
|
+
headers: {
|
|
2955
|
+
"content-type": "text/html; charset=utf-8"
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
function routeComponentModule(routeModule, metadataModule) {
|
|
2961
|
+
const component = routeModule.default ?? routeModule.App ?? Object.values(routeModule).find((value) => typeof value === "function");
|
|
2962
|
+
return {
|
|
2963
|
+
...routeModule,
|
|
2964
|
+
App: component,
|
|
2965
|
+
default: component,
|
|
2966
|
+
generateMetadata: metadataModule.generateMetadata,
|
|
2967
|
+
metadata: metadataModule.metadata,
|
|
2968
|
+
slots: routeModule.slots,
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
function selectComponent(module, label) {
|
|
2973
|
+
const component = module.default ?? module.App ?? Object.values(module).find((value) => typeof value === "function");
|
|
2974
|
+
if (typeof component !== "function") {
|
|
2975
|
+
throw new Error(\`No Cloudflare component export was found for \${label}.\`);
|
|
2976
|
+
}
|
|
2977
|
+
return component;
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
async function renderRouteSlots(slots, props) {
|
|
2981
|
+
if (slots === undefined) {
|
|
2982
|
+
return {};
|
|
2983
|
+
}
|
|
2984
|
+
const rendered = {};
|
|
2985
|
+
for (const [name, value] of Object.entries(slots)) {
|
|
2986
|
+
rendered[name] = typeof value === "function" ? String(await value(props) ?? "") : String(value ?? "");
|
|
2987
|
+
}
|
|
2988
|
+
return rendered;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
${cloudflareShellRuntimeSource()}`;
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2558
2994
|
async function buildCloudflareServerComponentModule(options: {
|
|
2559
2995
|
cacheDir?: string | undefined;
|
|
2560
2996
|
filename: string;
|
|
@@ -2571,15 +3007,10 @@ async function buildCloudflareServerComponentModule(options: {
|
|
|
2571
3007
|
root: options.projectRoot,
|
|
2572
3008
|
vitePlugins: options.vitePlugins,
|
|
2573
3009
|
});
|
|
2574
|
-
const entry =
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
export const App = component;
|
|
2579
|
-
export default component;
|
|
2580
|
-
export const generateMetadata = metadataModule.generateMetadata;
|
|
2581
|
-
export const metadata = metadataModule.metadata;
|
|
2582
|
-
export const slots = routeModule.slots;`;
|
|
3010
|
+
const entry = cloudflareServerComponentModuleEntry(
|
|
3011
|
+
options.filename,
|
|
3012
|
+
metadataModule === undefined ? undefined : "mreact:metadata",
|
|
3013
|
+
);
|
|
2583
3014
|
|
|
2584
3015
|
return bundleCloudflareVirtualModule({
|
|
2585
3016
|
entry,
|
|
@@ -2603,6 +3034,21 @@ export const slots = routeModule.slots;`;
|
|
|
2603
3034
|
});
|
|
2604
3035
|
}
|
|
2605
3036
|
|
|
3037
|
+
function cloudflareServerComponentModuleEntry(
|
|
3038
|
+
filename: string,
|
|
3039
|
+
metadataModuleId: string | undefined,
|
|
3040
|
+
): string {
|
|
3041
|
+
return `import * as routeModule from ${JSON.stringify(filename)};
|
|
3042
|
+
${metadataModuleId === undefined ? "const metadataModule = {};" : `import * as metadataModule from ${JSON.stringify(metadataModuleId)};`}
|
|
3043
|
+
|
|
3044
|
+
const component = routeModule.default ?? routeModule.App ?? Object.values(routeModule).find((value) => typeof value === "function");
|
|
3045
|
+
export const App = component;
|
|
3046
|
+
export default component;
|
|
3047
|
+
export const generateMetadata = metadataModule.generateMetadata;
|
|
3048
|
+
export const metadata = metadataModule.metadata;
|
|
3049
|
+
export const slots = routeModule.slots;`;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
2606
3052
|
async function buildCloudflareStringRouteComponentModule(options: {
|
|
2607
3053
|
cacheDir?: string | undefined;
|
|
2608
3054
|
filename: string;
|
|
@@ -3341,28 +3787,14 @@ async function bundleCloudflareVirtualModule(options: {
|
|
|
3341
3787
|
entry: options.entry,
|
|
3342
3788
|
filename: options.filename,
|
|
3343
3789
|
plugins: [
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
const contents = options.modules.get(args.path);
|
|
3353
|
-
|
|
3354
|
-
if (contents === undefined) {
|
|
3355
|
-
throw new Error(`Missing virtual Cloudflare module ${args.path}.`);
|
|
3356
|
-
}
|
|
3357
|
-
|
|
3358
|
-
return {
|
|
3359
|
-
contents,
|
|
3360
|
-
loader: "js",
|
|
3361
|
-
resolveDir: options.resolveDir,
|
|
3362
|
-
};
|
|
3363
|
-
});
|
|
3364
|
-
},
|
|
3365
|
-
},
|
|
3790
|
+
cloudflareVirtualModulesPlugin(
|
|
3791
|
+
new Map(
|
|
3792
|
+
Array.from(options.modules, ([id, contents]) => [
|
|
3793
|
+
id,
|
|
3794
|
+
{ contents, resolveDir: options.resolveDir },
|
|
3795
|
+
]),
|
|
3796
|
+
),
|
|
3797
|
+
),
|
|
3366
3798
|
...options.plugins,
|
|
3367
3799
|
],
|
|
3368
3800
|
resolveDir: options.resolveDir,
|
|
@@ -3371,6 +3803,38 @@ async function bundleCloudflareVirtualModule(options: {
|
|
|
3371
3803
|
});
|
|
3372
3804
|
}
|
|
3373
3805
|
|
|
3806
|
+
interface CloudflareVirtualModule {
|
|
3807
|
+
contents: string;
|
|
3808
|
+
resolveDir: string;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
function cloudflareVirtualModulesPlugin(
|
|
3812
|
+
modules: ReadonlyMap<string, CloudflareVirtualModule>,
|
|
3813
|
+
): RouterCompatPlugin {
|
|
3814
|
+
return {
|
|
3815
|
+
name: "mreact-cloudflare-virtual-modules",
|
|
3816
|
+
setup(buildApi) {
|
|
3817
|
+
buildApi.onResolve({ filter: /^mreact:/ }, (args) => ({
|
|
3818
|
+
namespace: "mreact-cloudflare-virtual",
|
|
3819
|
+
path: args.path,
|
|
3820
|
+
}));
|
|
3821
|
+
buildApi.onLoad({ filter: /.*/, namespace: "mreact-cloudflare-virtual" }, (args) => {
|
|
3822
|
+
const module = modules.get(args.path);
|
|
3823
|
+
|
|
3824
|
+
if (module === undefined) {
|
|
3825
|
+
throw new Error(`Missing virtual Cloudflare module ${args.path}.`);
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
return {
|
|
3829
|
+
contents: module.contents,
|
|
3830
|
+
loader: "js",
|
|
3831
|
+
resolveDir: module.resolveDir,
|
|
3832
|
+
};
|
|
3833
|
+
});
|
|
3834
|
+
},
|
|
3835
|
+
};
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3374
3838
|
async function cloudflareShellFilesForPage(
|
|
3375
3839
|
routesDir: string,
|
|
3376
3840
|
pageFile: string,
|