@rangojs/router 0.0.0-experimental.97 → 0.0.0-experimental.98914650

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 (356) hide show
  1. package/README.md +24 -9
  2. package/dist/bin/rango.js +157 -63
  3. package/dist/testing/vitest.js +82 -0
  4. package/dist/vite/index.js +1584 -639
  5. package/package.json +71 -21
  6. package/skills/api-client/SKILL.md +211 -0
  7. package/skills/breadcrumbs/SKILL.md +60 -0
  8. package/skills/bundle-analysis/SKILL.md +159 -0
  9. package/skills/cache-guide/SKILL.md +222 -30
  10. package/skills/caching/SKILL.md +263 -8
  11. package/skills/composability/SKILL.md +27 -2
  12. package/skills/css/SKILL.md +76 -0
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +3 -1
  15. package/skills/hooks/SKILL.md +235 -28
  16. package/skills/host-router/SKILL.md +122 -22
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +29 -5
  19. package/skills/layout/SKILL.md +13 -9
  20. package/skills/links/SKILL.md +173 -17
  21. package/skills/loader/SKILL.md +170 -23
  22. package/skills/middleware/SKILL.md +16 -10
  23. package/skills/migrate-nextjs/SKILL.md +38 -16
  24. package/skills/mime-routes/SKILL.md +27 -0
  25. package/skills/observability/SKILL.md +137 -0
  26. package/skills/parallel/SKILL.md +11 -7
  27. package/skills/prerender/SKILL.md +14 -33
  28. package/skills/rango/SKILL.md +250 -25
  29. package/skills/react-compiler/SKILL.md +168 -0
  30. package/skills/response-routes/SKILL.md +114 -47
  31. package/skills/route/SKILL.md +42 -5
  32. package/skills/router-setup/SKILL.md +3 -3
  33. package/skills/server-actions/SKILL.md +78 -42
  34. package/skills/tailwind/SKILL.md +27 -3
  35. package/skills/testing/SKILL.md +129 -0
  36. package/skills/testing/bindings.md +89 -0
  37. package/skills/testing/cache-prerender.md +124 -0
  38. package/skills/testing/client-components.md +122 -0
  39. package/skills/testing/e2e-parity.md +125 -0
  40. package/skills/testing/flight.md +92 -0
  41. package/skills/testing/handles.md +129 -0
  42. package/skills/testing/loader.md +128 -0
  43. package/skills/testing/middleware.md +99 -0
  44. package/skills/testing/render-handler.md +121 -0
  45. package/skills/testing/response-routes.md +95 -0
  46. package/skills/testing/reverse-and-types.md +84 -0
  47. package/skills/testing/server-actions.md +107 -0
  48. package/skills/testing/server-tree.md +128 -0
  49. package/skills/testing/setup.md +120 -0
  50. package/skills/typesafety/SKILL.md +316 -26
  51. package/skills/use-cache/SKILL.md +36 -5
  52. package/skills/vercel/SKILL.md +107 -0
  53. package/skills/view-transitions/SKILL.md +294 -0
  54. package/src/__augment-tests__/augment.ts +81 -0
  55. package/src/__augment-tests__/augmented.check.ts +116 -0
  56. package/src/__internal.ts +0 -65
  57. package/src/browser/action-coordinator.ts +53 -36
  58. package/src/browser/action-fence.ts +47 -0
  59. package/src/browser/app-shell.ts +14 -27
  60. package/src/browser/cookie-name.ts +140 -0
  61. package/src/browser/event-controller.ts +37 -143
  62. package/src/browser/history-state.ts +21 -0
  63. package/src/browser/index.ts +3 -3
  64. package/src/browser/invalidate-client-cache.ts +52 -0
  65. package/src/browser/navigation-bridge.ts +30 -59
  66. package/src/browser/navigation-client.ts +96 -84
  67. package/src/browser/navigation-store-handle.ts +38 -0
  68. package/src/browser/navigation-store.ts +32 -82
  69. package/src/browser/navigation-transaction.ts +9 -59
  70. package/src/browser/partial-update.ts +60 -127
  71. package/src/browser/prefetch/cache.ts +82 -72
  72. package/src/browser/prefetch/fetch.ts +108 -33
  73. package/src/browser/prefetch/queue.ts +6 -3
  74. package/src/browser/rango-state.ts +157 -115
  75. package/src/browser/react/Link.tsx +0 -2
  76. package/src/browser/react/NavigationProvider.tsx +41 -48
  77. package/src/browser/react/ScrollRestoration.tsx +10 -6
  78. package/src/browser/react/filter-segment-order.ts +0 -2
  79. package/src/browser/react/index.ts +0 -48
  80. package/src/browser/react/location-state-shared.ts +166 -8
  81. package/src/browser/react/location-state.ts +39 -14
  82. package/src/browser/react/use-action.ts +6 -15
  83. package/src/browser/react/use-handle.ts +17 -14
  84. package/src/browser/react/use-link-status.ts +0 -4
  85. package/src/browser/react/use-navigation.ts +0 -3
  86. package/src/browser/react/use-params.ts +11 -11
  87. package/src/browser/react/use-reverse.ts +106 -0
  88. package/src/browser/react/use-router.ts +20 -5
  89. package/src/browser/react/use-search-params.ts +0 -5
  90. package/src/browser/react/use-segments.ts +0 -13
  91. package/src/browser/response-adapter.ts +52 -1
  92. package/src/browser/rsc-router.tsx +70 -34
  93. package/src/browser/scroll-restoration.ts +22 -14
  94. package/src/browser/segment-structure-assert.ts +2 -2
  95. package/src/browser/server-action-bridge.ts +168 -44
  96. package/src/browser/types.ts +36 -21
  97. package/src/browser/validate-redirect-origin.ts +43 -16
  98. package/src/build/collect-fallback-refs.ts +107 -0
  99. package/src/build/generate-manifest.ts +60 -35
  100. package/src/build/generate-route-types.ts +3 -0
  101. package/src/build/index.ts +8 -2
  102. package/src/build/prefix-tree-utils.ts +123 -0
  103. package/src/build/route-trie.ts +89 -10
  104. package/src/build/route-types/codegen.ts +4 -4
  105. package/src/build/route-types/include-resolution.ts +1 -1
  106. package/src/build/route-types/param-extraction.ts +6 -3
  107. package/src/build/route-types/per-module-writer.ts +7 -4
  108. package/src/build/route-types/router-processing.ts +122 -22
  109. package/src/build/route-types/scan-filter.ts +1 -1
  110. package/src/build/route-types/source-scan.ts +118 -0
  111. package/src/build/runtime-discovery.ts +9 -20
  112. package/src/cache/cache-error.ts +104 -0
  113. package/src/cache/cache-policy.ts +68 -28
  114. package/src/cache/cache-runtime.ts +134 -32
  115. package/src/cache/cache-scope.ts +100 -74
  116. package/src/cache/cache-tag.ts +98 -0
  117. package/src/cache/cf/cf-cache-store.ts +2255 -238
  118. package/src/cache/cf/index.ts +6 -16
  119. package/src/cache/document-cache.ts +61 -20
  120. package/src/cache/handle-snapshot.ts +63 -0
  121. package/src/cache/index.ts +22 -20
  122. package/src/cache/memory-segment-store.ts +136 -37
  123. package/src/cache/profile-registry.ts +6 -30
  124. package/src/cache/read-through-swr.ts +41 -11
  125. package/src/cache/segment-codec.ts +0 -16
  126. package/src/cache/tag-invalidation.ts +230 -0
  127. package/src/cache/types.ts +33 -100
  128. package/src/cache/vercel/index.ts +11 -0
  129. package/src/cache/vercel/vercel-cache-store.ts +799 -0
  130. package/src/client.rsc.tsx +6 -21
  131. package/src/client.tsx +25 -61
  132. package/src/component-utils.ts +19 -0
  133. package/src/context-var.ts +17 -5
  134. package/src/decode-loader-results.ts +36 -0
  135. package/src/defer.ts +196 -0
  136. package/src/deps/ssr.ts +0 -1
  137. package/src/errors.ts +30 -4
  138. package/src/handle.ts +31 -23
  139. package/src/handles/MetaTags.tsx +0 -14
  140. package/src/handles/breadcrumbs.ts +16 -5
  141. package/src/handles/meta.ts +0 -39
  142. package/src/host/cookie-handler.ts +0 -36
  143. package/src/host/errors.ts +0 -24
  144. package/src/host/index.ts +8 -2
  145. package/src/host/pattern-matcher.ts +7 -50
  146. package/src/host/router.ts +107 -99
  147. package/src/host/testing.ts +40 -27
  148. package/src/host/types.ts +37 -4
  149. package/src/host/utils.ts +1 -1
  150. package/src/href-client.ts +137 -22
  151. package/src/index.rsc.ts +63 -9
  152. package/src/index.ts +64 -9
  153. package/src/internal-debug.ts +2 -4
  154. package/src/loader-store.ts +500 -0
  155. package/src/loader.rsc.ts +20 -13
  156. package/src/loader.ts +12 -11
  157. package/src/missing-id-error.ts +68 -0
  158. package/src/network-error-thrower.tsx +1 -6
  159. package/src/outlet-provider.tsx +1 -5
  160. package/src/prerender/param-hash.ts +10 -11
  161. package/src/prerender/store.ts +32 -37
  162. package/src/prerender.ts +61 -6
  163. package/src/redirect-origin.ts +100 -0
  164. package/src/response-utils.ts +9 -0
  165. package/src/reverse.ts +65 -40
  166. package/src/root-error-boundary.tsx +1 -19
  167. package/src/route-content-wrapper.tsx +7 -72
  168. package/src/route-definition/dsl-helpers.ts +244 -281
  169. package/src/route-definition/helper-factories.ts +29 -139
  170. package/src/route-definition/helpers-types.ts +40 -17
  171. package/src/route-definition/redirect.ts +43 -9
  172. package/src/route-definition/resolve-handler-use.ts +6 -0
  173. package/src/route-definition/use-item-types.ts +32 -0
  174. package/src/route-map-builder.ts +0 -16
  175. package/src/route-types.ts +19 -41
  176. package/src/router/basename.ts +14 -0
  177. package/src/router/content-negotiation.ts +15 -15
  178. package/src/router/error-handling.ts +13 -17
  179. package/src/router/find-match.ts +44 -23
  180. package/src/router/handler-context.ts +4 -41
  181. package/src/router/intercept-resolution.ts +14 -19
  182. package/src/router/lazy-includes.ts +9 -46
  183. package/src/router/loader-resolution.ts +91 -46
  184. package/src/router/logging.ts +0 -6
  185. package/src/router/manifest.ts +18 -29
  186. package/src/router/match-api.ts +0 -20
  187. package/src/router/match-context.ts +0 -22
  188. package/src/router/match-handlers.ts +57 -58
  189. package/src/router/match-middleware/background-revalidation.ts +0 -7
  190. package/src/router/match-middleware/cache-lookup.ts +150 -271
  191. package/src/router/match-middleware/cache-store.ts +3 -33
  192. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  193. package/src/router/match-middleware/segment-resolution.ts +0 -22
  194. package/src/router/match-pipelines.ts +1 -42
  195. package/src/router/match-result.ts +31 -80
  196. package/src/router/metrics.ts +0 -34
  197. package/src/router/middleware-types.ts +5 -112
  198. package/src/router/middleware.ts +118 -133
  199. package/src/router/navigation-snapshot.ts +0 -51
  200. package/src/router/params-util.ts +23 -0
  201. package/src/router/pattern-matching.ts +62 -67
  202. package/src/router/prerender-match.ts +99 -63
  203. package/src/router/preview-match.ts +3 -1
  204. package/src/router/request-classification.ts +28 -62
  205. package/src/router/revalidation.ts +50 -56
  206. package/src/router/route-snapshot.ts +0 -1
  207. package/src/router/router-context.ts +0 -27
  208. package/src/router/router-interfaces.ts +68 -35
  209. package/src/router/router-options.ts +55 -1
  210. package/src/router/router-registry.ts +2 -5
  211. package/src/router/segment-resolution/fresh.ts +44 -63
  212. package/src/router/segment-resolution/helpers.ts +34 -0
  213. package/src/router/segment-resolution/loader-cache.ts +40 -37
  214. package/src/router/segment-resolution/revalidation.ts +203 -285
  215. package/src/router/segment-resolution/static-store.ts +19 -5
  216. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  217. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  218. package/src/router/segment-resolution.ts +4 -1
  219. package/src/router/segment-wrappers.ts +0 -3
  220. package/src/router/state-cookie-name.ts +33 -0
  221. package/src/router/substitute-pattern-params.ts +56 -0
  222. package/src/router/telemetry-otel.ts +0 -20
  223. package/src/router/telemetry.ts +96 -19
  224. package/src/router/timeout.ts +0 -20
  225. package/src/router/trie-matching.ts +87 -48
  226. package/src/router/types.ts +9 -63
  227. package/src/router/url-params.ts +0 -5
  228. package/src/router.ts +80 -41
  229. package/src/rsc/handler-context.ts +3 -2
  230. package/src/rsc/handler.ts +83 -78
  231. package/src/rsc/helpers.ts +93 -5
  232. package/src/rsc/index.ts +1 -1
  233. package/src/rsc/json-route-result.ts +38 -0
  234. package/src/rsc/manifest-init.ts +28 -41
  235. package/src/rsc/origin-guard.ts +39 -25
  236. package/src/rsc/progressive-enhancement.ts +12 -1
  237. package/src/rsc/redirect-guard.ts +99 -0
  238. package/src/rsc/response-error.ts +79 -12
  239. package/src/rsc/response-route-handler.ts +76 -62
  240. package/src/rsc/rsc-rendering.ts +41 -60
  241. package/src/rsc/runtime-warnings.ts +23 -10
  242. package/src/rsc/server-action.ts +62 -67
  243. package/src/rsc/ssr-setup.ts +16 -0
  244. package/src/rsc/types.ts +10 -5
  245. package/src/runtime-env.ts +18 -0
  246. package/src/search-params.ts +4 -20
  247. package/src/segment-loader-promise.ts +14 -2
  248. package/src/segment-system.tsx +199 -142
  249. package/src/serialize.ts +243 -0
  250. package/src/server/context.ts +150 -51
  251. package/src/server/cookie-store.ts +80 -5
  252. package/src/server/handle-store.ts +7 -24
  253. package/src/server/loader-registry.ts +5 -24
  254. package/src/server/request-context.ts +165 -87
  255. package/src/ssr/index.tsx +14 -14
  256. package/src/static-handler.ts +10 -13
  257. package/src/testing/cache-status.ts +162 -0
  258. package/src/testing/collect-handle.ts +40 -0
  259. package/src/testing/dispatch.ts +618 -0
  260. package/src/testing/dom.entry.ts +22 -0
  261. package/src/testing/e2e/fixture.ts +188 -0
  262. package/src/testing/e2e/index.ts +128 -0
  263. package/src/testing/e2e/matchers.ts +35 -0
  264. package/src/testing/e2e/page-helpers.ts +272 -0
  265. package/src/testing/e2e/parity.ts +387 -0
  266. package/src/testing/e2e/server.ts +195 -0
  267. package/src/testing/flight-matchers.ts +97 -0
  268. package/src/testing/flight-normalize.ts +11 -0
  269. package/src/testing/flight-runtime.d.ts +57 -0
  270. package/src/testing/flight-tree.ts +682 -0
  271. package/src/testing/flight.entry.ts +52 -0
  272. package/src/testing/flight.ts +232 -0
  273. package/src/testing/generated-routes.ts +183 -0
  274. package/src/testing/index.ts +99 -0
  275. package/src/testing/internal/context.ts +348 -0
  276. package/src/testing/internal/flight-client-globals.ts +30 -0
  277. package/src/testing/internal/seed-vars.ts +54 -0
  278. package/src/testing/render-handler.ts +330 -0
  279. package/src/testing/render-route.tsx +566 -0
  280. package/src/testing/run-loader.ts +378 -0
  281. package/src/testing/run-middleware.ts +205 -0
  282. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  283. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  284. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  285. package/src/testing/vitest-stubs/version.ts +5 -0
  286. package/src/testing/vitest.ts +305 -0
  287. package/src/theme/ThemeProvider.tsx +0 -52
  288. package/src/theme/ThemeScript.tsx +0 -6
  289. package/src/theme/constants.ts +0 -12
  290. package/src/theme/index.ts +0 -7
  291. package/src/theme/theme-context.ts +1 -5
  292. package/src/theme/theme-script.ts +0 -14
  293. package/src/theme/use-theme.ts +0 -3
  294. package/src/types/boundaries.ts +0 -35
  295. package/src/types/cache-types.ts +13 -4
  296. package/src/types/error-types.ts +30 -90
  297. package/src/types/global-namespace.ts +54 -41
  298. package/src/types/handler-context.ts +97 -22
  299. package/src/types/index.ts +1 -10
  300. package/src/types/loader-types.ts +6 -3
  301. package/src/types/request-scope.ts +0 -19
  302. package/src/types/route-config.ts +6 -50
  303. package/src/types/route-entry.ts +0 -6
  304. package/src/types/segments.ts +18 -14
  305. package/src/urls/include-helper.ts +9 -56
  306. package/src/urls/index.ts +1 -11
  307. package/src/urls/path-helper-types.ts +19 -5
  308. package/src/urls/path-helper.ts +17 -106
  309. package/src/urls/pattern-types.ts +36 -19
  310. package/src/urls/response-types.ts +20 -19
  311. package/src/urls/type-extraction.ts +58 -139
  312. package/src/urls/urls-function.ts +1 -18
  313. package/src/use-loader.tsx +292 -107
  314. package/src/vite/debug.ts +1 -0
  315. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  316. package/src/vite/discovery/discover-routers.ts +95 -82
  317. package/src/vite/discovery/discovery-errors.ts +194 -0
  318. package/src/vite/discovery/prerender-collection.ts +26 -34
  319. package/src/vite/discovery/route-types-writer.ts +40 -84
  320. package/src/vite/discovery/state.ts +39 -1
  321. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  322. package/src/vite/index.ts +4 -0
  323. package/src/vite/plugin-types.ts +185 -10
  324. package/src/vite/plugins/cjs-to-esm.ts +3 -18
  325. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  326. package/src/vite/plugins/client-ref-hashing.ts +12 -11
  327. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  328. package/src/vite/plugins/expose-action-id.ts +4 -75
  329. package/src/vite/plugins/expose-id-utils.ts +3 -54
  330. package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
  331. package/src/vite/plugins/expose-ids/handler-transform.ts +6 -74
  332. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
  333. package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
  334. package/src/vite/plugins/expose-internal-ids.ts +57 -67
  335. package/src/vite/plugins/performance-tracks.ts +9 -16
  336. package/src/vite/plugins/refresh-cmd.ts +1 -1
  337. package/src/vite/plugins/use-cache-transform.ts +26 -49
  338. package/src/vite/plugins/vercel-output.ts +258 -0
  339. package/src/vite/plugins/version-injector.ts +2 -32
  340. package/src/vite/plugins/version-plugin.ts +32 -23
  341. package/src/vite/plugins/virtual-entries.ts +35 -17
  342. package/src/vite/rango.ts +148 -115
  343. package/src/vite/router-discovery.ts +220 -68
  344. package/src/vite/utils/ast-handler-extract.ts +15 -31
  345. package/src/vite/utils/bundle-analysis.ts +10 -15
  346. package/src/vite/utils/client-chunks.ts +184 -0
  347. package/src/vite/utils/forward-user-plugins.ts +171 -0
  348. package/src/vite/utils/manifest-utils.ts +4 -59
  349. package/src/vite/utils/package-resolution.ts +1 -73
  350. package/src/vite/utils/prerender-utils.ts +0 -34
  351. package/src/vite/utils/shared-utils.ts +95 -43
  352. package/src/browser/action-response-classifier.ts +0 -99
  353. package/src/browser/react/use-client-cache.ts +0 -58
  354. package/src/browser/shallow.ts +0 -40
  355. package/src/handles/index.ts +0 -7
  356. package/src/router/middleware-cookies.ts +0 -55
