@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
package/src/vite/rango.ts CHANGED
@@ -12,27 +12,31 @@ import { VIRTUAL_IDS } from "./plugins/virtual-entries.js";
12
12
  import {
13
13
  getExcludeDeps,
14
14
  getPackageAliases,
15
+ getPublishedPackageName,
16
+ getVendorAliases,
15
17
  } from "./utils/package-resolution.js";
16
- import {
17
- createScanFilter,
18
- findRouterFiles,
19
- } from "../build/generate-route-types.js";
18
+ import { findRouterFiles } from "../build/generate-route-types.js";
20
19
  import { createVersionPlugin } from "./plugins/version-plugin.js";
21
20
  import {
22
- sharedEsbuildOptions,
21
+ sharedRolldownOptions,
23
22
  createVirtualEntriesPlugin,
24
23
  onwarn,
25
24
  getManualChunks,
26
25
  } from "./utils/shared-utils.js";
27
- import type {
28
- RangoOptions,
29
- RangoNodeOptions,
30
- RscPluginOptions,
31
- } from "./plugin-types.js";
26
+ import {
27
+ resolveClientChunks,
28
+ type ClientChunkContext,
29
+ } from "./utils/client-chunks.js";
30
+ import type { RangoOptions, RangoVercelOptions } from "./plugin-types.js";
31
+ import { createVercelOutputPlugin } from "./plugins/vercel-output.js";
32
32
  import { printBanner, rangoVersion } from "./utils/banner.js";
33
33
  import { createVersionInjectorPlugin } from "./plugins/version-injector.js";
34
34
  import { createCjsToEsmPlugin } from "./plugins/cjs-to-esm.js";
35
35
  import { createRouterDiscoveryPlugin } from "./router-discovery.js";
36
+ import { performanceTracksPlugin } from "./plugins/performance-tracks.js";
37
+ import { createRangoDebugger, NS } from "./debug.js";
38
+
39
+ const debugConfig = createRangoDebugger(NS.config);
36
40
 
37
41
  /**
38
42
  * Vite plugin for @rangojs/router.
@@ -43,7 +47,7 @@ import { createRouterDiscoveryPlugin } from "./router-discovery.js";
43
47
  * @example Node.js (default)
44
48
  * ```ts
45
49
  * export default defineConfig({
46
- * plugins: [react(), rango({ router: './src/router.tsx' })],
50
+ * plugins: [react(), rango()],
47
51
  * });
48
52
  * ```
49
53
  *
@@ -59,18 +63,46 @@ import { createRouterDiscoveryPlugin } from "./router-discovery.js";
59
63
  * ```
60
64
  */
61
65
  export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
66
+ const rangoStart = performance.now();
62
67
  const resolvedOptions: RangoOptions = options ?? { preset: "node" };
63
68
  const preset = resolvedOptions.preset ?? "node";
64
69
  const showBanner = resolvedOptions.banner ?? true;
70
+ const clientChunksOption = resolvedOptions.clientChunks ?? true;
71
+ const useBuiltInClientChunks = clientChunksOption === true;
72
+ const clientChunkCtx: ClientChunkContext | undefined = useBuiltInClientChunks
73
+ ? { fallbackRefs: new Set<string>() }
74
+ : undefined;
75
+ const clientChunks = resolveClientChunks(clientChunksOption, clientChunkCtx);
76
+ debugConfig?.("rango(%s) setup start", preset);
65
77
 
66
78
  const plugins: PluginOption[] = [];
67
79
 
