@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1c0bdfad

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 (406) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +291 -61
  3. package/dist/bin/rango.js +544 -143
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3744 -1329
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +67 -13
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +312 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +322 -19
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +282 -60
  19. package/skills/host-router/SKILL.md +278 -0
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +297 -31
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +155 -111
  32. package/skills/rango/SKILL.md +251 -23
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +104 -9
  36. package/skills/router-setup/SKILL.md +124 -11
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +125 -222
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +357 -52
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +67 -40
  62. package/src/bin/rango.ts +18 -0
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +197 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/link-interceptor.ts +4 -0
  74. package/src/browser/navigation-bridge.ts +200 -30
  75. package/src/browser/navigation-client.ts +217 -58
  76. package/src/browser/navigation-store-handle.ts +38 -0
  77. package/src/browser/navigation-store.ts +76 -67
  78. package/src/browser/navigation-transaction.ts +18 -66
  79. package/src/browser/network-error-handler.ts +34 -7
  80. package/src/browser/partial-update.ts +187 -112
  81. package/src/browser/prefetch/cache.ts +312 -30
  82. package/src/browser/prefetch/fetch.ts +344 -47
  83. package/src/browser/prefetch/policy.ts +6 -0
  84. package/src/browser/prefetch/queue.ts +126 -20
  85. package/src/browser/prefetch/resource-ready.ts +77 -0
  86. package/src/browser/rango-state.ts +158 -76
  87. package/src/browser/react/Link.tsx +125 -18
  88. package/src/browser/react/NavigationProvider.tsx +135 -120
  89. package/src/browser/react/ScrollRestoration.tsx +10 -6
  90. package/src/browser/react/context.ts +7 -2
  91. package/src/browser/react/filter-segment-order.ts +66 -7
  92. package/src/browser/react/index.ts +0 -48
  93. package/src/browser/react/location-state-shared.ts +178 -8
  94. package/src/browser/react/location-state.ts +39 -14
  95. package/src/browser/react/use-action.ts +6 -15
  96. package/src/browser/react/use-handle.ts +23 -69
  97. package/src/browser/react/use-href.tsx +8 -1
  98. package/src/browser/react/use-link-status.ts +33 -8
  99. package/src/browser/react/use-navigation.ts +32 -7
  100. package/src/browser/react/use-params.ts +20 -10
  101. package/src/browser/react/use-reverse.ts +106 -0
  102. package/src/browser/react/use-router.ts +46 -11
  103. package/src/browser/react/use-search-params.ts +0 -5
  104. package/src/browser/react/use-segments.ts +11 -21
  105. package/src/browser/response-adapter.ts +80 -5
  106. package/src/browser/rsc-router.tsx +226 -75
  107. package/src/browser/scroll-restoration.ts +54 -42
  108. package/src/browser/segment-reconciler.ts +36 -9
  109. package/src/browser/segment-structure-assert.ts +2 -2
  110. package/src/browser/server-action-bridge.ts +619 -442
  111. package/src/browser/types.ts +115 -11
  112. package/src/browser/validate-redirect-origin.ts +43 -16
  113. package/src/build/collect-fallback-refs.ts +107 -0
  114. package/src/build/generate-manifest.ts +65 -40
  115. package/src/build/generate-route-types.ts +7 -1
  116. package/src/build/index.ts +8 -2
  117. package/src/build/prefix-tree-utils.ts +123 -0
  118. package/src/build/route-trie.ts +182 -37
  119. package/src/build/route-types/ast-route-extraction.ts +15 -8
  120. package/src/build/route-types/codegen.ts +16 -5
  121. package/src/build/route-types/include-resolution.ts +125 -24
  122. package/src/build/route-types/param-extraction.ts +6 -3
  123. package/src/build/route-types/per-module-writer.ts +22 -6
  124. package/src/build/route-types/router-processing.ts +392 -106
  125. package/src/build/route-types/scan-filter.ts +9 -2
  126. package/src/build/route-types/source-scan.ts +216 -0
  127. package/src/build/runtime-discovery.ts +9 -20
  128. package/src/cache/cache-error.ts +104 -0
  129. package/src/cache/cache-key-utils.ts +29 -13
  130. package/src/cache/cache-policy.ts +108 -34
  131. package/src/cache/cache-runtime.ts +214 -48
  132. package/src/cache/cache-scope.ts +236 -89
  133. package/src/cache/cache-tag.ts +103 -0
  134. package/src/cache/cf/cf-base64.ts +33 -0
  135. package/src/cache/cf/cf-cache-constants.ts +127 -0
  136. package/src/cache/cf/cf-cache-store.ts +2224 -171
  137. package/src/cache/cf/cf-cache-types.ts +349 -0
  138. package/src/cache/cf/cf-kv-utils.ts +46 -0
  139. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  140. package/src/cache/cf/index.ts +11 -17
  141. package/src/cache/document-cache.ts +89 -27
  142. package/src/cache/handle-snapshot.ts +70 -0
  143. package/src/cache/index.ts +11 -20
  144. package/src/cache/memory-segment-store.ts +136 -37
  145. package/src/cache/profile-registry.ts +31 -31
  146. package/src/cache/read-through-swr.ts +41 -11
  147. package/src/cache/segment-codec.ts +9 -17
  148. package/src/cache/tag-invalidation.ts +230 -0
  149. package/src/cache/taint.ts +55 -0
  150. package/src/cache/types.ts +37 -100
  151. package/src/client.rsc.tsx +45 -21
  152. package/src/client.tsx +120 -336
  153. package/src/cloudflare/index.ts +11 -0
  154. package/src/cloudflare/tracing.ts +109 -0
  155. package/src/component-utils.ts +19 -0
  156. package/src/components/DefaultDocument.tsx +8 -2
  157. package/src/context-var.ts +84 -2
  158. package/src/debug.ts +2 -2
  159. package/src/decode-loader-results.ts +52 -0
  160. package/src/defer.ts +196 -0
  161. package/src/deps/ssr.ts +0 -1
  162. package/src/encode-kv.ts +49 -0
  163. package/src/errors.ts +30 -4
  164. package/src/escape-script.ts +52 -0
  165. package/src/handle.ts +70 -22
  166. package/src/handles/MetaTags.tsx +56 -19
  167. package/src/handles/Scripts.tsx +183 -0
  168. package/src/handles/breadcrumbs.ts +95 -0
  169. package/src/handles/is-thenable.ts +19 -0
  170. package/src/handles/meta.ts +51 -40
  171. package/src/handles/script.ts +244 -0
  172. package/src/host/cookie-handler.ts +9 -60
  173. package/src/host/errors.ts +0 -24
  174. package/src/host/index.ts +8 -5
  175. package/src/host/pattern-matcher.ts +23 -52
  176. package/src/host/router.ts +107 -99
  177. package/src/host/testing.ts +40 -27
  178. package/src/host/types.ts +37 -4
  179. package/src/host/utils.ts +1 -1
  180. package/src/href-client.ts +137 -22
  181. package/src/index.rsc.ts +79 -29
  182. package/src/index.ts +149 -65
  183. package/src/internal-debug.ts +11 -10
  184. package/src/loader-store.ts +500 -0
  185. package/src/loader.rsc.ts +20 -13
  186. package/src/loader.ts +12 -11
  187. package/src/missing-id-error.ts +68 -0
  188. package/src/outlet-context.ts +1 -1
  189. package/src/outlet-provider.tsx +1 -5
  190. package/src/prerender/param-hash.ts +16 -16
  191. package/src/prerender/store.ts +63 -26
  192. package/src/prerender.ts +198 -82
  193. package/src/redirect-origin.ts +100 -0
  194. package/src/regex-escape.ts +8 -0
  195. package/src/render-error-thrower.tsx +20 -0
  196. package/src/response-utils.ts +62 -0
  197. package/src/reverse.ts +65 -15
  198. package/src/root-error-boundary.tsx +1 -19
  199. package/src/route-content-wrapper.tsx +7 -72
  200. package/src/route-definition/dsl-helpers.ts +469 -276
  201. package/src/route-definition/helper-factories.ts +29 -139
  202. package/src/route-definition/helpers-types.ts +113 -37
  203. package/src/route-definition/index.ts +3 -3
  204. package/src/route-definition/redirect.ts +53 -12
  205. package/src/route-definition/resolve-handler-use.ts +161 -0
  206. package/src/route-definition/use-item-types.ts +32 -0
  207. package/src/route-map-builder.ts +7 -17
  208. package/src/route-types.ts +37 -41
  209. package/src/router/basename.ts +14 -0
  210. package/src/router/content-negotiation.ts +164 -17
  211. package/src/router/error-handling.ts +45 -18
  212. package/src/router/find-match.ts +45 -22
  213. package/src/router/handler-context.ts +110 -39
  214. package/src/router/instrument.ts +350 -0
  215. package/src/router/intercept-resolution.ts +50 -24
  216. package/src/router/lazy-includes.ts +19 -53
  217. package/src/router/loader-resolution.ts +274 -56
  218. package/src/router/logging.ts +5 -8
  219. package/src/router/manifest.ts +49 -45
  220. package/src/router/match-api.ts +121 -205
  221. package/src/router/match-context.ts +0 -22
  222. package/src/router/match-handlers.ts +58 -58
  223. package/src/router/match-middleware/background-revalidation.ts +33 -6
  224. package/src/router/match-middleware/cache-lookup.ts +214 -263
  225. package/src/router/match-middleware/cache-store.ts +73 -33
  226. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  227. package/src/router/match-middleware/segment-resolution.ts +52 -18
  228. package/src/router/match-pipelines.ts +1 -42
  229. package/src/router/match-result.ts +104 -49
  230. package/src/router/metrics.ts +217 -26
  231. package/src/router/middleware-types.ts +24 -110
  232. package/src/router/middleware.ts +384 -197
  233. package/src/router/navigation-snapshot.ts +131 -0
  234. package/src/router/params-util.ts +23 -0
  235. package/src/router/pattern-matching.ts +148 -91
  236. package/src/router/prefetch-cache-ttl.ts +51 -0
  237. package/src/router/prerender-match.ts +199 -56
  238. package/src/router/preview-match.ts +32 -102
  239. package/src/router/request-classification.ts +276 -0
  240. package/src/router/revalidation.ts +144 -74
  241. package/src/router/route-snapshot.ts +244 -0
  242. package/src/router/router-context.ts +8 -28
  243. package/src/router/router-interfaces.ts +129 -36
  244. package/src/router/router-options.ts +185 -23
  245. package/src/router/router-registry.ts +2 -5
  246. package/src/router/segment-resolution/fresh.ts +281 -76
  247. package/src/router/segment-resolution/helpers.ts +116 -31
  248. package/src/router/segment-resolution/loader-cache.ts +63 -37
  249. package/src/router/segment-resolution/revalidation.ts +493 -391
  250. package/src/router/segment-resolution/static-store.ts +19 -5
  251. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  252. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  253. package/src/router/segment-resolution.ts +5 -1
  254. package/src/router/segment-wrappers.ts +8 -5
  255. package/src/router/state-cookie-name.ts +33 -0
  256. package/src/router/substitute-pattern-params.ts +56 -0
  257. package/src/router/telemetry-otel.ts +161 -199
  258. package/src/router/telemetry.ts +96 -19
  259. package/src/router/timeout.ts +0 -20
  260. package/src/router/tracing.ts +206 -0
  261. package/src/router/trie-matching.ts +180 -58
  262. package/src/router/types.ts +10 -63
  263. package/src/router/url-params.ts +44 -0
  264. package/src/router.ts +182 -54
  265. package/src/rsc/handler-context.ts +3 -2
  266. package/src/rsc/handler.ts +702 -460
  267. package/src/rsc/helpers.ts +168 -46
  268. package/src/rsc/index.ts +2 -25
  269. package/src/rsc/json-route-result.ts +38 -0
  270. package/src/rsc/loader-fetch.ts +127 -31
  271. package/src/rsc/manifest-init.ts +33 -42
  272. package/src/rsc/origin-guard.ts +39 -25
  273. package/src/rsc/progressive-enhancement.ts +98 -19
  274. package/src/rsc/redirect-guard.ts +99 -0
  275. package/src/rsc/response-cache-serve.ts +238 -0
  276. package/src/rsc/response-error.ts +79 -12
  277. package/src/rsc/response-route-handler.ts +99 -189
  278. package/src/rsc/rsc-rendering.ts +126 -106
  279. package/src/rsc/runtime-warnings.ts +23 -10
  280. package/src/rsc/server-action.ts +269 -114
  281. package/src/rsc/ssr-setup.ts +144 -0
  282. package/src/rsc/types.ts +34 -6
  283. package/src/runtime-env.ts +18 -0
  284. package/src/search-params.ts +49 -41
  285. package/src/segment-content-promise.ts +67 -0
  286. package/src/segment-loader-promise.ts +149 -0
  287. package/src/segment-system.tsx +281 -129
  288. package/src/serialize.ts +243 -0
  289. package/src/server/context.ts +317 -63
  290. package/src/server/cookie-parse.ts +32 -0
  291. package/src/server/cookie-store.ts +80 -5
  292. package/src/server/handle-store.ts +40 -38
  293. package/src/server/loader-registry.ts +26 -46
  294. package/src/server/request-context.ts +425 -177
  295. package/src/server.ts +6 -0
  296. package/src/ssr/index.tsx +25 -16
  297. package/src/static-handler.ts +27 -18
  298. package/src/testing/cache-status.ts +162 -0
  299. package/src/testing/collect-handle.ts +40 -0
  300. package/src/testing/dispatch.ts +701 -0
  301. package/src/testing/dom.entry.ts +22 -0
  302. package/src/testing/e2e/fixture.ts +188 -0
  303. package/src/testing/e2e/index.ts +128 -0
  304. package/src/testing/e2e/matchers.ts +35 -0
  305. package/src/testing/e2e/page-helpers.ts +272 -0
  306. package/src/testing/e2e/parity.ts +387 -0
  307. package/src/testing/e2e/server.ts +195 -0
  308. package/src/testing/flight-matchers.ts +97 -0
  309. package/src/testing/flight-normalize.ts +11 -0
  310. package/src/testing/flight-runtime.d.ts +57 -0
  311. package/src/testing/flight-tree.ts +682 -0
  312. package/src/testing/flight.entry.ts +52 -0
  313. package/src/testing/flight.ts +257 -0
  314. package/src/testing/generated-routes.ts +183 -0
  315. package/src/testing/index.ts +99 -0
  316. package/src/testing/internal/context.ts +371 -0
  317. package/src/testing/internal/flight-client-globals.ts +30 -0
  318. package/src/testing/internal/seed-vars.ts +54 -0
  319. package/src/testing/render-handler.ts +343 -0
  320. package/src/testing/render-route.tsx +581 -0
  321. package/src/testing/run-loader.ts +385 -0
  322. package/src/testing/run-middleware.ts +205 -0
  323. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  324. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  325. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  326. package/src/testing/vitest-stubs/version.ts +5 -0
  327. package/src/testing/vitest.ts +305 -0
  328. package/src/theme/ThemeProvider.tsx +20 -58
  329. package/src/theme/ThemeScript.tsx +7 -9
  330. package/src/theme/constants.ts +52 -13
  331. package/src/theme/index.ts +3 -19
  332. package/src/theme/theme-context.ts +1 -5
  333. package/src/theme/theme-script.ts +22 -21
  334. package/src/theme/use-theme.ts +0 -3
  335. package/src/types/boundaries.ts +0 -35
  336. package/src/types/cache-types.ts +17 -8
  337. package/src/types/error-types.ts +30 -90
  338. package/src/types/global-namespace.ts +54 -41
  339. package/src/types/handler-context.ts +236 -88
  340. package/src/types/index.ts +1 -10
  341. package/src/types/loader-types.ts +44 -15
  342. package/src/types/request-scope.ts +112 -0
  343. package/src/types/route-config.ts +10 -45
  344. package/src/types/route-entry.ts +19 -7
  345. package/src/types/segments.ts +37 -19
  346. package/src/urls/include-helper.ts +33 -70
  347. package/src/urls/index.ts +1 -11
  348. package/src/urls/path-helper-types.ts +58 -11
  349. package/src/urls/path-helper.ts +57 -111
  350. package/src/urls/pattern-types.ts +48 -19
  351. package/src/urls/response-types.ts +25 -22
  352. package/src/urls/type-extraction.ts +58 -139
  353. package/src/urls/urls-function.ts +1 -18
  354. package/src/use-loader.tsx +346 -89
  355. package/src/vite/debug.ts +185 -0
  356. package/src/vite/discovery/bundle-postprocess.ts +64 -91
  357. package/src/vite/discovery/discover-routers.ts +147 -88
  358. package/src/vite/discovery/discovery-errors.ts +194 -0
  359. package/src/vite/discovery/gate-state.ts +171 -0
  360. package/src/vite/discovery/prerender-collection.ts +247 -145
  361. package/src/vite/discovery/route-types-writer.ts +40 -84
  362. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  363. package/src/vite/discovery/state.ts +61 -13
  364. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  365. package/src/vite/index.ts +10 -3
  366. package/src/vite/inject-client-debug.ts +36 -0
  367. package/src/vite/plugin-types.ts +155 -65
  368. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  369. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  370. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  371. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  372. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  373. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  374. package/src/vite/plugins/expose-action-id.ts +49 -98
  375. package/src/vite/plugins/expose-id-utils.ts +96 -51
  376. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  377. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  378. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  379. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  380. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  381. package/src/vite/plugins/performance-tracks.ts +89 -0
  382. package/src/vite/plugins/refresh-cmd.ts +127 -0
  383. package/src/vite/plugins/use-cache-transform.ts +73 -83
  384. package/src/vite/plugins/version-injector.ts +21 -25
  385. package/src/vite/plugins/version-plugin.ts +46 -37
  386. package/src/vite/plugins/virtual-entries.ts +13 -18
  387. package/src/vite/rango.ts +241 -287
  388. package/src/vite/router-discovery.ts +956 -149
  389. package/src/vite/utils/ast-handler-extract.ts +26 -35
  390. package/src/vite/utils/banner.ts +4 -4
  391. package/src/vite/utils/bundle-analysis.ts +10 -15
  392. package/src/vite/utils/client-chunks.ts +184 -0
  393. package/src/vite/utils/directive-prologue.ts +40 -0
  394. package/src/vite/utils/forward-user-plugins.ts +171 -0
  395. package/src/vite/utils/manifest-utils.ts +4 -59
  396. package/src/vite/utils/package-resolution.ts +20 -52
  397. package/src/vite/utils/prerender-utils.ts +141 -34
  398. package/src/vite/utils/shared-utils.ts +92 -42
  399. package/CLAUDE.md +0 -5
  400. package/src/browser/action-response-classifier.ts +0 -99
  401. package/src/browser/react/use-client-cache.ts +0 -58
  402. package/src/browser/shallow.ts +0 -40
  403. package/src/handles/index.ts +0 -6
  404. package/src/network-error-thrower.tsx +0 -23
  405. package/src/route-definition/route-function.ts +0 -119
  406. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Debug logging for the Rango Vite plugin.
