@rangojs/router 0.0.0-experimental.31 → 0.0.0-experimental.3232cd17

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 (376) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +198 -44
  3. package/dist/bin/rango.js +287 -105
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3248 -1117
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +73 -21
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +107 -1
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +245 -21
  12. package/skills/caching/SKILL.md +302 -6
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/document-cache/SKILL.md +78 -55
  16. package/skills/handler-use/SKILL.md +364 -0
  17. package/skills/hooks/SKILL.md +270 -30
  18. package/skills/host-router/SKILL.md +82 -22
  19. package/skills/i18n/SKILL.md +276 -0
  20. package/skills/intercept/SKILL.md +49 -5
  21. package/skills/layout/SKILL.md +35 -9
  22. package/skills/links/SKILL.md +249 -17
  23. package/skills/loader/SKILL.md +294 -30
  24. package/skills/middleware/SKILL.md +52 -13
  25. package/skills/migrate-nextjs/SKILL.md +584 -0
  26. package/skills/migrate-react-router/SKILL.md +769 -0
  27. package/skills/mime-routes/SKILL.md +27 -0
  28. package/skills/observability/SKILL.md +137 -0
  29. package/skills/parallel/SKILL.md +203 -7
  30. package/skills/prerender/SKILL.md +123 -100
  31. package/skills/rango/SKILL.md +250 -22
  32. package/skills/react-compiler/SKILL.md +168 -0
  33. package/skills/response-routes/SKILL.md +122 -47
  34. package/skills/route/SKILL.md +97 -5
  35. package/skills/router-setup/SKILL.md +90 -5
  36. package/skills/server-actions/SKILL.md +775 -0
  37. package/skills/streams-and-websockets/SKILL.md +283 -0
  38. package/skills/tailwind/SKILL.md +27 -3
  39. package/skills/testing/SKILL.md +129 -0
  40. package/skills/testing/bindings.md +89 -0
  41. package/skills/testing/cache-prerender.md +124 -0
  42. package/skills/testing/client-components.md +122 -0
  43. package/skills/testing/e2e-parity.md +125 -0
  44. package/skills/testing/flight.md +92 -0
  45. package/skills/testing/handles.md +129 -0
  46. package/skills/testing/loader.md +128 -0
  47. package/skills/testing/middleware.md +99 -0
  48. package/skills/testing/render-handler.md +121 -0
  49. package/skills/testing/response-routes.md +95 -0
  50. package/skills/testing/reverse-and-types.md +84 -0
  51. package/skills/testing/server-actions.md +107 -0
  52. package/skills/testing/server-tree.md +128 -0
  53. package/skills/testing/setup.md +120 -0
  54. package/skills/typesafety/SKILL.md +329 -27
  55. package/skills/use-cache/SKILL.md +36 -5
  56. package/skills/view-transitions/SKILL.md +294 -0
  57. package/src/__augment-tests__/augment.ts +81 -0
  58. package/src/__augment-tests__/augmented.check.ts +116 -0
  59. package/src/__internal.ts +67 -40
  60. package/src/browser/action-coordinator.ts +53 -36
  61. package/src/browser/action-fence.ts +47 -0
  62. package/src/browser/app-shell.ts +39 -0
  63. package/src/browser/app-version.ts +14 -0
  64. package/src/browser/cookie-name.ts +140 -0
  65. package/src/browser/event-controller.ts +86 -147
  66. package/src/browser/history-state.ts +21 -0
  67. package/src/browser/index.ts +3 -3
  68. package/src/browser/invalidate-client-cache.ts +52 -0
  69. package/src/browser/link-interceptor.ts +4 -0
  70. package/src/browser/navigation-bridge.ts +148 -19
  71. package/src/browser/navigation-client.ts +187 -67
  72. package/src/browser/navigation-store-handle.ts +38 -0
  73. package/src/browser/navigation-store.ts +76 -67
  74. package/src/browser/navigation-transaction.ts +18 -66
  75. package/src/browser/partial-update.ts +123 -94
  76. package/src/browser/prefetch/cache.ts +214 -36
  77. package/src/browser/prefetch/fetch.ts +260 -38
  78. package/src/browser/prefetch/policy.ts +6 -0
  79. package/src/browser/prefetch/queue.ts +126 -20
  80. package/src/browser/prefetch/resource-ready.ts +77 -0
  81. package/src/browser/rango-state.ts +158 -76
  82. package/src/browser/react/Link.tsx +93 -11
  83. package/src/browser/react/NavigationProvider.tsx +115 -34
  84. package/src/browser/react/ScrollRestoration.tsx +10 -6
  85. package/src/browser/react/context.ts +7 -2
  86. package/src/browser/react/filter-segment-order.ts +49 -7
  87. package/src/browser/react/index.ts +0 -48
  88. package/src/browser/react/location-state-shared.ts +166 -8
  89. package/src/browser/react/location-state.ts +39 -14
  90. package/src/browser/react/use-action.ts +6 -15
  91. package/src/browser/react/use-handle.ts +23 -69
  92. package/src/browser/react/use-link-status.ts +0 -4
  93. package/src/browser/react/use-navigation.ts +22 -5
  94. package/src/browser/react/use-params.ts +20 -10
  95. package/src/browser/react/use-reverse.ts +106 -0
  96. package/src/browser/react/use-router.ts +46 -11
  97. package/src/browser/react/use-search-params.ts +0 -5
  98. package/src/browser/react/use-segments.ts +11 -21
  99. package/src/browser/response-adapter.ts +52 -1
  100. package/src/browser/rsc-router.tsx +215 -76
  101. package/src/browser/scroll-restoration.ts +46 -39
  102. package/src/browser/segment-reconciler.ts +36 -9
  103. package/src/browser/segment-structure-assert.ts +2 -2
  104. package/src/browser/server-action-bridge.ts +176 -50
  105. package/src/browser/types.ts +95 -11
  106. package/src/browser/validate-redirect-origin.ts +43 -16
  107. package/src/build/collect-fallback-refs.ts +107 -0
  108. package/src/build/generate-manifest.ts +65 -40
  109. package/src/build/generate-route-types.ts +5 -0
  110. package/src/build/index.ts +8 -2
  111. package/src/build/prefix-tree-utils.ts +123 -0
  112. package/src/build/route-trie.ts +137 -32
  113. package/src/build/route-types/codegen.ts +4 -4
  114. package/src/build/route-types/include-resolution.ts +9 -2
  115. package/src/build/route-types/param-extraction.ts +6 -3
  116. package/src/build/route-types/per-module-writer.ts +7 -4
  117. package/src/build/route-types/router-processing.ts +278 -96
  118. package/src/build/route-types/scan-filter.ts +9 -2
  119. package/src/build/route-types/source-scan.ts +118 -0
  120. package/src/build/runtime-discovery.ts +9 -20
  121. package/src/cache/cache-error.ts +104 -0
  122. package/src/cache/cache-policy.ts +68 -28
  123. package/src/cache/cache-runtime.ts +149 -43
  124. package/src/cache/cache-scope.ts +148 -81
  125. package/src/cache/cache-tag.ts +98 -0
  126. package/src/cache/cf/cf-cache-store.ts +2550 -93
  127. package/src/cache/cf/index.ts +11 -17
  128. package/src/cache/document-cache.ts +78 -27
  129. package/src/cache/handle-snapshot.ts +63 -0
  130. package/src/cache/index.ts +23 -20
  131. package/src/cache/memory-segment-store.ts +136 -37
  132. package/src/cache/profile-registry.ts +6 -30
  133. package/src/cache/read-through-swr.ts +41 -11
  134. package/src/cache/segment-codec.ts +0 -16
  135. package/src/cache/tag-invalidation.ts +230 -0
  136. package/src/cache/taint.ts +55 -0
  137. package/src/cache/types.ts +33 -100
  138. package/src/cache/vercel/index.ts +11 -0
  139. package/src/cache/vercel/vercel-cache-store.ts +799 -0
  140. package/src/client.rsc.tsx +6 -21
  141. package/src/client.tsx +108 -290
  142. package/src/component-utils.ts +19 -0
  143. package/src/context-var.ts +84 -2
  144. package/src/debug.ts +2 -2
  145. package/src/decode-loader-results.ts +36 -0
  146. package/src/defer.ts +196 -0
  147. package/src/deps/ssr.ts +0 -1
  148. package/src/errors.ts +30 -4
  149. package/src/handle.ts +70 -22
  150. package/src/handles/MetaTags.tsx +0 -14
  151. package/src/handles/breadcrumbs.ts +16 -5
  152. package/src/handles/meta.ts +0 -39
  153. package/src/host/cookie-handler.ts +0 -36
  154. package/src/host/errors.ts +0 -24
  155. package/src/host/index.ts +8 -2
  156. package/src/host/pattern-matcher.ts +7 -50
  157. package/src/host/router.ts +107 -99
  158. package/src/host/testing.ts +40 -27
  159. package/src/host/types.ts +37 -4
  160. package/src/host/utils.ts +1 -1
  161. package/src/href-client.ts +137 -22
  162. package/src/index.rsc.ts +52 -26
  163. package/src/index.ts +100 -38
  164. package/src/internal-debug.ts +2 -4
  165. package/src/loader-store.ts +500 -0
  166. package/src/loader.rsc.ts +20 -13
  167. package/src/loader.ts +12 -11
  168. package/src/missing-id-error.ts +68 -0
  169. package/src/network-error-thrower.tsx +1 -6
  170. package/src/outlet-context.ts +1 -1
  171. package/src/outlet-provider.tsx +1 -5
  172. package/src/prerender/param-hash.ts +10 -11
  173. package/src/prerender/store.ts +37 -41
  174. package/src/prerender.ts +198 -82
  175. package/src/redirect-origin.ts +100 -0
  176. package/src/response-utils.ts +37 -0
  177. package/src/reverse.ts +65 -15
  178. package/src/root-error-boundary.tsx +1 -19
  179. package/src/route-content-wrapper.tsx +7 -72
  180. package/src/route-definition/dsl-helpers.ts +437 -274
  181. package/src/route-definition/helper-factories.ts +29 -139
  182. package/src/route-definition/helpers-types.ts +113 -37
  183. package/src/route-definition/index.ts +3 -0
  184. package/src/route-definition/redirect.ts +52 -10
  185. package/src/route-definition/resolve-handler-use.ts +161 -0
  186. package/src/route-definition/use-item-types.ts +32 -0
  187. package/src/route-map-builder.ts +7 -17
  188. package/src/route-types.ts +37 -41
  189. package/src/router/basename.ts +14 -0
  190. package/src/router/content-negotiation.ts +108 -9
  191. package/src/router/error-handling.ts +13 -17
  192. package/src/router/find-match.ts +45 -22
  193. package/src/router/handler-context.ts +83 -41
  194. package/src/router/intercept-resolution.ts +25 -23
  195. package/src/router/lazy-includes.ts +19 -53
  196. package/src/router/loader-resolution.ts +213 -30
  197. package/src/router/logging.ts +5 -8
  198. package/src/router/manifest.ts +49 -45
  199. package/src/router/match-api.ts +121 -205
  200. package/src/router/match-context.ts +0 -22
  201. package/src/router/match-handlers.ts +58 -58
  202. package/src/router/match-middleware/background-revalidation.ts +27 -6
  203. package/src/router/match-middleware/cache-lookup.ts +205 -249
  204. package/src/router/match-middleware/cache-store.ts +45 -32
  205. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  206. package/src/router/match-middleware/segment-resolution.ts +52 -18
  207. package/src/router/match-pipelines.ts +1 -42
  208. package/src/router/match-result.ts +104 -40
  209. package/src/router/metrics.ts +5 -34
  210. package/src/router/middleware-types.ts +13 -142
  211. package/src/router/middleware.ts +173 -143
  212. package/src/router/navigation-snapshot.ts +131 -0
  213. package/src/router/params-util.ts +23 -0
  214. package/src/router/pattern-matching.ts +109 -63
  215. package/src/router/prerender-match.ts +192 -54
  216. package/src/router/preview-match.ts +32 -102
  217. package/src/router/request-classification.ts +276 -0
  218. package/src/router/revalidation.ts +63 -55
  219. package/src/router/route-snapshot.ts +244 -0
  220. package/src/router/router-context.ts +6 -28
  221. package/src/router/router-interfaces.ts +100 -35
  222. package/src/router/router-options.ts +91 -11
  223. package/src/router/router-registry.ts +2 -5
  224. package/src/router/segment-resolution/fresh.ts +242 -75
  225. package/src/router/segment-resolution/helpers.ts +64 -25
  226. package/src/router/segment-resolution/loader-cache.ts +41 -37
  227. package/src/router/segment-resolution/revalidation.ts +456 -372
  228. package/src/router/segment-resolution/static-store.ts +19 -5
  229. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  230. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  231. package/src/router/segment-resolution.ts +4 -1
  232. package/src/router/segment-wrappers.ts +2 -3
  233. package/src/router/state-cookie-name.ts +33 -0
  234. package/src/router/substitute-pattern-params.ts +56 -0
  235. package/src/router/telemetry-otel.ts +0 -20
  236. package/src/router/telemetry.ts +96 -19
  237. package/src/router/timeout.ts +0 -20
  238. package/src/router/trie-matching.ts +91 -46
  239. package/src/router/types.ts +10 -63
  240. package/src/router/url-params.ts +44 -0
  241. package/src/router.ts +134 -43
  242. package/src/rsc/handler-context.ts +3 -2
  243. package/src/rsc/handler.ts +492 -383
  244. package/src/rsc/helpers.ts +162 -46
  245. package/src/rsc/index.ts +1 -1
  246. package/src/rsc/json-route-result.ts +38 -0
  247. package/src/rsc/loader-fetch.ts +23 -3
  248. package/src/rsc/manifest-init.ts +33 -42
  249. package/src/rsc/origin-guard.ts +39 -25
  250. package/src/rsc/progressive-enhancement.ts +30 -3
  251. package/src/rsc/redirect-guard.ts +99 -0
  252. package/src/rsc/response-error.ts +79 -12
  253. package/src/rsc/response-route-handler.ts +90 -63
  254. package/src/rsc/rsc-rendering.ts +56 -54
  255. package/src/rsc/runtime-warnings.ts +23 -10
  256. package/src/rsc/server-action.ts +74 -67
  257. package/src/rsc/ssr-setup.ts +18 -2
  258. package/src/rsc/types.ts +25 -6
  259. package/src/runtime-env.ts +18 -0
  260. package/src/search-params.ts +4 -20
  261. package/src/segment-content-promise.ts +67 -0
  262. package/src/segment-loader-promise.ts +134 -0
  263. package/src/segment-system.tsx +272 -129
  264. package/src/serialize.ts +243 -0
  265. package/src/server/context.ts +309 -61
  266. package/src/server/cookie-store.ts +80 -5
  267. package/src/server/handle-store.ts +26 -24
  268. package/src/server/loader-registry.ts +10 -28
  269. package/src/server/request-context.ts +348 -128
  270. package/src/ssr/index.tsx +23 -15
  271. package/src/static-handler.ts +27 -18
  272. package/src/testing/cache-status.ts +162 -0
  273. package/src/testing/collect-handle.ts +40 -0
  274. package/src/testing/dispatch.ts +618 -0
  275. package/src/testing/dom.entry.ts +22 -0
  276. package/src/testing/e2e/fixture.ts +188 -0
  277. package/src/testing/e2e/index.ts +128 -0
  278. package/src/testing/e2e/matchers.ts +35 -0
  279. package/src/testing/e2e/page-helpers.ts +272 -0
  280. package/src/testing/e2e/parity.ts +387 -0
  281. package/src/testing/e2e/server.ts +195 -0
  282. package/src/testing/flight-matchers.ts +97 -0
  283. package/src/testing/flight-normalize.ts +11 -0
  284. package/src/testing/flight-runtime.d.ts +57 -0
  285. package/src/testing/flight-tree.ts +682 -0
  286. package/src/testing/flight.entry.ts +52 -0
  287. package/src/testing/flight.ts +232 -0
  288. package/src/testing/generated-routes.ts +183 -0
  289. package/src/testing/index.ts +99 -0
  290. package/src/testing/internal/context.ts +348 -0
  291. package/src/testing/internal/flight-client-globals.ts +30 -0
  292. package/src/testing/internal/seed-vars.ts +54 -0
  293. package/src/testing/render-handler.ts +330 -0
  294. package/src/testing/render-route.tsx +566 -0
  295. package/src/testing/run-loader.ts +378 -0
  296. package/src/testing/run-middleware.ts +205 -0
  297. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  298. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  299. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  300. package/src/testing/vitest-stubs/version.ts +5 -0
  301. package/src/testing/vitest.ts +305 -0
  302. package/src/theme/ThemeProvider.tsx +0 -52
  303. package/src/theme/ThemeScript.tsx +0 -6
  304. package/src/theme/constants.ts +0 -12
  305. package/src/theme/index.ts +0 -7
  306. package/src/theme/theme-context.ts +1 -5
  307. package/src/theme/theme-script.ts +0 -14
  308. package/src/theme/use-theme.ts +0 -3
  309. package/src/types/boundaries.ts +0 -35
  310. package/src/types/cache-types.ts +17 -8
  311. package/src/types/error-types.ts +30 -90
  312. package/src/types/global-namespace.ts +54 -41
  313. package/src/types/handler-context.ts +233 -81
  314. package/src/types/index.ts +1 -10
  315. package/src/types/loader-types.ts +44 -15
  316. package/src/types/request-scope.ts +107 -0
  317. package/src/types/route-config.ts +6 -50
  318. package/src/types/route-entry.ts +19 -7
  319. package/src/types/segments.ts +37 -14
  320. package/src/urls/include-helper.ts +33 -70
  321. package/src/urls/index.ts +1 -11
  322. package/src/urls/path-helper-types.ts +58 -11
  323. package/src/urls/path-helper.ts +57 -111
  324. package/src/urls/pattern-types.ts +48 -19
  325. package/src/urls/response-types.ts +25 -22
  326. package/src/urls/type-extraction.ts +58 -139
  327. package/src/urls/urls-function.ts +1 -18
  328. package/src/use-loader.tsx +346 -89
  329. package/src/vite/debug.ts +185 -0
  330. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  331. package/src/vite/discovery/discover-routers.ts +130 -85
  332. package/src/vite/discovery/discovery-errors.ts +194 -0
  333. package/src/vite/discovery/gate-state.ts +171 -0
  334. package/src/vite/discovery/prerender-collection.ts +192 -99
  335. package/src/vite/discovery/route-types-writer.ts +40 -84
  336. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  337. package/src/vite/discovery/state.ts +51 -6
  338. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  339. package/src/vite/index.ts +8 -0
  340. package/src/vite/plugin-types.ts +187 -69
  341. package/src/vite/plugins/cjs-to-esm.ts +8 -18
  342. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  343. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  344. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  345. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  346. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  347. package/src/vite/plugins/expose-action-id.ts +49 -98
  348. package/src/vite/plugins/expose-id-utils.ts +11 -50
  349. package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
  350. package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
  351. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
  352. package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
  353. package/src/vite/plugins/expose-internal-ids.ts +554 -317
  354. package/src/vite/plugins/performance-tracks.ts +89 -0
  355. package/src/vite/plugins/refresh-cmd.ts +89 -27
  356. package/src/vite/plugins/use-cache-transform.ts +73 -83
  357. package/src/vite/plugins/vercel-output.ts +258 -0
  358. package/src/vite/plugins/version-injector.ts +21 -25
  359. package/src/vite/plugins/version-plugin.ts +41 -20
  360. package/src/vite/plugins/virtual-entries.ts +2 -17
  361. package/src/vite/rango.ts +257 -289
  362. package/src/vite/router-discovery.ts +930 -140
  363. package/src/vite/utils/ast-handler-extract.ts +15 -31
  364. package/src/vite/utils/banner.ts +4 -4
  365. package/src/vite/utils/bundle-analysis.ts +10 -15
  366. package/src/vite/utils/client-chunks.ts +184 -0
  367. package/src/vite/utils/forward-user-plugins.ts +171 -0
  368. package/src/vite/utils/manifest-utils.ts +4 -59
  369. package/src/vite/utils/package-resolution.ts +20 -52
  370. package/src/vite/utils/prerender-utils.ts +27 -29
  371. package/src/vite/utils/shared-utils.ts +92 -42
  372. package/src/browser/action-response-classifier.ts +0 -99
  373. package/src/browser/react/use-client-cache.ts +0 -58
  374. package/src/browser/shallow.ts +0 -40
  375. package/src/handles/index.ts +0 -7
  376. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Vercel Build Output (Build Output API v3) emitter for `preset: "vercel"`.