68
- // Get package resolution info (workspace vs npm install)
69
- const rangoAliases = getPackageAliases();
70
- const excludeDeps = getExcludeDeps();
71
-
72
- // Track RSC entry path for version injection
73
- let rscEntryPath: string | null = null;
80
+ // Get package resolution info (workspace vs npm install).
81
+ // Vendor aliases redirect the bare plugin-rsc vendor specs (which plugin-rsc
82
+ // itself injects into optimizeDeps.include) to absolute paths resolved from
83
+ // this package — so strict-pnpm consumers don't hit "Failed to resolve
84
+ // dependency" warnings when those deps aren't hoisted to their app root.
85
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
86
+ const excludeDeps = [
87
+ ...getExcludeDeps(),
88
+ // plugin-rsc itself injects these into the client env's
89
+ // optimizeDeps.include, which overrides exclude for the dep's own
90
+ // pre-bundle entry. What exclude still controls is how *other*
91
+ // pre-bundled deps treat imports of these specs (external vs inlined)
92
+ // via esbuildCjsExternalPlugin. The cjs-to-esm transform in
93
+ // plugins/cjs-to-esm.ts is the fallback for strict-pnpm consumers,
94
+ // where client.browser's bare include fails to resolve and Vite ends up
95
+ // serving the raw CJS file at dev-serve time.
96
+ "@vitejs/plugin-rsc/browser",
97
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
98
+ ];
99
+
100
+ // Vite supports a nested `A > B` syntax in optimizeDeps.include that resolves
101
+ // B from A's location. We anchor transitive deps (rsc-html-stream,
102
+ // @vitejs/plugin-rsc/vendor/*) to @rangojs/router so pnpm consumers — where
103
+ // these aren't visible at the app root — can still pre-bundle them.
104
+ const pkg = getPublishedPackageName();
105
+ const nested = (spec: string) => `${pkg} > ${spec}`;
74
106
 
75
107
  // Mutable ref for router path (node preset only).
76
108
  // Set immediately when user-specified, or populated by the auto-discover
@@ -82,14 +114,8 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
82
114
  const prerenderEnabled = true;
83
115
 
