@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.71

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 (307) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +942 -4
  3. package/dist/bin/rango.js +1689 -0
  4. package/dist/vite/index.js +4951 -930
  5. package/package.json +70 -60
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +294 -0
  8. package/skills/caching/SKILL.md +93 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +334 -72
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +92 -31
  18. package/skills/loader/SKILL.md +404 -44
  19. package/skills/middleware/SKILL.md +173 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +204 -1
  22. package/skills/prerender/SKILL.md +685 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +257 -14
  26. package/skills/router-setup/SKILL.md +210 -32
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +328 -89
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +102 -4
  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/app-version.ts +14 -0
  36. package/src/browser/event-controller.ts +92 -64
  37. package/src/browser/history-state.ts +80 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +24 -4
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +20 -12
  42. package/src/browser/navigation-bridge.ts +296 -558
  43. package/src/browser/navigation-client.ts +179 -69
  44. package/src/browser/navigation-store.ts +73 -55
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +328 -313
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +150 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +160 -0
  53. package/src/browser/prefetch/resource-ready.ts +77 -0
  54. package/src/browser/rango-state.ts +112 -0
  55. package/src/browser/react/Link.tsx +230 -74
  56. package/src/browser/react/NavigationProvider.tsx +87 -11
  57. package/src/browser/react/context.ts +11 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +12 -12
  60. package/src/browser/react/location-state-shared.ts +95 -53
  61. package/src/browser/react/location-state.ts +60 -15
  62. package/src/browser/react/mount-context.ts +6 -1
  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 +29 -51
  66. package/src/browser/react/use-client-cache.ts +5 -3
  67. package/src/browser/react/use-handle.ts +30 -126
  68. package/src/browser/react/use-href.tsx +2 -2
  69. package/src/browser/react/use-link-status.ts +6 -5
  70. package/src/browser/react/use-navigation.ts +22 -63
  71. package/src/browser/react/use-params.ts +65 -0
  72. package/src/browser/react/use-pathname.ts +47 -0
  73. package/src/browser/react/use-router.ts +76 -0
  74. package/src/browser/react/use-search-params.ts +56 -0
  75. package/src/browser/react/use-segments.ts +80 -97
  76. package/src/browser/response-adapter.ts +73 -0
  77. package/src/browser/rsc-router.tsx +214 -58
  78. package/src/browser/scroll-restoration.ts +127 -52
  79. package/src/browser/segment-reconciler.ts +221 -0
  80. package/src/browser/segment-structure-assert.ts +16 -0
  81. package/src/browser/server-action-bridge.ts +510 -603
  82. package/src/browser/shallow.ts +6 -1
  83. package/src/browser/types.ts +141 -48
  84. package/src/browser/validate-redirect-origin.ts +29 -0
  85. package/src/build/generate-manifest.ts +235 -24
  86. package/src/build/generate-route-types.ts +39 -0
  87. package/src/build/index.ts +13 -0
  88. package/src/build/route-trie.ts +265 -0
  89. package/src/build/route-types/ast-helpers.ts +25 -0
  90. package/src/build/route-types/ast-route-extraction.ts +98 -0
  91. package/src/build/route-types/codegen.ts +102 -0
  92. package/src/build/route-types/include-resolution.ts +418 -0
  93. package/src/build/route-types/param-extraction.ts +48 -0
  94. package/src/build/route-types/per-module-writer.ts +128 -0
  95. package/src/build/route-types/router-processing.ts +618 -0
  96. package/src/build/route-types/scan-filter.ts +85 -0
  97. package/src/build/runtime-discovery.ts +231 -0
  98. package/src/cache/background-task.ts +34 -0
  99. package/src/cache/cache-key-utils.ts +44 -0
  100. package/src/cache/cache-policy.ts +125 -0
  101. package/src/cache/cache-runtime.ts +342 -0
  102. package/src/cache/cache-scope.ts +167 -309
  103. package/src/cache/cf/cf-cache-store.ts +571 -17
  104. package/src/cache/cf/index.ts +13 -3
  105. package/src/cache/document-cache.ts +116 -77
  106. package/src/cache/handle-capture.ts +81 -0
  107. package/src/cache/handle-snapshot.ts +41 -0
  108. package/src/cache/index.ts +1 -15
  109. package/src/cache/memory-segment-store.ts +191 -13
  110. package/src/cache/profile-registry.ts +73 -0
  111. package/src/cache/read-through-swr.ts +134 -0
  112. package/src/cache/segment-codec.ts +256 -0
  113. package/src/cache/taint.ts +153 -0
  114. package/src/cache/types.ts +72 -122
  115. package/src/client.rsc.tsx +3 -1
  116. package/src/client.tsx +105 -179
  117. package/src/component-utils.ts +4 -4
  118. package/src/components/DefaultDocument.tsx +5 -1
  119. package/src/context-var.ts +156 -0
  120. package/src/debug.ts +19 -9
  121. package/src/errors.ts +108 -2
  122. package/src/handle.ts +55 -29
  123. package/src/handles/MetaTags.tsx +73 -20
  124. package/src/handles/breadcrumbs.ts +66 -0
  125. package/src/handles/index.ts +1 -0
  126. package/src/handles/meta.ts +30 -13
  127. package/src/host/cookie-handler.ts +21 -15
  128. package/src/host/errors.ts +8 -8
  129. package/src/host/index.ts +4 -7
  130. package/src/host/pattern-matcher.ts +27 -27
  131. package/src/host/router.ts +61 -39
  132. package/src/host/testing.ts +8 -8
  133. package/src/host/types.ts +15 -7
  134. package/src/host/utils.ts +1 -1
  135. package/src/href-client.ts +119 -29
  136. package/src/index.rsc.ts +155 -19
  137. package/src/index.ts +223 -30
  138. package/src/internal-debug.ts +11 -0
  139. package/src/loader.rsc.ts +26 -157
  140. package/src/loader.ts +27 -10
  141. package/src/network-error-thrower.tsx +3 -1
  142. package/src/outlet-provider.tsx +45 -0
  143. package/src/prerender/param-hash.ts +37 -0
  144. package/src/prerender/store.ts +186 -0
  145. package/src/prerender.ts +524 -0
  146. package/src/reverse.ts +351 -0
  147. package/src/root-error-boundary.tsx +41 -29
  148. package/src/route-content-wrapper.tsx +7 -4
  149. package/src/route-definition/dsl-helpers.ts +982 -0
  150. package/src/route-definition/helper-factories.ts +200 -0
  151. package/src/route-definition/helpers-types.ts +434 -0
  152. package/src/route-definition/index.ts +55 -0
  153. package/src/route-definition/redirect.ts +101 -0
  154. package/src/route-definition/resolve-handler-use.ts +149 -0
  155. package/src/route-definition.ts +1 -1428
  156. package/src/route-map-builder.ts +217 -123
  157. package/src/route-name.ts +53 -0
  158. package/src/route-types.ts +70 -8
  159. package/src/router/content-negotiation.ts +215 -0
  160. package/src/router/debug-manifest.ts +72 -0
  161. package/src/router/error-handling.ts +9 -9
  162. package/src/router/find-match.ts +160 -0
  163. package/src/router/handler-context.ts +435 -86
  164. package/src/router/intercept-resolution.ts +402 -0
  165. package/src/router/lazy-includes.ts +237 -0
  166. package/src/router/loader-resolution.ts +356 -128
  167. package/src/router/logging.ts +251 -0
  168. package/src/router/manifest.ts +154 -35
  169. package/src/router/match-api.ts +555 -0
  170. package/src/router/match-context.ts +5 -3
  171. package/src/router/match-handlers.ts +440 -0
  172. package/src/router/match-middleware/background-revalidation.ts +108 -93
  173. package/src/router/match-middleware/cache-lookup.ts +459 -10
  174. package/src/router/match-middleware/cache-store.ts +98 -26
  175. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  176. package/src/router/match-middleware/segment-resolution.ts +80 -6
  177. package/src/router/match-pipelines.ts +10 -45
  178. package/src/router/match-result.ts +135 -35
  179. package/src/router/metrics.ts +240 -15
  180. package/src/router/middleware-cookies.ts +55 -0
  181. package/src/router/middleware-types.ts +220 -0
  182. package/src/router/middleware.ts +324 -369
  183. package/src/router/navigation-snapshot.ts +182 -0
  184. package/src/router/pattern-matching.ts +211 -43
  185. package/src/router/prerender-match.ts +502 -0
  186. package/src/router/preview-match.ts +98 -0
  187. package/src/router/request-classification.ts +310 -0
  188. package/src/router/revalidation.ts +137 -38
  189. package/src/router/route-snapshot.ts +245 -0
  190. package/src/router/router-context.ts +41 -21
  191. package/src/router/router-interfaces.ts +484 -0
  192. package/src/router/router-options.ts +618 -0
  193. package/src/router/router-registry.ts +24 -0
  194. package/src/router/segment-resolution/fresh.ts +748 -0
  195. package/src/router/segment-resolution/helpers.ts +268 -0
  196. package/src/router/segment-resolution/loader-cache.ts +199 -0
  197. package/src/router/segment-resolution/revalidation.ts +1379 -0
  198. package/src/router/segment-resolution/static-store.ts +67 -0
  199. package/src/router/segment-resolution.ts +21 -0
  200. package/src/router/segment-wrappers.ts +291 -0
  201. package/src/router/telemetry-otel.ts +299 -0
  202. package/src/router/telemetry.ts +300 -0
  203. package/src/router/timeout.ts +148 -0
  204. package/src/router/trie-matching.ts +239 -0
  205. package/src/router/types.ts +78 -3
  206. package/src/router.ts +740 -4252
  207. package/src/rsc/handler-context.ts +45 -0
  208. package/src/rsc/handler.ts +907 -797
  209. package/src/rsc/helpers.ts +140 -6
  210. package/src/rsc/index.ts +0 -20
  211. package/src/rsc/loader-fetch.ts +229 -0
  212. package/src/rsc/manifest-init.ts +90 -0
  213. package/src/rsc/nonce.ts +14 -0
  214. package/src/rsc/origin-guard.ts +141 -0
  215. package/src/rsc/progressive-enhancement.ts +391 -0
  216. package/src/rsc/response-error.ts +37 -0
  217. package/src/rsc/response-route-handler.ts +347 -0
  218. package/src/rsc/rsc-rendering.ts +246 -0
  219. package/src/rsc/runtime-warnings.ts +42 -0
  220. package/src/rsc/server-action.ts +356 -0
  221. package/src/rsc/ssr-setup.ts +128 -0
  222. package/src/rsc/types.ts +46 -11
  223. package/src/search-params.ts +230 -0
  224. package/src/segment-system.tsx +165 -17
  225. package/src/server/context.ts +315 -58
  226. package/src/server/cookie-store.ts +190 -0
  227. package/src/server/fetchable-loader-store.ts +37 -0
  228. package/src/server/handle-store.ts +113 -15
  229. package/src/server/loader-registry.ts +24 -64
  230. package/src/server/request-context.ts +607 -81
  231. package/src/server.ts +35 -130
  232. package/src/ssr/index.tsx +103 -30
  233. package/src/static-handler.ts +126 -0
  234. package/src/theme/ThemeProvider.tsx +21 -15
  235. package/src/theme/ThemeScript.tsx +5 -5
  236. package/src/theme/constants.ts +5 -2
  237. package/src/theme/index.ts +4 -14
  238. package/src/theme/theme-context.ts +4 -30
  239. package/src/theme/theme-script.ts +21 -18
  240. package/src/types/boundaries.ts +158 -0
  241. package/src/types/cache-types.ts +198 -0
  242. package/src/types/error-types.ts +192 -0
  243. package/src/types/global-namespace.ts +100 -0
  244. package/src/types/handler-context.ts +791 -0
  245. package/src/types/index.ts +88 -0
  246. package/src/types/loader-types.ts +210 -0
  247. package/src/types/route-config.ts +170 -0
  248. package/src/types/route-entry.ts +109 -0
  249. package/src/types/segments.ts +151 -0
  250. package/src/types.ts +1 -1623
  251. package/src/urls/include-helper.ts +197 -0
  252. package/src/urls/index.ts +53 -0
  253. package/src/urls/path-helper-types.ts +346 -0
  254. package/src/urls/path-helper.ts +364 -0
  255. package/src/urls/pattern-types.ts +107 -0
  256. package/src/urls/response-types.ts +116 -0
  257. package/src/urls/type-extraction.ts +372 -0
  258. package/src/urls/urls-function.ts +98 -0
  259. package/src/urls.ts +1 -802
  260. package/src/use-loader.tsx +161 -81
  261. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  262. package/src/vite/discovery/discover-routers.ts +348 -0
  263. package/src/vite/discovery/prerender-collection.ts +439 -0
  264. package/src/vite/discovery/route-types-writer.ts +258 -0
  265. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  266. package/src/vite/discovery/state.ts +117 -0
  267. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  268. package/src/vite/index.ts +15 -1129
  269. package/src/vite/plugin-types.ts +103 -0
  270. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  271. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  272. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  273. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  274. package/src/vite/plugins/expose-id-utils.ts +299 -0
  275. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  276. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  277. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  278. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  279. package/src/vite/plugins/expose-ids/types.ts +45 -0
  280. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  281. package/src/vite/plugins/performance-tracks.ts +88 -0
  282. package/src/vite/plugins/refresh-cmd.ts +127 -0
  283. package/src/vite/plugins/use-cache-transform.ts +323 -0
  284. package/src/vite/plugins/version-injector.ts +83 -0
  285. package/src/vite/plugins/version-plugin.ts +266 -0
  286. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  287. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  288. package/src/vite/rango.ts +462 -0
  289. package/src/vite/router-discovery.ts +918 -0
  290. package/src/vite/utils/ast-handler-extract.ts +517 -0
  291. package/src/vite/utils/banner.ts +36 -0
  292. package/src/vite/utils/bundle-analysis.ts +137 -0
  293. package/src/vite/utils/manifest-utils.ts +70 -0
  294. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  295. package/src/vite/utils/prerender-utils.ts +207 -0
  296. package/src/vite/utils/shared-utils.ts +170 -0
  297. package/CLAUDE.md +0 -43
  298. package/src/browser/lru-cache.ts +0 -69
  299. package/src/browser/request-controller.ts +0 -164
  300. package/src/cache/memory-store.ts +0 -253
  301. package/src/href-context.ts +0 -33
  302. package/src/href.ts +0 -255
  303. package/src/server/route-manifest-cache.ts +0 -173
  304. package/src/vite/expose-handle-id.ts +0 -209
  305. package/src/vite/expose-loader-id.ts +0 -426
  306. package/src/vite/expose-location-state-id.ts +0 -177
  307. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Router Discovery
