@rangojs/router 0.0.0-experimental.9 → 0.0.0-experimental.a5f27bd5

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 (299) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1531 -155
  4. package/dist/vite/index.js +4440 -2170
  5. package/package.json +60 -54
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +50 -21
  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 +6 -4
  13. package/skills/hooks/SKILL.md +333 -71
  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 +74 -15
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +15 -11
  21. package/skills/parallel/SKILL.md +78 -1
  22. package/skills/prerender/SKILL.md +405 -45
  23. package/skills/rango/SKILL.md +85 -21
  24. package/skills/response-routes/SKILL.md +144 -91
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/theme/SKILL.md +9 -8
  28. package/skills/typesafety/SKILL.md +316 -87
  29. package/skills/use-cache/SKILL.md +324 -0
  30. package/src/__internal.ts +102 -4
  31. package/src/bin/rango.ts +312 -15
  32. package/src/browser/action-coordinator.ts +97 -0
  33. package/src/browser/action-response-classifier.ts +99 -0
  34. package/src/browser/event-controller.ts +87 -64
  35. package/src/browser/history-state.ts +80 -0
  36. package/src/browser/intercept-utils.ts +52 -0
  37. package/src/browser/link-interceptor.ts +24 -4
  38. package/src/browser/logging.ts +55 -0
  39. package/src/browser/merge-segment-loaders.ts +20 -12
  40. package/src/browser/navigation-bridge.ts +285 -553
  41. package/src/browser/navigation-client.ts +123 -73
  42. package/src/browser/navigation-store.ts +33 -50
  43. package/src/browser/navigation-transaction.ts +295 -0
  44. package/src/browser/network-error-handler.ts +61 -0
  45. package/src/browser/partial-update.ts +261 -309
  46. package/src/browser/prefetch/cache.ts +154 -0
  47. package/src/browser/prefetch/fetch.ts +135 -0
  48. package/src/browser/prefetch/observer.ts +65 -0
  49. package/src/browser/prefetch/policy.ts +48 -0
  50. package/src/browser/prefetch/queue.ts +88 -0
  51. package/src/browser/rango-state.ts +112 -0
  52. package/src/browser/react/Link.tsx +182 -70
  53. package/src/browser/react/NavigationProvider.tsx +51 -11
  54. package/src/browser/react/context.ts +6 -0
  55. package/src/browser/react/filter-segment-order.ts +11 -0
  56. package/src/browser/react/index.ts +12 -12
  57. package/src/browser/react/location-state-shared.ts +95 -53
  58. package/src/browser/react/location-state.ts +60 -15
  59. package/src/browser/react/mount-context.ts +6 -1
  60. package/src/browser/react/nonce-context.ts +23 -0
  61. package/src/browser/react/shallow-equal.ts +27 -0
  62. package/src/browser/react/use-action.ts +29 -51
  63. package/src/browser/react/use-client-cache.ts +5 -3
  64. package/src/browser/react/use-handle.ts +29 -70
  65. package/src/browser/react/use-link-status.ts +6 -5
  66. package/src/browser/react/use-navigation.ts +22 -63
  67. package/src/browser/react/use-params.ts +65 -0
  68. package/src/browser/react/use-pathname.ts +47 -0
  69. package/src/browser/react/use-router.ts +63 -0
  70. package/src/browser/react/use-search-params.ts +56 -0
  71. package/src/browser/react/use-segments.ts +80 -97
  72. package/src/browser/response-adapter.ts +73 -0
  73. package/src/browser/rsc-router.tsx +106 -27
  74. package/src/browser/scroll-restoration.ts +92 -16
  75. package/src/browser/segment-reconciler.ts +216 -0
  76. package/src/browser/segment-structure-assert.ts +16 -0
  77. package/src/browser/server-action-bridge.ts +504 -599
  78. package/src/browser/shallow.ts +6 -1
  79. package/src/browser/types.ts +107 -47
  80. package/src/browser/validate-redirect-origin.ts +29 -0
  81. package/src/build/generate-manifest.ts +82 -21
  82. package/src/build/generate-route-types.ts +36 -752
  83. package/src/build/index.ts +6 -5
  84. package/src/build/route-trie.ts +39 -13
  85. package/src/build/route-types/ast-helpers.ts +25 -0
  86. package/src/build/route-types/ast-route-extraction.ts +98 -0
  87. package/src/build/route-types/codegen.ts +102 -0
  88. package/src/build/route-types/include-resolution.ts +411 -0
  89. package/src/build/route-types/param-extraction.ts +48 -0
  90. package/src/build/route-types/per-module-writer.ts +128 -0
  91. package/src/build/route-types/router-processing.ts +469 -0
  92. package/src/build/route-types/scan-filter.ts +78 -0
  93. package/src/build/runtime-discovery.ts +231 -0
  94. package/src/cache/background-task.ts +34 -0
  95. package/src/cache/cache-key-utils.ts +44 -0
  96. package/src/cache/cache-policy.ts +125 -0
  97. package/src/cache/cache-runtime.ts +338 -0
  98. package/src/cache/cache-scope.ts +120 -301
  99. package/src/cache/cf/cf-cache-store.ts +119 -7
  100. package/src/cache/cf/index.ts +8 -2
  101. package/src/cache/document-cache.ts +101 -72
  102. package/src/cache/handle-capture.ts +81 -0
  103. package/src/cache/handle-snapshot.ts +41 -0
  104. package/src/cache/index.ts +0 -15
  105. package/src/cache/memory-segment-store.ts +191 -13
  106. package/src/cache/profile-registry.ts +73 -0
  107. package/src/cache/read-through-swr.ts +134 -0
  108. package/src/cache/segment-codec.ts +256 -0
  109. package/src/cache/taint.ts +98 -0
  110. package/src/cache/types.ts +72 -122
  111. package/src/client.rsc.tsx +3 -1
  112. package/src/client.tsx +84 -126
  113. package/src/component-utils.ts +4 -4
  114. package/src/components/DefaultDocument.tsx +5 -1
  115. package/src/context-var.ts +86 -0
  116. package/src/debug.ts +17 -7
  117. package/src/errors.ts +77 -7
  118. package/src/handle.ts +15 -10
  119. package/src/handles/MetaTags.tsx +73 -20
  120. package/src/handles/breadcrumbs.ts +66 -0
  121. package/src/handles/index.ts +1 -0
  122. package/src/handles/meta.ts +30 -13
  123. package/src/host/cookie-handler.ts +21 -15
  124. package/src/host/errors.ts +8 -8
  125. package/src/host/index.ts +4 -7
  126. package/src/host/pattern-matcher.ts +27 -27
  127. package/src/host/router.ts +61 -39
  128. package/src/host/testing.ts +8 -8
  129. package/src/host/types.ts +15 -7
  130. package/src/host/utils.ts +1 -1
  131. package/src/href-client.ts +65 -45
  132. package/src/index.rsc.ts +133 -21
  133. package/src/index.ts +164 -52
  134. package/src/internal-debug.ts +11 -0
  135. package/src/loader.rsc.ts +25 -143
  136. package/src/loader.ts +27 -10
  137. package/src/network-error-thrower.tsx +3 -1
  138. package/src/outlet-provider.tsx +45 -0
  139. package/src/prerender/param-hash.ts +4 -2
  140. package/src/prerender/store.ts +158 -13
  141. package/src/prerender.ts +333 -26
  142. package/src/reverse.ts +184 -121
  143. package/src/root-error-boundary.tsx +41 -29
  144. package/src/route-content-wrapper.tsx +7 -4
  145. package/src/route-definition/dsl-helpers.ts +934 -0
  146. package/src/route-definition/helper-factories.ts +200 -0
  147. package/src/route-definition/helpers-types.ts +430 -0
  148. package/src/route-definition/index.ts +52 -0
  149. package/src/route-definition/redirect.ts +93 -0
  150. package/src/route-definition.ts +1 -1431
  151. package/src/route-map-builder.ts +156 -123
  152. package/src/route-name.ts +53 -0
  153. package/src/route-types.ts +48 -9
  154. package/src/router/content-negotiation.ts +116 -0
  155. package/src/router/debug-manifest.ts +72 -0
  156. package/src/router/error-handling.ts +9 -9
  157. package/src/router/find-match.ts +158 -0
  158. package/src/router/handler-context.ts +374 -81
  159. package/src/router/intercept-resolution.ts +24 -16
  160. package/src/router/lazy-includes.ts +234 -0
  161. package/src/router/loader-resolution.ts +215 -122
  162. package/src/router/logging.ts +248 -0
  163. package/src/router/manifest.ts +83 -32
  164. package/src/router/match-api.ts +118 -119
  165. package/src/router/match-context.ts +4 -2
  166. package/src/router/match-handlers.ts +440 -0
  167. package/src/router/match-middleware/background-revalidation.ts +80 -93
  168. package/src/router/match-middleware/cache-lookup.ts +336 -84
  169. package/src/router/match-middleware/cache-store.ts +43 -24
  170. package/src/router/match-middleware/intercept-resolution.ts +45 -20
  171. package/src/router/match-middleware/segment-resolution.ts +16 -8
  172. package/src/router/match-pipelines.ts +10 -45
  173. package/src/router/match-result.ts +34 -28
  174. package/src/router/metrics.ts +235 -15
  175. package/src/router/middleware-cookies.ts +55 -0
  176. package/src/router/middleware-types.ts +222 -0
  177. package/src/router/middleware.ts +324 -367
  178. package/src/router/pattern-matching.ts +197 -41
  179. package/src/router/prerender-match.ts +402 -0
  180. package/src/router/preview-match.ts +170 -0
  181. package/src/router/revalidation.ts +137 -38
  182. package/src/router/router-context.ts +36 -21
  183. package/src/router/router-interfaces.ts +452 -0
  184. package/src/router/router-options.ts +592 -0
  185. package/src/router/router-registry.ts +24 -0
  186. package/src/router/segment-resolution/fresh.ts +570 -0
  187. package/src/router/segment-resolution/helpers.ts +263 -0
  188. package/src/router/segment-resolution/loader-cache.ts +198 -0
  189. package/src/router/segment-resolution/revalidation.ts +1239 -0
  190. package/src/router/segment-resolution/static-store.ts +67 -0
  191. package/src/router/segment-resolution.ts +21 -1315
  192. package/src/router/segment-wrappers.ts +289 -0
  193. package/src/router/telemetry-otel.ts +299 -0
  194. package/src/router/telemetry.ts +300 -0
  195. package/src/router/timeout.ts +148 -0
  196. package/src/router/trie-matching.ts +96 -29
  197. package/src/router/types.ts +16 -9
  198. package/src/router.ts +590 -1983
  199. package/src/rsc/handler-context.ts +45 -0
  200. package/src/rsc/handler.ts +661 -1015
  201. package/src/rsc/helpers.ts +140 -6
  202. package/src/rsc/index.ts +0 -20
  203. package/src/rsc/loader-fetch.ts +209 -0
  204. package/src/rsc/manifest-init.ts +86 -0
  205. package/src/rsc/nonce.ts +14 -0
  206. package/src/rsc/origin-guard.ts +141 -0
  207. package/src/rsc/progressive-enhancement.ts +379 -0
  208. package/src/rsc/response-error.ts +37 -0
  209. package/src/rsc/response-route-handler.ts +347 -0
  210. package/src/rsc/rsc-rendering.ts +237 -0
  211. package/src/rsc/runtime-warnings.ts +42 -0
  212. package/src/rsc/server-action.ts +348 -0
  213. package/src/rsc/ssr-setup.ts +128 -0
  214. package/src/rsc/types.ts +38 -11
  215. package/src/search-params.ts +230 -0
  216. package/src/segment-system.tsx +25 -13
  217. package/src/server/context.ts +173 -48
  218. package/src/server/cookie-store.ts +190 -0
  219. package/src/server/fetchable-loader-store.ts +37 -0
  220. package/src/server/handle-store.ts +94 -15
  221. package/src/server/loader-registry.ts +15 -56
  222. package/src/server/request-context.ts +430 -70
  223. package/src/server.ts +35 -155
  224. package/src/ssr/index.tsx +100 -31
  225. package/src/static-handler.ts +114 -0
  226. package/src/theme/ThemeProvider.tsx +21 -15
  227. package/src/theme/ThemeScript.tsx +5 -5
  228. package/src/theme/constants.ts +5 -2
  229. package/src/theme/index.ts +4 -14
  230. package/src/theme/theme-context.ts +4 -30
  231. package/src/theme/theme-script.ts +21 -18
  232. package/src/types/boundaries.ts +158 -0
  233. package/src/types/cache-types.ts +198 -0
  234. package/src/types/error-types.ts +192 -0
  235. package/src/types/global-namespace.ts +100 -0
  236. package/src/types/handler-context.ts +687 -0
  237. package/src/types/index.ts +88 -0
  238. package/src/types/loader-types.ts +183 -0
  239. package/src/types/route-config.ts +170 -0
  240. package/src/types/route-entry.ts +102 -0
  241. package/src/types/segments.ts +148 -0
  242. package/src/types.ts +1 -1757
  243. package/src/urls/include-helper.ts +197 -0
  244. package/src/urls/index.ts +53 -0
  245. package/src/urls/path-helper-types.ts +339 -0
  246. package/src/urls/path-helper.ts +329 -0
  247. package/src/urls/pattern-types.ts +95 -0
  248. package/src/urls/response-types.ts +106 -0
  249. package/src/urls/type-extraction.ts +372 -0
  250. package/src/urls/urls-function.ts +98 -0
  251. package/src/urls.ts +1 -1282
  252. package/src/use-loader.tsx +85 -77
  253. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  254. package/src/vite/discovery/discover-routers.ts +344 -0
  255. package/src/vite/discovery/prerender-collection.ts +385 -0
  256. package/src/vite/discovery/route-types-writer.ts +258 -0
  257. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  258. package/src/vite/discovery/state.ts +110 -0
  259. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  260. package/src/vite/index.ts +11 -1963
  261. package/src/vite/plugin-types.ts +131 -0
  262. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  263. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  264. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  265. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
  266. package/src/vite/plugins/expose-id-utils.ts +287 -0
  267. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  268. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  269. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  270. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  271. package/src/vite/plugins/expose-ids/types.ts +45 -0
  272. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  273. package/src/vite/plugins/refresh-cmd.ts +65 -0
  274. package/src/vite/plugins/use-cache-transform.ts +323 -0
  275. package/src/vite/plugins/version-injector.ts +83 -0
  276. package/src/vite/plugins/version-plugin.ts +254 -0
  277. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  278. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  279. package/src/vite/rango.ts +510 -0
  280. package/src/vite/router-discovery.ts +785 -0
  281. package/src/vite/utils/ast-handler-extract.ts +517 -0
  282. package/src/vite/utils/banner.ts +36 -0
  283. package/src/vite/utils/bundle-analysis.ts +137 -0
  284. package/src/vite/utils/manifest-utils.ts +70 -0
  285. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  286. package/src/vite/utils/prerender-utils.ts +189 -0
  287. package/src/vite/utils/shared-utils.ts +169 -0
  288. package/CLAUDE.md +0 -43
  289. package/src/browser/lru-cache.ts +0 -69
  290. package/src/browser/request-controller.ts +0 -164
  291. package/src/cache/memory-store.ts +0 -253
  292. package/src/href-context.ts +0 -33
  293. package/src/router.gen.ts +0 -6
  294. package/src/urls.gen.ts +0 -8
  295. package/src/vite/expose-handle-id.ts +0 -209
  296. package/src/vite/expose-loader-id.ts +0 -426
  297. package/src/vite/expose-location-state-id.ts +0 -177
  298. package/src/vite/expose-prerender-handler-id.ts +0 -429
  299. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Discovery State
