@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -0
- package/README.md +126 -16
- package/dist/bin/rango.js +319 -95
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2724 -1053
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +11 -9
- package/skills/hooks/SKILL.md +243 -29
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +68 -19
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +190 -23
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +18 -10
- package/skills/migrate-nextjs/SKILL.md +43 -19
- package/skills/migrate-react-router/SKILL.md +8 -2
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +18 -7
- package/skills/prerender/SKILL.md +65 -60
- package/skills/rango/SKILL.md +251 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +115 -48
- package/skills/route/SKILL.md +46 -5
- package/skills/router-setup/SKILL.md +30 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -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 +84 -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/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +121 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +115 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +147 -128
- package/src/browser/prefetch/cache.ts +107 -56
- package/src/browser/prefetch/fetch.ts +204 -34
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +30 -7
- package/src/browser/react/NavigationProvider.tsx +283 -118
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +11 -11
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +91 -24
- package/src/browser/scroll-restoration.ts +30 -17
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +214 -55
- package/src/browser/types.ts +80 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +117 -14
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +117 -23
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +55 -28
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- 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 +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +36 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +96 -12
- package/src/index.ts +94 -14
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- 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 +34 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +82 -55
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +27 -43
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +10 -47
- package/src/router/loader-resolution.ts +155 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +9 -24
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +44 -74
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +247 -166
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +85 -94
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +104 -65
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +83 -35
- package/src/router/router-options.ts +136 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +97 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +272 -320
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +162 -64
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +110 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +264 -220
- package/src/rsc/helpers.ts +100 -6
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +117 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +88 -188
- package/src/rsc/rsc-rendering.ts +98 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +281 -117
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +23 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +31 -4
- package/src/segment-system.tsx +254 -143
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +163 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +287 -178
- package/src/ssr/index.tsx +21 -16
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -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 +183 -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 +581 -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 +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +29 -12
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +126 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +96 -68
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +44 -0
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +2 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +126 -8
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +109 -118
- package/src/vite/router-discovery.ts +718 -119
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +54 -39
- package/src/vite/utils/shared-utils.ts +90 -41
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -23,7 +23,7 @@ export function generatePerModuleTypesSource(
|
|
|
23
23
|
const valid = routes.filter(({ name }) => {
|
|
24
24
|
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
25
25
|
console.warn(
|
|
26
|
-
`[
|
|
26
|
+
`[rango] Skipping route with invalid name: ${JSON.stringify(name)}`,
|
|
27
27
|
);
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
@@ -42,7 +42,7 @@ export function generatePerModuleTypesSource(
|
|
|
42
42
|
for (const { name, pattern, params, search } of valid) {
|
|
43
43
|
if (deduped.has(name)) {
|
|
44
44
|
console.warn(
|
|
45
|
-
`[
|
|
45
|
+
`[rango] Duplicate route name "${name}" — keeping first definition`,
|
|
46
46
|
);
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
@@ -59,7 +59,7 @@ export function generatePerModuleTypesSource(
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Generates a .ts file that augments
|
|
62
|
+
* Generates a .ts file that augments Rango.GeneratedRouteMap
|
|
63
63
|
* with route name -> pattern mappings. This enables Handler<"routeName">
|
|
64
64
|
* without circular references since the file has no imports from the app.
|
|
65
65
|
*/
|
|
@@ -88,14 +88,25 @@ export function generateRouteTypesSource(
|
|
|
88
88
|
})
|
|
89
89
|
.join("\n");
|
|
90
90
|
|
|
91
|
+
// The global augmentation extends an alias of `typeof NamedRoutes` rather than
|
|
92
|
+
// `Readonly<typeof NamedRoutes>`. `as const` already makes the members readonly,
|
|
93
|
+
// so the two are behaviour-identical, but `Readonly<>` is a homomorphic mapped
|
|
94
|
+
// type the compiler instantiates once per route at the augmentation site
|
|
95
|
+
// (~4 instantiations/route). An interface `extends` clause cannot name a bare
|
|
96
|
+
// `typeof` query, so the alias is what lets us drop the wrapper. Measured: ~35%
|
|
97
|
+
// fewer type instantiations on a 14k-route app; negligible (but free) on small.
|
|
91
98
|
return `// Auto-generated by @rangojs/router - do not edit
|
|
92
99
|
export const NamedRoutes = {
|
|
93
100
|
${objectBody}
|
|
94
101
|
} as const;
|
|
95
102
|
|
|
103
|
+
// Aliased so the augmentation below does not pay a homomorphic mapped-type
|
|
104
|
+
// instantiation per route; \`as const\` already makes the members readonly.
|
|
105
|
+
type NamedRoutesShape = typeof NamedRoutes;
|
|
106
|
+
|
|
96
107
|
declare global {
|
|
97
|
-
namespace
|
|
98
|
-
interface GeneratedRouteMap extends
|
|
108
|
+
namespace Rango {
|
|
109
|
+
interface GeneratedRouteMap extends NamedRoutesShape {}
|
|
99
110
|
}
|
|
100
111
|
}
|
|
101
112
|
`;
|
|
@@ -22,6 +22,61 @@ export interface UnresolvableInclude {
|
|
|
22
22
|
detail: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Per-scan memo
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Per-scan memo shared across the include-resolution recursion. Keys are
|
|
31
|
+
* absolute file paths; values cache the readFileSync result and the
|
|
32
|
+
* ts.SourceFile parsed from the FULL file source. A separate blockSources map
|
|
33
|
+
* caches parses of extracted sub-blocks (urls() call text), keyed by the exact
|
|
34
|
+
* block string, so the two extractors (routes + includes) for the same block
|
|
35
|
+
* share a single parse.
|
|
36
|
+
*
|
|
37
|
+
* The memo is purely a performance accelerator: same input -> same parse, so
|
|
38
|
+
* generated output is identical to the un-memoized path. A fresh memo is
|
|
39
|
+
* created per top-level scan entry, so stale file edits between scans are never
|
|
40
|
+
* served.
|
|
41
|
+
*/
|
|
42
|
+
export interface ScanMemo {
|
|
43
|
+
files: Map<string, string>;
|
|
44
|
+
blockSourceFiles: Map<string, ts.SourceFile>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createScanMemo(): ScanMemo {
|
|
48
|
+
return { files: new Map(), blockSourceFiles: new Map() };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseBlock(memo: ScanMemo | undefined, block: string): ts.SourceFile {
|
|
52
|
+
if (memo) {
|
|
53
|
+
const cached = memo.blockSourceFiles.get(block);
|
|
54
|
+
if (cached) return cached;
|
|
55
|
+
}
|
|
56
|
+
const sf = ts.createSourceFile(
|
|
57
|
+
"input.tsx",
|
|
58
|
+
block,
|
|
59
|
+
ts.ScriptTarget.Latest,
|
|
60
|
+
true,
|
|
61
|
+
ts.ScriptKind.TSX,
|
|
62
|
+
);
|
|
63
|
+
if (memo) memo.blockSourceFiles.set(block, sf);
|
|
64
|
+
return sf;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readSourceMemoized(
|
|
68
|
+
memo: ScanMemo | undefined,
|
|
69
|
+
realPath: string,
|
|
70
|
+
): string {
|
|
71
|
+
if (memo) {
|
|
72
|
+
const cached = memo.files.get(realPath);
|
|
73
|
+
if (cached !== undefined) return cached;
|
|
74
|
+
}
|
|
75
|
+
const source = readFileSync(realPath, "utf-8");
|
|
76
|
+
if (memo) memo.files.set(realPath, source);
|
|
77
|
+
return source;
|
|
78
|
+
}
|
|
79
|
+
|
|
25
80
|
// ---------------------------------------------------------------------------
|
|
26
81
|
// AST-based include() parsing
|
|
27
82
|
// ---------------------------------------------------------------------------
|
|
@@ -47,7 +102,10 @@ function extractNamePrefixFromInclude(node: ts.CallExpression): string | null {
|
|
|
47
102
|
* Returns both resolved includes (identifier second args) and unresolvable
|
|
48
103
|
* includes (factory calls, etc.) with reasons.
|
|
49
104
|
*/
|
|
50
|
-
export function extractIncludesWithDiagnostics(
|
|
105
|
+
export function extractIncludesWithDiagnostics(
|
|
106
|
+
code: string,
|
|
107
|
+
sourceFileArg?: ts.SourceFile,
|
|
108
|
+
): {
|
|
51
109
|
resolved: Array<{
|
|
52
110
|
pathPrefix: string;
|
|
53
111
|
variableName: string;
|
|
@@ -60,13 +118,16 @@ export function extractIncludesWithDiagnostics(code: string): {
|
|
|
60
118
|
detail: string;
|
|
61
119
|
}>;
|
|
62
120
|
} {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
ts.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
121
|
+
// Reuse a caller-provided SourceFile (parsed once per scan) when given.
|
|
122
|
+
const sourceFile =
|
|
123
|
+
sourceFileArg ??
|
|
124
|
+
ts.createSourceFile(
|
|
125
|
+
"input.tsx",
|
|
126
|
+
code,
|
|
127
|
+
ts.ScriptTarget.Latest,
|
|
128
|
+
true,
|
|
129
|
+
ts.ScriptKind.TSX,
|
|
130
|
+
);
|
|
70
131
|
const resolved: Array<{
|
|
71
132
|
pathPrefix: string;
|
|
72
133
|
variableName: string;
|
|
@@ -140,7 +201,13 @@ export function resolveImportedVariable(
|
|
|
140
201
|
code: string,
|
|
141
202
|
localName: string,
|
|
142
203
|
): { specifier: string; exportedName: string } | null {
|
|
143
|
-
|
|
204
|
+
// Allow an optional leading default binding before the named-import brace so
|
|
205
|
+
// a combined `import Foo, { bar } from "..."` is matched (the named members
|
|
206
|
+
// are the only part we resolve; the default binding is skipped). Without the
|
|
207
|
+
// optional `(?:[\w$]+\s*,\s*)?` segment, the `Foo, ` prefix breaks the match
|
|
208
|
+
// and a legitimate static named import surfaces as `unresolvable-import`.
|
|
209
|
+
const importRegex =
|
|
210
|
+
/import\s*(?:[\w$]+\s*,\s*)?\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
144
211
|
let match;
|
|
145
212
|
|
|
146
213
|
while ((match = importRegex.exec(code)) !== null) {
|
|
@@ -206,14 +273,18 @@ export function resolveImportPath(
|
|
|
206
273
|
function extractUrlsBlockForVariable(
|
|
207
274
|
code: string,
|
|
208
275
|
varName: string,
|
|
276
|
+
sourceFileArg?: ts.SourceFile,
|
|
209
277
|
): string | null {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
ts.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
278
|
+
// Reuse a caller-provided full-source SourceFile (parsed once per scan).
|
|
279
|
+
const sourceFile =
|
|
280
|
+
sourceFileArg ??
|
|
281
|
+
ts.createSourceFile(
|
|
282
|
+
"input.tsx",
|
|
283
|
+
code,
|
|
284
|
+
ts.ScriptTarget.Latest,
|
|
285
|
+
true,
|
|
286
|
+
ts.ScriptKind.TSX,
|
|
287
|
+
);
|
|
217
288
|
let result: string | null = null;
|
|
218
289
|
|
|
219
290
|
function visit(node: ts.Node) {
|
|
@@ -249,11 +320,15 @@ function buildRouteMapFromBlock(
|
|
|
249
320
|
visited: Set<string>,
|
|
250
321
|
searchSchemasOut?: Record<string, Record<string, string>>,
|
|
251
322
|
diagnosticsOut?: UnresolvableInclude[],
|
|
323
|
+
memo?: ScanMemo,
|
|
252
324
|
): Record<string, string> {
|
|
253
325
|
const routeMap: Record<string, string> = {};
|
|
254
326
|
|
|
327
|
+
// Parse the block once and share the SourceFile between both extractors.
|
|
328
|
+
const blockSourceFile = parseBlock(memo, block);
|
|
329
|
+
|
|
255
330
|
// Extract local path() routes
|
|
256
|
-
const localRoutes = extractRoutesFromSource(block);
|
|
331
|
+
const localRoutes = extractRoutesFromSource(block, blockSourceFile);
|
|
257
332
|
for (const { name, pattern, search } of localRoutes) {
|
|
258
333
|
routeMap[name] = pattern;
|
|
259
334
|
if (search && searchSchemasOut) {
|
|
@@ -262,8 +337,10 @@ function buildRouteMapFromBlock(
|
|
|
262
337
|
}
|
|
263
338
|
|
|
264
339
|
// Extract include() calls with diagnostics for unresolvable ones
|
|
265
|
-
const { resolved: includes, unresolvable } =
|
|
266
|
-
|
|
340
|
+
const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
|
|
341
|
+
block,
|
|
342
|
+
blockSourceFile,
|
|
343
|
+
);
|
|
267
344
|
|
|
268
345
|
if (diagnosticsOut) {
|
|
269
346
|
for (const entry of unresolvable) {
|
|
@@ -298,12 +375,17 @@ function buildRouteMapFromBlock(
|
|
|
298
375
|
imported.exportedName,
|
|
299
376
|
visited,
|
|
300
377
|
diagnosticsOut,
|
|
378
|
+
undefined,
|
|
379
|
+
memo,
|
|
301
380
|
);
|
|
302
381
|
} else {
|
|
303
|
-
// Check if variable exists as a same-file urls() definition
|
|
382
|
+
// Check if variable exists as a same-file urls() definition. Share the
|
|
383
|
+
// full-source SourceFile parse via the memo (block === fullSource hits the
|
|
384
|
+
// same cache entry already used above).
|
|
304
385
|
const sameFileBlock = extractUrlsBlockForVariable(
|
|
305
386
|
fullSource,
|
|
306
387
|
variableName,
|
|
388
|
+
parseBlock(memo, fullSource),
|
|
307
389
|
);
|
|
308
390
|
if (!sameFileBlock) {
|
|
309
391
|
if (diagnosticsOut) {
|
|
@@ -322,6 +404,8 @@ function buildRouteMapFromBlock(
|
|
|
322
404
|
variableName,
|
|
323
405
|
visited,
|
|
324
406
|
diagnosticsOut,
|
|
407
|
+
undefined,
|
|
408
|
+
memo,
|
|
325
409
|
);
|
|
326
410
|
}
|
|
327
411
|
|
|
@@ -361,6 +445,9 @@ function buildRouteMapFromBlock(
|
|
|
361
445
|
* @param inlineBlock - Optional pre-extracted code block (e.g. from an inline
|
|
362
446
|
* builder function). When provided, variableName is ignored and the block
|
|
363
447
|
* is parsed directly for path()/include() calls.
|
|
448
|
+
* @param memo - Per-scan readFileSync/parse memo. A fresh one is created at the
|
|
449
|
+
* top-level entry and threaded through the recursion so each file is read and
|
|
450
|
+
* parsed once per scan. Behavior-preserving (same input -> same output).
|
|
364
451
|
*/
|
|
365
452
|
export function buildCombinedRouteMapWithSearch(
|
|
366
453
|
filePath: string,
|
|
@@ -368,22 +455,24 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
368
455
|
visited?: Set<string>,
|
|
369
456
|
diagnosticsOut?: UnresolvableInclude[],
|
|
370
457
|
inlineBlock?: string,
|
|
458
|
+
memo?: ScanMemo,
|
|
371
459
|
): {
|
|
372
460
|
routes: Record<string, string>;
|
|
373
461
|
searchSchemas: Record<string, Record<string, string>>;
|
|
374
462
|
} {
|
|
375
463
|
visited = visited ?? new Set();
|
|
464
|
+
memo = memo ?? createScanMemo();
|
|
376
465
|
const realPath = resolve(filePath);
|
|
377
466
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
378
467
|
if (visited.has(key)) {
|
|
379
|
-
console.warn(`[
|
|
468
|
+
console.warn(`[rango] Circular include detected, skipping: ${key}`);
|
|
380
469
|
return { routes: {}, searchSchemas: {} };
|
|
381
470
|
}
|
|
382
471
|
visited.add(key);
|
|
383
472
|
|
|
384
473
|
let source: string;
|
|
385
474
|
try {
|
|
386
|
-
source =
|
|
475
|
+
source = readSourceMemoized(memo, realPath);
|
|
387
476
|
} catch {
|
|
388
477
|
return { routes: {}, searchSchemas: {} };
|
|
389
478
|
}
|
|
@@ -392,7 +481,11 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
392
481
|
if (inlineBlock) {
|
|
393
482
|
block = inlineBlock;
|
|
394
483
|
} else if (variableName) {
|
|
395
|
-
const extracted = extractUrlsBlockForVariable(
|
|
484
|
+
const extracted = extractUrlsBlockForVariable(
|
|
485
|
+
source,
|
|
486
|
+
variableName,
|
|
487
|
+
parseBlock(memo, source),
|
|
488
|
+
);
|
|
396
489
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
397
490
|
block = extracted;
|
|
398
491
|
} else {
|
|
@@ -407,6 +500,7 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
407
500
|
visited,
|
|
408
501
|
searchSchemas,
|
|
409
502
|
diagnosticsOut,
|
|
503
|
+
memo,
|
|
410
504
|
);
|
|
411
505
|
|
|
412
506
|
// Remove from visited so sibling branches can include the same variable
|
|
@@ -37,12 +37,15 @@ export function formatRouteEntry(
|
|
|
37
37
|
): string {
|
|
38
38
|
const hasSearch = search && Object.keys(search).length > 0;
|
|
39
39
|
|
|
40
|
+
// JSON.stringify the pattern and search values so backslashes and quotes in a
|
|
41
|
+
// route pattern (e.g. a custom regex constraint) survive interpolation into
|
|
42
|
+
// both the type-level string and the runtime NamedRoutes value.
|
|
40
43
|
if (!hasSearch) {
|
|
41
|
-
return ` ${key}:
|
|
44
|
+
return ` ${key}: ${JSON.stringify(pattern)},`;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
const searchBody = Object.entries(search!)
|
|
45
|
-
.map(([k, v]) => `${k}:
|
|
48
|
+
.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)
|
|
46
49
|
.join(", ");
|
|
47
|
-
return ` ${key}: { path:
|
|
50
|
+
return ` ${key}: { path: ${JSON.stringify(pattern)}, search: { ${searchBody} } },`;
|
|
48
51
|
}
|
|
@@ -3,7 +3,10 @@ import ts from "typescript";
|
|
|
3
3
|
import { extractParamsFromPattern } from "./param-extraction.js";
|
|
4
4
|
import { extractRoutesFromSource } from "./ast-route-extraction.js";
|
|
5
5
|
import { generatePerModuleTypesSource } from "./codegen.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
buildCombinedRouteMapWithSearch,
|
|
8
|
+
createScanMemo,
|
|
9
|
+
} from "./include-resolution.js";
|
|
7
10
|
import type { ScanFilter } from "./scan-filter.js";
|
|
8
11
|
import { findTsFiles } from "./scan-filter.js";
|
|
9
12
|
|
|
@@ -78,10 +81,20 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
78
81
|
if (varNames.length > 0) {
|
|
79
82
|
// Follow includes recursively via the combined route map builder.
|
|
80
83
|
// The visited set in buildCombinedRouteMapWithSearch prevents infinite loops.
|
|
84
|
+
// Share one per-file scan memo across all urls() variables so a shared
|
|
85
|
+
// include target is read+parsed once for this file, not once per variable.
|
|
81
86
|
routes = [];
|
|
87
|
+
const memo = createScanMemo();
|
|
82
88
|
for (const varName of varNames) {
|
|
83
89
|
const { routes: routeMap, searchSchemas } =
|
|
84
|
-
buildCombinedRouteMapWithSearch(
|
|
90
|
+
buildCombinedRouteMapWithSearch(
|
|
91
|
+
filePath,
|
|
92
|
+
varName,
|
|
93
|
+
undefined,
|
|
94
|
+
undefined,
|
|
95
|
+
undefined,
|
|
96
|
+
memo,
|
|
97
|
+
);
|
|
85
98
|
for (const [name, pattern] of Object.entries(routeMap)) {
|
|
86
99
|
const params = extractParamsFromPattern(pattern);
|
|
87
100
|
routes.push({
|
|
@@ -97,7 +110,10 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
97
110
|
routes = extractRoutesFromSource(source);
|
|
98
111
|
}
|
|
99
112
|
|
|
100
|
-
|
|
113
|
+
// Match .ts/.tsx/.js/.jsx (same as router-processing.ts / router-transform.ts).
|
|
114
|
+
// Without the jsx? branch a .jsx/.js source produced genPath === filePath,
|
|
115
|
+
// overwriting the source file instead of writing a sibling .gen.ts.
|
|
116
|
+
const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
|
|
101
117
|
|
|
102
118
|
// When a urls() variable was found but static resolution yields zero
|
|
103
119
|
// routes, write an empty placeholder so generated imports stay
|
|
@@ -106,7 +122,7 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
106
122
|
if (varNames.length > 0 && !existsSync(genPath)) {
|
|
107
123
|
writeFileSync(genPath, generatePerModuleTypesSource([]));
|
|
108
124
|
console.log(
|
|
109
|
-
`[
|
|
125
|
+
`[rango] Generated route types (placeholder) -> ${genPath}`,
|
|
110
126
|
);
|
|
111
127
|
}
|
|
112
128
|
return;
|
|
@@ -118,11 +134,11 @@ export function writePerModuleRouteTypesForFile(filePath: string): void {
|
|
|
118
134
|
: null;
|
|
119
135
|
if (existing !== genSource) {
|
|
120
136
|
writeFileSync(genPath, genSource);
|
|
121
|
-
console.log(`[
|
|
137
|
+
console.log(`[rango] Generated route types -> ${genPath}`);
|
|
122
138
|
}
|
|
123
139
|
} catch (err) {
|
|
124
140
|
console.warn(
|
|
125
|
-
`[
|
|
141
|
+
`[rango] Failed to generate route types for ${filePath}: ${(err as Error).message}`,
|
|
126
142
|
);
|
|
127
143
|
}
|
|
128
144
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import ts from "typescript";
|
|
16
16
|
import { generateRouteTypesSource } from "./codegen.js";
|
|
17
17
|
import type { ScanFilter } from "./scan-filter.js";
|
|
18
|
+
import { firstCodeMatchIndex } from "./source-scan.js";
|
|
18
19
|
import {
|
|
19
20
|
resolveImportedVariable,
|
|
20
21
|
resolveImportPath,
|
|
@@ -38,6 +39,8 @@ function countPublicRouteEntries(source: string): number {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
|
|
42
|
+
// Global variant for the code-region scan (firstCodeMatchIndex sets lastIndex).
|
|
43
|
+
const ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
|
|
41
44
|
|
|
42
45
|
function isRoutableSourceFile(name: string): boolean {
|
|
43
46
|
return (
|
|
@@ -61,7 +64,7 @@ function findRouterFilesRecursive(
|
|
|
61
64
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
62
65
|
} catch (err) {
|
|
63
66
|
console.warn(
|
|
64
|
-
`[
|
|
67
|
+
`[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
|
|
65
68
|
);
|
|
66
69
|
return;
|
|
67
70
|
}
|
|
@@ -90,7 +93,17 @@ function findRouterFilesRecursive(
|
|
|
90
93
|
|
|
91
94
|
try {
|
|
92
95
|
const source = readFileSync(fullPath, "utf-8");
|
|
93
|
-
|
|
96
|
+
// Fast path: most files contain no `createRouter(` at all, so the cheap
|
|
97
|
+
// raw regex short-circuits before the code-region scan. Only a file that
|
|
98
|
+
// mentions the token (real call OR a comment/string mention) is rescanned
|
|
99
|
+
// over code regions — allocation-free, never building a stripped copy —
|
|
100
|
+
// so a mention inside a comment or string is not mistaken for a real
|
|
101
|
+
// router file (which previously triggered a spurious "Multiple routers
|
|
102
|
+
// found" error).
|
|
103
|
+
if (
|
|
104
|
+
ROUTER_CALL_PATTERN.test(source) &&
|
|
105
|
+
firstCodeMatchIndex(source, ROUTER_CALL_PATTERN_G) >= 0
|
|
106
|
+
) {
|
|
94
107
|
routerFilesInDir.push(fullPath);
|
|
95
108
|
}
|
|
96
109
|
} catch {
|
|
@@ -142,7 +155,7 @@ export function findNestedRouterConflict(
|
|
|
142
155
|
|
|
143
156
|
export function formatNestedRouterConflictError(
|
|
144
157
|
conflict: { ancestor: string; nested: string },
|
|
145
|
-
prefix = "[
|
|
158
|
+
prefix = "[rango]",
|
|
146
159
|
): string {
|
|
147
160
|
return (
|
|
148
161
|
`${prefix} Nested router roots are not supported.\n` +
|
|
@@ -309,12 +322,6 @@ export function extractBasenameFromRouter(code: string): string | undefined {
|
|
|
309
322
|
return result;
|
|
310
323
|
}
|
|
311
324
|
|
|
312
|
-
/** @deprecated Use extractUrlsFromRouter instead */
|
|
313
|
-
export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
314
|
-
const result = extractUrlsFromRouter(code);
|
|
315
|
-
return result?.kind === "variable" ? result.name : null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
325
|
/** Apply a basename prefix to all route patterns in a result set. */
|
|
319
326
|
function applyBasenameToRoutes(
|
|
320
327
|
result: {
|
|
@@ -339,6 +346,36 @@ function applyBasenameToRoutes(
|
|
|
339
346
|
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
340
347
|
}
|
|
341
348
|
|
|
349
|
+
// Filesystem path of the generated route-types file for a router source file.
|
|
350
|
+
// Native separators — matches the self-gen-tracking Map key the watcher compares.
|
|
351
|
+
export function genFileTsPath(sourceFile: string): string {
|
|
352
|
+
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
353
|
+
return join(dirname(sourceFile), `${base}.named-routes.gen.ts`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Search schemas for the gen file: prefer the runtime manifest's; when it omits
|
|
357
|
+
// them (some module-runner flows) fall back to static parsing filtered to the
|
|
358
|
+
// public route-name set. Returns the runtime value unchanged otherwise.
|
|
359
|
+
export function resolveSearchSchemas(
|
|
360
|
+
publicRouteNames: string[],
|
|
361
|
+
runtimeSchemas: Record<string, Record<string, string>> | undefined,
|
|
362
|
+
sourceFile: string,
|
|
363
|
+
): Record<string, Record<string, string>> | undefined {
|
|
364
|
+
if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
|
|
365
|
+
return runtimeSchemas;
|
|
366
|
+
}
|
|
367
|
+
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
368
|
+
if (Object.keys(staticParsed.searchSchemas).length === 0) {
|
|
369
|
+
return runtimeSchemas;
|
|
370
|
+
}
|
|
371
|
+
const filtered: Record<string, Record<string, string>> = {};
|
|
372
|
+
for (const name of publicRouteNames) {
|
|
373
|
+
const schema = staticParsed.searchSchemas[name];
|
|
374
|
+
if (schema) filtered[name] = schema;
|
|
375
|
+
}
|
|
376
|
+
return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
|
|
377
|
+
}
|
|
378
|
+
|
|
342
379
|
/**
|
|
343
380
|
* Resolve routes and search schemas from a router source file by following the
|
|
344
381
|
* variable passed to `.routes(...)` or `urls: ...` in createRouter options,
|
|
@@ -528,7 +565,10 @@ export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
|
|
|
528
565
|
export function writeCombinedRouteTypes(
|
|
529
566
|
root: string,
|
|
530
567
|
knownRouterFiles?: string[],
|
|
531
|
-
opts?: {
|
|
568
|
+
opts?: {
|
|
569
|
+
preserveIfLarger?: boolean;
|
|
570
|
+
onWrite?: (outPath: string, content: string) => void;
|
|
571
|
+
},
|
|
532
572
|
): void {
|
|
533
573
|
// Delete old combined named-routes.gen.ts if it exists (stale from older versions)
|
|
534
574
|
try {
|
|
@@ -536,7 +576,7 @@ export function writeCombinedRouteTypes(
|
|
|
536
576
|
if (existsSync(oldCombinedPath)) {
|
|
537
577
|
unlinkSync(oldCombinedPath);
|
|
538
578
|
console.log(
|
|
539
|
-
`[
|
|
579
|
+
`[rango] Removed stale combined route types: ${oldCombinedPath}`,
|
|
540
580
|
);
|
|
541
581
|
}
|
|
542
582
|
} catch {}
|
|
@@ -566,24 +606,15 @@ export function writeCombinedRouteTypes(
|
|
|
566
606
|
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
567
607
|
}
|
|
568
608
|
|
|
569
|
-
const
|
|
570
|
-
/\.(tsx?|jsx?)$/,
|
|
571
|
-
"",
|
|
572
|
-
);
|
|
573
|
-
const outPath = join(
|
|
574
|
-
dirname(routerFilePath),
|
|
575
|
-
`${routerBasename}.named-routes.gen.ts`,
|
|
576
|
-
);
|
|
609
|
+
const outPath = genFileTsPath(routerFilePath);
|
|
577
610
|
const existing = existsSync(outPath)
|
|
578
611
|
? readFileSync(outPath, "utf-8")
|
|
579
612
|
: null;
|
|
580
613
|
|
|
581
|
-
// When the static parser can't extract routes (e.g. callback-style urls()),
|
|
582
|
-
// write an empty placeholder so the build-time transform's injected import
|
|
583
|
-
// resolves. Runtime discovery will overwrite this with the real routes.
|
|
584
614
|
if (Object.keys(result.routes).length === 0) {
|
|
585
615
|
if (!existing) {
|
|
586
616
|
const emptySource = generateRouteTypesSource({});
|
|
617
|
+
opts?.onWrite?.(outPath, emptySource);
|
|
587
618
|
writeFileSync(outPath, emptySource);
|
|
588
619
|
}
|
|
589
620
|
continue;
|
|
@@ -595,11 +626,6 @@ export function writeCombinedRouteTypes(
|
|
|
595
626
|
hasSearchSchemas ? result.searchSchemas : undefined,
|
|
596
627
|
);
|
|
597
628
|
if (existing !== source) {
|
|
598
|
-
// On initial dev startup, don't overwrite a file from runtime discovery
|
|
599
|
-
// (which has all dynamic routes) with a smaller set from the static
|
|
600
|
-
// parser. The static parser can't see routes generated by Array.from()
|
|
601
|
-
// or other dynamic code. During HMR (file watcher), always write so
|
|
602
|
-
// newly added routes appear immediately.
|
|
603
629
|
if (opts?.preserveIfLarger && existing) {
|
|
604
630
|
const existingCount = countPublicRouteEntries(existing);
|
|
605
631
|
const newCount = Object.keys(result.routes).filter(
|
|
@@ -609,9 +635,10 @@ export function writeCombinedRouteTypes(
|
|
|
609
635
|
continue;
|
|
610
636
|
}
|
|
611
637
|
}
|
|
638
|
+
opts?.onWrite?.(outPath, source);
|
|
612
639
|
writeFileSync(outPath, source);
|
|
613
640
|
console.log(
|
|
614
|
-
`[
|
|
641
|
+
`[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`,
|
|
615
642
|
);
|
|
616
643
|
}
|
|
617
644
|
}
|
|
@@ -54,7 +54,7 @@ export function findTsFiles(dir: string, filter?: ScanFilter): string[] {
|
|
|
54
54
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
55
55
|
} catch (err) {
|
|
56
56
|
console.warn(
|
|
57
|
-
`[
|
|
57
|
+
`[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
|
|
58
58
|
);
|
|
59
59
|
return results;
|
|
60
60
|
}
|