@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847
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 +9 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1531 -212
- package/dist/vite/index.js +3995 -2489
- package/package.json +57 -52
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +85 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/hooks/SKILL.md +328 -70
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +62 -15
- package/skills/loader/SKILL.md +368 -42
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +14 -10
- package/skills/parallel/SKILL.md +137 -1
- package/skills/prerender/SKILL.md +366 -28
- package/skills/rango/SKILL.md +85 -21
- package/skills/response-routes/SKILL.md +136 -83
- package/skills/route/SKILL.md +195 -21
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +240 -102
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.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 +266 -558
- package/src/browser/navigation-client.ts +132 -75
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +303 -309
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +144 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +190 -70
- package/src/browser/react/NavigationProvider.tsx +78 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- 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 +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +29 -70
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +188 -57
- package/src/browser/scroll-restoration.ts +117 -44
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +488 -606
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +116 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +63 -21
- package/src/build/generate-route-types.ts +36 -1038
- package/src/build/index.ts +2 -5
- package/src/build/route-trie.ts +38 -12
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +479 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +122 -303
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +84 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +12 -7
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +104 -40
- package/src/index.ts +122 -67
- package/src/internal-debug.ts +9 -3
- package/src/loader.rsc.ts +18 -93
- package/src/loader.ts +26 -9
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +121 -17
- package/src/prerender.ts +325 -20
- package/src/reverse.ts +144 -124
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +959 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1450
- package/src/route-map-builder.ts +87 -133
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +41 -6
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +324 -116
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +179 -133
- package/src/router/logging.ts +112 -6
- package/src/router/manifest.ts +58 -19
- package/src/router/match-api.ts +89 -88
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +86 -89
- package/src/router/match-middleware/cache-lookup.ts +295 -49
- package/src/router/match-middleware/cache-store.ts +56 -13
- package/src/router/match-middleware/intercept-resolution.ts +45 -22
- package/src/router/match-middleware/segment-resolution.ts +20 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +44 -21
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +327 -369
- package/src/router/pattern-matching.ts +169 -31
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +105 -14
- package/src/router/router-context.ts +40 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +677 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1296 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1354
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +96 -29
- package/src/router/types.ts +15 -9
- package/src/router.ts +642 -2366
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +639 -1027
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +38 -11
- package/src/search-params.ts +66 -54
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +237 -54
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +438 -71
- package/src/server.ts +26 -164
- package/src/ssr/index.tsx +101 -31
- package/src/static-handler.ts +22 -4
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +773 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +150 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1323
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -2259
- package/src/vite/plugin-types.ts +48 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -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/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- 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/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/build/index.ts
CHANGED
|
@@ -17,15 +17,12 @@
|
|
|
17
17
|
|
|
18
18
|
export {
|
|
19
19
|
generateManifest,
|
|
20
|
+
generateManifestFull,
|
|
20
21
|
generateManifestCode,
|
|
21
22
|
type GeneratedManifest,
|
|
22
23
|
} from "./generate-manifest.js";
|
|
23
24
|
|
|
24
|
-
export {
|
|
25
|
-
buildRouteTrie,
|
|
26
|
-
type TrieNode,
|
|
27
|
-
type TrieLeaf,
|
|
28
|
-
} from "./route-trie.js";
|
|
25
|
+
export { buildRouteTrie, type TrieNode, type TrieLeaf } from "./route-trie.js";
|
|
29
26
|
|
|
30
27
|
export {
|
|
31
28
|
writePerModuleRouteTypes,
|
package/src/build/route-trie.ts
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* shortCodes for layout pruning.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
parsePattern,
|
|
11
|
+
type ParsedSegment,
|
|
12
|
+
} from "../router/pattern-matching.js";
|
|
10
13
|
|
|
11
14
|
// -- Trie data structures (compact keys for JSON serialization) --
|
|
12
15
|
|
|
@@ -21,6 +24,8 @@ export interface TrieLeaf {
|
|
|
21
24
|
op?: string[];
|
|
22
25
|
/** Constraint validation: paramName -> allowed values */
|
|
23
26
|
cv?: Record<string, string[]>;
|
|
27
|
+
/** Ordered param names for this route (positional) */
|
|
28
|
+
pa?: string[];
|
|
24
29
|
/** Trailing slash mode */
|
|
25
30
|
ts?: string;
|
|
26
31
|
/** Route has pre-rendered data available */
|
|
@@ -42,6 +47,8 @@ export interface TrieNode {
|
|
|
42
47
|
s?: Record<string, TrieNode>;
|
|
43
48
|
/** Param child: { n: paramName, c: child node } */
|
|
44
49
|
p?: { n: string; c: TrieNode };
|
|
50
|
+
/** Suffix-param children keyed by suffix (e.g., ".html" → { n: "productId", c: ... }) */
|
|
51
|
+
xp?: Record<string, { n: string; c: TrieNode }>;
|
|
45
52
|
/** Wildcard terminal: leaf + paramName */
|
|
46
53
|
w?: TrieLeaf & { pn: string };
|
|
47
54
|
}
|
|
@@ -98,23 +105,28 @@ function insertRoute(
|
|
|
98
105
|
node: TrieNode,
|
|
99
106
|
segments: ParsedSegment[],
|
|
100
107
|
index: number,
|
|
101
|
-
leaf: Omit<TrieLeaf, "op" | "cv">,
|
|
108
|
+
leaf: Omit<TrieLeaf, "op" | "cv" | "pa">,
|
|
102
109
|
): void {
|
|
103
|
-
// Collect optional param names and constraints across all segments
|
|
110
|
+
// Collect param names, optional param names, and constraints across all segments
|
|
111
|
+
const paramNames: string[] = [];
|
|
104
112
|
const optionalParams: string[] = [];
|
|
105
113
|
const constraints: Record<string, string[]> = {};
|
|
106
114
|
|
|
107
115
|
for (const seg of segments) {
|
|
108
|
-
if (seg.type === "param"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
if (seg.type === "param") {
|
|
117
|
+
paramNames.push(seg.value);
|
|
118
|
+
if (seg.optional) {
|
|
119
|
+
optionalParams.push(seg.value);
|
|
120
|
+
}
|
|
121
|
+
if (seg.constraint) {
|
|
122
|
+
constraints[seg.value] = seg.constraint;
|
|
123
|
+
}
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
|
|
116
127
|
const fullLeaf: TrieLeaf = {
|
|
117
128
|
...leaf,
|
|
129
|
+
...(paramNames.length > 0 ? { pa: paramNames } : {}),
|
|
118
130
|
...(optionalParams.length > 0 ? { op: optionalParams } : {}),
|
|
119
131
|
...(Object.keys(constraints).length > 0 ? { cv: constraints } : {}),
|
|
120
132
|
};
|
|
@@ -148,6 +160,11 @@ export function extractAncestryFromTrie(
|
|
|
148
160
|
visit(child);
|
|
149
161
|
}
|
|
150
162
|
}
|
|
163
|
+
if (node.xp) {
|
|
164
|
+
for (const child of Object.values(node.xp)) {
|
|
165
|
+
visit(child.c);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
151
168
|
if (node.p) {
|
|
152
169
|
visit(node.p.c);
|
|
153
170
|
}
|
|
@@ -225,14 +242,23 @@ function insertSegments(
|
|
|
225
242
|
mergeLeaf(node, leaf);
|
|
226
243
|
// AND continue with param child (param present)
|
|
227
244
|
}
|
|
228
|
-
if (
|
|
229
|
-
|
|
245
|
+
if (segment.suffix) {
|
|
246
|
+
// Suffix param: keyed by suffix string (e.g., ".html")
|
|
247
|
+
if (!node.xp) node.xp = {};
|
|
248
|
+
if (!node.xp[segment.suffix]) {
|
|
249
|
+
node.xp[segment.suffix] = { n: segment.value, c: {} };
|
|
250
|
+
}
|
|
251
|
+
insertSegments(node.xp[segment.suffix].c, segments, index + 1, leaf);
|
|
252
|
+
} else {
|
|
253
|
+
if (!node.p) {
|
|
254
|
+
node.p = { n: segment.value, c: {} };
|
|
255
|
+
}
|
|
256
|
+
insertSegments(node.p.c, segments, index + 1, leaf);
|
|
230
257
|
}
|
|
231
|
-
insertSegments(node.p.c, segments, index + 1, leaf);
|
|
232
258
|
} else if (segment.type === "wildcard") {
|
|
233
259
|
// Wildcard consumes all remaining segments
|
|
234
260
|
const wildLeaf = { ...leaf, pn: "*" };
|
|
235
|
-
const existing = node.w ? { ...node.w } as TrieLeaf : undefined;
|
|
261
|
+
const existing = node.w ? ({ ...node.w } as TrieLeaf) : undefined;
|
|
236
262
|
const merged = mergeLeaves(existing, wildLeaf);
|
|
237
263
|
node.w = merged as TrieLeaf & { pn: string };
|
|
238
264
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
export function getStringValue(node: ts.Node): string | null {
|
|
4
|
+
if (ts.isStringLiteral(node)) return node.text;
|
|
5
|
+
if (ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function extractObjectStringProperties(
|
|
10
|
+
node: ts.ObjectLiteralExpression,
|
|
11
|
+
): Record<string, string> {
|
|
12
|
+
const result: Record<string, string> = {};
|
|
13
|
+
for (const prop of node.properties) {
|
|
14
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
15
|
+
const key = ts.isIdentifier(prop.name)
|
|
16
|
+
? prop.name.text
|
|
17
|
+
: ts.isStringLiteral(prop.name)
|
|
18
|
+
? prop.name.text
|
|
19
|
+
: null;
|
|
20
|
+
if (!key) continue;
|
|
21
|
+
const val = getStringValue(prop.initializer);
|
|
22
|
+
if (val !== null) result[key] = val;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import {
|
|
3
|
+
getStringValue,
|
|
4
|
+
extractObjectStringProperties,
|
|
5
|
+
} from "./ast-helpers.js";
|
|
6
|
+
import { extractParamsFromPattern } from "./param-extraction.js";
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// AST-based route extraction
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract route definitions from source code by walking the TypeScript AST.
|
|
14
|
+
* Finds path() and path.json(), path.md(), etc. call expressions and extracts
|
|
15
|
+
* the pattern, name, params, and optional search schema from each.
|
|
16
|
+
* Skips unnamed paths (no { name: "..." }).
|
|
17
|
+
*/
|
|
18
|
+
export function extractRoutesFromSource(code: string): Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
pattern: string;
|
|
21
|
+
params?: Record<string, string>;
|
|
22
|
+
search?: Record<string, string>;
|
|
23
|
+
}> {
|
|
24
|
+
const sourceFile = ts.createSourceFile(
|
|
25
|
+
"input.tsx",
|
|
26
|
+
code,
|
|
27
|
+
ts.ScriptTarget.Latest,
|
|
28
|
+
true,
|
|
29
|
+
ts.ScriptKind.TSX,
|
|
30
|
+
);
|
|
31
|
+
const routes: Array<{
|
|
32
|
+
name: string;
|
|
33
|
+
pattern: string;
|
|
34
|
+
params?: Record<string, string>;
|
|
35
|
+
search?: Record<string, string>;
|
|
36
|
+
}> = [];
|
|
37
|
+
|
|
38
|
+
function visit(node: ts.Node) {
|
|
39
|
+
if (ts.isCallExpression(node)) {
|
|
40
|
+
const callee = node.expression;
|
|
41
|
+
const isPath =
|
|
42
|
+
(ts.isIdentifier(callee) && callee.text === "path") ||
|
|
43
|
+
(ts.isPropertyAccessExpression(callee) &&
|
|
44
|
+
ts.isIdentifier(callee.expression) &&
|
|
45
|
+
callee.expression.text === "path");
|
|
46
|
+
|
|
47
|
+
if (isPath && node.arguments.length >= 1) {
|
|
48
|
+
const route = extractRouteFromCallExpression(node);
|
|
49
|
+
if (route) routes.push(route);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
ts.forEachChild(node, visit);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
visit(sourceFile);
|
|
56
|
+
return routes;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function extractRouteFromCallExpression(node: ts.CallExpression): {
|
|
60
|
+
name: string;
|
|
61
|
+
pattern: string;
|
|
62
|
+
params?: Record<string, string>;
|
|
63
|
+
search?: Record<string, string>;
|
|
64
|
+
} | null {
|
|
65
|
+
const patternNode = node.arguments[0];
|
|
66
|
+
const pattern = getStringValue(patternNode);
|
|
67
|
+
if (pattern === null) return null;
|
|
68
|
+
|
|
69
|
+
let name: string | null = null;
|
|
70
|
+
let search: Record<string, string> | undefined;
|
|
71
|
+
|
|
72
|
+
for (let i = 1; i < node.arguments.length; i++) {
|
|
73
|
+
const arg = node.arguments[i];
|
|
74
|
+
if (ts.isObjectLiteralExpression(arg)) {
|
|
75
|
+
for (const prop of arg.properties) {
|
|
76
|
+
if (!ts.isPropertyAssignment(prop)) continue;
|
|
77
|
+
const propName = ts.isIdentifier(prop.name) ? prop.name.text : null;
|
|
78
|
+
if (propName === "name") {
|
|
79
|
+
name = getStringValue(prop.initializer);
|
|
80
|
+
} else if (
|
|
81
|
+
propName === "search" &&
|
|
82
|
+
ts.isObjectLiteralExpression(prop.initializer)
|
|
83
|
+
) {
|
|
84
|
+
search = extractObjectStringProperties(prop.initializer);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!name) return null;
|
|
91
|
+
const params = extractParamsFromPattern(pattern);
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
pattern,
|
|
95
|
+
...(params ? { params } : {}),
|
|
96
|
+
...(search && Object.keys(search).length > 0 ? { search } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractParamsFromPattern,
|
|
3
|
+
formatRouteEntry,
|
|
4
|
+
} from "./param-extraction.js";
|
|
5
|
+
import { isAutoGeneratedRouteName } from "../../route-name.js";
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Code generation
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate a per-module types file from extracted routes.
|
|
13
|
+
* Output has zero imports, preventing circular references.
|
|
14
|
+
*/
|
|
15
|
+
export function generatePerModuleTypesSource(
|
|
16
|
+
routes: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
pattern: string;
|
|
19
|
+
params?: Record<string, string>;
|
|
20
|
+
search?: Record<string, string>;
|
|
21
|
+
}>,
|
|
22
|
+
): string {
|
|
23
|
+
const valid = routes.filter(({ name }) => {
|
|
24
|
+
if (!name || /["'\\`\n\r]/.test(name)) {
|
|
25
|
+
console.warn(
|
|
26
|
+
`[rsc-router] Skipping route with invalid name: ${JSON.stringify(name)}`,
|
|
27
|
+
);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Deduplicate by name (first definition wins -- primary route before variants)
|
|
34
|
+
const deduped = new Map<
|
|
35
|
+
string,
|
|
36
|
+
{
|
|
37
|
+
pattern: string;
|
|
38
|
+
params?: Record<string, string>;
|
|
39
|
+
search?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
>();
|
|
42
|
+
for (const { name, pattern, params, search } of valid) {
|
|
43
|
+
if (deduped.has(name)) {
|
|
44
|
+
console.warn(
|
|
45
|
+
`[rsc-router] Duplicate route name "${name}" — keeping first definition`,
|
|
46
|
+
);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
deduped.set(name, { pattern, params, search });
|
|
50
|
+
}
|
|
51
|
+
const sorted = [...deduped.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
52
|
+
const body = sorted
|
|
53
|
+
.map(([name, { pattern, params, search }]) => {
|
|
54
|
+
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
55
|
+
return formatRouteEntry(key, pattern, params, search);
|
|
56
|
+
})
|
|
57
|
+
.join("\n");
|
|
58
|
+
return `// Auto-generated by @rangojs/router - do not edit\nexport const routes = {\n${body}\n} as const;\nexport type routes = typeof routes;\n`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Generates a .ts file that augments RSCRouter.GeneratedRouteMap
|
|
63
|
+
* with route name -> pattern mappings. This enables Handler<"routeName">
|
|
64
|
+
* without circular references since the file has no imports from the app.
|
|
65
|
+
*/
|
|
66
|
+
export function generateRouteTypesSource(
|
|
67
|
+
routeManifest: Record<string, string>,
|
|
68
|
+
searchSchemas?: Record<string, Record<string, string>>,
|
|
69
|
+
): string {
|
|
70
|
+
const entries = Object.entries(routeManifest)
|
|
71
|
+
.filter(([name]) => !isAutoGeneratedRouteName(name))
|
|
72
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
73
|
+
|
|
74
|
+
const filteredSearchSchemas = searchSchemas
|
|
75
|
+
? Object.fromEntries(
|
|
76
|
+
Object.entries(searchSchemas).filter(
|
|
77
|
+
([name]) => !isAutoGeneratedRouteName(name),
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
: undefined;
|
|
81
|
+
|
|
82
|
+
const objectBody = entries
|
|
83
|
+
.map(([name, pattern]) => {
|
|
84
|
+
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
85
|
+
const params = extractParamsFromPattern(pattern);
|
|
86
|
+
const search = filteredSearchSchemas?.[name];
|
|
87
|
+
return formatRouteEntry(key, pattern, params, search);
|
|
88
|
+
})
|
|
89
|
+
.join("\n");
|
|
90
|
+
|
|
91
|
+
return `// Auto-generated by @rangojs/router - do not edit
|
|
92
|
+
export const NamedRoutes = {
|
|
93
|
+
${objectBody}
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
declare global {
|
|
97
|
+
namespace RSCRouter {
|
|
98
|
+
interface GeneratedRouteMap extends Readonly<typeof NamedRoutes> {}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
}
|