84
116
  if (preset === "cloudflare") {
85
- // Cloudflare preset: configure entries for cloudflare worker setup
86
- // Router is not needed here - worker.rsc.tsx imports it directly
87
-
88
- // Dynamically import @vitejs/plugin-rsc
89
117
  const { default: rsc } = await import("@vitejs/plugin-rsc");
90
118
 
91
- // Only client and ssr entries - rsc entry is handled by cloudflare plugin
92
- // Always use virtual modules for cloudflare preset
93
119
  const finalEntries: { client: string; ssr: string } = {
94
120
  client: VIRTUAL_IDS.browser,
95
121
  ssr: VIRTUAL_IDS.ssr,
@@ -100,16 +126,21 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
100
126
  enforce: "pre",
101
127
 
102
128
  config() {
103
- // Configure environments for cloudflare deployment
104
129
  return {
105
- // Exclude rsc-router modules from optimization to prevent module duplication
106
- // This ensures the same Context instance is used by both browser entry and RSC proxy modules
107
130
  optimizeDeps: {
108
131
  exclude: excludeDeps,
109
- esbuildOptions: sharedEsbuildOptions,
132
+ rolldownOptions: sharedRolldownOptions,
110
133
  },
111
134
  resolve: {
112
135
  alias: rangoAliases,
136
+ // Force a single React/React-DOM copy across all three RSC
137
+ // environments. RSC requires exactly one react/react-dom instance
138
+ // per environment runtime; consumer install topologies (pnpm
139
+ // strict layout, experimental React pins, third-party "use client"
140
+ // packages) can otherwise resolve duplicate copies, causing
141
+ // "Invalid hook call" / lost context. Child environments inherit
142
+ // this root dedupe, and Vite merges it with any consumer dedupe.
143
+ dedupe: ["react", "react-dom"],
113
144
  },
114
145
  build: {
115
146
  rollupOptions: { onwarn },
@@ -118,30 +149,22 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
118
149
  client: {
119
150
  build: {
120
151
  rollupOptions: {
152
+ onwarn,
121
153
  output: {
122
154
  manualChunks: getManualChunks,
123
155
  },
124
156
  },
125
157
  },
126
- // Pre-bundle rsc-html-stream to prevent discovery during first request
127
- // Exclude rsc-router modules to ensure same Context instance
128
158
  optimizeDeps: {
129
- include: ["rsc-html-stream/client"],
159
+ include: [nested("rsc-html-stream/client")],
130
160
  exclude: excludeDeps,
131
- esbuildOptions: sharedEsbuildOptions,
161
+ rolldownOptions: sharedRolldownOptions,
132
162
  },
133
163
  },
134
164
  ssr: {
135
- // Build SSR inside RSC directory so wrangler can deploy self-contained dist/rsc
136
165
  build: {
137
166
  outDir: "./dist/rsc/ssr",
138
167
  },
139
- resolve: {
140
- // Ensure single React instance in SSR child environment
141
- dedupe: ["react", "react-dom"],
142
- },
143
- // Pre-bundle SSR entry and React for proper module linking with childEnvironments
144
- // All deps must be listed to avoid late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
145
168
  optimizeDeps: {
146
169
  entries: [finalEntries.ssr],
147
170
  include: [
@@ -151,26 +174,27 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
151
174
  "react-dom/static.edge",
152
175
  "react/jsx-runtime",
153
176
  "react/jsx-dev-runtime",
154
- "rsc-html-stream/server",
155
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
177
+ nested("rsc-html-stream/server"),
178
+ nested(
179
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
180
+ ),
156
181
  ],
157
182
  exclude: excludeDeps,
158
- esbuildOptions: sharedEsbuildOptions,
183
+ rolldownOptions: sharedRolldownOptions,
159
184
  },
160
185
  },
161
186
  rsc: {
162
- // RSC environment needs exclude list and esbuild options
163
- // Exclude rsc-router modules to prevent createContext in RSC environment
164
187
  optimizeDeps: {
165
- // Pre-bundle all RSC deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
166
188
  include: [
167
189
  "react",
168
190
  "react/jsx-runtime",
169
191
  "react/jsx-dev-runtime",
170
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
192
+ nested(
193
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
194
+ ),
171
195
  ],
172
196
  exclude: excludeDeps,
173
- esbuildOptions: sharedEsbuildOptions,
197
+ rolldownOptions: sharedRolldownOptions,
174
198
  },
175
199
  },
176
200
  },
@@ -191,232 +215,195 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
191
215
  });
192
216
 
193
217
  plugins.push(createVirtualEntriesPlugin(finalEntries));
194
-
195
- // Add RSC plugin with cloudflare-specific options
196
- // Note: loadModuleDevProxy should NOT be used with childEnvironments
197
- // since SSR runs in workerd alongside RSC
218
+ plugins.push(performanceTracksPlugin());
198
219
  plugins.push(
199
220
  rsc({
200
221
  entries: finalEntries,
201
222
  serverHandler: false,
223
+ clientChunks,
202
224
  }) as PluginOption,
203
225
  );
204
-
205
- // Deduplicate client references from third-party packages in dev mode.
206
- // Prevents module duplication when server components import "use client"
207
- // packages that are also imported directly by client components.
208
226
  plugins.push(clientRefDedup());
209
227
  } else {
210
- // Node preset: full RSC plugin integration
211
- const nodeOptions = resolvedOptions as RangoNodeOptions;
212
-
213
- routerRef.path = nodeOptions.router;
214
-
215
- // Auto-discover router using Vite's resolved root (not process.cwd())
216
- if (!routerRef.path) {
217
- plugins.push({
218
- name: "@rangojs/router:auto-discover",
219
- config(userConfig) {
220
- if (routerRef.path) return;
221
- const root = userConfig.root
222
- ? resolve(process.cwd(), userConfig.root)
223
- : process.cwd();
224
- const filter = createScanFilter(root, {
225
- include: resolvedOptions.include,
226
- exclude: resolvedOptions.exclude,
227
- });
228
- const candidates = findRouterFiles(root, filter);
229
- if (candidates.length === 1) {
230
- const abs = candidates[0];
231
- routerRef.path = (
232
- abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
233
- ).replaceAll("\\", "/");
234
- } else if (candidates.length > 1) {
235
- const list = candidates
236
- .map(
237
- (f) =>
238
- " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
239
- )
240
- .join("\n");
241
- throw new Error(
242
- `[rsc-router] Multiple routers found. Specify \`router\` to choose one:\n${list}`,
243
- );
244
- }
245
- // 0 found: routerRef.path stays undefined, warn at startup via discovery plugin
246
- },
247
- });
248
- }
249
-
250
- const rscOption = nodeOptions.rsc ?? true;
251
-
252
- // Add RSC plugin by default (can be disabled with rsc: false)
253
- if (rscOption !== false) {
254
- // Dynamically import @vitejs/plugin-rsc
255
- const { default: rsc } = await import("@vitejs/plugin-rsc");
256
-
257
- // Resolve entry paths: use explicit config or virtual modules
258
- const userEntries =
259
- typeof rscOption === "boolean" ? {} : rscOption.entries || {};
260
- const finalEntries = {
261
- client: userEntries.client ?? VIRTUAL_IDS.browser,
262
- ssr: userEntries.ssr ?? VIRTUAL_IDS.ssr,
263
- rsc: userEntries.rsc ?? VIRTUAL_IDS.rsc,
264
- };
228
+ plugins.push({
229
+ name: "@rangojs/router:auto-discover",
230
+ config(userConfig) {
231
+ if (routerRef.path) return;
232
+ const root = userConfig.root
233
+ ? resolve(process.cwd(), userConfig.root)
234
+ : process.cwd();
235
+ const candidates = findRouterFiles(root);
236
+ if (candidates.length === 1) {
237
+ const abs = candidates[0];
238
+ routerRef.path = (
239
+ abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
240
+ ).replaceAll("\\", "/");
241
+ } else if (candidates.length > 1) {
242
+ const list = candidates
243
+ .map(
244
+ (f) =>
245
+ " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
246
+ )
247
+ .join("\n");
248
+ throw new Error(`[rango] Multiple routers found:\n${list}`);
249
+ }
250
+ },
251
+ });
265
252
 
266
- // Track RSC entry for version injection (only if custom entry provided)
267
- rscEntryPath = userEntries.rsc ?? null;
253
+ const finalEntries = {
254
+ client: VIRTUAL_IDS.browser,
255
+ ssr: VIRTUAL_IDS.ssr,
256
+ rsc: VIRTUAL_IDS.rsc,
257
+ };
268
258
 
269
- // Create wrapper plugin that checks for duplicates
270
- let hasWarnedDuplicate = false;
259
+ const { default: rsc } = await import("@vitejs/plugin-rsc");
271
260
 
272
- plugins.push({
273
- name: "@rangojs/router:rsc-integration",
274
- enforce: "pre",
261
+ let hasWarnedDuplicate = false;
275
262
 
276
- config() {
277
- // Configure environments for RSC
278
- // When using virtual entries, we need to explicitly configure optimizeDeps
279
- // so Vite pre-bundles React before processing the virtual modules.
280
- // Without this, the dep optimizer may run multiple times with different hashes,
281
- // causing React instance mismatches.
282
- const useVirtualClient = finalEntries.client === VIRTUAL_IDS.browser;
283
- const useVirtualSSR = finalEntries.ssr === VIRTUAL_IDS.ssr;
284
- const useVirtualRSC = finalEntries.rsc === VIRTUAL_IDS.rsc;
263
+ plugins.push({
264
+ name: "@rangojs/router:rsc-integration",
265
+ enforce: "pre",
285
266
 
286
- return {
287
- // Exclude rsc-router modules from optimization to prevent module duplication
288
- // This ensures the same Context instance is used by both browser entry and RSC proxy modules
289
- optimizeDeps: {
290
- exclude: excludeDeps,
291
- esbuildOptions: sharedEsbuildOptions,
292
- },
293
- build: {
294
- rollupOptions: { onwarn },
295
- },
296
- resolve: {
297
- alias: rangoAliases,
298
- },
299
- environments: {
300
- client: {
301
- build: {
302
- rollupOptions: {
303
- output: {
304
- manualChunks: getManualChunks,
305
- },
267
+ config(_userConfig, configEnv) {
268
+ // Fold NODE_ENV for the vercel preset's build. The cloudflare plugin
269
+ // does this automatically and node apps do it themselves; vercel has no
270
+ // platform plugin, so without this React's CJS dev branch survives and
271
+ // doubles the SSR/RSC bundle (Bundle Hygiene rule #2). Only the exact
272
+ // `process.env.NODE_ENV` token is replaced.
273
+ const vercelDefine =
274
+ preset === "vercel" && configEnv.command === "build"
275
+ ? { "process.env.NODE_ENV": JSON.stringify("production") }
276
+ : undefined;
277
+ // The vercel preset's deployed function has no node_modules, so the
278
+ // server bundles must be fully self-contained. Bundle every dependency
279
+ // into the rsc + ssr builds instead of externalizing them (the node
280
+ // default, which only works because `vite preview` runs where
281
+ // node_modules exists). node: builtins stay external automatically.
282
+ const vercelServerEnv =
283
+ preset === "vercel"
284
+ ? { resolve: { noExternal: true as const } }
285
+ : undefined;
286
+ return {
287
+ ...(vercelDefine ? { define: vercelDefine } : {}),
288
+ optimizeDeps: {
289
+ exclude: excludeDeps,
290
+ rolldownOptions: sharedRolldownOptions,
291
+ },
292
+ build: {
293
+ rollupOptions: { onwarn },
294
+ },
295
+ resolve: {
296
+ alias: rangoAliases,
297
+ // Force a single React/React-DOM copy across all three RSC
298
+ // environments. RSC requires exactly one react/react-dom instance
299
+ // per environment runtime; consumer install topologies (pnpm
300
+ // strict layout, experimental React pins, third-party "use client"
301
+ // packages) can otherwise resolve duplicate copies, causing
302
+ // "Invalid hook call" / lost context. Child environments inherit
303
+ // this root dedupe, and Vite merges it with any consumer dedupe.
304
+ dedupe: ["react", "react-dom"],
305
+ },
306
+ environments: {
307
+ client: {
308
+ build: {
309
+ rollupOptions: {
310
+ onwarn,
311
+ output: {
312
+ manualChunks: getManualChunks,
306
313
  },
307
314
  },
308
- // Always exclude rsc-router modules, conditionally add virtual entry
309
- optimizeDeps: {
310
- // Pre-bundle React and rsc-html-stream to prevent late discovery
311
- // triggering ERR_OUTDATED_OPTIMIZED_DEP on cold starts
312
- include: [
313
- "react",
314
- "react-dom",
315
- "react/jsx-runtime",
316
- "react/jsx-dev-runtime",
317
- "rsc-html-stream/client",
318
- ],
319
- exclude: excludeDeps,
320
- esbuildOptions: sharedEsbuildOptions,
321
- ...(useVirtualClient && {
322
- // Tell Vite to scan the virtual entry for dependencies
323
- entries: [VIRTUAL_IDS.browser],
324
- }),
325
- },
326
315
  },
327
- ...(useVirtualSSR && {
328
- ssr: {
329
- optimizeDeps: {
330
- entries: [VIRTUAL_IDS.ssr],
331
- // Pre-bundle all SSR deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
332
- include: [
333
- "react",
334
- "react-dom",
335
- "react-dom/server.edge",
336
- "react-dom/static.edge",
337
- "react/jsx-runtime",
338
- "react/jsx-dev-runtime",
339
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
340
- ],
341
- exclude: excludeDeps,
342
- esbuildOptions: sharedEsbuildOptions,
343
- },
344
- },
345
- }),
346
- ...(useVirtualRSC && {
347
- rsc: {
348
- optimizeDeps: {
349
- entries: [VIRTUAL_IDS.rsc],
350
- // Pre-bundle all RSC deps to prevent late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
351
- include: [
352
- "react",
353
- "react/jsx-runtime",
354
- "react/jsx-dev-runtime",
355
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
356
- ],
357
- esbuildOptions: sharedEsbuildOptions,
358
- },
359
- },
360
- }),
316
+ optimizeDeps: {
317
+ include: [
318
+ "react",
319
+ "react-dom",
320
+ "react/jsx-runtime",
321
+ "react/jsx-dev-runtime",
322
+ nested("rsc-html-stream/client"),
323
+ ],
324
+ exclude: excludeDeps,
325
+ rolldownOptions: sharedRolldownOptions,
326
+ entries: [VIRTUAL_IDS.browser],
327
+ },
361
328
  },
362
- };
363
- },
364
-
365
- configResolved(config) {
366
- if (showBanner) {
367
- const mode =
368
- config.command === "serve"
369
- ? process.argv.includes("preview")
370
- ? "preview"
371
- : "dev"
372
- : "build";
373
- printBanner(mode, "node", rangoVersion);
374
- }
375
-
376
- // Count how many RSC base plugins there are (rsc:minimal is the main one)
377
- const rscMinimalCount = config.plugins.filter(
378
- (p) => p.name === "rsc:minimal",
379
- ).length;
329
+ ssr: {
330
+ ...(vercelServerEnv ?? {}),
331
+ optimizeDeps: {
332
+ entries: [VIRTUAL_IDS.ssr],
333
+ include: [
334
+ "react",
335
+ "react-dom",
336
+ "react-dom/server.edge",
337
+ "react-dom/static.edge",
338
+ "react/jsx-runtime",
339
+ "react/jsx-dev-runtime",
340
+ nested(
341
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
342
+ ),
343
+ ],
344
+ exclude: excludeDeps,
345
+ rolldownOptions: sharedRolldownOptions,
346
+ },
347
+ },
348
+ rsc: {
349
+ ...(vercelServerEnv ?? {}),
350
+ optimizeDeps: {
351
+ entries: [VIRTUAL_IDS.rsc],
352
+ include: [
353
+ "react",
354
+ "react/jsx-runtime",
355
+ "react/jsx-dev-runtime",
356
+ nested(
357
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
358
+ ),
359
+ ],
360
+ rolldownOptions: sharedRolldownOptions,
361
+ },
362
+ },
363
+ },
364
+ };
365
+ },
380
366
 
381
- if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
382
- hasWarnedDuplicate = true;
383
- console.warn(
384
- "[rsc-router] Duplicate @vitejs/plugin-rsc detected. " +
385
- "Remove rsc() from your config or use rango({ rsc: false }) for manual configuration.",
386
- );
387
- }
388
- },
389
- });
367
+ configResolved(config) {
368
+ if (showBanner) {
369
+ const mode =
370
+ config.command === "serve"
371
+ ? process.argv.includes("preview")
372
+ ? "preview"
373
+ : "dev"
374
+ : "build";
375
+ printBanner(
376
+ mode,
377
+ preset === "vercel" ? "vercel" : "node",
378
+ rangoVersion,
379
+ );
380
+ }
390
381
 
391
- // Add virtual entries plugin (RSC entry generated lazily from routerRef)
392
- plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
382
+ const rscMinimalCount = config.plugins.filter(
383
+ (p) => p.name === "rsc:minimal",
384
+ ).length;
393
385
 
394
- // Add the RSC plugin directly
395
- // Cast to PluginOption to handle type differences between bundled vite types
396
- plugins.push(
397
- rsc({
398
- entries: finalEntries,
399
- }) as PluginOption,
400
- );
401
- }
386
+ if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
387
+ hasWarnedDuplicate = true;
388
+ console.warn(
389
+ "[rango] Duplicate @vitejs/plugin-rsc detected. " +
390
+ "Remove rsc() from your vite config — rango() includes it automatically.",
391
+ );
392
+ }
393
+ },
394
+ });
402
395
 
403
- // Deduplicate client references from third-party packages in dev mode.
404
- // Prevents module duplication when server components import "use client"
405
- // packages that are also imported directly by client components.
396
+ plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
397
+ plugins.push(performanceTracksPlugin());
398
+ plugins.push(
399
+ rsc({
400
+ entries: finalEntries,
401
+ clientChunks,
402
+ }) as PluginOption,
403
+ );
406
404
  plugins.push(clientRefDedup());
407
405
  }
408
406
 
409
- // Fix HMR for "use client" components.
410
- //
411
- // @vitejs/plugin-rsc's hotUpdate returns undefined for "use client" files
412
- // in the RSC environment. Vite then tries to propagate through the RSC
413
- // module graph, but the proxy module has no import.meta.hot.accept()
414
- // boundary, causing a full page reload. The client env would handle it
415
- // fine via React Refresh, but the RSC env's full-reload arrives first.
416
- //
417
- // Fix: in the RSC env, return [] for "use client" files to signal
418
- // "handled, nothing to propagate". The client env is left alone so
419
- // React Refresh processes the update normally.
420
407
  plugins.push({
421
408
  name: "@rangojs/router:client-component-hmr",
422
409
  hotUpdate(ctx) {
@@ -440,71 +427,52 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
440
427
  trimmed.startsWith('"use client"') ||
441
428
  trimmed.startsWith("'use client'")
442
429
  ) {
443
- // Consume the update in RSC/SSR envs. The proxy module was already
444
- // re-transformed by the RSC plugin's hotUpdate. Without this, Vite
445
- // tries to propagate through the RSC/SSR module graph where the proxy
446
- // has no import.meta.hot.accept() boundary, triggering a full reload.
447
- // The actual component update is handled by React Refresh in the
448
- // client environment.
449
430
  return [];
450
431
  }
451
- } catch {
452
- // File deleted/moved during HMR, let default handling proceed
453
- }
432
+ } catch {}
454
433
  },
455
434
  });
