@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100

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 (329) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +1037 -4
  3. package/dist/bin/rango.js +1619 -157
  4. package/dist/vite/index.js +5762 -2301
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +71 -63
  7. package/skills/breadcrumbs/SKILL.md +252 -0
  8. package/skills/cache-guide/SKILL.md +294 -0
  9. package/skills/caching/SKILL.md +93 -23
  10. package/skills/composability/SKILL.md +172 -0
  11. package/skills/debug-manifest/SKILL.md +12 -8
  12. package/skills/document-cache/SKILL.md +18 -16
  13. package/skills/fonts/SKILL.md +6 -4
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +367 -71
  16. package/skills/host-router/SKILL.md +218 -0
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +176 -8
  19. package/skills/layout/SKILL.md +124 -3
  20. package/skills/links/SKILL.md +304 -25
  21. package/skills/loader/SKILL.md +474 -47
  22. package/skills/middleware/SKILL.md +207 -37
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +15 -11
  26. package/skills/parallel/SKILL.md +272 -1
  27. package/skills/prerender/SKILL.md +467 -65
  28. package/skills/rango/SKILL.md +89 -21
  29. package/skills/response-routes/SKILL.md +152 -91
  30. package/skills/route/SKILL.md +305 -14
  31. package/skills/router-setup/SKILL.md +210 -32
  32. package/skills/server-actions/SKILL.md +739 -0
  33. package/skills/streams-and-websockets/SKILL.md +283 -0
  34. package/skills/theme/SKILL.md +9 -8
  35. package/skills/typesafety/SKILL.md +333 -86
  36. package/skills/use-cache/SKILL.md +324 -0
  37. package/skills/view-transitions/SKILL.md +212 -0
  38. package/src/__internal.ts +102 -4
  39. package/src/bin/rango.ts +312 -15
  40. package/src/browser/action-coordinator.ts +97 -0
  41. package/src/browser/action-response-classifier.ts +99 -0
  42. package/src/browser/app-shell.ts +52 -0
  43. package/src/browser/app-version.ts +14 -0
  44. package/src/browser/event-controller.ts +136 -68
  45. package/src/browser/history-state.ts +80 -0
  46. package/src/browser/intercept-utils.ts +52 -0
  47. package/src/browser/link-interceptor.ts +24 -4
  48. package/src/browser/logging.ts +55 -0
  49. package/src/browser/merge-segment-loaders.ts +20 -12
  50. package/src/browser/navigation-bridge.ts +374 -561
  51. package/src/browser/navigation-client.ts +228 -70
  52. package/src/browser/navigation-store.ts +97 -55
  53. package/src/browser/navigation-transaction.ts +297 -0
  54. package/src/browser/network-error-handler.ts +61 -0
  55. package/src/browser/partial-update.ts +376 -315
  56. package/src/browser/prefetch/cache.ts +314 -0
  57. package/src/browser/prefetch/fetch.ts +282 -0
  58. package/src/browser/prefetch/observer.ts +65 -0
  59. package/src/browser/prefetch/policy.ts +48 -0
  60. package/src/browser/prefetch/queue.ts +191 -0
  61. package/src/browser/prefetch/resource-ready.ts +77 -0
  62. package/src/browser/rango-state.ts +152 -0
  63. package/src/browser/react/Link.tsx +255 -71
  64. package/src/browser/react/NavigationProvider.tsx +152 -24
  65. package/src/browser/react/context.ts +11 -0
  66. package/src/browser/react/filter-segment-order.ts +55 -0
  67. package/src/browser/react/index.ts +15 -12
  68. package/src/browser/react/location-state-shared.ts +95 -53
  69. package/src/browser/react/location-state.ts +60 -15
  70. package/src/browser/react/mount-context.ts +6 -1
  71. package/src/browser/react/nonce-context.ts +23 -0
  72. package/src/browser/react/shallow-equal.ts +27 -0
  73. package/src/browser/react/use-action.ts +29 -51
  74. package/src/browser/react/use-client-cache.ts +5 -3
  75. package/src/browser/react/use-handle.ts +30 -120
  76. package/src/browser/react/use-link-status.ts +6 -5
  77. package/src/browser/react/use-navigation.ts +44 -65
  78. package/src/browser/react/use-params.ts +78 -0
  79. package/src/browser/react/use-pathname.ts +47 -0
  80. package/src/browser/react/use-reverse.ts +99 -0
  81. package/src/browser/react/use-router.ts +83 -0
  82. package/src/browser/react/use-search-params.ts +56 -0
  83. package/src/browser/react/use-segments.ts +85 -99
  84. package/src/browser/response-adapter.ts +73 -0
  85. package/src/browser/rsc-router.tsx +246 -64
  86. package/src/browser/scroll-restoration.ts +127 -52
  87. package/src/browser/segment-reconciler.ts +243 -0
  88. package/src/browser/segment-structure-assert.ts +16 -0
  89. package/src/browser/server-action-bridge.ts +510 -603
  90. package/src/browser/shallow.ts +6 -1
  91. package/src/browser/types.ts +158 -48
  92. package/src/browser/validate-redirect-origin.ts +29 -0
  93. package/src/build/generate-manifest.ts +84 -23
  94. package/src/build/generate-route-types.ts +39 -828
  95. package/src/build/index.ts +4 -5
  96. package/src/build/route-trie.ts +85 -32
  97. package/src/build/route-types/ast-helpers.ts +25 -0
  98. package/src/build/route-types/ast-route-extraction.ts +98 -0
  99. package/src/build/route-types/codegen.ts +102 -0
  100. package/src/build/route-types/include-resolution.ts +418 -0
  101. package/src/build/route-types/param-extraction.ts +48 -0
  102. package/src/build/route-types/per-module-writer.ts +128 -0
  103. package/src/build/route-types/router-processing.ts +618 -0
  104. package/src/build/route-types/scan-filter.ts +85 -0
  105. package/src/build/runtime-discovery.ts +231 -0
  106. package/src/cache/background-task.ts +34 -0
  107. package/src/cache/cache-key-utils.ts +44 -0
  108. package/src/cache/cache-policy.ts +125 -0
  109. package/src/cache/cache-runtime.ts +342 -0
  110. package/src/cache/cache-scope.ts +167 -307
  111. package/src/cache/cf/cf-cache-store.ts +573 -21
  112. package/src/cache/cf/index.ts +13 -3
  113. package/src/cache/document-cache.ts +116 -77
  114. package/src/cache/handle-capture.ts +81 -0
  115. package/src/cache/handle-snapshot.ts +41 -0
  116. package/src/cache/index.ts +1 -15
  117. package/src/cache/memory-segment-store.ts +191 -13
  118. package/src/cache/profile-registry.ts +73 -0
  119. package/src/cache/read-through-swr.ts +134 -0
  120. package/src/cache/segment-codec.ts +256 -0
  121. package/src/cache/taint.ts +153 -0
  122. package/src/cache/types.ts +72 -122
  123. package/src/client.rsc.tsx +6 -1
  124. package/src/client.tsx +118 -302
  125. package/src/component-utils.ts +4 -4
  126. package/src/components/DefaultDocument.tsx +5 -1
  127. package/src/context-var.ts +156 -0
  128. package/src/debug.ts +19 -9
  129. package/src/errors.ts +77 -7
  130. package/src/handle.ts +55 -10
  131. package/src/handles/MetaTags.tsx +73 -20
  132. package/src/handles/breadcrumbs.ts +66 -0
  133. package/src/handles/index.ts +1 -0
  134. package/src/handles/meta.ts +30 -13
  135. package/src/host/cookie-handler.ts +21 -15
  136. package/src/host/errors.ts +8 -8
  137. package/src/host/index.ts +4 -7
  138. package/src/host/pattern-matcher.ts +27 -27
  139. package/src/host/router.ts +61 -39
  140. package/src/host/testing.ts +8 -8
  141. package/src/host/types.ts +15 -7
  142. package/src/host/utils.ts +1 -1
  143. package/src/href-client.ts +65 -45
  144. package/src/index.rsc.ts +138 -21
  145. package/src/index.ts +206 -51
  146. package/src/internal-debug.ts +11 -0
  147. package/src/loader.rsc.ts +25 -143
  148. package/src/loader.ts +27 -10
  149. package/src/network-error-thrower.tsx +3 -1
  150. package/src/outlet-context.ts +1 -1
  151. package/src/outlet-provider.tsx +45 -0
  152. package/src/prerender/param-hash.ts +4 -2
  153. package/src/prerender/store.ts +159 -13
  154. package/src/prerender.ts +397 -29
  155. package/src/response-utils.ts +28 -0
  156. package/src/reverse.ts +231 -121
  157. package/src/root-error-boundary.tsx +41 -29
  158. package/src/route-content-wrapper.tsx +7 -4
  159. package/src/route-definition/dsl-helpers.ts +1134 -0
  160. package/src/route-definition/helper-factories.ts +200 -0
  161. package/src/route-definition/helpers-types.ts +483 -0
  162. package/src/route-definition/index.ts +55 -0
  163. package/src/route-definition/redirect.ts +101 -0
  164. package/src/route-definition/resolve-handler-use.ts +155 -0
  165. package/src/route-definition.ts +1 -1431
  166. package/src/route-map-builder.ts +162 -123
  167. package/src/route-name.ts +53 -0
  168. package/src/route-types.ts +66 -9
  169. package/src/router/content-negotiation.ts +215 -0
  170. package/src/router/debug-manifest.ts +72 -0
  171. package/src/router/error-handling.ts +9 -9
  172. package/src/router/find-match.ts +160 -0
  173. package/src/router/handler-context.ts +418 -86
  174. package/src/router/intercept-resolution.ts +35 -20
  175. package/src/router/lazy-includes.ts +237 -0
  176. package/src/router/loader-resolution.ts +359 -128
  177. package/src/router/logging.ts +251 -0
  178. package/src/router/manifest.ts +98 -32
  179. package/src/router/match-api.ts +196 -261
  180. package/src/router/match-context.ts +4 -2
  181. package/src/router/match-handlers.ts +441 -0
  182. package/src/router/match-middleware/background-revalidation.ts +108 -93
  183. package/src/router/match-middleware/cache-lookup.ts +415 -86
  184. package/src/router/match-middleware/cache-store.ts +91 -29
  185. package/src/router/match-middleware/intercept-resolution.ts +48 -21
  186. package/src/router/match-middleware/segment-resolution.ts +73 -9
  187. package/src/router/match-pipelines.ts +10 -45
  188. package/src/router/match-result.ts +154 -35
  189. package/src/router/metrics.ts +240 -15
  190. package/src/router/middleware-cookies.ts +55 -0
  191. package/src/router/middleware-types.ts +209 -0
  192. package/src/router/middleware.ts +373 -371
  193. package/src/router/navigation-snapshot.ts +182 -0
  194. package/src/router/pattern-matching.ts +292 -52
  195. package/src/router/prerender-match.ts +502 -0
  196. package/src/router/preview-match.ts +98 -0
  197. package/src/router/request-classification.ts +310 -0
  198. package/src/router/revalidation.ts +152 -39
  199. package/src/router/route-snapshot.ts +245 -0
  200. package/src/router/router-context.ts +41 -21
  201. package/src/router/router-interfaces.ts +484 -0
  202. package/src/router/router-options.ts +618 -0
  203. package/src/router/router-registry.ts +24 -0
  204. package/src/router/segment-resolution/fresh.ts +756 -0
  205. package/src/router/segment-resolution/helpers.ts +268 -0
  206. package/src/router/segment-resolution/loader-cache.ts +199 -0
  207. package/src/router/segment-resolution/revalidation.ts +1407 -0
  208. package/src/router/segment-resolution/static-store.ts +67 -0
  209. package/src/router/segment-resolution.ts +21 -1315
  210. package/src/router/segment-wrappers.ts +291 -0
  211. package/src/router/substitute-pattern-params.ts +56 -0
  212. package/src/router/telemetry-otel.ts +299 -0
  213. package/src/router/telemetry.ts +300 -0
  214. package/src/router/timeout.ts +148 -0
  215. package/src/router/trie-matching.ts +111 -39
  216. package/src/router/types.ts +17 -9
  217. package/src/router/url-params.ts +49 -0
  218. package/src/router.ts +642 -2011
  219. package/src/rsc/handler-context.ts +45 -0
  220. package/src/rsc/handler.ts +864 -1114
  221. package/src/rsc/helpers.ts +181 -19
  222. package/src/rsc/index.ts +0 -20
  223. package/src/rsc/loader-fetch.ts +229 -0
  224. package/src/rsc/manifest-init.ts +90 -0
  225. package/src/rsc/nonce.ts +14 -0
  226. package/src/rsc/origin-guard.ts +141 -0
  227. package/src/rsc/progressive-enhancement.ts +395 -0
  228. package/src/rsc/response-error.ts +37 -0
  229. package/src/rsc/response-route-handler.ts +360 -0
  230. package/src/rsc/rsc-rendering.ts +256 -0
  231. package/src/rsc/runtime-warnings.ts +42 -0
  232. package/src/rsc/server-action.ts +360 -0
  233. package/src/rsc/ssr-setup.ts +128 -0
  234. package/src/rsc/types.ts +52 -11
  235. package/src/search-params.ts +230 -0
  236. package/src/segment-content-promise.ts +67 -0
  237. package/src/segment-loader-promise.ts +122 -0
  238. package/src/segment-system.tsx +187 -38
  239. package/src/server/context.ts +333 -59
  240. package/src/server/cookie-store.ts +190 -0
  241. package/src/server/fetchable-loader-store.ts +37 -0
  242. package/src/server/handle-store.ts +113 -15
  243. package/src/server/loader-registry.ts +24 -64
  244. package/src/server/request-context.ts +603 -109
  245. package/src/server.ts +35 -155
  246. package/src/ssr/index.tsx +107 -30
  247. package/src/static-handler.ts +126 -0
  248. package/src/theme/ThemeProvider.tsx +21 -15
  249. package/src/theme/ThemeScript.tsx +5 -5
  250. package/src/theme/constants.ts +5 -2
  251. package/src/theme/index.ts +4 -14
  252. package/src/theme/theme-context.ts +4 -30
  253. package/src/theme/theme-script.ts +21 -18
  254. package/src/types/boundaries.ts +158 -0
  255. package/src/types/cache-types.ts +198 -0
  256. package/src/types/error-types.ts +192 -0
  257. package/src/types/global-namespace.ts +100 -0
  258. package/src/types/handler-context.ts +764 -0
  259. package/src/types/index.ts +88 -0
  260. package/src/types/loader-types.ts +209 -0
  261. package/src/types/request-scope.ts +126 -0
  262. package/src/types/route-config.ts +170 -0
  263. package/src/types/route-entry.ts +120 -0
  264. package/src/types/segments.ts +167 -0
  265. package/src/types.ts +1 -1757
  266. package/src/urls/include-helper.ts +207 -0
  267. package/src/urls/index.ts +53 -0
  268. package/src/urls/path-helper-types.ts +372 -0
  269. package/src/urls/path-helper.ts +364 -0
  270. package/src/urls/pattern-types.ts +107 -0
  271. package/src/urls/response-types.ts +108 -0
  272. package/src/urls/type-extraction.ts +372 -0
  273. package/src/urls/urls-function.ts +98 -0
  274. package/src/urls.ts +1 -1282
  275. package/src/use-loader.tsx +161 -81
  276. package/src/vite/debug.ts +184 -0
  277. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  278. package/src/vite/discovery/discover-routers.ts +376 -0
  279. package/src/vite/discovery/gate-state.ts +171 -0
  280. package/src/vite/discovery/prerender-collection.ts +486 -0
  281. package/src/vite/discovery/route-types-writer.ts +258 -0
  282. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  283. package/src/vite/discovery/state.ts +117 -0
  284. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  285. package/src/vite/index.ts +15 -2063
  286. package/src/vite/plugin-types.ts +103 -0
  287. package/src/vite/plugins/cjs-to-esm.ts +98 -0
  288. package/src/vite/plugins/client-ref-dedup.ts +131 -0
  289. package/src/vite/plugins/client-ref-hashing.ts +117 -0
  290. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  291. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  292. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  293. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
  294. package/src/vite/plugins/expose-id-utils.ts +299 -0
  295. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  296. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  297. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  298. package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
  299. package/src/vite/plugins/expose-ids/types.ts +45 -0
  300. package/src/vite/plugins/expose-internal-ids.ts +816 -0
  301. package/src/vite/plugins/performance-tracks.ts +96 -0
  302. package/src/vite/plugins/refresh-cmd.ts +127 -0
  303. package/src/vite/plugins/use-cache-transform.ts +336 -0
  304. package/src/vite/plugins/version-injector.ts +109 -0
  305. package/src/vite/plugins/version-plugin.ts +266 -0
  306. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  307. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  308. package/src/vite/rango.ts +497 -0
  309. package/src/vite/router-discovery.ts +1423 -0
  310. package/src/vite/utils/ast-handler-extract.ts +517 -0
  311. package/src/vite/utils/banner.ts +36 -0
  312. package/src/vite/utils/bundle-analysis.ts +137 -0
  313. package/src/vite/utils/manifest-utils.ts +70 -0
  314. package/src/vite/utils/package-resolution.ts +161 -0
  315. package/src/vite/utils/prerender-utils.ts +222 -0
  316. package/src/vite/utils/shared-utils.ts +170 -0
  317. package/CLAUDE.md +0 -43
  318. package/src/browser/lru-cache.ts +0 -69
  319. package/src/browser/request-controller.ts +0 -164
  320. package/src/cache/memory-store.ts +0 -253
  321. package/src/href-context.ts +0 -33
  322. package/src/router.gen.ts +0 -6
  323. package/src/urls.gen.ts +0 -8
  324. package/src/vite/expose-handle-id.ts +0 -209
  325. package/src/vite/expose-loader-id.ts +0 -426
  326. package/src/vite/expose-location-state-id.ts +0 -177
  327. package/src/vite/expose-prerender-handler-id.ts +0 -429
  328. package/src/vite/package-resolution.ts +0 -125
  329. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,266 @@
