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

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 (312) 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 +4960 -935
  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/handler-use/SKILL.md +362 -0
  14. package/skills/hooks/SKILL.md +334 -72
  15. package/skills/host-router/SKILL.md +218 -0
  16. package/skills/intercept/SKILL.md +151 -8
  17. package/skills/layout/SKILL.md +122 -3
  18. package/skills/links/SKILL.md +92 -31
  19. package/skills/loader/SKILL.md +404 -44
  20. package/skills/middleware/SKILL.md +205 -37
  21. package/skills/migrate-nextjs/SKILL.md +560 -0
  22. package/skills/migrate-react-router/SKILL.md +764 -0
  23. package/skills/mime-routes/SKILL.md +128 -0
  24. package/skills/parallel/SKILL.md +263 -1
  25. package/skills/prerender/SKILL.md +685 -0
  26. package/skills/rango/SKILL.md +87 -16
  27. package/skills/response-routes/SKILL.md +411 -0
  28. package/skills/route/SKILL.md +281 -14
  29. package/skills/router-setup/SKILL.md +210 -32
  30. package/skills/tailwind/SKILL.md +129 -0
  31. package/skills/theme/SKILL.md +9 -8
  32. package/skills/typesafety/SKILL.md +328 -89
  33. package/skills/use-cache/SKILL.md +324 -0
  34. package/src/__internal.ts +102 -4
  35. package/src/bin/rango.ts +321 -0
  36. package/src/browser/action-coordinator.ts +97 -0
  37. package/src/browser/action-response-classifier.ts +99 -0
  38. package/src/browser/app-version.ts +14 -0
  39. package/src/browser/event-controller.ts +92 -64
  40. package/src/browser/history-state.ts +80 -0
  41. package/src/browser/intercept-utils.ts +52 -0
  42. package/src/browser/link-interceptor.ts +24 -4
  43. package/src/browser/logging.ts +55 -0
  44. package/src/browser/merge-segment-loaders.ts +20 -12
  45. package/src/browser/navigation-bridge.ts +317 -560
  46. package/src/browser/navigation-client.ts +206 -68
  47. package/src/browser/navigation-store.ts +73 -55
  48. package/src/browser/navigation-transaction.ts +297 -0
  49. package/src/browser/network-error-handler.ts +61 -0
  50. package/src/browser/partial-update.ts +343 -316
  51. package/src/browser/prefetch/cache.ts +216 -0
  52. package/src/browser/prefetch/fetch.ts +206 -0
  53. package/src/browser/prefetch/observer.ts +65 -0
  54. package/src/browser/prefetch/policy.ts +48 -0
  55. package/src/browser/prefetch/queue.ts +160 -0
  56. package/src/browser/prefetch/resource-ready.ts +77 -0
  57. package/src/browser/rango-state.ts +112 -0
  58. package/src/browser/react/Link.tsx +253 -74
  59. package/src/browser/react/NavigationProvider.tsx +87 -11
  60. package/src/browser/react/context.ts +11 -0
  61. package/src/browser/react/filter-segment-order.ts +11 -0
  62. package/src/browser/react/index.ts +12 -12
  63. package/src/browser/react/location-state-shared.ts +95 -53
  64. package/src/browser/react/location-state.ts +60 -15
  65. package/src/browser/react/mount-context.ts +6 -1
  66. package/src/browser/react/nonce-context.ts +23 -0
  67. package/src/browser/react/shallow-equal.ts +27 -0
  68. package/src/browser/react/use-action.ts +29 -51
  69. package/src/browser/react/use-client-cache.ts +5 -3
  70. package/src/browser/react/use-handle.ts +30 -126
  71. package/src/browser/react/use-href.tsx +2 -2
  72. package/src/browser/react/use-link-status.ts +6 -5
  73. package/src/browser/react/use-navigation.ts +44 -65
  74. package/src/browser/react/use-params.ts +65 -0
  75. package/src/browser/react/use-pathname.ts +47 -0
  76. package/src/browser/react/use-router.ts +76 -0
  77. package/src/browser/react/use-search-params.ts +56 -0
  78. package/src/browser/react/use-segments.ts +80 -97
  79. package/src/browser/response-adapter.ts +73 -0
  80. package/src/browser/rsc-router.tsx +214 -58
  81. package/src/browser/scroll-restoration.ts +127 -52
  82. package/src/browser/segment-reconciler.ts +243 -0
  83. package/src/browser/segment-structure-assert.ts +16 -0
  84. package/src/browser/server-action-bridge.ts +510 -603
  85. package/src/browser/shallow.ts +6 -1
  86. package/src/browser/types.ts +141 -48
  87. package/src/browser/validate-redirect-origin.ts +29 -0
  88. package/src/build/generate-manifest.ts +235 -24
  89. package/src/build/generate-route-types.ts +39 -0
  90. package/src/build/index.ts +13 -0
  91. package/src/build/route-trie.ts +291 -0
  92. package/src/build/route-types/ast-helpers.ts +25 -0
  93. package/src/build/route-types/ast-route-extraction.ts +98 -0
  94. package/src/build/route-types/codegen.ts +102 -0
  95. package/src/build/route-types/include-resolution.ts +418 -0
  96. package/src/build/route-types/param-extraction.ts +48 -0
  97. package/src/build/route-types/per-module-writer.ts +128 -0
  98. package/src/build/route-types/router-processing.ts +618 -0
  99. package/src/build/route-types/scan-filter.ts +85 -0
  100. package/src/build/runtime-discovery.ts +231 -0
  101. package/src/cache/background-task.ts +34 -0
  102. package/src/cache/cache-key-utils.ts +44 -0
  103. package/src/cache/cache-policy.ts +125 -0
  104. package/src/cache/cache-runtime.ts +342 -0
  105. package/src/cache/cache-scope.ts +167 -309
  106. package/src/cache/cf/cf-cache-store.ts +571 -17
  107. package/src/cache/cf/index.ts +13 -3
  108. package/src/cache/document-cache.ts +116 -77
  109. package/src/cache/handle-capture.ts +81 -0
  110. package/src/cache/handle-snapshot.ts +41 -0
  111. package/src/cache/index.ts +1 -15
  112. package/src/cache/memory-segment-store.ts +191 -13
  113. package/src/cache/profile-registry.ts +73 -0
  114. package/src/cache/read-through-swr.ts +134 -0
  115. package/src/cache/segment-codec.ts +256 -0
  116. package/src/cache/taint.ts +153 -0
  117. package/src/cache/types.ts +72 -122
  118. package/src/client.rsc.tsx +3 -1
  119. package/src/client.tsx +135 -301
  120. package/src/component-utils.ts +4 -4
  121. package/src/components/DefaultDocument.tsx +5 -1
  122. package/src/context-var.ts +156 -0
  123. package/src/debug.ts +19 -9
  124. package/src/errors.ts +108 -2
  125. package/src/handle.ts +55 -29
  126. package/src/handles/MetaTags.tsx +73 -20
  127. package/src/handles/breadcrumbs.ts +66 -0
  128. package/src/handles/index.ts +1 -0
  129. package/src/handles/meta.ts +30 -13
  130. package/src/host/cookie-handler.ts +21 -15
  131. package/src/host/errors.ts +8 -8
  132. package/src/host/index.ts +4 -7
  133. package/src/host/pattern-matcher.ts +27 -27
  134. package/src/host/router.ts +61 -39
  135. package/src/host/testing.ts +8 -8
  136. package/src/host/types.ts +15 -7
  137. package/src/host/utils.ts +1 -1
  138. package/src/href-client.ts +119 -29
  139. package/src/index.rsc.ts +155 -19
  140. package/src/index.ts +251 -30
  141. package/src/internal-debug.ts +11 -0
  142. package/src/loader.rsc.ts +26 -157
  143. package/src/loader.ts +27 -10
  144. package/src/network-error-thrower.tsx +3 -1
  145. package/src/outlet-provider.tsx +45 -0
  146. package/src/prerender/param-hash.ts +37 -0
  147. package/src/prerender/store.ts +186 -0
  148. package/src/prerender.ts +524 -0
  149. package/src/reverse.ts +354 -0
  150. package/src/root-error-boundary.tsx +41 -29
  151. package/src/route-content-wrapper.tsx +7 -4
  152. package/src/route-definition/dsl-helpers.ts +1121 -0
  153. package/src/route-definition/helper-factories.ts +200 -0
  154. package/src/route-definition/helpers-types.ts +478 -0
  155. package/src/route-definition/index.ts +55 -0
  156. package/src/route-definition/redirect.ts +101 -0
  157. package/src/route-definition/resolve-handler-use.ts +149 -0
  158. package/src/route-definition.ts +1 -1428
  159. package/src/route-map-builder.ts +217 -123
  160. package/src/route-name.ts +53 -0
  161. package/src/route-types.ts +77 -8
  162. package/src/router/content-negotiation.ts +215 -0
  163. package/src/router/debug-manifest.ts +72 -0
  164. package/src/router/error-handling.ts +9 -9
  165. package/src/router/find-match.ts +160 -0
  166. package/src/router/handler-context.ts +438 -86
  167. package/src/router/intercept-resolution.ts +402 -0
  168. package/src/router/lazy-includes.ts +237 -0
  169. package/src/router/loader-resolution.ts +356 -128
  170. package/src/router/logging.ts +251 -0
  171. package/src/router/manifest.ts +163 -35
  172. package/src/router/match-api.ts +555 -0
  173. package/src/router/match-context.ts +5 -3
  174. package/src/router/match-handlers.ts +440 -0
  175. package/src/router/match-middleware/background-revalidation.ts +108 -93
  176. package/src/router/match-middleware/cache-lookup.ts +460 -10
  177. package/src/router/match-middleware/cache-store.ts +98 -26
  178. package/src/router/match-middleware/intercept-resolution.ts +57 -17
  179. package/src/router/match-middleware/segment-resolution.ts +80 -6
  180. package/src/router/match-pipelines.ts +10 -45
  181. package/src/router/match-result.ts +135 -35
  182. package/src/router/metrics.ts +240 -15
  183. package/src/router/middleware-cookies.ts +55 -0
  184. package/src/router/middleware-types.ts +220 -0
  185. package/src/router/middleware.ts +324 -369
  186. package/src/router/navigation-snapshot.ts +182 -0
  187. package/src/router/pattern-matching.ts +211 -43
  188. package/src/router/prerender-match.ts +502 -0
  189. package/src/router/preview-match.ts +98 -0
  190. package/src/router/request-classification.ts +310 -0
  191. package/src/router/revalidation.ts +137 -38
  192. package/src/router/route-snapshot.ts +245 -0
  193. package/src/router/router-context.ts +41 -21
  194. package/src/router/router-interfaces.ts +484 -0
  195. package/src/router/router-options.ts +618 -0
  196. package/src/router/router-registry.ts +24 -0
  197. package/src/router/segment-resolution/fresh.ts +748 -0
  198. package/src/router/segment-resolution/helpers.ts +268 -0
  199. package/src/router/segment-resolution/loader-cache.ts +199 -0
  200. package/src/router/segment-resolution/revalidation.ts +1379 -0
  201. package/src/router/segment-resolution/static-store.ts +67 -0
  202. package/src/router/segment-resolution.ts +21 -0
  203. package/src/router/segment-wrappers.ts +291 -0
  204. package/src/router/telemetry-otel.ts +299 -0
  205. package/src/router/telemetry.ts +300 -0
  206. package/src/router/timeout.ts +148 -0
  207. package/src/router/trie-matching.ts +239 -0
  208. package/src/router/types.ts +78 -3
  209. package/src/router.ts +740 -4252
  210. package/src/rsc/handler-context.ts +45 -0
  211. package/src/rsc/handler.ts +907 -797
  212. package/src/rsc/helpers.ts +140 -6
  213. package/src/rsc/index.ts +0 -20
  214. package/src/rsc/loader-fetch.ts +229 -0
  215. package/src/rsc/manifest-init.ts +90 -0
  216. package/src/rsc/nonce.ts +14 -0
  217. package/src/rsc/origin-guard.ts +141 -0
  218. package/src/rsc/progressive-enhancement.ts +391 -0
  219. package/src/rsc/response-error.ts +37 -0
  220. package/src/rsc/response-route-handler.ts +347 -0
  221. package/src/rsc/rsc-rendering.ts +246 -0
  222. package/src/rsc/runtime-warnings.ts +42 -0
  223. package/src/rsc/server-action.ts +356 -0
  224. package/src/rsc/ssr-setup.ts +128 -0
  225. package/src/rsc/types.ts +46 -11
  226. package/src/search-params.ts +230 -0
  227. package/src/segment-content-promise.ts +67 -0
  228. package/src/segment-loader-promise.ts +122 -0
  229. package/src/segment-system.tsx +134 -36
  230. package/src/server/context.ts +341 -61
  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 +113 -15
  234. package/src/server/loader-registry.ts +24 -64
  235. package/src/server/request-context.ts +607 -81
  236. package/src/server.ts +35 -130
  237. package/src/ssr/index.tsx +103 -30
  238. package/src/static-handler.ts +126 -0
  239. package/src/theme/ThemeProvider.tsx +21 -15
  240. package/src/theme/ThemeScript.tsx +5 -5
  241. package/src/theme/constants.ts +5 -2
  242. package/src/theme/index.ts +4 -14
  243. package/src/theme/theme-context.ts +4 -30
  244. package/src/theme/theme-script.ts +21 -18
  245. package/src/types/boundaries.ts +158 -0
  246. package/src/types/cache-types.ts +198 -0
  247. package/src/types/error-types.ts +192 -0
  248. package/src/types/global-namespace.ts +100 -0
  249. package/src/types/handler-context.ts +791 -0
  250. package/src/types/index.ts +88 -0
  251. package/src/types/loader-types.ts +210 -0
  252. package/src/types/route-config.ts +170 -0
  253. package/src/types/route-entry.ts +120 -0
  254. package/src/types/segments.ts +150 -0
  255. package/src/types.ts +1 -1623
  256. package/src/urls/include-helper.ts +207 -0
  257. package/src/urls/index.ts +53 -0
  258. package/src/urls/path-helper-types.ts +372 -0
  259. package/src/urls/path-helper.ts +364 -0
  260. package/src/urls/pattern-types.ts +107 -0
  261. package/src/urls/response-types.ts +116 -0
  262. package/src/urls/type-extraction.ts +372 -0
  263. package/src/urls/urls-function.ts +98 -0
  264. package/src/urls.ts +1 -802
  265. package/src/use-loader.tsx +161 -81
  266. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  267. package/src/vite/discovery/discover-routers.ts +348 -0
  268. package/src/vite/discovery/prerender-collection.ts +439 -0
  269. package/src/vite/discovery/route-types-writer.ts +258 -0
  270. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  271. package/src/vite/discovery/state.ts +117 -0
  272. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  273. package/src/vite/index.ts +15 -1133
  274. package/src/vite/plugin-types.ts +103 -0
  275. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  276. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  277. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  278. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -53
  279. package/src/vite/plugins/expose-id-utils.ts +299 -0
  280. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  281. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  282. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  283. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  284. package/src/vite/plugins/expose-ids/types.ts +45 -0
  285. package/src/vite/plugins/expose-internal-ids.ts +786 -0
  286. package/src/vite/plugins/performance-tracks.ts +88 -0
  287. package/src/vite/plugins/refresh-cmd.ts +127 -0
  288. package/src/vite/plugins/use-cache-transform.ts +323 -0
  289. package/src/vite/plugins/version-injector.ts +83 -0
  290. package/src/vite/plugins/version-plugin.ts +266 -0
  291. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  292. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  293. package/src/vite/rango.ts +462 -0
  294. package/src/vite/router-discovery.ts +918 -0
  295. package/src/vite/utils/ast-handler-extract.ts +517 -0
  296. package/src/vite/utils/banner.ts +36 -0
  297. package/src/vite/utils/bundle-analysis.ts +137 -0
  298. package/src/vite/utils/manifest-utils.ts +70 -0
  299. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  300. package/src/vite/utils/prerender-utils.ts +221 -0
  301. package/src/vite/utils/shared-utils.ts +170 -0
  302. package/CLAUDE.md +0 -43
  303. package/src/browser/lru-cache.ts +0 -69
  304. package/src/browser/request-controller.ts +0 -164
  305. package/src/cache/memory-store.ts +0 -253
  306. package/src/href-context.ts +0 -33
  307. package/src/href.ts +0 -255
  308. package/src/server/route-manifest-cache.ts +0 -173
  309. package/src/vite/expose-handle-id.ts +0 -209
  310. package/src/vite/expose-loader-id.ts +0 -426
  311. package/src/vite/expose-location-state-id.ts +0 -177
  312. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Self-Generated File Tracking