456
435
 
457
436
  plugins.push(exposeActionId());
458
-
459
- // "use cache" directive transform (enforce: "post"):
460
- // Wraps exports with registerCachedFunction() for function-level caching.
461
437
  plugins.push(useCacheTransform());
462
-
463
- // Consolidated plugin for create* ID injection (enforce: "post"):
464
- // loaders, handles, location state, and prerender handlers.
465
438
  plugins.push(exposeInternalIds());
466
-
467
- // Router ID injection runs at normal priority (no enforce) to avoid
468
- // changing Vite's dep optimization timing.
469
439
  plugins.push(exposeRouterId());
470
-
471
- // Add version virtual module plugin for cache invalidation
472
440
  plugins.push(createVersionPlugin());
473
441
 
474
- // Entry path for discovery: user-specified value (if any) or undefined.
475
- // Auto-discovered path is passed separately via routerRef.
476
- // Cloudflare preset: deferred to configResolved (read from resolved Vite env config).
477
442
  const discoveryEntryPath =
478
443
  preset !== "cloudflare" ? routerRef.path : undefined;
479
- // Ref for deferred auto-discovery (node preset only, undefined for cloudflare)
480
444
  const discoveryRouterRef = preset !== "cloudflare" ? routerRef : undefined;
481
445
 
