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

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