@@ -12,7 +12,7 @@ export interface BuildEnvFactoryContext {
12
12
  /** Vite command ("serve" for dev, "build" for production). */
13
13
  command: "serve" | "build";
14
14
  /** Router deployment preset. */
15
- preset: "node" | "cloudflare";
15
+ preset: "node" | "cloudflare" | "vercel";
16
16
  }
17
17
 
18
18
  /**
@@ -47,6 +47,64 @@ export type BuildEnvOption =
47
47
  | Record<string, unknown>
48
48
  | BuildEnvFactory;
49
49
 
50
+ // -- Client chunking --------------------------------------------------------
51
+
52
+ /**
53
+ * Metadata for one client ("use client") module, passed to a {@link ClientChunks}
54
+ * function. Mirrors the shape `@vitejs/plugin-rsc` passes to its own
55
+ * `clientChunks` option.
56
+ */
57
+ export interface ClientChunkMeta {
58
+ /** Absolute module id of the "use client" file. */
59
+ id: string;
60
+ /** Normalized (posix) module id — convenient for path-based matching. */
61
+ normalizedId: string;
62
+ /**
63
+ * The RSC/server chunk that statically imports this client reference. This is
64
+ * the key used for the default grouping when no override is supplied: a single
65
+ * router that statically imports every route yields ONE `serverChunk`, hence
66
+ * one client chunk for all routes.
67
+ */
68
+ serverChunk: string;
69
+ }
70
+
71
+ /**
72
+ * Controls how client ("use client") components are grouped into browser
73
+ * chunks, i.e. per-route / per-feature code splitting of the client bundle.
74
+ *
75
+ * Without splitting, a single router ships ONE client chunk containing every
76
+ * route's client components (and their CSS) — navigating to one route downloads
77
+ * every other route's client code. (Host sub-apps loaded via a dynamic `import()`
78
+ * are the exception: each forms its own chunk.) This option controls how that
79
+ * monolith is split.
80
+ *
81
+ * Behavior branches:
82
+ * - `true` / omitted (**default**, pre-1.0): Rango's built-in **directory
83
+ * strategy**. It splits app `"use client"` modules by **route id** — the segment
84
+ * after a route-root directory (`routes`, `app`, `pages`, `features`, `handlers`,
85
+ * …) — so `routes/dashboard/**` becomes `app-dashboard` at any nesting depth.
86
+ * Where it finds NO route structure (a flat `src/components/`, or host sub-apps
87
+ * already split by a dynamic `import()`), it inherits the default grouping
88
+ * unchanged — so the shared `src/components` chunk stays shared and host apps do
89
+ * not leak across each other. Shared runtime (React, the router, `node_modules`)
90
+ * is never split.
91
+ * - `false`: opt out — inherit `@vitejs/plugin-rsc`'s default grouping everywhere
92
+ * (one chunk per router / per host sub-app).
93
+ * - function: full override. Return a chunk group name, or `undefined` to fall
94
+ * back to the default grouping for that one module. Forwarded directly to
95
+ * `@vitejs/plugin-rsc`'s `clientChunks`.
96
+ *
97
+ * Every module maps to exactly one group, so there is no byte duplication: a
98
+ * component used by two routes lives in one group and is fetched whenever it
99
+ * renders. Put genuinely shared client components OUTSIDE route directories so
100
+ * they land in the shared group rather than one route's chunk.
101
+ *
102
+ * @default true
103
+ */
104
+ export type ClientChunks =
105
+ | boolean
106
+ | ((meta: ClientChunkMeta) => string | undefined);
107
+
50
108
  // -- Plugin options ---------------------------------------------------------