482
- // Version injector: auto-injects VERSION and routes-manifest into custom entry.rsc files.
483
- // Only applies when there's an explicit rscEntryPath or for cloudflare preset (resolved
484
- // lazily in configResolved). For node preset without a custom entry, the router file
485
- // must NOT be transformed — injecting routes-manifest there creates a circular dependency.
486
- const injectorEntryPath =
487
- rscEntryPath ?? (preset === "cloudflare" ? undefined : null);
488
- if (injectorEntryPath !== null) {
489
- plugins.push(createVersionInjectorPlugin(injectorEntryPath));
446
+ if (preset === "cloudflare") {
447
+ plugins.push(createVersionInjectorPlugin(undefined));
490
448
  }
491
449
 
492
- // Transform CJS vendor files to ESM for browser compatibility
493
- // optimizeDeps.include doesn't work because the file is loaded after initial optimization
494
450
  plugins.push(createCjsToEsmPlugin());
495
-
496
- // Router discovery plugin for build-time manifest generation.
497
- // For cloudflare, the entry is resolved lazily in configResolved from the RSC environment.
498
- // For node, discoveryRouterRef provides the auto-discovered path when not user-specified.
499
451
  plugins.push(
500
452
  createRouterDiscoveryPlugin(discoveryEntryPath, {
501
453
  routerPathRef: discoveryRouterRef,
502
454
  enableBuildPrerender: prerenderEnabled,
503
- staticRouteTypesGeneration: resolvedOptions.staticRouteTypesGeneration,
504
- include: resolvedOptions.include,
505
- exclude: resolvedOptions.exclude,
455
+ buildEnv: options?.buildEnv,
456
+ preset,
457
+ discovery: options?.discovery,
458
+ clientChunkCtx,
506
459
  }),
507
460
  );
508
461
 
462
+ // Vercel preset: assemble .vercel/output from dist/ after the build. Pushed
463
+ // last so its (ssr-gated) closeBundle runs after the discovery plugin's
464
+ // rsc-env postprocess and after every environment has been written.
465
+ if (preset === "vercel") {
466
+ plugins.push(
467
+ createVercelOutputPlugin(resolvedOptions as RangoVercelOptions),
468
+ );
469
+ }
470
+
471
+ debugConfig?.(
472
+ "rango(%s) setup done: %d plugin(s) (%sms)",
473
+ preset,
474
+ plugins.length,
475
+ (performance.now() - rangoStart).toFixed(1),
476
+ );
509
477
  return plugins;
510
478
  }