3
+ *
4
+ * Tracks gen files recently written by the discovery plugin so the
5
+ * file watcher can distinguish self-triggered change events from
6
+ * manual edits.
7
+ */
8
+
9
+ import { createHash } from "node:crypto";
10
+ import { readFileSync } from "node:fs";
11
+ import type { DiscoveryState } from "./state.js";
12
+
13
+ export function markSelfGenWrite(
14
+ state: DiscoveryState,
15
+ filePath: string,
16
+ content: string,
17
+ ): void {
18
+ const hash = createHash("sha256").update(content).digest("hex");
19
+ state.selfWrittenGenFiles.set(filePath, { at: Date.now(), hash });
20
+ }
21
+
22
+ export function consumeSelfGenWrite(
23
+ state: DiscoveryState,
24
+ filePath: string,
25
+ ): boolean {
26
+ const info = state.selfWrittenGenFiles.get(filePath);
27
+ if (!info) return false;
28
+ if (Date.now() - info.at > state.SELF_WRITE_WINDOW_MS) {
29
+ state.selfWrittenGenFiles.delete(filePath);
30
+ return false;
31
+ }
32
+ try {
33
+ const current = readFileSync(filePath, "utf-8");
34
+ const currentHash = createHash("sha256").update(current).digest("hex");
35
+ if (currentHash === info.hash) {
36
+ state.selfWrittenGenFiles.delete(filePath);
37
+ return true;
38
+ }
39
+ // Hash mismatch: file was changed externally. Keep the entry so a
40
+ // subsequent watcher event from our own write can still be consumed
41
+ // (e.g. when multiple Vite servers watch the same directory).
42
+ return false;
43
+ } catch {
44
+ state.selfWrittenGenFiles.delete(filePath);
45
+ return false;
46
+ }
47
+ }
@@ -0,0 +1,117 @@
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
+ // Mutable ref for deferred auto-discovery (node preset).
17
+ // The auto-discover config() hook populates this before configResolved.
18
+ routerPathRef?: { path?: string };
19
+ /** Build-time env option from rango() config. */
20
+ buildEnv?: import("../plugin-types.js").BuildEnvOption;
21
+ /** Deployment preset (needed for buildEnv "auto" resolution). */
22
+ preset?: "node" | "cloudflare";
23
+ }
24
+
25
+ export interface PrecomputedEntry {
26
+ staticPrefix: string;
27
+ routes: Record<string, string>;
28
+ }
29
+
30
+ export interface ChunkInfo {
31
+ fileName: string;
32
+ exports: Array<{ name: string; handlerId: string; passthrough: boolean }>;
33
+ }
34
+
35
+ export interface PerRouterManifestEntry {
36
+ id: string;
37
+ routeManifest: Record<string, string>;
38
+ routeSearchSchemas?: Record<string, Record<string, string>>;
39
+ sourceFile?: string;
40
+ factoryOnlyPrefixes?: Set<string>;
41
+ }
42
+
43
+ export interface DiscoveryState {
44
+ resolvedEntryPath: string | undefined;
45
+ projectRoot: string;
46
+ isBuildMode: boolean;
47
+ userResolveAlias: any;
48
+ scanFilter: ScanFilter | undefined;
49
+ cachedRouterFiles: string[] | undefined;
50
+ opts: PluginOptions | undefined;
51
+
52
+ mergedRouteManifest: Record<string, string> | null;
53
+ perRouterManifests: PerRouterManifestEntry[];
54
+ mergedPrecomputedEntries: PrecomputedEntry[] | null;
55
+ mergedRouteTrie: any;
56
+
57
+ perRouterTrieMap: Map<string, any>;
58
+ perRouterPrecomputedMap: Map<string, PrecomputedEntry[]>;
59
+ perRouterManifestDataMap: Map<string, Record<string, string>>;
60
+
61
+ prerenderManifestEntries: Record<string, string> | null;
62
+ staticManifestEntries: Record<string, string> | null;
63
+ handlerChunkInfoMap: Map<string, ChunkInfo>;
64
+ staticHandlerChunkInfoMap: Map<string, ChunkInfo>;
65
+ rscEntryFileName: string | null;
66
+ resolvedPrerenderModules: Map<string, string[]> | undefined;
67
+ resolvedStaticModules: Map<string, string[]> | undefined;
68
+
69
+ discoveryDone: Promise<void> | null;
70
+ devServerOrigin: string | null;
71
+ devServer: any;
72
+ selfWrittenGenFiles: Map<string, { at: number; hash: string }>;
73
+ SELF_WRITE_WINDOW_MS: number;
74
+
75
+ /** Resolved build-time env bindings (set during buildStart/configureServer). */
76
+ resolvedBuildEnv?: Record<string, unknown>;
77
+ /** Cleanup function for build-time env resources (e.g., miniflare). */
78
+ buildEnvDispose?: (() => Promise<void> | void) | null;
79
+ }
80
+
81
+ export function createDiscoveryState(
82
+ entryPath: string | undefined,
83
+ opts: PluginOptions | undefined,
84
+ ): DiscoveryState {
85
+ return {
86
+ resolvedEntryPath: entryPath,
87
+ projectRoot: "",
88
+ isBuildMode: false,
89
+ userResolveAlias: undefined,
90
+ scanFilter: undefined,
91
+ cachedRouterFiles: undefined,
92
+ opts,
93
+
94
+ mergedRouteManifest: null,
95
+ perRouterManifests: [],
96
+ mergedPrecomputedEntries: null,
97
+ mergedRouteTrie: null,
98
+
99
+ perRouterTrieMap: new Map(),
100
+ perRouterPrecomputedMap: new Map(),
101
+ perRouterManifestDataMap: new Map(),
102
+
103
+ prerenderManifestEntries: null,
104
+ staticManifestEntries: null,
105
+ handlerChunkInfoMap: new Map(),
106
+ staticHandlerChunkInfoMap: new Map(),
107
+ rscEntryFileName: null,
108
+ resolvedPrerenderModules: undefined,
109
+ resolvedStaticModules: undefined,
110
+
111
+ discoveryDone: null,
112
+ devServerOrigin: null,
113
+ devServer: null,
114
+ selfWrittenGenFiles: new Map(),
115
+ SELF_WRITE_WINDOW_MS: 5_000,
116
+ };
117
+ }
@@ -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
+ }