3
+ *
4
+ * Thin wrapper over the `debug` package (the same one Vite uses for its
5
+ * own `vite:*` namespaces). Enable with either:
6
+ *
7
+ * DEBUG='rango:*' vite dev
8
+ * vite --debug rango:* # vite prepends `vite:`, we bridge it
9
+ *
10
+ * Returns `undefined` when no matching namespace is enabled, so call sites
11
+ * can guard expensive diagnostics with a simple truthiness check:
12
+ *
13
+ * const debug = createRangoDebugger(NS.routes);
14
+ * if (debug) debug("built manifest (%d routes) in %dms", n, ms);
15
+ *
16
+ * Back-compat: INTERNAL_RANGO_DEBUG=1 still enables all rango namespaces.
17
+ *
18
+ * Vite CLI note: `vite --debug <feat>` rewrites to `DEBUG=vite:<feat>` — it
19
+ * always prefixes with `vite:` and cannot enable bare `rango:*` namespaces.
20
+ * We work around this by registering a shadow `vite:rango:*` instance for
21
+ * each debugger, so either invocation works.
22
+ */
23
+
24
+ import debugFactory from "debug";
25
+
26
+ /**
27
+ * Canonical debug namespaces. Import as `NS.xxx` instead of string literals
28
+ * so typos become type errors and the full set lives in one place.
29
+ */
30
+ export const NS = {
31
+ config: "rango:config",
32
+ discovery: "rango:discovery",
33
+ routes: "rango:routes",
34
+ prerender: "rango:prerender",
35
+ build: "rango:build",
36
+ dev: "rango:dev",
37
+ transform: "rango:transform",
38
+ chunks: "rango:chunks",
39
+ } as const;
40
+
41
+ // Back-compat: the legacy INTERNAL_RANGO_DEBUG env var enabled per-site
42
+ // console.logs in this plugin. Map it to `rango:*` so those call sites can
43
+ // be migrated to the `debug` pipeline without breaking existing setups.
44
+ // Uses debug.enable() rather than mutating process.env because the `debug`
45
+ // package already snapshotted DEBUG when it was imported above.
46
+ if (process.env.INTERNAL_RANGO_DEBUG) {
47
+ const existing = debugFactory.disable();
48
+ debugFactory.enable(existing ? `${existing},rango:*` : "rango:*");
49
+ }
50
+
51
+ export type Debugger = (formatter: string, ...args: unknown[]) => void;
52
+
53
+ export function createRangoDebugger(namespace: string): Debugger | undefined {
54
+ const primary = debugFactory(namespace);
55
+ // Shadow namespace so `vite --debug rango:*` (which expands to
56
+ // DEBUG=vite:rango:*) and `vite --debug` (DEBUG=vite:*) both pick us up.
57
+ const shadow = debugFactory(`vite:${namespace}`);
58
+ if (primary.enabled) return primary as Debugger;
59
+ if (shadow.enabled) return shadow as Debugger;
60
+ return undefined;
61
+ }
62
+
63
+ /**
64
+ * Measure an async block and log its duration via `debug`. No-ops (still
65
+ * runs `fn`) when the namespace is disabled, so production cost is a single
66
+ * `.enabled` check per call.
67
+ *
68
+ * await timed(debug, "discover routers", () => discoverRouters(state));
69
+ */
70
+ export async function timed<T>(
71
+ debug: Debugger | undefined,
72
+ label: string,
73
+ fn: () => T | Promise<T>,
74
+ ): Promise<T> {
75
+ if (!debug) return await fn();
76
+ const start = performance.now();
77
+ try {
78
+ return await fn();
79
+ } finally {
80
+ debug("%s (%sms)", label, (performance.now() - start).toFixed(1));
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Synchronous variant of `timed`. Use for sync call sites — wrapping them
86
+ * with the async `timed` would create a floating promise that discards any
87
+ * throw, bypassing the surrounding try/catch.
88
+ */
89
+ export function timedSync<T>(
90
+ debug: Debugger | undefined,
91
+ label: string,
92
+ fn: () => T,
93
+ ): T {
94
+ if (!debug) return fn();
95
+ const start = performance.now();
96
+ try {
97
+ return fn();
98
+ } finally {
99
+ debug("%s (%sms)", label, (performance.now() - start).toFixed(1));
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Aggregate counter for high-frequency call sites (typically Vite
105
+ * `transform` hooks that run on many files). Per-call logging would
106
+ * drown real signal; this collects totals and reports once on flush.
107
+ *
108
+ * const counter = createCounter(debug, "use-cache-transform");
109
+ * // inside transform():
110
+ * return counter?.time(id, () => doWork()) ?? doWork();
111
+ * // or manually:
112
+ * counter?.record(id, ms);
113
+ * // flush on buildEnd (counter resets, so multi-env builds each get
114
+ * // their own summary line):
115
+ * counter?.flush();
116
+ *
117
+ * Returns `undefined` when the namespace is disabled so call sites pay
118
+ * nothing when off.
119
+ */
120
+ export interface Counter {
121
+ record(file: string, ms: number): void;
122
+ /**
123
+ * Convenience: time a sync or async block and record it. Propagates
124
+ * throws; records regardless of outcome. Returns the function's result.
125
+ */
126
+ time<T>(file: string, fn: () => T): T;
127
+ time<T>(file: string, fn: () => Promise<T>): Promise<T>;
128
+ flush(): void;
129
+ }
130
+
131
+ export function createCounter(
132
+ debug: Debugger | undefined,
133
+ label: string,
134
+ ): Counter | undefined {
135
+ if (!debug) return undefined;
136
+ let n = 0;
137
+ let totalMs = 0;
138
+ let slowestMs = 0;
139
+ let slowestFile = "";
140
+ const record = (file: string, ms: number): void => {
141
+ n++;
142
+ totalMs += ms;
143
+ if (ms > slowestMs) {
144
+ slowestMs = ms;
145
+ slowestFile = file;
146
+ }
147
+ };
148
+ return {
149
+ record,
150
+ time<T>(file: string, fn: () => T | Promise<T>): T | Promise<T> {
151
+ const start = performance.now();
152
+ let out: T | Promise<T>;
153
+ try {
154
+ out = fn();
155
+ } catch (err) {
156
+ record(file, performance.now() - start);
157
+ throw err;
158
+ }
159
+ if (out && typeof (out as any).then === "function") {
160
+ return (out as Promise<T>).finally(() =>
161
+ record(file, performance.now() - start),
162
+ );
163
+ }
164
+ record(file, performance.now() - start);
165
+ return out;
166
+ },
167
+ flush(): void {
168
+ if (n === 0) return;
169
+ debug(
170
+ "%s: %d files, %sms total, slowest %sms %s",
171
+ label,
172
+ n,
173
+ totalMs.toFixed(1),
174
+ slowestMs.toFixed(1),
175
+ slowestFile,
176
+ );
177
+ // Reset so buildEnd firing once per environment (Vite 6+ multi-env)
178
+ // gives one log line per env rather than silently dropping later data.
179
+ n = 0;
180
+ totalMs = 0;
181
+ slowestMs = 0;
182
+ slowestFile = "";
183
+ },
184
+ };
185
+ }
@@ -6,9 +6,10 @@
6
6
  */
7
7
 
8
8
  import { resolve } from "node:path";
9
- import { createHash } from "node:crypto";
10
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
9
+ import { readFileSync, writeFileSync, existsSync } from "node:fs";
11
10
  import { evictHandlerCode } from "../utils/bundle-analysis.js";
11
+ import { copyStagedBuildAssets } from "../utils/prerender-utils.js";
12
+ import { jsonParseExpression } from "../utils/manifest-utils.js";
12
13
  import type { DiscoveryState } from "./state.js";
13
14
 
14
15
  /**
@@ -17,11 +18,11 @@ import type { DiscoveryState } from "./state.js";
17
18
  */
18
19
  export function postprocessBundle(state: DiscoveryState): void {
19
20
  const hasPrerenderData =
20
- state.prerenderCollectedData &&
21
- Object.keys(state.prerenderCollectedData).length > 0;
21
+ state.prerenderManifestEntries &&
22
+ Object.keys(state.prerenderManifestEntries).length > 0;
22
23
  const hasStaticData =
23
- state.staticCollectedData &&
24
- Object.keys(state.staticCollectedData).length > 0;
24
+ state.staticManifestEntries &&
25
+ Object.keys(state.staticManifestEntries).length > 0;
25
26
  if (!hasPrerenderData && !hasStaticData) return;
26
27
 
27
28
  // Find RSC entry (recorded in generateBundle, fallback to dist/rsc/index.js)
@@ -31,25 +32,25 @@ export function postprocessBundle(state: DiscoveryState): void {
31
32
  state.rscEntryFileName ?? "index.js",
32
33
  );
33
34
 
34
- // 1. Evict handler code from __prerender-handlers and __static-handlers chunks.
35
- // handlerChunkInfo/staticHandlerChunkInfo are populated by generateBundle
35
+ // 1. Evict handler code from whichever chunks contain handler exports.
36
+ // handlerChunkInfoMap/staticHandlerChunkInfoMap are populated by generateBundle
36
37
  // after the production RSC build. In Vite 6 multi-environment builds, the
37
- // RSC build runs twice (analysis + production). Chunk info is only available
38
- // after the production pass, so we run eviction whenever it becomes available.
38
+ // RSC build runs twice (analysis + production). The maps are cleared at the
39
+ // start of each generateBundle pass so only production data is used here.
39
40
  const evictionTargets: Array<{
40
- info: typeof state.handlerChunkInfo;
41
+ infos: Iterable<import("./state.js").ChunkInfo>;
41
42
  fnName: string;
42
43
  brand: string;
43
44
  label: string;
44
45
  }> = [
45
46
  {
46
- info: state.handlerChunkInfo,
47
+ infos: state.handlerChunkInfoMap.values(),
47
48
  fnName: "Prerender",
48
49
  brand: "prerenderHandler",
49
50
  label: "handler code from RSC bundle",
50
51
  },
51
52
  {
52
- info: state.staticHandlerChunkInfo,
53
+ infos: state.staticHandlerChunkInfoMap.values(),
53
54
  fnName: "Static",
54
55
  brand: "staticHandler",
55
56
  label: "static handler code",
@@ -57,70 +58,58 @@ export function postprocessBundle(state: DiscoveryState): void {
57
58
  ];
58
59
 
59
60
  for (const target of evictionTargets) {
60
- if (!target.info) continue;
61
- const chunkPath = resolve(
62
- state.projectRoot,
63
- "dist/rsc",
64
- target.info.fileName,
65
- );
66
- try {
67
- const code = readFileSync(chunkPath, "utf-8");
68
- const result = evictHandlerCode(
69
- code,
70
- target.info.exports,
71
- target.fnName,
72
- target.brand,
73
- );
74
- if (result) {
75
- writeFileSync(chunkPath, result.code);
76
- const savedKB = (result.savedBytes / 1024).toFixed(1);
77
- console.log(
78
- `[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${target.info.fileName}`,
61
+ for (const info of target.infos) {
62
+ const chunkPath = resolve(state.projectRoot, "dist/rsc", info.fileName);
63
+ try {
64
+ const code = readFileSync(chunkPath, "utf-8");
65
+ const result = evictHandlerCode(
66
+ code,
67
+ info.exports,
68
+ target.fnName,
69
+ target.brand,
70
+ );
71
+ if (result) {
72
+ writeFileSync(chunkPath, result.code);
73
+ const savedKB = (result.savedBytes / 1024).toFixed(1);
74
+ console.log(
75
+ `[rango] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`,
76
+ );
77
+ }
78
+ } catch (replaceErr: any) {
79
+ console.warn(
80
+ `[rango] Failed to evict ${target.label}: ${replaceErr.message}`,
79
81
  );
80
82
  }
81
- } catch (replaceErr: any) {
82
- console.warn(
83
- `[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`,
84
- );
85
83
  }
86
84
  }
87
- state.handlerChunkInfo = null;
88
- state.staticHandlerChunkInfo = null;
85
+ state.handlerChunkInfoMap.clear();
86
+ state.staticHandlerChunkInfoMap.clear();
89
87
 
90
88
  // 2. Write prerender data as separate importable asset modules
91
- // and inject a manifest import into the RSC entry.
89
+ // and inject a lazy manifest loader into the RSC entry.
92
90
  if (hasPrerenderData && existsSync(rscEntryPath)) {
93
91
  const rscCode = readFileSync(rscEntryPath, "utf-8");
94
- // Check for the specific injection marker, not just the variable name.
95
- // The runtime code (prerender store) also references __PRERENDER_MANIFEST,
96
- // so a broad string check would false-positive and skip injection.
92
+ // Check for the specific injection marker to avoid double-injection.
97
93
  if (!rscCode.includes("__prerender-manifest.js")) {
98
94
  try {
99
- const assetsDir = resolve(state.projectRoot, "dist/rsc/assets");
100
- mkdirSync(assetsDir, { recursive: true });
101
-
102
- const manifestEntries: string[] = [];
103
- let totalBytes = 0;
95
+ let totalBytes = copyStagedBuildAssets(
96
+ state.projectRoot,
97
+ Object.values(state.prerenderManifestEntries!),
98
+ );
104
99
 
105
- for (const [key, entry] of Object.entries(
106
- state.prerenderCollectedData!,
100
+ const manifestMap: Record<string, string> = {};
101
+ for (const [key, assetFileName] of Object.entries(
102
+ state.prerenderManifestEntries!,
107
103
  )) {
108
- const entryJson = JSON.stringify(entry);
109
- const contentHash = createHash("sha256")
110
- .update(entryJson)
111
- .digest("hex")
112
- .slice(0, 8);
113
- const assetFileName = `__pr-${contentHash}.js`;
114
- const assetPath = resolve(assetsDir, assetFileName);
115
- const assetCode = `export default ${entryJson};\n`;
116
- writeFileSync(assetPath, assetCode);
117
- totalBytes += Buffer.byteLength(assetCode);
118
- manifestEntries.push(
119
- `${JSON.stringify(key)}:()=>import("./assets/${assetFileName}")`,
120
- );
104
+ manifestMap[key] = `./assets/${assetFileName}`;
121
105
  }
122
106
 
123
- const manifestCode = `const m={${manifestEntries.join(",")}};export default m;\n`;
107
+ const manifestCode = [
108
+ `const m=${jsonParseExpression(manifestMap)};`,
109
+ `export function loadPrerenderAsset(s){return import(s)}`,
110
+ `export default m;`,
111
+ "",
112
+ ].join("\n");
124
113
  const manifestPath = resolve(
125
114
  state.projectRoot,
126
115
  "dist/rsc/__prerender-manifest.js",
@@ -128,16 +117,16 @@ export function postprocessBundle(state: DiscoveryState): void {
128
117
  writeFileSync(manifestPath, manifestCode);
129
118
  totalBytes += Buffer.byteLength(manifestCode);
130
119
 
131
- const injection = `import __pm from "./__prerender-manifest.js";\nglobalThis.__PRERENDER_MANIFEST = __pm;\n`;
120
+ const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");\n`;
132
121
  writeFileSync(rscEntryPath, injection + rscCode);
133
122
 
134
123
  const totalKB = (totalBytes / 1024).toFixed(1);
135
124
  console.log(
136
- `[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderCollectedData!).length} entries)`,
125
+ `[rango] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
137
126
  );
138
127
  } catch (err: any) {
139
128
  throw new Error(
140
- `[rsc-router] Failed to write prerender assets: ${err.message}`,
129
+ `[rango] Failed to write prerender assets: ${err.message}`,
141
130
  );
142
131
  }
143
132
  }
@@ -147,33 +136,17 @@ export function postprocessBundle(state: DiscoveryState): void {
147
136
  // and inject a __STATIC_MANIFEST import into the RSC entry.
148
137
  if (hasStaticData && existsSync(rscEntryPath)) {
149
138
  const rscCode = readFileSync(rscEntryPath, "utf-8");
150
- if (!rscCode.includes("__STATIC_MANIFEST")) {
139
+ if (!rscCode.includes("__static-manifest.js")) {
151
140
  try {
152
- const assetsDir = resolve(state.projectRoot, "dist/rsc/assets");
153
- mkdirSync(assetsDir, { recursive: true });
154
-
155
141
  const manifestEntries: string[] = [];
156
- let totalBytes = 0;
142
+ let totalBytes = copyStagedBuildAssets(
143
+ state.projectRoot,
144
+ Object.values(state.staticManifestEntries!),
145
+ );
157
146
 
158
- for (const [handlerId, { encoded, handles }] of Object.entries(
159
- state.staticCollectedData!,
147
+ for (const [handlerId, assetFileName] of Object.entries(
148
+ state.staticManifestEntries!,
160
149
  )) {
161
- // Store both the Flight payload and handle data
162
- const hasHandles = Object.keys(handles).length > 0;
163
- const exportValue = hasHandles
164
- ? JSON.stringify({ encoded, handles })
165
- : JSON.stringify(encoded);
166
- // Hash the full payload that is written so distinct handle
167
- // snapshots produce distinct asset filenames.
168
- const contentHash = createHash("sha256")
169
- .update(exportValue)
170
- .digest("hex")
171
- .slice(0, 8);
172
- const assetFileName = `__st-${contentHash}.js`;
173
- const assetPath = resolve(assetsDir, assetFileName);
174
- const assetCode = `export default ${exportValue};\n`;
175
- writeFileSync(assetPath, assetCode);
176
- totalBytes += Buffer.byteLength(assetCode);
177
150
  manifestEntries.push(
178
151
  `${JSON.stringify(handlerId)}:()=>import("./assets/${assetFileName}")`,
179
152
  );
@@ -197,11 +170,11 @@ export function postprocessBundle(state: DiscoveryState): void {
197
170
 
198
171
  const totalKB = (totalBytes / 1024).toFixed(1);
199
172
  console.log(
200
- `[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticCollectedData!).length} entries)`,
173
+ `[rango] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
201
174
  );
202
175
  } catch (err: any) {
203
176
  throw new Error(
204
- `[rsc-router] Failed to write static assets: ${err.message}`,
177
+ `[rango] Failed to write static assets: ${err.message}`,
205
178
  );
206
179
  }
207
180
  }