@rangojs/router 0.0.0-experimental.32 → 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 +120 -204
  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 +190 -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 +63 -24
  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 +338 -126
  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
@@ -22,6 +22,32 @@ export function markSelfGenWrite(
22
22
  export function consumeSelfGenWrite(
23
23
  state: DiscoveryState,
24
24
  filePath: string,
25
+ ): boolean {
26
+ return checkSelfGenWrite(state, filePath, true);
27
+ }
28
+
29
+ /**
30
+ * Non-consuming variant. Used by the `handleHotUpdate` plugin hook to
31
+ * suppress vite's HMR cascade for our own gen-file writes WITHOUT
32
+ * consuming the entry — `consumeSelfGenWrite` (called later from the
33
+ * chokidar `change` handler in `handleRouteFileChange`) still needs to
34
+ * see and consume the same entry to short-circuit our regen path.
35
+ *
36
+ * Both hooks fire for the same file change event:
37
+ * - `handleHotUpdate` runs first (vite's HMR pipeline).
38
+ * - chokidar `change` callback runs after (filesystem watcher).
39
+ */
40
+ export function peekSelfGenWrite(
41
+ state: DiscoveryState,
42
+ filePath: string,
43
+ ): boolean {
44
+ return checkSelfGenWrite(state, filePath, false);
45
+ }
46
+
47
+ function checkSelfGenWrite(
48
+ state: DiscoveryState,
49
+ filePath: string,
50
+ consume: boolean,
25
51
  ): boolean {
26
52
  const info = state.selfWrittenGenFiles.get(filePath);
27
53
  if (!info) return false;
@@ -33,7 +59,7 @@ export function consumeSelfGenWrite(
33
59
  const current = readFileSync(filePath, "utf-8");
34
60
  const currentHash = createHash("sha256").update(current).digest("hex");
35
61
  if (currentHash === info.hash) {
36
- state.selfWrittenGenFiles.delete(filePath);
62
+ if (consume) state.selfWrittenGenFiles.delete(filePath);
37
63
  return true;
38
64
  }
39
65
  // Hash mismatch: file was changed externally. Keep the entry so a
@@ -13,11 +13,25 @@ export const VIRTUAL_ROUTES_MANIFEST_ID = "virtual:rsc-router/routes-manifest";
13
13
  export interface PluginOptions {
14
14
  enableBuildPrerender?: boolean;
15
15
  staticRouteTypesGeneration?: boolean;
16
- include?: string[];
17
- exclude?: string[];
18
16
  // Mutable ref for deferred auto-discovery (node preset).
19
17
  // The auto-discover config() hook populates this before configResolved.
20
18
  routerPathRef?: { path?: string };
19
+ /** Build-time env option from rango() config. */
20
+ buildEnv?: import("../plugin-types.js").BuildEnvOption;
21
+ /** Deployment preset (needed for buildEnv "auto" resolution). */
22
+ preset?: "node" | "cloudflare" | "vercel";
23
+ /**
24
+ * Route-discovery scan filter (glob include/exclude) from rango() config.
25
+ * Compiled into `DiscoveryState.scanFilter` once `projectRoot` is known.
26
+ */
27
+ discovery?: { include?: string[]; exclude?: string[] };
28
+ /**
29
+ * Shared context the built-in clientChunks strategy reads. Discovery populates
30
+ * it (registered fallback hashes + single-router name) before the client build
31
+ * invokes the strategy. Present only when the built-in strategy is active
32
+ * (`clientChunks: true`/default); undefined for `false` or a custom function.
33
+ */
34
+ clientChunkCtx?: import("../utils/client-chunks.js").ClientChunkContext;
21
35
  }
22
36
 
23
37
  export interface PrecomputedEntry {
@@ -43,6 +57,21 @@ export interface DiscoveryState {
43
57
  projectRoot: string;
44
58
  isBuildMode: boolean;
45
59
  userResolveAlias: any;
60
+ /**
61
+ * Data-only slice of the user's resolved config (resolve.* incl. native
62
+ * tsconfigPaths, define, oxc) mirrored into the discovery temp server so it
63
+ * resolves and transforms modules the same way the real environment does.
64
+ * See `utils/forward-user-plugins.ts`.
65
+ */
66
+ userRunnerConfig:
67
+ | import("../utils/forward-user-plugins.js").ForwardedRunnerConfig
68
+ | undefined;
69
+ /**
70
+ * User resolution plugins (resolveId/load), stripped to their resolution
71
+ * surface, forwarded into the discovery temp server. Lets third-party
72
+ * resolvers such as vite-tsconfig-paths participate in discovery.
73
+ */
74
+ userResolvePlugins: import("vite").Plugin[];
46
75
  scanFilter: ScanFilter | undefined;
47
76
  cachedRouterFiles: string[] | undefined;
48
77
  opts: PluginOptions | undefined;
@@ -58,8 +87,8 @@ export interface DiscoveryState {
58
87
 
59
88
  prerenderManifestEntries: Record<string, string> | null;
60
89
  staticManifestEntries: Record<string, string> | null;
61
- handlerChunkInfo: ChunkInfo | null;
62
- staticHandlerChunkInfo: ChunkInfo | null;
90
+ handlerChunkInfoMap: Map<string, ChunkInfo>;
91
+ staticHandlerChunkInfoMap: Map<string, ChunkInfo>;
63
92
  rscEntryFileName: string | null;
64
93
  resolvedPrerenderModules: Map<string, string[]> | undefined;
65
94
  resolvedStaticModules: Map<string, string[]> | undefined;
@@ -69,6 +98,19 @@ export interface DiscoveryState {
69
98
  devServer: any;
70
99
  selfWrittenGenFiles: Map<string, { at: number; hash: string }>;
71
100
  SELF_WRITE_WINDOW_MS: number;
101
+
102
+ /** Resolved build-time env bindings (set during buildStart/configureServer). */
103
+ resolvedBuildEnv?: Record<string, unknown>;
104
+ /** Cleanup function for build-time env resources (e.g., miniflare). */
105
+ buildEnvDispose?: (() => Promise<void> | void) | null;
106
+
107
+ /**
108
+ * Set when the most recent HMR re-discovery threw. Cleared on the next
109
+ * successful discovery. Surfaced via debug logs so we can detect "manifest
110
+ * frozen at last-good after error → user fix in non-route file → no
111
+ * rediscovery trigger" scenarios.
112
+ */
113
+ lastDiscoveryError?: { message: string; at: number } | null;
72
114
  }
73
115
 
74
116
  export function createDiscoveryState(
@@ -80,6 +122,8 @@ export function createDiscoveryState(
80
122
  projectRoot: "",
81
123
  isBuildMode: false,
82
124
  userResolveAlias: undefined,
125
+ userRunnerConfig: undefined,
126
+ userResolvePlugins: [],
83
127
  scanFilter: undefined,
84
128
  cachedRouterFiles: undefined,
85
129
  opts,
@@ -95,8 +139,8 @@ export function createDiscoveryState(
95
139
 
96
140
  prerenderManifestEntries: null,
97
141
  staticManifestEntries: null,
98
- handlerChunkInfo: null,
99
- staticHandlerChunkInfo: null,
142
+ handlerChunkInfoMap: new Map(),
143
+ staticHandlerChunkInfoMap: new Map(),
100
144
  rscEntryFileName: null,
101
145
  resolvedPrerenderModules: undefined,
102
146
  resolvedStaticModules: undefined,
@@ -106,5 +150,6 @@ export function createDiscoveryState(
106
150
  devServer: null,
107
151
  selfWrittenGenFiles: new Map(),
108
152
  SELF_WRITE_WINDOW_MS: 5_000,
153
+ lastDiscoveryError: null,
109
154
  };
110
155
  }
@@ -49,7 +49,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
49
49
  );
50
50
  genFileVars.push(varName);
51
51
  } else {
52
- // Routers without sourceFile: inline their manifest data directly
53
52
  routersWithoutGenFile.push({
54
53
  id: entry.id,
55
54
  manifest: entry.routeManifest,
@@ -58,7 +57,7 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
58
57
  }
59
58
 
60
59
  const lines = [
61
- `import { setCachedManifest, setPrecomputedEntries, setRouteTrie, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
60
+ `import { setCachedManifest, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
62
61
  ...genFileImports,
63
62
  // Clear stale per-router cached data (manifest, trie, precomputed entries)
64
63
  // before re-populating. In Cloudflare dev mode, program reloads re-evaluate
@@ -68,14 +67,12 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
68
67
  `clearAllRouterData();`,
69
68
  ];
70
69
 
71
- // Flatten NamedRoutes entries: search schema objects -> plain string paths
72
70
  if (genFileVars.length > 0) {
73
71
  lines.push(
74
72
  `function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`,
75
73
  );
76
74
  }
77
75
 
78
- // Build the merged manifest from gen file imports + inlined data
79
76
  if (genFileVars.length === 1 && routersWithoutGenFile.length === 0) {
80
77
  lines.push(`setCachedManifest(__flat(${genFileVars[0]}));`);
81
78
  } else {
@@ -86,7 +83,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
86
83
  lines.push(`setCachedManifest({ ${parts.join(", ")} });`);
87
84
  }
88
85
 
89
- // Set per-router manifests
90
86
  let genVarIdx = 0;
91
87
  for (const entry of state.perRouterManifests) {
92
88
  if (entry.sourceFile) {
@@ -101,31 +97,19 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
101
97
  }
102
98
  }
103
99
 
104
- // In dev mode, skip trie and precomputed entries injection. These are
105
- // computed once during initial discovery and become stale after route
106
- // changes. A stale trie would incorrectly match removed routes. The
107
- // handler falls back to Phase 2 regex matching against the live
108
- // router.urlpatterns, which is always correct after a program reload.
109
- // In build mode, the trie is always fresh (built from the final route
110
- // tree) so it's safe to inject.
111
- if (state.isBuildMode) {
112
- if (
113
- state.mergedPrecomputedEntries &&
114
- state.mergedPrecomputedEntries.length > 0
115
- ) {
116
- lines.push(
117
- `setPrecomputedEntries(${jsonParseExpression(state.mergedPrecomputedEntries)});`,
118
- );
119
- }
120
- if (state.mergedRouteTrie) {
121
- lines.push(
122
- `setRouteTrie(${jsonParseExpression(state.mergedRouteTrie)});`,
123
- );
124
- }
125
- }
100
+ // Per-router trie and precomputedEntries are NOT inlined eagerly.
101
+ // They live in the per-router lazy chunks (generatePerRouterModule) and
102
+ // are loaded via ensureRouterManifest(routerId), which is awaited before
103
+ // every request in router.fetch() and before findMatch is reached.
104
+ // Inlining the merged versions here would duplicate the per-router data
105
+ // (the merged trie/precomputedEntries equal the per-router data for
106
+ // single-router apps; for multi-router, the merged trie is dead code
107
+ // because find-match.ts only consumes per-router tries).
108
+ //
109
+ // In dev mode, the handler also falls back to Phase 2 regex matching
110
+ // against live router.urlpatterns, which is always correct after a
111
+ // program reload.
126
112
 
127
- // Register lazy loaders for per-router manifest modules.
128
- // Each import() uses a static string literal so Rollup creates separate chunks.
129
113
  for (const routerId of state.perRouterManifestDataMap.keys()) {
130
114
  lines.push(
131
115
  `registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`,
@@ -139,9 +123,6 @@ export function generateRoutesManifestModule(state: DiscoveryState): string {
139
123
  return lines.join("\n");
140
124
  }
141
125
 
142
- // No manifest: either discovery hasn't completed or no runner (Cloudflare dev).
143
- // Still inject __PRERENDER_DEV_URL so the prerender store can fetch on-demand.
144
- // Re-resolve origin now since the server is listening by module load time.
145
126
  if (!state.isBuildMode) {
146
127
  const origin =
147
128
  state.devServerOrigin ||
@@ -170,7 +151,6 @@ export function generatePerRouterModule(
170
151
  const lines: string[] = [];
171
152
 
172
153
  if (routerEntry?.sourceFile) {
173
- // Import manifest from the gen file so HMR auto-propagates
174
154
  const routerDir = dirname(routerEntry.sourceFile);
175
155
  const routerBasename = basename(routerEntry.sourceFile).replace(
176
156
  /\.(tsx?|jsx?)$/,
@@ -199,5 +179,5 @@ export function generatePerRouterModule(
199
179
  `export const precomputedEntries = ${jsonParseExpression(entries)};`,
200
180
  );
201
181
  }
202
- return lines.join("\n") || "// empty router manifest";
182
+ return lines.join("\n") || "";
203
183
  }
package/src/vite/index.ts CHANGED
@@ -12,5 +12,13 @@ export { poke } from "./plugins/refresh-cmd.js";
12
12
  export type {
13
13
  RangoNodeOptions,
14
14
  RangoCloudflareOptions,
15
+ RangoVercelOptions,
16
+ VercelPresetOptions,
15
17
  RangoOptions,
18
+ ClientChunks,
19
+ ClientChunkMeta,
20
+ BuildEnvOption,
21
+ BuildEnvFactory,
22
+ BuildEnvFactoryContext,
23
+ BuildEnvResult,
16
24
  } from "./plugin-types.js";
@@ -1,39 +1,112 @@
1
+ // -- Build-time environment types -------------------------------------------
2
+
1
3
  /**
2
- * RSC plugin entry points configuration.
3
- * All entries use virtual modules by default. Specify a path to use a custom entry file.
4
+ * Context passed to a buildEnv factory function.
5
+ * Provides Vite config details for conditional env setup.
4
6
  */
5
- export interface RscEntries {
6
- /**
7
- * Path to a custom browser/client entry file.
8
- * If not specified, a default virtual entry is used.
9
- */
10
- client?: string;
7
+ export interface BuildEnvFactoryContext {
8
+ /** Vite project root directory. */
9
+ root: string;
10
+ /** Vite mode (e.g. "development", "production"). */
11
+ mode: string;
12
+ /** Vite command ("serve" for dev, "build" for production). */
13
+ command: "serve" | "build";
14
+ /** Router deployment preset. */
15
+ preset: "node" | "cloudflare" | "vercel";
16
+ }
11
17
 
12
- /**
13
- * Path to a custom SSR entry file.
14
- * If not specified, a default virtual entry is used.
15
- */
16
- ssr?: string;
18
+ /**
19
+ * Factory function that creates build-time environment bindings.
20
+ * Called once at plugin startup. Return `dispose` to clean up resources.
21
+ */
22
+ export type BuildEnvFactory = (
23
+ ctx: BuildEnvFactoryContext,
24
+ ) => Promise<BuildEnvResult> | BuildEnvResult;
17
25
 
18
- /**
19
- * Path to a custom RSC entry file.
20
- * If not specified, a default virtual entry is used that imports the router from the `entry` option.
21
- */
22
- rsc?: string;
26
+ /**
27
+ * Result of resolving build-time environment bindings.
28
+ */
29
+ export interface BuildEnvResult {
30
+ /** Environment bindings available to Prerender/Static handlers via ctx.env. */
31
+ env: Record<string, unknown>;
32
+ /** Called after build completes to clean up resources (e.g., miniflare). */
33
+ dispose?: () => Promise<void> | void;
23
34
  }
24
35
 
25
36
  /**
26
- * Options for @vitejs/plugin-rsc integration
37
+ * Build-time environment configuration for Prerender and Static handlers.
38
+ *
39
+ * - `false` (default): no build-time env, `ctx.env` throws.
40
+ * - `"auto"`: calls `wrangler.getPlatformProxy()` (cloudflare preset only).
41
+ * - Object: used directly as `ctx.env` during build.
42
+ * - Factory: called once at startup, must return `{ env, dispose? }`.
27
43
  */
28
- export interface RscPluginOptions {
44
+ export type BuildEnvOption =
45
+ | false
46
+ | "auto"
47
+ | Record<string, unknown>
48
+ | BuildEnvFactory;
49
+
50
+ // -- Client chunking --------------------------------------------------------
51
+
52
+ /**
53
+ * Metadata for one client ("use client") module, passed to a {@link ClientChunks}
54
+ * function. Mirrors the shape `@vitejs/plugin-rsc` passes to its own
55
+ * `clientChunks` option.
56
+ */
57
+ export interface ClientChunkMeta {
58
+ /** Absolute module id of the "use client" file. */
59
+ id: string;
60
+ /** Normalized (posix) module id — convenient for path-based matching. */
61
+ normalizedId: string;
29
62
  /**
30
- * Entry points for client, ssr, and rsc environments.
31
- * All entries use virtual modules by default.
32
- * Specify paths only when you need custom entry files.
63
+ * The RSC/server chunk that statically imports this client reference. This is
64
+ * the key used for the default grouping when no override is supplied: a single
65
+ * router that statically imports every route yields ONE `serverChunk`, hence
66
+ * one client chunk for all routes.
33
67
  */
34
- entries?: RscEntries;
68
+ serverChunk: string;
35
69
  }
36
70
 
71
+ /**
72
+ * Controls how client ("use client") components are grouped into browser
73
+ * chunks, i.e. per-route / per-feature code splitting of the client bundle.
74
+ *
75
+ * Without splitting, a single router ships ONE client chunk containing every
76
+ * route's client components (and their CSS) — navigating to one route downloads
77
+ * every other route's client code. (Host sub-apps loaded via a dynamic `import()`
78
+ * are the exception: each forms its own chunk.) This option controls how that
79
+ * monolith is split.
80
+ *
81
+ * Behavior branches:
82
+ * - `true` / omitted (**default**, pre-1.0): Rango's built-in **directory
83
+ * strategy**. It splits app `"use client"` modules by **route id** — the segment
84
+ * after a route-root directory (`routes`, `app`, `pages`, `features`, `handlers`,
85
+ * …) — so `routes/dashboard/**` becomes `app-dashboard` at any nesting depth.
86
+ * Where it finds NO route structure (a flat `src/components/`, or host sub-apps
87
+ * already split by a dynamic `import()`), it inherits the default grouping
88
+ * unchanged — so the shared `src/components` chunk stays shared and host apps do
89
+ * not leak across each other. Shared runtime (React, the router, `node_modules`)
90
+ * is never split.
91
+ * - `false`: opt out — inherit `@vitejs/plugin-rsc`'s default grouping everywhere
92
+ * (one chunk per router / per host sub-app).
93
+ * - function: full override. Return a chunk group name, or `undefined` to fall
94
+ * back to the default grouping for that one module. Forwarded directly to
95
+ * `@vitejs/plugin-rsc`'s `clientChunks`.
96
+ *
97
+ * Every module maps to exactly one group, so there is no byte duplication: a
98
+ * component used by two routes lives in one group and is fetched whenever it
99
+ * renders. Put genuinely shared client components OUTSIDE route directories so
100
+ * they land in the shared group rather than one route's chunk.
101
+ *
102
+ * @default true
103
+ */
104
+ export type ClientChunks =
105
+ | boolean
106
+ | ((meta: ClientChunkMeta) => string | undefined);
107
+
108
+ // -- Plugin options ---------------------------------------------------------
109
+
37
110
  /**
38
111
  * Base options shared by all presets
39
112
  */
@@ -45,27 +118,27 @@ interface RangoBaseOptions {
45
118
  banner?: boolean;
46
119
 
47
120
  /**
48
- * Generate named-routes.gen.ts by parsing url modules at startup.
49
- * Provides type-safe Handler<"name"> and href() without executing router code.
50
- * Set to `false` to disable (run `npx rango extract-names` manually instead).
121
+ * Group client ("use client") components into browser chunks for per-route /
122
+ * per-feature code splitting. On by default (pre-1.0); pass `false` to opt out.
123
+ * See {@link ClientChunks}.
124
+ *
51
125
  * @default true
52
126
  */
53
- staticRouteTypesGeneration?: boolean;
54
-
55
- /**
56
- * Glob patterns for files to include in route type scanning.
57
- * Only files matching at least one pattern will be scanned.
58
- * Patterns are relative to the project root.
59
- * When unset, all .ts/.tsx files are scanned.
60
- */
61
- include?: string[];
127
+ clientChunks?: ClientChunks;
62
128
 
63
129
  /**
64
- * Glob patterns for files to exclude from route type scanning.
65
- * Takes precedence over `include`. Patterns are relative to the project root.
66
- * Defaults to common test/build directories.
130
+ * Filter which files route discovery scans, by glob. Paths are matched
131
+ * root-relative (e.g. `src/routes/**`). `include` restricts discovery to
132
+ * matching files; `exclude` removes matches (the defaults cover tests, dist,
133
+ * coverage, etc.). Mirrors the CLI's `--include`/`--exclude`.
134
+ *
135
+ * @example
136
+ * rango({ discovery: { include: ["src/routes/**"] } })
67
137
  */
68
- exclude?: string[];
138
+ discovery?: {
139
+ include?: string[];
140
+ exclude?: string[];
141
+ };
69
142
  }
70
143
 
71
144
  /**
@@ -78,36 +151,15 @@ export interface RangoNodeOptions extends RangoBaseOptions {
78
151
  preset?: "node";
79
152
 
80
153
  /**
81
- * Path to your router configuration file that exports the route tree.
82
- * This file must export a `router` object created with `createRouter()`.
83
- *
84
- * When omitted, auto-discovers the router by scanning for files containing
85
- * `createRouter`. If exactly one is found, it is used automatically.
86
- * If multiple are found, an error is thrown with the list of candidates.
87
- *
88
- * @example
89
- * ```ts
90
- * rango({ router: './src/router.tsx' })
91
- * // or simply:
92
- * rango()
93
- * ```
94
- */
95
- router?: string;
96
-
97
- /**
98
- * RSC plugin configuration. By default, rsc-router includes @vitejs/plugin-rsc
99
- * with sensible defaults.
100
- *
101
- * Entry files (browser, ssr, rsc) are optional - if they don't exist,
102
- * virtual defaults are used.
154
+ * Environment bindings available to Prerender and Static handlers at build
155
+ * time via `ctx.env`. Shared across all prerender invocations for the build.
103
156
  *
104
- * - Omit or pass `true`/`{}` to use defaults (recommended)
105
- * - Pass `{ entries: {...} }` to customize entry paths
106
- * - Pass `false` to disable (for manual @vitejs/plugin-rsc configuration)
157
+ * `"auto"` is Cloudflare-only (it resolves the wrangler platform proxy), so it
158
+ * is not accepted on the Node preset pass an object or a factory instead.
107
159
  *
108
- * @default true
160
+ * @default false
109
161
  */
110
- rsc?: boolean | RscPluginOptions;
162
+ buildEnv?: Exclude<BuildEnvOption, "auto">;
111
163
  }
112
164
 
113
165
  /**
@@ -117,15 +169,81 @@ export interface RangoCloudflareOptions extends RangoBaseOptions {
117
169
  /**
118
170
  * Deployment preset for Cloudflare Workers.
119
171
  * When using cloudflare preset:
120
- * - @vitejs/plugin-rsc is NOT added (cloudflare plugin adds it)
172
+ * - @vitejs/plugin-rsc IS still added by rango(), but with `serverHandler: false`
173
+ * (the cloudflare plugin owns the RSC worker/server entry); only `client` and
174
+ * `ssr` virtual entries are configured, no rsc entry
121
175
  * - Your worker entry (e.g., worker.rsc.tsx) imports the router directly
122
176
  * - Browser and SSR use virtual entries
123
177
  * - Build-time manifest generation is auto-detected from the resolved RSC environment config
124
178
  */
125
179
  preset: "cloudflare";
180
+
181
+ /**
182
+ * Environment bindings available to Prerender and Static handlers at build
183
+ * time via `ctx.env`. Shared across all prerender invocations for the build.
184
+ *
185
+ * `"auto"` resolves the Cloudflare platform proxy via wrangler
186
+ * `getPlatformProxy()`.
187
+ *
188
+ * @default false
189
+ */
190
+ buildEnv?: BuildEnvOption;
191
+ }
192
+
193
+ /**
194
+ * Per-function knobs for the Vercel deployment, written into the generated
195
+ * `.vc-config.json` (and `config.json` for `functionName`).
196
+ */
197
+ export interface VercelPresetOptions {
198
+ /** Node runtime for the function. @default "nodejs22.x" */
199
+ runtime?: string;
200
+ /** Max execution time in seconds. @default 30 */
201
+ maxDuration?: number;
202
+ /** Function memory in MB (platform default when omitted). */
203
+ memory?: number;
204
+ /** Regions to pin the function to (platform default when omitted). */
205
+ regions?: string[];
206
+ /**
207
+ * Function name — the `<name>.func` directory and the `config.json` route
208
+ * destination. @default "index"
209
+ */
210
+ functionName?: string;
211
+ }
212
+
213
+ /**
214
+ * Options for Vercel Functions deployment.
215
+ *
216
+ * Builds like the node preset (Vercel runs Node Functions, not Workers): rango
217
+ * owns the RSC entry, `process.env.NODE_ENV` is folded for the build, and after
218
+ * the build a `.vercel/output` directory (Build Output API v3) is assembled from
219
+ * `dist/` — a single streaming Node Function plus the static client assets. The
220
+ * app must install `@vercel/functions` (used by `VercelCacheStore` and the
221
+ * generated function launcher).
222
+ */
223
+ export interface RangoVercelOptions extends RangoBaseOptions {
224
+ /**
225
+ * Deployment preset for Vercel Functions.
226
+ */
227
+ preset: "vercel";
228
+
229
+ /**
230
+ * Environment bindings available to Prerender and Static handlers at build
231
+ * time via `ctx.env`. `"auto"` is Cloudflare-only; pass an object or a factory.
232
+ *
233
+ * @default false
234
+ */
235
+ buildEnv?: Exclude<BuildEnvOption, "auto">;
236
+
237
+ /**
238
+ * Vercel function configuration written into the Build Output.
239
+ */
240
+ vercel?: VercelPresetOptions;
126
241
  }
127
242
 
128
243
  /**
129
244
  * Options for rango() Vite plugin
130
245
  */
131
- export type RangoOptions = RangoNodeOptions | RangoCloudflareOptions;
246
+ export type RangoOptions =
247
+ | RangoNodeOptions
248
+ | RangoCloudflareOptions
249
+ | RangoVercelOptions;
@@ -1,4 +1,7 @@
1
1
  import type { Plugin } from "vite";
2
+ import { createRangoDebugger, NS } from "../debug.js";
3
+
4
+ const debug = createRangoDebugger(NS.transform);
2
5
 
3
6
  /**
4
7
  * Transform CJS vendor files from @vitejs/plugin-rsc to ESM for browser compatibility.
@@ -9,78 +12,65 @@ export function createCjsToEsmPlugin(): Plugin {
9
12
  name: "@rangojs/router:cjs-to-esm",
10
13
  enforce: "pre",
11
14
  transform(code, id) {
12
- const cleanId = id.split("?")[0];
15
+ const cleanId = id.split("?")[0].replaceAll("\\", "/");
13
16
 
14
- // Transform the client.browser.js entry point to re-export from CJS
15
- if (
16
- cleanId.includes("vendor/react-server-dom/client.browser.js") ||
17
- cleanId.includes("vendor\\react-server-dom\\client.browser.js")
18
- ) {
17
+ if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
19
18
  const isProd = process.env.NODE_ENV === "production";
20
19
  const cjsFile = isProd
21
20
  ? "./cjs/react-server-dom-webpack-client.browser.production.js"
22
21
  : "./cjs/react-server-dom-webpack-client.browser.development.js";
23
22
 
23
+ debug?.("cjs-to-esm entry redirect %s", id);
24
24
  return {
25
25
  code: `export * from "${cjsFile}";`,
26
26
  map: null,
27
27
  };
28
28
  }
29
29
 
30
- // Transform the actual CJS files to ESM
31
30
  if (
32
- (cleanId.includes("vendor/react-server-dom/cjs/") ||
33
- cleanId.includes("vendor\\react-server-dom\\cjs\\")) &&
31
+ cleanId.includes("vendor/react-server-dom/cjs/") &&
34
32
  cleanId.includes("client.browser")
35
33
  ) {
36
34
  let transformed = code;
37
35
 
38
- // Extract the license comment to preserve it
39
36
  const licenseMatch = transformed.match(/^\/\*\*[\s\S]*?\*\//);
40
37
  const license = licenseMatch ? licenseMatch[0] : "";
41
38
  if (license) {
42
39
  transformed = transformed.slice(license.length);
43
40
  }
44
41
 
45
- // Remove "use strict" (both dev and prod have this)
46
42
  transformed = transformed.replace(/^\s*["']use strict["'];\s*/, "");
47
43
 
48
- // Remove the conditional IIFE wrapper (development only)
49
44
  transformed = transformed.replace(
50
45
  /^\s*["']production["']\s*!==\s*process\.env\.NODE_ENV\s*&&\s*\(function\s*\(\)\s*\{/,
51
46
  "",
52
47
  );
53
48
 
54
- // Remove the closing of the conditional IIFE at the end (development only)
55
49
  transformed = transformed.replace(/\}\)\(\);?\s*$/, "");
56
50
 
57
- // Replace require('react') and require('react-dom') with imports (development)
58
51
  transformed = transformed.replace(
59
52
  /var\s+React\s*=\s*require\s*\(\s*["']react["']\s*\)\s*,[\s\n]+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
60
53
  'import React from "react";\nimport ReactDOM from "react-dom";\nvar ',
61
54
  );
62
55
 
63
- // Replace require('react-dom') only (production - doesn't import React)
64
56
  transformed = transformed.replace(
65
57
  /var\s+ReactDOM\s*=\s*require\s*\(\s*["']react-dom["']\s*\)\s*,/g,
66
58
  'import ReactDOM from "react-dom";\nvar ',
67
59
  );
68
60
 
69
- // Transform exports.xyz = function() to export function xyz()
70
61
  transformed = transformed.replace(
71
62
  /exports\.(\w+)\s*=\s*function\s*\(/g,
72
63
  "export function $1(",
73
64
  );
74
65
 
75
- // Transform exports.xyz = value to export const xyz = value
76
66
  transformed = transformed.replace(
77
67
  /exports\.(\w+)\s*=/g,
78
68
  "export const $1 =",
79
69
  );
80
70
 
81
- // Reconstruct with license at the top
82
71
  transformed = license + "\n" + transformed;
83
72
 
73
+ debug?.("cjs-to-esm body rewrite %s", id);
84
74
  return {
85
75
  code: transformed,
86
76
  map: null,