@rangojs/router 0.16.0 → 0.18.0

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 (309) hide show
  1. package/README.md +255 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/cookie-name.d.ts +6 -0
  4. package/dist/types/browser/history-state.d.ts +16 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  6. package/dist/types/browser/partial-update.d.ts +11 -0
  7. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  8. package/dist/types/browser/rsc-router.d.ts +1 -1
  9. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  10. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  11. package/dist/types/cache/cache-key-utils.d.ts +17 -2
  12. package/dist/types/cache/cache-policy.d.ts +17 -0
  13. package/dist/types/cache/cache-scope.d.ts +132 -19
  14. package/dist/types/cache/cache-tag.d.ts +92 -7
  15. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  16. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  17. package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
  18. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  19. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  20. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
  21. package/dist/types/cache/handle-capture.d.ts +41 -3
  22. package/dist/types/cache/handle-snapshot.d.ts +54 -13
  23. package/dist/types/cache/index.d.ts +1 -0
  24. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  25. package/dist/types/cache/segment-codec.d.ts +10 -3
  26. package/dist/types/cache/shell-memo.d.ts +178 -0
  27. package/dist/types/cache/shell-snapshot.d.ts +110 -21
  28. package/dist/types/cache/tag-invalidation.d.ts +30 -3
  29. package/dist/types/cache/taint.d.ts +1 -1
  30. package/dist/types/cache/types.d.ts +220 -44
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
  32. package/dist/types/client.d.ts +4 -4
  33. package/dist/types/client.rsc.d.ts +4 -5
  34. package/dist/types/components/DefaultDocument.d.ts +5 -2
  35. package/dist/types/context-var.d.ts +6 -5
  36. package/dist/types/escape-script.d.ts +1 -1
  37. package/dist/types/handles/MetaTags.d.ts +5 -4
  38. package/dist/types/handles/Scripts.d.ts +8 -7
  39. package/dist/types/handles/meta.d.ts +2 -2
  40. package/dist/types/handles/script.d.ts +17 -16
  41. package/dist/types/href-client.d.ts +1 -1
  42. package/dist/types/html.d.ts +12 -0
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +3 -2
  45. package/dist/types/loader-store.d.ts +7 -4
  46. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  47. package/dist/types/reverse.d.ts +36 -4
  48. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  49. package/dist/types/route-definition/helpers-types.d.ts +33 -9
  50. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  51. package/dist/types/route-types.d.ts +1 -1
  52. package/dist/types/router/debug-manifest.d.ts +3 -2
  53. package/dist/types/router/error-handling.d.ts +20 -0
  54. package/dist/types/router/handler-context.d.ts +6 -0
  55. package/dist/types/router/instrument.d.ts +8 -0
  56. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  57. package/dist/types/router/middleware-types.d.ts +9 -3
  58. package/dist/types/router/router-context.d.ts +3 -1
  59. package/dist/types/router/router-interfaces.d.ts +24 -2
  60. package/dist/types/router/router-options.d.ts +8 -5
  61. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  62. package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
  63. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  64. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
  65. package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  67. package/dist/types/router/segment-wrappers.d.ts +1 -1
  68. package/dist/types/rsc/full-payload.d.ts +14 -0
  69. package/dist/types/rsc/handler.d.ts +1 -1
  70. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  71. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  72. package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
  73. package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
  74. package/dist/types/rsc/shell-capture.d.ts +151 -19
  75. package/dist/types/rsc/shell-serve.d.ts +140 -17
  76. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  77. package/dist/types/rsc/types.d.ts +17 -0
  78. package/dist/types/server/context.d.ts +139 -15
  79. package/dist/types/server/cookie-store.d.ts +21 -0
  80. package/dist/types/server/handle-store.d.ts +26 -2
  81. package/dist/types/server/request-context.d.ts +182 -53
  82. package/dist/types/ssr/index.d.ts +24 -0
  83. package/dist/types/ssr/ssr-root.d.ts +9 -8
  84. package/dist/types/static-handler.d.ts +4 -1
  85. package/dist/types/testing/flight.entry.d.ts +7 -0
  86. package/dist/types/testing/generated-routes.d.ts +2 -2
  87. package/dist/types/testing/internal/context.d.ts +1 -1
  88. package/dist/types/testing/render-route.d.ts +32 -6
  89. package/dist/types/testing/run-loader.d.ts +18 -3
  90. package/dist/types/testing/serve-shell-request.d.ts +132 -0
  91. package/dist/types/testing/shell-status.d.ts +23 -10
  92. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  93. package/dist/types/testing/vitest.d.ts +35 -1
  94. package/dist/types/theme/ThemeScript.d.ts +3 -3
  95. package/dist/types/types/cache-types.d.ts +14 -10
  96. package/dist/types/types/handler-context.d.ts +28 -1
  97. package/dist/types/types/loader-types.d.ts +8 -6
  98. package/dist/types/urls/pattern-types.d.ts +22 -18
  99. package/dist/types/urls/response-types.d.ts +3 -12
  100. package/dist/types/use-loader.d.ts +12 -3
  101. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  102. package/dist/types/vite/rango.d.ts +1 -1
  103. package/dist/types/vite/router-discovery.d.ts +29 -2
  104. package/dist/vite/index.js +1420 -1348
  105. package/package.json +2 -2
  106. package/skills/api-client/SKILL.md +22 -10
  107. package/skills/breadcrumbs/SKILL.md +75 -22
  108. package/skills/bundle-analysis/SKILL.md +22 -22
  109. package/skills/cache-guide/SKILL.md +306 -124
  110. package/skills/caching/SKILL.md +530 -98
  111. package/skills/catalog.json +16 -16
  112. package/skills/client-urls/SKILL.md +65 -38
  113. package/skills/cloudflare/SKILL.md +87 -20
  114. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  115. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  116. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  117. package/skills/comparison/SKILL.md +5 -0
  118. package/skills/comparison/references/framework-comparison.md +75 -57
  119. package/skills/composability/SKILL.md +38 -29
  120. package/skills/css/SKILL.md +10 -13
  121. package/skills/debug-manifest/SKILL.md +74 -31
  122. package/skills/defer-hydration/SKILL.md +52 -31
  123. package/skills/deployment-caching/SKILL.md +11 -3
  124. package/skills/document-cache/SKILL.md +98 -28
  125. package/skills/fonts/SKILL.md +16 -8
  126. package/skills/handler-use/SKILL.md +55 -17
  127. package/skills/hooks/SKILL.md +28 -20
  128. package/skills/hooks/data.md +23 -3
  129. package/skills/hooks/handle-and-actions.md +34 -17
  130. package/skills/hooks/navigation.md +39 -10
  131. package/skills/hooks/outlets.md +13 -6
  132. package/skills/hooks/state.md +39 -15
  133. package/skills/hooks/urls.md +7 -5
  134. package/skills/host-router/SKILL.md +64 -12
  135. package/skills/i18n/SKILL.md +60 -34
  136. package/skills/intercept/SKILL.md +161 -26
  137. package/skills/layout/SKILL.md +106 -50
  138. package/skills/links/SKILL.md +51 -19
  139. package/skills/loader/SKILL.md +386 -128
  140. package/skills/middleware/SKILL.md +129 -26
  141. package/skills/migrate-nextjs/SKILL.md +156 -70
  142. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  143. package/skills/migrate-react-router/SKILL.md +51 -39
  144. package/skills/migrate-react-router/component-migration.md +28 -7
  145. package/skills/migrate-react-router/data-and-actions.md +21 -6
  146. package/skills/migrate-react-router/route-mapping.md +19 -10
  147. package/skills/mime-routes/SKILL.md +12 -7
  148. package/skills/observability/SKILL.md +92 -14
  149. package/skills/parallel/SKILL.md +88 -47
  150. package/skills/ppr/SKILL.md +866 -379
  151. package/skills/prerender/SKILL.md +210 -108
  152. package/skills/rango/SKILL.md +115 -65
  153. package/skills/react-compiler/SKILL.md +6 -5
  154. package/skills/response-routes/SKILL.md +133 -25
  155. package/skills/route/SKILL.md +170 -60
  156. package/skills/router-setup/SKILL.md +420 -240
  157. package/skills/scripts/SKILL.md +39 -21
  158. package/skills/server-actions/SKILL.md +231 -59
  159. package/skills/shell-manifest/SKILL.md +28 -14
  160. package/skills/streams-and-websockets/SKILL.md +32 -14
  161. package/skills/tailwind/SKILL.md +37 -33
  162. package/skills/testing/SKILL.md +73 -33
  163. package/skills/testing/cache-prerender.md +60 -33
  164. package/skills/testing/client-components.md +59 -6
  165. package/skills/testing/e2e-parity.md +12 -2
  166. package/skills/testing/flight.md +19 -11
  167. package/skills/testing/handles.md +8 -5
  168. package/skills/testing/loader.md +26 -6
  169. package/skills/testing/middleware.md +8 -3
  170. package/skills/testing/predicates.md +120 -0
  171. package/skills/testing/render-handler.md +70 -22
  172. package/skills/testing/response-routes.md +11 -4
  173. package/skills/testing/reverse-and-types.md +20 -14
  174. package/skills/testing/serve-shell-request.md +188 -0
  175. package/skills/testing/server-actions.md +12 -3
  176. package/skills/testing/server-tree.md +9 -0
  177. package/skills/testing/setup.md +9 -5
  178. package/skills/theme/SKILL.md +155 -30
  179. package/skills/typesafety/SKILL.md +20 -9
  180. package/skills/typesafety/env-and-bindings.md +137 -50
  181. package/skills/typesafety/generated-files-and-cli.md +26 -14
  182. package/skills/typesafety/params-and-search.md +44 -19
  183. package/skills/typesafety/route-types.md +50 -14
  184. package/skills/use-cache/SKILL.md +224 -83
  185. package/skills/vercel/SKILL.md +35 -13
  186. package/skills/view-transitions/SKILL.md +42 -21
  187. package/src/__augment-tests__/augmented.check.ts +26 -1
  188. package/src/browser/cookie-name.ts +23 -0
  189. package/src/browser/history-state.ts +23 -0
  190. package/src/browser/index.ts +2 -2
  191. package/src/browser/navigation-bridge.ts +3 -4
  192. package/src/browser/navigation-transaction.ts +22 -2
  193. package/src/browser/partial-update.ts +35 -25
  194. package/src/browser/react/ScrollRestoration.tsx +6 -6
  195. package/src/browser/rsc-router.tsx +6 -1
  196. package/src/browser/scroll-restoration.ts +53 -6
  197. package/src/cache/cache-exec-scope.ts +37 -4
  198. package/src/cache/cache-key-utils.ts +23 -8
  199. package/src/cache/cache-policy.ts +22 -0
  200. package/src/cache/cache-runtime.ts +264 -68
  201. package/src/cache/cache-scope.ts +419 -43
  202. package/src/cache/cache-tag.ts +375 -11
  203. package/src/cache/cf/cf-base64.ts +36 -4
  204. package/src/cache/cf/cf-cache-constants.ts +12 -0
  205. package/src/cache/cf/cf-cache-store.ts +990 -348
  206. package/src/cache/cf/cf-cache-types.ts +32 -4
  207. package/src/cache/cf/cf-shell-frame.ts +258 -0
  208. package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
  209. package/src/cache/document-cache.ts +15 -0
  210. package/src/cache/handle-capture.ts +90 -10
  211. package/src/cache/handle-snapshot.ts +133 -28
  212. package/src/cache/index.ts +2 -0
  213. package/src/cache/isolate-tag-memo.ts +308 -0
  214. package/src/cache/segment-codec.ts +26 -7
  215. package/src/cache/shell-memo.ts +341 -0
  216. package/src/cache/shell-snapshot.ts +217 -27
  217. package/src/cache/tag-invalidation.ts +108 -22
  218. package/src/cache/taint.ts +1 -1
  219. package/src/cache/types.ts +225 -44
  220. package/src/cache/vercel/vercel-cache-store.ts +545 -61
  221. package/src/client.rsc.tsx +10 -9
  222. package/src/client.tsx +5 -4
  223. package/src/components/DefaultDocument.tsx +7 -2
  224. package/src/context-var.ts +6 -5
  225. package/src/escape-script.ts +1 -1
  226. package/src/handles/MetaTags.tsx +13 -11
  227. package/src/handles/Scripts.tsx +11 -10
  228. package/src/handles/meta.ts +2 -2
  229. package/src/handles/script.ts +17 -16
  230. package/src/href-client.ts +1 -1
  231. package/src/html.ts +12 -0
  232. package/src/index.rsc.ts +5 -0
  233. package/src/index.ts +1 -0
  234. package/src/loader-store.ts +7 -4
  235. package/src/prerender/build-shell-capture.ts +73 -22
  236. package/src/reverse.ts +44 -4
  237. package/src/route-definition/dsl-helpers.ts +121 -33
  238. package/src/route-definition/helpers-types.ts +33 -9
  239. package/src/route-definition/resolve-handler-use.ts +121 -6
  240. package/src/route-types.ts +0 -3
  241. package/src/router/debug-manifest.ts +10 -2
  242. package/src/router/error-handling.ts +51 -15
  243. package/src/router/handler-context.ts +71 -28
  244. package/src/router/instrument.ts +54 -2
  245. package/src/router/intercept-resolution.ts +141 -43
  246. package/src/router/loader-resolution.ts +115 -39
  247. package/src/router/match-api.ts +40 -26
  248. package/src/router/match-handlers.ts +5 -0
  249. package/src/router/match-middleware/background-revalidation.ts +161 -96
  250. package/src/router/match-middleware/cache-lookup.ts +110 -10
  251. package/src/router/match-middleware/cache-store.ts +48 -101
  252. package/src/router/middleware-types.ts +9 -6
  253. package/src/router/middleware.ts +75 -13
  254. package/src/router/prerender-match.ts +59 -19
  255. package/src/router/route-snapshot.ts +11 -1
  256. package/src/router/router-context.ts +2 -0
  257. package/src/router/router-interfaces.ts +25 -2
  258. package/src/router/router-options.ts +8 -5
  259. package/src/router/segment-resolution/fresh.ts +62 -21
  260. package/src/router/segment-resolution/helpers.ts +4 -2
  261. package/src/router/segment-resolution/loader-cache.ts +407 -76
  262. package/src/router/segment-resolution/loader-mask.ts +12 -22
  263. package/src/router/segment-resolution/loader-snapshot.ts +208 -56
  264. package/src/router/segment-resolution/mask-nested.ts +138 -44
  265. package/src/router/segment-resolution/revalidation.ts +61 -16
  266. package/src/router/segment-wrappers.ts +4 -0
  267. package/src/rsc/full-payload.ts +25 -1
  268. package/src/rsc/handler.ts +14 -2
  269. package/src/rsc/progressive-enhancement.ts +3 -2
  270. package/src/rsc/render-pipeline.ts +3 -0
  271. package/src/rsc/response-cache-serve.ts +9 -6
  272. package/src/rsc/response-route-handler.ts +11 -3
  273. package/src/rsc/rsc-rendering.ts +608 -225
  274. package/src/rsc/shell-build-manifest.ts +27 -9
  275. package/src/rsc/shell-capture-constants.ts +73 -6
  276. package/src/rsc/shell-capture.ts +1068 -296
  277. package/src/rsc/shell-serve.ts +316 -34
  278. package/src/rsc/ssr-setup.ts +14 -8
  279. package/src/rsc/types.ts +18 -0
  280. package/src/server/context.ts +300 -32
  281. package/src/server/cookie-store.ts +87 -61
  282. package/src/server/handle-store.ts +232 -7
  283. package/src/server/request-context.ts +297 -67
  284. package/src/ssr/index.tsx +136 -44
  285. package/src/ssr/ssr-root.tsx +9 -8
  286. package/src/static-handler.ts +4 -1
  287. package/src/testing/flight-runtime.d.ts +21 -0
  288. package/src/testing/flight.entry.ts +14 -0
  289. package/src/testing/generated-routes.ts +2 -2
  290. package/src/testing/internal/context.ts +1 -1
  291. package/src/testing/internal/flight-client-globals.ts +7 -5
  292. package/src/testing/render-route.tsx +85 -24
  293. package/src/testing/run-loader.ts +68 -6
  294. package/src/testing/serve-shell-request.ts +378 -0
  295. package/src/testing/shell-status.ts +34 -12
  296. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  297. package/src/testing/vitest.ts +62 -1
  298. package/src/theme/ThemeProvider.tsx +7 -6
  299. package/src/theme/ThemeScript.tsx +3 -3
  300. package/src/types/cache-types.ts +14 -10
  301. package/src/types/handler-context.ts +28 -1
  302. package/src/types/loader-types.ts +8 -6
  303. package/src/urls/pattern-types.ts +22 -18
  304. package/src/urls/response-types.ts +3 -23
  305. package/src/use-loader.tsx +12 -3
  306. package/src/vite/discovery/shell-prerender-phase.ts +9 -0
  307. package/src/vite/plugins/use-cache-transform.ts +118 -74
  308. package/src/vite/rango.ts +7 -3
  309. package/src/vite/router-discovery.ts +14 -4
