@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.141

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 (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +293 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2508 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +24 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-snapshot.ts +368 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +222 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1113 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +173 -35
  205. package/src/index.ts +241 -73
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +527 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +897 -0
  313. package/src/rsc/shell-serve.ts +124 -0
  314. package/src/rsc/ssr-setup.ts +144 -0
  315. package/src/rsc/transition-gate.ts +89 -0
  316. package/src/rsc/types.ts +95 -12
  317. package/src/runtime-env.ts +18 -0
  318. package/src/search-params.ts +99 -82
  319. package/src/segment-content-promise.ts +67 -0
  320. package/src/segment-loader-promise.ts +149 -0
  321. package/src/segment-system.tsx +349 -134
  322. package/src/serialize.ts +243 -0
  323. package/src/server/context.ts +459 -85
  324. package/src/server/cookie-parse.ts +32 -0
  325. package/src/server/cookie-store.ts +310 -0
  326. package/src/server/fetchable-loader-store.ts +11 -6
  327. package/src/server/handle-store.ts +123 -42
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +848 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +443 -135
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +76 -98
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +44 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +280 -0
  389. package/src/urls/pattern-types.ts +160 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,76 @@
1
+ // Node ESM loader hook that resolves `cloudflare:*` imports to the same
2
+ // stub ESM the Vite transform produces for rewritten specifiers.
3
+ //
4
+ // Why both? The Vite transform (cloudflare-protocol-stub.ts) catches
5
+ // imports in modules that flow through Vite's plugin pipeline — covers
6
+ // user source and any node_modules package Vite fetches and transforms.
7
+ // But Vite/Rollup externalize certain packages (e.g. `partyserver`,
8
+ // which has `import { DurableObject, env } from "cloudflare:workers"`
9
+ // at its top level, and similar "workerd-native" libraries). Externalized
10
+ // modules bypass the transform: Rollup hands their resolution to Node's
11
+ // native ESM loader, which rejects URL-scheme specifiers. This loader
12
+ // hook registers via `module.register()` from `createTempRscServer` and
13
+ // intercepts `cloudflare:*` at Node's resolve layer — before the default
14
+ // loader throws ERR_UNSUPPORTED_ESM_URL_SCHEME.
15
+ //
16
+ // Lifecycle: the hook runs in a dedicated worker thread (Node ESM loader
17
+ // architecture) with its own globalThis. It cannot see the main thread's
18
+ // `__rango_build_env__` bridge, so the `env` export here is always `{}`.
19
+ // That's fine in practice — externalized libraries don't typically touch
20
+ // `env` at module top level; they read it at request time in workerd
21
+ // where the real module exists. Build-time prerender handlers in user
22
+ // source DO read `env`, but they flow through the Vite transform (which
23
+ // does bridge `env` from `getPlatformProxy()`), not through this loader.
24
+ //
25
+ // Keep STUBS in sync with cloudflare-protocol-stub.ts — both paths need
26
+ // to hand out the same base classes.
27
+
28
+ const CF_PREFIX = "cloudflare:";
29
+
30
+ const STUBS = {
31
+ "cloudflare:workers": `
32
+ export class DurableObject { constructor(_ctx, _env) {} }
33
+ export class WorkerEntrypoint { constructor(_ctx, _env) {} }
34
+ export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
35
+ export class RpcTarget {}
36
+ export const env = {};
37
+ export default {};
38
+ `,
39
+ "cloudflare:email": `
40
+ export class EmailMessage { constructor(_from, _to, _raw) {} }
41
+ export default {};
42
+ `,
43
+ "cloudflare:sockets": `
44
+ export function connect() { return {}; }
45
+ export default {};
46
+ `,
47
+ "cloudflare:workflows": `
48
+ export class NonRetryableError extends Error {
49
+ constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
50
+ }
51
+ export default {};
52
+ `,
53
+ };
54
+
55
+ // Policy: unknown `cloudflare:*` specifiers resolve permissively to an
56
+ // empty default export rather than throwing. Same reasoning as
57
+ // cloudflare-protocol-stub.ts's FALLBACK_STUB — we prioritize
58
+ // dependency-graph resilience over strict validation, because third-party
59
+ // packages can pull `cloudflare:*` modules we haven't curated.
60
+ const FALLBACK_STUB = `export default {};\n`;
61
+
62
+ function dataUrlFor(specifier) {
63
+ const body = STUBS[specifier] ?? FALLBACK_STUB;
64
+ return "data:text/javascript;base64," + Buffer.from(body).toString("base64");
65
+ }
66
+
67
+ export async function resolve(specifier, context, nextResolve) {
68
+ if (specifier.startsWith(CF_PREFIX)) {
69
+ return {
70
+ shortCircuit: true,
71
+ url: dataUrlFor(specifier),
72
+ format: "module",
73
+ };
74
+ }
75
+ return nextResolve(specifier, context);
76
+ }
@@ -0,0 +1,194 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ const VIRTUAL_PREFIX = "virtual:rango-cloudflare-stub-";
4
+ const NULL_PREFIX = "\0" + VIRTUAL_PREFIX;
5
+ const CF_PREFIX = "cloudflare:";
6
+
7
+ /**
8
+ * `globalThis` key the `cloudflare:workers` stub reads to populate its
9
+ * `env` export. Router discovery sets this to the resolved `buildEnv`
10
+ * proxy (from `wrangler.getPlatformProxy()` when `buildEnv: "auto"` is
11
+ * configured, or a user-supplied object otherwise) before importing the
12
+ * worker entry, and clears it after discovery disposes the proxy. When
13
+ * unset, the stub's `env` falls back to `{}`.
14
+ *
15
+ * Using `globalThis` is the only cross-module bridge that works here:
16
+ * the stub's `load` hook returns source text, not a live closure, but
17
+ * the stub module is evaluated in the same Node process as the
18
+ * discovery plugin — so reading a global at module-evaluation time
19
+ * reaches whatever the plugin assigned there. A symbol key would be
20
+ * cleaner in-process but awkward to name from the stub source.
21
+ *
22
+ * @internal
23
+ */
24
+ export const BUILD_ENV_GLOBAL_KEY = "__rango_build_env__";
25
+
26
+ const SOURCE_EXT_RE = /\.[mc]?[jt]sx?$/;
27
+
28
+ const IMPORT_NODE_TYPES = new Set([
29
+ "ImportDeclaration",
30
+ "ImportExpression",
31
+ "ExportNamedDeclaration",
32
+ "ExportAllDeclaration",
33
+ ]);
34
+
35
+ const STUBS: Record<string, string> = {
36
+ "cloudflare:workers": `
37
+ export class DurableObject { constructor(_ctx, _env) {} }
38
+ export class WorkerEntrypoint { constructor(_ctx, _env) {} }
39
+ export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
40
+ export class RpcTarget {}
41
+ export const env = globalThis[${JSON.stringify(BUILD_ENV_GLOBAL_KEY)}] ?? {};
42
+ export default {};
43
+ `,
44
+ "cloudflare:email": `
45
+ export class EmailMessage { constructor(_from, _to, _raw) {} }
46
+ export default {};
47
+ `,
48
+ "cloudflare:sockets": `
49
+ export function connect() { return {}; }
50
+ export default {};
51
+ `,
52
+ "cloudflare:workflows": `
53
+ export class NonRetryableError extends Error {
54
+ constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
55
+ }
56
+ export default {};
57
+ `,
58
+ };
59
+
60
+ const FALLBACK_STUB = `export default {};\n`;
61
+
62
+ interface AstNode {
63
+ type: string;
64
+ start?: number;
65
+ end?: number;
66
+ source?: AstNode | null;
67
+ value?: unknown;
68
+ [key: string]: unknown;
69
+ }
70
+
71
+ /**
72
+ * Stubs `cloudflare:*` imports for the discovery-time Node Vite server.
73
+ *
74
+ * Discovery only evaluates user module top-level code — it never invokes
75
+ * DurableObject / WorkerEntrypoint / Workflow handlers — so empty base
76
+ * classes are enough for `class X extends DurableObject {}` declarations
77
+ * to load in Node, where `cloudflare:*` is otherwise unresolvable.
78
+ *
79
+ * Interception point: a transform hook parses source with Rollup's
80
+ * plugin-context parser (`this.parse`) and rewrites only real import
81
+ * specifier spans (`import ... from "cloudflare:xxx"`,
82
+ * `import("cloudflare:xxx")`, `export ... from "cloudflare:xxx"`) to a
83
+ * plain virtual module name (`virtual:rango-cloudflare-stub-xxx`).
84
+ * This must be done in transform because Vite's module runner routes
85
+ * URL-scheme specifiers straight to Node's native ESM loader without
86
+ * consulting plugin `resolveId` hooks. Using the AST (instead of a
87
+ * text regex or a permissive lexer) guarantees that strings,
88
+ * comments, and template literals that merely contain import-like
89
+ * text are never mutated — the walker only looks at the four import
90
+ * node types.
91
+ *
92
+ * The transform runs on user source AND on compiled node_modules
93
+ * output: real-world CF packages (e.g. the Cloudflare Agents SDK)
94
+ * ship compiled JS that contains `import ... from "cloudflare:email"`
95
+ * and similar, so excluding node_modules would leave those imports
96
+ * unrewritten. Cost is small because the early exit (`code.includes`)
97
+ * skips files with no cloudflare: mention.
98
+ *
99
+ * The plugin intentionally runs at Vite's default ordering (no
100
+ * `enforce: "pre"`) so TS/JSX has already been compiled to plain JS
101
+ * by the time `this.parse` runs — acorn doesn't understand
102
+ * non-standard syntax.
103
+ *
104
+ * `cloudflare:workers`, `cloudflare:email`, `cloudflare:sockets`, and
105
+ * `cloudflare:workflows` each get curated stubs with the well-known
106
+ * symbols that appear in top-level `extends` positions. Any other
107
+ * `cloudflare:*` specifier falls back to an empty default export —
108
+ * discovery never executes the handlers, so an empty module is safe
109
+ * for anything the graph pulls in transitively.
110
+ *
111
+ * Only registered in the discovery temp server, not the user's runtime
112
+ * config.
113
+ * @internal
114
+ */
115
+ export function createCloudflareProtocolStubPlugin(): Plugin {
116
+ return {
117
+ name: "@rangojs/router:cloudflare-protocol-stub",
118
+ transform(code, id) {
119
+ const cleanId = id.split("?")[0] ?? id;
120
+ if (!SOURCE_EXT_RE.test(cleanId)) return null;
121
+ if (!code.includes(CF_PREFIX)) return null;
122
+
123
+ let ast: AstNode;
124
+ try {
125
+ ast = this.parse(code, { lang: "tsx" }) as unknown as AstNode;
126
+ } catch {
127
+ // Malformed source — let a downstream plugin surface the parse error.
128
+ return null;
129
+ }
130
+
131
+ const hits: Array<{ start: number; end: number; value: string }> = [];
132
+ walk(ast, (node) => {
133
+ if (!IMPORT_NODE_TYPES.has(node.type)) return;
134
+ const source = node.source;
135
+ if (!source || source.type !== "Literal") return;
136
+ if (typeof source.value !== "string") return;
137
+ if (!source.value.startsWith(CF_PREFIX)) return;
138
+ if (typeof source.start !== "number" || typeof source.end !== "number")
139
+ return;
140
+ hits.push({
141
+ start: source.start,
142
+ end: source.end,
143
+ value: source.value,
144
+ });
145
+ });
146
+
147
+ if (hits.length === 0) return null;
148
+
149
+ hits.sort((a, b) => b.start - a.start);
150
+ let out = code;
151
+ for (const hit of hits) {
152
+ const submodule = hit.value.slice(CF_PREFIX.length);
153
+ const quote = code[hit.start] === "'" ? "'" : '"';
154
+ out =
155
+ out.slice(0, hit.start) +
156
+ quote +
157
+ VIRTUAL_PREFIX +
158
+ submodule +
159
+ quote +
160
+ out.slice(hit.end);
161
+ }
162
+ return { code: out, map: null };
163
+ },
164
+ resolveId(id) {
165
+ if (id.startsWith(VIRTUAL_PREFIX)) {
166
+ return "\0" + id;
167
+ }
168
+ return null;
169
+ },
170
+ load(id) {
171
+ if (!id.startsWith(NULL_PREFIX)) return null;
172
+ const submodule = id.slice(NULL_PREFIX.length);
173
+ const specifier = CF_PREFIX + submodule;
174
+ return STUBS[specifier] ?? FALLBACK_STUB;
175
+ },
176
+ };
177
+ }
178
+
179
+ function walk(node: unknown, visit: (n: AstNode) => void): void {
180
+ if (!node || typeof node !== "object") return;
181
+ if (Array.isArray(node)) {
182
+ for (const child of node) walk(child, visit);
183
+ return;
184
+ }
185
+ const n = node as AstNode;
186
+ if (typeof n.type !== "string") return;
187
+ visit(n);
188
+ for (const key in n) {
189
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
190
+ continue;
191
+ }
192
+ walk(n[key], visit);
193
+ }
194
+ }
@@ -2,11 +2,11 @@ import type { Plugin, ResolvedConfig } from "vite";
2
2
  import MagicString from "magic-string";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs";
