@rangojs/router 0.0.0-experimental.a769fbe7 → 0.0.0-experimental.ac99d918
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/README.md +211 -43
- package/dist/bin/rango.js +279 -102
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3313 -1160
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +62 -11
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +63 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +222 -30
- package/skills/caching/SKILL.md +263 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +124 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +223 -9
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +77 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +66 -5
- package/skills/router-setup/SKILL.md +38 -3
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -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 +121 -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 +120 -0
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/vercel/SKILL.md +107 -0
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +1 -66
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +81 -147
- 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 +66 -14
- package/src/browser/navigation-client.ts +172 -109
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/partial-update.ts +79 -93
- package/src/browser/prefetch/cache.ts +180 -62
- package/src/browser/prefetch/fetch.ts +252 -39
- package/src/browser/prefetch/queue.ts +42 -8
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +72 -10
- package/src/browser/react/NavigationProvider.tsx +83 -31
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +111 -24
- package/src/browser/scroll-restoration.ts +29 -19
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -62
- package/src/browser/types.ts +60 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +6 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +333 -94
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +134 -32
- package/src/cache/cache-scope.ts +100 -74
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2256 -241
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +61 -20
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +22 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +17 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/browser.ts +0 -1
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- 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 +69 -10
- package/src/index.ts +112 -14
- package/src/internal-debug.ts +2 -4
- 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/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +413 -275
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +107 -32
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +50 -8
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +46 -30
- package/src/router/intercept-resolution.ts +23 -23
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +207 -30
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +161 -262
- package/src/router/match-middleware/cache-store.ts +3 -33
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +87 -39
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -140
- package/src/router/middleware.ts +169 -140
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +119 -65
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +40 -37
- package/src/router/segment-resolution/revalidation.ts +329 -305
- 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 +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +128 -42
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -409
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +18 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +28 -4
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +53 -56
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -69
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +22 -9
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +208 -201
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +211 -52
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +289 -124
- package/src/ssr/index.tsx +22 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -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 +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -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 +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -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 +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +124 -70
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +40 -11
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +36 -15
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -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 +192 -99
- 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 +51 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +236 -6
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +65 -207
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +32 -23
- package/src/vite/plugins/virtual-entries.ts +46 -17
- package/src/vite/rango.ts +207 -125
- package/src/vite/router-discovery.ts +931 -133
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- 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/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 +27 -29
- package/src/vite/utils/shared-utils.ts +97 -44
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/debug-channel.ts +0 -93
- 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/router/middleware-cookies.ts +0 -55
|
@@ -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` +
|
|
@@ -157,13 +170,26 @@ export function formatNestedRouterConflictError(
|
|
|
157
170
|
// ---------------------------------------------------------------------------
|
|
158
171
|
|
|
159
172
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
173
|
+
* Result of extracting URL patterns from a router file.
|
|
174
|
+
* - "variable": a named variable reference (e.g., `.routes(patterns)` or `urls: patterns`)
|
|
175
|
+
* - "inline": an inline builder function (e.g., `.routes(({ path }) => [...])` or `urls: ({ path }) => [...]`)
|
|
176
|
+
*/
|
|
177
|
+
export type UrlsExtractionResult =
|
|
178
|
+
| { kind: "variable"; name: string }
|
|
179
|
+
| { kind: "inline"; block: string };
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Extract the url patterns from a router file using AST.
|
|
183
|
+
* Detects four patterns:
|
|
162
184
|
* 1. createRouter(...).routes(variableName)
|
|
163
185
|
* 2. createRouter({ urls: variableName, ... })
|
|
164
|
-
*
|
|
186
|
+
* 3. createRouter(...).routes(({ path, ... }) => [...])
|
|
187
|
+
* 4. createRouter({ urls: ({ path, ... }) => [...], ... })
|
|
188
|
+
* Returns either a variable name or an inline code block.
|
|
165
189
|
*/
|
|
166
|
-
export function
|
|
190
|
+
export function extractUrlsFromRouter(
|
|
191
|
+
code: string,
|
|
192
|
+
): UrlsExtractionResult | null {
|
|
167
193
|
const sourceFile = ts.createSourceFile(
|
|
168
194
|
"router.tsx",
|
|
169
195
|
code,
|
|
@@ -171,7 +197,7 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
|
171
197
|
true,
|
|
172
198
|
ts.ScriptKind.TSX,
|
|
173
199
|
);
|
|
174
|
-
let result:
|
|
200
|
+
let result: UrlsExtractionResult | null = null;
|
|
175
201
|
|
|
176
202
|
function isCreateRouterCall(node: ts.Node): boolean {
|
|
177
203
|
if (!ts.isCallExpression(node)) return false;
|
|
@@ -179,44 +205,108 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
|
179
205
|
return ts.isIdentifier(callee) && callee.text === "createRouter";
|
|
180
206
|
}
|
|
181
207
|
|
|
208
|
+
/** Check if a node is an arrow/function expression (inline builder). */
|
|
209
|
+
function isInlineBuilder(node: ts.Node): boolean {
|
|
210
|
+
return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Check if a .routes() call chains from createRouter(). */
|
|
214
|
+
function isRoutesOnCreateRouter(node: ts.CallExpression): boolean {
|
|
215
|
+
if (
|
|
216
|
+
!ts.isPropertyAccessExpression(node.expression) ||
|
|
217
|
+
node.expression.name.text !== "routes"
|
|
218
|
+
)
|
|
219
|
+
return false;
|
|
220
|
+
let inner: ts.Expression = node.expression.expression;
|
|
221
|
+
while (
|
|
222
|
+
ts.isCallExpression(inner) &&
|
|
223
|
+
ts.isPropertyAccessExpression(inner.expression)
|
|
224
|
+
) {
|
|
225
|
+
inner = inner.expression.expression;
|
|
226
|
+
}
|
|
227
|
+
return isCreateRouterCall(inner);
|
|
228
|
+
}
|
|
229
|
+
|
|
182
230
|
function visit(node: ts.Node) {
|
|
183
231
|
if (result) return;
|
|
184
232
|
|
|
185
|
-
// Pattern 1: createRouter(...).routes(variableName)
|
|
186
|
-
// The AST shape is CallExpression(.routes) -> PropertyAccessExpression -> CallExpression(createRouter)
|
|
233
|
+
// Pattern 1 & 3: createRouter(...).routes(variableName | builder)
|
|
187
234
|
if (
|
|
188
235
|
ts.isCallExpression(node) &&
|
|
189
|
-
ts.isPropertyAccessExpression(node.expression) &&
|
|
190
|
-
node.expression.name.text === "routes" &&
|
|
191
236
|
node.arguments.length >= 1 &&
|
|
192
|
-
|
|
237
|
+
isRoutesOnCreateRouter(node)
|
|
193
238
|
) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
ts.isPropertyAccessExpression(inner.expression)
|
|
200
|
-
) {
|
|
201
|
-
inner = inner.expression.expression;
|
|
202
|
-
}
|
|
203
|
-
if (isCreateRouterCall(inner)) {
|
|
204
|
-
result = (node.arguments[0] as ts.Identifier).text;
|
|
205
|
-
return;
|
|
239
|
+
const arg = node.arguments[0];
|
|
240
|
+
if (ts.isIdentifier(arg)) {
|
|
241
|
+
result = { kind: "variable", name: arg.text };
|
|
242
|
+
} else if (isInlineBuilder(arg)) {
|
|
243
|
+
result = { kind: "inline", block: arg.getText(sourceFile) };
|
|
206
244
|
}
|
|
245
|
+
return;
|
|
207
246
|
}
|
|
208
247
|
|
|
209
|
-
// Pattern 2: createRouter({ urls: variableName, ... })
|
|
248
|
+
// Pattern 2 & 4: createRouter({ urls: variableName | builder, ... })
|
|
210
249
|
if (isCreateRouterCall(node)) {
|
|
211
250
|
const callExpr = node as ts.CallExpression;
|
|
212
|
-
for (const
|
|
251
|
+
for (const callArg of callExpr.arguments) {
|
|
252
|
+
if (ts.isObjectLiteralExpression(callArg)) {
|
|
253
|
+
for (const prop of callArg.properties) {
|
|
254
|
+
if (
|
|
255
|
+
ts.isPropertyAssignment(prop) &&
|
|
256
|
+
ts.isIdentifier(prop.name) &&
|
|
257
|
+
prop.name.text === "urls"
|
|
258
|
+
) {
|
|
259
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
260
|
+
result = { kind: "variable", name: prop.initializer.text };
|
|
261
|
+
} else if (isInlineBuilder(prop.initializer)) {
|
|
262
|
+
result = {
|
|
263
|
+
kind: "inline",
|
|
264
|
+
block: prop.initializer.getText(sourceFile),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
ts.forEachChild(node, visit);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
visit(sourceFile);
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Extract the `basename` string literal from createRouter({ basename: "..." }).
|
|
283
|
+
* Returns the basename value or undefined if not present.
|
|
284
|
+
*/
|
|
285
|
+
export function extractBasenameFromRouter(code: string): string | undefined {
|
|
286
|
+
const sourceFile = ts.createSourceFile(
|
|
287
|
+
"router.tsx",
|
|
288
|
+
code,
|
|
289
|
+
ts.ScriptTarget.Latest,
|
|
290
|
+
true,
|
|
291
|
+
ts.ScriptKind.TSX,
|
|
292
|
+
);
|
|
293
|
+
let result: string | undefined;
|
|
294
|
+
|
|
295
|
+
function visit(node: ts.Node) {
|
|
296
|
+
if (result !== undefined) return;
|
|
297
|
+
if (
|
|
298
|
+
ts.isCallExpression(node) &&
|
|
299
|
+
ts.isIdentifier(node.expression) &&
|
|
300
|
+
node.expression.text === "createRouter"
|
|
301
|
+
) {
|
|
302
|
+
for (const arg of node.arguments) {
|
|
213
303
|
if (ts.isObjectLiteralExpression(arg)) {
|
|
214
304
|
for (const prop of arg.properties) {
|
|
215
305
|
if (
|
|
216
306
|
ts.isPropertyAssignment(prop) &&
|
|
217
307
|
ts.isIdentifier(prop.name) &&
|
|
218
|
-
prop.name.text === "
|
|
219
|
-
ts.
|
|
308
|
+
prop.name.text === "basename" &&
|
|
309
|
+
ts.isStringLiteral(prop.initializer)
|
|
220
310
|
) {
|
|
221
311
|
result = prop.initializer.text;
|
|
222
312
|
return;
|
|
@@ -225,7 +315,6 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
|
225
315
|
}
|
|
226
316
|
}
|
|
227
317
|
}
|
|
228
|
-
|
|
229
318
|
ts.forEachChild(node, visit);
|
|
230
319
|
}
|
|
231
320
|
|
|
@@ -233,9 +322,70 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
|
233
322
|
return result;
|
|
234
323
|
}
|
|
235
324
|
|
|
325
|
+
/** @deprecated Use extractUrlsFromRouter instead */
|
|
326
|
+
export function extractUrlsVariableFromRouter(code: string): string | null {
|
|
327
|
+
const result = extractUrlsFromRouter(code);
|
|
328
|
+
return result?.kind === "variable" ? result.name : null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Apply a basename prefix to all route patterns in a result set. */
|
|
332
|
+
function applyBasenameToRoutes(
|
|
333
|
+
result: {
|
|
334
|
+
routes: Record<string, string>;
|
|
335
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
336
|
+
},
|
|
337
|
+
basename: string,
|
|
338
|
+
): {
|
|
339
|
+
routes: Record<string, string>;
|
|
340
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
341
|
+
} {
|
|
342
|
+
const prefixed: Record<string, string> = {};
|
|
343
|
+
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
344
|
+
if (pattern === "/") {
|
|
345
|
+
prefixed[name] = basename;
|
|
346
|
+
} else if (basename.endsWith("/") && pattern.startsWith("/")) {
|
|
347
|
+
prefixed[name] = basename + pattern.slice(1);
|
|
348
|
+
} else {
|
|
349
|
+
prefixed[name] = basename + pattern;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Filesystem path of the generated route-types file for a router source file.
|
|
356
|
+
// Native separators — matches the self-gen-tracking Map key the watcher compares.
|
|
357
|
+
export function genFileTsPath(sourceFile: string): string {
|
|
358
|
+
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
359
|
+
return join(dirname(sourceFile), `${base}.named-routes.gen.ts`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Search schemas for the gen file: prefer the runtime manifest's; when it omits
|
|
363
|
+
// them (some module-runner flows) fall back to static parsing filtered to the
|
|
364
|
+
// public route-name set. Returns the runtime value unchanged otherwise.
|
|
365
|
+
export function resolveSearchSchemas(
|
|
366
|
+
publicRouteNames: string[],
|
|
367
|
+
runtimeSchemas: Record<string, Record<string, string>> | undefined,
|
|
368
|
+
sourceFile: string,
|
|
369
|
+
): Record<string, Record<string, string>> | undefined {
|
|
370
|
+
if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
|
|
371
|
+
return runtimeSchemas;
|
|
372
|
+
}
|
|
373
|
+
const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
|
|
374
|
+
if (Object.keys(staticParsed.searchSchemas).length === 0) {
|
|
375
|
+
return runtimeSchemas;
|
|
376
|
+
}
|
|
377
|
+
const filtered: Record<string, Record<string, string>> = {};
|
|
378
|
+
for (const name of publicRouteNames) {
|
|
379
|
+
const schema = staticParsed.searchSchemas[name];
|
|
380
|
+
if (schema) filtered[name] = schema;
|
|
381
|
+
}
|
|
382
|
+
return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
|
|
383
|
+
}
|
|
384
|
+
|
|
236
385
|
/**
|
|
237
386
|
* Resolve routes and search schemas from a router source file by following the
|
|
238
|
-
* variable passed to `.routes(...)` or `urls: ...` in createRouter options
|
|
387
|
+
* variable passed to `.routes(...)` or `urls: ...` in createRouter options,
|
|
388
|
+
* or by parsing an inline builder function directly.
|
|
239
389
|
*/
|
|
240
390
|
export function buildCombinedRouteMapForRouterFile(routerFilePath: string): {
|
|
241
391
|
routes: Record<string, string>;
|
|
@@ -248,21 +398,54 @@ export function buildCombinedRouteMapForRouterFile(routerFilePath: string): {
|
|
|
248
398
|
return { routes: {}, searchSchemas: {} };
|
|
249
399
|
}
|
|
250
400
|
|
|
251
|
-
const
|
|
252
|
-
if (!
|
|
401
|
+
const extraction = extractUrlsFromRouter(routerSource);
|
|
402
|
+
if (!extraction) {
|
|
253
403
|
return { routes: {}, searchSchemas: {} };
|
|
254
404
|
}
|
|
255
405
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
406
|
+
// Detect basename from createRouter({ basename: "..." })
|
|
407
|
+
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
408
|
+
const basename = rawBasename
|
|
409
|
+
? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "")
|
|
410
|
+
: undefined;
|
|
411
|
+
|
|
412
|
+
let result: {
|
|
413
|
+
routes: Record<string, string>;
|
|
414
|
+
searchSchemas: Record<string, Record<string, string>>;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// Inline builder: extract routes directly from the function body
|
|
418
|
+
if (extraction.kind === "inline") {
|
|
419
|
+
result = buildCombinedRouteMapWithSearch(
|
|
420
|
+
routerFilePath,
|
|
421
|
+
undefined,
|
|
422
|
+
undefined,
|
|
423
|
+
undefined,
|
|
424
|
+
extraction.block,
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
// Variable reference: follow imports or same-file declaration
|
|
428
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
429
|
+
if (imported) {
|
|
430
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
431
|
+
if (!targetFile) {
|
|
432
|
+
return { routes: {}, searchSchemas: {} };
|
|
433
|
+
}
|
|
434
|
+
result = buildCombinedRouteMapWithSearch(
|
|
435
|
+
targetFile,
|
|
436
|
+
imported.exportedName,
|
|
437
|
+
);
|
|
438
|
+
} else {
|
|
439
|
+
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
261
440
|
}
|
|
262
|
-
return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
|
|
263
441
|
}
|
|
264
442
|
|
|
265
|
-
|
|
443
|
+
// Apply basename prefix to all extracted route patterns
|
|
444
|
+
if (basename) {
|
|
445
|
+
result = applyBasenameToRoutes(result, basename);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return result;
|
|
266
449
|
}
|
|
267
450
|
|
|
268
451
|
// ---------------------------------------------------------------------------
|
|
@@ -285,12 +468,26 @@ export function detectUnresolvableIncludes(
|
|
|
285
468
|
return [];
|
|
286
469
|
}
|
|
287
470
|
|
|
288
|
-
// Extract the urls
|
|
289
|
-
const
|
|
290
|
-
if (!
|
|
471
|
+
// Extract the urls source from the router file
|
|
472
|
+
const extraction = extractUrlsFromRouter(source);
|
|
473
|
+
if (!extraction) return [];
|
|
474
|
+
|
|
475
|
+
const diagnostics: UnresolvableInclude[] = [];
|
|
476
|
+
|
|
477
|
+
if (extraction.kind === "inline") {
|
|
478
|
+
// Inline builder: parse directly
|
|
479
|
+
buildCombinedRouteMapWithSearch(
|
|
480
|
+
realPath,
|
|
481
|
+
undefined,
|
|
482
|
+
new Set(),
|
|
483
|
+
diagnostics,
|
|
484
|
+
extraction.block,
|
|
485
|
+
);
|
|
486
|
+
return diagnostics;
|
|
487
|
+
}
|
|
291
488
|
|
|
292
|
-
//
|
|
293
|
-
const imported = resolveImportedVariable(source,
|
|
489
|
+
// Variable reference: resolve where it comes from
|
|
490
|
+
const imported = resolveImportedVariable(source, extraction.name);
|
|
294
491
|
let targetFile: string;
|
|
295
492
|
let exportedName: string | undefined;
|
|
296
493
|
|
|
@@ -312,10 +509,9 @@ export function detectUnresolvableIncludes(
|
|
|
312
509
|
} else {
|
|
313
510
|
// Same-file urls() definition
|
|
314
511
|
targetFile = realPath;
|
|
315
|
-
exportedName =
|
|
512
|
+
exportedName = extraction.name;
|
|
316
513
|
}
|
|
317
514
|
|
|
318
|
-
const diagnostics: UnresolvableInclude[] = [];
|
|
319
515
|
buildCombinedRouteMapWithSearch(
|
|
320
516
|
targetFile,
|
|
321
517
|
exportedName,
|
|
@@ -366,6 +562,73 @@ export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
|
|
|
366
562
|
return result;
|
|
367
563
|
}
|
|
368
564
|
|
|
565
|
+
const HOST_ROUTER_CALL_PATTERN = /\bcreateHostRouter\s*[<(]/;
|
|
566
|
+
const HOST_ROUTER_CALL_PATTERN_G = /\bcreateHostRouter\s*[<(]/g;
|
|
567
|
+
|
|
568
|
+
function findHostRouterFilesRecursive(
|
|
569
|
+
dir: string,
|
|
570
|
+
filter: ScanFilter | undefined,
|
|
571
|
+
results: string[],
|
|
572
|
+
): void {
|
|
573
|
+
let entries;
|
|
574
|
+
try {
|
|
575
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
576
|
+
} catch (err) {
|
|
577
|
+
console.warn(
|
|
578
|
+
`[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
|
|
579
|
+
);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
for (const entry of entries) {
|
|
584
|
+
const fullPath = join(dir, entry.name);
|
|
585
|
+
if (entry.isDirectory()) {
|
|
586
|
+
if (
|
|
587
|
+
entry.name === "node_modules" ||
|
|
588
|
+
entry.name === "dist" ||
|
|
589
|
+
entry.name === "coverage" ||
|
|
590
|
+
entry.name === "__tests__" ||
|
|
591
|
+
entry.name === "__mocks__" ||
|
|
592
|
+
entry.name.startsWith(".")
|
|
593
|
+
)
|
|
594
|
+
continue;
|
|
595
|
+
// Unlike createRouter scanning, do NOT stop at router roots: the host entry
|
|
596
|
+
// lives above the sub-app router directories, so descend the whole tree.
|
|
597
|
+
findHostRouterFilesRecursive(fullPath, filter, results);
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (!isRoutableSourceFile(entry.name)) continue;
|
|
602
|
+
if (filter && !filter(fullPath)) continue;
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
const source = readFileSync(fullPath, "utf-8");
|
|
606
|
+
if (
|
|
607
|
+
HOST_ROUTER_CALL_PATTERN.test(source) &&
|
|
608
|
+
firstCodeMatchIndex(source, HOST_ROUTER_CALL_PATTERN_G) >= 0
|
|
609
|
+
) {
|
|
610
|
+
results.push(fullPath);
|
|
611
|
+
}
|
|
612
|
+
} catch {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Scan for files containing createHostRouter() and return their paths. Unlike
|
|
620
|
+
* findRouterFiles, this does NOT stop at the first router-root directory -- a host
|
|
621
|
+
* entry typically sits above the sub-app router roots, so the whole tree is scanned.
|
|
622
|
+
*/
|
|
623
|
+
export function findHostRouterFiles(
|
|
624
|
+
root: string,
|
|
625
|
+
filter?: ScanFilter,
|
|
626
|
+
): string[] {
|
|
627
|
+
const result: string[] = [];
|
|
628
|
+
findHostRouterFilesRecursive(root, filter, result);
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
|
|
369
632
|
/**
|
|
370
633
|
* Write named-routes.gen.ts files from static source parsing.
|
|
371
634
|
* Dev-only: provides initial .gen.ts files for IDE types before runtime
|
|
@@ -375,7 +638,10 @@ export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
|
|
|
375
638
|
export function writeCombinedRouteTypes(
|
|
376
639
|
root: string,
|
|
377
640
|
knownRouterFiles?: string[],
|
|
378
|
-
opts?: {
|
|
641
|
+
opts?: {
|
|
642
|
+
preserveIfLarger?: boolean;
|
|
643
|
+
onWrite?: (outPath: string, content: string) => void;
|
|
644
|
+
},
|
|
379
645
|
): void {
|
|
380
646
|
// Delete old combined named-routes.gen.ts if it exists (stale from older versions)
|
|
381
647
|
try {
|
|
@@ -383,7 +649,7 @@ export function writeCombinedRouteTypes(
|
|
|
383
649
|
if (existsSync(oldCombinedPath)) {
|
|
384
650
|
unlinkSync(oldCombinedPath);
|
|
385
651
|
console.log(
|
|
386
|
-
`[
|
|
652
|
+
`[rango] Removed stale combined route types: ${oldCombinedPath}`,
|
|
387
653
|
);
|
|
388
654
|
}
|
|
389
655
|
} catch {}
|
|
@@ -397,54 +663,31 @@ export function writeCombinedRouteTypes(
|
|
|
397
663
|
}
|
|
398
664
|
|
|
399
665
|
for (const routerFilePath of routerFilePaths) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
const imported = resolveImportedVariable(routerSource, urlsVarName);
|
|
417
|
-
if (imported) {
|
|
418
|
-
// Variable is imported from another module
|
|
419
|
-
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
420
|
-
if (!targetFile) continue;
|
|
421
|
-
result = buildCombinedRouteMapWithSearch(
|
|
422
|
-
targetFile,
|
|
423
|
-
imported.exportedName,
|
|
424
|
-
);
|
|
425
|
-
} else {
|
|
426
|
-
// Variable is defined in the same file
|
|
427
|
-
result = buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
|
|
666
|
+
const result = buildCombinedRouteMapForRouterFile(routerFilePath);
|
|
667
|
+
if (
|
|
668
|
+
Object.keys(result.routes).length === 0 &&
|
|
669
|
+
Object.keys(result.searchSchemas).length === 0
|
|
670
|
+
) {
|
|
671
|
+
// Check if the file even has a createRouter call — if not, skip entirely.
|
|
672
|
+
// If it does, fall through to write an empty placeholder below.
|
|
673
|
+
let routerSource: string;
|
|
674
|
+
try {
|
|
675
|
+
routerSource = readFileSync(routerFilePath, "utf-8");
|
|
676
|
+
} catch {
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
428
680
|
}
|
|
429
681
|
|
|
430
|
-
const
|
|
431
|
-
/\.(tsx?|jsx?)$/,
|
|
432
|
-
"",
|
|
433
|
-
);
|
|
434
|
-
const outPath = join(
|
|
435
|
-
dirname(routerFilePath),
|
|
436
|
-
`${routerBasename}.named-routes.gen.ts`,
|
|
437
|
-
);
|
|
682
|
+
const outPath = genFileTsPath(routerFilePath);
|
|
438
683
|
const existing = existsSync(outPath)
|
|
439
684
|
? readFileSync(outPath, "utf-8")
|
|
440
685
|
: null;
|
|
441
686
|
|
|
442
|
-
// When the static parser can't extract routes (e.g. callback-style urls()),
|
|
443
|
-
// write an empty placeholder so the build-time transform's injected import
|
|
444
|
-
// resolves. Runtime discovery will overwrite this with the real routes.
|
|
445
687
|
if (Object.keys(result.routes).length === 0) {
|
|
446
688
|
if (!existing) {
|
|
447
689
|
const emptySource = generateRouteTypesSource({});
|
|
690
|
+
opts?.onWrite?.(outPath, emptySource);
|
|
448
691
|
writeFileSync(outPath, emptySource);
|
|
449
692
|
}
|
|
450
693
|
continue;
|
|
@@ -456,11 +699,6 @@ export function writeCombinedRouteTypes(
|
|
|
456
699
|
hasSearchSchemas ? result.searchSchemas : undefined,
|
|
457
700
|
);
|
|
458
701
|
if (existing !== source) {
|
|
459
|
-
// On initial dev startup, don't overwrite a file from runtime discovery
|
|
460
|
-
// (which has all dynamic routes) with a smaller set from the static
|
|
461
|
-
// parser. The static parser can't see routes generated by Array.from()
|
|
462
|
-
// or other dynamic code. During HMR (file watcher), always write so
|
|
463
|
-
// newly added routes appear immediately.
|
|
464
702
|
if (opts?.preserveIfLarger && existing) {
|
|
465
703
|
const existingCount = countPublicRouteEntries(existing);
|
|
466
704
|
const newCount = Object.keys(result.routes).filter(
|
|
@@ -470,9 +708,10 @@ export function writeCombinedRouteTypes(
|
|
|
470
708
|
continue;
|
|
471
709
|
}
|
|
472
710
|
}
|
|
711
|
+
opts?.onWrite?.(outPath, source);
|
|
473
712
|
writeFileSync(outPath, source);
|
|
474
713
|
console.log(
|
|
475
|
-
`[
|
|
714
|
+
`[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`,
|
|
476
715
|
);
|
|
477
716
|
}
|
|
478
717
|
}
|
|
@@ -54,14 +54,21 @@ 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
|
}
|
|
61
61
|
for (const entry of entries) {
|
|
62
62
|
const fullPath = join(dir, entry.name);
|
|
63
63
|
if (entry.isDirectory()) {
|
|
64
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
entry.name === "node_modules" ||
|
|
66
|
+
entry.name.startsWith(".") ||
|
|
67
|
+
entry.name === "dist" ||
|
|
68
|
+
entry.name === "build" ||
|
|
69
|
+
entry.name === "coverage"
|
|
70
|
+
)
|
|
71
|
+
continue;
|
|
65
72
|
results.push(...findTsFiles(fullPath, filter));
|
|
66
73
|
} else if (
|
|
67
74
|
(entry.name.endsWith(".ts") ||
|