@rangojs/router 0.0.0-experimental.32 → 0.0.0-experimental.3232cd17
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 +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -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 +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -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/__internal.ts +67 -40
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +63 -24
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +338 -126
- package/src/ssr/index.tsx +23 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -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 +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +192 -99
- 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 +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- 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 +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -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
|
+
}
|
|
@@ -9,6 +9,7 @@ import { resolve } from "node:path";
|
|
|
9
9
|
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
10
10
|
import { evictHandlerCode } from "../utils/bundle-analysis.js";
|
|
11
11
|
import { copyStagedBuildAssets } from "../utils/prerender-utils.js";
|
|
12
|
+
import { jsonParseExpression } from "../utils/manifest-utils.js";
|
|
12
13
|
import type { DiscoveryState } from "./state.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -31,25 +32,25 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
31
32
|
state.rscEntryFileName ?? "index.js",
|
|
32
33
|
);
|
|
33
34
|
|
|
34
|
-
// 1. Evict handler code from
|
|
35
|
-
//
|
|
35
|
+
// 1. Evict handler code from whichever chunks contain handler exports.
|
|
36
|
+
// handlerChunkInfoMap/staticHandlerChunkInfoMap are populated by generateBundle
|
|
36
37
|
// after the production RSC build. In Vite 6 multi-environment builds, the
|
|
37
|
-
// RSC build runs twice (analysis + production).
|
|
38
|
-
//
|
|
38
|
+
// RSC build runs twice (analysis + production). The maps are cleared at the
|
|
39
|
+
// start of each generateBundle pass so only production data is used here.
|
|
39
40
|
const evictionTargets: Array<{
|
|
40
|
-
|
|
41
|
+
infos: Iterable<import("./state.js").ChunkInfo>;
|
|
41
42
|
fnName: string;
|
|
42
43
|
brand: string;
|
|
43
44
|
label: string;
|
|
44
45
|
}> = [
|
|
45
46
|
{
|
|
46
|
-
|
|
47
|
+
infos: state.handlerChunkInfoMap.values(),
|
|
47
48
|
fnName: "Prerender",
|
|
48
49
|
brand: "prerenderHandler",
|
|
49
50
|
label: "handler code from RSC bundle",
|
|
50
51
|
},
|
|
51
52
|
{
|
|
52
|
-
|
|
53
|
+
infos: state.staticHandlerChunkInfoMap.values(),
|
|
53
54
|
fnName: "Static",
|
|
54
55
|
brand: "staticHandler",
|
|
55
56
|
label: "static handler code",
|
|
@@ -57,35 +58,32 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
57
58
|
];
|
|
58
59
|
|
|
59
60
|
for (const target of evictionTargets) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
for (const info of target.infos) {
|
|
62
|
+
const chunkPath = resolve(state.projectRoot, "dist/rsc", info.fileName);
|
|
63
|
+
try {
|
|
64
|
+
const code = readFileSync(chunkPath, "utf-8");
|
|
65
|
+
const result = evictHandlerCode(
|
|
66
|
+
code,
|
|
67
|
+
info.exports,
|
|
68
|
+
target.fnName,
|
|
69
|
+
target.brand,
|
|
70
|
+
);
|
|
71
|
+
if (result) {
|
|
72
|
+
writeFileSync(chunkPath, result.code);
|
|
73
|
+
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
74
|
+
console.log(
|
|
75
|
+
`[rango] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
} catch (replaceErr: any) {
|
|
79
|
+
console.warn(
|
|
80
|
+
`[rango] Failed to evict ${target.label}: ${replaceErr.message}`,
|
|
79
81
|
);
|
|
80
82
|
}
|
|
81
|
-
} catch (replaceErr: any) {
|
|
82
|
-
console.warn(
|
|
83
|
-
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`,
|
|
84
|
-
);
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
|
-
state.
|
|
88
|
-
state.
|
|
85
|
+
state.handlerChunkInfoMap.clear();
|
|
86
|
+
state.staticHandlerChunkInfoMap.clear();
|
|
89
87
|
|
|
90
88
|
// 2. Write prerender data as separate importable asset modules
|
|
91
89
|
// and inject a lazy manifest loader into the RSC entry.
|
|
@@ -107,7 +105,7 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
const manifestCode = [
|
|
110
|
-
`const m
|
|
108
|
+
`const m=${jsonParseExpression(manifestMap)};`,
|
|
111
109
|
`export function loadPrerenderAsset(s){return import(s)}`,
|
|
112
110
|
`export default m;`,
|
|
113
111
|
"",
|
|
@@ -124,11 +122,11 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
124
122
|
|
|
125
123
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
126
124
|
console.log(
|
|
127
|
-
`[
|
|
125
|
+
`[rango] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
|
|
128
126
|
);
|
|
129
127
|
} catch (err: any) {
|
|
130
128
|
throw new Error(
|
|
131
|
-
`[
|
|
129
|
+
`[rango] Failed to write prerender assets: ${err.message}`,
|
|
132
130
|
);
|
|
133
131
|
}
|
|
134
132
|
}
|
|
@@ -138,7 +136,7 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
138
136
|
// and inject a __STATIC_MANIFEST import into the RSC entry.
|
|
139
137
|
if (hasStaticData && existsSync(rscEntryPath)) {
|
|
140
138
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
141
|
-
if (!rscCode.includes("
|
|
139
|
+
if (!rscCode.includes("__static-manifest.js")) {
|
|
142
140
|
try {
|
|
143
141
|
const manifestEntries: string[] = [];
|
|
144
142
|
let totalBytes = copyStagedBuildAssets(
|
|
@@ -172,11 +170,11 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
172
170
|
|
|
173
171
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
174
172
|
console.log(
|
|
175
|
-
`[
|
|
173
|
+
`[rango] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
|
|
176
174
|
);
|
|
177
175
|
} catch (err: any) {
|
|
178
176
|
throw new Error(
|
|
179
|
-
`[
|
|
177
|
+
`[rango] Failed to write static assets: ${err.message}`,
|
|
180
178
|
);
|
|
181
179
|
}
|
|
182
180
|
}
|
|
@@ -11,6 +11,16 @@ import {
|
|
|
11
11
|
formatNestedRouterConflictError,
|
|
12
12
|
findNestedRouterConflict,
|
|
13
13
|
} from "../../build/generate-route-types.js";
|
|
14
|
+
// Pure data transforms over generateManifestFull's output. Imported directly
|
|
15
|
+
// from source (not the public ./build barrel, and not the runner) because they
|
|
16
|
+
// are realm-independent: buildRouteTrie/buildPerRouterTrie operate on plain
|
|
17
|
+
// manifest data, and collectFallbackClientRefs keys on the global-registry
|
|
18
|
+
// Symbol.for("react.client.reference"), so it detects client references in a
|
|
19
|
+
// boundary tree regardless of which realm imported the walker. Only
|
|
20
|
+
// generateManifestFull must stay on the runner (it invokes user handlers via
|
|
21
|
+
// RangoContext from the runner realm) — see the runner.import below.
|
|
22
|
+
import { buildRouteTrie, buildPerRouterTrie } from "../../build/route-trie.js";
|
|
23
|
+
import { collectFallbackClientRefs } from "../../build/collect-fallback-refs.js";
|
|
14
24
|
import {
|
|
15
25
|
flattenLeafEntries,
|
|
16
26
|
buildRouteToStaticPrefix,
|
|
@@ -20,6 +30,15 @@ import {
|
|
|
20
30
|
expandPrerenderRoutes,
|
|
21
31
|
renderStaticHandlers,
|
|
22
32
|
} from "./prerender-collection.js";
|
|
33
|
+
import {
|
|
34
|
+
resolveHostRouterHandlers,
|
|
35
|
+
DiscoveryError,
|
|
36
|
+
type CaughtDiscoveryError,
|
|
37
|
+
} from "./discovery-errors.js";
|
|
38
|
+
import { createRangoDebugger, timed, NS } from "../debug.js";
|
|
39
|
+
import { computeProductionHash } from "../plugins/client-ref-hashing.js";
|
|
40
|
+
|
|
41
|
+
const debug = createRangoDebugger(NS.discovery);
|
|
23
42
|
|
|
24
43
|
/**
|
|
25
44
|
* Import the user's entry via RSC runner, generate manifests for each
|
|
@@ -38,41 +57,40 @@ export async function discoverRouters(
|
|
|
38
57
|
// Import the entry file via RSC environment.
|
|
39
58
|
// For node preset: this is the router file (createRouter() registers in RouterRegistry).
|
|
40
59
|
// For cloudflare preset: this is the worker entry (which imports the router).
|
|
41
|
-
await
|
|
60
|
+
await timed(debug, "inner: import entry", () =>
|
|
61
|
+
rscEnv.runner.import(state.resolvedEntryPath),
|
|
62
|
+
);
|
|
42
63
|
|
|
43
64
|
// Import the router package to access the registry
|
|
44
|
-
const serverMod = await
|
|
65
|
+
const serverMod = await timed(
|
|
66
|
+
debug,
|
|
67
|
+
"inner: import @rangojs/router/server",
|
|
68
|
+
() => rscEnv.runner.import("@rangojs/router/server"),
|
|
69
|
+
);
|
|
45
70
|
let registry: Map<string, any> = serverMod.RouterRegistry;
|
|
46
71
|
|
|
47
72
|
if (!registry || registry.size === 0) {
|
|
48
73
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
49
74
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
75
|
+
//
|
|
76
|
+
// Handler failures are collected rather than swallowed: when the registry
|
|
77
|
+
// is still empty afterwards, these errors (typically a sub-app whose router
|
|
78
|
+
// module failed to import) are the most likely cause and are surfaced in
|
|
79
|
+
// the terminal "No routers found" error below.
|
|
80
|
+
const discoveryErrors: CaughtDiscoveryError[] = [];
|
|
50
81
|
try {
|
|
51
82
|
const hostRegistry: Map<string, any> | undefined =
|
|
52
83
|
serverMod.HostRouterRegistry;
|
|
53
84
|
|
|
54
85
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
55
86
|
console.log(
|
|
56
|
-
`[
|
|
87
|
+
`[rango] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`,
|
|
57
88
|
);
|
|
58
89
|
|
|
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
|
-
}
|
|
90
|
+
const handlerErrors = await resolveHostRouterHandlers(hostRegistry);
|
|
91
|
+
discoveryErrors.push(...handlerErrors);
|
|
92
|
+
for (const { context, error } of handlerErrors) {
|
|
93
|
+
debug?.("caught error while resolving %s: %O", context, error);
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
// Re-read RouterRegistry - sub-app createRouter() calls should have populated it
|
|
@@ -87,22 +105,31 @@ export async function discoverRouters(
|
|
|
87
105
|
registry = freshRegistry;
|
|
88
106
|
}
|
|
89
107
|
}
|
|
90
|
-
} catch {
|
|
91
|
-
// Host-router discovery is best-effort;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Host-router discovery is best-effort; record the failure so it can be
|
|
110
|
+
// surfaced if no routers are found.
|
|
111
|
+
discoveryErrors.push({ context: "host-router discovery", error });
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
// If still no routers after host router resolution, fail
|
|
95
115
|
if (!registry || registry.size === 0) {
|
|
96
|
-
throw new
|
|
97
|
-
`[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`,
|
|
98
|
-
);
|
|
116
|
+
throw new DiscoveryError(state.resolvedEntryPath, discoveryErrors);
|
|
99
117
|
}
|
|
100
118
|
}
|
|
101
119
|
|
|
102
|
-
//
|
|
103
|
-
|
|
120
|
+
// generateManifestFull must run in the RSC runner realm: it invokes the
|
|
121
|
+
// user's urlpatterns.handler() via RangoContext, consuming router instances
|
|
122
|
+
// from the runner. The trie/fallback-ref builders are pure transforms over
|
|
123
|
+
// its output and are imported directly from source above.
|
|
124
|
+
const buildMod = await timed(
|
|
125
|
+
debug,
|
|
126
|
+
"inner: import @rangojs/router/build",
|
|
127
|
+
() => rscEnv.runner.import("@rangojs/router/build"),
|
|
128
|
+
);
|
|
104
129
|
const generateManifestFull = buildMod.generateManifestFull;
|
|
105
130
|
|
|
131
|
+
debug?.("inner: found %d router(s) in registry", registry.size);
|
|
132
|
+
|
|
106
133
|
const nestedRouterConflict = findNestedRouterConflict(
|
|
107
134
|
[...registry.values()]
|
|
108
135
|
.map((router) => router.__sourceFile)
|
|
@@ -130,14 +157,55 @@ export async function discoverRouters(
|
|
|
130
157
|
// Collect all manifests for trie building (avoid re-running generateManifest)
|
|
131
158
|
const allManifests: Array<{ id: string; manifest: any }> = [];
|
|
132
159
|
|
|
160
|
+
// Built-in clientChunks context (present only when the built-in strategy is
|
|
161
|
+
// active). Collect the production hashes of "use client" error/notFound
|
|
162
|
+
// fallback modules so the strategy can route them into app-fallback.
|
|
163
|
+
const clientChunkCtx = state.opts?.clientChunkCtx;
|
|
164
|
+
const collectClientFallbackRef = clientChunkCtx
|
|
165
|
+
? (refKey: string) =>
|
|
166
|
+
clientChunkCtx.fallbackRefs.add(
|
|
167
|
+
computeProductionHash(state.projectRoot, refKey),
|
|
168
|
+
)
|
|
169
|
+
: undefined;
|
|
170
|
+
// Router-level boundary defaults (`createRouter({ defaultErrorBoundary, ... })`)
|
|
171
|
+
// are NOT in EntryData, so generateManifestFull's walk misses them. Collect any
|
|
172
|
+
// "use client" default boundary directly off the router instance. The value is
|
|
173
|
+
// commonly a handler function wrapping the client boundary in server providers,
|
|
174
|
+
// so collectFallbackClientRefs invokes + walks the tree. The walker keys on the
|
|
175
|
+
// global-registry Symbol.for("react.client.reference"), so it detects client
|
|
176
|
+
// references in a runner-realm boundary tree even when imported here directly.
|
|
177
|
+
const collectFromBoundaryNode = (node: unknown): void => {
|
|
178
|
+
if (collectClientFallbackRef) {
|
|
179
|
+
collectFallbackClientRefs(node, collectClientFallbackRef);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const manifestGenStart = debug ? performance.now() : 0;
|
|
133
184
|
for (const [id, router] of registry) {
|
|
134
185
|
if (!router.urlpatterns || !generateManifestFull) {
|
|
135
186
|
continue;
|
|
136
187
|
}
|
|
137
188
|
|
|
138
|
-
const manifest = generateManifestFull(
|
|
189
|
+
const manifest = generateManifestFull(
|
|
190
|
+
router.urlpatterns,
|
|
191
|
+
routerMountIndex,
|
|
192
|
+
{
|
|
193
|
+
...(router.__basename ? { urlPrefix: router.__basename } : {}),
|
|
194
|
+
...(collectClientFallbackRef ? { collectClientFallbackRef } : {}),
|
|
195
|
+
},
|
|
196
|
+
);
|
|
139
197
|
routerMountIndex++;
|
|
140
198
|
allManifests.push({ id, manifest });
|
|
199
|
+
|
|
200
|
+
// Router-level "use client" boundary defaults -> app-fallback (the
|
|
201
|
+
// route-tree errorBoundary()/notFoundBoundary() helpers are already
|
|
202
|
+
// collected inside generateManifestFull via collectClientFallbackRef).
|
|
203
|
+
if (collectClientFallbackRef) {
|
|
204
|
+
collectFromBoundaryNode(router.__defaultErrorBoundary);
|
|
205
|
+
collectFromBoundaryNode(router.__defaultNotFoundBoundary);
|
|
206
|
+
collectFromBoundaryNode(router.__notFound);
|
|
207
|
+
}
|
|
208
|
+
|
|
141
209
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
142
210
|
const staticRoutes = Object.values(manifest.routeManifest).filter(
|
|
143
211
|
(p: any) => !p.includes(":") && !p.includes("*"),
|
|
@@ -189,24 +257,23 @@ export async function discoverRouters(
|
|
|
189
257
|
// Flatten prefix tree leaf nodes into precomputed entries.
|
|
190
258
|
// Leaf nodes (no children) can have their routes used directly by
|
|
191
259
|
// evaluateLazyEntry() without running the handler at runtime.
|
|
260
|
+
// Walk once into a per-router array, then fold it into the merged array;
|
|
261
|
+
// the merged and per-router entries are identical, so a second walk is
|
|
262
|
+
// redundant. Append order is preserved within and across routers.
|
|
263
|
+
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
192
264
|
flattenLeafEntries(
|
|
193
265
|
manifest.prefixTree,
|
|
194
266
|
manifest.routeManifest,
|
|
195
|
-
|
|
267
|
+
routerPrecomputed,
|
|
196
268
|
);
|
|
269
|
+
newMergedPrecomputedEntries.push(...routerPrecomputed);
|
|
197
270
|
|
|
198
271
|
// Store per-router manifest and precomputed entries for isolated virtual modules.
|
|
199
272
|
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
200
|
-
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
201
|
-
flattenLeafEntries(
|
|
202
|
-
manifest.prefixTree,
|
|
203
|
-
manifest.routeManifest,
|
|
204
|
-
routerPrecomputed,
|
|
205
|
-
);
|
|
206
273
|
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
207
274
|
|
|
208
275
|
console.log(
|
|
209
|
-
`[
|
|
276
|
+
`[rango] Router "${id}" -> ${routeCount} routes ` +
|
|
210
277
|
`(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
|
|
211
278
|
);
|
|
212
279
|
}
|
|
@@ -222,7 +289,7 @@ export async function discoverRouters(
|
|
|
222
289
|
);
|
|
223
290
|
if (autoIds.length > 1) {
|
|
224
291
|
console.warn(
|
|
225
|
-
`[
|
|
292
|
+
`[rango] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
|
|
226
293
|
`In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
|
|
227
294
|
`to ensure per-router manifest data is matched correctly at runtime. ` +
|
|
228
295
|
`Example: createRouter({ id: "site", ... })`,
|
|
@@ -230,11 +297,17 @@ export async function discoverRouters(
|
|
|
230
297
|
}
|
|
231
298
|
}
|
|
232
299
|
|
|
300
|
+
debug?.(
|
|
301
|
+
"inner: generated manifests for %d router(s) (%sms)",
|
|
302
|
+
allManifests.length,
|
|
303
|
+
(performance.now() - manifestGenStart).toFixed(1),
|
|
304
|
+
);
|
|
305
|
+
|
|
233
306
|
// Build route trie from merged manifest + ancestry
|
|
234
307
|
let newMergedRouteTrie: any = null;
|
|
308
|
+
const trieStart = debug ? performance.now() : 0;
|
|
235
309
|
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
236
|
-
|
|
237
|
-
if (buildRouteTrie && mergedRouteAncestry) {
|
|
310
|
+
if (mergedRouteAncestry) {
|
|
238
311
|
// Build routeToStaticPrefix from saved manifests
|
|
239
312
|
const routeToStaticPrefix: Record<string, string> = {};
|
|
240
313
|
for (const { manifest } of allManifests) {
|
|
@@ -267,64 +340,36 @@ export async function discoverRouters(
|
|
|
267
340
|
}
|
|
268
341
|
}
|
|
269
342
|
|
|
343
|
+
// buildRouteTrie reads these via ?.has / ?.[] — empty is observationally
|
|
344
|
+
// identical to undefined, so no empty->undefined coercion is needed.
|
|
270
345
|
newMergedRouteTrie = buildRouteTrie(
|
|
271
346
|
newMergedRouteManifest,
|
|
272
347
|
mergedRouteAncestry,
|
|
273
348
|
routeToStaticPrefix,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
|
|
279
|
-
Object.keys(mergedResponseTypeRoutes).length > 0
|
|
280
|
-
? mergedResponseTypeRoutes
|
|
281
|
-
: undefined,
|
|
349
|
+
mergedRouteTrailingSlash,
|
|
350
|
+
prerenderRouteNames,
|
|
351
|
+
passthroughRouteNames,
|
|
352
|
+
mergedResponseTypeRoutes,
|
|
282
353
|
);
|
|
283
354
|
|
|
284
|
-
// Build per-router tries for multi-router isolation.
|
|
355
|
+
// Build per-router tries for multi-router isolation. Uses the single
|
|
356
|
+
// shared buildPerRouterTrie so the production serialized trie is built by
|
|
357
|
+
// exactly the same code as the dev/HMR runtime rebuild (manifest-init.ts).
|
|
358
|
+
// Returns null for route-less manifests (route-trie.ts).
|
|
285
359
|
for (const { id, manifest } of allManifests) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
)
|
|
290
|
-
continue;
|
|
291
|
-
const perRouterStaticPrefix: Record<string, string> = {};
|
|
292
|
-
for (const name of Object.keys(manifest.routeManifest)) {
|
|
293
|
-
perRouterStaticPrefix[name] = "";
|
|
360
|
+
const perRouterTrie = buildPerRouterTrie(manifest);
|
|
361
|
+
if (perRouterTrie) {
|
|
362
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
294
363
|
}
|
|
295
|
-
buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
|
|
296
|
-
|
|
297
|
-
const perRouterPrerenderNames = manifest.prerenderRoutes
|
|
298
|
-
? new Set<string>(manifest.prerenderRoutes)
|
|
299
|
-
: undefined;
|
|
300
|
-
const perRouterPassthroughNames = manifest.passthroughRoutes
|
|
301
|
-
? new Set<string>(manifest.passthroughRoutes)
|
|
302
|
-
: undefined;
|
|
303
|
-
|
|
304
|
-
const perRouterTrie = buildRouteTrie(
|
|
305
|
-
manifest.routeManifest,
|
|
306
|
-
manifest._routeAncestry,
|
|
307
|
-
perRouterStaticPrefix,
|
|
308
|
-
manifest.routeTrailingSlash &&
|
|
309
|
-
Object.keys(manifest.routeTrailingSlash).length > 0
|
|
310
|
-
? manifest.routeTrailingSlash
|
|
311
|
-
: undefined,
|
|
312
|
-
perRouterPrerenderNames && perRouterPrerenderNames.size > 0
|
|
313
|
-
? perRouterPrerenderNames
|
|
314
|
-
: undefined,
|
|
315
|
-
perRouterPassthroughNames && perRouterPassthroughNames.size > 0
|
|
316
|
-
? perRouterPassthroughNames
|
|
317
|
-
: undefined,
|
|
318
|
-
manifest.responseTypeRoutes &&
|
|
319
|
-
Object.keys(manifest.responseTypeRoutes).length > 0
|
|
320
|
-
? manifest.responseTypeRoutes
|
|
321
|
-
: undefined,
|
|
322
|
-
);
|
|
323
|
-
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
324
364
|
}
|
|
325
365
|
}
|
|
326
366
|
}
|
|
327
367
|
|
|
368
|
+
debug?.(
|
|
369
|
+
"inner: trie build done (%sms)",
|
|
370
|
+
(performance.now() - trieStart).toFixed(1),
|
|
371
|
+
);
|
|
372
|
+
|
|
328
373
|
// Commit all local state to the shared discovery state atomically.
|
|
329
374
|
// This ensures a failed re-discovery (e.g. from a transient module
|
|
330
375
|
// evaluation error) preserves the last known-good state.
|