3
+ *
4
+ * Core discovery logic: imports the user's entry file via the RSC
5
+ * environment's module runner, generates manifests for each discovered
6
+ * router, and builds route tries for O(path_length) matching.
7
+ */
8
+
9
+ import {
10
+ buildCombinedRouteMapForRouterFile,
11
+ formatNestedRouterConflictError,
12
+ findNestedRouterConflict,
13
+ } from "../../build/generate-route-types.js";
14
+ import {
15
+ flattenLeafEntries,
16
+ buildRouteToStaticPrefix,
17
+ } from "../utils/manifest-utils.js";
18
+ import type { DiscoveryState, PrecomputedEntry } from "./state.js";
19
+ import {
20
+ expandPrerenderRoutes,
21
+ renderStaticHandlers,
22
+ } from "./prerender-collection.js";
23
+
24
+ /**
25
+ * Import the user's entry via RSC runner, generate manifests for each
26
+ * discovered router, build route tries, and optionally run prerender
27
+ * expansion and static handler rendering (build mode only).
28
+ *
29
+ * Returns the imported `@rangojs/router/server` module so the caller
30
+ * can access the RouterRegistry and manifest setters.
31
+ */
32
+ export async function discoverRouters(
33
+ state: DiscoveryState,
34
+ rscEnv: any,
35
+ ): Promise<any> {
36
+ if (!state.resolvedEntryPath) return;
37
+
38
+ // Import the entry file via RSC environment.
39
+ // For node preset: this is the router file (createRouter() registers in RouterRegistry).
40
+ // For cloudflare preset: this is the worker entry (which imports the router).
41
+ await rscEnv.runner.import(state.resolvedEntryPath);
42
+
43
+ // Import the router package to access the registry
44
+ const serverMod = await rscEnv.runner.import("@rangojs/router/server");
45
+ let registry: Map<string, any> = serverMod.RouterRegistry;
46
+
47
+ if (!registry || registry.size === 0) {
48
+ // No RSC routers found directly. Check for host routers with lazy handlers
49
+ // that need to be resolved to trigger sub-app createRouter() calls.
50
+ try {
51
+ const hostRegistry: Map<string, any> | undefined =
52
+ serverMod.HostRouterRegistry;
53
+
54
+ if (hostRegistry && hostRegistry.size > 0) {
55
+ console.log(
56
+ `[rsc-router] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`,
57
+ );
58
+
59
+ for (const [, entry] of hostRegistry) {
60
+ for (const route of entry.routes) {
61
+ if (typeof route.handler === "function") {
62
+ try {
63
+ await route.handler();
64
+ } catch {
65
+ // Lazy handler may fail in temp server context, that's OK
66
+ }
67
+ }
68
+ }
69
+ if (entry.fallback && typeof entry.fallback.handler === "function") {
70
+ try {
71
+ await entry.fallback.handler();
72
+ } catch {
73
+ // Fallback handler may fail in temp server context
74
+ }
75
+ }
76
+ }
77
+
78
+ // Re-read RouterRegistry - sub-app createRouter() calls should have populated it
79
+ const freshServerMod = await rscEnv.runner.import(
80
+ "@rangojs/router/server",
81
+ );
82
+ const freshRegistry: Map<string, any> = freshServerMod.RouterRegistry;
83
+
84
+ if (freshRegistry && freshRegistry.size > 0) {
85
+ // Update references so the manifest generation below uses the fresh data
86
+ Object.assign(serverMod, freshServerMod);
87
+ registry = freshRegistry;
88
+ }
89
+ }
90
+ } catch {
91
+ // Host-router discovery is best-effort; skip if unavailable
92
+ }
93
+
94
+ // If still no routers after host router resolution, fail
95
+ if (!registry || registry.size === 0) {
96
+ throw new Error(
97
+ `[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`,
98
+ );
99
+ }
100
+ }
101
+
102
+ // Import build utilities for manifest generation
103
+ const buildMod = await rscEnv.runner.import("@rangojs/router/build");
104
+ const generateManifestFull = buildMod.generateManifestFull;
105
+
106
+ const nestedRouterConflict = findNestedRouterConflict(
107
+ [...registry.values()]
108
+ .map((router) => router.__sourceFile)
109
+ .filter(
110
+ (sourceFile): sourceFile is string => typeof sourceFile === "string",
111
+ ),
112
+ );
113
+ if (nestedRouterConflict) {
114
+ throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
115
+ }
116
+
117
+ // Build into local variables first. Only commit to state after the
118
+ // full pass succeeds, so a failed re-discovery preserves the last
119
+ // known-good state instead of leaving it partially wiped.
120
+ const newMergedRouteManifest: Record<string, string> = {};
121
+ const newMergedPrecomputedEntries: PrecomputedEntry[] = [];
122
+ const newPerRouterManifests: typeof state.perRouterManifests = [];
123
+ const newPerRouterManifestDataMap = new Map<string, any>();
124
+ const newPerRouterPrecomputedMap = new Map<string, PrecomputedEntry[]>();
125
+ const newPerRouterTrieMap = new Map<string, any>();
126
+ let mergedRouteAncestry: Record<string, string[]> = {};
127
+ let mergedRouteTrailingSlash: Record<string, string> = {};
128
+
129
+ let routerMountIndex = 0;
130
+ // Collect all manifests for trie building (avoid re-running generateManifest)
131
+ const allManifests: Array<{ id: string; manifest: any }> = [];
132
+
133
+ for (const [id, router] of registry) {
134
+ if (!router.urlpatterns || !generateManifestFull) {
135
+ continue;
136
+ }
137
+
138
+ const manifest = generateManifestFull(
139
+ router.urlpatterns,
140
+ routerMountIndex,
141
+ router.__basename ? { urlPrefix: router.__basename } : undefined,
142
+ );
143
+ routerMountIndex++;
144
+ allManifests.push({ id, manifest });
145
+ const routeCount = Object.keys(manifest.routeManifest).length;
146
+ const staticRoutes = Object.values(manifest.routeManifest).filter(
147
+ (p: any) => !p.includes(":") && !p.includes("*"),
148
+ ).length;
149
+ const dynamicRoutes = routeCount - staticRoutes;
150
+
151
+ // Merge into the combined manifest
152
+ Object.assign(newMergedRouteManifest, manifest.routeManifest);
153
+
154
+ // Compute factory-only prefixes: dot-prefixed groups in the runtime
155
+ // manifest that the static parser cannot see. These are routes created
156
+ // by factory functions (e.g. createDocsPatterns()) and should always be
157
+ // supplemented on file change since HMR won't re-discover them.
158
+ let factoryOnlyPrefixes: Set<string> | undefined;
159
+ if (router.__sourceFile) {
160
+ const staticParsed = buildCombinedRouteMapForRouterFile(
161
+ router.__sourceFile,
162
+ );
163
+ const staticNames = new Set(Object.keys(staticParsed.routes));
164
+ factoryOnlyPrefixes = new Set<string>();
165
+ for (const name of Object.keys(manifest.routeManifest)) {
166
+ if (staticNames.has(name)) continue;
167
+ const dotIdx = name.indexOf(".");
168
+ if (dotIdx <= 0) continue;
169
+ const prefix = name.substring(0, dotIdx + 1);
170
+ if ([...staticNames].some((n) => n.startsWith(prefix))) continue;
171
+ factoryOnlyPrefixes.add(prefix);
172
+ }
173
+ if (factoryOnlyPrefixes.size === 0) factoryOnlyPrefixes = undefined;
174
+ }
175
+
176
+ newPerRouterManifests.push({
177
+ id,
178
+ routeManifest: manifest.routeManifest,
179
+ routeSearchSchemas: manifest.routeSearchSchemas,
180
+ sourceFile: router.__sourceFile,
181
+ factoryOnlyPrefixes,
182
+ });
183
+
184
+ // Merge ancestry (internal field, used only for trie building)
185
+ if (manifest._routeAncestry) {
186
+ Object.assign(mergedRouteAncestry, manifest._routeAncestry);
187
+ }
188
+ // Merge trailing slash config
189
+ if (manifest.routeTrailingSlash) {
190
+ Object.assign(mergedRouteTrailingSlash, manifest.routeTrailingSlash);
191
+ }
192
+
193
+ // Flatten prefix tree leaf nodes into precomputed entries.
194
+ // Leaf nodes (no children) can have their routes used directly by
195
+ // evaluateLazyEntry() without running the handler at runtime.
196
+ flattenLeafEntries(
197
+ manifest.prefixTree,
198
+ manifest.routeManifest,
199
+ newMergedPrecomputedEntries,
200
+ );
201
+
202
+ // Store per-router manifest and precomputed entries for isolated virtual modules.
203
+ newPerRouterManifestDataMap.set(id, manifest.routeManifest);
204
+ const routerPrecomputed: PrecomputedEntry[] = [];
205
+ flattenLeafEntries(
206
+ manifest.prefixTree,
207
+ manifest.routeManifest,
208
+ routerPrecomputed,
209
+ );
210
+ newPerRouterPrecomputedMap.set(id, routerPrecomputed);
211
+
212
+ console.log(
213
+ `[rsc-router] Router "${id}" -> ${routeCount} routes ` +
214
+ `(${staticRoutes} static, ${dynamicRoutes} dynamic)`,
215
+ );
216
+ }
217
+
218
+ // Warn if multiple routers use auto-generated IDs (router_0, router_1, ...).
219
+ // Auto-IDs are assigned by counter and depend on module evaluation order,
220
+ // which can differ between build time and runtime (especially with dynamic
221
+ // imports in host routers). This causes per-router data to be loaded into
222
+ // the wrong router at runtime.
223
+ if (registry.size > 1) {
224
+ const autoIds = [...registry.keys()].filter((id) =>
225
+ /^router_\d+$/.test(id),
226
+ );
227
+ if (autoIds.length > 1) {
228
+ console.warn(
229
+ `[rsc-router] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). ` +
230
+ `In multi-router setups, each createRouter() must have an explicit \`id\` option ` +
231
+ `to ensure per-router manifest data is matched correctly at runtime. ` +
232
+ `Example: createRouter({ id: "site", ... })`,
233
+ );
234
+ }
235
+ }
236
+
237
+ // Build route trie from merged manifest + ancestry
238
+ let newMergedRouteTrie: any = null;
239
+ if (Object.keys(newMergedRouteManifest).length > 0) {
240
+ const buildRouteTrie = buildMod.buildRouteTrie;
241
+ if (buildRouteTrie && mergedRouteAncestry) {
242
+ // Build routeToStaticPrefix from saved manifests
243
+ const routeToStaticPrefix: Record<string, string> = {};
244
+ for (const { manifest } of allManifests) {
245
+ // Root-level routes have empty static prefix
246
+ for (const name of Object.keys(manifest.routeManifest)) {
247
+ if (!(name in routeToStaticPrefix)) {
248
+ routeToStaticPrefix[name] = "";
249
+ }
250
+ }
251
+ buildRouteToStaticPrefix(manifest.prefixTree, routeToStaticPrefix);
252
+ }
253
+
254
+ // Collect prerender route names and response type routes from all manifests
255
+ const prerenderRouteNames = new Set<string>();
256
+ const passthroughRouteNames = new Set<string>();
257
+ const mergedResponseTypeRoutes: Record<string, string> = {};
258
+ for (const { manifest } of allManifests) {
259
+ if (manifest.prerenderRoutes) {
260
+ for (const name of manifest.prerenderRoutes) {
261
+ prerenderRouteNames.add(name);
262
+ }
263
+ }
264
+ if (manifest.passthroughRoutes) {
265
+ for (const name of manifest.passthroughRoutes) {
266
+ passthroughRouteNames.add(name);
267
+ }
268
+ }
269
+ if (manifest.responseTypeRoutes) {
270
+ Object.assign(mergedResponseTypeRoutes, manifest.responseTypeRoutes);
271
+ }
272
+ }
273
+
274
+ newMergedRouteTrie = buildRouteTrie(
275
+ newMergedRouteManifest,
276
+ mergedRouteAncestry,
277
+ routeToStaticPrefix,
278
+ Object.keys(mergedRouteTrailingSlash).length > 0
279
+ ? mergedRouteTrailingSlash
280
+ : undefined,
281
+ prerenderRouteNames.size > 0 ? prerenderRouteNames : undefined,
282
+ passthroughRouteNames.size > 0 ? passthroughRouteNames : undefined,
283
+ Object.keys(mergedResponseTypeRoutes).length > 0
284
+ ? mergedResponseTypeRoutes
285
+ : undefined,
286
+ );
287
+
288
+ // Build per-router tries for multi-router isolation.
289
+ for (const { id, manifest } of allManifests) {
290
+ if (
291
+ !manifest._routeAncestry ||
292
+ Object.keys(manifest._routeAncestry).length === 0
293
+ )
294
+ continue;
295
+ const perRouterStaticPrefix: Record<string, string> = {};
296
+ for (const name of Object.keys(manifest.routeManifest)) {
297
+ perRouterStaticPrefix[name] = "";
298
+ }
299
+ buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
300
+
301
+ const perRouterPrerenderNames = manifest.prerenderRoutes
302
+ ? new Set<string>(manifest.prerenderRoutes)
303
+ : undefined;
304
+ const perRouterPassthroughNames = manifest.passthroughRoutes
305
+ ? new Set<string>(manifest.passthroughRoutes)
306
+ : undefined;
307
+
308
+ const perRouterTrie = buildRouteTrie(
309
+ manifest.routeManifest,
310
+ manifest._routeAncestry,
311
+ perRouterStaticPrefix,
312
+ manifest.routeTrailingSlash &&
313
+ Object.keys(manifest.routeTrailingSlash).length > 0
314
+ ? manifest.routeTrailingSlash
315
+ : undefined,
316
+ perRouterPrerenderNames && perRouterPrerenderNames.size > 0
317
+ ? perRouterPrerenderNames
318
+ : undefined,
319
+ perRouterPassthroughNames && perRouterPassthroughNames.size > 0
320
+ ? perRouterPassthroughNames
321
+ : undefined,
322
+ manifest.responseTypeRoutes &&
323
+ Object.keys(manifest.responseTypeRoutes).length > 0
324
+ ? manifest.responseTypeRoutes
325
+ : undefined,
326
+ );
327
+ newPerRouterTrieMap.set(id, perRouterTrie);
328
+ }
329
+ }
330
+ }
331
+
332
+ // Commit all local state to the shared discovery state atomically.
333
+ // This ensures a failed re-discovery (e.g. from a transient module
334
+ // evaluation error) preserves the last known-good state.
335
+ state.mergedRouteManifest = newMergedRouteManifest;
336
+ state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
337
+ state.perRouterManifests = newPerRouterManifests;
338
+ state.perRouterManifestDataMap = newPerRouterManifestDataMap;
339
+ state.perRouterPrecomputedMap = newPerRouterPrecomputedMap;
340
+ state.perRouterTrieMap = newPerRouterTrieMap;
341
+ state.mergedRouteTrie = newMergedRouteTrie;
342
+
343
+ // Expand prerender routes and render static handlers (build mode only)
344
+ await expandPrerenderRoutes(state, rscEnv, registry, allManifests);
345
+ await renderStaticHandlers(state, rscEnv, registry);
346
+
347
+ return serverMod;
348
+ }