@@ -16,14 +16,18 @@
16
16
  * - transformHoistInlineDirective() for function-level hoisting
17
17
  */
18
18
 
19
- import type { Plugin } from "vite";
19
+ import type { Plugin, Rollup } from "vite";
20
20
  import type { ModuleExportMeta } from "@vitejs/plugin-rsc/transforms";
21
21
  import path from "node:path";
22
- import MagicString from "magic-string";
23
22
  import { normalizePath, hashId } from "./expose-id-utils.js";
24
- import { createRangoDebugger, createCounter, NS } from "../debug.js";
23
+ import type { Counter } from "../debug.js";
25
24
 
26
- const debug = createRangoDebugger(NS.transform);
25
+ // No static import of `magic-string` or `../debug.js` (the `debug` package):
26
+ // `rangoUseCacheTransform()` bundles this module into `dist/testing/vitest.js`,
27
+ // whose static imports are node builtins only, and every package it needs
28
+ // loads lazily, as `rangoUseClientTransform()`'s `vite` and plugin-rsc
29
+ // imports do. src/__tests__/installed-vitest-config.test.ts loads that file
30
+ // from a fixture holding only `package.json` and `dist/`.
27
31
 
28
32
  const CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
29
33
 
@@ -33,12 +37,13 @@ const LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
33
37
 
