@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140
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 +17 -0
- package/README.md +432 -7
- package/dist/bin/rango.js +2073 -213
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +7258 -2714
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +140 -67
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +329 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +487 -0
- package/skills/caching/SKILL.md +357 -25
- 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 +246 -0
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +16 -10
- package/skills/document-cache/SKILL.md +87 -62
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +557 -79
- package/skills/host-router/SKILL.md +320 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +207 -15
- package/skills/layout/SKILL.md +146 -6
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +616 -54
- package/skills/middleware/SKILL.md +217 -37
- package/skills/migrate-nextjs/SKILL.md +611 -0
- package/skills/migrate-react-router/SKILL.md +927 -0
- package/skills/mime-routes/SKILL.md +42 -11
- package/skills/observability/SKILL.md +194 -0
- package/skills/parallel/SKILL.md +284 -3
- package/skills/ppr/SKILL.md +426 -0
- package/skills/prerender/SKILL.md +437 -52
- package/skills/rango/SKILL.md +369 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +263 -121
- package/skills/route/SKILL.md +350 -21
- package/skills/router-setup/SKILL.md +246 -33
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +126 -222
- 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 +9 -8
- package/skills/typesafety/SKILL.md +532 -103
- package/skills/use-cache/SKILL.md +367 -0
- 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 +77 -44
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +114 -0
- 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 +293 -202
- package/src/browser/history-state.ts +101 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +385 -576
- package/src/browser/navigation-client.ts +245 -75
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +184 -118
- package/src/browser/navigation-transaction.ts +247 -0
- package/src/browser/network-error-handler.ts +88 -0
- package/src/browser/partial-update.ts +412 -364
- package/src/browser/prefetch/cache.ts +359 -0
- package/src/browser/prefetch/fetch.ts +452 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +209 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +194 -0
- package/src/browser/react/Link.tsx +275 -68
- package/src/browser/react/NavigationProvider.tsx +265 -109
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +70 -0
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +272 -60
- package/src/browser/react/location-state.ts +90 -20
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +35 -66
- package/src/browser/react/use-handle.ts +39 -126
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +39 -13
- package/src/browser/react/use-navigation.ts +53 -69
- package/src/browser/react/use-params.ts +75 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +98 -0
- package/src/browser/react/use-search-params.ts +51 -0
- package/src/browser/react/use-segments.ts +72 -99
- package/src/browser/response-adapter.ts +164 -0
- package/src/browser/rsc-router.tsx +300 -72
- package/src/browser/scroll-restoration.ts +138 -50
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +17 -1
- package/src/browser/server-action-bridge.ts +668 -613
- package/src/browser/types.ts +223 -51
- package/src/browser/validate-redirect-origin.ts +56 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +252 -161
- package/src/build/generate-route-types.ts +41 -1038
- package/src/build/index.ts +12 -7
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +225 -42
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +105 -0
- package/src/build/route-types/codegen.ts +113 -0
- package/src/build/route-types/include-resolution.ts +812 -0
- package/src/build/route-types/param-extraction.ts +51 -0
- package/src/build/route-types/per-module-writer.ts +144 -0
- package/src/build/route-types/router-processing.ts +695 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +223 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +60 -0
- package/src/cache/cache-policy.ts +199 -0
- package/src/cache/cache-runtime.ts +525 -0
- package/src/cache/cache-scope.ts +298 -332
- package/src/cache/cache-tag.ts +103 -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 +2500 -158
- 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 +17 -17
- package/src/cache/document-cache.ts +199 -92
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +111 -0
- package/src/cache/index.ts +29 -35
- package/src/cache/memory-segment-store.ts +363 -30
- package/src/cache/profile-registry.ts +88 -0
- package/src/cache/read-through-swr.ts +178 -0
- package/src/cache/segment-codec.ts +248 -0
- package/src/cache/shell-cache.ts +386 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +156 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1102 -0
- package/src/client.rsc.tsx +43 -21
- package/src/client.tsx +131 -347
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +23 -4
- package/src/components/DefaultDocument.tsx +13 -3
- package/src/context-var.ts +168 -0
- package/src/debug.ts +19 -9
- 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 +106 -10
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +110 -35
- package/src/handles/MetaTags.tsx +83 -59
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +93 -0
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +44 -53
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +20 -65
- package/src/host/errors.ts +21 -30
- package/src/host/index.ts +13 -9
- package/src/host/pattern-matcher.ts +50 -79
- package/src/host/router.ts +151 -121
- package/src/host/testing.ts +45 -32
- package/src/host/types.ts +52 -11
- package/src/host/utils.ts +2 -2
- package/src/href-client.ts +192 -57
- package/src/index.rsc.ts +177 -35
- package/src/index.ts +255 -71
- package/src/internal-debug.ts +9 -2
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +31 -99
- package/src/loader.ts +30 -12
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +41 -0
- package/src/prerender/param-hash.ts +16 -14
- package/src/prerender/store.ts +121 -21
- package/src/prerender.ts +460 -26
- package/src/redirect-origin.ts +100 -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 +198 -128
- package/src/root-error-boundary.tsx +42 -48
- package/src/route-content-wrapper.tsx +22 -77
- package/src/route-definition/dsl-helpers.ts +1116 -0
- package/src/route-definition/helper-factories.ts +88 -0
- package/src/route-definition/helpers-types.ts +505 -0
- package/src/route-definition/index.ts +54 -0
- package/src/route-definition/redirect.ts +134 -0
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-definition.ts +1 -1481
- package/src/route-map-builder.ts +82 -144
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +71 -45
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +263 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +54 -27
- package/src/router/find-match.ts +245 -0
- package/src/router/handler-context.ts +377 -125
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +59 -28
- package/src/router/lazy-includes.ts +254 -0
- package/src/router/loader-resolution.ts +421 -157
- package/src/router/logging.ts +106 -6
- package/src/router/manifest.ts +131 -57
- package/src/router/match-api.ts +167 -246
- package/src/router/match-context.ts +4 -24
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +117 -93
- package/src/router/match-middleware/cache-lookup.ts +297 -150
- package/src/router/match-middleware/cache-store.ts +123 -51
- package/src/router/match-middleware/intercept-resolution.ts +44 -43
- package/src/router/match-middleware/segment-resolution.ts +64 -22
- package/src/router/match-pipelines.ts +11 -87
- package/src/router/match-result.ts +121 -50
- package/src/router/metrics.ts +219 -28
- package/src/router/middleware-types.ts +93 -0
- package/src/router/middleware.ts +505 -441
- 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 +311 -142
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +547 -0
- package/src/router/preview-match.ts +102 -0
- package/src/router/request-classification.ts +278 -0
- package/src/router/revalidation.ts +203 -62
- package/src/router/route-snapshot.ts +246 -0
- package/src/router/router-context.ts +45 -48
- package/src/router/router-interfaces.ts +554 -0
- package/src/router/router-options.ts +779 -0
- package/src/router/router-registry.ts +21 -0
- package/src/router/segment-resolution/fresh.ts +772 -0
- package/src/router/segment-resolution/helpers.ts +348 -0
- package/src/router/segment-resolution/loader-cache.ts +250 -0
- package/src/router/segment-resolution/loader-mask.ts +44 -0
- package/src/router/segment-resolution/revalidation.ts +1331 -0
- package/src/router/segment-resolution/static-store.ts +81 -0
- 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 +25 -1354
- package/src/router/segment-wrappers.ts +292 -0
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +261 -0
- package/src/router/telemetry.ts +377 -0
- package/src/router/timeout.ts +128 -0
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +240 -61
- package/src/router/types.ts +23 -70
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +781 -2378
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +46 -0
- package/src/rsc/handler.ts +905 -1142
- package/src/rsc/helpers.ts +275 -19
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +305 -0
- package/src/rsc/manifest-init.ts +77 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +155 -0
- package/src/rsc/progressive-enhancement.ts +502 -0
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +104 -0
- package/src/rsc/response-route-handler.ts +257 -0
- package/src/rsc/rsc-rendering.ts +337 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +522 -0
- package/src/rsc/shell-capture.ts +439 -0
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +95 -12
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +99 -82
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +349 -134
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +452 -85
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +310 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +123 -42
- package/src/server/live.ts +130 -0
- package/src/server/loader-registry.ts +51 -100
- package/src/server/request-context.ts +842 -157
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +412 -136
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +45 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +701 -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 +40 -72
- package/src/theme/ThemeScript.tsx +12 -14
- package/src/theme/constants.ts +57 -15
- package/src/theme/index.ts +3 -20
- package/src/theme/theme-context.ts +5 -35
- package/src/theme/theme-script.ts +43 -39
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +123 -0
- package/src/types/cache-types.ts +207 -0
- package/src/types/error-types.ts +132 -0
- package/src/types/global-namespace.ts +113 -0
- package/src/types/handler-context.ts +839 -0
- package/src/types/index.ts +81 -0
- package/src/types/loader-types.ts +212 -0
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +138 -0
- package/src/types/route-entry.ts +114 -0
- package/src/types/segments.ts +271 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +162 -0
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +43 -0
- package/src/urls/path-helper-types.ts +413 -0
- package/src/urls/path-helper.ts +275 -0
- package/src/urls/pattern-types.ts +124 -0
- package/src/urls/response-types.ts +109 -0
- package/src/urls/type-extraction.ts +316 -0
- package/src/urls/urls-function.ts +80 -0
- package/src/urls.ts +1 -1341
- package/src/use-loader.tsx +406 -141
- 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 +182 -0
- package/src/vite/discovery/discover-routers.ts +389 -0
- 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 +467 -0
- package/src/vite/discovery/route-types-writer.ts +214 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +161 -0
- package/src/vite/discovery/virtual-module-codegen.ts +183 -0
- package/src/vite/index.ts +23 -2255
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +303 -0
- package/src/vite/plugins/cjs-to-esm.ts +90 -0
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- package/src/vite/plugins/client-ref-hashing.ts +118 -0
- 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/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
- package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +805 -0
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +313 -0
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +94 -0
- package/src/vite/plugins/version-plugin.ts +263 -0
- package/src/vite/plugins/virtual-entries.ts +234 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +560 -0
- package/src/vite/router-discovery.ts +1638 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +132 -0
- 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 +15 -0
- package/src/vite/utils/package-resolution.ts +89 -0
- package/src/vite/utils/prerender-utils.ts +249 -0
- package/src/vite/utils/shared-utils.ts +269 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/react/use-client-cache.ts +0 -56
- package/src/browser/request-controller.ts +0 -164
- package/src/browser/shallow.ts +0 -35
- package/src/cache/memory-store.ts +0 -253
- package/src/handles/index.ts +0 -6
- package/src/href-context.ts +0 -33
- package/src/network-error-thrower.tsx +0 -21
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- package/src/vite/package-resolution.ts +0 -125
- package/src/vite/virtual-entries.ts +0 -114
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readFileSync,
|
|
3
|
+
writeFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
unlinkSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import {
|
|
9
|
+
join,
|
|
10
|
+
dirname,
|
|
11
|
+
resolve,
|
|
12
|
+
sep,
|
|
13
|
+
basename as pathBasename,
|
|
14
|
+
} from "node:path";
|
|
15
|
+
import ts from "typescript";
|
|
16
|
+
import { generateRouteTypesSource } from "./codegen.js";
|
|
17
|
+
import type { ScanFilter } from "./scan-filter.js";
|
|
18
|
+
import { firstCodeMatchIndex } from "./source-scan.js";
|
|
19
|
+
import {
|
|
20
|
+
resolveImportedVariable,
|
|
21
|
+
resolveImportPath,
|
|
22
|
+
buildCombinedRouteMapWithSearch,
|
|
23
|
+
type UnresolvableInclude,
|
|
24
|
+
} from "./include-resolution.js";
|
|
25
|
+
import { findUrlsVariableNames } from "./per-module-writer.js";
|
|
26
|
+
import { isAutoGeneratedRouteName } from "../../route-name.js";
|
|
27
|
+
|
|
28
|
+
function countPublicRouteEntries(source: string): number {
|
|
29
|
+
const matches =
|
|
30
|
+
source.matchAll(/^\s+(?:"([^"]+)"|([a-zA-Z_$][^:]*)):\s*["{]/gm) ?? [];
|
|
31
|
+
let count = 0;
|
|
32
|
+
for (const match of matches) {
|
|
33
|
+
const routeName = match[1] || match[2];
|
|
34
|
+
if (routeName && !isAutoGeneratedRouteName(routeName.trim())) {
|
|
35
|
+
count++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return count;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
|
|
42
|
+
// Global variant for the code-region scan (firstCodeMatchIndex sets lastIndex).
|
|
43
|
+
const ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
|
|
44
|
+
|
|
45
|
+
function isRoutableSourceFile(name: string): boolean {
|
|
46
|
+
return (
|
|
47
|
+
(name.endsWith(".ts") ||
|
|
48
|
+
name.endsWith(".tsx") ||
|
|
49
|
+
name.endsWith(".js") ||
|
|
50
|
+
name.endsWith(".jsx")) &&
|
|
51
|
+
!name.includes(".gen.") &&
|
|
52
|
+
!name.includes(".test.") &&
|
|
53
|
+
!name.includes(".spec.")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isExcludedScanDir(name: string): boolean {
|
|
58
|
+
return (
|
|
59
|
+
name === "node_modules" ||
|
|
60
|
+
name === "dist" ||
|
|
61
|
+
name === "coverage" ||
|
|
62
|
+
name === "__tests__" ||
|
|
63
|
+
name === "__mocks__" ||
|
|
64
|
+
name.startsWith(".")
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Recursively collect source files whose code contains `pattern` (a comment- or
|
|
70
|
+
* string-only mention is ignored via firstCodeMatchIndex). Shared by createRouter
|
|
71
|
+
* and createHostRouter discovery, which differ only in the call pattern and
|
|
72
|
+
* `stopAtMatchDir`: createRouter treats a directory containing a match as a router
|
|
73
|
+
* root and stops descending it; createHostRouter descends the whole tree (the host
|
|
74
|
+
* entry sits above the sub-app router roots).
|
|
75
|
+
*
|
|
76
|
+
* `pattern` is the non-global tester (no lastIndex state); `patternG` is its global
|
|
77
|
+
* twin for the code-region scan.
|
|
78
|
+
*/
|
|
79
|
+
function findCallSiteFilesRecursive(
|
|
80
|
+
dir: string,
|
|
81
|
+
pattern: RegExp,
|
|
82
|
+
patternG: RegExp,
|
|
83
|
+
stopAtMatchDir: boolean,
|
|
84
|
+
filter: ScanFilter | undefined,
|
|
85
|
+
results: string[],
|
|
86
|
+
): void {
|
|
87
|
+
let entries;
|
|
88
|
+
try {
|
|
89
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.warn(
|
|
92
|
+
`[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
|
|
93
|
+
);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const childDirs: string[] = [];
|
|
98
|
+
const matchesInDir: string[] = [];
|
|
99
|
+
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const fullPath = join(dir, entry.name);
|
|
102
|
+
if (entry.isDirectory()) {
|
|
103
|
+
if (!isExcludedScanDir(entry.name)) childDirs.push(fullPath);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!isRoutableSourceFile(entry.name)) continue;
|
|
108
|
+
if (filter && !filter(fullPath)) continue;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const source = readFileSync(fullPath, "utf-8");
|
|
112
|
+
// Fast path: most files contain no `createRouter(` at all, so the cheap
|
|
113
|
+
// raw regex short-circuits before the code-region scan. Only a file that
|
|
114
|
+
// mentions the token (real call OR a comment/string mention) is rescanned
|
|
115
|
+
// over code regions — allocation-free, never building a stripped copy —
|
|
116
|
+
// so a mention inside a comment or string is not mistaken for a real
|
|
117
|
+
// router file (which previously triggered a spurious "Multiple routers
|
|
118
|
+
// found" error).
|
|
119
|
+
if (pattern.test(source) && firstCodeMatchIndex(source, patternG) >= 0) {
|
|
120
|
+
matchesInDir.push(fullPath);
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
results.push(...matchesInDir);
|
|
128
|
+
|
|
129
|
+
// createRouter (stopAtMatchDir): a directory that contains a match is a router
|
|
130
|
+
// root, so deeper directories are skipped. createHostRouter: always descend.
|
|
131
|
+
if (!stopAtMatchDir || matchesInDir.length === 0) {
|
|
132
|
+
for (const childDir of childDirs) {
|
|
133
|
+
findCallSiteFilesRecursive(
|
|
134
|
+
childDir,
|
|
135
|
+
pattern,
|
|
136
|
+
patternG,
|
|
137
|
+
stopAtMatchDir,
|
|
138
|
+
filter,
|
|
139
|
+
results,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function findNestedRouterConflict(
|
|
146
|
+
routerFiles: string[],
|
|
147
|
+
): { ancestor: string; nested: string } | null {
|
|
148
|
+
const routerDirs = [
|
|
149
|
+
...new Set(routerFiles.map((filePath) => dirname(resolve(filePath)))),
|
|
150
|
+
].sort((a, b) => a.length - b.length);
|
|
151
|
+
|
|
152
|
+
for (let i = 0; i < routerDirs.length; i++) {
|
|
153
|
+
const ancestorDir = routerDirs[i];
|
|
154
|
+
const prefix = ancestorDir.endsWith(sep)
|
|
155
|
+
? ancestorDir
|
|
156
|
+
: `${ancestorDir}${sep}`;
|
|
157
|
+
for (let j = i + 1; j < routerDirs.length; j++) {
|
|
158
|
+
const nestedDir = routerDirs[j];
|
|
159
|
+
if (!nestedDir.startsWith(prefix)) continue;
|
|
160
|
+
const ancestorFile = routerFiles.find(
|
|
161
|
+
(filePath) => dirname(resolve(filePath)) === ancestorDir,
|
|
162
|
+
);
|
|
163
|
+
const nestedFile = routerFiles.find(
|
|
164
|
+
(filePath) => dirname(resolve(filePath)) === nestedDir,
|
|
165
|
+
);
|
|
166
|
+
if (ancestorFile && nestedFile) {
|
|
167
|
+
return { ancestor: ancestorFile, nested: nestedFile };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function formatNestedRouterConflictError(
|
|
176
|
+
conflict: { ancestor: string; nested: string },
|
|
177
|
+
prefix = "[rango]",
|
|
178
|
+
): string {
|
|
179
|
+
return (
|
|
180
|
+
`${prefix} Nested router roots are not supported.\n` +
|
|
181
|
+
`Router root: ${conflict.ancestor}\n` +
|
|
182
|
+
`Nested router: ${conflict.nested}\n` +
|
|
183
|
+
`Move the nested router into a sibling directory or configure it as a separate app root.`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// Router file URL extraction
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Result of extracting URL patterns from a router file.
|
|
193
|
+
* - "variable": a named variable reference (e.g., `.routes(patterns)` or `urls: patterns`)
|
|
194
|
+
* - "inline": an inline builder function (e.g., `.routes(({ path }) => [...])` or `urls: ({ path }) => [...]`)
|
|
195
|
+
*/
|
|
196
|
+
export type UrlsExtractionResult =
|
|
197
|
+
| { kind: "variable"; name: string }
|
|
198
|
+
| { kind: "inline"; block: string };
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Extract the url patterns from a router file using AST.
|
|
202
|
+
* Detects four patterns:
|
|
203
|
+
* 1. createRouter(...).routes(variableName)
|
|
204
|
+
* 2. createRouter({ urls: variableName, ... })
|
|
205
|
+
* 3. createRouter(...).routes(({ path, ... }) => [...])
|
|
206
|
+
* 4. createRouter({ urls: ({ path, ... }) => [...], ... })
|
|
207
|
+
* Returns either a variable name or an inline code block.
|
|
208
|
+
*/
|
|
209
|
+
export function extractUrlsFromRouter(
|
|
210
|
+
code: string,
|
|
211
|
+
): UrlsExtractionResult | null {
|
|
212
|
+
const sourceFile = ts.createSourceFile(
|
|
213
|
+
"router.tsx",
|
|
214
|
+
code,
|
|
215
|
+
ts.ScriptTarget.Latest,
|
|
216
|
+
true,
|
|
217
|
+
ts.ScriptKind.TSX,
|
|
218
|
+
);
|
|
219
|
+
let result: UrlsExtractionResult | null = null;
|
|
220
|
+
|
|
221
|
+
function isCreateRouterCall(node: ts.Node): boolean {
|
|
222
|
+
if (!ts.isCallExpression(node)) return false;
|
|
223
|
+
const callee = node.expression;
|
|
224
|
+
return ts.isIdentifier(callee) && callee.text === "createRouter";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** Check if a node is an arrow/function expression (inline builder). */
|
|
228
|
+
function isInlineBuilder(node: ts.Node): boolean {
|
|
229
|
+
return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Check if a .routes() call chains from createRouter(). */
|
|
233
|
+
function isRoutesOnCreateRouter(node: ts.CallExpression): boolean {
|
|
234
|
+
if (
|
|
235
|
+
!ts.isPropertyAccessExpression(node.expression) ||
|
|
236
|
+
node.expression.name.text !== "routes"
|
|
237
|
+
)
|
|
238
|
+
return false;
|
|
239
|
+
let inner: ts.Expression = node.expression.expression;
|
|
240
|
+
while (
|
|
241
|
+
ts.isCallExpression(inner) &&
|
|
242
|
+
ts.isPropertyAccessExpression(inner.expression)
|
|
243
|
+
) {
|
|
244
|
+
inner = inner.expression.expression;
|
|
245
|
+
}
|
|
246
|
+
return isCreateRouterCall(inner);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function visit(node: ts.Node) {
|
|
250
|
+
if (result) return;
|
|
251
|
+
|
|
252
|
+
// Pattern 1 & 3: createRouter(...).routes(variableName | builder)
|
|
253
|
+
if (
|
|
254
|
+
ts.isCallExpression(node) &&
|
|
255
|
+
node.arguments.length >= 1 &&
|
|
256
|
+
isRoutesOnCreateRouter(node)
|
|
257
|
+
) {
|
|
258
|
+
const arg = node.arguments[0];
|
|
259
|
+
if (ts.isIdentifier(arg)) {
|
|
260
|
+
result = { kind: "variable", name: arg.text };
|
|
261
|
+
} else if (isInlineBuilder(arg)) {
|
|
262
|
+
result = { kind: "inline", block: arg.getText(sourceFile) };
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Pattern 2 & 4: createRouter({ urls: variableName | builder, ... })
|
|
268
|
+
if (isCreateRouterCall(node)) {
|
|
269
|
+
const callExpr = node as ts.CallExpression;
|
|
270
|
+
for (const callArg of callExpr.arguments) {
|
|
271
|
+
if (ts.isObjectLiteralExpression(callArg)) {
|
|
272
|
+
for (const prop of callArg.properties) {
|
|
273
|
+
if (
|
|
274
|
+
ts.isPropertyAssignment(prop) &&
|
|
275
|
+
ts.isIdentifier(prop.name) &&
|
|
276
|
+
prop.name.text === "urls"
|
|
277
|
+
) {
|
|
278
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
279
|
+
result = { kind: "variable", name: prop.initializer.text };
|
|
280
|
+
} else if (isInlineBuilder(prop.initializer)) {
|
|
281
|
+
result = {
|
|
282
|
+
kind: "inline",
|
|
283
|
+
block: prop.initializer.getText(sourceFile),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
ts.forEachChild(node, visit);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
visit(sourceFile);
|
|
297
|
+
return result;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Extract the `basename` string literal from createRouter({ basename: "..." }).
|
|
302
|
+
* Returns the basename value or undefined if not present.
|
|
303
|
+
*/
|
|
304
|
+
export function extractBasenameFromRouter(code: string): string | undefined {
|
|
305
|
+
const sourceFile = ts.createSourceFile(
|
|
306
|
+
"router.tsx",
|
|
307
|
+
code,
|
|
308
|
+
ts.ScriptTarget.Latest,
|
|
309
|
+
true,
|
|
310
|
+
ts.ScriptKind.TSX,
|
|
311
|
+
);
|
|
312
|
+
let result: string | undefined;
|
|
313
|
+
|
|
314
|
+
function visit(node: ts.Node) {
|
|
315
|
+
if (result !== undefined) return;
|
|
316
|
+
if (
|
|
317
|
+
ts.isCallExpression(node) &&
|
|
318
|
+
ts.isIdentifier(node.expression) &&
|
|
319
|
+
node.expression.text === "createRouter"
|
|
320
|
+
) {
|
|
321
|
+
for (const arg of node.arguments) {
|
|
322
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
323
|
+
for (const prop of arg.properties) {
|
|
324
|
+
if (
|
|
325
|
+
ts.isPropertyAssignment(prop) &&
|
|
326
|
+
ts.isIdentifier(prop.name) &&
|
|
327
|
+
prop.name.text === "basename" &&
|
|
328
|
+
ts.isStringLiteral(prop.initializer)
|
|
329
|
+
) {
|
|
330
|
+
result = prop.initializer.text;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
ts.forEachChild(node, visit);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
visit(sourceFile);
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Apply a basename prefix to all route patterns in a result set. */
|
|
345
|
+
function applyBasenameToRoutes(
|
|
346
|
+
result: {
|
|
347
|
+
routes: Record<string, string>;
|
|
348
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
349
|
+
},
|
|
350
|
+
basename: string,
|
|
351
|
+
): {
|
|
352
|
+
routes: Record<string, string>;
|
|
353
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
354
|
+
} {
|
|
355
|
+
const prefixed: Record<string, string> = {};
|
|
356
|
+
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
357
|
+
if (pattern === "/") {
|
|
358
|
+
prefixed[name] = basename;
|
|
359
|
+
} else if (basename.endsWith("/") && pattern.startsWith("/")) {
|
|
360
|
+
prefixed[name] = basename + pattern.slice(1);
|
|
361
|
+
} else {
|
|
362
|
+
prefixed[name] = basename + pattern;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Filesystem path of the generated route-types file for a router source file.
|
|
369
|
+
// Native separators — matches the self-gen-tracking Map key the watcher compares.
|
|
370
|
+
export function genFileTsPath(sourceFile: string): string {
|
|
371
|
+
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
372
|
+
return join(dirname(sourceFile), `${base}.named-routes.gen.ts`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Search schemas for the gen file: prefer the runtime manifest's; when it omits
|
|
376
|
+
// them (some module-runner flows) fall back to static parsing filtered to the
|
|
377
|
+
// public route-name set. Returns the runtime value unchanged otherwise.
|
|
378
|
+
export function resolveSearchSchemas(
|
|
379
|
+
publicRouteNames: string[],
|
|
380
|
+
runtimeSchemas: Record<string, Record<string, string>> | undefined,
|
|
381
|
+
sourceFile: string,
|
|
382
|
+
): Record<string, Record<string, string>> | undefined {
|
|
383
|
+
if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
|
|
384
|
+
return runtimeSchemas;
|
|
385
|
+
}
|
|
386
|
+
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
387
|
+
if (Object.keys(staticParsed.searchSchemas).length === 0) {
|
|
388
|
+
return runtimeSchemas;
|
|
389
|
+
}
|
|
390
|
+
const filtered: Record<string, Record<string, string>> = {};
|
|
391
|
+
for (const name of publicRouteNames) {
|
|
392
|
+
const schema = staticParsed.searchSchemas[name];
|
|
393
|
+
if (schema) filtered[name] = schema;
|
|
394
|
+
}
|
|
395
|
+
return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Resolve routes and search schemas from a router source file by following the
|
|
400
|
+
* variable passed to `.routes(...)` or `urls: ...` in createRouter options,
|
|
401
|
+
* or by parsing an inline builder function directly.
|
|
402
|
+
*/
|
|
403
|
+
export function buildCombinedRouteMapForRouterFile(routerFilePath: string): {
|
|
404
|
+
routes: Record<string, string>;
|
|
405
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
406
|
+
} {
|
|
407
|
+
let routerSource: string;
|
|
408
|
+
try {
|
|
409
|
+
routerSource = readFileSync(routerFilePath, "utf-8");
|
|
410
|
+
} catch {
|
|
411
|
+
return { routes: {}, searchSchemas: {} };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const extraction = extractUrlsFromRouter(routerSource);
|
|
415
|
+
if (!extraction) {
|
|
416
|
+
return { routes: {}, searchSchemas: {} };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Detect basename from createRouter({ basename: "..." })
|
|
420
|
+
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
421
|
+
const basename = rawBasename
|
|
422
|
+
? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "")
|
|
423
|
+
: undefined;
|
|
424
|
+
|
|
425
|
+
let result: {
|
|
426
|
+
routes: Record<string, string>;
|
|
427
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// Inline builder: extract routes directly from the function body
|
|
431
|
+
if (extraction.kind === "inline") {
|
|
432
|
+
result = buildCombinedRouteMapWithSearch(
|
|
433
|
+
routerFilePath,
|
|
434
|
+
undefined,
|
|
435
|
+
undefined,
|
|
436
|
+
undefined,
|
|
437
|
+
extraction.block,
|
|
438
|
+
);
|
|
439
|
+
} else {
|
|
440
|
+
// Variable reference: follow imports or same-file declaration
|
|
441
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
442
|
+
if (imported) {
|
|
443
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
444
|
+
if (!targetFile) {
|
|
445
|
+
return { routes: {}, searchSchemas: {} };
|
|
446
|
+
}
|
|
447
|
+
result = buildCombinedRouteMapWithSearch(
|
|
448
|
+
targetFile,
|
|
449
|
+
imported.exportedName,
|
|
450
|
+
);
|
|
451
|
+
} else {
|
|
452
|
+
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Apply basename prefix to all extracted route patterns
|
|
457
|
+
if (basename) {
|
|
458
|
+
result = applyBasenameToRoutes(result, basename);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
465
|
+
// Unresolvable include detection (full include tree walk)
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Walk the full include tree starting from a router file and detect
|
|
470
|
+
* all includes that the static parser cannot resolve.
|
|
471
|
+
* Returns an array of diagnostics; empty means fully resolvable.
|
|
472
|
+
*/
|
|
473
|
+
export function detectUnresolvableIncludes(
|
|
474
|
+
routerFilePath: string,
|
|
475
|
+
): UnresolvableInclude[] {
|
|
476
|
+
const realPath = resolve(routerFilePath);
|
|
477
|
+
let source: string;
|
|
478
|
+
try {
|
|
479
|
+
source = readFileSync(realPath, "utf-8");
|
|
480
|
+
} catch {
|
|
481
|
+
return [];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Extract the urls source from the router file
|
|
485
|
+
const extraction = extractUrlsFromRouter(source);
|
|
486
|
+
if (!extraction) return [];
|
|
487
|
+
|
|
488
|
+
const diagnostics: UnresolvableInclude[] = [];
|
|
489
|
+
|
|
490
|
+
if (extraction.kind === "inline") {
|
|
491
|
+
// Inline builder: parse directly
|
|
492
|
+
buildCombinedRouteMapWithSearch(
|
|
493
|
+
realPath,
|
|
494
|
+
undefined,
|
|
495
|
+
new Set(),
|
|
496
|
+
diagnostics,
|
|
497
|
+
extraction.block,
|
|
498
|
+
);
|
|
499
|
+
return diagnostics;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Variable reference: resolve where it comes from
|
|
503
|
+
const imported = resolveImportedVariable(source, extraction.name);
|
|
504
|
+
let targetFile: string;
|
|
505
|
+
let exportedName: string | undefined;
|
|
506
|
+
|
|
507
|
+
if (imported) {
|
|
508
|
+
const resolved = resolveImportPath(imported.specifier, realPath);
|
|
509
|
+
if (!resolved) {
|
|
510
|
+
return [
|
|
511
|
+
{
|
|
512
|
+
pathPrefix: "/",
|
|
513
|
+
namePrefix: null,
|
|
514
|
+
reason: "file-not-found",
|
|
515
|
+
sourceFile: realPath,
|
|
516
|
+
detail: `import "${imported.specifier}" resolved to no file`,
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
}
|
|
520
|
+
targetFile = resolved;
|
|
521
|
+
exportedName = imported.exportedName;
|
|
522
|
+
} else {
|
|
523
|
+
// Same-file urls() definition
|
|
524
|
+
targetFile = realPath;
|
|
525
|
+
exportedName = extraction.name;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
buildCombinedRouteMapWithSearch(
|
|
529
|
+
targetFile,
|
|
530
|
+
exportedName,
|
|
531
|
+
new Set(),
|
|
532
|
+
diagnostics,
|
|
533
|
+
);
|
|
534
|
+
return diagnostics;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Walk the include tree for a standalone urls() module file and detect
|
|
539
|
+
* all unresolvable includes. Mirrors detectUnresolvableIncludes() but
|
|
540
|
+
* operates on urls() variable declarations instead of going through
|
|
541
|
+
* createRouter().
|
|
542
|
+
*/
|
|
543
|
+
export function detectUnresolvableIncludesForUrlsFile(
|
|
544
|
+
filePath: string,
|
|
545
|
+
): UnresolvableInclude[] {
|
|
546
|
+
const realPath = resolve(filePath);
|
|
547
|
+
let source: string;
|
|
548
|
+
try {
|
|
549
|
+
source = readFileSync(realPath, "utf-8");
|
|
550
|
+
} catch {
|
|
551
|
+
return [];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const varNames = findUrlsVariableNames(source);
|
|
555
|
+
if (varNames.length === 0) return [];
|
|
556
|
+
|
|
557
|
+
const diagnostics: UnresolvableInclude[] = [];
|
|
558
|
+
for (const varName of varNames) {
|
|
559
|
+
buildCombinedRouteMapWithSearch(realPath, varName, new Set(), diagnostics);
|
|
560
|
+
}
|
|
561
|
+
return diagnostics;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// ---------------------------------------------------------------------------
|
|
565
|
+
// Per-router named-routes.gen.ts writer
|
|
566
|
+
// ---------------------------------------------------------------------------
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Scan for files containing createRouter() and return their paths.
|
|
570
|
+
* Call once at startup; the result can be reused on subsequent watcher triggers.
|
|
571
|
+
*/
|
|
572
|
+
export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
|
|
573
|
+
const result: string[] = [];
|
|
574
|
+
findCallSiteFilesRecursive(
|
|
575
|
+
root,
|
|
576
|
+
ROUTER_CALL_PATTERN,
|
|
577
|
+
ROUTER_CALL_PATTERN_G,
|
|
578
|
+
true,
|
|
579
|
+
filter,
|
|
580
|
+
result,
|
|
581
|
+
);
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const HOST_ROUTER_CALL_PATTERN = /\bcreateHostRouter\s*[<(]/;
|
|
586
|
+
const HOST_ROUTER_CALL_PATTERN_G = /\bcreateHostRouter\s*[<(]/g;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Scan for files containing createHostRouter() and return their paths. Unlike
|
|
590
|
+
* findRouterFiles, this does NOT stop at the first router-root directory -- a host
|
|
591
|
+
* entry typically sits above the sub-app router roots, so the whole tree is scanned.
|
|
592
|
+
*/
|
|
593
|
+
export function findHostRouterFiles(
|
|
594
|
+
root: string,
|
|
595
|
+
filter?: ScanFilter,
|
|
596
|
+
): string[] {
|
|
597
|
+
const result: string[] = [];
|
|
598
|
+
findCallSiteFilesRecursive(
|
|
599
|
+
root,
|
|
600
|
+
HOST_ROUTER_CALL_PATTERN,
|
|
601
|
+
HOST_ROUTER_CALL_PATTERN_G,
|
|
602
|
+
false,
|
|
603
|
+
filter,
|
|
604
|
+
result,
|
|
605
|
+
);
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Write named-routes.gen.ts files from static source parsing.
|
|
611
|
+
* Dev-only: provides initial .gen.ts files for IDE types before runtime
|
|
612
|
+
* discovery runs. Must NOT be called during production builds -- runtime
|
|
613
|
+
* discovery in buildStart produces the definitive file.
|
|
614
|
+
*/
|
|
615
|
+
export function writeCombinedRouteTypes(
|
|
616
|
+
root: string,
|
|
617
|
+
knownRouterFiles?: string[],
|
|
618
|
+
opts?: {
|
|
619
|
+
preserveIfLarger?: boolean;
|
|
620
|
+
onWrite?: (outPath: string, content: string) => void;
|
|
621
|
+
},
|
|
622
|
+
): void {
|
|
623
|
+
// Delete old combined named-routes.gen.ts if it exists (stale from older versions)
|
|
624
|
+
try {
|
|
625
|
+
const oldCombinedPath = join(root, "src", "named-routes.gen.ts");
|
|
626
|
+
if (existsSync(oldCombinedPath)) {
|
|
627
|
+
unlinkSync(oldCombinedPath);
|
|
628
|
+
console.log(
|
|
629
|
+
`[rango] Removed stale combined route types: ${oldCombinedPath}`,
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
} catch {}
|
|
633
|
+
|
|
634
|
+
const routerFilePaths = knownRouterFiles ?? findRouterFiles(root);
|
|
635
|
+
if (routerFilePaths.length === 0) return;
|
|
636
|
+
|
|
637
|
+
const nestedRouterConflict = findNestedRouterConflict(routerFilePaths);
|
|
638
|
+
if (nestedRouterConflict) {
|
|
639
|
+
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
for (const routerFilePath of routerFilePaths) {
|
|
643
|
+
const result = buildCombinedRouteMapForRouterFile(routerFilePath);
|
|
644
|
+
if (
|
|
645
|
+
Object.keys(result.routes).length === 0 &&
|
|
646
|
+
Object.keys(result.searchSchemas).length === 0
|
|
647
|
+
) {
|
|
648
|
+
// Check if the file even has a createRouter call — if not, skip entirely.
|
|
649
|
+
// If it does, fall through to write an empty placeholder below.
|
|
650
|
+
let routerSource: string;
|
|
651
|
+
try {
|
|
652
|
+
routerSource = readFileSync(routerFilePath, "utf-8");
|
|
653
|
+
} catch {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const outPath = genFileTsPath(routerFilePath);
|
|
660
|
+
const existing = existsSync(outPath)
|
|
661
|
+
? readFileSync(outPath, "utf-8")
|
|
662
|
+
: null;
|
|
663
|
+
|
|
664
|
+
if (Object.keys(result.routes).length === 0) {
|
|
665
|
+
if (!existing) {
|
|
666
|
+
const emptySource = generateRouteTypesSource({});
|
|
667
|
+
opts?.onWrite?.(outPath, emptySource);
|
|
668
|
+
writeFileSync(outPath, emptySource);
|
|
669
|
+
}
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const hasSearchSchemas = Object.keys(result.searchSchemas).length > 0;
|
|
674
|
+
const source = generateRouteTypesSource(
|
|
675
|
+
result.routes,
|
|
676
|
+
hasSearchSchemas ? result.searchSchemas : undefined,
|
|
677
|
+
);
|
|
678
|
+
if (existing !== source) {
|
|
679
|
+
if (opts?.preserveIfLarger && existing) {
|
|
680
|
+
const existingCount = countPublicRouteEntries(existing);
|
|
681
|
+
const newCount = Object.keys(result.routes).filter(
|
|
682
|
+
(name) => !isAutoGeneratedRouteName(name),
|
|
683
|
+
).length;
|
|
684
|
+
if (existingCount > newCount) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
opts?.onWrite?.(outPath, source);
|
|
689
|
+
writeFileSync(outPath, source);
|
|
690
|
+
console.log(
|
|
691
|
+
`[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`,
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|