3
+ *
4
+ * After the full app build, restructures dist/ into .vercel/output:
5
+ *
6
+ * .vercel/output/
7
+ * config.json routing: static first, else the function
8
+ * static/ dist/client (browser assets, served at /)
9
+ * functions/<name>.func/
10
+ * .vc-config.json Node serverless, response streaming
11
+ * index.mjs bundled launcher (srvx + @vercel/functions)
12
+ * rsc/ dist/rsc (self-contained RSC server bundle)
13
+ * ssr/ dist/ssr (rsc imports ../ssr/index.js)
14
+ *
15
+ * A prebuilt .vercel/output gets no `npm install`, so everything the function
16
+ * imports must physically live inside the .func directory. This relies on two
17
+ * things the vercel preset arranges (each is a failure only a real deploy — or
18
+ * the isolated smoke test — catches, since a local in-place run is masked by the
19
+ * app's own package.json + node_modules up the tree):
20
+ *
21
+ * 1. The rsc/ssr builds are fully bundled (`resolve.noExternal`, set in
22
+ * rango.ts for this preset). The node default externalizes node_modules
23
+ * deps, which works under `vite preview` but leaves bare imports
24
+ * (@vercel/functions, react-dom/server.edge, ...) that have no node_modules
25
+ * to resolve against on Vercel.
26
+ * 2. A `package.json` with `"type": "module"` is written into the .func dir.
27
+ * The rsc/ssr bundles are ESM but use a `.js` extension; without a
28
+ * type:module in scope the deployed (isolated) function loads them as
29
+ * CommonJS and fails on the first `import`.
30
+ *
31
+ * The launcher is bundled with srvx (the Web->Node streaming bridge, a
32
+ * @rangojs/router dependency) and @vercel/functions (resolved from the app)
33
+ * inlined, keeping the RSC bundle a runtime-relative external.
34
+ *
35
+ * Timing: this runs in the `buildApp` hook (order "post"), which fires once
36
+ * after every environment has built, so dist/{client,rsc,ssr} all exist.
37
+ * closeBundle is unusable here — it fires per environment, and twice for ssr
38
+ * (the server-reference scan and the real build), so it would run before
39
+ * dist/client exists. rango's own prerender/static emitters hardcode dist/rsc,
40
+ * so we build to dist/ and restructure here rather than retargeting outDir.
41
+ */
42
+
43
+ import type { Plugin } from "vite";
44
+ import { rm, mkdir, cp, writeFile } from "node:fs/promises";
45
+ import { existsSync } from "node:fs";
46
+ import { resolve, join } from "node:path";
47
+ import { createRequire } from "node:module";
48
+ import { pathToFileURL } from "node:url";
49
+ import type { RangoVercelOptions } from "../plugin-types.js";
50
+
51
+ // Minimal structural types for the esbuild API we use, resolved dynamically from
52
+ // the app so @rangojs/router does not depend on esbuild's type package.
53
+ interface EsbuildPluginBuild {
54
+ onResolve(
55
+ options: { filter: RegExp },
56
+ callback: () => { path: string; external: boolean },
57
+ ): void;
58
+ }
59
+ type EsbuildBuild = (options: Record<string, unknown>) => Promise<unknown>;
60
+ interface EsbuildModule {
61
+ build?: EsbuildBuild;
62
+ default?: { build?: EsbuildBuild };
63
+ }
64
+
65
+ const LAUNCHER_SOURCE = `import { toNodeHandler } from "srvx/node";
66
+ import { waitUntil } from "@vercel/functions";
67
+ import rscHandler from "./rsc/index.js";
68
+
69
+ // The Vercel Node launcher invokes a Node (req, res) handler, not a Web fetch
70
+ // handler. srvx's toNodeHandler bridges the Rango Web fetch handler and pipes
71
+ // the streamed Response to the Node response (set supportsResponseStreaming).
72
+ const onVercel = Boolean(process.env.VERCEL);
73
+
74
+ const fetchHandler = (request) =>
75
+ rscHandler(request, {
76
+ env: process.env,
77
+ // Forward Vercel's waitUntil so cache writes / revalidation run off the
78
+ // response path. Omitted off-platform so those writes settle inline.
79
+ ctx: onVercel ? { waitUntil } : undefined,
80
+ });
81
+
82
+ export default toNodeHandler(fetchHandler);
83
+ `;
84
+
85
+ async function assemble(
86
+ root: string,
87
+ options: RangoVercelOptions,
88
+ ): Promise<void> {
89
+ const dist = join(root, "dist");
90
+ for (const dir of ["client", "rsc", "ssr"]) {
91
+ if (!existsSync(join(dist, dir))) {
92
+ throw new Error(
93
+ `[rango] preset "vercel": missing dist/${dir}. Run the production build first.`,
94
+ );
95
+ }
96
+ }
97
+
98
+ const vercel = options.vercel ?? {};
99
+ const functionName = vercel.functionName ?? "index";
100
+ const out = join(root, ".vercel", "output");
101
+ const funcDir = join(out, "functions", `${functionName}.func`);
102
+
103
+ await rm(out, { recursive: true, force: true });
104
+ await mkdir(funcDir, { recursive: true });
105
+
106
+ // 1. Static client assets -> served from the CDN at the root.
107
+ await cp(join(dist, "client"), join(out, "static"), { recursive: true });
108
+
109
+ // 2. Server bundle into the function. Preserve the rsc -> ../ssr/index.js
110
+ // relative import by mirroring the dist/{rsc,ssr} layout inside the func.
111
+ await cp(join(dist, "rsc"), join(funcDir, "rsc"), { recursive: true });
112
+ await cp(join(dist, "ssr"), join(funcDir, "ssr"), { recursive: true });
113
+
114
+ // Prerender/static payload manifests (present only when routes are prerendered).
115
+ if (existsSync(join(dist, "static"))) {
116
+ await cp(join(dist, "static"), join(funcDir, "static"), {
117
+ recursive: true,
118
+ });
119
+ }
120
+
121
+ // 3. Bundle the Node launcher. srvx (a @rangojs/router dependency) is aliased
122
+ // to its resolved path; @vercel/functions resolves from the app; the RSC
123
+ // server bundle stays a runtime-relative external.
124
+ const rangoRequire = createRequire(import.meta.url);
125
+ let srvxNodePath: string;
126
+ try {
127
+ srvxNodePath = rangoRequire.resolve("srvx/node");
128
+ } catch {
129
+ throw new Error(
130
+ '[rango] preset "vercel" requires "srvx" (a dependency of @rangojs/router). Reinstall dependencies.',
131
+ );
132
+ }
133
+
134
+ // esbuild ships with Vite; resolve it from the app so we never add it as a
135
+ // @rangojs/router dependency. Minimal structural types avoid coupling to
136
+ // esbuild's type package at compile time.
137
+ const appRequire = createRequire(join(root, "package.json"));
138
+ let esbuildModule: EsbuildModule;
139
+ try {
140
+ esbuildModule = (await import(
141
+ pathToFileURL(appRequire.resolve("esbuild")).href
142
+ )) as EsbuildModule;
143
+ } catch {
144
+ throw new Error(
145
+ '[rango] preset "vercel" requires "esbuild" to bundle the function launcher. It ships with Vite; reinstall dependencies.',
146
+ );
147
+ }
148
+ const esbuildBuild = esbuildModule.build ?? esbuildModule.default?.build;
149
+ if (typeof esbuildBuild !== "function") {
150
+ throw new Error('[rango] preset "vercel": could not load esbuild.build.');
151
+ }
152
+
153
+ try {
154
+ await esbuildBuild({
155
+ stdin: {
156
+ contents: LAUNCHER_SOURCE,
157
+ resolveDir: root,
158
+ sourcefile: "func-entry.mjs",
159
+ loader: "js",
160
+ },
161
+ outfile: join(funcDir, "index.mjs"),
162
+ bundle: true,
163
+ format: "esm",
164
+ platform: "node",
165
+ target: "node18",
166
+ alias: { "srvx/node": srvxNodePath },
167
+ plugins: [
168
+ {
169
+ name: "external-rsc-entry",
170
+ setup(b: EsbuildPluginBuild) {
171
+ b.onResolve({ filter: /^\.\/rsc\/index\.js$/ }, () => ({
172
+ path: "./rsc/index.js",
173
+ external: true,
174
+ }));
175
+ },
176
+ },
177
+ ],
178
+ });
179
+ } catch (error) {
180
+ const message = error instanceof Error ? error.message : String(error);
181
+ if (/@vercel\/functions/.test(message)) {
182
+ throw new Error(
183
+ '[rango] preset "vercel": could not resolve "@vercel/functions". Add it to your app dependencies (it also backs VercelCacheStore).\n' +
184
+ message,
185
+ );
186
+ }
187
+ throw error;
188
+ }
189
+
190
+ // 3b. Mark the function as ESM. The rsc/ssr bundles are .js ESM files with no
191
+ // package.json in scope on the deployed function (it is isolated at
192
+ // /var/task), so Node would load them as CommonJS and fail on `import`.
193
+ // Locally this is masked because the func inherits the app's
194
+ // "type": "module" up the tree; the deployed func has nothing above it.
195
+ await writeFile(
196
+ join(funcDir, "package.json"),
197
+ JSON.stringify({ type: "module" }, null, 2) + "\n",
198
+ );
199
+
200
+ // 4. Function config: Node serverless with response streaming.
201
+ const vcConfig: Record<string, unknown> = {
202
+ runtime: vercel.runtime ?? "nodejs22.x",
203
+ handler: "index.mjs",
204
+ launcherType: "Nodejs",
205
+ shouldAddHelpers: false,
206
+ supportsResponseStreaming: true,
207
+ maxDuration: vercel.maxDuration ?? 30,
208
+ };
209
+ if (vercel.memory != null) vcConfig.memory = vercel.memory;
210
+ if (vercel.regions != null) vcConfig.regions = vercel.regions;
211
+ await writeFile(
212
+ join(funcDir, ".vc-config.json"),
213
+ JSON.stringify(vcConfig, null, 2) + "\n",
214
+ );
215
+
216
+ // 5. Routing: filesystem (static/) first, then everything to the function.
217
+ await writeFile(
218
+ join(out, "config.json"),
219
+ JSON.stringify(
220
+ {
221
+ version: 3,
222
+ routes: [
223
+ { handle: "filesystem" },
224
+ { src: "/(.*)", dest: `/${functionName}` },
225
+ ],
226
+ },
227
+ null,
228
+ 2,
229
+ ) + "\n",
230
+ );
231
+
232
+ console.log(
233
+ `[rango] assembled .vercel/output (function: ${functionName}.func)`,
234
+ );
235
+ }
236
+
237
+ export function createVercelOutputPlugin(options: RangoVercelOptions): Plugin {
238
+ let root = process.cwd();
239
+ let isBuild = false;
240
+ return {
241
+ name: "@rangojs/router:vercel-output",
242
+ configResolved(config) {
243
+ root = resolve(config.root);
244
+ isBuild = config.command === "build";
245
+ },
246
+ // buildApp runs once after the whole multi-environment build (rsc, client,
247
+ // ssr), so dist/ is complete here. closeBundle is unusable for this: it
248
+ // fires per environment, and twice for ssr (the server-reference scan and
249
+ // the real build), so it would run before dist/client exists.
250
+ buildApp: {
251
+ order: "post",
252
+ async handler() {
253
+ if (!isBuild) return;
254
+ await assemble(root, options);
255
+ },
256
+ },
257
+ };
258
+ }
@@ -1,6 +1,7 @@
1
1
  import type { Plugin } from "vite";
2
2
  import { resolve } from "node:path";
3
3
  import * as Vite from "vite";
4
+ import { resolveRscEntryFromConfig } from "../utils/shared-utils.js";
4
5
 
5
6
  /**
6
7
  * Plugin that auto-injects VERSION and routes-manifest into custom entry.rsc files.
@@ -20,18 +21,7 @@ export function createVersionInjectorPlugin(
20
21
 
21
22
  configResolved(config) {
22
23
  let entryPath = rscEntryPath;
23
- // Cloudflare preset: read entry from resolved environment config.
24
- // The @cloudflare/vite-plugin reads wrangler config (toml/json/jsonc)
25
- // and sets optimizeDeps.entries on the RSC environment.
26
- if (!entryPath) {
27
- const rscEnvConfig = (config.environments as any)?.["rsc"];
28
- const entries = rscEnvConfig?.optimizeDeps?.entries;
29
- if (typeof entries === "string") {
30
- entryPath = entries;
31
- } else if (Array.isArray(entries) && entries.length > 0) {
32
- entryPath = entries[0];
33
- }
34
- }
24
+ if (!entryPath) entryPath = resolveRscEntryFromConfig(config);
35
25
  if (entryPath) {
36
26
  resolvedEntryPath = resolve(config.root, entryPath);
37
27
  }
@@ -39,7 +29,6 @@ export function createVersionInjectorPlugin(
39
29
 
40
30
  transform(code, id) {
41
31
  if (!resolvedEntryPath) return null;
42
- // Only transform the RSC entry file
43
32
  const normalizedId = Vite.normalizePath(id);
44
33
  const normalizedEntry = Vite.normalizePath(resolvedEntryPath);
45
34
 
@@ -47,16 +36,11 @@ export function createVersionInjectorPlugin(
47
36
  return null;
48
37
  }
49
38
 
50
- // Prepend imports at the top of the file. ES imports are hoisted
51
- // by the module system, so source position is irrelevant.
52
- const prepend: string[] = [];
53
- let newCode = code;
54
-
55
- if (!code.includes("virtual:rsc-router/routes-manifest")) {
56
- prepend.push(`import "virtual:rsc-router/routes-manifest";`);
57
- }
39
+ const prepend: string[] = [
40
+ `import "virtual:rsc-router/routes-manifest";`,
41
+ ];
58
42
 
59
- // Auto-inject VERSION if file uses createRSCHandler without version
43
+ let newCode = code;
60
44
  const needsVersion =
61
45
  code.includes("createRSCHandler") &&
62
46
  !code.includes("@rangojs/router:version") &&
@@ -70,9 +54,21 @@ export function createVersionInjectorPlugin(
70
54
  );
71
55
  }
72
56
 
73
- if (prepend.length === 0 && newCode === code) return null;
74
-
75
- newCode = prepend.join("\n") + (prepend.length > 0 ? "\n" : "") + newCode;
57
+ const lines = newCode.split("\n");
58
+ let insertAt = 0;
59
+ while (insertAt < lines.length) {
60
+ const trimmed = lines[insertAt]!.trim();
61
+ if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
62
+ insertAt++;
63
+ } else {
64
+ break;
65
+ }
66
+ }
67
+ newCode = [
68
+ ...lines.slice(0, insertAt),
69
+ ...prepend,
70
+ ...lines.slice(insertAt),
71
+ ].join("\n");
76
72
 
77
73
  return {
78
74
  code: newCode,
@@ -18,7 +18,7 @@ function getClientModuleSignature(
18
18
  ): ClientModuleSignature | undefined {
19
19
  let program: any;
20
20
  try {
21
- program = parseAst(source, { jsx: true });
21
+ program = parseAst(source, { lang: "tsx" });
22
22
  } catch {
23
23
  return undefined;
24
24
  }
@@ -133,11 +133,13 @@ export function createVersionPlugin(): Plugin {
133
133
  let currentVersion = buildVersion;
134
134
  let isDev = false;
135
135
  let server: any = null;
136
+ let resolvedCacheDir: string | undefined;
136
137
  const clientModuleSignatures = new Map<string, ClientModuleSignature>();
137
138
 
139
+ let versionCounter = 0;
138
140
  const bumpVersion = (reason: string) => {
139
- currentVersion = Date.now().toString(16);
140
- console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
141
+ currentVersion = Date.now().toString(16) + String(++versionCounter);
142
+ console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
141
143
 
142
144
  const rscEnv = server?.environments?.rsc;
143
145
  const versionMod = rscEnv?.moduleGraph?.getModuleById(
@@ -154,6 +156,9 @@ export function createVersionPlugin(): Plugin {
154
156
 
155
157
  configResolved(config) {
156
158
  isDev = config.command === "serve";
159
+ resolvedCacheDir = config.cacheDir
160
+ ? String(config.cacheDir).replace(/\\/g, "/")
161
+ : undefined;
157
162
  },
158
163
 
159
164
  configureServer(devServer) {
@@ -200,17 +205,22 @@ export function createVersionPlugin(): Plugin {
200
205
  return null;
201
206
  },
202
207
 
203
- // Track RSC module changes and update version
204
208
  async hotUpdate(ctx) {
205
209
  if (!isDev) return;
206
210
 
207
- // Check if this is an RSC environment update (not client/ssr)
208
- // RSC modules affect server-rendered content and cached payloads
209
- // In Vite 6, environment is accessed via `this.environment`
210
211
  const isRscModule = this.environment?.name === "rsc";
211
212
 
212
213
  if (!isRscModule) return;
213
214
 
215
+ if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
216
+
217
+ if (
218
+ ctx.modules.length === 1 &&
219
+ ctx.modules[0].id === "\0" + VIRTUAL_IDS.version
220
+ ) {
221
+ return;
222
+ }
223
+
214
224
  if (isCodeModule(ctx.file)) {
215
225
  const filePath = normalizeModuleId(ctx.file);
216
226
  const previousSignature = clientModuleSignatures.get(filePath);
@@ -218,9 +228,6 @@ export function createVersionPlugin(): Plugin {
218
228
  const source = await ctx.read();
219
229
  const nextSignature = getClientModuleSignature(source);
220
230
  if (nextSignature) {
221
- // "use client" file — compare export signatures.
222
- // client-component-hmr may have cleared ctx.modules, so we
223
- // cannot rely on ctx.modules.length for these files.
224
231
  clientModuleSignatures.set(filePath, nextSignature);
225
232
  if (
226
233
  previousSignature &&
@@ -231,20 +238,11 @@ export function createVersionPlugin(): Plugin {
231
238
  } else {
232
239
  clientModuleSignatures.delete(filePath);
233
240
  if (!previousSignature) {
234
- // Not and never was "use client" — use module graph check.
235
- // ctx.modules is reliable for pure server files (only
236
- // client-component-hmr clears it for "use client" modules).
237
241
  if (ctx.modules.length === 0) return;
238
242
  }
239
- // Was "use client" but directive removed — boundary changed,
240
- // bump below.
241
243
  }
242
- } catch {
243
- // Fail open: if we can't read or parse the update, invalidate.
244
- }
244
+ } catch {}
245
245
  } else {
246
- // Non-code file (json, css, etc.) — only bump if it's actually
247
- // referenced by the RSC module graph.
248
246
  if (ctx.modules.length === 0) return;
249
247
  }
250
248
 
@@ -252,3 +250,26 @@ export function createVersionPlugin(): Plugin {
252
250
  },
253
251
  };
254
252
  }
253
+
254
+ export function isViteDepCachePath(
255
+ filePath: string | undefined,
256
+ cacheDir?: string,
257
+ ): boolean {
258
+ if (!filePath) return false;
259
+ const normalized = filePath.replace(/\\/g, "/");
260
+
261
+ if (cacheDir) {
262
+ const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
263
+ if (
264
+ normalized === normalizedCacheDir ||
265
+ normalized.startsWith(normalizedCacheDir + "/")
266
+ ) {
267
+ return true;
268
+ }
269
+ }
270
+
271
+ return (
272
+ /\/node_modules\/\.vite[^/]*\//.test(normalized) ||
273
+ normalized.includes("/.vite-isolated/")
274
+ );
275
+ }
@@ -1,8 +1,3 @@
1
- /**
2
- * Default virtual entry file contents for rsc-router.
3
- * These are used when users don't provide their own entry files.
4
- */
5
-
6
1
  export const VIRTUAL_ENTRY_BROWSER: string = `
7
2
  import {
8
3
  createFromReadableStream,
@@ -14,7 +9,7 @@ import {
14
9
  import { createElement, StrictMode } from "react";
15
10
  import { hydrateRoot } from "react-dom/client";
16
11
  import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
17
- import { initBrowserApp, RSCRouter } from "@rangojs/router/browser";
12
+ import { initBrowserApp, Rango } from "@rangojs/router/browser";
18
13
 
19
14
  async function initializeApp() {
20
15
  const deps = {
@@ -29,7 +24,7 @@ async function initializeApp() {
29
24
 
30
25
  hydrateRoot(
31
26
  document,
32
- createElement(StrictMode, null, createElement(RSCRouter))
27
+ createElement(StrictMode, null, createElement(Rango))
33
28
  );
34
29
  }
35
30
 
@@ -51,9 +46,6 @@ export const renderHTML = createSSRHandler({
51
46
  });
52
47
  `.trim();
53
48
 
54
- /**
55
- * Generate the RSC entry content with the specified router path
56
- */
57
49
  export function getVirtualEntryRSC(routerPath: string): string {
58
50
  return `
59
51
  import {
@@ -104,9 +96,6 @@ export default function handler(request, env) {
104
96
  `.trim();
105
97
  }
106
98
 
107
- /**
108
- * Virtual module IDs
109
- */
110
99
  export const VIRTUAL_IDS = {
111
100
  browser: "virtual:rsc-router/entry.browser.js",
112
101
  ssr: "virtual:rsc-router/entry.ssr.js",
@@ -114,10 +103,6 @@ export const VIRTUAL_IDS = {
114
103
  version: "@rangojs/router:version",
115
104
  } as const;
116
105
 
117
- /**
118
- * Virtual module content for version.
119
- * Exports VERSION - a timestamp that changes on server restart (dev) or at build time (production).
120
- */
121
106
  export function getVirtualVersionContent(version: string): string {
122
107
  return `export const VERSION = ${JSON.stringify(version)};`;
123
108
  }