@rangojs/router 0.0.0-experimental.0f44aca1

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.
Files changed (305) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +5214 -0
  5. package/package.json +176 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +220 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +645 -0
  43. package/src/browser/navigation-client.ts +215 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +295 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +550 -0
  48. package/src/browser/prefetch/cache.ts +146 -0
  49. package/src/browser/prefetch/fetch.ts +135 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +42 -0
  52. package/src/browser/prefetch/queue.ts +88 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +360 -0
  55. package/src/browser/react/NavigationProvider.tsx +386 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +431 -0
  79. package/src/browser/scroll-restoration.ts +400 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +538 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +469 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +540 -0
  105. package/src/cache/cf/index.ts +25 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +43 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +275 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +158 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +395 -0
  172. package/src/router/lazy-includes.ts +234 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +248 -0
  175. package/src/router/manifest.ts +267 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +192 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +748 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +316 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1239 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +289 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1002 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +235 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +914 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +102 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +110 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +131 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +365 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +254 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +510 -0
  298. package/src/vite/router-discovery.ts +785 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Build-time manifest generation for @rangojs/router
3
+ *
4
+ * Extracts the prefix tree and route manifest from UrlPatterns at build time.
5
+ * This enables:
6
+ * - Pre-computed prefix tree for fast short-circuit checks
7
+ * - Complete route manifest for href() without runtime evaluation
8
+ * - Support for nested includes
9
+ */
10
+
11
+ import type { UrlPatterns } from "../urls.js";
12
+ import type { AllUseItems } from "../route-types.js";
13
+ import { extractStaticPrefix } from "../router/pattern-matching.js";
14
+ import { RSCRouterContext, runWithPrefixes } from "../server/context.js";
15
+ import type { EntryData, TrackedInclude } from "../server/context.js";
16
+ import type { TrailingSlashMode } from "../types.js";
17
+ import { createRouteHelpers } from "../route-definition.js";
18
+ import MapRootLayout from "../server/root-layout.js";
19
+
20
+ /**
21
+ * Node in the prefix tree
22
+ */
23
+ export interface PrefixTreeNode {
24
+ /** The static prefix for this node */
25
+ staticPrefix: string;
26
+ /** The full URL prefix (including parent prefixes) */
27
+ fullPrefix: string;
28
+ /** Name prefix for routes in this include */
29
+ namePrefix?: string;
30
+ /** Child nodes (nested includes) */
31
+ children: Record<string, PrefixTreeNode>;
32
+ /** Route names defined directly in this include (not in children) */
33
+ routes: string[];
34
+ }
35
+
36
+ /**
37
+ * Generated manifest containing prefix tree and route mappings
38
+ */
39
+ export interface GeneratedManifest {
40
+ /** Nested prefix tree for short-circuit optimization */
41
+ prefixTree: Record<string, PrefixTreeNode>;
42
+ /** Complete route name → pattern mapping for href() */
43
+ routeManifest: Record<string, string>;
44
+ /** Route name → trailing slash mode for trie redirect handling */
45
+ routeTrailingSlash?: Record<string, string>;
46
+ /** Route names using Prerender (for dev-mode Node.js delegation) */
47
+ prerenderRoutes?: string[];
48
+ /** Route names with passthrough: true (handler kept in bundle for live fallback) */
49
+ passthroughRoutes?: string[];
50
+ /** Route name → response type for non-RSC routes */
51
+ responseTypeRoutes?: Record<string, string>;
52
+ /** Route name -> search schema descriptor for typed URL helpers */
53
+ routeSearchSchemas?: Record<string, Record<string, string>>;
54
+ }
55
+
56
+ /**
57
+ * Build prefix tree node by running the patterns with proper context.
58
+ * Uses a visited set to detect circular includes and prevent infinite recursion.
59
+ */
60
+ function buildPrefixTreeNode(
61
+ urlPrefix: string,
62
+ namePrefix: string | undefined,
63
+ patterns: UrlPatterns<any>,
64
+ routeManifest: Record<string, string>,
65
+ routeAncestry: Record<string, string[]>, // internal: feeds trie building, not exported
66
+ mountIndex: number,
67
+ visited: Set<unknown> = new Set(),
68
+ routeTrailingSlash?: Record<string, string>,
69
+ prerenderRoutes?: string[],
70
+ prerenderDefs?: Record<string, any>,
71
+ passthroughRoutes?: string[],
72
+ responseTypeRoutes?: Record<string, string>,
73
+ routeSearchSchemas?: Record<string, Record<string, string>>,
74
+ ): PrefixTreeNode {
75
+ if (visited.has(patterns)) {
76
+ console.warn(
77
+ `[@rangojs/router] Circular include detected at prefix "${urlPrefix}". Skipping.`,
78
+ );
79
+ return {
80
+ staticPrefix: extractStaticPrefix(urlPrefix),
81
+ fullPrefix: urlPrefix,
82
+ namePrefix: namePrefix || undefined,
83
+ children: {},
84
+ routes: [],
85
+ };
86
+ }
87
+ visited.add(patterns);
88
+ // Create context for running patterns with include tracking
89
+ const manifest = new Map<string, EntryData>();
90
+ const patternsMap = new Map<string, string>();
91
+ const patternsByPrefix = new Map<string, Map<string, string>>();
92
+ const trailingSlashMap = new Map<string, TrailingSlashMode>();
93
+ const searchSchemasMap = new Map<string, Record<string, string>>();
94
+ const trackedIncludes: TrackedInclude[] = [];
95
+
96
+ RSCRouterContext.run(
97
+ {
98
+ manifest,
99
+ patterns: patternsMap,
100
+ patternsByPrefix,
101
+ trailingSlash: trailingSlashMap,
102
+ searchSchemas: searchSchemasMap,
103
+ namespace: "build",
104
+ parent: null,
105
+ counters: {},
106
+ mountIndex,
107
+ trackedIncludes, // Enable nested include tracking
108
+ },
109
+ () => {
110
+ const helpers = createRouteHelpers();
111
+ // Wrap in root layout for correct parent hierarchy (matches runtime)
112
+ helpers.layout(MapRootLayout, () => {
113
+ if (urlPrefix || namePrefix) {
114
+ return runWithPrefixes(urlPrefix, namePrefix, () => {
115
+ return patterns.handler() as AllUseItems[];
116
+ });
117
+ }
118
+ return patterns.handler() as AllUseItems[];
119
+ });
120
+ },
121
+ );
122
+
123
+ // Collect route names defined in this include (routes have prefixes applied)
124
+ const routes: string[] = [];
125
+ for (const [name, pattern] of patternsMap.entries()) {
126
+ routes.push(name);
127
+ routeManifest[name] = pattern;
128
+ }
129
+
130
+ // Collect trailing slash config
131
+ if (routeTrailingSlash) {
132
+ for (const [name, mode] of trailingSlashMap.entries()) {
133
+ routeTrailingSlash[name] = mode;
134
+ }
135
+ }
136
+ if (routeSearchSchemas) {
137
+ for (const [name, schema] of searchSchemasMap.entries()) {
138
+ routeSearchSchemas[name] = schema;
139
+ }
140
+ }
141
+
142
+ // Capture ancestry from manifest entries' parent chains
143
+ captureAncestry(manifest, routeAncestry);
144
+
145
+ // Collect prerender route names and handler definitions from manifest entries
146
+ if (prerenderRoutes) {
147
+ for (const [name, entry] of manifest) {
148
+ if (entry.type === "route" && entry.isPrerender) {
149
+ prerenderRoutes.push(name);
150
+ if (prerenderDefs && entry.prerenderDef) {
151
+ prerenderDefs[name] = entry.prerenderDef;
152
+ }
153
+ if (
154
+ passthroughRoutes &&
155
+ entry.prerenderDef?.options?.passthrough === true
156
+ ) {
157
+ passthroughRoutes.push(name);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ // Collect response type routes from manifest entries
164
+ if (responseTypeRoutes) {
165
+ for (const [name, entry] of manifest) {
166
+ if (entry.type === "route" && entry.responseType) {
167
+ responseTypeRoutes[name] = entry.responseType;
168
+ }
169
+ }
170
+ }
171
+
172
+ // Build children from tracked nested includes.
173
+ // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
174
+ // include("/", patternsB)). Merge their routes instead of overwriting.
175
+ const children: Record<string, PrefixTreeNode> = {};
176
+
177
+ for (const include of trackedIncludes) {
178
+ const childNode = buildPrefixTreeNode(
179
+ include.fullPrefix,
180
+ include.namePrefix,
181
+ include.patterns as UrlPatterns<any>,
182
+ routeManifest,
183
+ routeAncestry,
184
+ mountIndex,
185
+ visited,
186
+ routeTrailingSlash,
187
+ prerenderRoutes,
188
+ prerenderDefs,
189
+ passthroughRoutes,
190
+ responseTypeRoutes,
191
+ routeSearchSchemas,
192
+ );
193
+
194
+ const existing = children[include.fullPrefix];
195
+ if (existing) {
196
+ existing.routes.push(...childNode.routes);
197
+ Object.assign(existing.children, childNode.children);
198
+ } else {
199
+ children[include.fullPrefix] = childNode;
200
+ }
201
+ }
202
+
203
+ // Remove from visited so sibling branches can reuse the same patterns
204
+ // without false circular-include detection. Only ancestors in the current
205
+ // recursion path should trigger the cycle guard.
206
+ visited.delete(patterns);
207
+
208
+ return {
209
+ staticPrefix: extractStaticPrefix(urlPrefix),
210
+ fullPrefix: urlPrefix,
211
+ namePrefix: namePrefix || undefined,
212
+ children,
213
+ routes,
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Walk parent chains of route entries to extract ancestry shortCodes.
219
+ */
220
+ function captureAncestry(
221
+ manifest: Map<string, EntryData>,
222
+ routeAncestry: Record<string, string[]>,
223
+ ): void {
224
+ for (const [routeName, entry] of manifest) {
225
+ if (entry.type === "route") {
226
+ const ancestry: string[] = [];
227
+ let current: EntryData | null = entry;
228
+ while (current) {
229
+ ancestry.unshift(current.shortCode);
230
+ current = current.parent;
231
+ }
232
+ routeAncestry[routeName] = ancestry;
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Internal manifest result including build-pipeline-only fields.
239
+ * Not part of the public API — use generateManifest() for the public surface.
240
+ */
241
+ export interface FullManifest extends GeneratedManifest {
242
+ _routeAncestry: Record<string, string[]>;
243
+ _prerenderDefs?: Record<string, any>;
244
+ }
245
+
246
+ /**
247
+ * Generate manifest from UrlPatterns (public API).
248
+ *
249
+ * Returns only the public GeneratedManifest fields. Internal build pipeline
250
+ * consumers that need _routeAncestry or _prerenderDefs should use
251
+ * generateManifestFull() instead.
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * import { generateManifest } from "@rangojs/router/build";
256
+ * import { urlpatterns } from "./urls";
257
+ *
258
+ * const manifest = generateManifest(urlpatterns);
259
+ * // Write to file for runtime use
260
+ * fs.writeFileSync(
261
+ * "src/generated/route-manifest.json",
262
+ * JSON.stringify(manifest, null, 2)
263
+ * );
264
+ * ```
265
+ */
266
+ export function generateManifest<TEnv>(
267
+ urlpatterns: UrlPatterns<TEnv, any>,
268
+ mountIndex: number = 0,
269
+ ): GeneratedManifest {
270
+ const {
271
+ _routeAncestry: _,
272
+ _prerenderDefs: __,
273
+ ...publicManifest
274
+ } = generateManifestFull(urlpatterns, mountIndex);
275
+ return publicManifest;
276
+ }
277
+
278
+ /**
279
+ * Generate manifest with internal build-pipeline fields.
280
+ *
281
+ * Used by the Vite plugin (discover-routers via dynamic import through
282
+ * @rangojs/router/build), manifest-init (direct import), and trie
283
+ * building. Not intended for external use.
284
+ */
285
+ export function generateManifestFull<TEnv>(
286
+ urlpatterns: UrlPatterns<TEnv, any>,
287
+ mountIndex: number = 0,
288
+ ): FullManifest {
289
+ const routeManifest: Record<string, string> = {};
290
+ const routeAncestry: Record<string, string[]> = {};
291
+ const prefixTree: Record<string, PrefixTreeNode> = {};
292
+
293
+ // Run the root patterns handler with tracking enabled
294
+ const manifest = new Map<string, EntryData>();
295
+ const patternsMap = new Map<string, string>();
296
+ const patternsByPrefix = new Map<string, Map<string, string>>();
297
+ const trailingSlashMap = new Map<string, TrailingSlashMode>();
298
+ const searchSchemasMap = new Map<string, Record<string, string>>();
299
+ const trackedIncludes: TrackedInclude[] = [];
300
+
301
+ RSCRouterContext.run(
302
+ {
303
+ manifest,
304
+ patterns: patternsMap,
305
+ patternsByPrefix,
306
+ trailingSlash: trailingSlashMap,
307
+ searchSchemas: searchSchemasMap,
308
+ namespace: "build",
309
+ parent: null,
310
+ counters: {},
311
+ mountIndex,
312
+ trackedIncludes, // Enable include tracking
313
+ },
314
+ () => {
315
+ const helpers = createRouteHelpers();
316
+ // Wrap in root layout for correct parent hierarchy (matches runtime)
317
+ helpers.layout(MapRootLayout, () => {
318
+ return urlpatterns.handler() as AllUseItems[];
319
+ });
320
+ },
321
+ );
322
+
323
+ // Collect root-level routes and trailing slash config
324
+ const routeTrailingSlash: Record<string, string> = {};
325
+ for (const [name, pattern] of patternsMap.entries()) {
326
+ routeManifest[name] = pattern;
327
+ }
328
+ for (const [name, mode] of trailingSlashMap.entries()) {
329
+ routeTrailingSlash[name] = mode;
330
+ }
331
+ const routeSearchSchemas: Record<string, Record<string, string>> = {};
332
+ for (const [name, schema] of searchSchemasMap.entries()) {
333
+ routeSearchSchemas[name] = schema;
334
+ }
335
+
336
+ // Capture ancestry from manifest entries' parent chains
337
+ captureAncestry(manifest, routeAncestry);
338
+
339
+ // Collect prerender route names and handler definitions across all levels
340
+ const prerenderRoutes: string[] = [];
341
+ const prerenderDefs: Record<string, any> = {};
342
+ const passthroughRoutes: string[] = [];
343
+ const responseTypeRoutes: Record<string, string> = {};
344
+ for (const [name, entry] of manifest) {
345
+ if (entry.type === "route" && entry.isPrerender) {
346
+ prerenderRoutes.push(name);
347
+ if (entry.prerenderDef) {
348
+ prerenderDefs[name] = entry.prerenderDef;
349
+ }
350
+ if (entry.prerenderDef?.options?.passthrough === true) {
351
+ passthroughRoutes.push(name);
352
+ }
353
+ }
354
+ if (entry.type === "route" && entry.responseType) {
355
+ responseTypeRoutes[name] = entry.responseType;
356
+ }
357
+ }
358
+
359
+ // Build prefix tree from tracked includes (shared visited set for cycle detection).
360
+ // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
361
+ // include("/", patternsB)). Merge their routes instead of overwriting.
362
+ const visited = new Set<unknown>();
363
+ for (const include of trackedIncludes) {
364
+ const node = buildPrefixTreeNode(
365
+ include.fullPrefix,
366
+ include.namePrefix,
367
+ include.patterns as UrlPatterns<any>,
368
+ routeManifest,
369
+ routeAncestry,
370
+ mountIndex,
371
+ visited,
372
+ routeTrailingSlash,
373
+ prerenderRoutes,
374
+ prerenderDefs,
375
+ passthroughRoutes,
376
+ responseTypeRoutes,
377
+ routeSearchSchemas,
378
+ );
379
+
380
+ const existing = prefixTree[include.fullPrefix];
381
+ if (existing) {
382
+ existing.routes.push(...node.routes);
383
+ Object.assign(existing.children, node.children);
384
+ } else {
385
+ prefixTree[include.fullPrefix] = node;
386
+ }
387
+ }
388
+
389
+ return {
390
+ prefixTree,
391
+ routeManifest,
392
+ routeTrailingSlash:
393
+ Object.keys(routeTrailingSlash).length > 0
394
+ ? routeTrailingSlash
395
+ : undefined,
396
+ prerenderRoutes: prerenderRoutes.length > 0 ? prerenderRoutes : undefined,
397
+ passthroughRoutes:
398
+ passthroughRoutes.length > 0 ? passthroughRoutes : undefined,
399
+ responseTypeRoutes:
400
+ Object.keys(responseTypeRoutes).length > 0
401
+ ? responseTypeRoutes
402
+ : undefined,
403
+ routeSearchSchemas:
404
+ Object.keys(routeSearchSchemas).length > 0
405
+ ? routeSearchSchemas
406
+ : undefined,
407
+ _routeAncestry: routeAncestry,
408
+ // Internal: prerender handler definitions for build-time getParams() access
409
+ _prerenderDefs:
410
+ Object.keys(prerenderDefs).length > 0 ? prerenderDefs : undefined,
411
+ };
412
+ }
413
+
414
+ /**
415
+ * Generate TypeScript code for the manifest
416
+ *
417
+ * @example
418
+ * ```typescript
419
+ * const code = generateManifestCode(urlpatterns);
420
+ * fs.writeFileSync("src/generated/route-manifest.ts", code);
421
+ * ```
422
+ */
423
+ export function generateManifestCode<TEnv>(
424
+ urlpatterns: UrlPatterns<TEnv, any>,
425
+ ): string {
426
+ const manifest = generateManifest(urlpatterns);
427
+
428
+ return `/**
429
+ * Auto-generated route manifest
430
+ *
431
+ * DO NOT EDIT - This file is generated by @rangojs/router
432
+ */
433
+
434
+ export const routeManifest = ${JSON.stringify(manifest.routeManifest, null, 2)} as const;
435
+
436
+ export type RouteNames = keyof typeof routeManifest;
437
+ `;
438
+ }
@@ -0,0 +1,36 @@
1
+ // Barrel re-export -- see route-types/ for implementations.
2
+ export {
3
+ extractParamsFromPattern,
4
+ formatRouteEntry,
5
+ } from "./route-types/param-extraction.js";
6
+ export { extractRoutesFromSource } from "./route-types/ast-route-extraction.js";
7
+ export {
8
+ generatePerModuleTypesSource,
9
+ generateRouteTypesSource,
10
+ } from "./route-types/codegen.js";
11
+ export {
12
+ DEFAULT_EXCLUDE_PATTERNS,
13
+ type ScanFilter,
14
+ createScanFilter,
15
+ findTsFiles,
16
+ } from "./route-types/scan-filter.js";
17
+ export {
18
+ writePerModuleRouteTypes,
19
+ writePerModuleRouteTypesForFile,
20
+ } from "./route-types/per-module-writer.js";
21
+ export {
22
+ type UnresolvableReason,
23
+ type UnresolvableInclude,
24
+ extractIncludesWithDiagnostics,
25
+ } from "./route-types/include-resolution.js";
26
+ export {
27
+ extractUrlsVariableFromRouter,
28
+ buildCombinedRouteMapForRouterFile,
29
+ detectUnresolvableIncludes,
30
+ detectUnresolvableIncludesForUrlsFile,
31
+ findNestedRouterConflict,
32
+ formatNestedRouterConflictError,
33
+ findRouterFiles,
34
+ writeCombinedRouteTypes,
35
+ } from "./route-types/router-processing.js";
36
+ export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Build-time utilities for @rangojs/router
3
+ *
4
+ * Note: Route manifest is automatically generated at runtime on first request.
5
+ * These build-time utilities are only needed for advanced use cases like
6
+ * custom tooling or pre-generating manifests.
7
+ *
8
+ * @example Generate manifest programmatically (for custom tooling)
9
+ * ```typescript
10
+ * import { generateManifest } from "@rangojs/router/build";
11
+ * import { urlpatterns } from "./urls";
12
+ *
13
+ * const manifest = generateManifest(urlpatterns);
14
+ * console.log(manifest.routeManifest); // { home: "/", about: "/about", ... }
15
+ * ```
16
+ */
17
+
18
+ export {
19
+ generateManifest,
20
+ generateManifestFull,
21
+ generateManifestCode,
22
+ type GeneratedManifest,
23
+ } from "./generate-manifest.js";
24
+
25
+ export { buildRouteTrie, type TrieNode, type TrieLeaf } from "./route-trie.js";
26
+
27
+ export {
28
+ writePerModuleRouteTypes,
29
+ extractRoutesFromSource,
30
+ generatePerModuleTypesSource,
31
+ createScanFilter,
32
+ type ScanFilter,
33
+ } from "./generate-route-types.js";
34
+
35
+ export { hashParams } from "../prerender/param-hash.js";