34
38
  export const USE_CACHE_DIRECTIVE_RE: RegExp = /^use cache(:\s*[\w-]+)?$/;
35
39
 
36
- export function useCacheTransform(): Plugin {
40
+ /**
41
+ * @param counter `rango:transform` timing counter, created by `rango()`
42
+ * (`createCounter` in ../debug.js); omitted when debugging is off.
43
+ */
44
+ export function useCacheTransform(counter?: Counter): Plugin {
37
45
  let projectRoot = "";
38
46
  let isBuild = false;
39
- let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null =
40
- null;
41
- const counter = createCounter(debug, "use-cache");
42
47
 
43
48
  return {
44
49
  name: "@rangojs/router:use-cache",
@@ -56,75 +61,15 @@ export function useCacheTransform(): Plugin {
56
61
  async transform(code, id) {
57
62
  // Only process in RSC environment
58
63
  if (this.environment?.name !== "rsc") return;
59
-
60
- // Quick bail: no "use cache" in source
61
- if (!code.includes("use cache")) return;
62
-
63
- // Skip node_modules and virtual modules
64
- if (id.includes("/node_modules/") || id.startsWith("\0")) return;
65
-
66
- // Only JS/TS files
67
- if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
64
+ if (!isUseCacheCandidate(code, id)) return;
68
65
 
69
66
  const start = counter ? performance.now() : 0;
70
67
  try {
71
- if (!rscTransforms) {
72
- try {
73
- rscTransforms = await import("@vitejs/plugin-rsc/transforms");
74
- } catch {
75
- return;
76
- }
77
- }
78
-
79
- const {
80
- hasDirective,
81
- transformWrapExport,
82
- transformHoistInlineDirective,
83
- } = rscTransforms;
84
-
85
- let ast: any;
86
- try {
87
- const { parseAst } = await import("vite");
88
- ast = parseAst(code, { lang: "tsx" });
89
- } catch {
90
- return;
91
- }
92
-
93
- // plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
94
- // after `"directive" in node`. Vite/oxc parseAst now emits
95
- // `directive: null` on ordinary ExpressionStatements, so a file that
96
- // mixes a `"use cache"` function with a sibling handler whose first
97
- // statement is an expression throws and the wrap is dropped.
98
- stripNullDirectiveFields(ast);
99
-
100
- const filePath = normalizePath(path.relative(projectRoot, id));
101
- const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
102
-
103
- if (hasDirective(ast.body, "use cache")) {
104
- return transformFileLevelUseCache(
105
- code,
106
- ast,
107
- filePath,
108
- id,
109
- isBuild,
110
- isLayoutOrTemplate,
111
- transformWrapExport,
112
- hasDirective,
113
- );
114
- }
115
-
116
- const functionResult = transformFunctionLevelUseCache(
117
- code,
118
- ast,
119
- filePath,
120
- id,
68
+ return await transformUseCache(code, id, {
69
+ root: projectRoot,
121
70
  isBuild,
122
- transformHoistInlineDirective,
123
- );
124
-
125
- warnOnNearMissDirectives(ast, id, this.warn.bind(this));
126
-
127
- if (functionResult) return functionResult;
71
+ warn: (message) => this.warn(message),
72
+ });
128
73
  } finally {
129
74
  counter?.record(id, performance.now() - start);
130
75
  }
@@ -132,7 +77,105 @@ export function useCacheTransform(): Plugin {
132
77
  };
133
78
  }
134
79
 
135
- function transformFileLevelUseCache(
80
+ /** Options for {@link transformUseCache}. */
81
+ export interface UseCacheTransformOptions {
82
+ /** Root the ids are relative to (Vite's `config.root`). */
83
+ root: string;
84
+ /** Build ids hash the path (`hashId`); dev ids are `<path>#<export>`. */
85
+ isBuild: boolean;
86
+ /** Sink for the near-miss directive warning. */
87
+ warn: (message: string) => void;
88
+ /**
89
+ * Called when `@vitejs/plugin-rsc/transforms` fails to load; the module is
90
+ * then left unwrapped. The rango plugin omits it (skip silently).
91
+ */
92
+ onTransformsError?: (error: unknown) => void;
93
+ }
94
+
95
+ /** Whether a module may carry a `"use cache"` directive the transform wraps. */
96
+ export function isUseCacheCandidate(code: string, id: string): boolean {
97
+ return (
98
+ // Quick bail: no "use cache" in source
99
+ code.includes("use cache") &&
100
+ // Skip node_modules and virtual modules
101
+ !id.includes("/node_modules/") &&
102
+ !id.startsWith("\0") &&
103
+ // Only JS/TS files
104
+ /\.(tsx?|jsx?|mjs)$/.test(id)
105
+ );
106
+ }
107
+
108
+ let rscTransforms: typeof import("@vitejs/plugin-rsc/transforms") | null = null;
109
+
110
+ /**
111
+ * Wrap a module's `"use cache"` functions with `registerCachedFunction`. The
112
+ * rango plugin runs it in the rsc environment; `rangoUseCacheTransform()`
113
+ * (testing/vitest.ts) runs it in a Vitest project, whose environment is `ssr`.
114
+ * Call it only for an {@link isUseCacheCandidate} module.
115
+ */
116
+ export async function transformUseCache(
117
+ code: string,
118
+ id: string,
119
+ options: UseCacheTransformOptions,
120
+ ): Promise<{ code: string; map: Rollup.ExistingRawSourceMap } | undefined> {
121
+ if (!rscTransforms) {
122
+ try {
123
+ rscTransforms = await import("@vitejs/plugin-rsc/transforms");
124
+ } catch (error) {
125
+ options.onTransformsError?.(error);
126
+ return;
127
+ }
128
+ }
129
+
130
+ const { hasDirective, transformWrapExport, transformHoistInlineDirective } =
131
+ rscTransforms;
132
+
133
+ let ast: any;
134
+ try {
135
+ const { parseAst } = await import("vite");
136
+ ast = parseAst(code, { lang: "tsx" });
137
+ } catch {
138
+ return;
139
+ }
140
+
141
+ // plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
142
+ // after `"directive" in node`. Vite/oxc parseAst now emits
143
+ // `directive: null` on ordinary ExpressionStatements, so a file that
144
+ // mixes a `"use cache"` function with a sibling handler whose first
145
+ // statement is an expression throws and the wrap is dropped.
146
+ stripNullDirectiveFields(ast);
147
+
148
+ const filePath = normalizePath(path.relative(options.root, id));
149
+ const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
150
+
151
+ if (hasDirective(ast.body, "use cache")) {
152
+ return transformFileLevelUseCache(
153
+ code,
154
+ ast,
155
+ filePath,
156
+ id,
157
+ options.isBuild,
158
+ isLayoutOrTemplate,
159
+ transformWrapExport,
160
+ hasDirective,
161
+ );
162
+ }
163
+
164
+ const functionResult = transformFunctionLevelUseCache(
165
+ code,
166
+ ast,
167
+ filePath,
168
+ id,
169
+ options.isBuild,
170
+ transformHoistInlineDirective,
171
+ );
172
+
173
+ warnOnNearMissDirectives(ast, id, options.warn);
174
+
175
+ if (functionResult) return functionResult;
176
+ }
177
+
178
+ async function transformFileLevelUseCache(
136
179
  code: string,
137
180
  ast: any,
138
181
  filePath: string,
@@ -193,6 +236,7 @@ function transformFileLevelUseCache(
193
236
  }
194
237
 
195
238
  if (exportNames.length === 0) {
239
+ const { default: MagicString } = await import("magic-string");
196
240
  const s = new MagicString(code);
197
241
  const directive = findFileLevelDirective(ast);
198
242
  if (directive) {
package/src/vite/rango.ts CHANGED
@@ -45,7 +45,7 @@ import { createVersionInjectorPlugin } from "./plugins/version-injector.js";
45
45
  import { createCjsToEsmPlugin } from "./plugins/cjs-to-esm.js";
46
46
  import { createRouterDiscoveryPlugin } from "./router-discovery.js";
47
47
  import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
48
- import { createRangoDebugger, NS } from "./debug.js";
48
+ import { createCounter, createRangoDebugger, NS } from "./debug.js";
49
49
 
50
50
  const debugConfig = createRangoDebugger(NS.config);
51
51
 
@@ -100,7 +100,7 @@ export { hasUseClientDirective };
100
100
  * plugins: [
101
101
  * react(),
102
102
  * rango({ preset: 'cloudflare' }),
103
- * cloudflare({ viteEnvironment: { name: 'rsc' } }),
103
+ * cloudflare({ viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'] } }),
104
104
  * ],
105
105
  * });
106
106
  * ```
@@ -565,7 +565,11 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
565
565
  plugins.push(createLoaderDirectiveGuardPlugin());
566
566
  plugins.push(createLoaderScanStubPlugin());
567
567
  plugins.push(exposeActionId());
568
- plugins.push(useCacheTransform());
568
+ plugins.push(
569
+ useCacheTransform(
570
+ createCounter(createRangoDebugger(NS.transform), "use-cache"),
571
+ ),
572
+ );
569
573
  plugins.push(exposeInternalIds());
570
574
  plugins.push(exposeRouterId());
571
575
  plugins.push(createVersionPlugin());
@@ -6,7 +6,7 @@
6
6
  * generation, virtual module codegen, and bundle post-processing.
7
7
  */
8
8
 
9
- import type { Plugin } from "vite";
9
+ import type { Plugin, ViteDevServer } from "vite";
10
10
  import { createServer as createViteServer } from "vite";
11
11
  import { resolve } from "node:path";
12
12
  import { readFileSync } from "node:fs";
@@ -177,8 +177,9 @@ const injectedShellNotReadyPaths = new Set<string>();
177
177
  * - Build: hashed IDs (forceBuild), hashClientRefs for production bundles
178
178
  *
179
179
  * Returns the ViteDevServer instance. Callers access .environments.rsc as needed.
180
+ * @internal Exported for tests.
180
181
  */
181
- async function createTempRscServer(
182
+ export async function createTempRscServer(
182
183
  state: DiscoveryState,
183
184
  options: {
184
185
  forceBuild?: boolean;
@@ -195,7 +196,7 @@ async function createTempRscServer(
195
196
  */
196
197
  realSsrEntry?: boolean;
197
198
  } = {},
198
- ) {
199
+ ): Promise<ViteDevServer> {
199
200
  // Install the Node ESM loader hook before any module evaluation so
200
201
  // `cloudflare:*` specifiers in externalized/loader-delegated modules
201
202
  // (e.g. packages plugin-rsc marks as external) resolve to stubs
@@ -216,7 +217,16 @@ async function createTempRscServer(
216
217
  return createViteServer({
217
218
  root: state.projectRoot,
218
219
  configFile: false,
219
- server: { middlewareMode: true },
220
+ server: {
221
+ middlewareMode: true,
222
+ // Build mode: no file watching. buildStart installs the per-router tries
223
+ // into this realm and runShellPrerenderPhase reuses it after the bundles
224
+ // are written. A watched write in between (writeRouteTypesFiles' own
225
+ // <router>.named-routes.gen.ts, which every router file imports) can
226
+ // make Vite full-reload the RSC runner; the shell phase then matches in
227
+ // a fresh realm with no tries and a root path("/*") wins (#947).
228
+ ...(options.forceBuild ? { watch: null } : {}),
229
+ },
220
230
  appType: "custom",
221
231
  logLevel: "silent",
222
232
  resolve: resolveConfig,