@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.141
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 +293 -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 +2508 -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 +24 -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-snapshot.ts +368 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +222 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1113 -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 +173 -35
- package/src/index.ts +241 -73
- 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 +527 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +522 -0
- package/src/rsc/shell-capture.ts +897 -0
- package/src/rsc/shell-serve.ts +124 -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 +459 -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/loader-registry.ts +51 -100
- package/src/server/request-context.ts +848 -157
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +443 -135
- 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 +76 -98
- 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 +44 -0
- package/src/urls/path-helper-types.ts +413 -0
- package/src/urls/path-helper.ts +280 -0
- package/src/urls/pattern-types.ts +160 -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
package/dist/bin/rango.js
CHANGED
|
@@ -1,42 +1,169 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
2
16
|
|
|
3
|
-
// src/
|
|
4
|
-
|
|
17
|
+
// src/build/route-types/param-extraction.ts
|
|
18
|
+
function extractParamsFromPattern(pattern) {
|
|
19
|
+
const params = {};
|
|
20
|
+
const regex = /:([a-zA-Z_$][\w$]*)(?:\([^)]+\))?(\?)?/g;
|
|
21
|
+
let match;
|
|
22
|
+
while ((match = regex.exec(pattern)) !== null) {
|
|
23
|
+
params[match[1]] = match[2] ? "string?" : "string";
|
|
24
|
+
}
|
|
25
|
+
return Object.keys(params).length > 0 ? params : void 0;
|
|
26
|
+
}
|
|
27
|
+
function formatRouteEntry(key, pattern, _params, search) {
|
|
28
|
+
const hasSearch = search && Object.keys(search).length > 0;
|
|
29
|
+
if (!hasSearch) {
|
|
30
|
+
return ` ${key}: ${JSON.stringify(pattern)},`;
|
|
31
|
+
}
|
|
32
|
+
const searchBody = Object.entries(search).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
|
|
33
|
+
return ` ${key}: { path: ${JSON.stringify(pattern)}, search: { ${searchBody} } },`;
|
|
34
|
+
}
|
|
35
|
+
var init_param_extraction = __esm({
|
|
36
|
+
"src/build/route-types/param-extraction.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
}
|
|
39
|
+
});
|
|
5
40
|
|
|
6
|
-
// src/build/
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
41
|
+
// src/build/route-types/ast-helpers.ts
|
|
42
|
+
import ts from "typescript";
|
|
43
|
+
function getStringValue(node) {
|
|
44
|
+
if (ts.isStringLiteral(node)) return node.text;
|
|
45
|
+
if (ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function extractObjectStringProperties(node) {
|
|
49
|
+
const result = {};
|
|
50
|
+
for (const prop of node.properties) {
|
|
51
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
52
|
+
const key = ts.isIdentifier(prop.name) ? prop.name.text : ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
53
|
+
if (!key) continue;
|
|
54
|
+
const val = getStringValue(prop.initializer);
|
|
55
|
+
if (val !== null) result[key] = val;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
var init_ast_helpers = __esm({
|
|
60
|
+
"src/build/route-types/ast-helpers.ts"() {
|
|
61
|
+
"use strict";
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// src/build/route-types/ast-route-extraction.ts
|
|
66
|
+
import ts2 from "typescript";
|
|
67
|
+
function extractRoutesFromSource(code, sourceFileArg) {
|
|
68
|
+
const sourceFile = sourceFileArg ?? ts2.createSourceFile(
|
|
69
|
+
"input.tsx",
|
|
70
|
+
code,
|
|
71
|
+
ts2.ScriptTarget.Latest,
|
|
72
|
+
true,
|
|
73
|
+
ts2.ScriptKind.TSX
|
|
74
|
+
);
|
|
11
75
|
const routes = [];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
76
|
+
function visit(node) {
|
|
77
|
+
if (ts2.isCallExpression(node)) {
|
|
78
|
+
const callee = node.expression;
|
|
79
|
+
const isPath = ts2.isIdentifier(callee) && callee.text === "path" || ts2.isPropertyAccessExpression(callee) && ts2.isIdentifier(callee.expression) && callee.expression.text === "path";
|
|
80
|
+
if (isPath && node.arguments.length >= 1) {
|
|
81
|
+
const route = extractRouteFromCallExpression(node);
|
|
82
|
+
if (route) routes.push(route);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
ts2.forEachChild(node, visit);
|
|
17
86
|
}
|
|
87
|
+
visit(sourceFile);
|
|
18
88
|
return routes;
|
|
19
89
|
}
|
|
90
|
+
function extractRouteFromCallExpression(node) {
|
|
91
|
+
const patternNode = node.arguments[0];
|
|
92
|
+
const pattern = getStringValue(patternNode);
|
|
93
|
+
if (pattern === null) return null;
|
|
94
|
+
let name = null;
|
|
95
|
+
let search;
|
|
96
|
+
for (let i = 1; i < node.arguments.length; i++) {
|
|
97
|
+
const arg = node.arguments[i];
|
|
98
|
+
if (ts2.isObjectLiteralExpression(arg)) {
|
|
99
|
+
for (const prop of arg.properties) {
|
|
100
|
+
if (!ts2.isPropertyAssignment(prop)) continue;
|
|
101
|
+
const propName = ts2.isIdentifier(prop.name) ? prop.name.text : null;
|
|
102
|
+
if (propName === "name") {
|
|
103
|
+
name = getStringValue(prop.initializer);
|
|
104
|
+
} else if (propName === "search" && ts2.isObjectLiteralExpression(prop.initializer)) {
|
|
105
|
+
search = extractObjectStringProperties(prop.initializer);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!name) return null;
|
|
111
|
+
const params = extractParamsFromPattern(pattern);
|
|
112
|
+
return {
|
|
113
|
+
name,
|
|
114
|
+
pattern,
|
|
115
|
+
...params ? { params } : {},
|
|
116
|
+
...search && Object.keys(search).length > 0 ? { search } : {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
var init_ast_route_extraction = __esm({
|
|
120
|
+
"src/build/route-types/ast-route-extraction.ts"() {
|
|
121
|
+
"use strict";
|
|
122
|
+
init_ast_helpers();
|
|
123
|
+
init_param_extraction();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/route-name.ts
|
|
128
|
+
function isAutoGeneratedRouteName(name) {
|
|
129
|
+
return name.split(".").some((segment) => {
|
|
130
|
+
return segment.startsWith(AUTO_GENERATED_ROUTE_PREFIX) || segment.startsWith(INTERNAL_INCLUDE_SCOPE_PREFIX);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
var AUTO_GENERATED_ROUTE_PREFIX, INTERNAL_INCLUDE_SCOPE_PREFIX;
|
|
134
|
+
var init_route_name = __esm({
|
|
135
|
+
"src/route-name.ts"() {
|
|
136
|
+
"use strict";
|
|
137
|
+
AUTO_GENERATED_ROUTE_PREFIX = "$path_";
|
|
138
|
+
INTERNAL_INCLUDE_SCOPE_PREFIX = "$prefix_";
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// src/build/route-types/codegen.ts
|
|
20
143
|
function generatePerModuleTypesSource(routes) {
|
|
21
144
|
const valid = routes.filter(({ name }) => {
|
|
22
145
|
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
23
|
-
console.warn(
|
|
146
|
+
console.warn(
|
|
147
|
+
`[rango] Skipping route with invalid name: ${JSON.stringify(name)}`
|
|
148
|
+
);
|
|
24
149
|
return false;
|
|
25
150
|
}
|
|
26
151
|
return true;
|
|
27
152
|
});
|
|
28
153
|
const deduped = /* @__PURE__ */ new Map();
|
|
29
|
-
for (const { name, pattern, search } of valid) {
|
|
30
|
-
deduped.
|
|
154
|
+
for (const { name, pattern, params, search } of valid) {
|
|
155
|
+
if (deduped.has(name)) {
|
|
156
|
+
console.warn(
|
|
157
|
+
`[rango] Duplicate route name "${name}" \u2014 keeping first definition`
|
|
158
|
+
);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
deduped.set(name, { pattern, params, search });
|
|
31
162
|
}
|
|
32
163
|
const sorted = [...deduped.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
33
|
-
const body = sorted.map(([name, { pattern, search }]) => {
|
|
164
|
+
const body = sorted.map(([name, { pattern, params, search }]) => {
|
|
34
165
|
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
35
|
-
|
|
36
|
-
const searchBody = Object.entries(search).map(([k, v]) => `${k}: "${v}"`).join(", ");
|
|
37
|
-
return ` ${key}: { path: "${pattern}", search: { ${searchBody} } },`;
|
|
38
|
-
}
|
|
39
|
-
return ` ${key}: "${pattern}",`;
|
|
166
|
+
return formatRouteEntry(key, pattern, params, search);
|
|
40
167
|
}).join("\n");
|
|
41
168
|
return `// Auto-generated by @rangojs/router - do not edit
|
|
42
169
|
export const routes = {
|
|
@@ -45,243 +172,1976 @@ ${body}
|
|
|
45
172
|
export type routes = typeof routes;
|
|
46
173
|
`;
|
|
47
174
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
175
|
+
function generateRouteTypesSource(routeManifest, searchSchemas) {
|
|
176
|
+
const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b));
|
|
177
|
+
const filteredSearchSchemas = searchSchemas ? Object.fromEntries(
|
|
178
|
+
Object.entries(searchSchemas).filter(
|
|
179
|
+
([name]) => !isAutoGeneratedRouteName(name)
|
|
180
|
+
)
|
|
181
|
+
) : void 0;
|
|
182
|
+
const objectBody = entries.map(([name, pattern]) => {
|
|
183
|
+
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
184
|
+
const params = extractParamsFromPattern(pattern);
|
|
185
|
+
const search = filteredSearchSchemas?.[name];
|
|
186
|
+
return formatRouteEntry(key, pattern, params, search);
|
|
187
|
+
}).join("\n");
|
|
188
|
+
return `// Auto-generated by @rangojs/router - do not edit
|
|
189
|
+
export const NamedRoutes = {
|
|
190
|
+
${objectBody}
|
|
191
|
+
} as const;
|
|
192
|
+
|
|
193
|
+
// Aliased so the augmentation below does not pay a homomorphic mapped-type
|
|
194
|
+
// instantiation per route; \`as const\` already makes the members readonly.
|
|
195
|
+
type NamedRoutesShape = typeof NamedRoutes;
|
|
196
|
+
|
|
197
|
+
declare global {
|
|
198
|
+
namespace Rango {
|
|
199
|
+
interface GeneratedRouteMap extends NamedRoutesShape {}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
var init_codegen = __esm({
|
|
205
|
+
"src/build/route-types/codegen.ts"() {
|
|
206
|
+
"use strict";
|
|
207
|
+
init_param_extraction();
|
|
208
|
+
init_route_name();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// src/build/route-types/scan-filter.ts
|
|
213
|
+
import { join, relative } from "node:path";
|
|
214
|
+
import { readdirSync } from "node:fs";
|
|
215
|
+
import picomatch from "picomatch";
|
|
216
|
+
function findTsFiles(dir, filter) {
|
|
217
|
+
const results = [];
|
|
218
|
+
let entries;
|
|
219
|
+
try {
|
|
220
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.warn(
|
|
223
|
+
`[rango] Failed to scan directory ${dir}: ${err.message}`
|
|
224
|
+
);
|
|
225
|
+
return results;
|
|
226
|
+
}
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
const fullPath = join(dir, entry.name);
|
|
229
|
+
if (entry.isDirectory()) {
|
|
230
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".") || entry.name === "dist" || entry.name === "build" || entry.name === "coverage")
|
|
231
|
+
continue;
|
|
232
|
+
results.push(...findTsFiles(fullPath, filter));
|
|
233
|
+
} else if ((entry.name.endsWith(".ts") || entry.name.endsWith(".tsx") || entry.name.endsWith(".js") || entry.name.endsWith(".jsx")) && !entry.name.includes(".gen.")) {
|
|
234
|
+
if (filter && !filter(fullPath)) continue;
|
|
235
|
+
results.push(fullPath);
|
|
64
236
|
}
|
|
65
|
-
|
|
66
|
-
|
|
237
|
+
}
|
|
238
|
+
return results;
|
|
239
|
+
}
|
|
240
|
+
var init_scan_filter = __esm({
|
|
241
|
+
"src/build/route-types/scan-filter.ts"() {
|
|
242
|
+
"use strict";
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// src/build/route-types/include-resolution.ts
|
|
247
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
248
|
+
import { dirname, resolve } from "node:path";
|
|
249
|
+
import ts3 from "typescript";
|
|
250
|
+
function createScanMemo() {
|
|
251
|
+
return { files: /* @__PURE__ */ new Map(), blockSourceFiles: /* @__PURE__ */ new Map() };
|
|
252
|
+
}
|
|
253
|
+
function parseBlock(memo, block) {
|
|
254
|
+
if (memo) {
|
|
255
|
+
const cached = memo.blockSourceFiles.get(block);
|
|
256
|
+
if (cached) return cached;
|
|
257
|
+
}
|
|
258
|
+
const sf = ts3.createSourceFile(
|
|
259
|
+
"input.tsx",
|
|
260
|
+
block,
|
|
261
|
+
ts3.ScriptTarget.Latest,
|
|
262
|
+
true,
|
|
263
|
+
ts3.ScriptKind.TSX
|
|
264
|
+
);
|
|
265
|
+
if (memo) memo.blockSourceFiles.set(block, sf);
|
|
266
|
+
return sf;
|
|
267
|
+
}
|
|
268
|
+
function readSourceMemoized(memo, realPath) {
|
|
269
|
+
if (memo) {
|
|
270
|
+
const cached = memo.files.get(realPath);
|
|
271
|
+
if (cached !== void 0) return cached;
|
|
272
|
+
}
|
|
273
|
+
const source = readFileSync(realPath, "utf-8");
|
|
274
|
+
if (memo) memo.files.set(realPath, source);
|
|
275
|
+
return source;
|
|
276
|
+
}
|
|
277
|
+
function extractNamePrefixFromInclude(node) {
|
|
278
|
+
if (node.arguments.length >= 3) {
|
|
279
|
+
const thirdArg = node.arguments[2];
|
|
280
|
+
if (ts3.isObjectLiteralExpression(thirdArg)) {
|
|
281
|
+
for (const prop of thirdArg.properties) {
|
|
282
|
+
if (!ts3.isPropertyAssignment(prop)) continue;
|
|
283
|
+
const propName = ts3.isIdentifier(prop.name) ? prop.name.text : null;
|
|
284
|
+
if (propName === "name") {
|
|
285
|
+
return getStringValue(prop.initializer);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
67
288
|
}
|
|
68
|
-
value += code[pos];
|
|
69
|
-
pos++;
|
|
70
289
|
}
|
|
71
290
|
return null;
|
|
72
291
|
}
|
|
73
|
-
function
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
292
|
+
function thenSelectsNonDefaultMember(expr) {
|
|
293
|
+
const findThenCall = (e) => {
|
|
294
|
+
if (ts3.isCallExpression(e) && ts3.isPropertyAccessExpression(e.expression) && e.expression.name.text === "then") {
|
|
295
|
+
return e;
|
|
296
|
+
}
|
|
297
|
+
if (ts3.isCallExpression(e) && ts3.isPropertyAccessExpression(e.expression)) {
|
|
298
|
+
return findThenCall(e.expression.expression);
|
|
299
|
+
}
|
|
300
|
+
if (ts3.isPropertyAccessExpression(e)) return findThenCall(e.expression);
|
|
301
|
+
if (ts3.isParenthesizedExpression(e)) return findThenCall(e.expression);
|
|
302
|
+
if (ts3.isAwaitExpression(e)) return findThenCall(e.expression);
|
|
303
|
+
return null;
|
|
304
|
+
};
|
|
305
|
+
const thenCall = findThenCall(expr);
|
|
306
|
+
if (!thenCall || thenCall.arguments.length === 0) return false;
|
|
307
|
+
const cb = thenCall.arguments[0];
|
|
308
|
+
if (!ts3.isArrowFunction(cb) && !ts3.isFunctionExpression(cb)) return false;
|
|
309
|
+
let ret;
|
|
310
|
+
if (ts3.isBlock(cb.body)) {
|
|
311
|
+
for (const stmt of cb.body.statements) {
|
|
312
|
+
if (ts3.isReturnStatement(stmt) && stmt.expression) {
|
|
313
|
+
ret = stmt.expression;
|
|
314
|
+
break;
|
|
81
315
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
ret = cb.body;
|
|
319
|
+
}
|
|
320
|
+
if (!ret) return false;
|
|
321
|
+
if (ts3.isPropertyAccessExpression(ret)) {
|
|
322
|
+
return ret.name.text !== "default";
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
function extractDynamicImportSpecifier(node) {
|
|
327
|
+
let body;
|
|
328
|
+
if (ts3.isArrowFunction(node) || ts3.isFunctionExpression(node)) {
|
|
329
|
+
body = node.body;
|
|
330
|
+
}
|
|
331
|
+
if (!body) return null;
|
|
332
|
+
let expr;
|
|
333
|
+
if (ts3.isBlock(body)) {
|
|
334
|
+
for (const stmt of body.statements) {
|
|
335
|
+
if (ts3.isReturnStatement(stmt) && stmt.expression) {
|
|
336
|
+
expr = stmt.expression;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
expr = body;
|
|
342
|
+
}
|
|
343
|
+
if (!expr) return null;
|
|
344
|
+
const findImportCall = (e) => {
|
|
345
|
+
if (ts3.isCallExpression(e) && e.expression.kind === ts3.SyntaxKind.ImportKeyword) {
|
|
346
|
+
return e;
|
|
347
|
+
}
|
|
348
|
+
if (ts3.isCallExpression(e) && ts3.isPropertyAccessExpression(e.expression)) {
|
|
349
|
+
return findImportCall(e.expression.expression);
|
|
350
|
+
}
|
|
351
|
+
if (ts3.isPropertyAccessExpression(e)) return findImportCall(e.expression);
|
|
352
|
+
if (ts3.isParenthesizedExpression(e)) return findImportCall(e.expression);
|
|
353
|
+
if (ts3.isAwaitExpression(e)) return findImportCall(e.expression);
|
|
354
|
+
return null;
|
|
355
|
+
};
|
|
356
|
+
const importCall = findImportCall(expr);
|
|
357
|
+
if (!importCall || importCall.arguments.length === 0) return null;
|
|
358
|
+
if (thenSelectsNonDefaultMember(expr)) return null;
|
|
359
|
+
return getStringValue(importCall.arguments[0]);
|
|
360
|
+
}
|
|
361
|
+
function resolveDefaultExportTarget(code, sourceFile) {
|
|
362
|
+
let result = null;
|
|
363
|
+
function visit(node) {
|
|
364
|
+
if (result) return;
|
|
365
|
+
if (ts3.isExportAssignment(node) && !node.isExportEquals) {
|
|
366
|
+
const expr = node.expression;
|
|
367
|
+
if (ts3.isIdentifier(expr)) {
|
|
368
|
+
result = { variableName: expr.text };
|
|
369
|
+
} else if (ts3.isCallExpression(expr)) {
|
|
370
|
+
const callee = expr.expression;
|
|
371
|
+
if (ts3.isIdentifier(callee) && callee.text === "urls") {
|
|
372
|
+
result = { inlineBlock: expr.getText(sourceFile) };
|
|
95
373
|
}
|
|
96
|
-
continue;
|
|
97
374
|
}
|
|
98
|
-
|
|
375
|
+
return;
|
|
99
376
|
}
|
|
100
|
-
|
|
377
|
+
ts3.forEachChild(node, visit);
|
|
101
378
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
379
|
+
visit(sourceFile);
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
function extractIncludesWithDiagnostics(code, sourceFileArg) {
|
|
383
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
384
|
+
"input.tsx",
|
|
385
|
+
code,
|
|
386
|
+
ts3.ScriptTarget.Latest,
|
|
387
|
+
true,
|
|
388
|
+
ts3.ScriptKind.TSX
|
|
389
|
+
);
|
|
390
|
+
const resolved = [];
|
|
391
|
+
const unresolvable = [];
|
|
392
|
+
function visit(node) {
|
|
393
|
+
if (ts3.isCallExpression(node)) {
|
|
394
|
+
const callee = node.expression;
|
|
395
|
+
if (ts3.isIdentifier(callee) && callee.text === "include") {
|
|
396
|
+
if (node.arguments.length < 2) {
|
|
397
|
+
ts3.forEachChild(node, visit);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const pathPrefix = getStringValue(node.arguments[0]);
|
|
401
|
+
if (pathPrefix === null) {
|
|
402
|
+
ts3.forEachChild(node, visit);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const secondArg = node.arguments[1];
|
|
406
|
+
const namePrefix = extractNamePrefixFromInclude(node);
|
|
407
|
+
const dynamicImportSpec = extractDynamicImportSpecifier(secondArg);
|
|
408
|
+
if (ts3.isIdentifier(secondArg)) {
|
|
409
|
+
resolved.push({
|
|
410
|
+
pathPrefix,
|
|
411
|
+
variableName: secondArg.text,
|
|
412
|
+
namePrefix
|
|
413
|
+
});
|
|
414
|
+
} else if (dynamicImportSpec !== null) {
|
|
415
|
+
resolved.push({
|
|
416
|
+
pathPrefix,
|
|
417
|
+
moduleSpecifier: dynamicImportSpec,
|
|
418
|
+
namePrefix
|
|
419
|
+
});
|
|
420
|
+
} else if (ts3.isCallExpression(secondArg)) {
|
|
421
|
+
const callText = secondArg.expression.getText(sourceFile);
|
|
422
|
+
unresolvable.push({
|
|
423
|
+
pathPrefix,
|
|
424
|
+
namePrefix,
|
|
425
|
+
reason: "factory-call",
|
|
426
|
+
detail: `${callText}()`
|
|
427
|
+
});
|
|
428
|
+
} else {
|
|
429
|
+
unresolvable.push({
|
|
430
|
+
pathPrefix,
|
|
431
|
+
namePrefix,
|
|
432
|
+
reason: "dynamic-expression",
|
|
433
|
+
detail: secondArg.getText(sourceFile)
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
107
437
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
pos++;
|
|
126
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
127
|
-
return readString(code, pos);
|
|
128
|
-
}
|
|
129
|
-
function matchesSearchColon(code, pos) {
|
|
130
|
-
if (code.slice(pos, pos + 6) !== "search") return false;
|
|
131
|
-
if (pos > 0 && /\w/.test(code[pos - 1])) return false;
|
|
132
|
-
const afterSearch = pos + 6;
|
|
133
|
-
if (afterSearch < code.length && /\w/.test(code[afterSearch])) return false;
|
|
134
|
-
let checkPos = afterSearch;
|
|
135
|
-
while (checkPos < code.length && isWhitespace(code[checkPos])) checkPos++;
|
|
136
|
-
return code[checkPos] === ":";
|
|
137
|
-
}
|
|
138
|
-
function extractSearchValue(code, pos) {
|
|
139
|
-
pos += 6;
|
|
140
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
141
|
-
pos++;
|
|
142
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
143
|
-
if (code[pos] !== "{") return null;
|
|
144
|
-
pos++;
|
|
145
|
-
const schema = {};
|
|
146
|
-
while (pos < code.length) {
|
|
147
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
148
|
-
if (code[pos] === "}") return { value: schema, end: pos + 1 };
|
|
149
|
-
if (code[pos] === ",") {
|
|
150
|
-
pos++;
|
|
151
|
-
continue;
|
|
438
|
+
ts3.forEachChild(node, visit);
|
|
439
|
+
}
|
|
440
|
+
visit(sourceFile);
|
|
441
|
+
return { resolved, unresolvable };
|
|
442
|
+
}
|
|
443
|
+
function resolveImportedVariable(code, localName) {
|
|
444
|
+
const importRegex = /import\s*(?:[\w$]+\s*,\s*)?\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
445
|
+
let match;
|
|
446
|
+
while ((match = importRegex.exec(code)) !== null) {
|
|
447
|
+
const imports = match[1];
|
|
448
|
+
const specifier = match[2];
|
|
449
|
+
const parts = imports.split(",").map((s) => s.trim()).filter(Boolean);
|
|
450
|
+
for (const part of parts) {
|
|
451
|
+
const asMatch = part.match(/^(\w+)\s+as\s+(\w+)$/);
|
|
452
|
+
if (asMatch && asMatch[2] === localName)
|
|
453
|
+
return { specifier, exportedName: asMatch[1] };
|
|
454
|
+
if (part === localName) return { specifier, exportedName: localName };
|
|
152
455
|
}
|
|
153
|
-
let key;
|
|
154
|
-
if (code[pos] === '"' || code[pos] === "'") {
|
|
155
|
-
const keyStr = readString(code, pos);
|
|
156
|
-
if (!keyStr) return null;
|
|
157
|
-
key = keyStr.value;
|
|
158
|
-
pos = keyStr.end;
|
|
159
|
-
} else {
|
|
160
|
-
const keyStart = pos;
|
|
161
|
-
while (pos < code.length && /[\w$]/.test(code[pos])) pos++;
|
|
162
|
-
if (pos === keyStart) return null;
|
|
163
|
-
key = code.slice(keyStart, pos);
|
|
164
|
-
}
|
|
165
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
166
|
-
if (code[pos] !== ":") return null;
|
|
167
|
-
pos++;
|
|
168
|
-
while (pos < code.length && isWhitespace(code[pos])) pos++;
|
|
169
|
-
const valStr = readString(code, pos);
|
|
170
|
-
if (!valStr) return null;
|
|
171
|
-
schema[key] = valStr.value;
|
|
172
|
-
pos = valStr.end;
|
|
173
456
|
}
|
|
174
457
|
return null;
|
|
175
458
|
}
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
459
|
+
function resolveImportPath(importSpec, fromFile) {
|
|
460
|
+
if (!importSpec.startsWith(".")) return null;
|
|
461
|
+
const dir = dirname(fromFile);
|
|
462
|
+
let base = importSpec;
|
|
463
|
+
if (base.endsWith(".js")) base = base.slice(0, -3);
|
|
464
|
+
else if (base.endsWith(".mjs")) base = base.slice(0, -4);
|
|
465
|
+
else if (base.endsWith(".jsx")) base = base.slice(0, -4);
|
|
466
|
+
const candidates = [
|
|
467
|
+
resolve(dir, base + ".ts"),
|
|
468
|
+
resolve(dir, base + ".tsx"),
|
|
469
|
+
resolve(dir, base + ".js"),
|
|
470
|
+
resolve(dir, base + ".jsx"),
|
|
471
|
+
resolve(dir, base + "/index.ts"),
|
|
472
|
+
resolve(dir, base + "/index.tsx"),
|
|
473
|
+
resolve(dir, base + "/index.js"),
|
|
474
|
+
resolve(dir, base + "/index.jsx")
|
|
475
|
+
];
|
|
476
|
+
for (const candidate of candidates) {
|
|
477
|
+
if (existsSync(candidate)) return candidate;
|
|
478
|
+
}
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
function extractUrlsBlockForVariable(code, varName, sourceFileArg) {
|
|
482
|
+
const sourceFile = sourceFileArg ?? ts3.createSourceFile(
|
|
483
|
+
"input.tsx",
|
|
484
|
+
code,
|
|
485
|
+
ts3.ScriptTarget.Latest,
|
|
486
|
+
true,
|
|
487
|
+
ts3.ScriptKind.TSX
|
|
488
|
+
);
|
|
489
|
+
let result = null;
|
|
490
|
+
function visit(node) {
|
|
491
|
+
if (result) return;
|
|
492
|
+
if (ts3.isVariableDeclaration(node) && ts3.isIdentifier(node.name) && node.name.text === varName && node.initializer && ts3.isCallExpression(node.initializer)) {
|
|
493
|
+
const callee = node.initializer.expression;
|
|
494
|
+
if (ts3.isIdentifier(callee) && callee.text === "urls") {
|
|
495
|
+
result = node.initializer.getText(sourceFile);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
190
498
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
499
|
+
ts3.forEachChild(node, visit);
|
|
500
|
+
}
|
|
501
|
+
visit(sourceFile);
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut, memo) {
|
|
505
|
+
const routeMap = {};
|
|
506
|
+
const blockSourceFile = parseBlock(memo, block);
|
|
507
|
+
const localRoutes = extractRoutesFromSource(block, blockSourceFile);
|
|
508
|
+
for (const { name, pattern, search } of localRoutes) {
|
|
509
|
+
routeMap[name] = pattern;
|
|
510
|
+
if (search && searchSchemasOut) {
|
|
511
|
+
searchSchemasOut[name] = search;
|
|
195
512
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
513
|
+
}
|
|
514
|
+
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
515
|
+
block,
|
|
516
|
+
blockSourceFile
|
|
517
|
+
);
|
|
518
|
+
if (diagnosticsOut) {
|
|
519
|
+
for (const entry of unresolvable) {
|
|
520
|
+
diagnosticsOut.push({ ...entry, sourceFile: filePath });
|
|
202
521
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
522
|
+
}
|
|
523
|
+
for (const inc of includes) {
|
|
524
|
+
const { pathPrefix, namePrefix } = inc;
|
|
525
|
+
let childResult;
|
|
526
|
+
if (inc.moduleSpecifier) {
|
|
527
|
+
const targetFile = resolveImportPath(inc.moduleSpecifier, filePath);
|
|
528
|
+
if (!targetFile) {
|
|
529
|
+
diagnosticsOut?.push({
|
|
530
|
+
pathPrefix,
|
|
531
|
+
namePrefix,
|
|
532
|
+
reason: "file-not-found",
|
|
533
|
+
sourceFile: filePath,
|
|
534
|
+
detail: `import("${inc.moduleSpecifier}") resolved to no file`
|
|
535
|
+
});
|
|
208
536
|
continue;
|
|
209
537
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
538
|
+
let targetSource;
|
|
539
|
+
try {
|
|
540
|
+
targetSource = readSourceMemoized(memo, resolve(targetFile));
|
|
541
|
+
} catch (err) {
|
|
542
|
+
diagnosticsOut?.push({
|
|
543
|
+
pathPrefix,
|
|
544
|
+
namePrefix,
|
|
545
|
+
reason: "file-not-found",
|
|
546
|
+
sourceFile: filePath,
|
|
547
|
+
detail: `import("${inc.moduleSpecifier}") resolved to "${targetFile}" but reading it failed: ${err?.message ?? String(err)}`
|
|
548
|
+
});
|
|
216
549
|
continue;
|
|
217
550
|
}
|
|
551
|
+
const def = resolveDefaultExportTarget(
|
|
552
|
+
targetSource,
|
|
553
|
+
parseBlock(memo, targetSource)
|
|
554
|
+
);
|
|
555
|
+
if (!def) {
|
|
556
|
+
diagnosticsOut?.push({
|
|
557
|
+
pathPrefix,
|
|
558
|
+
namePrefix,
|
|
559
|
+
reason: "unresolvable-import",
|
|
560
|
+
sourceFile: filePath,
|
|
561
|
+
detail: `import("${inc.moduleSpecifier}") has no resolvable \`export default urls(...)\``
|
|
562
|
+
});
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (def.inlineBlock) {
|
|
566
|
+
childResult = buildCombinedRouteMapWithSearch(
|
|
567
|
+
targetFile,
|
|
568
|
+
void 0,
|
|
569
|
+
visited,
|
|
570
|
+
diagnosticsOut,
|
|
571
|
+
def.inlineBlock,
|
|
572
|
+
memo
|
|
573
|
+
);
|
|
574
|
+
} else {
|
|
575
|
+
const variableName = def.variableName;
|
|
576
|
+
const resolved = resolveIncludedVariable({
|
|
577
|
+
variableName,
|
|
578
|
+
resolutionFile: targetFile,
|
|
579
|
+
resolutionSource: targetSource,
|
|
580
|
+
reportFile: filePath,
|
|
581
|
+
memo,
|
|
582
|
+
visited,
|
|
583
|
+
diagnosticsOut,
|
|
584
|
+
pathPrefix,
|
|
585
|
+
namePrefix,
|
|
586
|
+
fileNotFoundDetail: (specifier) => `import("${inc.moduleSpecifier}") default re-exports "${variableName}" from "${specifier}", which resolved to no file`,
|
|
587
|
+
notFoundDetail: () => `import("${inc.moduleSpecifier}") default "${variableName}" not found in its imports or same-file scope`
|
|
588
|
+
});
|
|
589
|
+
if (!resolved) continue;
|
|
590
|
+
childResult = resolved;
|
|
591
|
+
}
|
|
592
|
+
} else if (inc.variableName) {
|
|
593
|
+
const variableName = inc.variableName;
|
|
594
|
+
const resolved = resolveIncludedVariable({
|
|
595
|
+
variableName,
|
|
596
|
+
resolutionFile: filePath,
|
|
597
|
+
resolutionSource: fullSource,
|
|
598
|
+
reportFile: filePath,
|
|
599
|
+
memo,
|
|
600
|
+
visited,
|
|
601
|
+
diagnosticsOut,
|
|
602
|
+
pathPrefix,
|
|
603
|
+
namePrefix,
|
|
604
|
+
fileNotFoundDetail: (specifier) => `import "${specifier}" resolved to no file`,
|
|
605
|
+
notFoundDetail: () => `variable "${variableName}" not found in imports or same-file scope`
|
|
606
|
+
});
|
|
607
|
+
if (!resolved) continue;
|
|
608
|
+
childResult = resolved;
|
|
609
|
+
} else {
|
|
610
|
+
continue;
|
|
218
611
|
}
|
|
219
|
-
if (
|
|
220
|
-
pos = skipStringLiteral(code, pos);
|
|
612
|
+
if (namePrefix === null) {
|
|
221
613
|
continue;
|
|
222
614
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
615
|
+
for (const [name, pattern] of Object.entries(childResult.routes)) {
|
|
616
|
+
const prefixedName = namePrefix ? `${namePrefix}.${name}` : name;
|
|
617
|
+
let prefixedPattern;
|
|
618
|
+
if (pattern === "/") {
|
|
619
|
+
prefixedPattern = pathPrefix || "/";
|
|
620
|
+
} else if (pathPrefix.endsWith("/") && pattern.startsWith("/")) {
|
|
621
|
+
prefixedPattern = pathPrefix + pattern.slice(1);
|
|
622
|
+
} else {
|
|
623
|
+
prefixedPattern = pathPrefix + pattern;
|
|
624
|
+
}
|
|
625
|
+
routeMap[prefixedName] = prefixedPattern;
|
|
626
|
+
if (childResult.searchSchemas[name] && searchSchemasOut) {
|
|
627
|
+
searchSchemasOut[prefixedName] = childResult.searchSchemas[name];
|
|
628
|
+
}
|
|
629
|
+
}
|
|
226
630
|
}
|
|
227
|
-
|
|
228
|
-
return { name, pattern, ...search ? { search } : {} };
|
|
631
|
+
return routeMap;
|
|
229
632
|
}
|
|
230
|
-
function
|
|
231
|
-
const
|
|
232
|
-
|
|
633
|
+
function resolveIncludedVariable(opts) {
|
|
634
|
+
const {
|
|
635
|
+
variableName,
|
|
636
|
+
resolutionFile,
|
|
637
|
+
resolutionSource,
|
|
638
|
+
reportFile,
|
|
639
|
+
memo,
|
|
640
|
+
visited,
|
|
641
|
+
diagnosticsOut,
|
|
642
|
+
pathPrefix,
|
|
643
|
+
namePrefix
|
|
644
|
+
} = opts;
|
|
645
|
+
const imported = resolveImportedVariable(resolutionSource, variableName);
|
|
646
|
+
if (imported) {
|
|
647
|
+
const targetFile = resolveImportPath(imported.specifier, resolutionFile);
|
|
648
|
+
if (!targetFile) {
|
|
649
|
+
diagnosticsOut?.push({
|
|
650
|
+
pathPrefix,
|
|
651
|
+
namePrefix,
|
|
652
|
+
reason: "file-not-found",
|
|
653
|
+
sourceFile: reportFile,
|
|
654
|
+
detail: opts.fileNotFoundDetail(imported.specifier)
|
|
655
|
+
});
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
return buildCombinedRouteMapWithSearch(
|
|
659
|
+
targetFile,
|
|
660
|
+
imported.exportedName,
|
|
661
|
+
visited,
|
|
662
|
+
diagnosticsOut,
|
|
663
|
+
void 0,
|
|
664
|
+
memo
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
const sameFileBlock = extractUrlsBlockForVariable(
|
|
668
|
+
resolutionSource,
|
|
669
|
+
variableName,
|
|
670
|
+
parseBlock(memo, resolutionSource)
|
|
671
|
+
);
|
|
672
|
+
if (!sameFileBlock) {
|
|
673
|
+
diagnosticsOut?.push({
|
|
674
|
+
pathPrefix,
|
|
675
|
+
namePrefix,
|
|
676
|
+
reason: "unresolvable-import",
|
|
677
|
+
sourceFile: reportFile,
|
|
678
|
+
detail: opts.notFoundDetail()
|
|
679
|
+
});
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
return buildCombinedRouteMapWithSearch(
|
|
683
|
+
resolutionFile,
|
|
684
|
+
variableName,
|
|
685
|
+
visited,
|
|
686
|
+
diagnosticsOut,
|
|
687
|
+
void 0,
|
|
688
|
+
memo
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock, memo) {
|
|
692
|
+
visited = visited ?? /* @__PURE__ */ new Set();
|
|
693
|
+
memo = memo ?? createScanMemo();
|
|
694
|
+
const realPath = resolve(filePath);
|
|
695
|
+
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
696
|
+
if (visited.has(key)) {
|
|
697
|
+
console.warn(`[rango] Circular include detected, skipping: ${key}`);
|
|
698
|
+
return { routes: {}, searchSchemas: {} };
|
|
699
|
+
}
|
|
700
|
+
visited.add(key);
|
|
701
|
+
let source;
|
|
233
702
|
try {
|
|
234
|
-
|
|
235
|
-
} catch
|
|
236
|
-
|
|
237
|
-
return results;
|
|
703
|
+
source = readSourceMemoized(memo, realPath);
|
|
704
|
+
} catch {
|
|
705
|
+
return { routes: {}, searchSchemas: {} };
|
|
238
706
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
707
|
+
let block;
|
|
708
|
+
if (inlineBlock) {
|
|
709
|
+
block = inlineBlock;
|
|
710
|
+
} else if (variableName) {
|
|
711
|
+
const extracted = extractUrlsBlockForVariable(
|
|
712
|
+
source,
|
|
713
|
+
variableName,
|
|
714
|
+
parseBlock(memo, source)
|
|
715
|
+
);
|
|
716
|
+
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
717
|
+
block = extracted;
|
|
718
|
+
} else {
|
|
719
|
+
block = source;
|
|
720
|
+
}
|
|
721
|
+
const searchSchemas = {};
|
|
722
|
+
const routes = buildRouteMapFromBlock(
|
|
723
|
+
block,
|
|
724
|
+
source,
|
|
725
|
+
realPath,
|
|
726
|
+
visited,
|
|
727
|
+
searchSchemas,
|
|
728
|
+
diagnosticsOut,
|
|
729
|
+
memo
|
|
730
|
+
);
|
|
731
|
+
visited.delete(key);
|
|
732
|
+
return { routes, searchSchemas };
|
|
733
|
+
}
|
|
734
|
+
var init_include_resolution = __esm({
|
|
735
|
+
"src/build/route-types/include-resolution.ts"() {
|
|
736
|
+
"use strict";
|
|
737
|
+
init_ast_helpers();
|
|
738
|
+
init_ast_route_extraction();
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
// src/build/route-types/per-module-writer.ts
|
|
743
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "node:fs";
|
|
744
|
+
import ts4 from "typescript";
|
|
745
|
+
function findUrlsVariableNames(code) {
|
|
746
|
+
const sourceFile = ts4.createSourceFile(
|
|
747
|
+
"input.tsx",
|
|
748
|
+
code,
|
|
749
|
+
ts4.ScriptTarget.Latest,
|
|
750
|
+
true,
|
|
751
|
+
ts4.ScriptKind.TSX
|
|
752
|
+
);
|
|
753
|
+
const names = [];
|
|
754
|
+
function visit(node) {
|
|
755
|
+
if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.initializer && ts4.isCallExpression(node.initializer)) {
|
|
756
|
+
const callee = node.initializer.expression;
|
|
757
|
+
if (ts4.isIdentifier(callee) && callee.text === "urls") {
|
|
758
|
+
names.push(node.name.text);
|
|
759
|
+
}
|
|
247
760
|
}
|
|
761
|
+
ts4.forEachChild(node, visit);
|
|
248
762
|
}
|
|
249
|
-
|
|
763
|
+
visit(sourceFile);
|
|
764
|
+
return names;
|
|
250
765
|
}
|
|
251
766
|
function writePerModuleRouteTypesForFile(filePath) {
|
|
252
767
|
try {
|
|
253
|
-
const source =
|
|
768
|
+
const source = readFileSync2(filePath, "utf-8");
|
|
254
769
|
if (!source.includes("urls(")) return;
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
770
|
+
const varNames = findUrlsVariableNames(source);
|
|
771
|
+
let routes;
|
|
772
|
+
if (varNames.length > 0) {
|
|
773
|
+
routes = [];
|
|
774
|
+
const memo = createScanMemo();
|
|
775
|
+
for (const varName of varNames) {
|
|
776
|
+
const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
|
|
777
|
+
filePath,
|
|
778
|
+
varName,
|
|
779
|
+
void 0,
|
|
780
|
+
void 0,
|
|
781
|
+
void 0,
|
|
782
|
+
memo
|
|
783
|
+
);
|
|
784
|
+
for (const [name, pattern] of Object.entries(routeMap)) {
|
|
785
|
+
const params = extractParamsFromPattern(pattern);
|
|
786
|
+
routes.push({
|
|
787
|
+
name,
|
|
788
|
+
pattern,
|
|
789
|
+
...params ? { params } : {},
|
|
790
|
+
...searchSchemas[name] ? { search: searchSchemas[name] } : {}
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
routes = extractRoutesFromSource(source);
|
|
796
|
+
}
|
|
797
|
+
const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
|
|
798
|
+
if (routes.length === 0) {
|
|
799
|
+
if (varNames.length > 0 && !existsSync2(genPath)) {
|
|
800
|
+
writeFileSync(genPath, generatePerModuleTypesSource([]));
|
|
801
|
+
console.log(
|
|
802
|
+
`[rango] Generated route types (placeholder) -> ${genPath}`
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
258
807
|
const genSource = generatePerModuleTypesSource(routes);
|
|
259
|
-
const existing =
|
|
808
|
+
const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
|
|
260
809
|
if (existing !== genSource) {
|
|
261
810
|
writeFileSync(genPath, genSource);
|
|
262
|
-
console.log(`[
|
|
811
|
+
console.log(`[rango] Generated route types -> ${genPath}`);
|
|
263
812
|
}
|
|
264
813
|
} catch (err) {
|
|
265
|
-
console.warn(
|
|
814
|
+
console.warn(
|
|
815
|
+
`[rango] Failed to generate route types for ${filePath}: ${err.message}`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
var init_per_module_writer = __esm({
|
|
820
|
+
"src/build/route-types/per-module-writer.ts"() {
|
|
821
|
+
"use strict";
|
|
822
|
+
init_param_extraction();
|
|
823
|
+
init_ast_route_extraction();
|
|
824
|
+
init_codegen();
|
|
825
|
+
init_include_resolution();
|
|
826
|
+
init_scan_filter();
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
// src/build/route-types/source-scan.ts
|
|
831
|
+
function isLineTerminator(ch) {
|
|
832
|
+
const c = ch.charCodeAt(0);
|
|
833
|
+
return c === 10 || c === 13 || c === 8232 || c === 8233;
|
|
834
|
+
}
|
|
835
|
+
function isRegexPositionAt(code, slashPos, prevChar) {
|
|
836
|
+
if (prevChar === void 0) return true;
|
|
837
|
+
if (prevChar === ")" || prevChar === "]" || prevChar === "}") return false;
|
|
838
|
+
if (!/[\w$]/.test(prevChar)) return true;
|
|
839
|
+
let k = slashPos - 1;
|
|
840
|
+
while (k >= 0 && /\s/.test(code[k])) k--;
|
|
841
|
+
const wordEnd = k + 1;
|
|
842
|
+
while (k >= 0 && /[\w$]/.test(code[k])) k--;
|
|
843
|
+
return REGEX_PRECEDING_KEYWORDS.has(code.slice(k + 1, wordEnd));
|
|
844
|
+
}
|
|
845
|
+
function makeCodeClassifier(code) {
|
|
846
|
+
const n = code.length;
|
|
847
|
+
let i = 0;
|
|
848
|
+
let skipStart = -1;
|
|
849
|
+
let skipEnd = -1;
|
|
850
|
+
let lastSig;
|
|
851
|
+
return (q) => {
|
|
852
|
+
if (q >= skipStart && q < skipEnd) return false;
|
|
853
|
+
while (i < n && i <= q) {
|
|
854
|
+
const c = code[i];
|
|
855
|
+
const d = i + 1 < n ? code[i + 1] : "";
|
|
856
|
+
let end = -1;
|
|
857
|
+
let transparent = false;
|
|
858
|
+
if (c === "/" && d === "/") {
|
|
859
|
+
let j = i + 2;
|
|
860
|
+
while (j < n && !isLineTerminator(code[j])) j++;
|
|
861
|
+
end = j;
|
|
862
|
+
transparent = true;
|
|
863
|
+
} else if (c === "/" && d === "*") {
|
|
864
|
+
let j = i + 2;
|
|
865
|
+
while (j < n && !(code[j] === "*" && code[j + 1] === "/")) j++;
|
|
866
|
+
end = Math.min(n, j + 2);
|
|
867
|
+
transparent = true;
|
|
868
|
+
} else if (c === '"' || c === "'" || c === "`") {
|
|
869
|
+
let j = i + 1;
|
|
870
|
+
while (j < n) {
|
|
871
|
+
if (code[j] === "\\") {
|
|
872
|
+
j += 2;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
if (code[j] === c) {
|
|
876
|
+
j++;
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
j++;
|
|
880
|
+
}
|
|
881
|
+
end = j;
|
|
882
|
+
} else if (c === "/" && d !== "/" && d !== "*" && isRegexPositionAt(code, i, lastSig)) {
|
|
883
|
+
let j = i + 1;
|
|
884
|
+
let inClass = false;
|
|
885
|
+
let closed = false;
|
|
886
|
+
while (j < n && !isLineTerminator(code[j])) {
|
|
887
|
+
const r = code[j];
|
|
888
|
+
if (r === "\\") {
|
|
889
|
+
j += 2;
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
if (r === "[") inClass = true;
|
|
893
|
+
else if (r === "]") inClass = false;
|
|
894
|
+
else if (r === "/" && !inClass) {
|
|
895
|
+
j++;
|
|
896
|
+
closed = true;
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
j++;
|
|
900
|
+
}
|
|
901
|
+
if (closed) {
|
|
902
|
+
while (j < n && /[a-z]/.test(code[j])) j++;
|
|
903
|
+
end = j;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
if (end >= 0) {
|
|
907
|
+
if (q < end) {
|
|
908
|
+
skipStart = i;
|
|
909
|
+
skipEnd = end;
|
|
910
|
+
return false;
|
|
911
|
+
}
|
|
912
|
+
i = end;
|
|
913
|
+
if (!transparent) lastSig = "x";
|
|
914
|
+
} else {
|
|
915
|
+
if (!/\s/.test(c)) lastSig = c;
|
|
916
|
+
i++;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return true;
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function firstCodeMatchIndex(code, pattern) {
|
|
923
|
+
const inCode = makeCodeClassifier(code);
|
|
924
|
+
pattern.lastIndex = 0;
|
|
925
|
+
let m;
|
|
926
|
+
while ((m = pattern.exec(code)) !== null) {
|
|
927
|
+
if (inCode(m.index)) return m.index;
|
|
928
|
+
if (pattern.lastIndex <= m.index) pattern.lastIndex = m.index + 1;
|
|
929
|
+
}
|
|
930
|
+
return -1;
|
|
931
|
+
}
|
|
932
|
+
var REGEX_PRECEDING_KEYWORDS;
|
|
933
|
+
var init_source_scan = __esm({
|
|
934
|
+
"src/build/route-types/source-scan.ts"() {
|
|
935
|
+
"use strict";
|
|
936
|
+
REGEX_PRECEDING_KEYWORDS = /* @__PURE__ */ new Set([
|
|
937
|
+
"return",
|
|
938
|
+
"typeof",
|
|
939
|
+
"instanceof",
|
|
940
|
+
"in",
|
|
941
|
+
"of",
|
|
942
|
+
"new",
|
|
943
|
+
"delete",
|
|
944
|
+
"void",
|
|
945
|
+
"do",
|
|
946
|
+
"else",
|
|
947
|
+
"yield",
|
|
948
|
+
"await",
|
|
949
|
+
"case",
|
|
950
|
+
"throw"
|
|
951
|
+
]);
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
// src/build/route-types/router-processing.ts
|
|
956
|
+
import {
|
|
957
|
+
readFileSync as readFileSync3,
|
|
958
|
+
writeFileSync as writeFileSync2,
|
|
959
|
+
existsSync as existsSync3,
|
|
960
|
+
unlinkSync,
|
|
961
|
+
readdirSync as readdirSync2
|
|
962
|
+
} from "node:fs";
|
|
963
|
+
import {
|
|
964
|
+
join as join2,
|
|
965
|
+
dirname as dirname2,
|
|
966
|
+
resolve as resolve2,
|
|
967
|
+
sep,
|
|
968
|
+
basename as pathBasename
|
|
969
|
+
} from "node:path";
|
|
970
|
+
import ts5 from "typescript";
|
|
971
|
+
function countPublicRouteEntries(source) {
|
|
972
|
+
const matches = source.matchAll(/^\s+(?:"([^"]+)"|([a-zA-Z_$][^:]*)):\s*["{]/gm) ?? [];
|
|
973
|
+
let count = 0;
|
|
974
|
+
for (const match of matches) {
|
|
975
|
+
const routeName = match[1] || match[2];
|
|
976
|
+
if (routeName && !isAutoGeneratedRouteName(routeName.trim())) {
|
|
977
|
+
count++;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return count;
|
|
981
|
+
}
|
|
982
|
+
function isRoutableSourceFile(name) {
|
|
983
|
+
return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.") && !name.includes(".test.") && !name.includes(".spec.");
|
|
984
|
+
}
|
|
985
|
+
function isExcludedScanDir(name) {
|
|
986
|
+
return name === "node_modules" || name === "dist" || name === "coverage" || name === "__tests__" || name === "__mocks__" || name.startsWith(".");
|
|
987
|
+
}
|
|
988
|
+
function findCallSiteFilesRecursive(dir, pattern, patternG, stopAtMatchDir, filter, results) {
|
|
989
|
+
let entries;
|
|
990
|
+
try {
|
|
991
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
992
|
+
} catch (err) {
|
|
993
|
+
console.warn(
|
|
994
|
+
`[rango] Failed to scan directory ${dir}: ${err.message}`
|
|
995
|
+
);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const childDirs = [];
|
|
999
|
+
const matchesInDir = [];
|
|
1000
|
+
for (const entry of entries) {
|
|
1001
|
+
const fullPath = join2(dir, entry.name);
|
|
1002
|
+
if (entry.isDirectory()) {
|
|
1003
|
+
if (!isExcludedScanDir(entry.name)) childDirs.push(fullPath);
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
if (!isRoutableSourceFile(entry.name)) continue;
|
|
1007
|
+
if (filter && !filter(fullPath)) continue;
|
|
1008
|
+
try {
|
|
1009
|
+
const source = readFileSync3(fullPath, "utf-8");
|
|
1010
|
+
if (pattern.test(source) && firstCodeMatchIndex(source, patternG) >= 0) {
|
|
1011
|
+
matchesInDir.push(fullPath);
|
|
1012
|
+
}
|
|
1013
|
+
} catch {
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
results.push(...matchesInDir);
|
|
1018
|
+
if (!stopAtMatchDir || matchesInDir.length === 0) {
|
|
1019
|
+
for (const childDir of childDirs) {
|
|
1020
|
+
findCallSiteFilesRecursive(
|
|
1021
|
+
childDir,
|
|
1022
|
+
pattern,
|
|
1023
|
+
patternG,
|
|
1024
|
+
stopAtMatchDir,
|
|
1025
|
+
filter,
|
|
1026
|
+
results
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function findNestedRouterConflict(routerFiles) {
|
|
1032
|
+
const routerDirs = [
|
|
1033
|
+
...new Set(routerFiles.map((filePath) => dirname2(resolve2(filePath))))
|
|
1034
|
+
].sort((a, b) => a.length - b.length);
|
|
1035
|
+
for (let i = 0; i < routerDirs.length; i++) {
|
|
1036
|
+
const ancestorDir = routerDirs[i];
|
|
1037
|
+
const prefix = ancestorDir.endsWith(sep) ? ancestorDir : `${ancestorDir}${sep}`;
|
|
1038
|
+
for (let j = i + 1; j < routerDirs.length; j++) {
|
|
1039
|
+
const nestedDir = routerDirs[j];
|
|
1040
|
+
if (!nestedDir.startsWith(prefix)) continue;
|
|
1041
|
+
const ancestorFile = routerFiles.find(
|
|
1042
|
+
(filePath) => dirname2(resolve2(filePath)) === ancestorDir
|
|
1043
|
+
);
|
|
1044
|
+
const nestedFile = routerFiles.find(
|
|
1045
|
+
(filePath) => dirname2(resolve2(filePath)) === nestedDir
|
|
1046
|
+
);
|
|
1047
|
+
if (ancestorFile && nestedFile) {
|
|
1048
|
+
return { ancestor: ancestorFile, nested: nestedFile };
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
1054
|
+
function formatNestedRouterConflictError(conflict, prefix = "[rango]") {
|
|
1055
|
+
return `${prefix} Nested router roots are not supported.
|
|
1056
|
+
Router root: ${conflict.ancestor}
|
|
1057
|
+
Nested router: ${conflict.nested}
|
|
1058
|
+
Move the nested router into a sibling directory or configure it as a separate app root.`;
|
|
1059
|
+
}
|
|
1060
|
+
function extractUrlsFromRouter(code) {
|
|
1061
|
+
const sourceFile = ts5.createSourceFile(
|
|
1062
|
+
"router.tsx",
|
|
1063
|
+
code,
|
|
1064
|
+
ts5.ScriptTarget.Latest,
|
|
1065
|
+
true,
|
|
1066
|
+
ts5.ScriptKind.TSX
|
|
1067
|
+
);
|
|
1068
|
+
let result = null;
|
|
1069
|
+
function isCreateRouterCall(node) {
|
|
1070
|
+
if (!ts5.isCallExpression(node)) return false;
|
|
1071
|
+
const callee = node.expression;
|
|
1072
|
+
return ts5.isIdentifier(callee) && callee.text === "createRouter";
|
|
1073
|
+
}
|
|
1074
|
+
function isInlineBuilder(node) {
|
|
1075
|
+
return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
|
|
1076
|
+
}
|
|
1077
|
+
function isRoutesOnCreateRouter(node) {
|
|
1078
|
+
if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
|
|
1079
|
+
return false;
|
|
1080
|
+
let inner = node.expression.expression;
|
|
1081
|
+
while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
|
|
1082
|
+
inner = inner.expression.expression;
|
|
1083
|
+
}
|
|
1084
|
+
return isCreateRouterCall(inner);
|
|
1085
|
+
}
|
|
1086
|
+
function visit(node) {
|
|
1087
|
+
if (result) return;
|
|
1088
|
+
if (ts5.isCallExpression(node) && node.arguments.length >= 1 && isRoutesOnCreateRouter(node)) {
|
|
1089
|
+
const arg = node.arguments[0];
|
|
1090
|
+
if (ts5.isIdentifier(arg)) {
|
|
1091
|
+
result = { kind: "variable", name: arg.text };
|
|
1092
|
+
} else if (isInlineBuilder(arg)) {
|
|
1093
|
+
result = { kind: "inline", block: arg.getText(sourceFile) };
|
|
1094
|
+
}
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (isCreateRouterCall(node)) {
|
|
1098
|
+
const callExpr = node;
|
|
1099
|
+
for (const callArg of callExpr.arguments) {
|
|
1100
|
+
if (ts5.isObjectLiteralExpression(callArg)) {
|
|
1101
|
+
for (const prop of callArg.properties) {
|
|
1102
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls") {
|
|
1103
|
+
if (ts5.isIdentifier(prop.initializer)) {
|
|
1104
|
+
result = { kind: "variable", name: prop.initializer.text };
|
|
1105
|
+
} else if (isInlineBuilder(prop.initializer)) {
|
|
1106
|
+
result = {
|
|
1107
|
+
kind: "inline",
|
|
1108
|
+
block: prop.initializer.getText(sourceFile)
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
ts5.forEachChild(node, visit);
|
|
1118
|
+
}
|
|
1119
|
+
visit(sourceFile);
|
|
1120
|
+
return result;
|
|
1121
|
+
}
|
|
1122
|
+
function extractBasenameFromRouter(code) {
|
|
1123
|
+
const sourceFile = ts5.createSourceFile(
|
|
1124
|
+
"router.tsx",
|
|
1125
|
+
code,
|
|
1126
|
+
ts5.ScriptTarget.Latest,
|
|
1127
|
+
true,
|
|
1128
|
+
ts5.ScriptKind.TSX
|
|
1129
|
+
);
|
|
1130
|
+
let result;
|
|
1131
|
+
function visit(node) {
|
|
1132
|
+
if (result !== void 0) return;
|
|
1133
|
+
if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "createRouter") {
|
|
1134
|
+
for (const arg of node.arguments) {
|
|
1135
|
+
if (ts5.isObjectLiteralExpression(arg)) {
|
|
1136
|
+
for (const prop of arg.properties) {
|
|
1137
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "basename" && ts5.isStringLiteral(prop.initializer)) {
|
|
1138
|
+
result = prop.initializer.text;
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
ts5.forEachChild(node, visit);
|
|
1146
|
+
}
|
|
1147
|
+
visit(sourceFile);
|
|
1148
|
+
return result;
|
|
1149
|
+
}
|
|
1150
|
+
function applyBasenameToRoutes(result, basename) {
|
|
1151
|
+
const prefixed = {};
|
|
1152
|
+
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
1153
|
+
if (pattern === "/") {
|
|
1154
|
+
prefixed[name] = basename;
|
|
1155
|
+
} else if (basename.endsWith("/") && pattern.startsWith("/")) {
|
|
1156
|
+
prefixed[name] = basename + pattern.slice(1);
|
|
1157
|
+
} else {
|
|
1158
|
+
prefixed[name] = basename + pattern;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
1162
|
+
}
|
|
1163
|
+
function genFileTsPath(sourceFile) {
|
|
1164
|
+
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
1165
|
+
return join2(dirname2(sourceFile), `${base}.named-routes.gen.ts`);
|
|
1166
|
+
}
|
|
1167
|
+
function resolveSearchSchemas(publicRouteNames, runtimeSchemas, sourceFile) {
|
|
1168
|
+
if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
|
|
1169
|
+
return runtimeSchemas;
|
|
1170
|
+
}
|
|
1171
|
+
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
1172
|
+
if (Object.keys(staticParsed.searchSchemas).length === 0) {
|
|
1173
|
+
return runtimeSchemas;
|
|
1174
|
+
}
|
|
1175
|
+
const filtered = {};
|
|
1176
|
+
for (const name of publicRouteNames) {
|
|
1177
|
+
const schema = staticParsed.searchSchemas[name];
|
|
1178
|
+
if (schema) filtered[name] = schema;
|
|
1179
|
+
}
|
|
1180
|
+
return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
|
|
1181
|
+
}
|
|
1182
|
+
function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
1183
|
+
let routerSource;
|
|
1184
|
+
try {
|
|
1185
|
+
routerSource = readFileSync3(routerFilePath, "utf-8");
|
|
1186
|
+
} catch {
|
|
1187
|
+
return { routes: {}, searchSchemas: {} };
|
|
1188
|
+
}
|
|
1189
|
+
const extraction = extractUrlsFromRouter(routerSource);
|
|
1190
|
+
if (!extraction) {
|
|
1191
|
+
return { routes: {}, searchSchemas: {} };
|
|
1192
|
+
}
|
|
1193
|
+
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
1194
|
+
const basename = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
|
|
1195
|
+
let result;
|
|
1196
|
+
if (extraction.kind === "inline") {
|
|
1197
|
+
result = buildCombinedRouteMapWithSearch(
|
|
1198
|
+
routerFilePath,
|
|
1199
|
+
void 0,
|
|
1200
|
+
void 0,
|
|
1201
|
+
void 0,
|
|
1202
|
+
extraction.block
|
|
1203
|
+
);
|
|
1204
|
+
} else {
|
|
1205
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
1206
|
+
if (imported) {
|
|
1207
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
1208
|
+
if (!targetFile) {
|
|
1209
|
+
return { routes: {}, searchSchemas: {} };
|
|
1210
|
+
}
|
|
1211
|
+
result = buildCombinedRouteMapWithSearch(
|
|
1212
|
+
targetFile,
|
|
1213
|
+
imported.exportedName
|
|
1214
|
+
);
|
|
1215
|
+
} else {
|
|
1216
|
+
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
if (basename) {
|
|
1220
|
+
result = applyBasenameToRoutes(result, basename);
|
|
1221
|
+
}
|
|
1222
|
+
return result;
|
|
1223
|
+
}
|
|
1224
|
+
function detectUnresolvableIncludes(routerFilePath) {
|
|
1225
|
+
const realPath = resolve2(routerFilePath);
|
|
1226
|
+
let source;
|
|
1227
|
+
try {
|
|
1228
|
+
source = readFileSync3(realPath, "utf-8");
|
|
1229
|
+
} catch {
|
|
1230
|
+
return [];
|
|
1231
|
+
}
|
|
1232
|
+
const extraction = extractUrlsFromRouter(source);
|
|
1233
|
+
if (!extraction) return [];
|
|
1234
|
+
const diagnostics = [];
|
|
1235
|
+
if (extraction.kind === "inline") {
|
|
1236
|
+
buildCombinedRouteMapWithSearch(
|
|
1237
|
+
realPath,
|
|
1238
|
+
void 0,
|
|
1239
|
+
/* @__PURE__ */ new Set(),
|
|
1240
|
+
diagnostics,
|
|
1241
|
+
extraction.block
|
|
1242
|
+
);
|
|
1243
|
+
return diagnostics;
|
|
1244
|
+
}
|
|
1245
|
+
const imported = resolveImportedVariable(source, extraction.name);
|
|
1246
|
+
let targetFile;
|
|
1247
|
+
let exportedName;
|
|
1248
|
+
if (imported) {
|
|
1249
|
+
const resolved = resolveImportPath(imported.specifier, realPath);
|
|
1250
|
+
if (!resolved) {
|
|
1251
|
+
return [
|
|
1252
|
+
{
|
|
1253
|
+
pathPrefix: "/",
|
|
1254
|
+
namePrefix: null,
|
|
1255
|
+
reason: "file-not-found",
|
|
1256
|
+
sourceFile: realPath,
|
|
1257
|
+
detail: `import "${imported.specifier}" resolved to no file`
|
|
1258
|
+
}
|
|
1259
|
+
];
|
|
1260
|
+
}
|
|
1261
|
+
targetFile = resolved;
|
|
1262
|
+
exportedName = imported.exportedName;
|
|
1263
|
+
} else {
|
|
1264
|
+
targetFile = realPath;
|
|
1265
|
+
exportedName = extraction.name;
|
|
1266
|
+
}
|
|
1267
|
+
buildCombinedRouteMapWithSearch(
|
|
1268
|
+
targetFile,
|
|
1269
|
+
exportedName,
|
|
1270
|
+
/* @__PURE__ */ new Set(),
|
|
1271
|
+
diagnostics
|
|
1272
|
+
);
|
|
1273
|
+
return diagnostics;
|
|
1274
|
+
}
|
|
1275
|
+
function detectUnresolvableIncludesForUrlsFile(filePath) {
|
|
1276
|
+
const realPath = resolve2(filePath);
|
|
1277
|
+
let source;
|
|
1278
|
+
try {
|
|
1279
|
+
source = readFileSync3(realPath, "utf-8");
|
|
1280
|
+
} catch {
|
|
1281
|
+
return [];
|
|
1282
|
+
}
|
|
1283
|
+
const varNames = findUrlsVariableNames(source);
|
|
1284
|
+
if (varNames.length === 0) return [];
|
|
1285
|
+
const diagnostics = [];
|
|
1286
|
+
for (const varName of varNames) {
|
|
1287
|
+
buildCombinedRouteMapWithSearch(realPath, varName, /* @__PURE__ */ new Set(), diagnostics);
|
|
1288
|
+
}
|
|
1289
|
+
return diagnostics;
|
|
1290
|
+
}
|
|
1291
|
+
function findRouterFiles(root, filter) {
|
|
1292
|
+
const result = [];
|
|
1293
|
+
findCallSiteFilesRecursive(
|
|
1294
|
+
root,
|
|
1295
|
+
ROUTER_CALL_PATTERN,
|
|
1296
|
+
ROUTER_CALL_PATTERN_G,
|
|
1297
|
+
true,
|
|
1298
|
+
filter,
|
|
1299
|
+
result
|
|
1300
|
+
);
|
|
1301
|
+
return result;
|
|
1302
|
+
}
|
|
1303
|
+
function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
1304
|
+
try {
|
|
1305
|
+
const oldCombinedPath = join2(root, "src", "named-routes.gen.ts");
|
|
1306
|
+
if (existsSync3(oldCombinedPath)) {
|
|
1307
|
+
unlinkSync(oldCombinedPath);
|
|
1308
|
+
console.log(
|
|
1309
|
+
`[rango] Removed stale combined route types: ${oldCombinedPath}`
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
} catch {
|
|
266
1313
|
}
|
|
1314
|
+
const routerFilePaths = knownRouterFiles ?? findRouterFiles(root);
|
|
1315
|
+
if (routerFilePaths.length === 0) return;
|
|
1316
|
+
const nestedRouterConflict = findNestedRouterConflict(routerFilePaths);
|
|
1317
|
+
if (nestedRouterConflict) {
|
|
1318
|
+
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
1319
|
+
}
|
|
1320
|
+
for (const routerFilePath of routerFilePaths) {
|
|
1321
|
+
const result = buildCombinedRouteMapForRouterFile(routerFilePath);
|
|
1322
|
+
if (Object.keys(result.routes).length === 0 && Object.keys(result.searchSchemas).length === 0) {
|
|
1323
|
+
let routerSource;
|
|
1324
|
+
try {
|
|
1325
|
+
routerSource = readFileSync3(routerFilePath, "utf-8");
|
|
1326
|
+
} catch {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
1330
|
+
}
|
|
1331
|
+
const outPath = genFileTsPath(routerFilePath);
|
|
1332
|
+
const existing = existsSync3(outPath) ? readFileSync3(outPath, "utf-8") : null;
|
|
1333
|
+
if (Object.keys(result.routes).length === 0) {
|
|
1334
|
+
if (!existing) {
|
|
1335
|
+
const emptySource = generateRouteTypesSource({});
|
|
1336
|
+
opts?.onWrite?.(outPath, emptySource);
|
|
1337
|
+
writeFileSync2(outPath, emptySource);
|
|
1338
|
+
}
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
const hasSearchSchemas = Object.keys(result.searchSchemas).length > 0;
|
|
1342
|
+
const source = generateRouteTypesSource(
|
|
1343
|
+
result.routes,
|
|
1344
|
+
hasSearchSchemas ? result.searchSchemas : void 0
|
|
1345
|
+
);
|
|
1346
|
+
if (existing !== source) {
|
|
1347
|
+
if (opts?.preserveIfLarger && existing) {
|
|
1348
|
+
const existingCount = countPublicRouteEntries(existing);
|
|
1349
|
+
const newCount = Object.keys(result.routes).filter(
|
|
1350
|
+
(name) => !isAutoGeneratedRouteName(name)
|
|
1351
|
+
).length;
|
|
1352
|
+
if (existingCount > newCount) {
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
opts?.onWrite?.(outPath, source);
|
|
1357
|
+
writeFileSync2(outPath, source);
|
|
1358
|
+
console.log(
|
|
1359
|
+
`[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
var ROUTER_CALL_PATTERN, ROUTER_CALL_PATTERN_G;
|
|
1365
|
+
var init_router_processing = __esm({
|
|
1366
|
+
"src/build/route-types/router-processing.ts"() {
|
|
1367
|
+
"use strict";
|
|
1368
|
+
init_codegen();
|
|
1369
|
+
init_source_scan();
|
|
1370
|
+
init_include_resolution();
|
|
1371
|
+
init_per_module_writer();
|
|
1372
|
+
init_route_name();
|
|
1373
|
+
ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
|
|
1374
|
+
ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
// src/build/generate-route-types.ts
|
|
1379
|
+
var init_generate_route_types = __esm({
|
|
1380
|
+
"src/build/generate-route-types.ts"() {
|
|
1381
|
+
"use strict";
|
|
1382
|
+
init_param_extraction();
|
|
1383
|
+
init_ast_route_extraction();
|
|
1384
|
+
init_codegen();
|
|
1385
|
+
init_scan_filter();
|
|
1386
|
+
init_per_module_writer();
|
|
1387
|
+
init_include_resolution();
|
|
1388
|
+
init_router_processing();
|
|
1389
|
+
init_per_module_writer();
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// src/vite/plugins/virtual-entries.ts
|
|
1394
|
+
function getVirtualVersionContent(version) {
|
|
1395
|
+
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
1396
|
+
}
|
|
1397
|
+
var VIRTUAL_ENTRY_BROWSER, VIRTUAL_ENTRY_SSR, VIRTUAL_IDS;
|
|
1398
|
+
var init_virtual_entries = __esm({
|
|
1399
|
+
"src/vite/plugins/virtual-entries.ts"() {
|
|
1400
|
+
"use strict";
|
|
1401
|
+
VIRTUAL_ENTRY_BROWSER = `
|
|
1402
|
+
import {
|
|
1403
|
+
createFromReadableStream,
|
|
1404
|
+
createFromFetch,
|
|
1405
|
+
setServerCallback,
|
|
1406
|
+
encodeReply,
|
|
1407
|
+
createTemporaryReferenceSet,
|
|
1408
|
+
} from "@rangojs/router/internal/deps/browser";
|
|
1409
|
+
import { createElement, StrictMode } from "react";
|
|
1410
|
+
import { hydrateRoot } from "react-dom/client";
|
|
1411
|
+
import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
|
|
1412
|
+
import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
1413
|
+
|
|
1414
|
+
async function initializeApp() {
|
|
1415
|
+
const deps = {
|
|
1416
|
+
createFromFetch,
|
|
1417
|
+
createFromReadableStream,
|
|
1418
|
+
encodeReply,
|
|
1419
|
+
setServerCallback,
|
|
1420
|
+
createTemporaryReferenceSet,
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
// initBrowserApp resolves the initial payload and returns the browser app
|
|
1424
|
+
// context, including strictMode (default true) from createRouter. StrictMode
|
|
1425
|
+
// is the default; createRouter({ strictMode: false }) ships the opt-out in the
|
|
1426
|
+
// payload metadata. StrictMode emits no DOM, so toggling never changes markup.
|
|
1427
|
+
const { strictMode } = await initBrowserApp({ rscStream, deps });
|
|
1428
|
+
|
|
1429
|
+
const app = createElement(Rango);
|
|
1430
|
+
hydrateRoot(
|
|
1431
|
+
document,
|
|
1432
|
+
strictMode === false ? app : createElement(StrictMode, null, app)
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
initializeApp().catch(console.error);
|
|
1437
|
+
`.trim();
|
|
1438
|
+
VIRTUAL_ENTRY_SSR = `
|
|
1439
|
+
import { createFromReadableStream } from "@rangojs/router/internal/deps/ssr";
|
|
1440
|
+
import { renderToReadableStream, resume } from "react-dom/server.edge";
|
|
1441
|
+
import { prerender } from "react-dom/static.edge";
|
|
1442
|
+
import { injectRSCPayload } from "@rangojs/router/internal/deps/html-stream-server";
|
|
1443
|
+
import {
|
|
1444
|
+
createSSRHandler,
|
|
1445
|
+
createShellCaptureHandler,
|
|
1446
|
+
createShellResumeHandler,
|
|
1447
|
+
} from "@rangojs/router/ssr";
|
|
1448
|
+
|
|
1449
|
+
export const renderHTML = createSSRHandler({
|
|
1450
|
+
createFromReadableStream,
|
|
1451
|
+
renderToReadableStream,
|
|
1452
|
+
injectRSCPayload,
|
|
1453
|
+
loadBootstrapScriptContent: () =>
|
|
1454
|
+
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
export const captureShellHTML = createShellCaptureHandler({
|
|
1458
|
+
createFromReadableStream,
|
|
1459
|
+
renderToReadableStream,
|
|
1460
|
+
injectRSCPayload,
|
|
1461
|
+
prerender,
|
|
1462
|
+
resume,
|
|
1463
|
+
loadBootstrapScriptContent: () =>
|
|
1464
|
+
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
1465
|
+
});
|
|
1466
|
+
|
|
1467
|
+
export const resumeShellHTML = createShellResumeHandler({
|
|
1468
|
+
createFromReadableStream,
|
|
1469
|
+
renderToReadableStream,
|
|
1470
|
+
injectRSCPayload,
|
|
1471
|
+
prerender,
|
|
1472
|
+
resume,
|
|
1473
|
+
loadBootstrapScriptContent: () =>
|
|
1474
|
+
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
1475
|
+
});
|
|
1476
|
+
`.trim();
|
|
1477
|
+
VIRTUAL_IDS = {
|
|
1478
|
+
browser: "virtual:rsc-router/entry.browser.js",
|
|
1479
|
+
ssr: "virtual:rsc-router/entry.ssr.js",
|
|
1480
|
+
rsc: "virtual:rsc-router/entry.rsc.js",
|
|
1481
|
+
version: "@rangojs/router:version"
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
// src/vite/utils/directive-prologue.ts
|
|
1487
|
+
import { parseAst } from "vite";
|
|
1488
|
+
function hasUseClientDirective(source) {
|
|
1489
|
+
let program;
|
|
1490
|
+
try {
|
|
1491
|
+
program = parseAst(source, { lang: "tsx" });
|
|
1492
|
+
} catch {
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
for (const node of program.body ?? []) {
|
|
1496
|
+
if (node?.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
|
|
1497
|
+
if (node.expression.value === "use client") return true;
|
|
1498
|
+
continue;
|
|
1499
|
+
}
|
|
1500
|
+
break;
|
|
1501
|
+
}
|
|
1502
|
+
return false;
|
|
267
1503
|
}
|
|
1504
|
+
var init_directive_prologue = __esm({
|
|
1505
|
+
"src/vite/utils/directive-prologue.ts"() {
|
|
1506
|
+
"use strict";
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
// src/vite/plugins/version-plugin.ts
|
|
1511
|
+
var version_plugin_exports = {};
|
|
1512
|
+
__export(version_plugin_exports, {
|
|
1513
|
+
createVersionPlugin: () => createVersionPlugin,
|
|
1514
|
+
isViteDepCachePath: () => isViteDepCachePath
|
|
1515
|
+
});
|
|
1516
|
+
import { parseAst as parseAst2 } from "vite";
|
|
1517
|
+
function isCodeModule(id) {
|
|
1518
|
+
return /\.(tsx?|jsx?)($|\?)/.test(id);
|
|
1519
|
+
}
|
|
1520
|
+
function normalizeModuleId(id) {
|
|
1521
|
+
return id.split("?", 1)[0];
|
|
1522
|
+
}
|
|
1523
|
+
function getClientModuleSignature(source) {
|
|
1524
|
+
if (!hasUseClientDirective(source)) return void 0;
|
|
1525
|
+
let program;
|
|
1526
|
+
try {
|
|
1527
|
+
program = parseAst2(source, { lang: "tsx" });
|
|
1528
|
+
} catch {
|
|
1529
|
+
return void 0;
|
|
1530
|
+
}
|
|
1531
|
+
const exports = /* @__PURE__ */ new Set();
|
|
1532
|
+
let hasDefault = false;
|
|
1533
|
+
let hasExportAll = false;
|
|
1534
|
+
const collectBindingNames = (pattern) => {
|
|
1535
|
+
if (!pattern) return;
|
|
1536
|
+
if (pattern.type === "Identifier") {
|
|
1537
|
+
exports.add(pattern.name);
|
|
1538
|
+
} else if (pattern.type === "ObjectPattern") {
|
|
1539
|
+
for (const prop of pattern.properties ?? []) {
|
|
1540
|
+
if (prop?.type === "RestElement") {
|
|
1541
|
+
collectBindingNames(prop.argument);
|
|
1542
|
+
} else {
|
|
1543
|
+
collectBindingNames(prop?.value);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
} else if (pattern.type === "ArrayPattern") {
|
|
1547
|
+
for (const el of pattern.elements ?? []) {
|
|
1548
|
+
if (el?.type === "RestElement") {
|
|
1549
|
+
collectBindingNames(el.argument);
|
|
1550
|
+
} else {
|
|
1551
|
+
collectBindingNames(el);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
};
|
|
1556
|
+
const collectDeclarationNames = (declaration) => {
|
|
1557
|
+
if (!declaration) return;
|
|
1558
|
+
if (declaration.type === "VariableDeclaration") {
|
|
1559
|
+
for (const decl of declaration.declarations ?? []) {
|
|
1560
|
+
collectBindingNames(decl?.id);
|
|
1561
|
+
}
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
collectBindingNames(declaration.id);
|
|
1565
|
+
};
|
|
1566
|
+
for (const node of program.body ?? []) {
|
|
1567
|
+
if (node?.type === "ExportDefaultDeclaration") {
|
|
1568
|
+
hasDefault = true;
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
if (node?.type === "ExportAllDeclaration") {
|
|
1572
|
+
hasExportAll = true;
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (node?.type !== "ExportNamedDeclaration") continue;
|
|
1576
|
+
collectDeclarationNames(node.declaration);
|
|
1577
|
+
for (const specifier of node.specifiers ?? []) {
|
|
1578
|
+
const exportedName = specifier?.exported?.name ?? specifier?.exported?.value;
|
|
1579
|
+
if (exportedName === "default") {
|
|
1580
|
+
hasDefault = true;
|
|
1581
|
+
} else if (typeof exportedName === "string") {
|
|
1582
|
+
exports.add(exportedName);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
return {
|
|
1587
|
+
key: JSON.stringify({
|
|
1588
|
+
default: hasDefault,
|
|
1589
|
+
exportAll: hasExportAll,
|
|
1590
|
+
exports: [...exports].sort()
|
|
1591
|
+
})
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
function createVersionPlugin() {
|
|
1595
|
+
const buildVersion = Date.now().toString(16);
|
|
1596
|
+
let currentVersion = buildVersion;
|
|
1597
|
+
let isDev = false;
|
|
1598
|
+
let server = null;
|
|
1599
|
+
let resolvedCacheDir;
|
|
1600
|
+
const clientModuleSignatures = /* @__PURE__ */ new Map();
|
|
1601
|
+
let versionCounter = 0;
|
|
1602
|
+
const bumpVersion = (reason) => {
|
|
1603
|
+
currentVersion = Date.now().toString(16) + String(++versionCounter);
|
|
1604
|
+
console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
|
|
1605
|
+
const rscEnv = server?.environments?.rsc;
|
|
1606
|
+
const versionMod = rscEnv?.moduleGraph?.getModuleById(
|
|
1607
|
+
"\0" + VIRTUAL_IDS.version
|
|
1608
|
+
);
|
|
1609
|
+
if (versionMod) {
|
|
1610
|
+
rscEnv.moduleGraph.invalidateModule(versionMod);
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
return {
|
|
1614
|
+
name: "@rangojs/router:version",
|
|
1615
|
+
enforce: "pre",
|
|
1616
|
+
configResolved(config) {
|
|
1617
|
+
isDev = config.command === "serve";
|
|
1618
|
+
resolvedCacheDir = config.cacheDir ? String(config.cacheDir).replace(/\\/g, "/") : void 0;
|
|
1619
|
+
},
|
|
1620
|
+
configureServer(devServer) {
|
|
1621
|
+
server = devServer;
|
|
1622
|
+
devServer.watcher.on("unlink", (filePath) => {
|
|
1623
|
+
if (!isDev) return;
|
|
1624
|
+
if (!clientModuleSignatures.has(filePath)) return;
|
|
1625
|
+
clientModuleSignatures.delete(filePath);
|
|
1626
|
+
bumpVersion("Client module removed");
|
|
1627
|
+
});
|
|
1628
|
+
},
|
|
1629
|
+
resolveId(id) {
|
|
1630
|
+
if (id === VIRTUAL_IDS.version) {
|
|
1631
|
+
return "\0" + id;
|
|
1632
|
+
}
|
|
1633
|
+
return null;
|
|
1634
|
+
},
|
|
1635
|
+
load(id) {
|
|
1636
|
+
if (id === "\0" + VIRTUAL_IDS.version) {
|
|
1637
|
+
return getVirtualVersionContent(currentVersion);
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
},
|
|
1641
|
+
transform(code, id) {
|
|
1642
|
+
if (!isDev || !isCodeModule(id)) return null;
|
|
1643
|
+
const normalizedId = normalizeModuleId(id);
|
|
1644
|
+
if (!code.includes("use client") && !clientModuleSignatures.has(normalizedId)) {
|
|
1645
|
+
return null;
|
|
1646
|
+
}
|
|
1647
|
+
const signature = getClientModuleSignature(code);
|
|
1648
|
+
if (signature) {
|
|
1649
|
+
clientModuleSignatures.set(normalizedId, signature);
|
|
1650
|
+
} else {
|
|
1651
|
+
clientModuleSignatures.delete(normalizedId);
|
|
1652
|
+
}
|
|
1653
|
+
return null;
|
|
1654
|
+
},
|
|
1655
|
+
async hotUpdate(ctx) {
|
|
1656
|
+
if (!isDev) return;
|
|
1657
|
+
const isRscModule = this.environment?.name === "rsc";
|
|
1658
|
+
if (!isRscModule) return;
|
|
1659
|
+
if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
|
|
1660
|
+
if (ctx.modules.length === 1 && ctx.modules[0].id === "\0" + VIRTUAL_IDS.version) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (isCodeModule(ctx.file)) {
|
|
1664
|
+
const filePath = normalizeModuleId(ctx.file);
|
|
1665
|
+
const previousSignature = clientModuleSignatures.get(filePath);
|
|
1666
|
+
try {
|
|
1667
|
+
const source = await ctx.read();
|
|
1668
|
+
const nextSignature = getClientModuleSignature(source);
|
|
1669
|
+
if (nextSignature) {
|
|
1670
|
+
clientModuleSignatures.set(filePath, nextSignature);
|
|
1671
|
+
if (previousSignature && previousSignature.key === nextSignature.key) {
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
} else {
|
|
1675
|
+
clientModuleSignatures.delete(filePath);
|
|
1676
|
+
if (!previousSignature) {
|
|
1677
|
+
if (ctx.modules.length === 0) return;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
} else {
|
|
1683
|
+
if (ctx.modules.length === 0) return;
|
|
1684
|
+
}
|
|
1685
|
+
bumpVersion("RSC module changed");
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
function isViteDepCachePath(filePath, cacheDir) {
|
|
1690
|
+
if (!filePath) return false;
|
|
1691
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
1692
|
+
if (cacheDir) {
|
|
1693
|
+
const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1694
|
+
if (normalized === normalizedCacheDir || normalized.startsWith(normalizedCacheDir + "/")) {
|
|
1695
|
+
return true;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
return /\/node_modules\/\.vite[^/]*\//.test(normalized) || normalized.includes("/.vite-isolated/");
|
|
1699
|
+
}
|
|
1700
|
+
var init_version_plugin = __esm({
|
|
1701
|
+
"src/vite/plugins/version-plugin.ts"() {
|
|
1702
|
+
"use strict";
|
|
1703
|
+
init_virtual_entries();
|
|
1704
|
+
init_directive_prologue();
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
// src/vite/plugins/virtual-stub-plugin.ts
|
|
1709
|
+
var virtual_stub_plugin_exports = {};
|
|
1710
|
+
__export(virtual_stub_plugin_exports, {
|
|
1711
|
+
createVirtualStubPlugin: () => createVirtualStubPlugin
|
|
1712
|
+
});
|
|
1713
|
+
function createVirtualStubPlugin() {
|
|
1714
|
+
const STUB_PREFIXES = [
|
|
1715
|
+
"virtual:rsc-router/",
|
|
1716
|
+
"virtual:entry-",
|
|
1717
|
+
"virtual:vite-rsc/"
|
|
1718
|
+
];
|
|
1719
|
+
return {
|
|
1720
|
+
name: "@rangojs/router:virtual-stubs",
|
|
1721
|
+
resolveId(id) {
|
|
1722
|
+
if (STUB_PREFIXES.some((p) => id.startsWith(p))) {
|
|
1723
|
+
return "\0stub:" + id;
|
|
1724
|
+
}
|
|
1725
|
+
return null;
|
|
1726
|
+
},
|
|
1727
|
+
load(id) {
|
|
1728
|
+
if (id.startsWith("\0stub:")) {
|
|
1729
|
+
return "export default {}";
|
|
1730
|
+
}
|
|
1731
|
+
return null;
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
var init_virtual_stub_plugin = __esm({
|
|
1736
|
+
"src/vite/plugins/virtual-stub-plugin.ts"() {
|
|
1737
|
+
"use strict";
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
// src/build/runtime-discovery.ts
|
|
1742
|
+
var runtime_discovery_exports = {};
|
|
1743
|
+
__export(runtime_discovery_exports, {
|
|
1744
|
+
discoverAndWriteRouteTypes: () => discoverAndWriteRouteTypes
|
|
1745
|
+
});
|
|
1746
|
+
import { resolve as resolve3 } from "node:path";
|
|
1747
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1748
|
+
async function discoverAndWriteRouteTypes(opts) {
|
|
1749
|
+
let createViteServer;
|
|
1750
|
+
let loadConfigFromFile;
|
|
1751
|
+
let rsc;
|
|
1752
|
+
try {
|
|
1753
|
+
const vite = await import("vite");
|
|
1754
|
+
createViteServer = vite.createServer;
|
|
1755
|
+
loadConfigFromFile = vite.loadConfigFromFile;
|
|
1756
|
+
} catch {
|
|
1757
|
+
throw new Error(
|
|
1758
|
+
"Runtime discovery requires 'vite'. Install it with: pnpm add -D vite"
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
try {
|
|
1762
|
+
const rscMod = await import("@vitejs/plugin-rsc");
|
|
1763
|
+
rsc = rscMod.default;
|
|
1764
|
+
} catch {
|
|
1765
|
+
throw new Error(
|
|
1766
|
+
"Runtime discovery requires '@vitejs/plugin-rsc'. Install it with: pnpm add -D @vitejs/plugin-rsc"
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
const { createVersionPlugin: createVersionPlugin2 } = await Promise.resolve().then(() => (init_version_plugin(), version_plugin_exports));
|
|
1770
|
+
const { createVirtualStubPlugin: createVirtualStubPlugin2 } = await Promise.resolve().then(() => (init_virtual_stub_plugin(), virtual_stub_plugin_exports));
|
|
1771
|
+
let userResolveAlias = opts.resolveAlias;
|
|
1772
|
+
if (!userResolveAlias) {
|
|
1773
|
+
const configPath = opts.configFile;
|
|
1774
|
+
try {
|
|
1775
|
+
const loaded = await loadConfigFromFile(
|
|
1776
|
+
{ command: "serve", mode: "development" },
|
|
1777
|
+
configPath,
|
|
1778
|
+
opts.root
|
|
1779
|
+
);
|
|
1780
|
+
if (loaded?.config?.resolve?.alias) {
|
|
1781
|
+
userResolveAlias = loaded.config.resolve.alias;
|
|
1782
|
+
}
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
const entryPath = resolve3(opts.entry);
|
|
1787
|
+
let tempServer = null;
|
|
1788
|
+
try {
|
|
1789
|
+
tempServer = await createViteServer({
|
|
1790
|
+
root: opts.root,
|
|
1791
|
+
configFile: false,
|
|
1792
|
+
server: { middlewareMode: true },
|
|
1793
|
+
appType: "custom",
|
|
1794
|
+
logLevel: "silent",
|
|
1795
|
+
cacheDir: "node_modules/.vite_rango_generate",
|
|
1796
|
+
resolve: userResolveAlias ? { alias: userResolveAlias } : void 0,
|
|
1797
|
+
esbuild: { jsx: "automatic", jsxImportSource: "react" },
|
|
1798
|
+
plugins: [
|
|
1799
|
+
rsc({
|
|
1800
|
+
entries: {
|
|
1801
|
+
client: "virtual:entry-client",
|
|
1802
|
+
ssr: "virtual:entry-ssr",
|
|
1803
|
+
rsc: entryPath
|
|
1804
|
+
}
|
|
1805
|
+
}),
|
|
1806
|
+
createVersionPlugin2(),
|
|
1807
|
+
createVirtualStubPlugin2()
|
|
1808
|
+
]
|
|
1809
|
+
});
|
|
1810
|
+
const rscEnv = tempServer.environments?.rsc;
|
|
1811
|
+
if (!rscEnv?.runner) {
|
|
1812
|
+
throw new Error("RSC environment runner not available");
|
|
1813
|
+
}
|
|
1814
|
+
await rscEnv.runner.import(entryPath);
|
|
1815
|
+
const serverMod = await rscEnv.runner.import("@rangojs/router/server");
|
|
1816
|
+
const registry = serverMod.RouterRegistry;
|
|
1817
|
+
if (!registry || registry.size === 0) {
|
|
1818
|
+
throw new Error(
|
|
1819
|
+
`No routers found in registry after importing ${opts.entry}`
|
|
1820
|
+
);
|
|
1821
|
+
}
|
|
1822
|
+
const buildMod = await rscEnv.runner.import("@rangojs/router/build");
|
|
1823
|
+
const generateManifest = buildMod.generateManifest;
|
|
1824
|
+
if (!generateManifest) {
|
|
1825
|
+
throw new Error("generateManifest not found in @rangojs/router/build");
|
|
1826
|
+
}
|
|
1827
|
+
const outputFiles = [];
|
|
1828
|
+
let totalRouteCount = 0;
|
|
1829
|
+
let routerMountIndex = 0;
|
|
1830
|
+
for (const [id, router] of registry) {
|
|
1831
|
+
if (!router.urlpatterns) continue;
|
|
1832
|
+
const manifest = await generateManifest(
|
|
1833
|
+
router.urlpatterns,
|
|
1834
|
+
routerMountIndex
|
|
1835
|
+
);
|
|
1836
|
+
routerMountIndex++;
|
|
1837
|
+
const rawManifest = manifest.routeManifest;
|
|
1838
|
+
const routeManifest = {};
|
|
1839
|
+
for (const [name, pattern] of Object.entries(rawManifest)) {
|
|
1840
|
+
if (!isAutoGeneratedRouteName(name)) {
|
|
1841
|
+
routeManifest[name] = pattern;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
let routeSearchSchemas = manifest.routeSearchSchemas;
|
|
1845
|
+
const sourceFile = router.__sourceFile;
|
|
1846
|
+
if (!sourceFile) {
|
|
1847
|
+
console.warn(
|
|
1848
|
+
`[rango] Router "${id}" has no __sourceFile, skipping gen file`
|
|
1849
|
+
);
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
if (sourceFile.includes("node_modules")) {
|
|
1853
|
+
throw new Error(
|
|
1854
|
+
`[rango] Router "${id}" has sourceFile inside node_modules: ${sourceFile}
|
|
1855
|
+
This means createRouter() stack trace parsing matched an internal frame.
|
|
1856
|
+
Set an explicit \`id\` on createRouter() or check the call site.`
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
routeSearchSchemas = resolveSearchSchemas(
|
|
1860
|
+
Object.keys(routeManifest),
|
|
1861
|
+
routeSearchSchemas,
|
|
1862
|
+
sourceFile
|
|
1863
|
+
);
|
|
1864
|
+
const outPath = genFileTsPath(sourceFile);
|
|
1865
|
+
const source = generateRouteTypesSource(
|
|
1866
|
+
routeManifest,
|
|
1867
|
+
routeSearchSchemas && Object.keys(routeSearchSchemas).length > 0 ? routeSearchSchemas : void 0
|
|
1868
|
+
);
|
|
1869
|
+
const existing = existsSync4(outPath) ? readFileSync4(outPath, "utf-8") : null;
|
|
1870
|
+
if (existing !== source) {
|
|
1871
|
+
writeFileSync3(outPath, source);
|
|
1872
|
+
}
|
|
1873
|
+
const routeCount = Object.keys(routeManifest).length;
|
|
1874
|
+
totalRouteCount += routeCount;
|
|
1875
|
+
outputFiles.push(outPath);
|
|
1876
|
+
console.log(
|
|
1877
|
+
`[rango] Generated route types (${routeCount} routes) -> ${outPath}`
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
return {
|
|
1881
|
+
routerCount: routerMountIndex,
|
|
1882
|
+
routeCount: totalRouteCount,
|
|
1883
|
+
outputFiles
|
|
1884
|
+
};
|
|
1885
|
+
} finally {
|
|
1886
|
+
if (tempServer) {
|
|
1887
|
+
await tempServer.close();
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
var init_runtime_discovery = __esm({
|
|
1892
|
+
"src/build/runtime-discovery.ts"() {
|
|
1893
|
+
"use strict";
|
|
1894
|
+
init_generate_route_types();
|
|
1895
|
+
init_route_name();
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
268
1898
|
|
|
269
1899
|
// src/bin/rango.ts
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
1900
|
+
init_generate_route_types();
|
|
1901
|
+
import { resolve as resolve4, dirname as dirname3 } from "node:path";
|
|
1902
|
+
import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "node:fs";
|
|
1903
|
+
var [command, ...rawArgs] = process.argv.slice(2);
|
|
1904
|
+
if (command === "generate") {
|
|
1905
|
+
let mode = "default";
|
|
1906
|
+
let configFile;
|
|
1907
|
+
const positionalArgs = [];
|
|
1908
|
+
for (let i = 0; i < rawArgs.length; i++) {
|
|
1909
|
+
const arg = rawArgs[i];
|
|
1910
|
+
if (arg === "--runtime") {
|
|
1911
|
+
mode = "runtime";
|
|
1912
|
+
} else if (arg === "--static") {
|
|
1913
|
+
mode = "static";
|
|
1914
|
+
} else if (arg === "--config") {
|
|
1915
|
+
configFile = rawArgs[++i];
|
|
1916
|
+
if (!configFile) {
|
|
1917
|
+
console.error("[rango] --config requires a path argument");
|
|
1918
|
+
process.exit(1);
|
|
1919
|
+
}
|
|
1920
|
+
} else if (arg.startsWith("--")) {
|
|
1921
|
+
console.error(`[rango] Unknown flag: ${arg}`);
|
|
1922
|
+
process.exit(1);
|
|
1923
|
+
} else {
|
|
1924
|
+
positionalArgs.push(arg);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
if (positionalArgs.length === 0) {
|
|
1928
|
+
console.error(
|
|
1929
|
+
"[rango] Usage: rango generate <file|dir> [file2 ...] [--runtime|--static] [--config <path>]"
|
|
1930
|
+
);
|
|
1931
|
+
process.exit(1);
|
|
1932
|
+
}
|
|
1933
|
+
if (configFile && mode !== "runtime") {
|
|
1934
|
+
console.warn("[rango] --config is only used with --runtime, ignoring");
|
|
1935
|
+
}
|
|
1936
|
+
if (mode === "runtime") {
|
|
1937
|
+
runRuntimeDiscovery(positionalArgs, configFile).catch((err) => {
|
|
1938
|
+
console.error(`[rango] Runtime discovery failed: ${err.message}`);
|
|
1939
|
+
process.exit(1);
|
|
1940
|
+
});
|
|
1941
|
+
} else {
|
|
1942
|
+
runStaticGeneration(positionalArgs, mode);
|
|
278
1943
|
}
|
|
279
|
-
console.log(`[rango] Scanned ${files.length} file(s)`);
|
|
280
|
-
process.exit(0);
|
|
281
1944
|
} else {
|
|
282
|
-
|
|
1945
|
+
if (command && command !== "help" && command !== "--help" && command !== "-h") {
|
|
1946
|
+
console.error(`[rango] Unknown command: ${command}
|
|
1947
|
+
`);
|
|
1948
|
+
}
|
|
1949
|
+
console.log(`Usage: rango generate <file|dir> [file2 ...] [--runtime|--static] [--config <path>]
|
|
1950
|
+
|
|
1951
|
+
Auto-detects file type (createRouter, urls) and generates
|
|
1952
|
+
the appropriate .gen.ts route type files.
|
|
1953
|
+
|
|
1954
|
+
Modes:
|
|
1955
|
+
(default) Static parser with error on unresolvable includes
|
|
1956
|
+
--runtime Vite-based runtime discovery (100% coverage)
|
|
1957
|
+
Requires vite and @vitejs/plugin-rsc
|
|
1958
|
+
--static Static parser, accept partial output with warnings
|
|
1959
|
+
|
|
1960
|
+
Options:
|
|
1961
|
+
--config <path> Path to vite.config.ts (--runtime only, auto-detected if omitted)
|
|
283
1962
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
1963
|
+
Examples:
|
|
1964
|
+
rango generate src/router.tsx
|
|
1965
|
+
rango generate src/router.tsx --runtime
|
|
1966
|
+
rango generate src/ --static`);
|
|
1967
|
+
process.exit(
|
|
1968
|
+
command && command !== "help" && command !== "--help" && command !== "-h" ? 1 : 0
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
function findProjectRoot(fromPath) {
|
|
1972
|
+
let dir = dirname3(resolve4(fromPath));
|
|
1973
|
+
while (dir !== dirname3(dir)) {
|
|
1974
|
+
if (existsSync5(resolve4(dir, "package.json")) || existsSync5(resolve4(dir, "vite.config.ts")) || existsSync5(resolve4(dir, "vite.config.js"))) {
|
|
1975
|
+
return dir;
|
|
1976
|
+
}
|
|
1977
|
+
dir = dirname3(dir);
|
|
1978
|
+
}
|
|
1979
|
+
return process.cwd();
|
|
1980
|
+
}
|
|
1981
|
+
function runStaticGeneration(args, mode) {
|
|
1982
|
+
const files = [];
|
|
1983
|
+
for (const arg of args) {
|
|
1984
|
+
const resolved = resolve4(arg);
|
|
1985
|
+
try {
|
|
1986
|
+
if (statSync(resolved).isDirectory()) {
|
|
1987
|
+
files.push(...findTsFiles(resolved));
|
|
1988
|
+
} else {
|
|
1989
|
+
files.push(resolved);
|
|
1990
|
+
}
|
|
1991
|
+
} catch {
|
|
1992
|
+
console.warn(`[rango] Skipping ${arg}: not found`);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if (files.length === 0) {
|
|
1996
|
+
console.log("[rango] No files to process");
|
|
1997
|
+
process.exit(0);
|
|
1998
|
+
}
|
|
1999
|
+
const routerFiles = [];
|
|
2000
|
+
const urlsFiles = [];
|
|
2001
|
+
for (const filePath of files) {
|
|
2002
|
+
try {
|
|
2003
|
+
const source = readFileSync5(filePath, "utf-8");
|
|
2004
|
+
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
2005
|
+
routerFiles.push(filePath);
|
|
2006
|
+
}
|
|
2007
|
+
if (source.includes("urls(")) {
|
|
2008
|
+
urlsFiles.push(filePath);
|
|
2009
|
+
}
|
|
2010
|
+
} catch (err) {
|
|
2011
|
+
console.warn(
|
|
2012
|
+
`[rango] Failed to process ${filePath}: ${err.message}`
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
const allDiagnostics = [];
|
|
2017
|
+
for (const routerFile of routerFiles) {
|
|
2018
|
+
const diagnostics = detectUnresolvableIncludes(routerFile);
|
|
2019
|
+
for (const d of diagnostics) {
|
|
2020
|
+
allDiagnostics.push({ ...d, routerFile });
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
const routerFileSet = new Set(routerFiles);
|
|
2024
|
+
for (const urlsFile of urlsFiles) {
|
|
2025
|
+
if (routerFileSet.has(urlsFile)) continue;
|
|
2026
|
+
const diagnostics = detectUnresolvableIncludesForUrlsFile(urlsFile);
|
|
2027
|
+
for (const d of diagnostics) {
|
|
2028
|
+
allDiagnostics.push({ ...d, routerFile: urlsFile });
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2032
|
+
const uniqueDiagnostics = allDiagnostics.filter((d) => {
|
|
2033
|
+
const key = `${d.sourceFile}:${d.pathPrefix}:${d.reason}`;
|
|
2034
|
+
if (seen.has(key)) return false;
|
|
2035
|
+
seen.add(key);
|
|
2036
|
+
return true;
|
|
2037
|
+
});
|
|
2038
|
+
if (uniqueDiagnostics.length > 0 && mode === "default") {
|
|
2039
|
+
console.error("\n[rango] Unresolvable includes detected:\n");
|
|
2040
|
+
formatDiagnostics(uniqueDiagnostics);
|
|
2041
|
+
console.error(
|
|
2042
|
+
"\nThe static parser cannot resolve these includes because they use factory functions or dynamic expressions.\n\nOptions:\n rango generate <path> --runtime Use Vite-based discovery (requires vite)\n rango generate <path> --static Accept partial output (missing routes above)\n"
|
|
2043
|
+
);
|
|
2044
|
+
process.exit(1);
|
|
2045
|
+
}
|
|
2046
|
+
if (uniqueDiagnostics.length > 0 && mode === "static") {
|
|
2047
|
+
console.warn(
|
|
2048
|
+
"\n[rango] Warning: partial output (unresolvable includes):\n"
|
|
2049
|
+
);
|
|
2050
|
+
formatDiagnostics(uniqueDiagnostics);
|
|
2051
|
+
console.warn("");
|
|
2052
|
+
}
|
|
2053
|
+
const nestedRouterConflict = findNestedRouterConflict(routerFiles);
|
|
2054
|
+
if (nestedRouterConflict) {
|
|
2055
|
+
console.error(
|
|
2056
|
+
`
|
|
2057
|
+
${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
|
|
2058
|
+
`
|
|
2059
|
+
);
|
|
2060
|
+
process.exit(1);
|
|
2061
|
+
}
|
|
2062
|
+
for (const urlsFile of urlsFiles) {
|
|
2063
|
+
writePerModuleRouteTypesForFile(urlsFile);
|
|
2064
|
+
}
|
|
2065
|
+
for (const routerFile of routerFiles) {
|
|
2066
|
+
const projectRoot = findProjectRoot(routerFile);
|
|
2067
|
+
writeCombinedRouteTypes(projectRoot, [routerFile]);
|
|
2068
|
+
}
|
|
2069
|
+
console.log(
|
|
2070
|
+
`[rango] Processed ${files.length} file(s)${routerFiles.length ? ` (${routerFiles.length} router)` : ""}`
|
|
2071
|
+
);
|
|
2072
|
+
process.exit(0);
|
|
2073
|
+
}
|
|
2074
|
+
async function runRuntimeDiscovery(args, configFile) {
|
|
2075
|
+
const files = [];
|
|
2076
|
+
for (const arg of args) {
|
|
2077
|
+
const resolved = resolve4(arg);
|
|
2078
|
+
try {
|
|
2079
|
+
if (statSync(resolved).isDirectory()) {
|
|
2080
|
+
files.push(...findTsFiles(resolved));
|
|
2081
|
+
} else {
|
|
2082
|
+
files.push(resolved);
|
|
2083
|
+
}
|
|
2084
|
+
} catch {
|
|
2085
|
+
console.warn(`[rango] Skipping ${arg}: not found`);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
const routerEntries = [];
|
|
2089
|
+
for (const filePath of files) {
|
|
2090
|
+
try {
|
|
2091
|
+
const source = readFileSync5(filePath, "utf-8");
|
|
2092
|
+
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
2093
|
+
routerEntries.push(filePath);
|
|
2094
|
+
}
|
|
2095
|
+
if (source.includes("urls(")) {
|
|
2096
|
+
writePerModuleRouteTypesForFile(filePath);
|
|
2097
|
+
}
|
|
2098
|
+
} catch {
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
if (routerEntries.length === 0) {
|
|
2102
|
+
console.error("[rango] No router files found in the provided paths");
|
|
2103
|
+
process.exit(1);
|
|
2104
|
+
}
|
|
2105
|
+
const nestedRouterConflict = findNestedRouterConflict(routerEntries);
|
|
2106
|
+
if (nestedRouterConflict) {
|
|
2107
|
+
console.error(
|
|
2108
|
+
`
|
|
2109
|
+
${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
|
|
2110
|
+
`
|
|
2111
|
+
);
|
|
2112
|
+
process.exit(1);
|
|
2113
|
+
}
|
|
2114
|
+
let discoverAndWriteRouteTypes2;
|
|
2115
|
+
try {
|
|
2116
|
+
const mod = await Promise.resolve().then(() => (init_runtime_discovery(), runtime_discovery_exports));
|
|
2117
|
+
discoverAndWriteRouteTypes2 = mod.discoverAndWriteRouteTypes;
|
|
2118
|
+
} catch (err) {
|
|
2119
|
+
if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
|
|
2120
|
+
console.error(
|
|
2121
|
+
"[rango] Runtime discovery requires 'vite' and '@vitejs/plugin-rsc'.\nInstall them with: pnpm add -D vite @vitejs/plugin-rsc"
|
|
2122
|
+
);
|
|
2123
|
+
} else {
|
|
2124
|
+
console.error(`[rango] Failed to load runtime discovery: ${err.message}`);
|
|
2125
|
+
}
|
|
2126
|
+
process.exit(1);
|
|
2127
|
+
}
|
|
2128
|
+
for (const entry of routerEntries) {
|
|
2129
|
+
const projectRoot = findProjectRoot(entry);
|
|
2130
|
+
const result = await discoverAndWriteRouteTypes2({
|
|
2131
|
+
root: projectRoot,
|
|
2132
|
+
configFile,
|
|
2133
|
+
entry
|
|
2134
|
+
});
|
|
2135
|
+
console.log(
|
|
2136
|
+
`[rango] Runtime discovery: ${result.routerCount} router(s), ${result.routeCount} route(s)`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
function formatDiagnostics(diagnostics) {
|
|
2141
|
+
for (const d of diagnostics) {
|
|
2142
|
+
const prefix = d.namePrefix ? `${d.namePrefix}.*` : `${d.pathPrefix}*`;
|
|
2143
|
+
console.error(` ${prefix}`);
|
|
2144
|
+
console.error(` Reason: ${d.reason} -- ${d.detail}`);
|
|
2145
|
+
console.error(` Source: ${d.sourceFile}`);
|
|
2146
|
+
}
|
|
287
2147
|
}
|