@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,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
+ }