1
+ import { parseAst, type Plugin } from "vite";
2
+ import { VIRTUAL_IDS, getVirtualVersionContent } from "./virtual-entries.js";
3
+
4
+ interface ClientModuleSignature {
5
+ key: string;
6
+ }
7
+
8
+ function isCodeModule(id: string): boolean {
9
+ return /\.(tsx?|jsx?)($|\?)/.test(id);
10
+ }
11
+
12
+ function normalizeModuleId(id: string): string {
13
+ return id.split("?", 1)[0];
14
+ }
15
+
16
+ function getClientModuleSignature(
17
+ source: string,
18
+ ): ClientModuleSignature | undefined {
19
+ let program: any;
20
+ try {
21
+ program = parseAst(source, { jsx: true });
22
+ } catch {
23
+ return undefined;
24
+ }
25
+
26
+ let isUseClient = false;
27
+ for (const node of program.body ?? []) {
28
+ if (
29
+ node?.type === "ExpressionStatement" &&
30
+ node.expression?.type === "Literal" &&
31
+ typeof node.expression.value === "string"
32
+ ) {
33
+ if (node.expression.value === "use client") {
34
+ isUseClient = true;
35
+ }
36
+ continue;
37
+ }
38
+ break;
39
+ }
40
+
41
+ if (!isUseClient) return undefined;
42
+
43
+ const exports = new Set<string>();
44
+ let hasDefault = false;
45
+ let hasExportAll = false;
46
+
47
+ const collectBindingNames = (pattern: any) => {
48
+ if (!pattern) return;
49
+ if (pattern.type === "Identifier") {
50
+ exports.add(pattern.name);
51
+ } else if (pattern.type === "ObjectPattern") {
52
+ for (const prop of pattern.properties ?? []) {
53
+ if (prop?.type === "RestElement") {
54
+ collectBindingNames(prop.argument);
55
+ } else {
56
+ collectBindingNames(prop?.value);
57
+ }
58
+ }
59
+ } else if (pattern.type === "ArrayPattern") {
60
+ for (const el of pattern.elements ?? []) {
61
+ if (el?.type === "RestElement") {
62
+ collectBindingNames(el.argument);
63
+ } else {
64
+ collectBindingNames(el);
65
+ }
66
+ }
67
+ }
68
+ };
69
+
70
+ const collectDeclarationNames = (declaration: any) => {
71
+ if (!declaration) return;
72
+ if (declaration.type === "VariableDeclaration") {
73
+ for (const decl of declaration.declarations ?? []) {
74
+ collectBindingNames(decl?.id);
75
+ }
76
+ return;
77
+ }
78
+ collectBindingNames(declaration.id);
79
+ };
80
+
81
+ for (const node of program.body ?? []) {
82
+ if (node?.type === "ExportDefaultDeclaration") {
83
+ hasDefault = true;
84
+ continue;
85
+ }
86
+ if (node?.type === "ExportAllDeclaration") {
87
+ hasExportAll = true;
88
+ continue;
89
+ }
90
+ if (node?.type !== "ExportNamedDeclaration") continue;
91
+
92
+ collectDeclarationNames(node.declaration);
93
+
94
+ for (const specifier of node.specifiers ?? []) {
95
+ const exportedName =
96
+ specifier?.exported?.name ?? specifier?.exported?.value;
97
+ if (exportedName === "default") {
98
+ hasDefault = true;
99
+ } else if (typeof exportedName === "string") {
100
+ exports.add(exportedName);
101
+ }
102
+ }
103
+ }
104
+
105
+ return {
106
+ key: JSON.stringify({
107
+ default: hasDefault,
108
+ exportAll: hasExportAll,
109
+ exports: [...exports].sort(),
110
+ }),
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Plugin providing rsc-router:version virtual module.
116
+ * Exports VERSION that changes when RSC modules change (dev) or at build time (production).
117
+ *
118
+ * The version is used for:
119
+ * 1. Cache invalidation - CFCacheStore uses VERSION to invalidate stale cache
120
+ * 2. Version mismatch detection - client sends version, server reloads on mismatch
121
+ *
122
+ * In dev mode, the version updates when:
123
+ * - Server starts (initial version)
124
+ * - RSC modules change via HMR (triggers version module invalidation)
125
+ *
126
+ * Client-only HMR changes don't update the version since they don't affect
127
+ * server-rendered content or cached RSC payloads.
128
+ * @internal
129
+ */
130
+ export function createVersionPlugin(): Plugin {
131
+ // Generate version at plugin creation time (build/server start)
132
+ const buildVersion = Date.now().toString(16);
133
+ let currentVersion = buildVersion;
134
+ let isDev = false;
135
+ let server: any = null;
136
+ const clientModuleSignatures = new Map<string, ClientModuleSignature>();
137
+
138
+ let versionCounter = 0;
139
+ const bumpVersion = (reason: string) => {
140
+ // Use timestamp + counter to guarantee uniqueness even when multiple
141
+ // bumps happen within the same millisecond (e.g. cascading HMR events).
142
+ currentVersion = Date.now().toString(16) + String(++versionCounter);
143
+ console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
144
+
145
+ const rscEnv = server?.environments?.rsc;
146
+ const versionMod = rscEnv?.moduleGraph?.getModuleById(
147
+ "\0" + VIRTUAL_IDS.version,
148
+ );
149
+ if (versionMod) {
150
+ rscEnv.moduleGraph.invalidateModule(versionMod);
151
+ }
152
+ };
153
+
154
+ return {
155
+ name: "@rangojs/router:version",
156
+ enforce: "pre",
157
+
158
+ configResolved(config) {
159
+ isDev = config.command === "serve";
160
+ },
161
+
162
+ configureServer(devServer) {
163
+ server = devServer;
164
+
165
+ devServer.watcher.on("unlink", (filePath) => {
166
+ if (!isDev) return;
167
+ if (!clientModuleSignatures.has(filePath)) return;
168
+ clientModuleSignatures.delete(filePath);
169
+ bumpVersion("Client module removed");
170
+ });
171
+ },
172
+
173
+ resolveId(id) {
174
+ if (id === VIRTUAL_IDS.version) {
175
+ return "\0" + id;
176
+ }
177
+ return null;
178
+ },
179
+
180
+ load(id) {
181
+ if (id === "\0" + VIRTUAL_IDS.version) {
182
+ return getVirtualVersionContent(currentVersion);
183
+ }
184
+ return null;
185
+ },
186
+
187
+ transform(code, id) {
188
+ if (!isDev || !isCodeModule(id)) return null;
189
+ const normalizedId = normalizeModuleId(id);
190
+ if (
191
+ !code.includes("use client") &&
192
+ !clientModuleSignatures.has(normalizedId)
193
+ ) {
194
+ return null;
195
+ }
196
+
197
+ const signature = getClientModuleSignature(code);
198
+ if (signature) {
199
+ clientModuleSignatures.set(normalizedId, signature);
200
+ } else {
201
+ clientModuleSignatures.delete(normalizedId);
202
+ }
203
+ return null;
204
+ },
205
+
206
+ // Track RSC module changes and update version
207
+ async hotUpdate(ctx) {
208
+ if (!isDev) return;
209
+
210
+ // Check if this is an RSC environment update (not client/ssr)
211
+ // RSC modules affect server-rendered content and cached payloads
212
+ // In Vite 6, environment is accessed via `this.environment`
213
+ const isRscModule = this.environment?.name === "rsc";
214
+
215
+ if (!isRscModule) return;
216
+
217
+ // Skip re-bumping when the version virtual module itself is invalidated
218
+ // (our own bumpVersion() invalidates it, which re-triggers hotUpdate).
219
+ if (
220
+ ctx.modules.length === 1 &&
221
+ ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
222
+ ) {
223
+ return;
224
+ }
225
+
226
+ if (isCodeModule(ctx.file)) {
227
+ const filePath = normalizeModuleId(ctx.file);
228
+ const previousSignature = clientModuleSignatures.get(filePath);
229
+ try {
230
+ const source = await ctx.read();
231
+ const nextSignature = getClientModuleSignature(source);
232
+ if (nextSignature) {
233
+ // "use client" file — compare export signatures.
234
+ // client-component-hmr may have cleared ctx.modules, so we
235
+ // cannot rely on ctx.modules.length for these files.
236
+ clientModuleSignatures.set(filePath, nextSignature);
237
+ if (
238
+ previousSignature &&
239
+ previousSignature.key === nextSignature.key
240
+ ) {
241
+ return;
242
+ }
243
+ } else {
244
+ clientModuleSignatures.delete(filePath);
245
+ if (!previousSignature) {
246
+ // Not and never was "use client" — use module graph check.
247
+ // ctx.modules is reliable for pure server files (only
248
+ // client-component-hmr clears it for "use client" modules).
249
+ if (ctx.modules.length === 0) return;
250
+ }
251
+ // Was "use client" but directive removed — boundary changed,
252
+ // bump below.
253
+ }
254
+ } catch {
255
+ // Fail open: if we can't read or parse the update, invalidate.
256
+ }
257
+ } else {
258
+ // Non-code file (json, css, etc.) — only bump if it's actually
259
+ // referenced by the RSC module graph.
260
+ if (ctx.modules.length === 0) return;
261
+ }
262
+
263
+ bumpVersion("RSC module changed");
264
+ },
265
+ };
266
+ }
@@ -78,20 +78,29 @@ import "virtual:rsc-router/loader-manifest";
78
78
  // In dev mode, this is a no-op (manifest is populated in-memory by the discovery plugin).
79
79
  import "virtual:rsc-router/routes-manifest";
80
80
 
81
- export default createRSCHandler({
82
- router,
83
- version: VERSION,
84
- deps: {
85
- renderToReadableStream,
86
- decodeReply,
87
- createTemporaryReferenceSet,
88
- loadServerAction,
89
- decodeAction,
90
- decodeFormState,
91
- },
92
- loadSSRModule: () =>
93
- import.meta.viteRsc.loadModule("ssr", "index"),
94
- });
81
+ // Lazily create the handler on first request so that ESM live bindings
82
+ // have resolved by the time we read \`router\`. During HMR the module may
83
+ // re-evaluate before router.tsx finishes, leaving the import undefined.
84
+ let _handler;
85
+ export default function handler(request, env) {
86
+ if (!_handler) {
87
+ _handler = createRSCHandler({
88
+ router,
89
+ version: VERSION,
90
+ deps: {
91
+ renderToReadableStream,
92
+ decodeReply,
93
+ createTemporaryReferenceSet,
94
+ loadServerAction,
95
+ decodeAction,
96
+ decodeFormState,
97
+ },
98
+ loadSSRModule: () =>
99
+ import.meta.viteRsc.loadModule("ssr", "index"),
100
+ });
101
+ }
102
+ return _handler(request, env);
103
+ }
95
104
  `.trim();
96
105
  }
97
106
 
@@ -0,0 +1,29 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ /**
4
+ * Stub plugin that resolves and provides empty exports for virtual modules
5
+ * that the RSC entry may import but aren't needed for route discovery.
6
+ * @internal
7
+ */
8
+ export function createVirtualStubPlugin(): Plugin {
9
+ const STUB_PREFIXES = [
10
+ "virtual:rsc-router/",
11
+ "virtual:entry-",
12
+ "virtual:vite-rsc/",
13
+ ];
14
+ return {
15
+ name: "@rangojs/router:virtual-stubs",
16
+ resolveId(id) {
17
+ if (STUB_PREFIXES.some((p) => id.startsWith(p))) {
18
+ return "\0stub:" + id;
19
+ }
20
+ return null;
21
+ },
22
+ load(id) {
23
+ if (id.startsWith("\0stub:")) {
24
+ return "export default {}";
25
+ }
26
+ return null;
27
+ },
28
+ };
29
+ }