3
+ *
4
+ * Shared mutable state for the router discovery plugin.
5
+ * Created once by createRouterDiscoveryPlugin() and passed
6
+ * to all extracted helper functions.
7
+ */
8
+
9
+ import type { ScanFilter } from "../../build/generate-route-types.js";
10
+
11
+ export const VIRTUAL_ROUTES_MANIFEST_ID = "virtual:rsc-router/routes-manifest";
12
+
13
+ export interface PluginOptions {
14
+ enableBuildPrerender?: boolean;
15
+ staticRouteTypesGeneration?: boolean;
16
+ include?: string[];
17
+ exclude?: string[];
18
+ // Mutable ref for deferred auto-discovery (node preset).
19
+ // The auto-discover config() hook populates this before configResolved.
20
+ routerPathRef?: { path?: string };
21
+ }
22
+
23
+ export interface PrecomputedEntry {
24
+ staticPrefix: string;
25
+ routes: Record<string, string>;
26
+ }
27
+
28
+ export interface ChunkInfo {
29
+ fileName: string;
30
+ exports: Array<{ name: string; handlerId: string; passthrough: boolean }>;
31
+ }
32
+
33
+ export interface PerRouterManifestEntry {
34
+ id: string;
35
+ routeManifest: Record<string, string>;
36
+ routeSearchSchemas?: Record<string, Record<string, string>>;
37
+ sourceFile?: string;
38
+ factoryOnlyPrefixes?: Set<string>;
39
+ }
40
+
41
+ export interface DiscoveryState {
42
+ resolvedEntryPath: string | undefined;
43
+ projectRoot: string;
44
+ isBuildMode: boolean;
45
+ userResolveAlias: any;
46
+ scanFilter: ScanFilter | undefined;
47
+ cachedRouterFiles: string[] | undefined;
48
+ opts: PluginOptions | undefined;
49
+
50
+ mergedRouteManifest: Record<string, string> | null;
51
+ perRouterManifests: PerRouterManifestEntry[];
52
+ mergedPrecomputedEntries: PrecomputedEntry[] | null;
53
+ mergedRouteTrie: any;
54
+
55
+ perRouterTrieMap: Map<string, any>;
56
+ perRouterPrecomputedMap: Map<string, PrecomputedEntry[]>;
57
+ perRouterManifestDataMap: Map<string, Record<string, string>>;
58
+
59
+ prerenderManifestEntries: Record<string, string> | null;
60
+ staticManifestEntries: Record<string, string> | null;
61
+ handlerChunkInfo: ChunkInfo | null;
62
+ staticHandlerChunkInfo: ChunkInfo | null;
63
+ rscEntryFileName: string | null;
64
+ resolvedPrerenderModules: Map<string, string[]> | undefined;
65
+ resolvedStaticModules: Map<string, string[]> | undefined;
66
+
67
+ discoveryDone: Promise<void> | null;
68
+ devServerOrigin: string | null;
69
+ devServer: any;
70
+ selfWrittenGenFiles: Map<string, { at: number; hash: string }>;
71
+ SELF_WRITE_WINDOW_MS: number;
72
+ }
73
+
74
+ export function createDiscoveryState(
75
+ entryPath: string | undefined,
76
+ opts: PluginOptions | undefined,
77
+ ): DiscoveryState {
78
+ return {
79
+ resolvedEntryPath: entryPath,
80
+ projectRoot: "",
81
+ isBuildMode: false,
82
+ userResolveAlias: undefined,
83
+ scanFilter: undefined,
84
+ cachedRouterFiles: undefined,
85
+ opts,
86
+
87
+ mergedRouteManifest: null,
88
+ perRouterManifests: [],
89
+ mergedPrecomputedEntries: null,
90
+ mergedRouteTrie: null,
91
+
92
+ perRouterTrieMap: new Map(),
93
+ perRouterPrecomputedMap: new Map(),
94
+ perRouterManifestDataMap: new Map(),
95
+
96
+ prerenderManifestEntries: null,
97
+ staticManifestEntries: null,
98
+ handlerChunkInfo: null,
99
+ staticHandlerChunkInfo: null,
100
+ rscEntryFileName: null,
101
+ resolvedPrerenderModules: undefined,
102
+ resolvedStaticModules: undefined,
103
+
104
+ discoveryDone: null,
105
+ devServerOrigin: null,
106
+ devServer: null,
107
+ selfWrittenGenFiles: new Map(),
108
+ SELF_WRITE_WINDOW_MS: 5_000,
109
+ };
110
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Virtual Module Code Generation
3
+ *
4
+ * Generates the code for virtual:rsc-router/routes-manifest and
5
+ * per-router virtual modules used by the load() hook.
6
+ */
7
+
8
+ import { dirname, basename, join } from "node:path";
9
+ import { jsonParseExpression } from "../utils/manifest-utils.js";
10
+ import { VIRTUAL_ROUTES_MANIFEST_ID } from "./state.js";
11
+ import type { DiscoveryState } from "./state.js";
12
+
13
+ /**
14
+ * Generate the code for the main virtual:rsc-router/routes-manifest module.
15
+ */
16
+ export function generateRoutesManifestModule(state: DiscoveryState): string {
17
+ const hasManifest =
18
+ state.mergedRouteManifest &&
19
+ Object.keys(state.mergedRouteManifest).length > 0;
20
+
21
+ if (hasManifest) {
22
+ // Build gen file import statements for each router with a sourceFile.
23
+ // This creates a dependency in Vite's module graph: when the gen file
24
+ // changes (e.g. after HMR route edits), Vite invalidates this virtual
25
+ // module and re-evaluates it on the next request, calling
26
+ // setCachedManifest() with fresh data. No manual sync needed.
27
+ const genFileImports: string[] = [];
28
+ const genFileVars: string[] = [];
29
+ const routersWithoutGenFile: Array<{
30
+ id: string;
31
+ manifest: Record<string, string>;
32
+ }> = [];
33
+ let varIdx = 0;
34
+
35
+ for (const entry of state.perRouterManifests) {
36
+ if (entry.sourceFile) {
37
+ const routerDir = dirname(entry.sourceFile);
38
+ const routerBasename = basename(entry.sourceFile).replace(
39
+ /\.(tsx?|jsx?)$/,
40
+ "",
41
+ );
42
+ const genPath = join(
43
+ routerDir,
44
+ `${routerBasename}.named-routes.gen.js`,
45
+ ).replaceAll("\\", "/");
46
+ const varName = `_r${varIdx++}`;
47
+ genFileImports.push(
48
+ `import { NamedRoutes as ${varName} } from ${JSON.stringify(genPath)};`,
49
+ );
50
+ genFileVars.push(varName);
51
+ } else {
52
+ // Routers without sourceFile: inline their manifest data directly
53
+ routersWithoutGenFile.push({
54
+ id: entry.id,
55
+ manifest: entry.routeManifest,
56
+ });
57
+ }
58
+ }
59
+
60
+ const lines = [
61
+ `import { setCachedManifest, setPrecomputedEntries, setRouteTrie, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
62
+ ...genFileImports,
63
+ // Clear stale per-router cached data (manifest, trie, precomputed entries)
64
+ // before re-populating. In Cloudflare dev mode, program reloads re-evaluate
65
+ // this virtual module but the route-map-builder singleton retains old data
66
+ // because it's not in the HMR invalidation chain. Without this clear, the
67
+ // handler finds stale trie data and never rebuilds from updated urlpatterns.
68
+ `clearAllRouterData();`,
69
+ ];
70
+
71
+ // Flatten NamedRoutes entries: search schema objects -> plain string paths
72
+ if (genFileVars.length > 0) {
73
+ lines.push(
74
+ `function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
75
+ );
76
+ }
77
+
78
+ // Build the merged manifest from gen file imports + inlined data
79
+ if (genFileVars.length === 1 && routersWithoutGenFile.length === 0) {
80
+ lines.push(`setCachedManifest(__flat(${genFileVars[0]}));`);
81
+ } else {
82
+ const parts: string[] = [];
83
+ for (const v of genFileVars) parts.push(`...__flat(${v})`);
84
+ for (const { manifest } of routersWithoutGenFile)
85
+ parts.push(`...${jsonParseExpression(manifest)}`);
86
+ lines.push(`setCachedManifest({ ${parts.join(", ")} });`);
87
+ }
88
+
89
+ // Set per-router manifests
90
+ let genVarIdx = 0;
91
+ for (const entry of state.perRouterManifests) {
92
+ if (entry.sourceFile) {
93
+ const varName = genFileVars[genVarIdx++];
94
+ lines.push(
95
+ `setRouterManifest(${JSON.stringify(entry.id)}, __flat(${varName}));`,
96
+ );
97
+ } else {
98
+ lines.push(
99
+ `setRouterManifest(${JSON.stringify(entry.id)}, ${jsonParseExpression(entry.routeManifest)});`,
100
+ );
101
+ }
102
+ }
103
+
104
+ // In dev mode, skip trie and precomputed entries injection. These are
105
+ // computed once during initial discovery and become stale after route
106
+ // changes. A stale trie would incorrectly match removed routes. The
107
+ // handler falls back to Phase 2 regex matching against the live
108
+ // router.urlpatterns, which is always correct after a program reload.
109
+ // In build mode, the trie is always fresh (built from the final route
110
+ // tree) so it's safe to inject.
111
+ if (state.isBuildMode) {
112
+ if (
113
+ state.mergedPrecomputedEntries &&
114
+ state.mergedPrecomputedEntries.length > 0
115
+ ) {
116
+ lines.push(
117
+ `setPrecomputedEntries(${jsonParseExpression(state.mergedPrecomputedEntries)});`,
118
+ );
119
+ }
120
+ if (state.mergedRouteTrie) {
121
+ lines.push(
122
+ `setRouteTrie(${jsonParseExpression(state.mergedRouteTrie)});`,
123
+ );
124
+ }
125
+ }
126
+
127
+ // Register lazy loaders for per-router manifest modules.
128
+ // Each import() uses a static string literal so Rollup creates separate chunks.
129
+ for (const routerId of state.perRouterManifestDataMap.keys()) {
130
+ lines.push(
131
+ `registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`,
132
+ );
133
+ }
134
+ if (!state.isBuildMode && state.devServerOrigin) {
135
+ lines.push(
136
+ `globalThis.__PRERENDER_DEV_URL = ${JSON.stringify(state.devServerOrigin)};`,
137
+ );
138
+ }
139
+ return lines.join("\n");
140
+ }
141
+
142
+ // No manifest: either discovery hasn't completed or no runner (Cloudflare dev).
143
+ // Still inject __PRERENDER_DEV_URL so the prerender store can fetch on-demand.
144
+ // Re-resolve origin now since the server is listening by module load time.
145
+ if (!state.isBuildMode) {
146
+ const origin =
147
+ state.devServerOrigin ||
148
+ state.devServer?.resolvedUrls?.local?.[0]?.replace(/\/$/, "") ||
149
+ (state.devServer &&
150
+ `http://localhost:${state.devServer.config.server.port || 5173}`);
151
+ if (origin) {
152
+ state.devServerOrigin = origin;
153
+ return `globalThis.__PRERENDER_DEV_URL = ${JSON.stringify(origin)};`;
154
+ }
155
+ }
156
+ return `// Route manifest will be populated at runtime`;
157
+ }
158
+
159
+ /**
160
+ * Generate the code for a per-router virtual module.
161
+ */
162
+ export function generatePerRouterModule(
163
+ state: DiscoveryState,
164
+ routerId: string,
165
+ ): string {
166
+ // Find the per-router entry to get the gen file path
167
+ const routerEntry = state.perRouterManifests.find((e) => e.id === routerId);
168
+ const trie = state.perRouterTrieMap.get(routerId);
169
+ const entries = state.perRouterPrecomputedMap.get(routerId);
170
+ const lines: string[] = [];
171
+
172
+ if (routerEntry?.sourceFile) {
173
+ // Import manifest from the gen file so HMR auto-propagates
174
+ const routerDir = dirname(routerEntry.sourceFile);
175
+ const routerBasename = basename(routerEntry.sourceFile).replace(
176
+ /\.(tsx?|jsx?)$/,
177
+ "",
178
+ );
179
+ const genPath = join(
180
+ routerDir,
181
+ `${routerBasename}.named-routes.gen.js`,
182
+ ).replaceAll("\\", "/");
183
+ lines.push(`import { NamedRoutes as _r } from ${JSON.stringify(genPath)};`);
184
+ lines.push(
185
+ `function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
186
+ );
187
+ lines.push(`export const manifest = __flat(_r);`);
188
+ } else {
189
+ const manifest = state.perRouterManifestDataMap.get(routerId);
190
+ if (manifest) {
191
+ lines.push(`export const manifest = ${jsonParseExpression(manifest)};`);
192
+ }
193
+ }
194
+ if (trie) {
195
+ lines.push(`export const trie = ${jsonParseExpression(trie)};`);
196
+ }
197
+ if (entries && entries.length > 0) {
198
+ lines.push(
199
+ `export const precomputedEntries = ${jsonParseExpression(entries)};`,
200
+ );
201
+ }
202
+ return lines.join("\n") || "// empty router manifest";
203
+ }