51
109
 
52
110
  /**
@@ -60,16 +118,27 @@ interface RangoBaseOptions {
60
118
  banner?: boolean;
61
119
 
62
120
  /**
63
- * Environment bindings available to Prerender and Static handlers at build
64
- * time via `ctx.env`. Applies to both production build and dev on-demand
65
- * prerender (`/__rsc_prerender`).
121
+ * Group client ("use client") components into browser chunks for per-route /
122
+ * per-feature code splitting. On by default (pre-1.0); pass `false` to opt out.
123
+ * See {@link ClientChunks}.
66
124
  *
67
- * This is the build-time env supplied by the Vite plugin, not the live
68
- * request env. It is shared across all prerender invocations for the build.
125
+ * @default true
126
+ */
127
+ clientChunks?: ClientChunks;
128
+
129
+ /**
130
+ * Filter which files route discovery scans, by glob. Paths are matched
131
+ * root-relative (e.g. `src/routes/**`). `include` restricts discovery to
132
+ * matching files; `exclude` removes matches (the defaults cover tests, dist,
133
+ * coverage, etc.). Mirrors the CLI's `--include`/`--exclude`.
69
134
  *
70
- * @default false
135
+ * @example
136
+ * rango({ discovery: { include: ["src/routes/**"] } })
71
137
  */
