@rangojs/router 0.0.0-experimental.d7eeaa75 → 0.0.0-experimental.d98a8e9d
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 +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2154 -861
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +57 -11
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +45 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +46 -4
- package/skills/layout/SKILL.md +28 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +71 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +243 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +57 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +128 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +98 -0
- package/skills/testing/client-components.md +121 -0
- package/skills/testing/e2e-parity.md +124 -0
- package/skills/testing/flight.md +89 -0
- package/skills/testing/handles.md +127 -0
- package/skills/testing/loader.md +108 -0
- package/skills/testing/middleware.md +97 -0
- package/skills/testing/render-handler.md +102 -0
- package/skills/testing/response-routes.md +94 -0
- package/skills/testing/reverse-and-types.md +83 -0
- package/skills/testing/server-actions.md +89 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +84 -11
- package/src/browser/navigation-client.ts +104 -68
- package/src/browser/navigation-store.ts +32 -9
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +64 -26
- package/src/browser/prefetch/cache.ts +183 -44
- package/src/browser/prefetch/fetch.ts +228 -37
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +30 -2
- package/src/browser/react/NavigationProvider.tsx +72 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +22 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +32 -1
- package/src/browser/rsc-router.tsx +69 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +23 -30
- package/src/browser/types.ts +21 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +8 -1
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +95 -25
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-scope.ts +28 -42
- package/src/cache/cf/cf-cache-store.ts +54 -13
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +96 -205
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -4
- package/src/handle.ts +32 -14
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -21
- package/src/index.rsc.ts +10 -6
- package/src/index.ts +54 -17
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +25 -7
- package/src/loader.ts +16 -9
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender.ts +27 -6
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -36
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +384 -257
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +100 -28
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +54 -6
- package/src/router/handler-context.ts +21 -38
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +41 -22
- package/src/router/loader-resolution.ts +82 -36
- package/src/router/manifest.ts +41 -19
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +63 -20
- package/src/router/match-middleware/cache-lookup.ts +44 -91
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +116 -19
- package/src/router/prerender-match.ts +1 -1
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +4 -28
- package/src/router/revalidation.ts +58 -2
- package/src/router/router-interfaces.ts +45 -28
- package/src/router/router-options.ts +40 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/trie-matching.ts +40 -16
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +52 -30
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +28 -69
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +57 -61
- package/src/rsc/rsc-rendering.ts +35 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +17 -37
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +175 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +67 -51
- package/src/ssr/index.tsx +5 -1
- package/src/static-handler.ts +25 -3
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +581 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +326 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +110 -0
- package/src/testing/flight-normalize.ts +38 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +51 -0
- package/src/testing/flight.ts +234 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +106 -0
- package/src/testing/internal/context.ts +304 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +42 -0
- package/src/testing/render-handler.ts +323 -0
- package/src/testing/render-route.tsx +590 -0
- package/src/testing/run-loader.ts +363 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +285 -0
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +1 -5
- package/src/urls/path-helper-types.ts +41 -7
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +106 -75
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +67 -26
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- 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 +54 -30
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +750 -100
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +8 -59
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +21 -6
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug logging for the Rango Vite plugin.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper over the `debug` package (the same one Vite uses for its
|
|
5
|
+
* own `vite:*` namespaces). Enable with either:
|
|
6
|
+
*
|
|
7
|
+
* DEBUG='rango:*' vite dev
|
|
8
|
+
* vite --debug rango:* # vite prepends `vite:`, we bridge it
|
|
9
|
+
*
|
|
10
|
+
* Returns `undefined` when no matching namespace is enabled, so call sites
|
|
11
|
+
* can guard expensive diagnostics with a simple truthiness check:
|
|
12
|
+
*
|
|
13
|
+
* const debug = createRangoDebugger(NS.routes);
|
|
14
|
+
* if (debug) debug("built manifest (%d routes) in %dms", n, ms);
|
|
15
|
+
*
|
|
16
|
+
* Back-compat: INTERNAL_RANGO_DEBUG=1 still enables all rango namespaces.
|
|
17
|
+
*
|
|
18
|
+
* Vite CLI note: `vite --debug <feat>` rewrites to `DEBUG=vite:<feat>` — it
|
|
19
|
+
* always prefixes with `vite:` and cannot enable bare `rango:*` namespaces.
|
|
20
|
+
* We work around this by registering a shadow `vite:rango:*` instance for
|
|
21
|
+
* each debugger, so either invocation works.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import debugFactory from "debug";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Canonical debug namespaces. Import as `NS.xxx` instead of string literals
|
|
28
|
+
* so typos become type errors and the full set lives in one place.
|
|
29
|
+
*/
|
|
30
|
+
export const NS = {
|
|
31
|
+
config: "rango:config",
|
|
32
|
+
discovery: "rango:discovery",
|
|
33
|
+
routes: "rango:routes",
|
|
34
|
+
prerender: "rango:prerender",
|
|
35
|
+
build: "rango:build",
|
|
36
|
+
dev: "rango:dev",
|
|
37
|
+
transform: "rango:transform",
|
|
38
|
+
chunks: "rango:chunks",
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
// Back-compat: the legacy INTERNAL_RANGO_DEBUG env var enabled per-site
|
|
42
|
+
// console.logs in this plugin. Map it to `rango:*` so those call sites can
|
|
43
|
+
// be migrated to the `debug` pipeline without breaking existing setups.
|
|
44
|
+
// Uses debug.enable() rather than mutating process.env because the `debug`
|
|
45
|
+
// package already snapshotted DEBUG when it was imported above.
|
|
46
|
+
if (process.env.INTERNAL_RANGO_DEBUG) {
|
|
47
|
+
const existing = debugFactory.disable();
|
|
48
|
+
debugFactory.enable(existing ? `${existing},rango:*` : "rango:*");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type Debugger = (formatter: string, ...args: unknown[]) => void;
|
|
52
|
+
|
|
53
|
+
export function createRangoDebugger(namespace: string): Debugger | undefined {
|
|
54
|
+
const primary = debugFactory(namespace);
|
|
55
|
+
// Shadow namespace so `vite --debug rango:*` (which expands to
|
|
56
|
+
// DEBUG=vite:rango:*) and `vite --debug` (DEBUG=vite:*) both pick us up.
|
|
57
|
+
const shadow = debugFactory(`vite:${namespace}`);
|
|
58
|
+
if (primary.enabled) return primary as Debugger;
|
|
59
|
+
if (shadow.enabled) return shadow as Debugger;
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Measure an async block and log its duration via `debug`. No-ops (still
|
|
65
|
+
* runs `fn`) when the namespace is disabled, so production cost is a single
|
|
66
|
+
* `.enabled` check per call.
|
|
67
|
+
*
|
|
68
|
+
* await timed(debug, "discover routers", () => discoverRouters(state));
|
|
69
|
+
*/
|
|
70
|
+
export async function timed<T>(
|
|
71
|
+
debug: Debugger | undefined,
|
|
72
|
+
label: string,
|
|
73
|
+
fn: () => T | Promise<T>,
|
|
74
|
+
): Promise<T> {
|
|
75
|
+
if (!debug) return await fn();
|
|
76
|
+
const start = performance.now();
|
|
77
|
+
try {
|
|
78
|
+
return await fn();
|
|
79
|
+
} finally {
|
|
80
|
+
debug("%s (%sms)", label, (performance.now() - start).toFixed(1));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Synchronous variant of `timed`. Use for sync call sites — wrapping them
|
|
86
|
+
* with the async `timed` would create a floating promise that discards any
|
|
87
|
+
* throw, bypassing the surrounding try/catch.
|
|
88
|
+
*/
|
|
89
|
+
export function timedSync<T>(
|
|
90
|
+
debug: Debugger | undefined,
|
|
91
|
+
label: string,
|
|
92
|
+
fn: () => T,
|
|
93
|
+
): T {
|
|
94
|
+
if (!debug) return fn();
|
|
95
|
+
const start = performance.now();
|
|
96
|
+
try {
|
|
97
|
+
return fn();
|
|
98
|
+
} finally {
|
|
99
|
+
debug("%s (%sms)", label, (performance.now() - start).toFixed(1));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Aggregate counter for high-frequency call sites (typically Vite
|
|
105
|
+
* `transform` hooks that run on many files). Per-call logging would
|
|
106
|
+
* drown real signal; this collects totals and reports once on flush.
|
|
107
|
+
*
|
|
108
|
+
* const counter = createCounter(debug, "use-cache-transform");
|
|
109
|
+
* // inside transform():
|
|
110
|
+
* return counter?.time(id, () => doWork()) ?? doWork();
|
|
111
|
+
* // or manually:
|
|
112
|
+
* counter?.record(id, ms);
|
|
113
|
+
* // flush on buildEnd (counter resets, so multi-env builds each get
|
|
114
|
+
* // their own summary line):
|
|
115
|
+
* counter?.flush();
|
|
116
|
+
*
|
|
117
|
+
* Returns `undefined` when the namespace is disabled so call sites pay
|
|
118
|
+
* nothing when off.
|
|
119
|
+
*/
|
|
120
|
+
export interface Counter {
|
|
121
|
+
record(file: string, ms: number): void;
|
|
122
|
+
/**
|
|
123
|
+
* Convenience: time a sync or async block and record it. Propagates
|
|
124
|
+
* throws; records regardless of outcome. Returns the function's result.
|
|
125
|
+
*/
|
|
126
|
+
time<T>(file: string, fn: () => T): T;
|
|
127
|
+
time<T>(file: string, fn: () => Promise<T>): Promise<T>;
|
|
128
|
+
flush(): void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createCounter(
|
|
132
|
+
debug: Debugger | undefined,
|
|
133
|
+
label: string,
|
|
134
|
+
): Counter | undefined {
|
|
135
|
+
if (!debug) return undefined;
|
|
136
|
+
let n = 0;
|
|
137
|
+
let totalMs = 0;
|
|
138
|
+
let slowestMs = 0;
|
|
139
|
+
let slowestFile = "";
|
|
140
|
+
const record = (file: string, ms: number): void => {
|
|
141
|
+
n++;
|
|
142
|
+
totalMs += ms;
|
|
143
|
+
if (ms > slowestMs) {
|
|
144
|
+
slowestMs = ms;
|
|
145
|
+
slowestFile = file;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
record,
|
|
150
|
+
time<T>(file: string, fn: () => T | Promise<T>): T | Promise<T> {
|
|
151
|
+
const start = performance.now();
|
|
152
|
+
let out: T | Promise<T>;
|
|
153
|
+
try {
|
|
154
|
+
out = fn();
|
|
155
|
+
} catch (err) {
|
|
156
|
+
record(file, performance.now() - start);
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
if (out && typeof (out as any).then === "function") {
|
|
160
|
+
return (out as Promise<T>).finally(() =>
|
|
161
|
+
record(file, performance.now() - start),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
record(file, performance.now() - start);
|
|
165
|
+
return out;
|
|
166
|
+
},
|
|
167
|
+
flush(): void {
|
|
168
|
+
if (n === 0) return;
|
|
169
|
+
debug(
|
|
170
|
+
"%s: %d files, %sms total, slowest %sms %s",
|
|
171
|
+
label,
|
|
172
|
+
n,
|
|
173
|
+
totalMs.toFixed(1),
|
|
174
|
+
slowestMs.toFixed(1),
|
|
175
|
+
slowestFile,
|
|
176
|
+
);
|
|
177
|
+
// Reset so buildEnd firing once per environment (Vite 6+ multi-env)
|
|
178
|
+
// gives one log line per env rather than silently dropping later data.
|
|
179
|
+
n = 0;
|
|
180
|
+
totalMs = 0;
|
|
181
|
+
slowestMs = 0;
|
|
182
|
+
slowestFile = "";
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -71,12 +71,12 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
71
71
|
writeFileSync(chunkPath, result.code);
|
|
72
72
|
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
73
73
|
console.log(
|
|
74
|
-
`[
|
|
74
|
+
`[rango] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`,
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
} catch (replaceErr: any) {
|
|
78
78
|
console.warn(
|
|
79
|
-
`[
|
|
79
|
+
`[rango] Failed to evict ${target.label}: ${replaceErr.message}`,
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -121,11 +121,11 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
121
121
|
|
|
122
122
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
123
123
|
console.log(
|
|
124
|
-
`[
|
|
124
|
+
`[rango] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
|
|
125
125
|
);
|
|
126
126
|
} catch (err: any) {
|
|
127
127
|
throw new Error(
|
|
128
|
-
`[
|
|
128
|
+
`[rango] Failed to write prerender assets: ${err.message}`,
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -169,11 +169,11 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
169
169
|
|
|
170
170
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
171
171
|
console.log(
|
|
172
|
-
`[
|
|
172
|
+
`[rango] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
|
|
173
173
|
);
|
|
174
174
|
} catch (err: any) {
|
|
175
175
|
throw new Error(
|
|
176
|
-
`[
|
|
176
|
+
`[rango] Failed to write static assets: ${err.message}`,
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
@@ -20,6 +20,15 @@ import {
|
|
|
20
20
|
expandPrerenderRoutes,
|
|
21
21
|
renderStaticHandlers,
|
|
22
22
|
} from "./prerender-collection.js";
|
|
23
|
+
import {
|
|
24
|
+
resolveHostRouterHandlers,
|
|
25
|
+
DiscoveryError,
|
|
26
|
+
type CaughtDiscoveryError,
|
|
27
|
+
} from "./discovery-errors.js";
|
|
28
|
+
import { createRangoDebugger, timed, NS } from "../debug.js";
|
|
29
|
+
import { computeProductionHash } from "../plugins/client-ref-hashing.js";
|
|
30
|
+
|
|
31
|
+
const debug = createRangoDebugger(NS.discovery);
|
|
23
32
|
|
|
24
33
|
/**
|
|
25
34
|
* Import the user's entry via RSC runner, generate manifests for each
|
|
@@ -38,41 +47,40 @@ export async function discoverRouters(
|
|
|
38
47
|
// Import the entry file via RSC environment.
|
|
39
48
|
// For node preset: this is the router file (createRouter() registers in RouterRegistry).
|
|
40
49
|
// For cloudflare preset: this is the worker entry (which imports the router).
|
|
41
|
-
await
|
|
50
|
+
await timed(debug, "inner: import entry", () =>
|
|
51
|
+
rscEnv.runner.import(state.resolvedEntryPath),
|
|
52
|
+
);
|
|
42
53
|
|
|
43
54
|
// Import the router package to access the registry
|
|
44
|
-
const serverMod = await
|
|
55
|
+
const serverMod = await timed(
|
|
56
|
+
debug,
|
|
57
|
+
"inner: import @rangojs/router/server",
|
|
58
|
+
() => rscEnv.runner.import("@rangojs/router/server"),
|
|
59
|
+
);
|
|
45
60
|
let registry: Map<string, any> = serverMod.RouterRegistry;
|
|
46
61
|
|
|
47
62
|
if (!registry || registry.size === 0) {
|
|
48
63
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
49
64
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
65
|
+
//
|
|
66
|
+
// Handler failures are collected rather than swallowed: when the registry
|
|
67
|
+
// is still empty afterwards, these errors (typically a sub-app whose router
|
|
68
|
+
// module failed to import) are the most likely cause and are surfaced in
|
|
69
|
+
// the terminal "No routers found" error below.
|
|
70
|
+
const discoveryErrors: CaughtDiscoveryError[] = [];
|
|
50
71
|
try {
|
|
51
72
|
const hostRegistry: Map<string, any> | undefined =
|
|
52
73
|
serverMod.HostRouterRegistry;
|
|
53
74
|
|
|
54
75
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
55
76
|
console.log(
|
|
56
|
-
`[
|
|
77
|
+
`[rango] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`,
|
|
57
78
|
);
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await route.handler();
|
|
64
|
-
} catch {
|
|
65
|
-
// Lazy handler may fail in temp server context, that's OK
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (entry.fallback && typeof entry.fallback.handler === "function") {
|
|
70
|
-
try {
|
|
71
|
-
await entry.fallback.handler();
|
|
72
|
-
} catch {
|
|
73
|
-
// Fallback handler may fail in temp server context
|
|
74
|
-
}
|
|
75
|
-
}
|
|
80
|
+
const handlerErrors = await resolveHostRouterHandlers(hostRegistry);
|
|
81
|
+
discoveryErrors.push(...handlerErrors);
|
|
82
|
+
for (const { context, error } of handlerErrors) {
|
|
83
|
+
debug?.("caught error while resolving %s: %O", context, error);
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
// Re-read RouterRegistry - sub-app createRouter() calls should have populated it
|
|
@@ -87,22 +95,28 @@ export async function discoverRouters(
|
|
|
87
95
|
registry = freshRegistry;
|
|
88
96
|
}
|
|
89
97
|
}
|
|
90
|
-
} catch {
|
|
91
|
-
// Host-router discovery is best-effort;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// Host-router discovery is best-effort; record the failure so it can be
|
|
100
|
+
// surfaced if no routers are found.
|
|
101
|
+
discoveryErrors.push({ context: "host-router discovery", error });
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
// If still no routers after host router resolution, fail
|
|
95
105
|
if (!registry || registry.size === 0) {
|
|
96
|
-
throw new
|
|
97
|
-
`[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`,
|
|
98
|
-
);
|
|
106
|
+
throw new DiscoveryError(state.resolvedEntryPath, discoveryErrors);
|
|
99
107
|
}
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
// Import build utilities for manifest generation
|
|
103
|
-
const buildMod = await
|
|
111
|
+
const buildMod = await timed(
|
|
112
|
+
debug,
|
|
113
|
+
"inner: import @rangojs/router/build",
|
|
114
|
+
() => rscEnv.runner.import("@rangojs/router/build"),
|
|
115
|
+
);
|
|
104
116
|
const generateManifestFull = buildMod.generateManifestFull;
|
|
105
117
|
|
|
118
|
+
debug?.("inner: found %d router(s) in registry", registry.size);
|
|
119
|
+
|
|
106
120
|
const nestedRouterConflict = findNestedRouterConflict(
|
|
107
121
|
[...registry.values()]
|
|
108
122
|
.map((router) => router.__sourceFile)
|
|
@@ -130,6 +144,29 @@ export async function discoverRouters(
|
|
|
130
144
|
// Collect all manifests for trie building (avoid re-running generateManifest)
|
|
131
145
|
const allManifests: Array<{ id: string; manifest: any }> = [];
|
|
132
146
|
|
|
147
|
+
// Built-in clientChunks context (present only when the built-in strategy is
|
|
148
|
+
// active). Collect the production hashes of "use client" error/notFound
|
|
149
|
+
// fallback modules so the strategy can route them into app-fallback.
|
|
150
|
+
const clientChunkCtx = state.opts?.clientChunkCtx;
|
|
151
|
+
const collectClientFallbackRef = clientChunkCtx
|
|
152
|
+
? (refKey: string) =>
|
|
153
|
+
clientChunkCtx.fallbackRefs.add(
|
|
154
|
+
computeProductionHash(state.projectRoot, refKey),
|
|
155
|
+
)
|
|
156
|
+
: undefined;
|
|
157
|
+
// Router-level boundary defaults (`createRouter({ defaultErrorBoundary, ... })`)
|
|
158
|
+
// are NOT in EntryData, so generateManifestFull's walk misses them. Collect any
|
|
159
|
+
// "use client" default boundary directly off the router instance. The value is
|
|
160
|
+
// commonly a handler function wrapping the client boundary in server providers,
|
|
161
|
+
// so collectFallbackClientRefs invokes + walks the tree. Routed through buildMod
|
|
162
|
+
// so it runs in the same RSC runner realm the boundary value came from.
|
|
163
|
+
const collectFromBoundaryNode = (node: unknown): void => {
|
|
164
|
+
if (collectClientFallbackRef && buildMod.collectFallbackClientRefs) {
|
|
165
|
+
buildMod.collectFallbackClientRefs(node, collectClientFallbackRef);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const manifestGenStart = debug ? performance.now() : 0;
|
|
133
170
|
for (const [id, router] of registry) {
|
|
134
171
|
if (!router.urlpatterns || !generateManifestFull) {
|
|
135
172
|
continue;
|
|
@@ -138,10 +175,23 @@ export async function discoverRouters(
|
|
|
138
175
|
const manifest = generateManifestFull(
|
|
139
176
|
router.urlpatterns,
|
|
140
177
|
routerMountIndex,
|
|
141
|
-
|
|
178
|
+
{
|
|
179
|
+
...(router.__basename ? { urlPrefix: router.__basename } : {}),
|
|
180
|
+
...(collectClientFallbackRef ? { collectClientFallbackRef } : {}),
|
|
181
|
+
},
|
|
142
182
|
);
|
|
143
183
|
routerMountIndex++;
|
|
144
184
|
allManifests.push({ id, manifest });
|
|
185
|
+
|
|
186
|
+
// Router-level "use client" boundary defaults -> app-fallback (the
|
|
187
|
+
// route-tree errorBoundary()/notFoundBoundary() helpers are already
|
|
188
|
+
// collected inside generateManifestFull via collectClientFallbackRef).
|
|
189
|
+
if (collectClientFallbackRef) {
|
|
190
|
+
collectFromBoundaryNode(router.__defaultErrorBoundary);
|
|
191
|
+
collectFromBoundaryNode(router.__defaultNotFoundBoundary);
|
|
192
|
+
collectFromBoundaryNode(router.__notFound);
|
|
193
|
+
}
|
|
194
|
+
|
|
145
195
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
146
196
|
const staticRoutes = Object.values(manifest.routeManifest).filter(
|
|
147
197
|
(p: any) => !p.includes(":") && !p.includes("*"),
|
|
@@ -210,7 +260,7 @@ export async function discoverRouters(
|
|
|
210
260
|
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
211
261
|
|
|
212
262
|
console.log(
|
|
213
|
-
`[
|
|
263
|
+
`[rango] Router "${id}" -> ${routeCount} routes ` +
|
|
214
264
|
`(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
|
|
215
265
|
);
|
|
216
266
|
}
|
|
@@ -226,7 +276,7 @@ export async function discoverRouters(
|
|
|
226
276
|
);
|
|
227
277
|
if (autoIds.length > 1) {
|
|
228
278
|
console.warn(
|
|
229
|
-
`[
|
|
279
|
+
`[rango] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
|
|
230
280
|
`In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
|
|
231
281
|
`to ensure per-router manifest data is matched correctly at runtime. ` +
|
|
232
282
|
`Example: createRouter({ id: "site", ... })`,
|
|
@@ -234,8 +284,15 @@ export async function discoverRouters(
|
|
|
234
284
|
}
|
|
235
285
|
}
|
|
236
286
|
|
|
287
|
+
debug?.(
|
|
288
|
+
"inner: generated manifests for %d router(s) (%sms)",
|
|
289
|
+
allManifests.length,
|
|
290
|
+
(performance.now() - manifestGenStart).toFixed(1),
|
|
291
|
+
);
|
|
292
|
+
|
|
237
293
|
// Build route trie from merged manifest + ancestry
|
|
238
294
|
let newMergedRouteTrie: any = null;
|
|
295
|
+
const trieStart = debug ? performance.now() : 0;
|
|
239
296
|
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
240
297
|
const buildRouteTrie = buildMod.buildRouteTrie;
|
|
241
298
|
if (buildRouteTrie && mergedRouteAncestry) {
|
|
@@ -271,64 +328,38 @@ export async function discoverRouters(
|
|
|
271
328
|
}
|
|
272
329
|
}
|
|
273
330
|
|
|
331
|
+
// buildRouteTrie reads these via ?.has / ?.[] — empty is observationally
|
|
332
|
+
// identical to undefined, so no empty->undefined coercion is needed.
|
|
274
333
|
newMergedRouteTrie = buildRouteTrie(
|
|
275
334
|
newMergedRouteManifest,
|
|
276
335
|
mergedRouteAncestry,
|
|
277
336
|
routeToStaticPrefix,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
|
|
283
|
-
Object.keys(mergedResponseTypeRoutes).length > 0
|
|
284
|
-
? mergedResponseTypeRoutes
|
|
285
|
-
: undefined,
|
|
337
|
+
mergedRouteTrailingSlash,
|
|
338
|
+
prerenderRouteNames,
|
|
339
|
+
passthroughRouteNames,
|
|
340
|
+
mergedResponseTypeRoutes,
|
|
286
341
|
);
|
|
287
342
|
|
|
288
|
-
// Build per-router tries for multi-router isolation.
|
|
343
|
+
// Build per-router tries for multi-router isolation. Uses the single
|
|
344
|
+
// shared buildPerRouterTrie so the production serialized trie is built by
|
|
345
|
+
// exactly the same code as the dev/HMR runtime rebuild (manifest-init.ts).
|
|
346
|
+
const buildPerRouterTrie = buildMod.buildPerRouterTrie;
|
|
289
347
|
for (const { id, manifest } of allManifests) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
const perRouterStaticPrefix: Record<string, string> = {};
|
|
296
|
-
for (const name of Object.keys(manifest.routeManifest)) {
|
|
297
|
-
perRouterStaticPrefix[name] = "";
|
|
348
|
+
const perRouterTrie = buildPerRouterTrie
|
|
349
|
+
? buildPerRouterTrie(manifest)
|
|
350
|
+
: null;
|
|
351
|
+
if (perRouterTrie) {
|
|
352
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
298
353
|
}
|
|
299
|
-
buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
|
|
300
|
-
|
|
301
|
-
const perRouterPrerenderNames = manifest.prerenderRoutes
|
|
302
|
-
? new Set<string>(manifest.prerenderRoutes)
|
|
303
|
-
: undefined;
|
|
304
|
-
const perRouterPassthroughNames = manifest.passthroughRoutes
|
|
305
|
-
? new Set<string>(manifest.passthroughRoutes)
|
|
306
|
-
: undefined;
|
|
307
|
-
|
|
308
|
-
const perRouterTrie = buildRouteTrie(
|
|
309
|
-
manifest.routeManifest,
|
|
310
|
-
manifest._routeAncestry,
|
|
311
|
-
perRouterStaticPrefix,
|
|
312
|
-
manifest.routeTrailingSlash &&
|
|
313
|
-
Object.keys(manifest.routeTrailingSlash).length > 0
|
|
314
|
-
? manifest.routeTrailingSlash
|
|
315
|
-
: undefined,
|
|
316
|
-
perRouterPrerenderNames && perRouterPrerenderNames.size > 0
|
|
317
|
-
? perRouterPrerenderNames
|
|
318
|
-
: undefined,
|
|
319
|
-
perRouterPassthroughNames && perRouterPassthroughNames.size > 0
|
|
320
|
-
? perRouterPassthroughNames
|
|
321
|
-
: undefined,
|
|
322
|
-
manifest.responseTypeRoutes &&
|
|
323
|
-
Object.keys(manifest.responseTypeRoutes).length > 0
|
|
324
|
-
? manifest.responseTypeRoutes
|
|
325
|
-
: undefined,
|
|
326
|
-
);
|
|
327
|
-
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
328
354
|
}
|
|
329
355
|
}
|
|
330
356
|
}
|
|
331
357
|
|
|
358
|
+
debug?.(
|
|
359
|
+
"inner: trie build done (%sms)",
|
|
360
|
+
(performance.now() - trieStart).toFixed(1),
|
|
361
|
+
);
|
|
362
|
+
|
|
332
363
|
// Commit all local state to the shared discovery state atomically.
|
|
333
364
|
// This ensures a failed re-discovery (e.g. from a transient module
|
|
334
365
|
// evaluation error) preserves the last known-good state.
|