@rangojs/router 0.0.0-experimental.8a4d0430 → 0.0.0-experimental.8bcfea43
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 +126 -38
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +1171 -461
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +19 -16
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +28 -20
- 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 +88 -45
- 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 +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/route/SKILL.md +55 -0
- package/skills/router-setup/SKILL.md +87 -2
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +13 -1
- package/src/__internal.ts +1 -1
- 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/navigation-bridge.ts +90 -16
- package/src/browser/navigation-client.ts +167 -59
- 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 +184 -16
- package/src/browser/prefetch/fetch.ts +180 -33
- 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 +81 -9
- 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 +168 -65
- 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 +49 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +50 -24
- 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.tsx +84 -230
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +6 -1
- package/src/index.ts +49 -6
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +5 -4
- 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 -0
- 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 +101 -25
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +10 -7
- package/src/router/loader-resolution.ts +159 -21
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +127 -192
- 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 +104 -10
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +8 -30
- package/src/router/middleware.ts +36 -10
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +60 -9
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +198 -20
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +438 -300
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +10 -4
- package/src/router/types.ts +1 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +60 -8
- package/src/rsc/handler.ts +478 -374
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +16 -2
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +19 -1
- package/src/rsc/server-action.ts +10 -0
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +9 -1
- 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 +166 -17
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +194 -60
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +137 -65
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +2 -0
- 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/debug.ts +55 -0
- package/src/vite/discovery/bundle-postprocess.ts +30 -33
- package/src/vite/discovery/discover-routers.ts +5 -1
- package/src/vite/discovery/prerender-collection.ts +128 -74
- package/src/vite/discovery/state.ts +13 -6
- package/src/vite/index.ts +4 -0
- 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 +86 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +204 -217
- package/src/vite/router-discovery.ts +335 -64
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +37 -5
- package/src/vite/utils/shared-utils.ts +3 -2
|
@@ -10,14 +10,19 @@ import type { Plugin } from "vite";
|
|
|
10
10
|
import { createServer as createViteServer } from "vite";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { readFileSync } from "node:fs";
|
|
13
|
+
import { createRequire, register } from "node:module";
|
|
14
|
+
import { pathToFileURL } from "node:url";
|
|
13
15
|
import {
|
|
14
16
|
formatNestedRouterConflictError,
|
|
15
17
|
findNestedRouterConflict,
|
|
16
18
|
findRouterFiles,
|
|
17
|
-
createScanFilter,
|
|
18
19
|
} from "../build/generate-route-types.js";
|
|
19
20
|
import { createVersionPlugin } from "./plugins/version-plugin.js";
|
|
20
21
|
import { createVirtualStubPlugin } from "./plugins/virtual-stub-plugin.js";
|
|
22
|
+
import {
|
|
23
|
+
BUILD_ENV_GLOBAL_KEY,
|
|
24
|
+
createCloudflareProtocolStubPlugin,
|
|
25
|
+
} from "./plugins/cloudflare-protocol-stub.js";
|
|
21
26
|
import {
|
|
22
27
|
exposeInternalIds,
|
|
23
28
|
exposeRouterId,
|
|
@@ -43,9 +48,56 @@ import {
|
|
|
43
48
|
} from "./discovery/virtual-module-codegen.js";
|
|
44
49
|
import { postprocessBundle } from "./discovery/bundle-postprocess.js";
|
|
45
50
|
import { resetStagedBuildAssets } from "./utils/prerender-utils.js";
|
|
51
|
+
import { createRangoDebugger, timed } from "./debug.js";
|
|
52
|
+
|
|
53
|
+
const debugDiscovery = createRangoDebugger("rango:discovery");
|
|
54
|
+
const debugRoutes = createRangoDebugger("rango:routes");
|
|
46
55
|
|
|
47
56
|
export { VIRTUAL_ROUTES_MANIFEST_ID };
|
|
48
57
|
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Node ESM Loader Hook Registration
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Registers a Node ESM loader hook that resolves `cloudflare:*` specifiers
|
|
64
|
+
* to a data: URL stub. Defense-in-depth alongside the Vite transform in
|
|
65
|
+
* `cloudflare-protocol-stub.ts`:
|
|
66
|
+
*
|
|
67
|
+
* - The Vite transform catches `cloudflare:*` imports in modules that flow
|
|
68
|
+
* through Vite's plugin pipeline. That's the vast majority of cases.
|
|
69
|
+
* - The Node loader catches imports in modules that Vite/Rollup externalize
|
|
70
|
+
* (e.g. the `partyserver` package, which has a top-level
|
|
71
|
+
* `import { DurableObject, env } from "cloudflare:workers"` and ships
|
|
72
|
+
* shapes plugin-rsc marks as external). Externalized modules are loaded
|
|
73
|
+
* via Node's native ESM loader, which rejects URL schemes.
|
|
74
|
+
*
|
|
75
|
+
* Registration is process-global and one-shot. The hook only intercepts
|
|
76
|
+
* `cloudflare:*` specifiers; everything else passes through via
|
|
77
|
+
* `nextResolve()`. It runs in a separate worker thread (Node ESM loader
|
|
78
|
+
* architecture), so it can't read the `globalThis[BUILD_ENV_GLOBAL_KEY]`
|
|
79
|
+
* bridge that the Vite transform uses — the stubs served here always
|
|
80
|
+
* return `env = {}`. That's fine because externalized libraries don't
|
|
81
|
+
* typically access `env` at module top level; user source (where real
|
|
82
|
+
* `env` matters at build time) flows through the Vite transform.
|
|
83
|
+
*/
|
|
84
|
+
let loaderHookRegistered = false;
|
|
85
|
+
function ensureCloudflareProtocolLoaderRegistered(): void {
|
|
86
|
+
if (loaderHookRegistered) return;
|
|
87
|
+
loaderHookRegistered = true;
|
|
88
|
+
try {
|
|
89
|
+
register(
|
|
90
|
+
new URL("./plugins/cloudflare-protocol-loader-hook.mjs", import.meta.url),
|
|
91
|
+
);
|
|
92
|
+
} catch (err: any) {
|
|
93
|
+
// register() requires Node 18.19+ / 20.6+. Older Node still has the
|
|
94
|
+
// Vite transform as primary defense.
|
|
95
|
+
console.warn(
|
|
96
|
+
`[rsc-router] Could not register Node ESM loader hook for cloudflare:* imports (${err?.message ?? err}). Falling back to Vite transform only.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
49
101
|
// ============================================================================
|
|
50
102
|
// Temp Server Factory
|
|
51
103
|
// ============================================================================
|
|
@@ -65,6 +117,11 @@ async function createTempRscServer(
|
|
|
65
117
|
state: DiscoveryState,
|
|
66
118
|
options: { forceBuild?: boolean; cacheDir?: string } = {},
|
|
67
119
|
) {
|
|
120
|
+
// Install the Node ESM loader hook before any module evaluation so
|
|
121
|
+
// `cloudflare:*` specifiers in externalized/loader-delegated modules
|
|
122
|
+
// (e.g. packages plugin-rsc marks as external) resolve to stubs
|
|
123
|
+
// instead of crashing Node's native loader.
|
|
124
|
+
ensureCloudflareProtocolLoaderRegistered();
|
|
68
125
|
const { default: rsc } = await import("@vitejs/plugin-rsc");
|
|
69
126
|
return createViteServer({
|
|
70
127
|
root: state.projectRoot,
|
|
@@ -87,6 +144,7 @@ async function createTempRscServer(
|
|
|
87
144
|
...(options.forceBuild ? [hashClientRefs(state.projectRoot)] : []),
|
|
88
145
|
createVersionPlugin(),
|
|
89
146
|
createVirtualStubPlugin(),
|
|
147
|
+
createCloudflareProtocolStubPlugin(),
|
|
90
148
|
// Dev prerender must use dev-mode IDs (path-based) to match the workerd
|
|
91
149
|
// runtime. forceBuild produces hashed IDs for production bundle consistency.
|
|
92
150
|
exposeInternalIds(options.forceBuild ? { forceBuild: true } : undefined),
|
|
@@ -95,6 +153,111 @@ async function createTempRscServer(
|
|
|
95
153
|
});
|
|
96
154
|
}
|
|
97
155
|
|
|
156
|
+
// ============================================================================
|
|
157
|
+
// Build-Time Env Resolution
|
|
158
|
+
// ============================================================================
|
|
159
|
+
|
|
160
|
+
import type {
|
|
161
|
+
BuildEnvOption,
|
|
162
|
+
BuildEnvFactoryContext,
|
|
163
|
+
BuildEnvResult,
|
|
164
|
+
} from "./plugin-types.js";
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Resolve the buildEnv option into a concrete { env, dispose? } result.
|
|
168
|
+
* Handles all four input shapes: false, "auto", factory, plain object.
|
|
169
|
+
*/
|
|
170
|
+
async function resolveBuildEnv(
|
|
171
|
+
option: BuildEnvOption | undefined,
|
|
172
|
+
factoryCtx: BuildEnvFactoryContext,
|
|
173
|
+
): Promise<BuildEnvResult | null> {
|
|
174
|
+
if (!option) return null;
|
|
175
|
+
|
|
176
|
+
if (option === "auto") {
|
|
177
|
+
if (factoryCtx.preset !== "cloudflare") {
|
|
178
|
+
throw new Error(
|
|
179
|
+
'[rsc-router] buildEnv: "auto" is only supported with preset: "cloudflare". ' +
|
|
180
|
+
"Use a factory function or plain object for other presets.",
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
// Resolve wrangler from the user's project root (not the router package)
|
|
185
|
+
const userRequire = createRequire(
|
|
186
|
+
resolve(factoryCtx.root, "package.json"),
|
|
187
|
+
);
|
|
188
|
+
const wranglerPath = userRequire.resolve("wrangler");
|
|
189
|
+
const { getPlatformProxy } = (await import(
|
|
190
|
+
pathToFileURL(wranglerPath).href
|
|
191
|
+
)) as {
|
|
192
|
+
getPlatformProxy: (opts?: any) => Promise<any>;
|
|
193
|
+
};
|
|
194
|
+
const proxy = await getPlatformProxy();
|
|
195
|
+
return {
|
|
196
|
+
env: proxy.env as Record<string, unknown>,
|
|
197
|
+
dispose: proxy.dispose,
|
|
198
|
+
};
|
|
199
|
+
} catch (err: any) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
'[rsc-router] buildEnv: "auto" requires wrangler to be installed.\n' +
|
|
202
|
+
`Install it with: pnpm add -D wrangler\n${err.message}`,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (typeof option === "function") {
|
|
208
|
+
return await option(factoryCtx);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Plain object
|
|
212
|
+
return { env: option };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Acquire build-time env bindings and store on discovery state.
|
|
217
|
+
* Returns true if env was acquired, false if buildEnv is disabled.
|
|
218
|
+
*/
|
|
219
|
+
async function acquireBuildEnv(
|
|
220
|
+
s: DiscoveryState,
|
|
221
|
+
command: "serve" | "build",
|
|
222
|
+
mode: string,
|
|
223
|
+
): Promise<boolean> {
|
|
224
|
+
const option = s.opts?.buildEnv;
|
|
225
|
+
if (!option) return false;
|
|
226
|
+
|
|
227
|
+
const result = await resolveBuildEnv(option, {
|
|
228
|
+
root: s.projectRoot,
|
|
229
|
+
mode,
|
|
230
|
+
command,
|
|
231
|
+
preset: s.opts?.preset ?? "node",
|
|
232
|
+
});
|
|
233
|
+
if (!result) return false;
|
|
234
|
+
|
|
235
|
+
s.resolvedBuildEnv = result.env;
|
|
236
|
+
s.buildEnvDispose = result.dispose ?? null;
|
|
237
|
+
// Bridge the resolved env into `cloudflare:workers`'s stubbed `env`
|
|
238
|
+
// export so user code that does `import { env } from "cloudflare:workers"`
|
|
239
|
+
// sees the real bindings proxy during discovery + prerender instead of
|
|
240
|
+
// an empty object. The stub reads this global at module-evaluation time.
|
|
241
|
+
(globalThis as Record<string, unknown>)[BUILD_ENV_GLOBAL_KEY] = result.env;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Release build-time env resources and clear state.
|
|
247
|
+
*/
|
|
248
|
+
async function releaseBuildEnv(s: DiscoveryState): Promise<void> {
|
|
249
|
+
if (s.buildEnvDispose) {
|
|
250
|
+
try {
|
|
251
|
+
await s.buildEnvDispose();
|
|
252
|
+
} catch (err: any) {
|
|
253
|
+
console.warn(`[rsc-router] buildEnv dispose failed: ${err.message}`);
|
|
254
|
+
}
|
|
255
|
+
s.buildEnvDispose = null;
|
|
256
|
+
}
|
|
257
|
+
s.resolvedBuildEnv = undefined;
|
|
258
|
+
delete (globalThis as Record<string, unknown>)[BUILD_ENV_GLOBAL_KEY];
|
|
259
|
+
}
|
|
260
|
+
|
|
98
261
|
/**
|
|
99
262
|
* Plugin that discovers router instances at dev/build time via the RSC environment.
|
|
100
263
|
*
|
|
@@ -112,6 +275,8 @@ export function createRouterDiscoveryPlugin(
|
|
|
112
275
|
opts?: PluginOptions,
|
|
113
276
|
): Plugin {
|
|
114
277
|
const s = createDiscoveryState(entryPath, opts);
|
|
278
|
+
let viteCommand: "serve" | "build" = "build";
|
|
279
|
+
let viteMode = "production";
|
|
115
280
|
|
|
116
281
|
return {
|
|
117
282
|
name: "@rangojs/router:discovery",
|
|
@@ -122,32 +287,20 @@ export function createRouterDiscoveryPlugin(
|
|
|
122
287
|
__RANGO_DEBUG__: JSON.stringify(!!process.env.INTERNAL_RANGO_DEBUG),
|
|
123
288
|
},
|
|
124
289
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
manualChunks(id: string) {
|
|
132
|
-
if (s.resolvedPrerenderModules?.has(id)) {
|
|
133
|
-
return "__prerender-handlers";
|
|
134
|
-
}
|
|
135
|
-
if (s.resolvedStaticModules?.has(id)) {
|
|
136
|
-
return "__static-handlers";
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
290
|
+
// Prerender/static handler modules are bundled naturally with the
|
|
291
|
+
// rest of the RSC entry. A previous design forced them into dedicated
|
|
292
|
+
// __prerender-handlers / __static-handlers chunks via manualChunks,
|
|
293
|
+
// but Rollup hoisted all shared dependencies into those chunks,
|
|
294
|
+
// inflating them to ~1 MB with active runtime code. Handler code is
|
|
295
|
+
// evicted in closeBundle regardless of which chunk it lands in.
|
|
145
296
|
return config;
|
|
146
297
|
},
|
|
147
298
|
|
|
148
299
|
configResolved(config) {
|
|
149
300
|
s.projectRoot = config.root;
|
|
150
301
|
s.isBuildMode = config.command === "build";
|
|
302
|
+
viteCommand = config.command as "serve" | "build";
|
|
303
|
+
viteMode = config.mode;
|
|
151
304
|
// Capture user's resolve aliases for the temp server
|
|
152
305
|
s.userResolveAlias = config.resolve.alias;
|
|
153
306
|
// Node preset: pick up auto-discovered router path from the config() hook.
|
|
@@ -168,13 +321,6 @@ export function createRouterDiscoveryPlugin(
|
|
|
168
321
|
s.resolvedEntryPath = entries[0];
|
|
169
322
|
}
|
|
170
323
|
}
|
|
171
|
-
// Compile include/exclude patterns into a scan filter
|
|
172
|
-
if (opts?.include || opts?.exclude) {
|
|
173
|
-
s.scanFilter = createScanFilter(s.projectRoot, {
|
|
174
|
-
include: opts.include,
|
|
175
|
-
exclude: opts.exclude,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
324
|
// Generate combined named-routes.gen.ts from static source parsing.
|
|
179
325
|
// Runs before the dev server starts so the gen file exists immediately for IDE.
|
|
180
326
|
// In build mode, the runtime discovery in buildStart produces the definitive
|
|
@@ -225,12 +371,13 @@ export function createRouterDiscoveryPlugin(
|
|
|
225
371
|
let prerenderTempServer: any = null;
|
|
226
372
|
let prerenderNodeRegistry: Map<string, any> | null = null;
|
|
227
373
|
|
|
228
|
-
// Clean up the temporary server when the dev server shuts down
|
|
374
|
+
// Clean up the temporary server and build env when the dev server shuts down
|
|
229
375
|
server.httpServer?.on("close", () => {
|
|
230
376
|
if (prerenderTempServer) {
|
|
231
377
|
prerenderTempServer.close().catch(() => {});
|
|
232
378
|
prerenderTempServer = null;
|
|
233
379
|
}
|
|
380
|
+
releaseBuildEnv(s).catch(() => {});
|
|
234
381
|
});
|
|
235
382
|
|
|
236
383
|
async function getOrCreateTempServer(): Promise<any | null> {
|
|
@@ -260,20 +407,35 @@ export function createRouterDiscoveryPlugin(
|
|
|
260
407
|
}
|
|
261
408
|
|
|
262
409
|
const discover = async () => {
|
|
410
|
+
const discoverStart = performance.now();
|
|
263
411
|
const rscEnv = (server.environments as any)?.rsc;
|
|
264
412
|
if (!rscEnv?.runner) {
|
|
265
413
|
// Cloudflare dev: no module runner available (workerd-based RSC env).
|
|
266
414
|
// Set devServerOrigin so the virtual module can inject __PRERENDER_DEV_URL
|
|
267
415
|
// for on-demand prerender via the /__rsc_prerender endpoint.
|
|
416
|
+
debugDiscovery?.("dev: no rsc runner (cloudflare path)");
|
|
268
417
|
s.devServerOrigin = getDevServerOrigin();
|
|
269
418
|
|
|
270
419
|
// Create a temp Node.js server to run runtime discovery and generate
|
|
271
420
|
// named route types (static parser can't resolve factory calls).
|
|
272
421
|
try {
|
|
273
|
-
|
|
422
|
+
// Acquire build-time env bindings for dev prerender
|
|
423
|
+
await timed(debugDiscovery, "acquireBuildEnv", () =>
|
|
424
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
const tempRscEnv = await timed(
|
|
428
|
+
debugDiscovery,
|
|
429
|
+
"getOrCreateTempServer",
|
|
430
|
+
() => getOrCreateTempServer(),
|
|
431
|
+
);
|
|
274
432
|
if (tempRscEnv) {
|
|
275
|
-
await discoverRouters(
|
|
276
|
-
|
|
433
|
+
await timed(debugDiscovery, "discoverRouters (cloudflare)", () =>
|
|
434
|
+
discoverRouters(s, tempRscEnv),
|
|
435
|
+
);
|
|
436
|
+
timed(debugDiscovery, "writeRouteTypesFiles", () =>
|
|
437
|
+
writeRouteTypesFiles(s),
|
|
438
|
+
);
|
|
277
439
|
}
|
|
278
440
|
} catch (err: any) {
|
|
279
441
|
console.warn(
|
|
@@ -281,21 +443,35 @@ export function createRouterDiscoveryPlugin(
|
|
|
281
443
|
);
|
|
282
444
|
}
|
|
283
445
|
|
|
446
|
+
debugDiscovery?.(
|
|
447
|
+
"dev discovery done (%sms)",
|
|
448
|
+
(performance.now() - discoverStart).toFixed(1),
|
|
449
|
+
);
|
|
284
450
|
resolveDiscovery!();
|
|
285
451
|
return;
|
|
286
452
|
}
|
|
287
453
|
|
|
288
454
|
try {
|
|
455
|
+
// Acquire build-time env bindings for dev prerender (Node.js path)
|
|
456
|
+
debugDiscovery?.("dev: node path start");
|
|
457
|
+
await timed(debugDiscovery, "acquireBuildEnv", () =>
|
|
458
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
459
|
+
);
|
|
460
|
+
|
|
289
461
|
// Set the readiness gate BEFORE discovery so early requests
|
|
290
462
|
// block until manifest is populated
|
|
291
|
-
const serverMod = await
|
|
292
|
-
|
|
463
|
+
const serverMod = await timed(
|
|
464
|
+
debugDiscovery,
|
|
465
|
+
"import @rangojs/router/server",
|
|
466
|
+
() => rscEnv.runner.import("@rangojs/router/server"),
|
|
293
467
|
);
|
|
294
468
|
if (serverMod?.setManifestReadyPromise) {
|
|
295
469
|
serverMod.setManifestReadyPromise(discoveryPromise);
|
|
296
470
|
}
|
|
297
471
|
|
|
298
|
-
await
|
|
472
|
+
await timed(debugDiscovery, "discoverRouters", () =>
|
|
473
|
+
discoverRouters(s, rscEnv),
|
|
474
|
+
);
|
|
299
475
|
|
|
300
476
|
// Store server origin for dev prerender endpoint (virtual module injection)
|
|
301
477
|
s.devServerOrigin = getDevServerOrigin();
|
|
@@ -305,15 +481,23 @@ export function createRouterDiscoveryPlugin(
|
|
|
305
481
|
// routes (e.g. Array.from loops) that the static parser cannot see.
|
|
306
482
|
// writeRouteTypesFiles() only writes when content changes, so this
|
|
307
483
|
// won't cause unnecessary HMR triggers.
|
|
308
|
-
writeRouteTypesFiles(
|
|
484
|
+
timed(debugDiscovery, "writeRouteTypesFiles", () =>
|
|
485
|
+
writeRouteTypesFiles(s),
|
|
486
|
+
);
|
|
309
487
|
|
|
310
488
|
// Populate the route map and per-router data in the RSC env
|
|
311
|
-
await propagateDiscoveryState(
|
|
489
|
+
await timed(debugDiscovery, "propagateDiscoveryState", () =>
|
|
490
|
+
propagateDiscoveryState(rscEnv),
|
|
491
|
+
);
|
|
312
492
|
} catch (err: any) {
|
|
313
493
|
console.warn(
|
|
314
494
|
`[rsc-router] Router discovery failed: ${err.message}\n${err.stack}`,
|
|
315
495
|
);
|
|
316
496
|
} finally {
|
|
497
|
+
debugDiscovery?.(
|
|
498
|
+
"dev discovery done (%sms)",
|
|
499
|
+
(performance.now() - discoverStart).toFixed(1),
|
|
500
|
+
);
|
|
317
501
|
resolveDiscovery!();
|
|
318
502
|
}
|
|
319
503
|
};
|
|
@@ -390,9 +574,31 @@ export function createRouterDiscoveryPlugin(
|
|
|
390
574
|
return;
|
|
391
575
|
}
|
|
392
576
|
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
|
|
577
|
+
// Import the user's entry module to force re-evaluation of any
|
|
578
|
+
// HMR-invalidated modules in the chain (entry → router → urls → handlers).
|
|
579
|
+
// This ensures createRouter() re-runs with updated handler code before
|
|
580
|
+
// we read RouterRegistry. Without this, edits to prerender handler files
|
|
581
|
+
// produce stale content because the old router instance remains registered.
|
|
582
|
+
const rscEnv = (server.environments as any)?.rsc;
|
|
583
|
+
let registry: Map<string, any> | null = null;
|
|
584
|
+
if (rscEnv?.runner && s.resolvedEntryPath) {
|
|
585
|
+
try {
|
|
586
|
+
await rscEnv.runner.import(s.resolvedEntryPath);
|
|
587
|
+
const serverMod = await rscEnv.runner.import(
|
|
588
|
+
"@rangojs/router/server",
|
|
589
|
+
);
|
|
590
|
+
registry = serverMod.RouterRegistry ?? null;
|
|
591
|
+
} catch (err: any) {
|
|
592
|
+
console.warn(
|
|
593
|
+
`[rsc-router] Dev prerender module refresh failed: ${err.message}`,
|
|
594
|
+
);
|
|
595
|
+
res.statusCode = 500;
|
|
596
|
+
res.end(`Prerender handler error: ${err.message}`);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
} else {
|
|
600
|
+
registry = mainRegistry;
|
|
601
|
+
}
|
|
396
602
|
|
|
397
603
|
if (!registry) {
|
|
398
604
|
// No main registry: the RSC env has no module runner (Cloudflare dev).
|
|
@@ -421,6 +627,8 @@ export function createRouterDiscoveryPlugin(
|
|
|
421
627
|
{},
|
|
422
628
|
undefined,
|
|
423
629
|
wantPassthrough,
|
|
630
|
+
s.resolvedBuildEnv,
|
|
631
|
+
true, // devMode: check getParams for passthrough routes
|
|
424
632
|
);
|
|
425
633
|
if (!result) continue;
|
|
426
634
|
if (result.passthrough) continue;
|
|
@@ -499,15 +707,26 @@ export function createRouterDiscoveryPlugin(
|
|
|
499
707
|
const rscEnv = (server.environments as any)?.rsc;
|
|
500
708
|
if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
|
|
501
709
|
runtimeRediscoveryInProgress = true;
|
|
710
|
+
const hmrStart = performance.now();
|
|
502
711
|
try {
|
|
503
|
-
await
|
|
504
|
-
|
|
505
|
-
|
|
712
|
+
await timed(debugDiscovery, "hmr discoverRouters", () =>
|
|
713
|
+
discoverRouters(s, rscEnv),
|
|
714
|
+
);
|
|
715
|
+
timed(debugDiscovery, "hmr writeRouteTypesFiles", () =>
|
|
716
|
+
writeRouteTypesFiles(s),
|
|
717
|
+
);
|
|
718
|
+
await timed(debugDiscovery, "hmr propagateDiscoveryState", () =>
|
|
719
|
+
propagateDiscoveryState(rscEnv),
|
|
720
|
+
);
|
|
506
721
|
} catch (err: any) {
|
|
507
722
|
console.warn(
|
|
508
723
|
`[rsc-router] Runtime re-discovery failed: ${err.message}`,
|
|
509
724
|
);
|
|
510
725
|
} finally {
|
|
726
|
+
debugDiscovery?.(
|
|
727
|
+
"hmr re-discovery done (%sms)",
|
|
728
|
+
(performance.now() - hmrStart).toFixed(1),
|
|
729
|
+
);
|
|
511
730
|
runtimeRediscoveryInProgress = false;
|
|
512
731
|
}
|
|
513
732
|
};
|
|
@@ -605,10 +824,17 @@ export function createRouterDiscoveryPlugin(
|
|
|
605
824
|
if (!s.isBuildMode) return;
|
|
606
825
|
// Only run once across environment builds
|
|
607
826
|
if (s.mergedRouteManifest !== null) return;
|
|
827
|
+
const buildStartTime = performance.now();
|
|
828
|
+
debugDiscovery?.("build: start");
|
|
608
829
|
resetStagedBuildAssets(s.projectRoot);
|
|
609
830
|
s.prerenderManifestEntries = null;
|
|
610
831
|
s.staticManifestEntries = null;
|
|
611
832
|
|
|
833
|
+
// Acquire build-time env bindings if configured
|
|
834
|
+
await timed(debugDiscovery, "build acquireBuildEnv", () =>
|
|
835
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
836
|
+
);
|
|
837
|
+
|
|
612
838
|
let tempServer: any = null;
|
|
613
839
|
// Signal to user-space code (e.g. reverse.ts) that build-time discovery
|
|
614
840
|
// is active. Uses globalThis because the temp server's module runner
|
|
@@ -616,7 +842,11 @@ export function createRouterDiscoveryPlugin(
|
|
|
616
842
|
// between the vite plugin and user code loaded via runner.import().
|
|
617
843
|
(globalThis as any).__rscRouterDiscoveryActive = true;
|
|
618
844
|
try {
|
|
619
|
-
tempServer = await
|
|
845
|
+
tempServer = await timed(
|
|
846
|
+
debugDiscovery,
|
|
847
|
+
"build createTempRscServer",
|
|
848
|
+
() => createTempRscServer(s, { forceBuild: true }),
|
|
849
|
+
);
|
|
620
850
|
|
|
621
851
|
const rscEnv = (tempServer.environments as any)?.rsc;
|
|
622
852
|
if (!rscEnv?.runner) {
|
|
@@ -636,11 +866,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
636
866
|
s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
|
|
637
867
|
}
|
|
638
868
|
|
|
639
|
-
await
|
|
869
|
+
await timed(debugDiscovery, "build discoverRouters", () =>
|
|
870
|
+
discoverRouters(s, rscEnv),
|
|
871
|
+
);
|
|
640
872
|
// Update named-routes.gen.ts from runtime discovery.
|
|
641
873
|
// The runtime manifest includes dynamically generated routes
|
|
642
874
|
// that the static parser cannot extract from source code.
|
|
643
|
-
writeRouteTypesFiles(
|
|
875
|
+
timed(debugDiscovery, "build writeRouteTypesFiles", () =>
|
|
876
|
+
writeRouteTypesFiles(s),
|
|
877
|
+
);
|
|
644
878
|
} catch (err: any) {
|
|
645
879
|
// Extract the user source file from the stack trace (skip internal frames)
|
|
646
880
|
const sourceFile = err.stack
|
|
@@ -665,8 +899,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
665
899
|
} finally {
|
|
666
900
|
delete (globalThis as any).__rscRouterDiscoveryActive;
|
|
667
901
|
if (tempServer) {
|
|
668
|
-
await tempServer.close()
|
|
902
|
+
await timed(debugDiscovery, "build tempServer.close", () =>
|
|
903
|
+
tempServer.close(),
|
|
904
|
+
);
|
|
669
905
|
}
|
|
906
|
+
await releaseBuildEnv(s);
|
|
907
|
+
debugDiscovery?.(
|
|
908
|
+
"build discovery done (%sms)",
|
|
909
|
+
(performance.now() - buildStartTime).toFixed(1),
|
|
910
|
+
);
|
|
670
911
|
}
|
|
671
912
|
},
|
|
672
913
|
|
|
@@ -690,19 +931,34 @@ export function createRouterDiscoveryPlugin(
|
|
|
690
931
|
// This is critical for Cloudflare dev where the worker runs in a separate
|
|
691
932
|
// Miniflare process and can only receive manifest data via the virtual module.
|
|
692
933
|
if (s.discoveryDone) {
|
|
693
|
-
await
|
|
934
|
+
await timed(debugRoutes, "await discoveryDone (manifest)", () =>
|
|
935
|
+
Promise.resolve(s.discoveryDone),
|
|
936
|
+
);
|
|
694
937
|
}
|
|
695
|
-
|
|
938
|
+
const code = await timed(
|
|
939
|
+
debugRoutes,
|
|
940
|
+
"generateRoutesManifestModule",
|
|
941
|
+
() => generateRoutesManifestModule(s),
|
|
942
|
+
);
|
|
943
|
+
debugRoutes?.("manifest module emitted (%d bytes)", code?.length ?? 0);
|
|
944
|
+
return code;
|
|
696
945
|
}
|
|
697
946
|
// Per-router virtual modules: pure data exports (no side effects).
|
|
698
947
|
// ensureRouterManifest() imports the module and stores the data.
|
|
699
948
|
const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
|
|
700
949
|
if (id.startsWith(perRouterPrefix)) {
|
|
701
950
|
if (s.discoveryDone) {
|
|
702
|
-
await
|
|
951
|
+
await timed(debugRoutes, "await discoveryDone (per-router)", () =>
|
|
952
|
+
Promise.resolve(s.discoveryDone),
|
|
953
|
+
);
|
|
703
954
|
}
|
|
704
955
|
const routerId = id.slice(perRouterPrefix.length);
|
|
705
|
-
|
|
956
|
+
const code = await timed(
|
|
957
|
+
debugRoutes,
|
|
958
|
+
`generatePerRouterModule ${routerId}`,
|
|
959
|
+
() => generatePerRouterModule(s, routerId),
|
|
960
|
+
);
|
|
961
|
+
return code;
|
|
706
962
|
}
|
|
707
963
|
// virtual:rsc-router/prerender-paths load handler removed
|
|
708
964
|
return null;
|
|
@@ -727,33 +983,40 @@ export function createRouterDiscoveryPlugin(
|
|
|
727
983
|
if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size)
|
|
728
984
|
return;
|
|
729
985
|
|
|
986
|
+
// Clear maps at the start of each RSC generateBundle pass.
|
|
987
|
+
// Vite 6 multi-environment builds run RSC twice (analysis + production);
|
|
988
|
+
// clearing prevents stale/duplicate records from the analysis pass.
|
|
989
|
+
s.handlerChunkInfoMap.clear();
|
|
990
|
+
s.staticHandlerChunkInfoMap.clear();
|
|
991
|
+
|
|
730
992
|
for (const [fileName, chunk] of Object.entries(bundle) as [
|
|
731
993
|
string,
|
|
732
994
|
any,
|
|
733
995
|
][]) {
|
|
734
996
|
if (chunk.type !== "chunk") continue;
|
|
735
997
|
|
|
736
|
-
//
|
|
737
|
-
if (
|
|
738
|
-
fileName.includes("__prerender-handlers") &&
|
|
739
|
-
s.resolvedPrerenderModules?.size
|
|
740
|
-
) {
|
|
998
|
+
// Scan all chunks for handler exports (handlers may land in any chunk)
|
|
999
|
+
if (s.resolvedPrerenderModules?.size) {
|
|
741
1000
|
const handlers = extractHandlerExportsFromChunk(
|
|
742
1001
|
chunk.code,
|
|
743
1002
|
s.resolvedPrerenderModules,
|
|
744
1003
|
"Prerender",
|
|
745
|
-
|
|
1004
|
+
false,
|
|
746
1005
|
);
|
|
747
1006
|
if (handlers.length > 0) {
|
|
748
|
-
|
|
1007
|
+
const existing = s.handlerChunkInfoMap.get(fileName);
|
|
1008
|
+
if (existing) {
|
|
1009
|
+
existing.exports.push(...handlers);
|
|
1010
|
+
} else {
|
|
1011
|
+
s.handlerChunkInfoMap.set(fileName, {
|
|
1012
|
+
fileName,
|
|
1013
|
+
exports: handlers,
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
749
1016
|
}
|
|
750
1017
|
}
|
|
751
1018
|
|
|
752
|
-
|
|
753
|
-
if (
|
|
754
|
-
fileName.includes("__static-handlers") &&
|
|
755
|
-
s.resolvedStaticModules?.size
|
|
756
|
-
) {
|
|
1019
|
+
if (s.resolvedStaticModules?.size) {
|
|
757
1020
|
const handlers = extractHandlerExportsFromChunk(
|
|
758
1021
|
chunk.code,
|
|
759
1022
|
s.resolvedStaticModules,
|
|
@@ -761,7 +1024,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
761
1024
|
false,
|
|
762
1025
|
);
|
|
763
1026
|
if (handlers.length > 0) {
|
|
764
|
-
|
|
1027
|
+
const existing = s.staticHandlerChunkInfoMap.get(fileName);
|
|
1028
|
+
if (existing) {
|
|
1029
|
+
existing.exports.push(...handlers);
|
|
1030
|
+
} else {
|
|
1031
|
+
s.staticHandlerChunkInfoMap.set(fileName, {
|
|
1032
|
+
fileName,
|
|
1033
|
+
exports: handlers,
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
765
1036
|
}
|
|
766
1037
|
}
|
|
767
1038
|
}
|
package/src/vite/utils/banner.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import packageJson from "../../../package.json"
|
|
1
|
+
import packageJson from "../../../package.json";
|
|
2
2
|
|
|
3
3
|
export const rangoVersion: string = packageJson.version;
|
|
4
4
|
|
|
@@ -23,11 +23,11 @@ ${dim} ╱${reset} ${bold}╔═╗${reset}${dim} * ╱
|
|
|
23
23
|
${dim} ${reset}${bold}║ ║${reset} ${bold}╔═╗${reset}${dim} * ✧. ╱${reset}
|
|
24
24
|
${dim} ${reset}${bold}╔╗ ║ ║ ║ ║${reset}${dim} * ╱${reset}
|
|
25
25
|
${dim} ${reset}${bold}║║ ║ ║ ║ ║ ╦═╗╔═╗╔╗╔╔═╗╔═╗${reset}${dim} ✧ ✦${reset}
|
|
26
|
-
${dim}
|
|
26
|
+
${dim} ${reset}${bold}║║ ║ ╠═╝ ║ ╠╦╝╠═╣║║║║ ╦║ ║${reset}${dim} * ✧${reset}
|
|
27
27
|
${dim} ${reset}${bold}║╚═╝ ╔═══╝ ╩╚═╩ ╩╝╚╝╚═╝╚═╝${reset}${dim} ✦ . *${reset}
|
|
28
28
|
${dim} ${reset}${bold}╚══╗ ║${reset}${dim} * RSC Wrangler ✧ ✦${reset}
|
|
29
|
-
${dim} * ${reset}${bold}║
|
|
30
|
-
${bold}
|
|
29
|
+
${dim} * ${reset}${bold}║ ║${reset}${dim} * ✧. ╱${reset}
|
|
30
|
+
${dim} ${reset}${bold}═══╝ ╚════${reset}${dim} ✦ *${reset}
|
|
31
31
|
|
|
32
32
|
v${version} · ${preset} · ${mode}
|
|
33
33
|
`;
|