5
- import { normalizePath } from "./expose-id-utils.ts";
5
+ import { normalizePath } from "./expose-id-utils.js";
6
+ import { createRangoDebugger, createCounter, NS } from "../debug.js";
7
+
8
+ const debug = createRangoDebugger(NS.transform);
6
9
 
7
- /**
8
- * Type for the RSC plugin's manager API
9
- */
10
10
  interface RscPluginManager {
11
11
  serverReferenceMetaMap: Record<
12
12
  string,
@@ -23,24 +23,19 @@ interface RscPluginApi {
23
23
  manager: RscPluginManager;
24
24
  }
25
25
 
26
- /**
27
- * Get the RSC plugin's API from Vite config
28
- */
29
26
  function getRscPluginApi(config: ResolvedConfig): RscPluginApi | undefined {
30
- // Try by name first
31
27
  let plugin = config.plugins.find((p) => p.name === "rsc:minimal");
32
28
 
33
- // Fallback: find by API structure if name lookup fails
34
29
  if (!plugin) {
35
30
  plugin = config.plugins.find(
36
31
  (p) =>
37
32
  (p.api as RscPluginApi | undefined)?.manager?.serverReferenceMetaMap !==
38
- undefined
33
+ undefined,
39
34
  );
40
35
  if (plugin) {
41
36
  console.warn(
42
- `[rsc-router:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). ` +
43
- `Consider updating the name lookup if the plugin was renamed.`
37
+ `[rango:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). ` +
38
+ `Consider updating the name lookup if the plugin was renamed.`,
44
39
  );
45
40
  }
46
41
  }
@@ -59,13 +54,11 @@ function getRscPluginApi(config: ResolvedConfig): RscPluginApi | undefined {
59
54
  function isUseServerModule(filePath: string): boolean {
60
55
  try {
61
56
  const content = fs.readFileSync(filePath, "utf-8");
62
- // Remove leading comments and whitespace to find the first meaningful content
63
57
  const trimmed = content
64
- .replace(/^\s*\/\/[^\n]*\n/gm, "") // Remove single-line comments
65
- .replace(/^\s*\/\*[\s\S]*?\*\/\s*/gm, "") // Remove multi-line comments
58
+ .replace(/^\s*\/\/[^\n]*\n/gm, "")
59
+ .replace(/^\s*\/\*[\s\S]*?\*\/\s*/gm, "")
66
60
  .trimStart();
67
61
 
68
- // Check if the file starts with "use server" directive
69
62
  return (
70
63
  trimmed.startsWith('"use server"') || trimmed.startsWith("'use server'")
71
64
  );
@@ -74,29 +67,18 @@ function isUseServerModule(filePath: string): boolean {
74
67
  }
75
68
  }
76
69
 
77
- /**
78
- * Transform code to expose action IDs on createServerReference calls.
79
- * Wraps each call with an IIFE that attaches $id to the returned function.
80
- *
81
- * @param code - The source code to transform
82
- * @param sourceId - The source file identifier (for sourcemap)
83
- * @param hashToFileMap - Optional mapping from hash to file path (for server bundles)
84
- */
85
- function transformServerReferences(
70
+ function applyServerReferenceWrapping(
86
71
  code: string,
87
- sourceId?: string,
88
- hashToFileMap?: Map<string, string>
89
- ): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
72
+ s: MagicString,
73
+ hashToFileMap?: Map<string, string>,
74
+ ): boolean {
90
75
  if (!code.includes("createServerReference(")) {
91
- return null;
76
+ return false;
92
77
  }
93
78
 
94
- // Match: createServerReference("hash#actionName", ...) or $$ReactClient.createServerReference(...)
95
- // The RSC plugin uses $$ReactClient namespace in transformed code
96
79
  const pattern =
97
80
  /((?:\$\$\w+\.)?createServerReference)\(("[^"]+#[^"]+")([^)]*)\)/g;
98
81
 
99
- const s = new MagicString(code);
100
82
  let hasChanges = false;
101
83
  let match: RegExpExecArray | null;
102
84
 
@@ -106,28 +88,33 @@ function transformServerReferences(
106
88
  const start = match.index;
107
89
  const end = start + fullMatch.length;
108
90
 
109
- // Parse the ID to potentially replace hash with file path
110
91
  let finalIdArg = idArg;
111
92
  if (hashToFileMap) {
112
- // idArg is like '"hash#actionName"', extract the parts
113
93
  const idValue = idArg.slice(1, -1); // Remove quotes
114
94
  const hashMatch = idValue.match(/^([^#]+)#(.+)$/);
115
95
  if (hashMatch) {
116
96
  const [, hash, actionName] = hashMatch;
117
97
  const filePath = hashToFileMap.get(hash);
118
98
  if (filePath) {
119
- // Replace hash with file path for server-side
120
99
  finalIdArg = `"${filePath}#${actionName}"`;
121
100
  }
122
101
  }
123
102
  }
124
103
 
125
- // Wrap the createServerReference call to attach $$id to the returned function
126
104
  const replacement = `(function(fn) { fn.$$id = ${finalIdArg}; return fn; })(${fnCall}(${idArg}${rest}))`;
127
105
  s.overwrite(start, end, replacement);
128
106
  }
129
107
 
130
- if (!hasChanges) {
108
+ return hasChanges;
109
+ }
110
+
111
+ function transformServerReferences(
112
+ code: string,
113
+ sourceId?: string,
114
+ hashToFileMap?: Map<string, string>,
115
+ ): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
116
+ const s = new MagicString(code);
117
+ if (!applyServerReferenceWrapping(code, s, hashToFileMap)) {
131
118
  return null;
132
119
  }
133
120
 
@@ -137,36 +124,18 @@ function transformServerReferences(
137
124
  };
138
125
  }
139
126
 
140
- /**
141
- * Transform registerServerReference calls in server bundles to use file paths instead of hashes.
142
- * Pattern: registerServerReference(fn, "hash", "exportName")
143
- * React's registerServerReference sets $$id = hash + "#" + exportName
144
- * By replacing the hash with file path, $$id will contain the file path for revalidation matching.
145
- *
146
- * Only actions from module-level "use server" files are transformed.
147
- * Inline actions (defined in RSC components with "use server" inside a function) are NOT in
148
- * hashToFileMap and keep their hashed IDs. This is intentional for client security:
149
- * - Module-level "use server" files: shared action modules, file path helps revalidation
150
- * - Inline actions: one-off actions in RSC, hash ID prevents file path exposure to client
151
- *
152
- * @param code - The source code to transform
153
- * @param sourceId - The source file identifier (for sourcemap)
154
- * @param hashToFileMap - Mapping from hash to file path (only module-level "use server" files)
155
- */
156
- function transformRegisterServerReference(
127
+ function applyRegisterReferenceWrapping(
157
128
  code: string,
158
- sourceId?: string,
159
- hashToFileMap?: Map<string, string>
160
- ): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
161
- if (!hashToFileMap || !code.includes("registerServerReference(")) {
162
- return null;
129
+ s: MagicString,
130
+ hashToFileMap: Map<string, string>,
131
+ ): boolean {
132
+ if (!code.includes("registerServerReference(")) {
133
+ return false;
163
134
  }
164
135
 
165
- // Match: registerServerReference(fn, "hash", "exportName")
166
- // The hash is the second argument, exportName is the third
167
- const pattern = /registerServerReference\(([^,]+),\s*"([^"]+)",\s*"([^"]+)"\)/g;
136
+ const pattern =
137
+ /registerServerReference\(([^,]+),\s*"([^"]+)",\s*"([^"]+)"\)/g;
168
138
 
169
- const s = new MagicString(code);
170
139
  let hasChanges = false;
171
140
  let match: RegExpExecArray | null;
172
141
 
@@ -175,22 +144,27 @@ function transformRegisterServerReference(
175
144
  const start = match.index;
176
145
  const end = start + fullMatch.length;
177
146
 
178
- // Look up the file path for this hash
179
147
  const filePath = hashToFileMap.get(hash);
180
148
  if (filePath) {
181
149
  hasChanges = true;
182
- // WRAP the call to add $id property with file path
183
- // Keep the original hash for React's action registry (so loadServerAction works)
184
- // Add $id (single dollar) with file path for revalidation matching
185
- // Note: We use $id instead of $$id because React's registerServerReference
186
- // sets $$id as a non-writable property
187
150
  const filePathId = `${filePath}#${exportName}`;
188
151
  const replacement = `(function(fn) { fn.$id = "${filePathId}"; return fn; })(registerServerReference(${fnArg}, "${hash}", "${exportName}"))`;
189
152
  s.overwrite(start, end, replacement);
190
153
  }
191
154
  }
192
155
 
193
- if (!hasChanges) {
156
+ return hasChanges;
157
+ }
158
+
159
+ function transformRegisterServerReference(
160
+ code: string,
161
+ sourceId?: string,
162
+ hashToFileMap?: Map<string, string>,
163
+ ): { code: string; map: ReturnType<MagicString["generateMap"]> } | null {
164
+ if (!hashToFileMap) return null;
165
+
166
+ const s = new MagicString(code);
167
+ if (!applyRegisterReferenceWrapping(code, s, hashToFileMap)) {
194
168
  return null;
195
169
  }
196
170
 
@@ -221,6 +195,8 @@ export function exposeActionId(): Plugin {
221
195
  let isBuild = false;
222
196
  let hashToFileMap: Map<string, string> | undefined;
223
197
  let rscPluginApi: RscPluginApi | undefined;
198
+ const counterTransform = createCounter(debug, "expose-action-id transform");
199
+ const counterRender = createCounter(debug, "expose-action-id renderChunk");
224
200
 
225
201
  return {
226
202
  name: "@rangojs/router:expose-action-id",
@@ -235,6 +211,11 @@ export function exposeActionId(): Plugin {
235
211
  rscPluginApi = getRscPluginApi(config);
236
212
  },
237
213
 
214
+ buildEnd() {
215
+ counterTransform?.flush();
216
+ counterRender?.flush();
217
+ },
218
+
238
219
  buildStart() {
239
220
  // Verify RSC plugin is present at build start (after all config hooks have run)
240
221
  // This allows rsc-router:rsc-integration to dynamically add the RSC plugin
@@ -244,10 +225,8 @@ export function exposeActionId(): Plugin {
244
225
 
245
226
  if (!rscPluginApi) {
246
227
  throw new Error(
247
- "[rsc-router] Could not find @vitejs/plugin-rsc. " +
248
- "@rangojs/router requires the Vite RSC plugin.\n" +
249
- "The RSC plugin should be included automatically. If you disabled it with\n" +
250
- "rango({ rsc: false }), add rsc() before rango() in your config."
228
+ "[rango] Could not find @vitejs/plugin-rsc. " +
229
+ "@rangojs/router requires the Vite RSC plugin, which is included automatically by rango().",
251
230
  );
252
231
  }
253
232
 
@@ -257,7 +236,7 @@ export function exposeActionId(): Plugin {
257
236
  const { serverReferenceMetaMap } = rscPluginApi.manager;
258
237
 
259
238
  for (const [absolutePath, meta] of Object.entries(
260
- serverReferenceMetaMap
239
+ serverReferenceMetaMap,
261
240
  )) {
262
241
  // Only include module-level "use server" files
263
242
  // Inline actions (defined in RSC components) should keep hashed IDs for client security
@@ -266,7 +245,7 @@ export function exposeActionId(): Plugin {
266
245
  }
267
246
 
268
247
  const relativePath = normalizePath(
269
- path.relative(config.root, absolutePath)
248
+ path.relative(config.root, absolutePath),
270
249
  );
271
250
 
272
251
  // The referenceKey in build mode is the hash
@@ -275,7 +254,6 @@ export function exposeActionId(): Plugin {
275
254
  }
276
255
  },
277
256
 
278
-
279
257
  // Dev mode only: transform hook runs after RSC plugin creates server references
280
258
  // In dev mode, IDs already contain file paths, not hashes
281
259
  transform(code, id) {
@@ -294,45 +272,45 @@ export function exposeActionId(): Plugin {
294
272
  return;
295
273
  }
296
274
 
297
- // Dev mode: no hash-to-file mapping needed (IDs are already file paths)
298
- return transformServerReferences(code, id);
275
+ const start = counterTransform ? performance.now() : 0;
276
+ try {
277
+ return transformServerReferences(code, id);
278
+ } finally {
279
+ counterTransform?.record(id, performance.now() - start);
280
+ }
299
281
  },
300
282
 
301
- // Build mode: renderChunk runs after all transforms and bundling complete
302
283
  renderChunk(code, chunk) {
303
- // Only RSC bundle should get file paths for revalidation matching
304
- // SSR bundle must NOT use file paths because client components run there
305
- // and need to match the client bundle during hydration (otherwise: error #418)
306
- const isRscEnv = this.environment?.name === "rsc";
307
-
308
- // Only use file path mapping for RSC environment
309
- const effectiveMap = isRscEnv ? hashToFileMap : undefined;
310
-
311
- // Transform createServerReference calls (client-side)
312
- const result = transformServerReferences(
313
- code,
314
- chunk.fileName,
315
- effectiveMap
316
- );
317
-
318
- // For RSC bundles, also transform registerServerReference calls
319
- // This replaces hashed IDs with file paths so $id contains the actual path
320
- if (isRscEnv && hashToFileMap) {
321
- const codeToTransform = result ? result.code : code;
322
- const registerResult = transformRegisterServerReference(
323
- codeToTransform,
324
- chunk.fileName,
325
- hashToFileMap
326
- );
327
- if (registerResult) {
328
- return { code: registerResult.code, map: registerResult.map };
284
+ const start = counterRender ? performance.now() : 0;
285
+ try {
286
+ const isRscEnv = this.environment?.name === "rsc";
287
+
288
+ const effectiveMap = isRscEnv ? hashToFileMap : undefined;
289
+
290
+ if (isRscEnv && hashToFileMap) {
291
+ const s = new MagicString(code);
292
+ const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
293
+ const changed2 = applyRegisterReferenceWrapping(
294
+ code,
295
+ s,
296
+ hashToFileMap,
297
+ );
298
+ if (changed1 || changed2) {
299
+ return {
300
+ code: s.toString(),
301
+ map: s.generateMap({
302
+ source: chunk.fileName,
303
+ includeContent: true,
304
+ }),
305
+ };
306
+ }
307
+ return null;
329
308
  }
330
- }
331
309
 
332
- if (result) {
333
- return { code: result.code, map: result.map };
310
+ return transformServerReferences(code, chunk.fileName, effectiveMap);
311
+ } finally {
312
+ counterRender?.record(chunk.fileName, performance.now() - start);
334
313
  }
335
- return null;
336
314
  },
337
315
  };
338
316
  }