@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.20dbba0c
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/AGENTS.md +4 -0
- package/README.md +172 -50
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +1160 -508
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +17 -16
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +49 -8
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +61 -51
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +91 -17
- package/skills/loader/SKILL.md +107 -24
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +765 -0
- package/skills/parallel/SKILL.md +185 -0
- package/skills/prerender/SKILL.md +112 -70
- package/skills/rango/SKILL.md +24 -23
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/route/SKILL.md +58 -4
- package/skills/router-setup/SKILL.md +95 -5
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +38 -24
- package/src/__internal.ts +92 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +175 -17
- package/src/browser/navigation-client.ts +177 -44
- package/src/browser/navigation-store.ts +68 -9
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +113 -17
- package/src/browser/prefetch/cache.ts +275 -28
- package/src/browser/prefetch/fetch.ts +191 -46
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +98 -14
- package/src/browser/react/NavigationProvider.tsx +89 -14
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +11 -1
- package/src/browser/react/use-router.ts +29 -9
- package/src/browser/rsc-router.tsx +177 -66
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +73 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +67 -25
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +223 -74
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +455 -15
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +2 -1
- package/src/client.tsx +85 -276
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/host/index.ts +0 -3
- package/src/index.rsc.ts +9 -36
- package/src/index.ts +79 -70
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +57 -15
- package/src/prerender.ts +138 -77
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +27 -2
- package/src/route-definition/dsl-helpers.ts +240 -40
- package/src/route-definition/helpers-types.ts +67 -19
- package/src/route-definition/index.ts +3 -3
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +129 -26
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +10 -7
- package/src/router/loader-resolution.ts +160 -22
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +128 -193
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +94 -17
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +103 -18
- package/src/router/metrics.ts +238 -13
- package/src/router/middleware-types.ts +48 -27
- package/src/router/middleware.ts +201 -86
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +77 -11
- package/src/router/prerender-match.ts +114 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +27 -7
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +50 -5
- package/src/router/router-options.ts +50 -19
- package/src/router/segment-resolution/fresh.ts +215 -19
- package/src/router/segment-resolution/helpers.ts +30 -25
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +454 -301
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +30 -6
- package/src/router/types.ts +1 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +89 -17
- package/src/rsc/handler.ts +563 -364
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +37 -10
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +47 -44
- package/src/rsc/server-action.ts +24 -10
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +11 -1
- package/src/search-params.ts +16 -13
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +109 -23
- package/src/server/context.ts +174 -19
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +218 -65
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/theme/index.ts +4 -13
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +140 -72
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +17 -8
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +2 -5
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +18 -16
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/bundle-postprocess.ts +61 -89
- package/src/vite/discovery/discover-routers.ts +7 -4
- package/src/vite/discovery/prerender-collection.ts +162 -88
- package/src/vite/discovery/state.ts +17 -13
- package/src/vite/index.ts +8 -3
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +190 -217
- package/src/vite/router-discovery.ts +241 -45
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/package-resolution.ts +34 -1
- package/src/vite/utils/prerender-utils.ts +97 -5
- package/src/vite/utils/shared-utils.ts +3 -2
- package/skills/testing/SKILL.md +0 -226
- package/src/route-definition/route-function.ts +0 -119
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
|
-
import {
|
|
10
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
11
10
|
import { evictHandlerCode } from "../utils/bundle-analysis.js";
|
|
11
|
+
import { copyStagedBuildAssets } from "../utils/prerender-utils.js";
|
|
12
12
|
import type { DiscoveryState } from "./state.js";
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -17,11 +17,11 @@ import type { DiscoveryState } from "./state.js";
|
|
|
17
17
|
*/
|
|
18
18
|
export function postprocessBundle(state: DiscoveryState): void {
|
|
19
19
|
const hasPrerenderData =
|
|
20
|
-
state.
|
|
21
|
-
Object.keys(state.
|
|
20
|
+
state.prerenderManifestEntries &&
|
|
21
|
+
Object.keys(state.prerenderManifestEntries).length > 0;
|
|
22
22
|
const hasStaticData =
|
|
23
|
-
state.
|
|
24
|
-
Object.keys(state.
|
|
23
|
+
state.staticManifestEntries &&
|
|
24
|
+
Object.keys(state.staticManifestEntries).length > 0;
|
|
25
25
|
if (!hasPrerenderData && !hasStaticData) return;
|
|
26
26
|
|
|
27
27
|
// Find RSC entry (recorded in generateBundle, fallback to dist/rsc/index.js)
|
|
@@ -31,25 +31,25 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
31
31
|
state.rscEntryFileName ?? "index.js",
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
-
// 1. Evict handler code from
|
|
35
|
-
//
|
|
34
|
+
// 1. Evict handler code from whichever chunks contain handler exports.
|
|
35
|
+
// handlerChunkInfoMap/staticHandlerChunkInfoMap are populated by generateBundle
|
|
36
36
|
// after the production RSC build. In Vite 6 multi-environment builds, the
|
|
37
|
-
// RSC build runs twice (analysis + production).
|
|
38
|
-
//
|
|
37
|
+
// RSC build runs twice (analysis + production). The maps are cleared at the
|
|
38
|
+
// start of each generateBundle pass so only production data is used here.
|
|
39
39
|
const evictionTargets: Array<{
|
|
40
|
-
|
|
40
|
+
infos: Iterable<import("./state.js").ChunkInfo>;
|
|
41
41
|
fnName: string;
|
|
42
42
|
brand: string;
|
|
43
43
|
label: string;
|
|
44
44
|
}> = [
|
|
45
45
|
{
|
|
46
|
-
|
|
46
|
+
infos: state.handlerChunkInfoMap.values(),
|
|
47
47
|
fnName: "Prerender",
|
|
48
48
|
brand: "prerenderHandler",
|
|
49
49
|
label: "handler code from RSC bundle",
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
|
-
|
|
52
|
+
infos: state.staticHandlerChunkInfoMap.values(),
|
|
53
53
|
fnName: "Static",
|
|
54
54
|
brand: "staticHandler",
|
|
55
55
|
label: "static handler code",
|
|
@@ -57,70 +57,58 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
57
57
|
];
|
|
58
58
|
|
|
59
59
|
for (const target of evictionTargets) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
60
|
+
for (const info of target.infos) {
|
|
61
|
+
const chunkPath = resolve(state.projectRoot, "dist/rsc", info.fileName);
|
|
62
|
+
try {
|
|
63
|
+
const code = readFileSync(chunkPath, "utf-8");
|
|
64
|
+
const result = evictHandlerCode(
|
|
65
|
+
code,
|
|
66
|
+
info.exports,
|
|
67
|
+
target.fnName,
|
|
68
|
+
target.brand,
|
|
69
|
+
);
|
|
70
|
+
if (result) {
|
|
71
|
+
writeFileSync(chunkPath, result.code);
|
|
72
|
+
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
73
|
+
console.log(
|
|
74
|
+
`[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
} catch (replaceErr: any) {
|
|
78
|
+
console.warn(
|
|
79
|
+
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`,
|
|
79
80
|
);
|
|
80
81
|
}
|
|
81
|
-
} catch (replaceErr: any) {
|
|
82
|
-
console.warn(
|
|
83
|
-
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`,
|
|
84
|
-
);
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
|
-
state.
|
|
88
|
-
state.
|
|
84
|
+
state.handlerChunkInfoMap.clear();
|
|
85
|
+
state.staticHandlerChunkInfoMap.clear();
|
|
89
86
|
|
|
90
87
|
// 2. Write prerender data as separate importable asset modules
|
|
91
|
-
// and inject a manifest
|
|
88
|
+
// and inject a lazy manifest loader into the RSC entry.
|
|
92
89
|
if (hasPrerenderData && existsSync(rscEntryPath)) {
|
|
93
90
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
94
|
-
// Check for the specific injection marker
|
|
95
|
-
// The runtime code (prerender store) also references __PRERENDER_MANIFEST,
|
|
96
|
-
// so a broad string check would false-positive and skip injection.
|
|
91
|
+
// Check for the specific injection marker to avoid double-injection.
|
|
97
92
|
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
98
93
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let totalBytes = 0;
|
|
94
|
+
let totalBytes = copyStagedBuildAssets(
|
|
95
|
+
state.projectRoot,
|
|
96
|
+
Object.values(state.prerenderManifestEntries!),
|
|
97
|
+
);
|
|
104
98
|
|
|
105
|
-
|
|
106
|
-
|
|
99
|
+
const manifestMap: Record<string, string> = {};
|
|
100
|
+
for (const [key, assetFileName] of Object.entries(
|
|
101
|
+
state.prerenderManifestEntries!,
|
|
107
102
|
)) {
|
|
108
|
-
|
|
109
|
-
const contentHash = createHash("sha256")
|
|
110
|
-
.update(entryJson)
|
|
111
|
-
.digest("hex")
|
|
112
|
-
.slice(0, 8);
|
|
113
|
-
const assetFileName = `__pr-${contentHash}.js`;
|
|
114
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
115
|
-
const assetCode = `export default ${entryJson};\n`;
|
|
116
|
-
writeFileSync(assetPath, assetCode);
|
|
117
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
118
|
-
manifestEntries.push(
|
|
119
|
-
`${JSON.stringify(key)}:()=>import("./assets/${assetFileName}")`,
|
|
120
|
-
);
|
|
103
|
+
manifestMap[key] = `./assets/${assetFileName}`;
|
|
121
104
|
}
|
|
122
105
|
|
|
123
|
-
const manifestCode =
|
|
106
|
+
const manifestCode = [
|
|
107
|
+
`const m=JSON.parse('${JSON.stringify(manifestMap).replace(/'/g, "\\'")}');`,
|
|
108
|
+
`export function loadPrerenderAsset(s){return import(s)}`,
|
|
109
|
+
`export default m;`,
|
|
110
|
+
"",
|
|
111
|
+
].join("\n");
|
|
124
112
|
const manifestPath = resolve(
|
|
125
113
|
state.projectRoot,
|
|
126
114
|
"dist/rsc/__prerender-manifest.js",
|
|
@@ -128,12 +116,12 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
128
116
|
writeFileSync(manifestPath, manifestCode);
|
|
129
117
|
totalBytes += Buffer.byteLength(manifestCode);
|
|
130
118
|
|
|
131
|
-
const injection = `
|
|
119
|
+
const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");\n`;
|
|
132
120
|
writeFileSync(rscEntryPath, injection + rscCode);
|
|
133
121
|
|
|
134
122
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
135
123
|
console.log(
|
|
136
|
-
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.
|
|
124
|
+
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
|
|
137
125
|
);
|
|
138
126
|
} catch (err: any) {
|
|
139
127
|
throw new Error(
|
|
@@ -147,33 +135,17 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
147
135
|
// and inject a __STATIC_MANIFEST import into the RSC entry.
|
|
148
136
|
if (hasStaticData && existsSync(rscEntryPath)) {
|
|
149
137
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
150
|
-
if (!rscCode.includes("
|
|
138
|
+
if (!rscCode.includes("__static-manifest.js")) {
|
|
151
139
|
try {
|
|
152
|
-
const assetsDir = resolve(state.projectRoot, "dist/rsc/assets");
|
|
153
|
-
mkdirSync(assetsDir, { recursive: true });
|
|
154
|
-
|
|
155
140
|
const manifestEntries: string[] = [];
|
|
156
|
-
let totalBytes =
|
|
141
|
+
let totalBytes = copyStagedBuildAssets(
|
|
142
|
+
state.projectRoot,
|
|
143
|
+
Object.values(state.staticManifestEntries!),
|
|
144
|
+
);
|
|
157
145
|
|
|
158
|
-
for (const [handlerId,
|
|
159
|
-
state.
|
|
146
|
+
for (const [handlerId, assetFileName] of Object.entries(
|
|
147
|
+
state.staticManifestEntries!,
|
|
160
148
|
)) {
|
|
161
|
-
// Store both the Flight payload and handle data
|
|
162
|
-
const hasHandles = Object.keys(handles).length > 0;
|
|
163
|
-
const exportValue = hasHandles
|
|
164
|
-
? JSON.stringify({ encoded, handles })
|
|
165
|
-
: JSON.stringify(encoded);
|
|
166
|
-
// Hash the full payload that is written so distinct handle
|
|
167
|
-
// snapshots produce distinct asset filenames.
|
|
168
|
-
const contentHash = createHash("sha256")
|
|
169
|
-
.update(exportValue)
|
|
170
|
-
.digest("hex")
|
|
171
|
-
.slice(0, 8);
|
|
172
|
-
const assetFileName = `__st-${contentHash}.js`;
|
|
173
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
174
|
-
const assetCode = `export default ${exportValue};\n`;
|
|
175
|
-
writeFileSync(assetPath, assetCode);
|
|
176
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
177
149
|
manifestEntries.push(
|
|
178
150
|
`${JSON.stringify(handlerId)}:()=>import("./assets/${assetFileName}")`,
|
|
179
151
|
);
|
|
@@ -197,7 +169,7 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
197
169
|
|
|
198
170
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
199
171
|
console.log(
|
|
200
|
-
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.
|
|
172
|
+
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
|
|
201
173
|
);
|
|
202
174
|
} catch (err: any) {
|
|
203
175
|
throw new Error(
|
|
@@ -48,9 +48,8 @@ export async function discoverRouters(
|
|
|
48
48
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
49
49
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
50
50
|
try {
|
|
51
|
-
const hostMod = await rscEnv.runner.import("@rangojs/router/host");
|
|
52
51
|
const hostRegistry: Map<string, any> | undefined =
|
|
53
|
-
|
|
52
|
+
serverMod.HostRouterRegistry;
|
|
54
53
|
|
|
55
54
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
56
55
|
console.log(
|
|
@@ -89,7 +88,7 @@ export async function discoverRouters(
|
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
} catch {
|
|
92
|
-
//
|
|
91
|
+
// Host-router discovery is best-effort; skip if unavailable
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
// If still no routers after host router resolution, fail
|
|
@@ -136,7 +135,11 @@ export async function discoverRouters(
|
|
|
136
135
|
continue;
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
const manifest = generateManifestFull(
|
|
138
|
+
const manifest = generateManifestFull(
|
|
139
|
+
router.urlpatterns,
|
|
140
|
+
routerMountIndex,
|
|
141
|
+
router.__basename ? { urlPrefix: router.__basename } : undefined,
|
|
142
|
+
);
|
|
140
143
|
routerMountIndex++;
|
|
141
144
|
allManifests.push({ id, manifest });
|
|
142
145
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
runWithConcurrency,
|
|
14
14
|
groupByConcurrency,
|
|
15
15
|
notifyOnError,
|
|
16
|
+
stageBuildAssetModule,
|
|
16
17
|
} from "../utils/prerender-utils.js";
|
|
17
18
|
import type { DiscoveryState } from "./state.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Expand prerender routes into concrete URLs and render them via the
|
|
21
|
-
* RSC runner.
|
|
22
|
+
* RSC runner. Stages asset modules and stores key-to-file entries in
|
|
23
|
+
* state.prerenderManifestEntries.
|
|
22
24
|
*/
|
|
23
25
|
export async function expandPrerenderRoutes(
|
|
24
26
|
state: DiscoveryState,
|
|
@@ -49,93 +51,144 @@ export async function expandPrerenderRoutes(
|
|
|
49
51
|
return substituteRouteParams(pattern, params);
|
|
50
52
|
};
|
|
51
53
|
|
|
54
|
+
let resolvedRoutes = 0;
|
|
55
|
+
let totalDynamic = 0;
|
|
56
|
+
|
|
57
|
+
// Count dynamic routes upfront for progress reporting
|
|
52
58
|
for (const { manifest } of allManifests) {
|
|
53
59
|
if (!manifest.prerenderRoutes) continue;
|
|
54
|
-
const defs = manifest._prerenderDefs || {};
|
|
55
60
|
for (const routeName of manifest.prerenderRoutes) {
|
|
56
61
|
const pattern = manifest.routeManifest[routeName];
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
62
|
+
if (pattern && (pattern.includes(":") || pattern.includes("*"))) {
|
|
63
|
+
totalDynamic++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Periodic progress log so long getParams() calls don't look stalled
|
|
69
|
+
const paramsStart = performance.now();
|
|
70
|
+
const progressInterval =
|
|
71
|
+
totalDynamic > 0
|
|
72
|
+
? setInterval(() => {
|
|
73
|
+
const elapsed = ((performance.now() - paramsStart) / 1000).toFixed(1);
|
|
74
|
+
console.log(
|
|
75
|
+
`[rsc-router] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`,
|
|
76
|
+
);
|
|
77
|
+
}, 5000)
|
|
78
|
+
: undefined;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
for (const { manifest } of allManifests) {
|
|
82
|
+
if (!manifest.prerenderRoutes) continue;
|
|
83
|
+
const defs = manifest._prerenderDefs || {};
|
|
84
|
+
const passthroughSet = new Set(manifest.passthroughRoutes || []);
|
|
85
|
+
for (const routeName of manifest.prerenderRoutes) {
|
|
86
|
+
const pattern = manifest.routeManifest[routeName];
|
|
87
|
+
if (!pattern) continue;
|
|
88
|
+
const def = defs[routeName];
|
|
89
|
+
const isPassthroughRoute = passthroughSet.has(routeName);
|
|
90
|
+
const hasDynamic = pattern.includes(":") || pattern.includes("*");
|
|
91
|
+
if (!hasDynamic) {
|
|
92
|
+
// Static route: use pattern directly (strip trailing slash for URL)
|
|
93
|
+
entries.push({
|
|
94
|
+
urlPath: pattern.replace(/\/$/, "") || "/",
|
|
95
|
+
routeName,
|
|
96
|
+
concurrency: 1,
|
|
97
|
+
isPassthroughRoute,
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
// Dynamic route: call getParams() to enumerate param combinations
|
|
101
|
+
if (def?.getParams) {
|
|
102
|
+
try {
|
|
103
|
+
const buildVars: Record<string, any> = {};
|
|
104
|
+
const buildEnv = state.resolvedBuildEnv;
|
|
105
|
+
const getParamsCtx = {
|
|
106
|
+
build: true as const,
|
|
107
|
+
dev: !state.isBuildMode,
|
|
108
|
+
set: ((keyOrVar: any, value: any) => {
|
|
109
|
+
contextSet(buildVars, keyOrVar, value);
|
|
110
|
+
}) as any,
|
|
111
|
+
reverse: getParamsReverse,
|
|
112
|
+
get env() {
|
|
113
|
+
if (buildEnv !== undefined) return buildEnv;
|
|
114
|
+
throw new Error(
|
|
115
|
+
"[rsc-router] ctx.env is not available during build-time getParams(). " +
|
|
116
|
+
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
const paramsList = await def.getParams(getParamsCtx);
|
|
121
|
+
const concurrency = def.options?.concurrency ?? 1;
|
|
122
|
+
const hasBuildVars =
|
|
123
|
+
Object.keys(buildVars).length > 0 ||
|
|
124
|
+
Object.getOwnPropertySymbols(buildVars).length > 0;
|
|
125
|
+
for (const params of paramsList) {
|
|
126
|
+
let url = substituteRouteParams(
|
|
127
|
+
pattern,
|
|
128
|
+
params as Record<string, string>,
|
|
129
|
+
encodePathParam,
|
|
130
|
+
);
|
|
131
|
+
// Anonymous wildcard fallback: use conventional keys if provided
|
|
132
|
+
if (url.includes("*")) {
|
|
133
|
+
const wildcardValue =
|
|
134
|
+
(params as Record<string, string>)["*"] ??
|
|
135
|
+
(params as Record<string, string>).splat;
|
|
136
|
+
if (wildcardValue !== undefined) {
|
|
137
|
+
url = url.replace(
|
|
138
|
+
/\*[^/]*$/,
|
|
139
|
+
encodePathParam(wildcardValue),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
99
142
|
}
|
|
143
|
+
entries.push({
|
|
144
|
+
urlPath: url.replace(/\/$/, "") || "/",
|
|
145
|
+
routeName,
|
|
146
|
+
concurrency,
|
|
147
|
+
...(hasBuildVars ? { buildVars } : {}),
|
|
148
|
+
isPassthroughRoute,
|
|
149
|
+
});
|
|
100
150
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
151
|
+
resolvedRoutes++;
|
|
152
|
+
} catch (err: any) {
|
|
153
|
+
resolvedRoutes++;
|
|
154
|
+
// Skip in getParams() skips the entire route
|
|
155
|
+
if (err.name === "Skip") {
|
|
156
|
+
console.log(
|
|
157
|
+
`[rsc-router] SKIP route "${routeName}" - ${err.message}`,
|
|
158
|
+
);
|
|
159
|
+
notifyOnError(
|
|
160
|
+
registry,
|
|
161
|
+
err,
|
|
162
|
+
"prerender",
|
|
163
|
+
routeName,
|
|
164
|
+
undefined,
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// Regular error: fail the build
|
|
170
|
+
console.error(
|
|
171
|
+
`[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`,
|
|
122
172
|
);
|
|
123
|
-
|
|
173
|
+
notifyOnError(registry, err, "prerender", routeName);
|
|
174
|
+
throw err;
|
|
124
175
|
}
|
|
125
|
-
|
|
126
|
-
console.
|
|
127
|
-
`[rsc-router]
|
|
176
|
+
} else {
|
|
177
|
+
console.warn(
|
|
178
|
+
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`,
|
|
128
179
|
);
|
|
129
|
-
notifyOnError(registry, err, "prerender", routeName);
|
|
130
|
-
throw err;
|
|
131
180
|
}
|
|
132
|
-
} else {
|
|
133
|
-
console.warn(
|
|
134
|
-
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`,
|
|
135
|
-
);
|
|
136
181
|
}
|
|
137
182
|
}
|
|
138
183
|
}
|
|
184
|
+
} finally {
|
|
185
|
+
if (progressInterval) {
|
|
186
|
+
clearInterval(progressInterval);
|
|
187
|
+
const elapsed = ((performance.now() - paramsStart) / 1000).toFixed(1);
|
|
188
|
+
console.log(
|
|
189
|
+
`[rsc-router] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
139
192
|
}
|
|
140
193
|
|
|
141
194
|
if (entries.length === 0) return;
|
|
@@ -150,7 +203,7 @@ export async function expandPrerenderRoutes(
|
|
|
150
203
|
|
|
151
204
|
const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
|
|
152
205
|
|
|
153
|
-
const
|
|
206
|
+
const manifestEntries: Record<string, string> = {};
|
|
154
207
|
let doneCount = 0;
|
|
155
208
|
let skipCount = 0;
|
|
156
209
|
const startTotal = performance.now();
|
|
@@ -173,6 +226,7 @@ export async function expandPrerenderRoutes(
|
|
|
173
226
|
{},
|
|
174
227
|
entry.buildVars,
|
|
175
228
|
entry.isPassthroughRoute,
|
|
229
|
+
state.resolvedBuildEnv,
|
|
176
230
|
);
|
|
177
231
|
if (!result) continue;
|
|
178
232
|
|
|
@@ -187,18 +241,30 @@ export async function expandPrerenderRoutes(
|
|
|
187
241
|
}
|
|
188
242
|
|
|
189
243
|
const paramHash = hashParams(result.params || {});
|
|
190
|
-
|
|
244
|
+
const mainKey = `${result.routeName}/${paramHash}`;
|
|
245
|
+
const mainValue = JSON.stringify({
|
|
191
246
|
segments: result.segments,
|
|
192
247
|
handles: result.handles,
|
|
193
|
-
};
|
|
248
|
+
});
|
|
249
|
+
manifestEntries[mainKey] = stageBuildAssetModule(
|
|
250
|
+
state.projectRoot,
|
|
251
|
+
"__pr",
|
|
252
|
+
mainValue,
|
|
253
|
+
);
|
|
194
254
|
if (result.interceptSegments?.length) {
|
|
195
|
-
|
|
255
|
+
const interceptKey = `${result.routeName}/${paramHash}/i`;
|
|
256
|
+
const interceptValue = JSON.stringify({
|
|
196
257
|
segments: [...result.segments, ...result.interceptSegments],
|
|
197
258
|
handles: {
|
|
198
259
|
...result.handles,
|
|
199
260
|
...(result.interceptHandles || {}),
|
|
200
261
|
},
|
|
201
|
-
};
|
|
262
|
+
});
|
|
263
|
+
manifestEntries[interceptKey] = stageBuildAssetModule(
|
|
264
|
+
state.projectRoot,
|
|
265
|
+
"__pr",
|
|
266
|
+
interceptValue,
|
|
267
|
+
);
|
|
202
268
|
}
|
|
203
269
|
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
204
270
|
console.log(
|
|
@@ -244,7 +310,7 @@ export async function expandPrerenderRoutes(
|
|
|
244
310
|
|
|
245
311
|
const totalElapsed = (performance.now() - startTotal).toFixed(0);
|
|
246
312
|
if (doneCount > 0) {
|
|
247
|
-
state.
|
|
313
|
+
state.prerenderManifestEntries = manifestEntries;
|
|
248
314
|
}
|
|
249
315
|
const parts = [`${doneCount} done`];
|
|
250
316
|
if (skipCount > 0) parts.push(`${skipCount} skipped`);
|
|
@@ -256,7 +322,8 @@ export async function expandPrerenderRoutes(
|
|
|
256
322
|
/**
|
|
257
323
|
* Render Static handlers at build time. Each Static handler is called
|
|
258
324
|
* with a synthetic BuildContext and its output is RSC-serialized.
|
|
259
|
-
*
|
|
325
|
+
* Stages asset modules and stores handlerId-to-file entries in
|
|
326
|
+
* state.staticManifestEntries.
|
|
260
327
|
*/
|
|
261
328
|
export async function renderStaticHandlers(
|
|
262
329
|
state: DiscoveryState,
|
|
@@ -270,10 +337,7 @@ export async function renderStaticHandlers(
|
|
|
270
337
|
)
|
|
271
338
|
return;
|
|
272
339
|
|
|
273
|
-
const
|
|
274
|
-
string,
|
|
275
|
-
{ encoded: string; handles: Record<string, unknown[]> }
|
|
276
|
-
> = {};
|
|
340
|
+
const manifestEntries: Record<string, string> = {};
|
|
277
341
|
let staticDone = 0;
|
|
278
342
|
let staticSkip = 0;
|
|
279
343
|
let totalStaticCount = 0;
|
|
@@ -314,9 +378,19 @@ export async function renderStaticHandlers(
|
|
|
314
378
|
def.handler,
|
|
315
379
|
def.$$id,
|
|
316
380
|
(def as any).$$routePrefix,
|
|
381
|
+
state.resolvedBuildEnv,
|
|
382
|
+
!state.isBuildMode,
|
|
317
383
|
);
|
|
318
384
|
if (result) {
|
|
319
|
-
|
|
385
|
+
const hasHandles = Object.keys(result.handles).length > 0;
|
|
386
|
+
const exportValue = hasHandles
|
|
387
|
+
? JSON.stringify(result)
|
|
388
|
+
: JSON.stringify(result.encoded);
|
|
389
|
+
manifestEntries[def.$$id] = stageBuildAssetModule(
|
|
390
|
+
state.projectRoot,
|
|
391
|
+
"__st",
|
|
392
|
+
exportValue,
|
|
393
|
+
);
|
|
320
394
|
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
321
395
|
console.log(
|
|
322
396
|
`[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`,
|
|
@@ -355,7 +429,7 @@ export async function renderStaticHandlers(
|
|
|
355
429
|
|
|
356
430
|
const totalStaticElapsed = (performance.now() - startStatic).toFixed(0);
|
|
357
431
|
if (staticDone > 0) {
|
|
358
|
-
state.
|
|
432
|
+
state.staticManifestEntries = manifestEntries;
|
|
359
433
|
}
|
|
360
434
|
const staticParts = [`${staticDone} done`];
|
|
361
435
|
if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
|