@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bdaf10aa
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 +303 -741
- package/dist/bin/rango.js +730 -184
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +4344 -1335
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +86 -15
- 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 +251 -24
- package/skills/caching/SKILL.md +375 -17
- 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 +364 -0
- 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 +94 -18
- package/skills/layout/SKILL.md +62 -19
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +302 -54
- package/skills/middleware/SKILL.md +59 -16
- package/skills/migrate-nextjs/SKILL.md +745 -0
- package/skills/migrate-react-router/SKILL.md +153 -0
- 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 +225 -10
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +178 -124
- package/skills/rango/SKILL.md +318 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +172 -9
- package/skills/router-setup/SKILL.md +131 -11
- 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 -616
- 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 +1 -66
- 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/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +257 -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 +132 -33
- package/src/browser/navigation-client.ts +218 -68
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +203 -80
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +241 -127
- package/src/browser/prefetch/cache.ts +271 -44
- package/src/browser/prefetch/fetch.ts +367 -40
- package/src/browser/prefetch/queue.ts +144 -23
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +121 -16
- package/src/browser/react/NavigationProvider.tsx +240 -122
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- 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 +23 -69
- 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 +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 +99 -8
- package/src/browser/rsc-router.tsx +272 -80
- package/src/browser/scroll-restoration.ts +56 -22
- package/src/browser/segment-reconciler.ts +44 -7
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +244 -71
- package/src/browser/types.ts +136 -12
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +207 -158
- package/src/build/generate-route-types.ts +6 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +198 -41
- 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 +464 -63
- 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 +336 -110
- package/src/build/route-types/scan-filter.ts +9 -2
- 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 +454 -97
- package/src/cache/cache-scope.ts +235 -103
- 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 +2446 -170
- 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 +11 -17
- package/src/cache/document-cache.ts +144 -49
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +24 -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/taint.ts +55 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +41 -21
- package/src/client.tsx +116 -290
- 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 +84 -2
- package/src/debug.ts +2 -2
- 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 +104 -34
- 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 +100 -13
- package/src/index.ts +143 -19
- 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 +37 -41
- package/src/prerender.ts +215 -86
- 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 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +485 -303
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +153 -77
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +53 -12
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +48 -21
- package/src/route-types.ts +37 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +130 -29
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +89 -63
- package/src/router/loader-resolution.ts +286 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +105 -56
- package/src/router/match-api.ts +178 -218
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +66 -22
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +105 -50
- 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 +128 -44
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +301 -177
- package/src/router/navigation-snapshot.ts +133 -0
- 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 +203 -58
- package/src/router/preview-match.ts +35 -103
- package/src/router/request-classification.ts +291 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +256 -0
- package/src/router/router-context.ts +11 -29
- package/src/router/router-interfaces.ts +146 -35
- package/src/router/router-options.ts +202 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +301 -78
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +156 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +477 -385
- 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 +8 -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 +10 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +210 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +682 -508
- 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 +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +138 -15
- 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 +509 -73
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -113
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +62 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +167 -0
- package/src/segment-system.tsx +449 -132
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +367 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +38 -46
- package/src/server/request-context.ts +558 -173
- package/src/ssr/index.tsx +491 -174
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +27 -18
- 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 +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +234 -82
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +137 -14
- package/src/urls/include-helper.ts +40 -75
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +102 -23
- package/src/urls/path-helper.ts +62 -111
- package/src/urls/pattern-types.ts +84 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +346 -89
- 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 +36 -38
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +214 -132
- 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 +57 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +15 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +234 -62
- 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 +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- 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 +553 -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 +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +353 -303
- package/src/vite/router-discovery.ts +1090 -166
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- 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/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 +98 -38
- package/src/vite/utils/shared-utils.ts +144 -44
- 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
package/src/build/route-trie.ts
CHANGED
|
@@ -10,9 +10,24 @@ import {
|
|
|
10
10
|
parsePattern,
|
|
11
11
|
type ParsedSegment,
|
|
12
12
|
} from "../router/pattern-matching.js";
|
|
13
|
+
import { buildRouteToStaticPrefix } from "./prefix-tree-utils.js";
|
|
14
|
+
import type { FullManifest } from "./generate-manifest.js";
|
|
13
15
|
|
|
14
16
|
// -- Trie data structures (compact keys for JSON serialization) --
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* A response-type variant folded into a primary leaf's negotiate list. `pa` is
|
|
20
|
+
* the variant's own positional param-name array, carried so the runtime can
|
|
21
|
+
* re-key the matched params under the variant's names when it wins negotiation
|
|
22
|
+
* (the trie match extracts params under the PRIMARY leaf's pa). Omitted when the
|
|
23
|
+
* variant has no params; absent/identical pa means no re-key is needed.
|
|
24
|
+
*/
|
|
25
|
+
export interface NegotiateVariant {
|
|
26
|
+
routeKey: string;
|
|
27
|
+
responseType: string;
|
|
28
|
+
pa?: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
export interface TrieLeaf {
|
|
17
32
|
/** Route name (e.g., "site.l1_500") */
|
|
18
33
|
n: string;
|
|
@@ -20,8 +35,6 @@ export interface TrieLeaf {
|
|
|
20
35
|
sp: string;
|
|
21
36
|
/** Ancestry shortCodes from root to route [M0L0, M0L0L0, M0L0L0R499] */
|
|
22
37
|
a: string[];
|
|
23
|
-
/** Optional param names (absent params get empty string value) */
|
|
24
|
-
op?: string[];
|
|
25
38
|
/** Constraint validation: paramName -> allowed values */
|
|
26
39
|
cv?: Record<string, string[]>;
|
|
27
40
|
/** Ordered param names for this route (positional) */
|
|
@@ -35,7 +48,7 @@ export interface TrieLeaf {
|
|
|
35
48
|
/** Response type for non-RSC routes (json, text, image, any) */
|
|
36
49
|
rt?: string;
|
|
37
50
|
/** Negotiate variants: response-type routes sharing this path */
|
|
38
|
-
nv?:
|
|
51
|
+
nv?: NegotiateVariant[];
|
|
39
52
|
/** RSC-first: RSC route was defined before response-type variants */
|
|
40
53
|
rf?: true;
|
|
41
54
|
}
|
|
@@ -49,8 +62,13 @@ export interface TrieNode {
|
|
|
49
62
|
p?: { n: string; c: TrieNode };
|
|
50
63
|
/** Suffix-param children keyed by suffix (e.g., ".html" → { n: "productId", c: ... }) */
|
|
51
64
|
xp?: Record<string, { n: string; c: TrieNode }>;
|
|
52
|
-
/**
|
|
53
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Wildcard terminal: leaf + paramName (`pn`). `pn` is "*" for the bare `/*`
|
|
67
|
+
* form and the param name for a named catch-all (`:name+`/`:name*`). `w1`
|
|
68
|
+
* marks a one-or-more catch-all (`:name+`): the runtime walker then rejects
|
|
69
|
+
* the zero-segment/empty-remainder case. Absent `w1` is zero-or-more.
|
|
70
|
+
*/
|
|
71
|
+
w?: TrieLeaf & { pn: string; w1?: true };
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
/**
|
|
@@ -60,6 +78,9 @@ export interface TrieNode {
|
|
|
60
78
|
* @param routeAncestry - Map of route name to ancestry shortCodes
|
|
61
79
|
* @param routeToStaticPrefix - Map of route name to its entry's staticPrefix
|
|
62
80
|
* @param routeTrailingSlash - Optional map of route name to trailing slash mode
|
|
81
|
+
* @param prerenderRouteNames - Optional set of prerendered route names (sets leaf.pr)
|
|
82
|
+
* @param passthroughRouteNames - Optional set of passthrough route names (sets leaf.pt)
|
|
83
|
+
* @param responseTypeRoutes - Optional map of route name to response type (sets leaf.rt)
|
|
63
84
|
*/
|
|
64
85
|
export function buildRouteTrie(
|
|
65
86
|
routeManifest: Record<string, string>,
|
|
@@ -94,51 +115,124 @@ export function buildRouteTrie(
|
|
|
94
115
|
});
|
|
95
116
|
}
|
|
96
117
|
|
|
118
|
+
sortSuffixParams(root);
|
|
97
119
|
return root;
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
/**
|
|
101
|
-
*
|
|
102
|
-
* the
|
|
123
|
+
* Sort every node's suffix-param map (`node.xp`) by descending suffix length so
|
|
124
|
+
* the matcher tries the most specific suffix first. Overlapping suffixes like
|
|
125
|
+
* `.min.js` and `.js` must resolve by specificity, not route declaration order:
|
|
126
|
+
* a request for `/app.min.js` should match `:file.min.js`, not `:file.js`.
|
|
127
|
+
*
|
|
128
|
+
* This started as a bug — `walkTrie` iterates `node.xp` in object order and
|
|
129
|
+
* returns the first suffix the segment ends with, so the winner depended on
|
|
130
|
+
* which route was declared first. Sorting at build time fixes it allocation-free
|
|
131
|
+
* on the match hot path: the serialized production trie preserves this key order
|
|
132
|
+
* through JSON.parse, so dev (per-request rebuild) and production match
|
|
133
|
+
* identically. Array.prototype.sort is stable (ES2019+), so equal-length
|
|
134
|
+
* suffixes keep their declaration order — the router's existing tiebreak.
|
|
135
|
+
*/
|
|
136
|
+
function sortSuffixParams(node: TrieNode): void {
|
|
137
|
+
if (node.xp) {
|
|
138
|
+
const sorted: Record<string, { n: string; c: TrieNode }> = {};
|
|
139
|
+
for (const suffix of Object.keys(node.xp).sort(
|
|
140
|
+
(a, b) => b.length - a.length,
|
|
141
|
+
)) {
|
|
142
|
+
sorted[suffix] = node.xp[suffix];
|
|
143
|
+
}
|
|
144
|
+
node.xp = sorted;
|
|
145
|
+
for (const child of Object.values(node.xp)) {
|
|
146
|
+
sortSuffixParams(child.c);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (node.s) {
|
|
150
|
+
for (const child of Object.values(node.s)) {
|
|
151
|
+
sortSuffixParams(child);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (node.p) {
|
|
155
|
+
sortSuffixParams(node.p.c);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build a per-router trie from a generated manifest. This is the single
|
|
161
|
+
* construction path shared by build/discovery (discover-routers.ts, serialized
|
|
162
|
+
* into the production chunk) and the dev/HMR runtime rebuild
|
|
163
|
+
* (rsc/manifest-init.ts). Keeping one code path is what guarantees the dev
|
|
164
|
+
* runtime trie and the production serialized trie are byte-for-byte identical
|
|
165
|
+
* (modulo `leaf.a` ancestry, which embeds the mount index and is debug-only).
|
|
166
|
+
*
|
|
167
|
+
* Returns null when the manifest has no route ancestry (no routes), matching
|
|
168
|
+
* the prior guard at both call sites.
|
|
169
|
+
*/
|
|
170
|
+
export function buildPerRouterTrie(manifest: FullManifest): TrieNode | null {
|
|
171
|
+
const ancestry = manifest._routeAncestry;
|
|
172
|
+
if (!ancestry || Object.keys(ancestry).length === 0) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Seed every route to the root static prefix (""), then override with each
|
|
177
|
+
// route's include() scope prefix from the prefix tree so the trie returns the
|
|
178
|
+
// correct `sp` for lazy-entry lookup in find-match.
|
|
179
|
+
const routeToStaticPrefix: Record<string, string> = {};
|
|
180
|
+
for (const name of Object.keys(manifest.routeManifest)) {
|
|
181
|
+
routeToStaticPrefix[name] = "";
|
|
182
|
+
}
|
|
183
|
+
if (manifest.prefixTree) {
|
|
184
|
+
buildRouteToStaticPrefix(manifest.prefixTree, routeToStaticPrefix);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return buildRouteTrie(
|
|
188
|
+
manifest.routeManifest,
|
|
189
|
+
ancestry,
|
|
190
|
+
routeToStaticPrefix,
|
|
191
|
+
manifest.routeTrailingSlash,
|
|
192
|
+
manifest.prerenderRoutes ? new Set(manifest.prerenderRoutes) : undefined,
|
|
193
|
+
manifest.passthroughRoutes
|
|
194
|
+
? new Set(manifest.passthroughRoutes)
|
|
195
|
+
: undefined,
|
|
196
|
+
manifest.responseTypeRoutes,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Insert a route into the trie. Optional params expand into two branches at
|
|
202
|
+
* registration time (skip-first, then present), so each terminal lives at the
|
|
203
|
+
* correct depth for its number of bound params and carries a branch-local
|
|
204
|
+
* `pa` listing only those names. The trie's single-slot `node.p` is reused
|
|
205
|
+
* across branches because matching ignores `node.p.n` — the leaf's `pa` is
|
|
206
|
+
* the source of truth for naming. Skip-first ordering lets `mergeLeaf`'s
|
|
207
|
+
* last-wins rule produce greedy-leftmost semantics for free at any shared
|
|
208
|
+
* terminal depth.
|
|
103
209
|
*/
|
|
104
210
|
function insertRoute(
|
|
105
211
|
node: TrieNode,
|
|
106
212
|
segments: ParsedSegment[],
|
|
107
213
|
index: number,
|
|
108
|
-
leaf: Omit<TrieLeaf, "
|
|
214
|
+
leaf: Omit<TrieLeaf, "cv" | "pa">,
|
|
109
215
|
): void {
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
const optionalParams: string[] = [];
|
|
216
|
+
// cv (full constraint map) is route-level and identical on every terminal,
|
|
217
|
+
// so compute it once on the shared base.
|
|
113
218
|
const constraints: Record<string, string[]> = {};
|
|
114
219
|
|
|
115
220
|
for (const seg of segments) {
|
|
116
221
|
if (seg.type === "param") {
|
|
117
|
-
paramNames.push(seg.value);
|
|
118
|
-
if (seg.optional) {
|
|
119
|
-
optionalParams.push(seg.value);
|
|
120
|
-
}
|
|
121
222
|
if (seg.constraint) {
|
|
122
223
|
constraints[seg.value] = seg.constraint;
|
|
123
224
|
}
|
|
124
225
|
}
|
|
125
226
|
}
|
|
126
227
|
|
|
127
|
-
const
|
|
228
|
+
const leafBase: Omit<TrieLeaf, "pa"> = {
|
|
128
229
|
...leaf,
|
|
129
|
-
...(paramNames.length > 0 ? { pa: paramNames } : {}),
|
|
130
|
-
...(optionalParams.length > 0 ? { op: optionalParams } : {}),
|
|
131
230
|
...(Object.keys(constraints).length > 0 ? { cv: constraints } : {}),
|
|
132
231
|
};
|
|
133
232
|
|
|
134
|
-
insertSegments(node, segments, index,
|
|
233
|
+
insertSegments(node, segments, index, leafBase, []);
|
|
135
234
|
}
|
|
136
235
|
|
|
137
|
-
/**
|
|
138
|
-
* Recursively insert segments into the trie.
|
|
139
|
-
* For optional params, we add a terminal at the current node (param absent)
|
|
140
|
-
* AND continue inserting into the param child (param present).
|
|
141
|
-
*/
|
|
142
236
|
/**
|
|
143
237
|
* Extract ancestry map from a built trie by visiting all leaf nodes.
|
|
144
238
|
* Returns { routeName: ancestryShortCodes[] } for every route in the trie.
|
|
@@ -181,6 +275,19 @@ export function extractAncestryFromTrie(
|
|
|
181
275
|
* appended to the nv (negotiate variants) array.
|
|
182
276
|
* Multiple response types on the same path are supported (json + text + xml).
|
|
183
277
|
*/
|
|
278
|
+
/**
|
|
279
|
+
* Build a negotiate-variant entry from a leaf being folded into another leaf's
|
|
280
|
+
* nv list. Carries the variant's positional param names (`pa`) so the runtime
|
|
281
|
+
* can re-key matched params under the variant's names; omitted when the variant
|
|
282
|
+
* has none (the common case where primary and variant share the same names is a
|
|
283
|
+
* no-op re-key regardless).
|
|
284
|
+
*/
|
|
285
|
+
function toVariant(leaf: TrieLeaf, responseType: string): NegotiateVariant {
|
|
286
|
+
return leaf.pa
|
|
287
|
+
? { routeKey: leaf.n, responseType, pa: leaf.pa }
|
|
288
|
+
: { routeKey: leaf.n, responseType };
|
|
289
|
+
}
|
|
290
|
+
|
|
184
291
|
function mergeLeaves(existing: TrieLeaf | undefined, leaf: TrieLeaf): TrieLeaf {
|
|
185
292
|
if (!existing) return leaf;
|
|
186
293
|
|
|
@@ -188,7 +295,7 @@ function mergeLeaves(existing: TrieLeaf | undefined, leaf: TrieLeaf): TrieLeaf {
|
|
|
188
295
|
// Both are response-type: preserve old as variant
|
|
189
296
|
const merged = leaf;
|
|
190
297
|
merged.nv = existing.nv || [];
|
|
191
|
-
merged.nv.push(
|
|
298
|
+
merged.nv.push(toVariant(existing, existing.rt));
|
|
192
299
|
return merged;
|
|
193
300
|
}
|
|
194
301
|
if (leaf.rt && !existing.rt) {
|
|
@@ -198,7 +305,7 @@ function mergeLeaves(existing: TrieLeaf | undefined, leaf: TrieLeaf): TrieLeaf {
|
|
|
198
305
|
existing.nv = [];
|
|
199
306
|
existing.rf = true;
|
|
200
307
|
}
|
|
201
|
-
existing.nv.push(
|
|
308
|
+
existing.nv.push(toVariant(leaf, leaf.rt));
|
|
202
309
|
return existing;
|
|
203
310
|
}
|
|
204
311
|
if (!leaf.rt && existing.rt) {
|
|
@@ -206,7 +313,7 @@ function mergeLeaves(existing: TrieLeaf | undefined, leaf: TrieLeaf): TrieLeaf {
|
|
|
206
313
|
// RSC was defined second (response-type was already the existing leaf)
|
|
207
314
|
if (!leaf.nv) leaf.nv = [];
|
|
208
315
|
if (existing.nv) leaf.nv.push(...existing.nv);
|
|
209
|
-
leaf.nv.push(
|
|
316
|
+
leaf.nv.push(toVariant(existing, existing.rt));
|
|
210
317
|
// rf intentionally not set — RSC came after response-type variants
|
|
211
318
|
return leaf;
|
|
212
319
|
}
|
|
@@ -218,15 +325,25 @@ function mergeLeaf(node: TrieNode, leaf: TrieLeaf): void {
|
|
|
218
325
|
node.r = mergeLeaves(node.r, leaf);
|
|
219
326
|
}
|
|
220
327
|
|
|
328
|
+
function buildLeaf(
|
|
329
|
+
leafBase: Omit<TrieLeaf, "pa">,
|
|
330
|
+
paramNames: string[],
|
|
331
|
+
): TrieLeaf {
|
|
332
|
+
return paramNames.length > 0
|
|
333
|
+
? { ...leafBase, pa: [...paramNames] }
|
|
334
|
+
: { ...leafBase };
|
|
335
|
+
}
|
|
336
|
+
|
|
221
337
|
function insertSegments(
|
|
222
338
|
node: TrieNode,
|
|
223
339
|
segments: ParsedSegment[],
|
|
224
340
|
index: number,
|
|
225
|
-
|
|
341
|
+
leafBase: Omit<TrieLeaf, "pa">,
|
|
342
|
+
paramNames: string[],
|
|
226
343
|
): void {
|
|
227
|
-
// Base case: all segments consumed, add terminal
|
|
344
|
+
// Base case: all segments consumed, add terminal with branch-local pa
|
|
228
345
|
if (index >= segments.length) {
|
|
229
|
-
mergeLeaf(node,
|
|
346
|
+
mergeLeaf(node, buildLeaf(leafBase, paramNames));
|
|
230
347
|
return;
|
|
231
348
|
}
|
|
232
349
|
|
|
@@ -235,12 +352,19 @@ function insertSegments(
|
|
|
235
352
|
if (segment.type === "static") {
|
|
236
353
|
if (!node.s) node.s = {};
|
|
237
354
|
if (!node.s[segment.value]) node.s[segment.value] = {};
|
|
238
|
-
insertSegments(
|
|
355
|
+
insertSegments(
|
|
356
|
+
node.s[segment.value],
|
|
357
|
+
segments,
|
|
358
|
+
index + 1,
|
|
359
|
+
leafBase,
|
|
360
|
+
paramNames,
|
|
361
|
+
);
|
|
239
362
|
} else if (segment.type === "param") {
|
|
240
363
|
if (segment.optional) {
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
//
|
|
364
|
+
// SKIP first: continue at the same node without binding this name.
|
|
365
|
+
// Skip-first ordering means the present-branch's TAKE overwrites any
|
|
366
|
+
// shared terminal later, giving greedy-leftmost semantics.
|
|
367
|
+
insertSegments(node, segments, index + 1, leafBase, paramNames);
|
|
244
368
|
}
|
|
245
369
|
if (segment.suffix) {
|
|
246
370
|
// Suffix param: keyed by suffix string (e.g., ".html")
|
|
@@ -248,18 +372,51 @@ function insertSegments(
|
|
|
248
372
|
if (!node.xp[segment.suffix]) {
|
|
249
373
|
node.xp[segment.suffix] = { n: segment.value, c: {} };
|
|
250
374
|
}
|
|
251
|
-
insertSegments(node.xp[segment.suffix].c, segments, index + 1,
|
|
375
|
+
insertSegments(node.xp[segment.suffix].c, segments, index + 1, leafBase, [
|
|
376
|
+
...paramNames,
|
|
377
|
+
segment.value,
|
|
378
|
+
]);
|
|
252
379
|
} else {
|
|
253
380
|
if (!node.p) {
|
|
254
381
|
node.p = { n: segment.value, c: {} };
|
|
255
382
|
}
|
|
256
|
-
insertSegments(node.p.c, segments, index + 1,
|
|
383
|
+
insertSegments(node.p.c, segments, index + 1, leafBase, [
|
|
384
|
+
...paramNames,
|
|
385
|
+
segment.value,
|
|
386
|
+
]);
|
|
257
387
|
}
|
|
258
388
|
} else if (segment.type === "wildcard") {
|
|
259
|
-
// Wildcard consumes all remaining segments
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
389
|
+
// Wildcard consumes all remaining segments. Carry any params bound before
|
|
390
|
+
// the wildcard in pa so they zip correctly against paramValues at match.
|
|
391
|
+
// `pn` is "*" for the bare `/*` and the param name for a named catch-all;
|
|
392
|
+
// `w1` marks the one-or-more variant (`:name+`) so the walker rejects the
|
|
393
|
+
// empty-remainder case.
|
|
394
|
+
const wildLeaf: TrieLeaf & { pn: string; w1?: true } = {
|
|
395
|
+
...buildLeaf(leafBase, paramNames),
|
|
396
|
+
pn: segment.value,
|
|
397
|
+
...(segment.oneOrMore ? { w1: true as const } : {}),
|
|
398
|
+
};
|
|
399
|
+
const existing = node.w;
|
|
400
|
+
// Merge when there's no existing wildcard, when this is a response-type
|
|
401
|
+
// content-negotiation variant of the same catch-all (one side carries `rt`),
|
|
402
|
+
// or when it's the SAME catch-all identity (same param name + arity).
|
|
403
|
+
// Otherwise two DISTINCT catch-all forms (`/x/*` vs `/x/:p+`) would collide on
|
|
404
|
+
// the single wildcard slot with no non-lossy merge — so keep the first-declared
|
|
405
|
+
// (matching the regex matcher's declaration-order tiebreak) rather than let
|
|
406
|
+
// mergeLeaves' last-wins overwrite silently drop its `pn`/`w1` identity (which
|
|
407
|
+
// stranded the first route and fell through to a corrupt regex-fallback redirect).
|
|
408
|
+
const canMerge =
|
|
409
|
+
existing === undefined ||
|
|
410
|
+
Boolean(existing.rt) ||
|
|
411
|
+
Boolean(wildLeaf.rt) ||
|
|
412
|
+
(existing.pn === wildLeaf.pn &&
|
|
413
|
+
Boolean(existing.w1) === Boolean(wildLeaf.w1));
|
|
414
|
+
if (canMerge) {
|
|
415
|
+
const merged = mergeLeaves(
|
|
416
|
+
existing ? ({ ...existing } as TrieLeaf) : undefined,
|
|
417
|
+
wildLeaf,
|
|
418
|
+
);
|
|
419
|
+
node.w = merged as TrieLeaf & { pn: string; w1?: true };
|
|
420
|
+
}
|
|
264
421
|
}
|
|
265
422
|
}
|
|
@@ -15,19 +15,26 @@ import { extractParamsFromPattern } from "./param-extraction.js";
|
|
|
15
15
|
* the pattern, name, params, and optional search schema from each.
|
|
16
16
|
* Skips unnamed paths (no { name: "..." }).
|
|
17
17
|
*/
|
|
18
|
-
export function extractRoutesFromSource(
|
|
18
|
+
export function extractRoutesFromSource(
|
|
19
|
+
code: string,
|
|
20
|
+
sourceFileArg?: ts.SourceFile,
|
|
21
|
+
): Array<{
|
|
19
22
|
name: string;
|
|
20
23
|
pattern: string;
|
|
21
24
|
params?: Record<string, string>;
|
|
22
25
|
search?: Record<string, string>;
|
|
23
26
|
}> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
// Reuse a caller-provided SourceFile (parsed once per scan) when given;
|
|
28
|
+
// otherwise parse the block here. The walk does not mutate the tree.
|
|
29
|
+
const sourceFile =
|
|
30
|
+
sourceFileArg ??
|
|
31
|
+
ts.createSourceFile(
|
|
32
|
+
"input.tsx",
|
|
33
|
+
code,
|
|
34
|
+
ts.ScriptTarget.Latest,
|
|
35
|
+
true,
|
|
36
|
+
ts.ScriptKind.TSX,
|
|
37
|
+
);
|
|
31
38
|
const routes: Array<{
|
|
32
39
|
name: string;
|
|
33
40
|
pattern: string;
|
|
@@ -23,7 +23,7 @@ export function generatePerModuleTypesSource(
|
|
|
23
23
|
const valid = routes.filter(({ name }) => {
|
|
24
24
|
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
25
25
|
console.warn(
|
|
26
|
-
`[
|
|
26
|
+
`[rango] Skipping route with invalid name: ${JSON.stringify(name)}`,
|
|
27
27
|
);
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
@@ -42,7 +42,7 @@ export function generatePerModuleTypesSource(
|
|
|
42
42
|
for (const { name, pattern, params, search } of valid) {
|
|
43
43
|
if (deduped.has(name)) {
|
|
44
44
|
console.warn(
|
|
45
|
-
`[
|
|
45
|
+
`[rango] Duplicate route name "${name}" — keeping first definition`,
|
|
46
46
|
);
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
@@ -59,7 +59,7 @@ export function generatePerModuleTypesSource(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Generates a .ts file that augments
|
|
62
|
+
* Generates a .ts file that augments Rango.GeneratedRouteMap
|
|
63
63
|
* with route name -> pattern mappings. This enables Handler<"routeName">
|
|
64
64
|
* without circular references since the file has no imports from the app.
|
|
65
65
|
*/
|
|
@@ -88,14 +88,25 @@ export function generateRouteTypesSource(
|
|
|
88
88
|
})
|
|
89
89
|
.join("\n");
|
|
90
90
|
|
|
91
|
+
// The global augmentation extends an alias of `typeof NamedRoutes` rather than
|
|
92
|
+
// `Readonly<typeof NamedRoutes>`. `as const` already makes the members readonly,
|
|
93
|
+
// so the two are behaviour-identical, but `Readonly<>` is a homomorphic mapped
|
|
94
|
+
// type the compiler instantiates once per route at the augmentation site
|
|
95
|
+
// (~4 instantiations/route). An interface `extends` clause cannot name a bare
|
|
96
|
+
// `typeof` query, so the alias is what lets us drop the wrapper. Measured: ~35%
|
|
97
|
+
// fewer type instantiations on a 14k-route app; negligible (but free) on small.
|
|
91
98
|
return `// Auto-generated by @rangojs/router - do not edit
|
|
92
99
|
export const NamedRoutes = {
|
|
93
100
|
${objectBody}
|
|
94
101
|
} as const;
|
|
95
102
|
|
|
103
|
+
// Aliased so the augmentation below does not pay a homomorphic mapped-type
|
|
104
|
+
// instantiation per route; \`as const\` already makes the members readonly.
|
|
105
|
+
type NamedRoutesShape = typeof NamedRoutes;
|
|
106
|
+
|
|
96
107
|
declare global {
|
|
97
|
-
namespace
|
|
98
|
-
interface GeneratedRouteMap extends
|
|
108
|
+
namespace Rango {
|
|
109
|
+
interface GeneratedRouteMap extends NamedRoutesShape {}
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
`;
|