@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327
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 +8 -4
- package/README.md +301 -797
- package/dist/bin/rango.js +603 -145
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3750 -1160
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +96 -24
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +85 -6
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +228 -33
- package/skills/caching/SKILL.md +336 -19
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +110 -4
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +87 -56
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +12 -10
- package/skills/hooks/SKILL.md +73 -691
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +129 -27
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +75 -19
- package/skills/layout/SKILL.md +40 -19
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +248 -10
- package/skills/middleware/SKILL.md +25 -13
- package/skills/migrate-nextjs/SKILL.md +205 -20
- package/skills/migrate-react-router/SKILL.md +59 -670
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +29 -2
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +40 -10
- package/skills/ppr/SKILL.md +616 -0
- package/skills/prerender/SKILL.md +72 -60
- package/skills/rango/SKILL.md +318 -26
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +117 -9
- package/skills/router-setup/SKILL.md +44 -9
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -626
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +74 -15
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +252 -158
- 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/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +144 -79
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +161 -73
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +183 -144
- package/src/browser/prefetch/cache.ts +242 -77
- package/src/browser/prefetch/fetch.ts +325 -69
- package/src/browser/prefetch/queue.ts +61 -12
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +58 -20
- package/src/browser/react/NavigationProvider.tsx +202 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -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 +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- 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 +25 -3
- 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 +99 -8
- package/src/browser/rsc-router.tsx +145 -28
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +236 -65
- package/src/browser/types.ts +102 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +203 -154
- package/src/build/generate-route-types.ts +3 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -21
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +456 -62
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +128 -51
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +13 -21
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +421 -58
- package/src/cache/cache-scope.ts +187 -96
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2202 -372
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +180 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1127 -0
- package/src/client.rsc.tsx +41 -21
- package/src/client.tsx +33 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +67 -37
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +9 -2
- package/src/host/pattern-matcher.ts +23 -52
- 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 +97 -12
- package/src/index.ts +98 -14
- package/src/internal-debug.ts +11 -10
- 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/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +78 -10
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +65 -39
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +87 -59
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +27 -42
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +82 -59
- package/src/router/loader-resolution.ts +167 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +80 -55
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +69 -79
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +298 -172
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +181 -150
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +112 -67
- package/src/router/preview-match.ts +6 -2
- package/src/router/request-classification.ts +50 -69
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +115 -36
- package/src/router/router-options.ts +166 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +131 -86
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +139 -39
- package/src/router/segment-resolution/loader-mask.ts +67 -0
- package/src/router/segment-resolution/loader-snapshot.ts +251 -0
- package/src/router/segment-resolution/revalidation.ts +272 -320
- 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 +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +105 -20
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -59
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +157 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +291 -217
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +124 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +421 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +282 -116
- package/src/rsc/shell-capture.ts +1158 -0
- package/src/rsc/shell-serve.ts +150 -0
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +53 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +350 -149
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +208 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +395 -176
- package/src/ssr/index.tsx +458 -178
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -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 +257 -0
- package/src/testing/generated-routes.ts +199 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -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 +357 -0
- package/src/testing/render-route.tsx +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -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 +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +19 -64
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +63 -17
- package/src/urls/path-helper.ts +22 -106
- package/src/urls/pattern-types.ts +72 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +127 -86
- package/src/vite/discovery/discovery-errors.ts +255 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +96 -68
- 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 +45 -1
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +4 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +210 -10
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- 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 +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +236 -138
- package/src/vite/router-discovery.ts +927 -136
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -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 +71 -43
- package/src/vite/utils/shared-utils.ts +142 -43
- 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/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -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,18 +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 = await generateManifestFull(
|
|
139
190
|
router.urlpatterns,
|
|
140
191
|
routerMountIndex,
|
|
141
|
-
|
|
192
|
+
{
|
|
193
|
+
...(router.__basename ? { urlPrefix: router.__basename } : {}),
|
|
194
|
+
...(collectClientFallbackRef ? { collectClientFallbackRef } : {}),
|
|
195
|
+
},
|
|
142
196
|
);
|
|
143
197
|
routerMountIndex++;
|
|
144
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
|
+
|
|
145
209
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
146
210
|
const staticRoutes = Object.values(manifest.routeManifest).filter(
|
|
147
211
|
(p: any) => !p.includes(":") && !p.includes("*"),
|
|
@@ -193,24 +257,23 @@ export async function discoverRouters(
|
|
|
193
257
|
// Flatten prefix tree leaf nodes into precomputed entries.
|
|
194
258
|
// Leaf nodes (no children) can have their routes used directly by
|
|
195
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[] = [];
|
|
196
264
|
flattenLeafEntries(
|
|
197
265
|
manifest.prefixTree,
|
|
198
266
|
manifest.routeManifest,
|
|
199
|
-
|
|
267
|
+
routerPrecomputed,
|
|
200
268
|
);
|
|
269
|
+
newMergedPrecomputedEntries.push(...routerPrecomputed);
|
|
201
270
|
|
|
202
271
|
// Store per-router manifest and precomputed entries for isolated virtual modules.
|
|
203
272
|
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
204
|
-
const routerPrecomputed: PrecomputedEntry[] = [];
|
|
205
|
-
flattenLeafEntries(
|
|
206
|
-
manifest.prefixTree,
|
|
207
|
-
manifest.routeManifest,
|
|
208
|
-
routerPrecomputed,
|
|
209
|
-
);
|
|
210
273
|
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
211
274
|
|
|
212
275
|
console.log(
|
|
213
|
-
`[
|
|
276
|
+
`[rango] Router "${id}" -> ${routeCount} routes ` +
|
|
214
277
|
`(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
|
|
215
278
|
);
|
|
216
279
|
}
|
|
@@ -226,7 +289,7 @@ export async function discoverRouters(
|
|
|
226
289
|
);
|
|
227
290
|
if (autoIds.length > 1) {
|
|
228
291
|
console.warn(
|
|
229
|
-
`[
|
|
292
|
+
`[rango] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
|
|
230
293
|
`In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
|
|
231
294
|
`to ensure per-router manifest data is matched correctly at runtime. ` +
|
|
232
295
|
`Example: createRouter({ id: "site", ... })`,
|
|
@@ -234,11 +297,17 @@ export async function discoverRouters(
|
|
|
234
297
|
}
|
|
235
298
|
}
|
|
236
299
|
|
|
300
|
+
debug?.(
|
|
301
|
+
"inner: generated manifests for %d router(s) (%sms)",
|
|
302
|
+
allManifests.length,
|
|
303
|
+
(performance.now() - manifestGenStart).toFixed(1),
|
|
304
|
+
);
|
|
305
|
+
|
|
237
306
|
// Build route trie from merged manifest + ancestry
|
|
238
307
|
let newMergedRouteTrie: any = null;
|
|
308
|
+
const trieStart = debug ? performance.now() : 0;
|
|
239
309
|
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
240
|
-
|
|
241
|
-
if (buildRouteTrie && mergedRouteAncestry) {
|
|
310
|
+
if (mergedRouteAncestry) {
|
|
242
311
|
// Build routeToStaticPrefix from saved manifests
|
|
243
312
|
const routeToStaticPrefix: Record<string, string> = {};
|
|
244
313
|
for (const { manifest } of allManifests) {
|
|
@@ -271,64 +340,36 @@ export async function discoverRouters(
|
|
|
271
340
|
}
|
|
272
341
|
}
|
|
273
342
|
|
|
343
|
+
// buildRouteTrie reads these via ?.has / ?.[] — empty is observationally
|
|
344
|
+
// identical to undefined, so no empty->undefined coercion is needed.
|
|
274
345
|
newMergedRouteTrie = buildRouteTrie(
|
|
275
346
|
newMergedRouteManifest,
|
|
276
347
|
mergedRouteAncestry,
|
|
277
348
|
routeToStaticPrefix,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
|
|
283
|
-
Object.keys(mergedResponseTypeRoutes).length > 0
|
|
284
|
-
? mergedResponseTypeRoutes
|
|
285
|
-
: undefined,
|
|
349
|
+
mergedRouteTrailingSlash,
|
|
350
|
+
prerenderRouteNames,
|
|
351
|
+
passthroughRouteNames,
|
|
352
|
+
mergedResponseTypeRoutes,
|
|
286
353
|
);
|
|
287
354
|
|
|
288
|
-
// 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).
|
|
289
359
|
for (const { id, manifest } of allManifests) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
294
|
-
continue;
|
|
295
|
-
const perRouterStaticPrefix: Record<string, string> = {};
|
|
296
|
-
for (const name of Object.keys(manifest.routeManifest)) {
|
|
297
|
-
perRouterStaticPrefix[name] = "";
|
|
360
|
+
const perRouterTrie = buildPerRouterTrie(manifest);
|
|
361
|
+
if (perRouterTrie) {
|
|
362
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
298
363
|
}
|
|
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
364
|
}
|
|
329
365
|
}
|
|
330
366
|
}
|
|
331
367
|
|
|
368
|
+
debug?.(
|
|
369
|
+
"inner: trie build done (%sms)",
|
|
370
|
+
(performance.now() - trieStart).toFixed(1),
|
|
371
|
+
);
|
|
372
|
+
|
|
332
373
|
// Commit all local state to the shared discovery state atomically.
|
|
333
374
|
// This ensures a failed re-discovery (e.g. from a transient module
|
|
334
375
|
// evaluation error) preserves the last known-good state.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router discovery error aggregation.
|
|
3
|
+
*
|
|
4
|
+
* During host-router discovery the lazy mounts registered by a host router are
|
|
5
|
+
* invoked to trigger each sub-app's createRouter() registration. Some mount
|
|
6
|
+
* failures are expected in the temporary discovery server context (a sub-app may
|
|
7
|
+
* reference runtime-only bindings), so each is invoked defensively and its error
|
|
8
|
+
* is collected rather than thrown.
|
|
9
|
+
*
|
|
10
|
+
* Previously these errors were discarded with an empty `catch {}`. When a real
|
|
11
|
+
* failure - typically a sub-app whose router module fails to import - left the
|
|
12
|
+
* registry empty, discovery reported the misleading "No routers found" message
|
|
13
|
+
* with no trace of the underlying cause. The collected errors are now surfaced
|
|
14
|
+
* via the `DiscoveryError` thrown at the end of discovery (issue #499).
|
|
15
|
+
*
|
|
16
|
+
* Which entries to invoke is taken from the consumer's declared intent, not
|
|
17
|
+
* inferred from the function's shape. A host route is registered either with
|
|
18
|
+
* `.map((request) => Response)` (an inline request handler, `kind: "handler"`)
|
|
19
|
+
* or `.lazy(() => import("./sub-app"))` (a lazy mount, `kind: "lazy"`). Only
|
|
20
|
+
* `kind === "lazy"` entries are invoked here; inline handlers are never invoked
|
|
21
|
+
* during discovery (they need a Request and register no routers). Because a lazy
|
|
22
|
+
* entry is known to be a module loader, ANY failure it produces - a synchronous
|
|
23
|
+
* throw or a rejected promise - is a genuine discovery failure and is collected.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** An error caught (and previously swallowed) while resolving host routers. */
|
|
27
|
+
export interface CaughtDiscoveryError {
|
|
28
|
+
/** Human-readable description of where the error was caught. */
|
|
29
|
+
context: string;
|
|
30
|
+
/** The caught value (an Error or otherwise). */
|
|
31
|
+
error: unknown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Minimal shape of a host registry entry needed for mount resolution.
|
|
36
|
+
* Mirrors the runtime HostRouterRegistry value without coupling to its type.
|
|
37
|
+
*/
|
|
38
|
+
interface HostRegistryRoute {
|
|
39
|
+
handler?: unknown;
|
|
40
|
+
kind?: string;
|
|
41
|
+
}
|
|
42
|
+
interface HostRegistryEntry {
|
|
43
|
+
routes: HostRegistryRoute[];
|
|
44
|
+
fallback?: HostRegistryRoute | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Indent every non-empty line of `text` by `pad`. */
|
|
48
|
+
function indent(text: string, pad: string): string {
|
|
49
|
+
return text
|
|
50
|
+
.split("\n")
|
|
51
|
+
.map((line) => (line.length > 0 ? pad + line : line))
|
|
52
|
+
.join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Invoke a single lazy mount to trigger its sub-app import (and createRouter()
|
|
57
|
+
* registration), collecting any failure under `context`. The entry is known to
|
|
58
|
+
* be a loader (`kind === "lazy"`), so both a synchronous throw and a rejected
|
|
59
|
+
* promise are genuine failures - no shape heuristics are needed.
|
|
60
|
+
*/
|
|
61
|
+
async function invokeLazyMount(
|
|
62
|
+
loader: () => unknown,
|
|
63
|
+
context: string,
|
|
64
|
+
errors: CaughtDiscoveryError[],
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
await loader();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
errors.push({ context, error });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Whether a registry route is a `.lazy()` mount with an invokable loader. */
|
|
74
|
+
function isLazyMount(
|
|
75
|
+
route: HostRegistryRoute | null | undefined,
|
|
76
|
+
): route is { handler: () => unknown; kind: "lazy" } {
|
|
77
|
+
return (
|
|
78
|
+
!!route && route.kind === "lazy" && typeof route.handler === "function"
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Invoke every lazy mount in the host registry to trigger sub-app
|
|
84
|
+
* createRouter() registration, collecting (not throwing) any failures.
|
|
85
|
+
*
|
|
86
|
+
* Only `.lazy()` entries are invoked; `.map()` inline request handlers are
|
|
87
|
+
* skipped (they need a Request and register no routers). Failures are returned
|
|
88
|
+
* rather than thrown because some mounts legitimately fail in the temporary
|
|
89
|
+
* discovery server context; the caller decides whether the failures matter,
|
|
90
|
+
* which is only when discovery finds no routers at all.
|
|
91
|
+
*/
|
|
92
|
+
export async function resolveHostRouterHandlers(
|
|
93
|
+
hostRegistry: Map<string, HostRegistryEntry>,
|
|
94
|
+
): Promise<CaughtDiscoveryError[]> {
|
|
95
|
+
const errors: CaughtDiscoveryError[] = [];
|
|
96
|
+
|
|
97
|
+
for (const [hostId, entry] of hostRegistry) {
|
|
98
|
+
for (const route of entry.routes) {
|
|
99
|
+
if (isLazyMount(route)) {
|
|
100
|
+
await invokeLazyMount(
|
|
101
|
+
route.handler,
|
|
102
|
+
`host "${hostId}" route handler`,
|
|
103
|
+
errors,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (isLazyMount(entry.fallback)) {
|
|
108
|
+
await invokeLazyMount(
|
|
109
|
+
entry.fallback.handler,
|
|
110
|
+
`host "${hostId}" fallback handler`,
|
|
111
|
+
errors,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return errors;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build the terminal "No routers found" message, appending any errors caught
|
|
121
|
+
* during host-router discovery so the real cause is visible.
|
|
122
|
+
*
|
|
123
|
+
* The aggregated errors are inlined into the message (in addition to being
|
|
124
|
+
* attached via `cause` on `DiscoveryError`) so they survive every caller: the
|
|
125
|
+
* dev/HMR paths log `err.message`, and the build path re-throws using
|
|
126
|
+
* `err.stack`, which begins with the message. None of those callers traverse
|
|
127
|
+
* `cause`, so the message must carry the detail. Each error includes its stack
|
|
128
|
+
* when available.
|
|
129
|
+
*/
|
|
130
|
+
export function formatNoRoutersError(
|
|
131
|
+
entryPath: string | undefined,
|
|
132
|
+
errors: CaughtDiscoveryError[],
|
|
133
|
+
): string {
|
|
134
|
+
const base = `[rango] No routers found in registry after importing ${entryPath}`;
|
|
135
|
+
if (errors.length === 0) {
|
|
136
|
+
return base;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const formatted = errors
|
|
140
|
+
.map(({ context, error }) => {
|
|
141
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
142
|
+
const detail = err.stack ?? err.message;
|
|
143
|
+
return ` - while resolving ${context}:\n${indent(detail, " ")}`;
|
|
144
|
+
})
|
|
145
|
+
.join("\n");
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
`${base}\n\n` +
|
|
149
|
+
`${errors.length} error(s) were caught during host-router discovery and ` +
|
|
150
|
+
`likely explain why no routers were registered:\n${formatted}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Reduce the caught errors to an `ErrorOptions.cause`: a single failure becomes
|
|
156
|
+
* the direct cause; multiple failures are wrapped in an `AggregateError` so
|
|
157
|
+
* each underlying error remains reachable. No errors -> no cause.
|
|
158
|
+
*/
|
|
159
|
+
function toCause(errors: CaughtDiscoveryError[]): unknown {
|
|
160
|
+
if (errors.length === 0) return undefined;
|
|
161
|
+
if (errors.length === 1) return errors[0].error;
|
|
162
|
+
return new AggregateError(
|
|
163
|
+
errors.map((e) => e.error),
|
|
164
|
+
"Multiple host-router handlers failed during discovery",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Thrown when router discovery completes without finding any routers.
|
|
170
|
+
*
|
|
171
|
+
* Carries the entry path and the individual failures caught while resolving
|
|
172
|
+
* host-router lazy handlers. The formatted detail is embedded in `message` (for
|
|
173
|
+
* callers that log `err.message`/`err.stack`) and the underlying error(s) are
|
|
174
|
+
* also attached via `cause` (a single failure directly, multiple wrapped in an
|
|
175
|
+
* `AggregateError`) for cause-aware tooling such as the Vite error overlay.
|
|
176
|
+
*/
|
|
177
|
+
export class DiscoveryError extends Error {
|
|
178
|
+
/** The entry file that was imported before discovery gave up. */
|
|
179
|
+
readonly entryPath: string | undefined;
|
|
180
|
+
/** Individual failures caught while resolving host-router handlers. */
|
|
181
|
+
readonly caught: CaughtDiscoveryError[];
|
|
182
|
+
|
|
183
|
+
constructor(entryPath: string | undefined, caught: CaughtDiscoveryError[]) {
|
|
184
|
+
super(formatNoRoutersError(entryPath, caught));
|
|
185
|
+
const cause = toCause(caught);
|
|
186
|
+
if (cause !== undefined) {
|
|
187
|
+
this.cause = cause;
|
|
188
|
+
}
|
|
189
|
+
this.name = "DiscoveryError";
|
|
190
|
+
this.entryPath = entryPath;
|
|
191
|
+
this.caught = caught;
|
|
192
|
+
Object.setPrototypeOf(this, DiscoveryError.prototype);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** How the dev caller should surface a discovery failure. */
|
|
197
|
+
export interface DiscoveryFailureReport {
|
|
198
|
+
level: "error" | "warn";
|
|
199
|
+
message: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Decide how to surface a dev-boot discovery failure in the terminal.
|
|
204
|
+
*
|
|
205
|
+
* The bare "no routers found" case (a DiscoveryError with no caught host-handler
|
|
206
|
+
* failures) is ambiguous. It is either:
|
|
207
|
+
* - a genuine misconfiguration — the entry never calls createRouter(), or the
|
|
208
|
+
* configured entry path is wrong; or
|
|
209
|
+
* - a transient artifact of a Vite dependency re-optimization racing with boot
|
|
210
|
+
* discovery (a module read before the entry import resolves to the
|
|
211
|
+
* pre-optimize copy of the runner graph while createRouter() populated the
|
|
212
|
+
* post-optimize copy — see router-discovery.ts).
|
|
213
|
+
*
|
|
214
|
+
* We can only tell them apart when the caller observed the dep optimizer's
|
|
215
|
+
* `browserHash` change across the discovery attempt (`reoptimizeObserved`): a
|
|
216
|
+
* reload-causing re-optimization landed mid-flight, so the empty read was
|
|
217
|
+
* transient and the app self-heals per-request (handler.ts builds the trie from
|
|
218
|
+
* the router's live urlpatterns) with discovery re-running on the next boot.
|
|
219
|
+
*
|
|
220
|
+
* A DiscoveryError that DOES carry caught host-handler failures already embeds
|
|
221
|
+
* the real cause in its message, and any non-DiscoveryError is a hard failure;
|
|
222
|
+
* both stay loud with full detail.
|
|
223
|
+
*/
|
|
224
|
+
export function describeDiscoveryFailure(
|
|
225
|
+
err: unknown,
|
|
226
|
+
opts: { reoptimizeObserved?: boolean } = {},
|
|
227
|
+
): DiscoveryFailureReport {
|
|
228
|
+
if (err instanceof DiscoveryError && err.caught.length === 0) {
|
|
229
|
+
const entry = err.entryPath ?? "the router entry";
|
|
230
|
+
if (opts.reoptimizeObserved) {
|
|
231
|
+
return {
|
|
232
|
+
level: "warn",
|
|
233
|
+
message:
|
|
234
|
+
`[rango] No routers found while Vite was re-optimizing dependencies on ` +
|
|
235
|
+
`dev boot. This is transient: routes are served per-request and ` +
|
|
236
|
+
`discovery re-runs automatically, so it clears on the next boot. If ` +
|
|
237
|
+
`routes still 404, confirm ${entry} calls createRouter().`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
level: "error",
|
|
242
|
+
message:
|
|
243
|
+
`${err.message}\n` +
|
|
244
|
+
` Ensure ${entry} calls createRouter() at module top level and that the ` +
|
|
245
|
+
`configured router entry path is correct.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const e = err as { stack?: string; message?: string };
|
|
250
|
+
const detail = e?.stack ?? e?.message ?? String(err);
|
|
251
|
+
return {
|
|
252
|
+
level: "error",
|
|
253
|
+
message: `[rango] Router discovery failed: ${detail}`,
|
|
254
|
+
};
|
|
255
|
+
}
|