72
- buildEnv?: BuildEnvOption;
138
+ discovery?: {
139
+ include?: string[];
140
+ exclude?: string[];
141
+ };
73
142
  }
74
143
 
75
144
  /**
@@ -80,6 +149,31 @@ export interface RangoNodeOptions extends RangoBaseOptions {
80
149
  * Deployment preset. Defaults to 'node' when not specified.
81
150
  */
82
151
  preset?: "node";
152
+
153
+ /**
154
+ * Path to a host-router entry (a module that calls `createHostRouter()` and
155
+ * exports the instance) to serve instead of a single `createRouter()` app.
156
+ * Root-relative (e.g. `"./src/worker.rsc.tsx"`).
157
+ *
158
+ * Set this when the app is a multi-app host router: auto-discovery otherwise
159
+ * finds the sub-apps' multiple `createRouter()` files and cannot pick an entry.
160
+ * When omitted, rango auto-detects a single `createHostRouter()` file if the
161
+ * app has several `createRouter()` files. The host module must export the
162
+ * `HostRouter` instance (default export or a named `hostRouter`/`router`
163
+ * export), not a Cloudflare-style `{ fetch }` object.
164
+ */
165
+ host?: string;
166
+
167
+ /**
168
+ * Environment bindings available to Prerender and Static handlers at build
169
+ * time via `ctx.env`. Shared across all prerender invocations for the build.
170
+ *
171
+ * `"auto"` is Cloudflare-only (it resolves the wrangler platform proxy), so it
172
+ * is not accepted on the Node preset — pass an object or a factory instead.
173
+ *
174
+ * @default false
175
+ */
176
+ buildEnv?: Exclude<BuildEnvOption, "auto">;
83
177
  }
