@rangojs/router 0.0.0-experimental.8 → 0.0.0-experimental.8a4d0430

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 (300) hide show
  1. package/AGENTS.md +5 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1601 -0
  4. package/dist/vite/index.js +4474 -867
  5. package/package.json +60 -51
  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 +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 +89 -30
  18. package/skills/loader/SKILL.md +388 -38
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +78 -1
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +85 -16
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +226 -14
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +9 -8
  29. package/skills/typesafety/SKILL.md +318 -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/event-controller.ts +87 -64
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/intercept-utils.ts +52 -0
  38. package/src/browser/link-interceptor.ts +24 -4
  39. package/src/browser/logging.ts +55 -0
  40. package/src/browser/merge-segment-loaders.ts +20 -12
  41. package/src/browser/navigation-bridge.ts +285 -553
  42. package/src/browser/navigation-client.ts +124 -71
  43. package/src/browser/navigation-store.ts +33 -50
  44. package/src/browser/navigation-transaction.ts +295 -0
  45. package/src/browser/network-error-handler.ts +61 -0
  46. package/src/browser/partial-update.ts +258 -308
  47. package/src/browser/prefetch/cache.ts +146 -0
  48. package/src/browser/prefetch/fetch.ts +135 -0
  49. package/src/browser/prefetch/observer.ts +65 -0
  50. package/src/browser/prefetch/policy.ts +42 -0
  51. package/src/browser/prefetch/queue.ts +88 -0
  52. package/src/browser/rango-state.ts +112 -0
  53. package/src/browser/react/Link.tsx +185 -73
  54. package/src/browser/react/NavigationProvider.tsx +51 -11
  55. package/src/browser/react/context.ts +6 -0
  56. package/src/browser/react/filter-segment-order.ts +11 -0
  57. package/src/browser/react/index.ts +12 -12
  58. package/src/browser/react/location-state-shared.ts +95 -53
  59. package/src/browser/react/location-state.ts +60 -15
  60. package/src/browser/react/mount-context.ts +6 -1
  61. package/src/browser/react/nonce-context.ts +23 -0
  62. package/src/browser/react/shallow-equal.ts +27 -0
  63. package/src/browser/react/use-action.ts +29 -51
  64. package/src/browser/react/use-client-cache.ts +5 -3
  65. package/src/browser/react/use-handle.ts +32 -79
  66. package/src/browser/react/use-href.tsx +2 -2
  67. package/src/browser/react/use-link-status.ts +6 -5
  68. package/src/browser/react/use-navigation.ts +22 -63
  69. package/src/browser/react/use-params.ts +65 -0
  70. package/src/browser/react/use-pathname.ts +47 -0
  71. package/src/browser/react/use-router.ts +63 -0
  72. package/src/browser/react/use-search-params.ts +56 -0
  73. package/src/browser/react/use-segments.ts +80 -97
  74. package/src/browser/response-adapter.ts +73 -0
  75. package/src/browser/rsc-router.tsx +107 -26
  76. package/src/browser/scroll-restoration.ts +92 -16
  77. package/src/browser/segment-reconciler.ts +216 -0
  78. package/src/browser/segment-structure-assert.ts +16 -0
  79. package/src/browser/server-action-bridge.ts +504 -599
  80. package/src/browser/shallow.ts +6 -1
  81. package/src/browser/types.ts +109 -47
  82. package/src/browser/validate-redirect-origin.ts +29 -0
  83. package/src/build/generate-manifest.ts +235 -24
  84. package/src/build/generate-route-types.ts +36 -0
  85. package/src/build/index.ts +13 -0
  86. package/src/build/route-trie.ts +265 -0
  87. package/src/build/route-types/ast-helpers.ts +25 -0
  88. package/src/build/route-types/ast-route-extraction.ts +98 -0
  89. package/src/build/route-types/codegen.ts +102 -0
  90. package/src/build/route-types/include-resolution.ts +411 -0
  91. package/src/build/route-types/param-extraction.ts +48 -0
  92. package/src/build/route-types/per-module-writer.ts +128 -0
  93. package/src/build/route-types/router-processing.ts +469 -0
  94. package/src/build/route-types/scan-filter.ts +78 -0
  95. package/src/build/runtime-discovery.ts +231 -0
  96. package/src/cache/background-task.ts +34 -0
  97. package/src/cache/cache-key-utils.ts +44 -0
  98. package/src/cache/cache-policy.ts +125 -0
  99. package/src/cache/cache-runtime.ts +338 -0
  100. package/src/cache/cache-scope.ts +120 -303
  101. package/src/cache/cf/cf-cache-store.ts +119 -7
  102. package/src/cache/cf/index.ts +8 -2
  103. package/src/cache/document-cache.ts +101 -72
  104. package/src/cache/handle-capture.ts +81 -0
  105. package/src/cache/handle-snapshot.ts +41 -0
  106. package/src/cache/index.ts +0 -15
  107. package/src/cache/memory-segment-store.ts +191 -13
  108. package/src/cache/profile-registry.ts +73 -0
  109. package/src/cache/read-through-swr.ts +134 -0
  110. package/src/cache/segment-codec.ts +256 -0
  111. package/src/cache/taint.ts +98 -0
  112. package/src/cache/types.ts +72 -122
  113. package/src/client.rsc.tsx +3 -1
  114. package/src/client.tsx +106 -126
  115. package/src/component-utils.ts +4 -4
  116. package/src/components/DefaultDocument.tsx +5 -1
  117. package/src/context-var.ts +86 -0
  118. package/src/debug.ts +17 -7
  119. package/src/errors.ts +108 -2
  120. package/src/handle.ts +15 -29
  121. package/src/handles/MetaTags.tsx +73 -20
  122. package/src/handles/breadcrumbs.ts +66 -0
  123. package/src/handles/index.ts +1 -0
  124. package/src/handles/meta.ts +30 -13
  125. package/src/host/cookie-handler.ts +21 -15
  126. package/src/host/errors.ts +8 -8
  127. package/src/host/index.ts +4 -7
  128. package/src/host/pattern-matcher.ts +27 -27
  129. package/src/host/router.ts +61 -39
  130. package/src/host/testing.ts +8 -8
  131. package/src/host/types.ts +15 -7
  132. package/src/host/utils.ts +1 -1
  133. package/src/href-client.ts +119 -29
  134. package/src/index.rsc.ts +153 -19
  135. package/src/index.ts +211 -30
  136. package/src/internal-debug.ts +11 -0
  137. package/src/loader.rsc.ts +26 -157
  138. package/src/loader.ts +27 -10
  139. package/src/network-error-thrower.tsx +3 -1
  140. package/src/outlet-provider.tsx +45 -0
  141. package/src/prerender/param-hash.ts +37 -0
  142. package/src/prerender/store.ts +185 -0
  143. package/src/prerender.ts +463 -0
  144. package/src/reverse.ts +330 -0
  145. package/src/root-error-boundary.tsx +41 -29
  146. package/src/route-content-wrapper.tsx +7 -4
  147. package/src/route-definition/dsl-helpers.ts +934 -0
  148. package/src/route-definition/helper-factories.ts +200 -0
  149. package/src/route-definition/helpers-types.ts +430 -0
  150. package/src/route-definition/index.ts +52 -0
  151. package/src/route-definition/redirect.ts +93 -0
  152. package/src/route-definition.ts +1 -1428
  153. package/src/route-map-builder.ts +211 -123
  154. package/src/route-name.ts +53 -0
  155. package/src/route-types.ts +59 -8
  156. package/src/router/content-negotiation.ts +116 -0
  157. package/src/router/debug-manifest.ts +72 -0
  158. package/src/router/error-handling.ts +9 -9
  159. package/src/router/find-match.ts +158 -0
  160. package/src/router/handler-context.ts +374 -81
  161. package/src/router/intercept-resolution.ts +395 -0
  162. package/src/router/lazy-includes.ts +234 -0
  163. package/src/router/loader-resolution.ts +215 -122
  164. package/src/router/logging.ts +248 -0
  165. package/src/router/manifest.ts +148 -35
  166. package/src/router/match-api.ts +620 -0
  167. package/src/router/match-context.ts +5 -3
  168. package/src/router/match-handlers.ts +440 -0
  169. package/src/router/match-middleware/background-revalidation.ts +80 -93
  170. package/src/router/match-middleware/cache-lookup.ts +382 -9
  171. package/src/router/match-middleware/cache-store.ts +51 -22
  172. package/src/router/match-middleware/intercept-resolution.ts +55 -17
  173. package/src/router/match-middleware/segment-resolution.ts +24 -6
  174. package/src/router/match-pipelines.ts +10 -45
  175. package/src/router/match-result.ts +34 -28
  176. package/src/router/metrics.ts +235 -15
  177. package/src/router/middleware-cookies.ts +55 -0
  178. package/src/router/middleware-types.ts +222 -0
  179. package/src/router/middleware.ts +324 -367
  180. package/src/router/pattern-matching.ts +211 -43
  181. package/src/router/prerender-match.ts +402 -0
  182. package/src/router/preview-match.ts +170 -0
  183. package/src/router/revalidation.ts +137 -38
  184. package/src/router/router-context.ts +36 -21
  185. package/src/router/router-interfaces.ts +452 -0
  186. package/src/router/router-options.ts +592 -0
  187. package/src/router/router-registry.ts +24 -0
  188. package/src/router/segment-resolution/fresh.ts +570 -0
  189. package/src/router/segment-resolution/helpers.ts +263 -0
  190. package/src/router/segment-resolution/loader-cache.ts +198 -0
  191. package/src/router/segment-resolution/revalidation.ts +1241 -0
  192. package/src/router/segment-resolution/static-store.ts +67 -0
  193. package/src/router/segment-resolution.ts +21 -0
  194. package/src/router/segment-wrappers.ts +289 -0
  195. package/src/router/telemetry-otel.ts +299 -0
  196. package/src/router/telemetry.ts +300 -0
  197. package/src/router/timeout.ts +148 -0
  198. package/src/router/trie-matching.ts +239 -0
  199. package/src/router/types.ts +77 -3
  200. package/src/router.ts +692 -4257
  201. package/src/rsc/handler-context.ts +45 -0
  202. package/src/rsc/handler.ts +764 -754
  203. package/src/rsc/helpers.ts +140 -6
  204. package/src/rsc/index.ts +0 -20
  205. package/src/rsc/loader-fetch.ts +209 -0
  206. package/src/rsc/manifest-init.ts +86 -0
  207. package/src/rsc/nonce.ts +14 -0
  208. package/src/rsc/origin-guard.ts +141 -0
  209. package/src/rsc/progressive-enhancement.ts +379 -0
  210. package/src/rsc/response-error.ts +37 -0
  211. package/src/rsc/response-route-handler.ts +347 -0
  212. package/src/rsc/rsc-rendering.ts +235 -0
  213. package/src/rsc/runtime-warnings.ts +42 -0
  214. package/src/rsc/server-action.ts +348 -0
  215. package/src/rsc/ssr-setup.ts +128 -0
  216. package/src/rsc/types.ts +38 -11
  217. package/src/search-params.ts +230 -0
  218. package/src/segment-system.tsx +25 -13
  219. package/src/server/context.ts +182 -51
  220. package/src/server/cookie-store.ts +190 -0
  221. package/src/server/fetchable-loader-store.ts +37 -0
  222. package/src/server/handle-store.ts +94 -15
  223. package/src/server/loader-registry.ts +15 -56
  224. package/src/server/request-context.ts +430 -70
  225. package/src/server.ts +35 -130
  226. package/src/ssr/index.tsx +100 -31
  227. package/src/static-handler.ts +114 -0
  228. package/src/theme/ThemeProvider.tsx +21 -15
  229. package/src/theme/ThemeScript.tsx +5 -5
  230. package/src/theme/constants.ts +5 -2
  231. package/src/theme/index.ts +4 -14
  232. package/src/theme/theme-context.ts +4 -30
  233. package/src/theme/theme-script.ts +21 -18
  234. package/src/types/boundaries.ts +158 -0
  235. package/src/types/cache-types.ts +198 -0
  236. package/src/types/error-types.ts +192 -0
  237. package/src/types/global-namespace.ts +100 -0
  238. package/src/types/handler-context.ts +687 -0
  239. package/src/types/index.ts +88 -0
  240. package/src/types/loader-types.ts +183 -0
  241. package/src/types/route-config.ts +170 -0
  242. package/src/types/route-entry.ts +102 -0
  243. package/src/types/segments.ts +148 -0
  244. package/src/types.ts +1 -1623
  245. package/src/urls/include-helper.ts +197 -0
  246. package/src/urls/index.ts +53 -0
  247. package/src/urls/path-helper-types.ts +339 -0
  248. package/src/urls/path-helper.ts +329 -0
  249. package/src/urls/pattern-types.ts +95 -0
  250. package/src/urls/response-types.ts +106 -0
  251. package/src/urls/type-extraction.ts +372 -0
  252. package/src/urls/urls-function.ts +98 -0
  253. package/src/urls.ts +1 -802
  254. package/src/use-loader.tsx +85 -77
  255. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  256. package/src/vite/discovery/discover-routers.ts +344 -0
  257. package/src/vite/discovery/prerender-collection.ts +385 -0
  258. package/src/vite/discovery/route-types-writer.ts +258 -0
  259. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  260. package/src/vite/discovery/state.ts +110 -0
  261. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  262. package/src/vite/index.ts +11 -1133
  263. package/src/vite/plugin-types.ts +131 -0
  264. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  265. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  266. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  267. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
  268. package/src/vite/plugins/expose-id-utils.ts +287 -0
  269. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  270. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  271. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  272. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  273. package/src/vite/plugins/expose-ids/types.ts +45 -0
  274. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  275. package/src/vite/plugins/refresh-cmd.ts +65 -0
  276. package/src/vite/plugins/use-cache-transform.ts +323 -0
  277. package/src/vite/plugins/version-injector.ts +83 -0
  278. package/src/vite/plugins/version-plugin.ts +254 -0
  279. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  280. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  281. package/src/vite/rango.ts +510 -0
  282. package/src/vite/router-discovery.ts +785 -0
  283. package/src/vite/utils/ast-handler-extract.ts +517 -0
  284. package/src/vite/utils/banner.ts +36 -0
  285. package/src/vite/utils/bundle-analysis.ts +137 -0
  286. package/src/vite/utils/manifest-utils.ts +70 -0
  287. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  288. package/src/vite/utils/prerender-utils.ts +189 -0
  289. package/src/vite/utils/shared-utils.ts +169 -0
  290. package/CLAUDE.md +0 -43
  291. package/src/browser/lru-cache.ts +0 -69
  292. package/src/browser/request-controller.ts +0 -164
  293. package/src/cache/memory-store.ts +0 -253
  294. package/src/href-context.ts +0 -33
  295. package/src/href.ts +0 -255
  296. package/src/server/route-manifest-cache.ts +0 -173
  297. package/src/vite/expose-handle-id.ts +0 -209
  298. package/src/vite/expose-loader-id.ts +0 -426
  299. package/src/vite/expose-location-state-id.ts +0 -177
  300. /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
+ }