@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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/README.md +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -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)
|
|
@@ -88,36 +88,30 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
88
88
|
state.staticHandlerChunkInfo = null;
|
|
89
89
|
|
|
90
90
|
// 2. Write prerender data as separate importable asset modules
|
|
91
|
-
// and inject a manifest
|
|
91
|
+
// and inject a lazy manifest loader into the RSC entry.
|
|
92
92
|
if (hasPrerenderData && existsSync(rscEntryPath)) {
|
|
93
93
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
94
|
-
|
|
94
|
+
// Check for the specific injection marker to avoid double-injection.
|
|
95
|
+
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
95
96
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let totalBytes = 0;
|
|
97
|
+
let totalBytes = copyStagedBuildAssets(
|
|
98
|
+
state.projectRoot,
|
|
99
|
+
Object.values(state.prerenderManifestEntries!),
|
|
100
|
+
);
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
const manifestMap: Record<string, string> = {};
|
|
103
|
+
for (const [key, assetFileName] of Object.entries(
|
|
104
|
+
state.prerenderManifestEntries!,
|
|
104
105
|
)) {
|
|
105
|
-
|
|
106
|
-
const contentHash = createHash("sha256")
|
|
107
|
-
.update(entryJson)
|
|
108
|
-
.digest("hex")
|
|
109
|
-
.slice(0, 8);
|
|
110
|
-
const assetFileName = `__pr-${contentHash}.js`;
|
|
111
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
112
|
-
const assetCode = `export default ${entryJson};\n`;
|
|
113
|
-
writeFileSync(assetPath, assetCode);
|
|
114
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
115
|
-
manifestEntries.push(
|
|
116
|
-
`${JSON.stringify(key)}:()=>import("./assets/${assetFileName}")`,
|
|
117
|
-
);
|
|
106
|
+
manifestMap[key] = `./assets/${assetFileName}`;
|
|
118
107
|
}
|
|
119
108
|
|
|
120
|
-
const manifestCode =
|
|
109
|
+
const manifestCode = [
|
|
110
|
+
`const m=JSON.parse('${JSON.stringify(manifestMap).replace(/'/g, "\\'")}');`,
|
|
111
|
+
`export function loadPrerenderAsset(s){return import(s)}`,
|
|
112
|
+
`export default m;`,
|
|
113
|
+
"",
|
|
114
|
+
].join("\n");
|
|
121
115
|
const manifestPath = resolve(
|
|
122
116
|
state.projectRoot,
|
|
123
117
|
"dist/rsc/__prerender-manifest.js",
|
|
@@ -125,12 +119,12 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
125
119
|
writeFileSync(manifestPath, manifestCode);
|
|
126
120
|
totalBytes += Buffer.byteLength(manifestCode);
|
|
127
121
|
|
|
128
|
-
const injection = `
|
|
122
|
+
const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");\n`;
|
|
129
123
|
writeFileSync(rscEntryPath, injection + rscCode);
|
|
130
124
|
|
|
131
125
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
132
126
|
console.log(
|
|
133
|
-
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.
|
|
127
|
+
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
|
|
134
128
|
);
|
|
135
129
|
} catch (err: any) {
|
|
136
130
|
throw new Error(
|
|
@@ -146,29 +140,15 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
146
140
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
147
141
|
if (!rscCode.includes("__STATIC_MANIFEST")) {
|
|
148
142
|
try {
|
|
149
|
-
const assetsDir = resolve(state.projectRoot, "dist/rsc/assets");
|
|
150
|
-
mkdirSync(assetsDir, { recursive: true });
|
|
151
|
-
|
|
152
143
|
const manifestEntries: string[] = [];
|
|
153
|
-
let totalBytes =
|
|
144
|
+
let totalBytes = copyStagedBuildAssets(
|
|
145
|
+
state.projectRoot,
|
|
146
|
+
Object.values(state.staticManifestEntries!),
|
|
147
|
+
);
|
|
154
148
|
|
|
155
|
-
for (const [handlerId,
|
|
156
|
-
state.
|
|
149
|
+
for (const [handlerId, assetFileName] of Object.entries(
|
|
150
|
+
state.staticManifestEntries!,
|
|
157
151
|
)) {
|
|
158
|
-
const contentHash = createHash("sha256")
|
|
159
|
-
.update(encoded)
|
|
160
|
-
.digest("hex")
|
|
161
|
-
.slice(0, 8);
|
|
162
|
-
const assetFileName = `__st-${contentHash}.js`;
|
|
163
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
164
|
-
// Store both the Flight payload and handle data
|
|
165
|
-
const hasHandles = Object.keys(handles).length > 0;
|
|
166
|
-
const exportValue = hasHandles
|
|
167
|
-
? JSON.stringify({ encoded, handles })
|
|
168
|
-
: JSON.stringify(encoded);
|
|
169
|
-
const assetCode = `export default ${exportValue};\n`;
|
|
170
|
-
writeFileSync(assetPath, assetCode);
|
|
171
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
172
152
|
manifestEntries.push(
|
|
173
153
|
`${JSON.stringify(handlerId)}:()=>import("./assets/${assetFileName}")`,
|
|
174
154
|
);
|
|
@@ -192,7 +172,7 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
192
172
|
|
|
193
173
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
194
174
|
console.log(
|
|
195
|
-
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.
|
|
175
|
+
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
|
|
196
176
|
);
|
|
197
177
|
} catch (err: any) {
|
|
198
178
|
throw new Error(
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
* router, and builds route tries for O(path_length) matching.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
buildCombinedRouteMapForRouterFile,
|
|
11
|
+
formatNestedRouterConflictError,
|
|
12
|
+
findNestedRouterConflict,
|
|
13
|
+
} from "../../build/generate-route-types.js";
|
|
10
14
|
import {
|
|
11
15
|
flattenLeafEntries,
|
|
12
16
|
buildRouteToStaticPrefix,
|
|
@@ -44,9 +48,8 @@ export async function discoverRouters(
|
|
|
44
48
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
45
49
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
46
50
|
try {
|
|
47
|
-
const hostMod = await rscEnv.runner.import("@rangojs/router/host");
|
|
48
51
|
const hostRegistry: Map<string, any> | undefined =
|
|
49
|
-
|
|
52
|
+
serverMod.HostRouterRegistry;
|
|
50
53
|
|
|
51
54
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
52
55
|
console.log(
|
|
@@ -85,7 +88,7 @@ export async function discoverRouters(
|
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
} catch {
|
|
88
|
-
//
|
|
91
|
+
// Host-router discovery is best-effort; skip if unavailable
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
// If still no routers after host router resolution, fail
|
|
@@ -98,14 +101,28 @@ export async function discoverRouters(
|
|
|
98
101
|
|
|
99
102
|
// Import build utilities for manifest generation
|
|
100
103
|
const buildMod = await rscEnv.runner.import("@rangojs/router/build");
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
const generateManifestFull = buildMod.generateManifestFull;
|
|
105
|
+
|
|
106
|
+
const nestedRouterConflict = findNestedRouterConflict(
|
|
107
|
+
[...registry.values()]
|
|
108
|
+
.map((router) => router.__sourceFile)
|
|
109
|
+
.filter(
|
|
110
|
+
(sourceFile): sourceFile is string => typeof sourceFile === "string",
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
if (nestedRouterConflict) {
|
|
114
|
+
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Build into local variables first. Only commit to state after the
|
|
118
|
+
// full pass succeeds, so a failed re-discovery preserves the last
|
|
119
|
+
// known-good state instead of leaving it partially wiped.
|
|
120
|
+
const newMergedRouteManifest: Record<string, string> = {};
|
|
121
|
+
const newMergedPrecomputedEntries: PrecomputedEntry[] = [];
|
|
122
|
+
const newPerRouterManifests: typeof state.perRouterManifests = [];
|
|
123
|
+
const newPerRouterManifestDataMap = new Map<string, any>();
|
|
124
|
+
const newPerRouterPrecomputedMap = new Map<string, PrecomputedEntry[]>();
|
|
125
|
+
const newPerRouterTrieMap = new Map<string, any>();
|
|
109
126
|
let mergedRouteAncestry: Record<string, string[]> = {};
|
|
110
127
|
let mergedRouteTrailingSlash: Record<string, string> = {};
|
|
111
128
|
|
|
@@ -114,11 +131,11 @@ export async function discoverRouters(
|
|
|
114
131
|
const allManifests: Array<{ id: string; manifest: any }> = [];
|
|
115
132
|
|
|
116
133
|
for (const [id, router] of registry) {
|
|
117
|
-
if (!router.urlpatterns || !
|
|
134
|
+
if (!router.urlpatterns || !generateManifestFull) {
|
|
118
135
|
continue;
|
|
119
136
|
}
|
|
120
137
|
|
|
121
|
-
const manifest =
|
|
138
|
+
const manifest = generateManifestFull(router.urlpatterns, routerMountIndex);
|
|
122
139
|
routerMountIndex++;
|
|
123
140
|
allManifests.push({ id, manifest });
|
|
124
141
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
@@ -128,7 +145,7 @@ export async function discoverRouters(
|
|
|
128
145
|
const dynamicRoutes = routeCount - staticRoutes;
|
|
129
146
|
|
|
130
147
|
// Merge into the combined manifest
|
|
131
|
-
Object.assign(
|
|
148
|
+
Object.assign(newMergedRouteManifest, manifest.routeManifest);
|
|
132
149
|
|
|
133
150
|
// Compute factory-only prefixes: dot-prefixed groups in the runtime
|
|
134
151
|
// manifest that the static parser cannot see. These are routes created
|
|
@@ -152,7 +169,7 @@ export async function discoverRouters(
|
|
|
152
169
|
if (factoryOnlyPrefixes.size === 0) factoryOnlyPrefixes = undefined;
|
|
153
170
|
}
|
|
154
171
|
|
|
155
|
-
|
|
172
|
+
newPerRouterManifests.push({
|
|
156
173
|
id,
|
|
157
174
|
routeManifest: manifest.routeManifest,
|
|
158
175
|
routeSearchSchemas: manifest.routeSearchSchemas,
|
|
@@ -175,18 +192,18 @@ export async function discoverRouters(
|
|
|
175
192
|
flattenLeafEntries(
|
|
176
193
|
manifest.prefixTree,
|
|
177
194
|
manifest.routeManifest,
|
|
178
|
-
|
|
195
|
+
newMergedPrecomputedEntries,
|
|
179
196
|
);
|
|
180
197
|
|
|
181
198
|
// Store per-router manifest and precomputed entries for isolated virtual modules.
|
|
182
|
-
|
|
199
|
+
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
183
200
|
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
184
201
|
flattenLeafEntries(
|
|
185
202
|
manifest.prefixTree,
|
|
186
203
|
manifest.routeManifest,
|
|
187
204
|
routerPrecomputed,
|
|
188
205
|
);
|
|
189
|
-
|
|
206
|
+
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
190
207
|
|
|
191
208
|
console.log(
|
|
192
209
|
`[rsc-router] Router "${id}" -> ${routeCount} routes ` +
|
|
@@ -214,10 +231,8 @@ export async function discoverRouters(
|
|
|
214
231
|
}
|
|
215
232
|
|
|
216
233
|
// Build route trie from merged manifest + ancestry
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Object.keys(state.mergedRouteManifest).length > 0
|
|
220
|
-
) {
|
|
234
|
+
let newMergedRouteTrie: any = null;
|
|
235
|
+
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
221
236
|
const buildRouteTrie = buildMod.buildRouteTrie;
|
|
222
237
|
if (buildRouteTrie && mergedRouteAncestry) {
|
|
223
238
|
// Build routeToStaticPrefix from saved manifests
|
|
@@ -252,8 +267,8 @@ export async function discoverRouters(
|
|
|
252
267
|
}
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
|
|
256
|
-
|
|
270
|
+
newMergedRouteTrie = buildRouteTrie(
|
|
271
|
+
newMergedRouteManifest,
|
|
257
272
|
mergedRouteAncestry,
|
|
258
273
|
routeToStaticPrefix,
|
|
259
274
|
Object.keys(mergedRouteTrailingSlash).length > 0
|
|
@@ -305,11 +320,22 @@ export async function discoverRouters(
|
|
|
305
320
|
? manifest.responseTypeRoutes
|
|
306
321
|
: undefined,
|
|
307
322
|
);
|
|
308
|
-
|
|
323
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
309
324
|
}
|
|
310
325
|
}
|
|
311
326
|
}
|
|
312
327
|
|
|
328
|
+
// Commit all local state to the shared discovery state atomically.
|
|
329
|
+
// This ensures a failed re-discovery (e.g. from a transient module
|
|
330
|
+
// evaluation error) preserves the last known-good state.
|
|
331
|
+
state.mergedRouteManifest = newMergedRouteManifest;
|
|
332
|
+
state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
|
|
333
|
+
state.perRouterManifests = newPerRouterManifests;
|
|
334
|
+
state.perRouterManifestDataMap = newPerRouterManifestDataMap;
|
|
335
|
+
state.perRouterPrecomputedMap = newPerRouterPrecomputedMap;
|
|
336
|
+
state.perRouterTrieMap = newPerRouterTrieMap;
|
|
337
|
+
state.mergedRouteTrie = newMergedRouteTrie;
|
|
338
|
+
|
|
313
339
|
// Expand prerender routes and render static handlers (build mode only)
|
|
314
340
|
await expandPrerenderRoutes(state, rscEnv, registry, allManifests);
|
|
315
341
|
await renderStaticHandlers(state, rscEnv, registry);
|
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
import { contextSet } from "../../context-var.js";
|
|
10
10
|
import {
|
|
11
11
|
encodePathParam,
|
|
12
|
-
|
|
12
|
+
substituteRouteParams,
|
|
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,
|
|
@@ -33,6 +35,7 @@ export async function expandPrerenderRoutes(
|
|
|
33
35
|
routeName: string;
|
|
34
36
|
concurrency: number;
|
|
35
37
|
buildVars?: Record<string, any>;
|
|
38
|
+
isPassthroughRoute?: boolean;
|
|
36
39
|
};
|
|
37
40
|
const entries: PrerenderEntry[] = [];
|
|
38
41
|
|
|
@@ -44,19 +47,8 @@ export async function expandPrerenderRoutes(
|
|
|
44
47
|
const getParamsReverse = (name: string, params?: Record<string, string>) => {
|
|
45
48
|
const pattern = allRoutes[name];
|
|
46
49
|
if (!pattern) throw new Error(`Unknown route: "${name}"`);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for (const [key, value] of Object.entries(params)) {
|
|
50
|
-
// Strip constraint syntax: :param(a|b) -> value
|
|
51
|
-
const escaped = escapeRegExp(key);
|
|
52
|
-
result = result.replace(
|
|
53
|
-
new RegExp(`:${escaped}(\\([^)]*\\))?`),
|
|
54
|
-
encodeURIComponent(value),
|
|
55
|
-
);
|
|
56
|
-
result = result.replace(`*${key}`, encodeURIComponent(value));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return result;
|
|
50
|
+
if (!params) return pattern;
|
|
51
|
+
return substituteRouteParams(pattern, params);
|
|
60
52
|
};
|
|
61
53
|
|
|
62
54
|
for (const { manifest } of allManifests) {
|
|
@@ -65,6 +57,8 @@ export async function expandPrerenderRoutes(
|
|
|
65
57
|
for (const routeName of manifest.prerenderRoutes) {
|
|
66
58
|
const pattern = manifest.routeManifest[routeName];
|
|
67
59
|
if (!pattern) continue;
|
|
60
|
+
const def = defs[routeName];
|
|
61
|
+
const isPassthroughRoute = !!def?.options?.passthrough;
|
|
68
62
|
const hasDynamic = pattern.includes(":") || pattern.includes("*");
|
|
69
63
|
if (!hasDynamic) {
|
|
70
64
|
// Static route: use pattern directly (strip trailing slash for URL)
|
|
@@ -72,10 +66,10 @@ export async function expandPrerenderRoutes(
|
|
|
72
66
|
urlPath: pattern.replace(/\/$/, "") || "/",
|
|
73
67
|
routeName,
|
|
74
68
|
concurrency: 1,
|
|
69
|
+
isPassthroughRoute,
|
|
75
70
|
});
|
|
76
71
|
} else {
|
|
77
72
|
// Dynamic route: call getParams() to enumerate param combinations
|
|
78
|
-
const def = defs[routeName];
|
|
79
73
|
if (def?.getParams) {
|
|
80
74
|
try {
|
|
81
75
|
const buildVars: Record<string, any> = {};
|
|
@@ -92,19 +86,11 @@ export async function expandPrerenderRoutes(
|
|
|
92
86
|
Object.keys(buildVars).length > 0 ||
|
|
93
87
|
Object.getOwnPropertySymbols(buildVars).length > 0;
|
|
94
88
|
for (const params of paramsList) {
|
|
95
|
-
let url =
|
|
96
|
-
|
|
89
|
+
let url = substituteRouteParams(
|
|
90
|
+
pattern,
|
|
97
91
|
params as Record<string, string>,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Strip constraint syntax: :param(a|b) -> value
|
|
101
|
-
const escaped = escapeRegExp(key);
|
|
102
|
-
url = url.replace(
|
|
103
|
-
new RegExp(`:${escaped}(\\([^)]*\\))?`),
|
|
104
|
-
encoded,
|
|
105
|
-
);
|
|
106
|
-
url = url.replace(`*${key}`, encoded);
|
|
107
|
-
}
|
|
92
|
+
encodePathParam,
|
|
93
|
+
);
|
|
108
94
|
// Anonymous wildcard fallback: use conventional keys if provided
|
|
109
95
|
if (url.includes("*")) {
|
|
110
96
|
const wildcardValue =
|
|
@@ -119,6 +105,7 @@ export async function expandPrerenderRoutes(
|
|
|
119
105
|
routeName,
|
|
120
106
|
concurrency,
|
|
121
107
|
...(hasBuildVars ? { buildVars } : {}),
|
|
108
|
+
isPassthroughRoute,
|
|
122
109
|
});
|
|
123
110
|
}
|
|
124
111
|
} catch (err: any) {
|
|
@@ -165,7 +152,7 @@ export async function expandPrerenderRoutes(
|
|
|
165
152
|
|
|
166
153
|
const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
|
|
167
154
|
|
|
168
|
-
const
|
|
155
|
+
const manifestEntries: Record<string, string> = {};
|
|
169
156
|
let doneCount = 0;
|
|
170
157
|
let skipCount = 0;
|
|
171
158
|
const startTotal = performance.now();
|
|
@@ -187,21 +174,45 @@ export async function expandPrerenderRoutes(
|
|
|
187
174
|
entry.urlPath,
|
|
188
175
|
{},
|
|
189
176
|
entry.buildVars,
|
|
177
|
+
entry.isPassthroughRoute,
|
|
190
178
|
);
|
|
191
179
|
if (!result) continue;
|
|
180
|
+
|
|
181
|
+
// Handler returned ctx.passthrough() — skip manifest entry
|
|
182
|
+
if (result.passthrough) {
|
|
183
|
+
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
184
|
+
console.log(
|
|
185
|
+
`[rsc-router] PASS ${entry.urlPath.padEnd(40)} (${elapsed}ms) - live fallback`,
|
|
186
|
+
);
|
|
187
|
+
doneCount++;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
|
|
192
191
|
const paramHash = hashParams(result.params || {});
|
|
193
|
-
|
|
192
|
+
const mainKey = `${result.routeName}/${paramHash}`;
|
|
193
|
+
const mainValue = JSON.stringify({
|
|
194
194
|
segments: result.segments,
|
|
195
195
|
handles: result.handles,
|
|
196
|
-
};
|
|
196
|
+
});
|
|
197
|
+
manifestEntries[mainKey] = stageBuildAssetModule(
|
|
198
|
+
state.projectRoot,
|
|
199
|
+
"__pr",
|
|
200
|
+
mainValue,
|
|
201
|
+
);
|
|
197
202
|
if (result.interceptSegments?.length) {
|
|
198
|
-
|
|
203
|
+
const interceptKey = `${result.routeName}/${paramHash}/i`;
|
|
204
|
+
const interceptValue = JSON.stringify({
|
|
199
205
|
segments: [...result.segments, ...result.interceptSegments],
|
|
200
206
|
handles: {
|
|
201
207
|
...result.handles,
|
|
202
208
|
...(result.interceptHandles || {}),
|
|
203
209
|
},
|
|
204
|
-
};
|
|
210
|
+
});
|
|
211
|
+
manifestEntries[interceptKey] = stageBuildAssetModule(
|
|
212
|
+
state.projectRoot,
|
|
213
|
+
"__pr",
|
|
214
|
+
interceptValue,
|
|
215
|
+
);
|
|
205
216
|
}
|
|
206
217
|
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
207
218
|
console.log(
|
|
@@ -247,7 +258,7 @@ export async function expandPrerenderRoutes(
|
|
|
247
258
|
|
|
248
259
|
const totalElapsed = (performance.now() - startTotal).toFixed(0);
|
|
249
260
|
if (doneCount > 0) {
|
|
250
|
-
state.
|
|
261
|
+
state.prerenderManifestEntries = manifestEntries;
|
|
251
262
|
}
|
|
252
263
|
const parts = [`${doneCount} done`];
|
|
253
264
|
if (skipCount > 0) parts.push(`${skipCount} skipped`);
|
|
@@ -259,7 +270,8 @@ export async function expandPrerenderRoutes(
|
|
|
259
270
|
/**
|
|
260
271
|
* Render Static handlers at build time. Each Static handler is called
|
|
261
272
|
* with a synthetic BuildContext and its output is RSC-serialized.
|
|
262
|
-
*
|
|
273
|
+
* Stages asset modules and stores handlerId-to-file entries in
|
|
274
|
+
* state.staticManifestEntries.
|
|
263
275
|
*/
|
|
264
276
|
export async function renderStaticHandlers(
|
|
265
277
|
state: DiscoveryState,
|
|
@@ -273,10 +285,7 @@ export async function renderStaticHandlers(
|
|
|
273
285
|
)
|
|
274
286
|
return;
|
|
275
287
|
|
|
276
|
-
const
|
|
277
|
-
string,
|
|
278
|
-
{ encoded: string; handles: Record<string, unknown[]> }
|
|
279
|
-
> = {};
|
|
288
|
+
const manifestEntries: Record<string, string> = {};
|
|
280
289
|
let staticDone = 0;
|
|
281
290
|
let staticSkip = 0;
|
|
282
291
|
let totalStaticCount = 0;
|
|
@@ -319,7 +328,15 @@ export async function renderStaticHandlers(
|
|
|
319
328
|
(def as any).$$routePrefix,
|
|
320
329
|
);
|
|
321
330
|
if (result) {
|
|
322
|
-
|
|
331
|
+
const hasHandles = Object.keys(result.handles).length > 0;
|
|
332
|
+
const exportValue = hasHandles
|
|
333
|
+
? JSON.stringify(result)
|
|
334
|
+
: JSON.stringify(result.encoded);
|
|
335
|
+
manifestEntries[def.$$id] = stageBuildAssetModule(
|
|
336
|
+
state.projectRoot,
|
|
337
|
+
"__st",
|
|
338
|
+
exportValue,
|
|
339
|
+
);
|
|
323
340
|
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
324
341
|
console.log(
|
|
325
342
|
`[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`,
|
|
@@ -358,7 +375,7 @@ export async function renderStaticHandlers(
|
|
|
358
375
|
|
|
359
376
|
const totalStaticElapsed = (performance.now() - startStatic).toFixed(0);
|
|
360
377
|
if (staticDone > 0) {
|
|
361
|
-
state.
|
|
378
|
+
state.staticManifestEntries = manifestEntries;
|
|
362
379
|
}
|
|
363
380
|
const staticParts = [`${staticDone} done`];
|
|
364
381
|
if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
|
|
@@ -15,20 +15,20 @@ import {
|
|
|
15
15
|
} from "../../build/generate-route-types.js";
|
|
16
16
|
import type { DiscoveryState } from "./state.js";
|
|
17
17
|
import { markSelfGenWrite } from "./self-gen-tracking.js";
|
|
18
|
+
import { isAutoGeneratedRouteName } from "../../route-name.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Filter out auto-generated route names from a manifest.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* which never produces these names.
|
|
22
|
+
* Unnamed routes get "$path_"-prefixed names at runtime (see path-helper.ts).
|
|
23
|
+
* These should not appear in the typed gen file. User-defined names
|
|
24
|
+
* containing "$" (e.g. "$admin") are valid and preserved.
|
|
25
25
|
*/
|
|
26
26
|
function filterUserNamedRoutes(
|
|
27
27
|
manifest: Record<string, string>,
|
|
28
28
|
): Record<string, string> {
|
|
29
29
|
const filtered: Record<string, string> = {};
|
|
30
30
|
for (const [name, pattern] of Object.entries(manifest)) {
|
|
31
|
-
if (!name
|
|
31
|
+
if (!isAutoGeneratedRouteName(name)) {
|
|
32
32
|
filtered[name] = pattern;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -222,8 +222,8 @@ export function supplementGenFilesWithRuntimeRoutes(
|
|
|
222
222
|
};
|
|
223
223
|
|
|
224
224
|
for (const [name, pattern] of Object.entries(routeManifest)) {
|
|
225
|
-
// Skip
|
|
226
|
-
if (name
|
|
225
|
+
// Skip internal runtime-only names from unnamed routes/includes.
|
|
226
|
+
if (isAutoGeneratedRouteName(name)) continue;
|
|
227
227
|
const dotIdx = name.indexOf(".");
|
|
228
228
|
if (dotIdx <= 0) continue;
|
|
229
229
|
const prefix = name.substring(0, dotIdx + 1);
|
|
@@ -15,6 +15,9 @@ export interface PluginOptions {
|
|
|
15
15
|
staticRouteTypesGeneration?: boolean;
|
|
16
16
|
include?: string[];
|
|
17
17
|
exclude?: string[];
|
|
18
|
+
// Mutable ref for deferred auto-discovery (node preset).
|
|
19
|
+
// The auto-discover config() hook populates this before configResolved.
|
|
20
|
+
routerPathRef?: { path?: string };
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface PrecomputedEntry {
|
|
@@ -53,11 +56,8 @@ export interface DiscoveryState {
|
|
|
53
56
|
perRouterPrecomputedMap: Map<string, PrecomputedEntry[]>;
|
|
54
57
|
perRouterManifestDataMap: Map<string, Record<string, string>>;
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
string,
|
|
59
|
-
{ encoded: string; handles: Record<string, unknown[]> }
|
|
60
|
-
> | null;
|
|
59
|
+
prerenderManifestEntries: Record<string, string> | null;
|
|
60
|
+
staticManifestEntries: Record<string, string> | null;
|
|
61
61
|
handlerChunkInfo: ChunkInfo | null;
|
|
62
62
|
staticHandlerChunkInfo: ChunkInfo | null;
|
|
63
63
|
rscEntryFileName: string | null;
|
|
@@ -93,8 +93,8 @@ export function createDiscoveryState(
|
|
|
93
93
|
perRouterPrecomputedMap: new Map(),
|
|
94
94
|
perRouterManifestDataMap: new Map(),
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
prerenderManifestEntries: null,
|
|
97
|
+
staticManifestEntries: null,
|
|
98
98
|
handlerChunkInfo: null,
|
|
99
99
|
staticHandlerChunkInfo: null,
|
|
100
100
|
rscEntryFileName: null,
|
|
@@ -42,7 +42,7 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
|
|
|
42
42
|
const genPath = join(
|
|
43
43
|
routerDir,
|
|
44
44
|
`${routerBasename}.named-routes.gen.js`,
|
|
45
|
-
);
|
|
45
|
+
).replaceAll("\\", "/");
|
|
46
46
|
const varName = `_r${varIdx++}`;
|
|
47
47
|
genFileImports.push(
|
|
48
48
|
`import { NamedRoutes as ${varName} } from ${JSON.stringify(genPath)};`,
|
|
@@ -176,7 +176,10 @@ export function generatePerRouterModule(
|
|
|
176
176
|
/\.(tsx?|jsx?)$/,
|
|
177
177
|
"",
|
|
178
178
|
);
|
|
179
|
-
const genPath = join(
|
|
179
|
+
const genPath = join(
|
|
180
|
+
routerDir,
|
|
181
|
+
`${routerBasename}.named-routes.gen.js`,
|
|
182
|
+
).replaceAll("\\", "/");
|
|
180
183
|
lines.push(`import { NamedRoutes as _r } from ${JSON.stringify(genPath)};`);
|
|
181
184
|
lines.push(
|
|
182
185
|
`function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
|