84
178
 
85
179
  /**
@@ -89,15 +183,96 @@ export interface RangoCloudflareOptions extends RangoBaseOptions {
89
183
  /**
90
184
  * Deployment preset for Cloudflare Workers.
91
185
  * When using cloudflare preset:
92
- * - @vitejs/plugin-rsc is NOT added (cloudflare plugin adds it)
186
+ * - @vitejs/plugin-rsc IS still added by rango(), but with `serverHandler: false`
187
+ * (the cloudflare plugin owns the RSC worker/server entry); only `client` and
188
+ * `ssr` virtual entries are configured, no rsc entry
93
189
  * - Your worker entry (e.g., worker.rsc.tsx) imports the router directly
94
190
  * - Browser and SSR use virtual entries
95
191
  * - Build-time manifest generation is auto-detected from the resolved RSC environment config
96
192
  */
97
193
  preset: "cloudflare";
194
+
195
+ /**
196
+ * Environment bindings available to Prerender and Static handlers at build
197
+ * time via `ctx.env`. Shared across all prerender invocations for the build.
198
+ *
199
+ * `"auto"` resolves the Cloudflare platform proxy via wrangler
200
+ * `getPlatformProxy()`.
201
+ *
202
+ * @default false
203
+ */
204
+ buildEnv?: BuildEnvOption;
205
+ }
206
+
207
+ /**
208
+ * Per-function knobs for the Vercel deployment, written into the generated
209
+ * `.vc-config.json` (and `config.json` for `functionName`).
210
+ */
211
+ export interface VercelPresetOptions {
212
+ /** Node runtime for the function. @default "nodejs22.x" */
213
+ runtime?: string;
214
+ /** Max execution time in seconds. @default 30 */
215
+ maxDuration?: number;
216
+ /** Function memory in MB (platform default when omitted). */
217
+ memory?: number;
218
+ /** Regions to pin the function to (platform default when omitted). */
219
+ regions?: string[];
220
+ /**
221
+ * Function name — the `<name>.func` directory and the `config.json` route
222
+ * destination. @default "index"
223
+ */
224
+ functionName?: string;
225
+ }
226
+
227
+ /**
228
+ * Options for Vercel Functions deployment.
229
+ *
230
+ * Builds like the node preset (Vercel runs Node Functions, not Workers): rango
231
+ * owns the RSC entry, `process.env.NODE_ENV` is folded for the build, and after
232
+ * the build a `.vercel/output` directory (Build Output API v3) is assembled from
233
+ * `dist/` — a single streaming Node Function plus the static client assets. The
234
+ * app must install `@vercel/functions` (used by `VercelCacheStore` and the
235
+ * generated function launcher).
236
+ */
237
+ export interface RangoVercelOptions extends RangoBaseOptions {
238
+ /**
239
+ * Deployment preset for Vercel Functions.
240
+ */
241
+ preset: "vercel";
242
+
243
+ /**
244
+ * Path to a host-router entry (a module that calls `createHostRouter()` and
245
+ * exports the instance) to serve instead of a single `createRouter()` app.
246
+ * Root-relative (e.g. `"./src/worker.rsc.tsx"`).
247
+ *
248
+ * Set this when the app is a multi-app host router: auto-discovery otherwise
249
+ * finds the sub-apps' multiple `createRouter()` files and cannot pick an entry.
250
+ * When omitted, rango auto-detects a single `createHostRouter()` file if the
251
+ * app has several `createRouter()` files. The host module must export the
252
+ * `HostRouter` instance (default export or a named `hostRouter`/`router`
253
+ * export), not a Cloudflare-style `{ fetch }` object. The Vercel function then
254
+ * runs `hostRouter.match()` for every request (single-function deploy).
255
+ */
256
+ host?: string;
257
+
258
+ /**
259
+ * Environment bindings available to Prerender and Static handlers at build
260
+ * time via `ctx.env`. `"auto"` is Cloudflare-only; pass an object or a factory.
261
+ *
262
+ * @default false
263
+ */
264
+ buildEnv?: Exclude<BuildEnvOption, "auto">;
265
+
266
+ /**
267
+ * Vercel function configuration written into the Build Output.
268
+ */
269
+ vercel?: VercelPresetOptions;
98
270
  }
99
271
 
100
272
  /**
101
273
  * Options for rango() Vite plugin
102
274
  */
103
- export type RangoOptions = RangoNodeOptions | RangoCloudflareOptions;
275
+ export type RangoOptions =
276
+ | RangoNodeOptions
277
+ | RangoCloudflareOptions
278
+ | RangoVercelOptions;
@@ -12,13 +12,9 @@ export function createCjsToEsmPlugin(): Plugin {
12
12
  name: "@rangojs/router:cjs-to-esm",
13
13
  enforce: "pre",
14
14
  transform(code, id) {
15
- const cleanId = id.split("?")[0];
15
+ const cleanId = id.split("?")[0].replaceAll("\\", "/");
16
16
 
17
- // Transform the client.browser.js entry point to re-export from CJS
18
- if (
19
- cleanId.includes("vendor/react-server-dom/client.browser.js") ||
20
- cleanId.includes("vendor\\react-server-dom\\client.browser.js")
21
- ) {
17
+ if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
22
18
  const isProd = process.env.NODE_ENV === "production";
23
19
  const cjsFile = isProd
24
20
  ? "./cjs/react-server-dom-webpack-client.browser.production.js"
@@ -31,58 +27,47 @@ export function createCjsToEsmPlugin(): Plugin {
31
27
  };
32
28
  }
33
29
 
34
- // Transform the actual CJS files to ESM
35
30
  if (
36
- (cleanId.includes("vendor/react-server-dom/cjs/") ||
37
- cleanId.includes("vendor\\react-server-dom\\cjs\\")) &&
31
+ cleanId.includes("vendor/react-server-dom/cjs/") &&
38
32
  cleanId.includes("client.browser")
39
33
  ) {
40
34
  let transformed = code;
41
35
 
42
- // Extract the license comment to preserve it
43
36
  const licenseMatch = transformed.match(/^\/\*\*[\s\S]*?\*\//);
44
37
  const license = licenseMatch ? licenseMatch[0] : "";
45
38
  if (license) {
46
39
  transformed = transformed.slice(license.length);
47
40
  }
48
41
 
49
- // Remove "use strict" (both dev and prod have this)
50
42
  transformed = transformed.replace(/^\s*["']use strict["'];\s*/, "");
51
43
 
52
- // Remove the conditional IIFE wrapper (development only)
53
44
  transformed = transformed.replace(
54
45
  /^\s*["']production["']\s*!==\s*process\.env\.NODE_ENV\s*&&\s*\(function\s*\(\)\s*\{/,
55
46
  "",
56
47
  );
57
48
 
58
- // Remove the closing of the conditional IIFE at the end (development only)
59
49
  transformed = transformed.replace(/\}\)\(\);?\s*$/, "");
60
50
 
61
- // Replace require('react') and require('react-dom') with imports (development)
62
51
  transformed = transformed.replace(
63
52
  /var\s+React\s*=\s*require\s*\(\s*["']react["']\s*\)\s*,[\s\n]+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
64
53
  'import React from "react";\nimport ReactDOM from "react-dom";\nvar ',
65
54
  );
66
55
 
67
- // Replace require('react-dom') only (production - doesn't import React)
68
56
  transformed = transformed.replace(
69
57
  /var\s+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
70
58
  'import ReactDOM from "react-dom";\nvar ',
71
59
  );
72
60
 
73
- // Transform exports.xyz = function() to export function xyz()
74
61
  transformed = transformed.replace(
75
62
  /exports\.(\w+)\s*=\s*function\s*\(/g,
76
63
  "export function $1(",
77
64
  );
78
65
 
79
- // Transform exports.xyz = value to export const xyz = value
80
66
  transformed = transformed.replace(
81
67
  /exports\.(\w+)\s*=/g,
82
68
  "export const $1 =",
83
69
  );
84
70
 
85
- // Reconstruct with license at the top
86
71
  transformed = license + "\n" + transformed;
87
72
 
88
73
  debug?.("cjs-to-esm body rewrite %s", id);
@@ -73,8 +73,6 @@ export function clientRefDedup(): Plugin {
73
73
  apply: "serve",
74
74
 
75
75
  configResolved(config: ResolvedConfig) {
76
- // Respect user's optimizeDeps.exclude — if a package is explicitly
77
- // excluded from pre-bundling, we shouldn't redirect it there.
78
76
  const clientEnv = config.environments?.["client"];
79
77
  clientExclude =
80
78
  clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
@@ -91,27 +89,19 @@ export function clientRefDedup(): Plugin {
91
89
  },
92
90
 
93
91
  resolveId(source, importer, options) {
94
- // Only intercept in the client environment
95
92
  if (this.environment?.name !== "client") return;
96
93
 
97
- // Only handle imports from client-in-server-package-proxy virtual modules
98
94
  if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
99
95
 
100
- // Only handle absolute node_modules paths
101
96
  if (!source.includes("/node_modules/")) return;
102
97
 
103
- // Must have an importer
104
- if (!importer) return;
105
-
106
98
  const packageName = extractPackageName(source);
107
99
  if (!packageName) return;
108
100
 
109
- // Don't redirect packages that are excluded from optimization
110
101
  if (clientExclude.includes(packageName)) return;
111
102
 
112
103
  if (debug) dedupedPackages.add(packageName);
113
104
 
114
- // Return a virtual module that re-exports via bare specifier
115
105
  return `\0rango:dedup/${packageName}`;
116
106
  },
117
107
 
@@ -120,7 +110,6 @@ export function clientRefDedup(): Plugin {
120
110
 
121
111
  const packageName = id.slice("\0rango:dedup/".length);
122
112
 
123
- // Re-export via bare specifier so Vite routes through pre-bundling
124
113
  return [
125
114
  `export * from ${JSON.stringify(packageName)};`,
126
115
  `import * as __all__ from ${JSON.stringify(packageName)};`,
@@ -5,7 +5,6 @@ import { createRangoDebugger, createCounter, NS } from "../debug.js";
5
5
 
6
6
  const debug = createRangoDebugger(NS.transform);
7
7
 
8
- // Dev-mode client-reference key prefixes emitted by @vitejs/plugin-rsc
9
8
  const CLIENT_PKG_PROXY_PREFIX =
10
9
  "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
11
10
  const CLIENT_IN_SERVER_PKG_PROXY_PREFIX =
@@ -22,6 +21,17 @@ const FS_PREFIX = "/@fs/";
22
21
  * Returns the input unchanged if it doesn't match a known dev-mode pattern
23
22
  * (e.g., already a production hash).
24
23
  */
24
+ /**
25
+ * The production client-reference key hash: `sha256(relativeId).slice(0,12)`,
26
+ * matching @vitejs/plugin-rsc's `hashString`. Exported so the client-chunks
27
+ * strategy can hash a `clientChunks` callback's `meta.normalizedId` (already the
28
+ * project-root-relative id) and compare it against fallback hashes collected
29
+ * during discovery.
30
+ */
31
+ export function hashRefKey(relativeId: string): string {
32
+ return createHash("sha256").update(relativeId).digest("hex").slice(0, 12);
33
+ }
34
+
25
35
  export function computeProductionHash(
26
36
  projectRoot: string,
27
37
  refKey: string,
@@ -29,32 +39,24 @@ export function computeProductionHash(
29
39
  let toHash: string;
30
40
 
31
41
  if (refKey.startsWith(CLIENT_PKG_PROXY_PREFIX)) {
32
- // /@id/__x00__virtual:vite-rsc/client-package-proxy/<pkg> -> hash("<pkg>")
33
42
  toHash = refKey.slice(CLIENT_PKG_PROXY_PREFIX.length);
34
43
  } else if (refKey.startsWith(CLIENT_IN_SERVER_PKG_PROXY_PREFIX)) {
35
- // /@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/<encodedAbsPath>
36
44
  const absPath = decodeURIComponent(
37
45
  refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length),
38
46
  );
39
47
  toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
40
48
  } else if (refKey.startsWith(FS_PREFIX)) {
41
- // /@fs/abs/path.tsx -> hash(relative(root, "/abs/path.tsx"))
42
49
  const absPath = refKey.slice(FS_PREFIX.length - 1); // keep leading /
43
50
  toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
44
51
  } else if (refKey.startsWith("/")) {
45
- // /src/Button.tsx -> hash("src/Button.tsx")
46
52
  toHash = refKey.slice(1);
47
53
  } else {
48
- // Already hashed or unknown format — return unchanged
49
54
  return refKey;
50
55
  }
51
56
 
52
- return createHash("sha256").update(toHash).digest("hex").slice(0, 12);
57
+ return hashRefKey(toHash);
53
58
  }
54
59
 
55
- // Regex to match registerClientReference() calls as emitted by @vitejs/plugin-rsc.
56
- // Captures the reference key (second argument) from the call.
57
- // Handles two proxy forms: parenthesized expression `(expr)` and arrow-throw `() => { ... }`.
58
60
  const REGISTER_CLIENT_REF_RE =
59
61
  /registerClientReference\(\s*(?:(?:\([^)]*\))|(?:\(\)[\s\S]*?\}))\s*,\s*"([^"]+)"\s*,\s*"[^"]+"\s*\)/g;
60
62
 
@@ -95,7 +97,6 @@ export function hashClientRefs(projectRoot: string): Plugin {
95
97
  const counter = createCounter(debug, "hash-client-refs");
96
98
  return {
97
99
  name: "@rangojs/router:hash-client-refs",
98
- // Run after the RSC plugin's transform (default enforce is normal)
99
100
  enforce: "post",
100
101
  applyToEnvironment(env) {
101
102
  return env.name === "rsc";
@@ -32,14 +32,6 @@ const IMPORT_NODE_TYPES = new Set([
32
32
  "ExportAllDeclaration",
33
33
  ]);
34
34
 
35
- // Keep in sync with `STUBS` in cloudflare-protocol-loader-hook.mjs —
36
- // both paths (Vite transform and Node loader) need to hand out the same
37
- // classes. Unknown `cloudflare:*` modules fall back to an empty default
38
- // export so third-party packages (e.g. the Cloudflare Agents SDK) can
39
- // pull them into the graph without crashing discovery. Discovery only
40
- // evaluates module top-level code — no handlers run — so missing named
41
- // exports only fail if something does `class X extends Missing {}` at
42
- // module scope, which is rare outside the already-stubbed classes.
43
35
  const STUBS: Record<string, string> = {
44
36
  "cloudflare:workers": `
45
37
  export class DurableObject { constructor(_ctx, _env) {} }
@@ -65,15 +57,6 @@ export default {};
65
57
  `,
66
58
  };
67
59
 
68
- // Policy: unknown `cloudflare:*` specifiers resolve permissively (empty
69
- // default export) rather than throwing. We prioritize dependency-graph
70
- // resilience over strict validation of user imports because third-party
71
- // packages can pull `cloudflare:*` modules we haven't curated, and
72
- // discovery should not fail just because those modules appear in the graph.
73
- // Tradeoff: unsupported user-authored `cloudflare:*` imports may fail later
74
- // with a generic JS/module error instead of a tailored rango-branded hint.
75
- // The test below pins this behavior so dependency compatibility is not
76
- // regressed accidentally.
77
60
  const FALLBACK_STUB = `export default {};\n`;
78
61
 
79
62
  interface AstNode {
@@ -139,7 +122,7 @@ export function createCloudflareProtocolStubPlugin(): Plugin {
139
122
 
140
123
  let ast: AstNode;
141
124
  try {
142
- ast = this.parse(code) as unknown as AstNode;
125
+ ast = this.parse(code, { lang: "tsx" }) as unknown as AstNode;
143
126
  } catch {
144
127
  // Malformed source — let a downstream plugin surface the parse error.
145
128
  return null;
@@ -163,9 +146,6 @@ export function createCloudflareProtocolStubPlugin(): Plugin {
163
146
 
164
147
  if (hits.length === 0) return null;
165
148
 
166
- // Rewrite from last to first so earlier offsets stay valid. `start`/
167
- // `end` span the full literal including quotes, so we re-emit the
168
- // same quote character around the new specifier.
169
149
  hits.sort((a, b) => b.start - a.start);
170
150
  let out = code;
171
151
  for (const hit of hits) {
@@ -7,9 +7,6 @@ import { createRangoDebugger, createCounter, NS } from "../debug.js";
7
7
 
8
8
  const debug = createRangoDebugger(NS.transform);
9
9
 
10
- /**
11
- * Type for the RSC plugin's manager API
12
- */
13
10
  interface RscPluginManager {
14
11
  serverReferenceMetaMap: Record<
15
12
  string,
@@ -26,14 +23,9 @@ interface RscPluginApi {
26
23
  manager: RscPluginManager;
27
24
  }
28
25
 
29
- /**
30
- * Get the RSC plugin's API from Vite config
31
- */
32
26
  function getRscPluginApi(config: ResolvedConfig): RscPluginApi | undefined {
33
- // Try by name first
34
27
  let plugin = config.plugins.find((p) => p.name === "rsc:minimal");
35
28
 
36
- // Fallback: find by API structure if name lookup fails
37
29
  if (!plugin) {
38
30
  plugin = config.plugins.find(
39
31
  (p) =>
@@ -42,7 +34,7 @@ function getRscPluginApi(config: ResolvedConfig): RscPluginApi | undefined {
42
34
  );
43
35
  if (plugin) {
44
36
  console.warn(
45
- `[rsc-router:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). ` +
37
+ `[rango:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). ` +
46
38
  `Consider updating the name lookup if the plugin was renamed.`,
47
39
  );
48
40
  }
@@ -62,13 +54,11 @@ function getRscPluginApi(config: ResolvedConfig): RscPluginApi | undefined {
62
54
  function isUseServerModule(filePath: string): boolean {
63
55
  try {
64
56
  const content = fs.readFileSync(filePath, "utf-8");
65
- // Remove leading comments and whitespace to find the first meaningful content
66
57
  const trimmed = content
67
- .replace(/^\s*\/\/[^\n]*\n/gm, "") // Remove single-line comments
68
- .replace(/^\s*\/\*[\s\S]*?\*\/\s*/gm, "") // Remove multi-line comments
58
+ .replace(/^\s*\/\/[^\n]*\n/gm, "")
59
+ .replace(/^\s*\/\*[\s\S]*?\*\/\s*/gm, "")
69
60
  .trimStart();
70
61
 
71
- // Check if the file starts with "use server" directive
72
62
  return (
73
63
  trimmed.startsWith('"use server"') || trimmed.startsWith("'use server'")
74
64
  );
@@ -77,18 +67,6 @@ function isUseServerModule(filePath: string): boolean {
77
67
  }
78
68
  }
79
69
 
80
- /**
81
- * Transform code to expose action IDs on createServerReference calls.
82
- * Wraps each call with an IIFE that attaches $id to the returned function.
83
- *
84
- * @param code - The source code to transform
85
- * @param sourceId - The source file identifier (for sourcemap)
86
- * @param hashToFileMap - Optional mapping from hash to file path (for server bundles)
87
- */
88
- /**
89
- * Apply createServerReference wrapping to a MagicString instance.
90
- * Returns true if any changes were made.
91
- */
92
70
  function applyServerReferenceWrapping(
93
71
  code: string,
94
72
  s: MagicString,
@@ -98,11 +76,6 @@ function applyServerReferenceWrapping(
98
76
  return false;
99
77
  }
100
78
 
101
- // Match: createServerReference("hash#actionName", ...) or $$ReactClient.createServerReference(...)
102
- // The RSC plugin uses $$ReactClient namespace in transformed code.
103
- // Note: [^)]* cannot handle nested parens in trailing args. This is safe in practice
104
- // because the RSC plugin always generates simple variable references (e.g., callServer)
105
- // as the second argument, never nested function calls.
106
79
  const pattern =
107
80
  /((?:\$\$\w+\.)?createServerReference)\(("[^"]+#[^"]+")([^)]*)\)/g;
108
81
 
@@ -115,23 +88,19 @@ function applyServerReferenceWrapping(
115
88
  const start = match.index;
116
89
  const end = start + fullMatch.length;
117
90
 
118
- // Parse the ID to potentially replace hash with file path
119
91
  let finalIdArg = idArg;
120
92
  if (hashToFileMap) {
121
- // idArg is like '"hash#actionName"', extract the parts
122
93
  const idValue = idArg.slice(1, -1); // Remove quotes
123
94
  const hashMatch = idValue.match(/^([^#]+)#(.+)$/);
124
95
  if (hashMatch) {
125
96
  const [, hash, actionName] = hashMatch;
126
97
  const filePath = hashToFileMap.get(hash);
127
98
  if (filePath) {
128
- // Replace hash with file path for server-side
129
99
  finalIdArg = `"${filePath}#${actionName}"`;
130
100
  }
131
101
  }
132
102
  }
133
103
 
134
- // Wrap the createServerReference call to attach $$id to the returned function
135
104
  const replacement = `(function(fn) { fn.$$id = ${finalIdArg}; return fn; })(${fnCall}(${idArg}${rest}))`;
136
105
  s.overwrite(start, end, replacement);
137
106
  }
@@ -155,29 +124,6 @@ function transformServerReferences(
155
124
  };
156
125
  }
157
126
 
158
- /**
159
- * Transform registerServerReference calls in server bundles to use file paths instead of hashes.
160
- * Pattern: registerServerReference(fn, "hash", "exportName")
161
- * React's registerServerReference sets $$id = hash + "#" + exportName
162
- * By replacing the hash with file path, $$id will contain the file path for revalidation matching.
163
- *
164
- * Only actions from module-level "use server" files are transformed.
165
- * Inline actions (defined in RSC components with "use server" inside a function) are NOT in
166
- * hashToFileMap and keep their hashed IDs. This is intentional for client security:
167
- * - Module-level "use server" files: shared action modules, file path helps revalidation
168
- * - Inline actions: one-off actions in RSC, hash ID prevents file path exposure to client
169
- *
170
- * @param code - The source code to transform
171
- * @param sourceId - The source file identifier (for sourcemap)
172
- * @param hashToFileMap - Mapping from hash to file path (only module-level "use server" files)
173
- */
174
- /**
175
- * Apply registerServerReference wrapping to a MagicString instance.
176
- * Returns true if any changes were made.
177
- *
178
- * Only actions from module-level "use server" files are transformed.
179
- * Inline actions keep their hashed IDs for client security.
180
- */
181
127
  function applyRegisterReferenceWrapping(
182
128
  code: string,
183
129
  s: MagicString,
@@ -187,8 +133,6 @@ function applyRegisterReferenceWrapping(
187
133
  return false;
188
134
  }
189
135
 
190
- // Match: registerServerReference(fn, "hash", "exportName")
191
- // The hash is the second argument, exportName is the third
192
136
  const pattern =
193
137
  /registerServerReference\(([^,]+),\s*"([^"]+)",\s*"([^"]+)"\)/g;
194
138
 
@@ -200,15 +144,9 @@ function applyRegisterReferenceWrapping(
200
144
  const start = match.index;
201
145
  const end = start + fullMatch.length;
202
146
 
203
- // Look up the file path for this hash
204
147
  const filePath = hashToFileMap.get(hash);
205
148
  if (filePath) {
206
149
  hasChanges = true;
207
- // WRAP the call to add $id property with file path
208
- // Keep the original hash for React's action registry (so loadServerAction works)
209
- // Add $id (single dollar) with file path for revalidation matching
210
- // Note: We use $id instead of $$id because React's registerServerReference
211
- // sets $$id as a non-writable property
212
150
  const filePathId = `${filePath}#${exportName}`;
213
151
  const replacement = `(function(fn) { fn.$id = "${filePathId}"; return fn; })(registerServerReference(${fnArg}, "${hash}", "${exportName}"))`;
214
152
  s.overwrite(start, end, replacement);
@@ -287,7 +225,7 @@ export function exposeActionId(): Plugin {
287
225
 
288
226
  if (!rscPluginApi) {
289
227
  throw new Error(
290
- "[rsc-router] Could not find @vitejs/plugin-rsc. " +
228
+ "[rango] Could not find @vitejs/plugin-rsc. " +
291
229
  "@rangojs/router requires the Vite RSC plugin, which is included automatically by rango().",
292
230
  );
293
231
  }
@@ -336,27 +274,19 @@ export function exposeActionId(): Plugin {
336
274
 
337
275
  const start = counterTransform ? performance.now() : 0;
338
276
  try {
339
- // Dev mode: no hash-to-file mapping needed (IDs are already file paths)
340
277
  return transformServerReferences(code, id);
341
278
  } finally {
342
279
  counterTransform?.record(id, performance.now() - start);
343
280
  }
344
281
  },
345
282
 
346
- // Build mode: renderChunk runs after all transforms and bundling complete
347
283
  renderChunk(code, chunk) {
348
284
  const start = counterRender ? performance.now() : 0;
349
285
  try {
350
- // Only RSC bundle should get file paths for revalidation matching
351
- // SSR bundle must NOT use file paths because client components run there
352
- // and need to match the client bundle during hydration (otherwise: error #418)
353
286
  const isRscEnv = this.environment?.name === "rsc";
354
287
 
355
- // Only use file path mapping for RSC environment
356
288
  const effectiveMap = isRscEnv ? hashToFileMap : undefined;
357
289
 
358
- // For RSC bundles, both createServerReference and registerServerReference
359
- // may need transforming. Use a single MagicString for correct sourcemaps.
360
290
  if (isRscEnv && hashToFileMap) {
361
291
  const s = new MagicString(code);
362
292
  const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
@@ -377,7 +307,6 @@ export function exposeActionId(): Plugin {
377
307
  return null;
378
308
  }
379
309
 
380
- // Non-RSC environments: only transform createServerReference calls
381
310
  return transformServerReferences(code, chunk.fileName, effectiveMap);
382
311
  } finally {
383
312
  counterRender?.record(chunk.fileName, performance.now() - start);