@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
@@ -6,19 +6,25 @@
6
6
  * generation.
7
7
  */
8
8
 
9
- import { contextSet } from "../../context-var.js";
9
+ import { contextSet, hasContextVars } from "../../context-var.js";
10
10
  import {
11
11
  encodePathParam,
12
12
  substituteRouteParams,
13
13
  runWithConcurrency,
14
14
  groupByConcurrency,
15
15
  notifyOnError,
16
+ resolvePrerenderError,
17
+ stageBuildAssetModule,
16
18
  } from "../utils/prerender-utils.js";
17
19
  import type { DiscoveryState } from "./state.js";
20
+ import { createRangoDebugger, NS } from "../debug.js";
21
+
22
+ const debug = createRangoDebugger(NS.prerender);
18
23
 
19
24
  /**
20
25
  * Expand prerender routes into concrete URLs and render them via the
21
- * RSC runner. Stores collected data in state.prerenderCollectedData.
26
+ * RSC runner. Stages asset modules and stores key-to-file entries in
27
+ * state.prerenderManifestEntries.
22
28
  */
23
29
  export async function expandPrerenderRoutes(
24
30
  state: DiscoveryState,
@@ -28,6 +34,12 @@ export async function expandPrerenderRoutes(
28
34
  ): Promise<void> {
29
35
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode) return;
30
36
 
37
+ const overallStart = debug ? performance.now() : 0;
38
+ debug?.(
39
+ "expandPrerenderRoutes: start (%d router manifest(s))",
40
+ allManifests.length,
41
+ );
42
+
31
43
  type PrerenderEntry = {
32
44
  urlPath: string;
33
45
  routeName: string;
@@ -49,110 +61,181 @@ export async function expandPrerenderRoutes(
49
61
  return substituteRouteParams(pattern, params);
50
62
  };
51
63
 
64
+ let resolvedRoutes = 0;
65
+ let totalDynamic = 0;
66
+
67
+ // Count dynamic routes upfront for progress reporting
52
68
  for (const { manifest } of allManifests) {
53
69
  if (!manifest.prerenderRoutes) continue;
54
- const defs = manifest._prerenderDefs || {};
55
70
  for (const routeName of manifest.prerenderRoutes) {
56
71
  const pattern = manifest.routeManifest[routeName];
57
- if (!pattern) continue;
58
- const def = defs[routeName];
59
- const isPassthroughRoute = !!def?.options?.passthrough;
60
- const hasDynamic = pattern.includes(":") || pattern.includes("*");
61
- if (!hasDynamic) {
62
- // Static route: use pattern directly (strip trailing slash for URL)
63
- entries.push({
64
- urlPath: pattern.replace(/\/$/, "") || "/",
65
- routeName,
66
- concurrency: 1,
67
- isPassthroughRoute,
68
- });
69
- } else {
70
- // Dynamic route: call getParams() to enumerate param combinations
71
- if (def?.getParams) {
72
- try {
73
- const buildVars: Record<string, any> = {};
74
- const getParamsCtx = {
75
- build: true as const,
76
- set: ((keyOrVar: any, value: any) => {
77
- contextSet(buildVars, keyOrVar, value);
78
- }) as any,
79
- reverse: getParamsReverse,
80
- };
81
- const paramsList = await def.getParams(getParamsCtx);
82
- const concurrency = def.options?.concurrency ?? 1;
83
- const hasBuildVars =
84
- Object.keys(buildVars).length > 0 ||
85
- Object.getOwnPropertySymbols(buildVars).length > 0;
86
- for (const params of paramsList) {
87
- let url = substituteRouteParams(
88
- pattern,
89
- params as Record<string, string>,
90
- encodePathParam,
72
+ if (pattern && (pattern.includes(":") || pattern.includes("*"))) {
73
+ totalDynamic++;
74
+ }
75
+ }
76
+ }
77
+
78
+ // Periodic progress log so long getParams() calls don't look stalled
79
+ const paramsStart = performance.now();
80
+ const progressInterval =
81
+ totalDynamic > 0
82
+ ? setInterval(() => {
83
+ const elapsed = ((performance.now() - paramsStart) / 1000).toFixed(1);
84
+ console.log(
85
+ `[rango] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`,
86
+ );
87
+ }, 5000)
88
+ : undefined;
89
+
90
+ try {
91
+ for (const { manifest } of allManifests) {
92
+ if (!manifest.prerenderRoutes) continue;
93
+ const defs = manifest._prerenderDefs || {};
94
+ const passthroughSet = new Set(manifest.passthroughRoutes || []);
95
+ for (const routeName of manifest.prerenderRoutes) {
96
+ const pattern = manifest.routeManifest[routeName];
97
+ if (!pattern) continue;
98
+ const def = defs[routeName];
99
+ const isPassthroughRoute = passthroughSet.has(routeName);
100
+ const hasDynamic = pattern.includes(":") || pattern.includes("*");
101
+ if (!hasDynamic) {
102
+ // Static route: use pattern directly (strip trailing slash for URL)
103
+ entries.push({
104
+ urlPath: pattern.replace(/\/$/, "") || "/",
105
+ routeName,
106
+ concurrency: 1,
107
+ isPassthroughRoute,
108
+ });
109
+ } else {
110
+ // Dynamic route: call getParams() to enumerate param combinations
111
+ if (def?.getParams) {
112
+ const getParamsStart = debug ? performance.now() : 0;
113
+ try {
114
+ const buildVars: Record<string, any> = {};
115
+ const buildEnv = state.resolvedBuildEnv;
116
+ const getParamsCtx = {
117
+ build: true as const,
118
+ dev: !state.isBuildMode,
119
+ set: ((keyOrVar: any, value: any) => {
120
+ contextSet(buildVars, keyOrVar, value);
121
+ }) as any,
122
+ reverse: getParamsReverse,
123
+ get env() {
124
+ if (buildEnv !== undefined) return buildEnv;
125
+ throw new Error(
126
+ "[rango] ctx.env is not available during build-time getParams(). " +
127
+ "Configure buildEnv in your rango() plugin options to enable build-time env access.",
128
+ );
129
+ },
130
+ };
131
+ const paramsList = await def.getParams(getParamsCtx);
132
+ debug?.(
133
+ "getParams %s -> %d params (%sms)",
134
+ routeName,
135
+ paramsList.length,
136
+ (performance.now() - getParamsStart).toFixed(1),
91
137
  );
92
- // Anonymous wildcard fallback: use conventional keys if provided
93
- if (url.includes("*")) {
94
- const wildcardValue =
95
- (params as Record<string, string>)["*"] ??
96
- (params as Record<string, string>).splat;
97
- if (wildcardValue !== undefined) {
98
- url = url.replace(/\*[^/]*$/, encodePathParam(wildcardValue));
138
+ const concurrency = def.options?.concurrency ?? 1;
139
+ const hasBuildVars = hasContextVars(buildVars);
140
+ for (const params of paramsList) {
141
+ let url = substituteRouteParams(
142
+ pattern,
143
+ params as Record<string, string>,
144
+ encodePathParam,
145
+ );
146
+ // Anonymous wildcard fallback: use conventional keys if provided
147
+ if (url.includes("*")) {
148
+ const wildcardValue =
149
+ (params as Record<string, string>)["*"] ??
150
+ (params as Record<string, string>).splat;
151
+ if (wildcardValue !== undefined) {
152
+ url = url.replace(
153
+ /\*[^/]*$/,
154
+ encodePathParam(wildcardValue),
155
+ );
156
+ }
99
157
  }
158
+ entries.push({
159
+ urlPath: url.replace(/\/$/, "") || "/",
160
+ routeName,
161
+ concurrency,
162
+ ...(hasBuildVars ? { buildVars } : {}),
163
+ isPassthroughRoute,
164
+ });
100
165
  }
101
- entries.push({
102
- urlPath: url.replace(/\/$/, "") || "/",
103
- routeName,
104
- concurrency,
105
- ...(hasBuildVars ? { buildVars } : {}),
106
- isPassthroughRoute,
107
- });
108
- }
109
- } catch (err: any) {
110
- // Skip in getParams() skips the entire route
111
- if (err.name === "Skip") {
112
- console.log(
113
- `[rsc-router] SKIP route "${routeName}" - ${err.message}`,
114
- );
115
- notifyOnError(
116
- registry,
117
- err,
118
- "prerender",
119
- routeName,
120
- undefined,
121
- true,
166
+ resolvedRoutes++;
167
+ } catch (err: any) {
168
+ resolvedRoutes++;
169
+ // Skip in getParams() skips the entire route
170
+ if (err.name === "Skip") {
171
+ console.log(
172
+ `[rango] SKIP route "${routeName}" - ${err.message}`,
173
+ );
174
+ notifyOnError(
175
+ registry,
176
+ err,
177
+ "prerender",
178
+ routeName,
179
+ undefined,
180
+ true,
181
+ );
182
+ continue;
183
+ }
184
+ // Regular error: fail the build
185
+ console.error(
186
+ `[rango] Failed to get params for prerender route "${routeName}": ${err.message}`,
122
187
  );
123
- continue;
188
+ notifyOnError(registry, err, "prerender", routeName);
189
+ throw err;
124
190
  }
125
- // Regular error: fail the build
126
- console.error(
127
- `[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`,
191
+ } else {
192
+ console.warn(
193
+ `[rango] Dynamic prerender route "${routeName}" has no getParams(), skipping`,
128
194
  );
129
- notifyOnError(registry, err, "prerender", routeName);
130
- throw err;
131
195
  }
132
- } else {
133
- console.warn(
134
- `[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`,
135
- );
136
196
  }
137
197
  }
138
198
  }
199
+ } finally {
200
+ if (progressInterval) {
201
+ clearInterval(progressInterval);
202
+ const elapsed = ((performance.now() - paramsStart) / 1000).toFixed(1);
203
+ console.log(
204
+ `[rango] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`,
205
+ );
206
+ }
139
207
  }
140
208
 
141
- if (entries.length === 0) return;
209
+ if (entries.length === 0) {
210
+ debug?.(
211
+ "no prerender entries (done in %sms)",
212
+ (performance.now() - overallStart).toFixed(1),
213
+ );
214
+ return;
215
+ }
142
216
 
143
217
  // Determine the max concurrency for the log header
144
218
  const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
145
219
  const concurrencyNote =
146
220
  maxConcurrency > 1 ? ` (concurrency: ${maxConcurrency})` : "";
147
221
  console.log(
148
- `[rsc-router] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`,
222
+ `[rango] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`,
223
+ );
224
+ debug?.(
225
+ "prerender loop: %d entries, max concurrency %d",
226
+ entries.length,
227
+ maxConcurrency,
149
228
  );
150
229
 
151
230
  const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
152
231
 
153
- const collectedData: Record<string, any> = {};
232
+ const manifestEntries: Record<string, string> = {};
154
233
  let doneCount = 0;
155
234
  let skipCount = 0;
235
+ // #587: a render error reaches here (matchForPrerender now re-throws it rather
236
+ // than baking the error boundary). Default "fail" fails the build; "warn" logs
237
+ // and skips baking the URL.
238
+ const prerenderOnError = state.opts?.prerenderOnError ?? "fail";
156
239
  const startTotal = performance.now();
157
240
 
158
241
  // Group entries by concurrency for batched rendering.
@@ -173,6 +256,7 @@ export async function expandPrerenderRoutes(
173
256
  {},
174
257
  entry.buildVars,
175
258
  entry.isPassthroughRoute,
259
+ state.resolvedBuildEnv,
176
260
  );
177
261
  if (!result) continue;
178
262
 
@@ -180,62 +264,60 @@ export async function expandPrerenderRoutes(
180
264
  if (result.passthrough) {
181
265
  const elapsed = (performance.now() - startUrl).toFixed(0);
182
266
  console.log(
183
- `[rsc-router] PASS ${entry.urlPath.padEnd(40)} (${elapsed}ms) - live fallback`,
267
+ `[rango] PASS ${entry.urlPath.padEnd(40)} (${elapsed}ms) - live fallback`,
184
268
  );
185
269
  doneCount++;
186
270
  break;
187
271
  }
188
272
 
189
273
  const paramHash = hashParams(result.params || {});
190
- collectedData[`${result.routeName}/${paramHash}`] = {
274
+ const mainKey = `${result.routeName}/${paramHash}`;
275
+ const mainValue = JSON.stringify({
191
276
  segments: result.segments,
192
277
  handles: result.handles,
193
- };
278
+ });
279
+ manifestEntries[mainKey] = stageBuildAssetModule(
280
+ state.projectRoot,
281
+ "__pr",
282
+ mainValue,
283
+ );
194
284
  if (result.interceptSegments?.length) {
195
- collectedData[`${result.routeName}/${paramHash}/i`] = {
285
+ const interceptKey = `${result.routeName}/${paramHash}/i`;
286
+ const interceptValue = JSON.stringify({
196
287
  segments: [...result.segments, ...result.interceptSegments],
197
- handles: {
198
- ...result.handles,
199
- ...(result.interceptHandles || {}),
200
- },
201
- };
288
+ // interceptHandles is the pre-encoded MERGED (main + intercept)
289
+ // handle string (the producer merged before encoding).
290
+ handles: result.interceptHandles ?? "",
291
+ });
292
+ manifestEntries[interceptKey] = stageBuildAssetModule(
293
+ state.projectRoot,
294
+ "__pr",
295
+ interceptValue,
296
+ );
202
297
  }
203
298
  const elapsed = (performance.now() - startUrl).toFixed(0);
204
299
  console.log(
205
- `[rsc-router] OK ${entry.urlPath.padEnd(40)} (${elapsed}ms)`,
300
+ `[rango] OK ${entry.urlPath.padEnd(40)} (${elapsed}ms)`,
206
301
  );
207
302
  doneCount++;
208
303
  break;
209
304
  } catch (err: any) {
210
- if (err.name === "Skip") {
211
- const elapsed = (performance.now() - startUrl).toFixed(0);
212
- console.log(
213
- `[rsc-router] SKIP ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`,
214
- );
215
- skipCount++;
216
- notifyOnError(
217
- registry,
218
- err,
219
- "prerender",
220
- entry.routeName,
221
- entry.urlPath,
222
- true,
223
- );
224
- break;
225
- }
226
- // Regular error: log, notify, and fail the build
305
+ // Skip, or a render error under prerender.onError "warn": skip the
306
+ // URL (never bake the error page, #587). A render error under the
307
+ // default "fail" re-throws below to fail the build.
227
308
  const elapsed = (performance.now() - startUrl).toFixed(0);
228
- console.error(
229
- `[rsc-router] FAIL ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`,
230
- );
231
- notifyOnError(
309
+ resolvePrerenderError(
232
310
  registry,
233
311
  err,
312
+ prerenderOnError,
313
+ entry.urlPath.padEnd(40),
314
+ elapsed,
234
315
  "prerender",
235
316
  entry.routeName,
236
317
  entry.urlPath,
237
318
  );
238
- throw err;
319
+ skipCount++;
320
+ break;
239
321
  }
240
322
  }
241
323
  },
@@ -244,19 +326,27 @@ export async function expandPrerenderRoutes(
244
326
 
245
327
  const totalElapsed = (performance.now() - startTotal).toFixed(0);
246
328
  if (doneCount > 0) {
247
- state.prerenderCollectedData = collectedData;
329
+ state.prerenderManifestEntries = manifestEntries;
248
330
  }
249
331
  const parts = [`${doneCount} done`];
250
332
  if (skipCount > 0) parts.push(`${skipCount} skipped`);
251
333
  console.log(
252
- `[rsc-router] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`,
334
+ `[rango] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`,
335
+ );
336
+ debug?.(
337
+ "expandPrerenderRoutes done: %d done, %d skipped, %sms (overall %sms)",
338
+ doneCount,
339
+ skipCount,
340
+ totalElapsed,
341
+ (performance.now() - overallStart).toFixed(1),
253
342
  );
254
343
  }
255
344
 
256
345
  /**
257
346
  * Render Static handlers at build time. Each Static handler is called
258
347
  * with a synthetic BuildContext and its output is RSC-serialized.
259
- * Stores collected data in state.staticCollectedData.
348
+ * Stages asset modules and stores handlerId-to-file entries in
349
+ * state.staticManifestEntries.
260
350
  */
261
351
  export async function renderStaticHandlers(
262
352
  state: DiscoveryState,
@@ -270,22 +360,24 @@ export async function renderStaticHandlers(
270
360
  )
271
361
  return;
272
362
 
273
- const collected: Record<
274
- string,
275
- { encoded: string; handles: Record<string, unknown[]> }
276
- > = {};
363
+ const overallStart = debug ? performance.now() : 0;
364
+ debug?.(
365
+ "renderStaticHandlers: start (%d static module(s))",
366
+ state.resolvedStaticModules.size,
367
+ );
368
+
369
+ const manifestEntries: Record<string, string> = {};
277
370
  let staticDone = 0;
278
371
  let staticSkip = 0;
279
372
  let totalStaticCount = 0;
373
+ const prerenderOnError = state.opts?.prerenderOnError ?? "fail";
280
374
 
281
375
  // Count handlers for the log header
282
376
  for (const [, exportNames] of state.resolvedStaticModules) {
283
377
  totalStaticCount += exportNames.length;
284
378
  }
285
379
  const startStatic = performance.now();
286
- console.log(
287
- `[rsc-router] Rendering ${totalStaticCount} static handler(s)...`,
288
- );
380
+ console.log(`[rango] Rendering ${totalStaticCount} static handler(s)...`);
289
381
 
290
382
  for (const [moduleId, exportNames] of state.resolvedStaticModules) {
291
383
  let mod: any;
@@ -293,7 +385,7 @@ export async function renderStaticHandlers(
293
385
  mod = await rscEnv!.runner.import(moduleId);
294
386
  } catch (err: any) {
295
387
  console.error(
296
- `[rsc-router] Failed to import static module ${moduleId}: ${err.message}`,
388
+ `[rango] Failed to import static module ${moduleId}: ${err.message}`,
297
389
  );
298
390
  notifyOnError(registry, err, "static");
299
391
  throw err;
@@ -314,52 +406,62 @@ export async function renderStaticHandlers(
314
406
  def.handler,
315
407
  def.$$id,
316
408
  (def as any).$$routePrefix,
409
+ state.resolvedBuildEnv,
410
+ !state.isBuildMode,
317
411
  );
318
412
  if (result) {
319
- collected[def.$$id] = result;
320
- const elapsed = (performance.now() - startHandler).toFixed(0);
321
- console.log(
322
- `[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`,
413
+ // result.handles is the pre-encoded handle string ("" when none).
414
+ const hasHandles = result.handles !== "";
415
+ const exportValue = hasHandles
416
+ ? JSON.stringify(result)
417
+ : JSON.stringify(result.encoded);
418
+ manifestEntries[def.$$id] = stageBuildAssetModule(
419
+ state.projectRoot,
420
+ "__st",
421
+ exportValue,
323
422
  );
423
+ const elapsed = (performance.now() - startHandler).toFixed(0);
424
+ console.log(`[rango] OK ${name.padEnd(40)} (${elapsed}ms)`);
324
425
  staticDone++;
325
426
  handled = true;
326
427
  break;
327
428
  }
328
429
  } catch (err: any) {
329
- if (err.name === "Skip") {
330
- const elapsed = (performance.now() - startHandler).toFixed(0);
331
- console.log(
332
- `[rsc-router] SKIP ${name.padEnd(40)} (${elapsed}ms) - ${err.message}`,
333
- );
334
- staticSkip++;
335
- notifyOnError(registry, err, "static", undefined, undefined, true);
336
- handled = true;
337
- break;
338
- }
339
- // Regular error: log, notify, and fail the build
430
+ // Same Skip/warn/fail policy as the prerender loop (shared helper).
340
431
  const elapsed = (performance.now() - startHandler).toFixed(0);
341
- console.error(
342
- `[rsc-router] FAIL ${name.padEnd(40)} (${elapsed}ms) - ${err.message}`,
432
+ resolvePrerenderError(
433
+ registry,
434
+ err,
435
+ prerenderOnError,
436
+ name.padEnd(40),
437
+ elapsed,
438
+ "static",
343
439
  );
344
- notifyOnError(registry, err, "static");
345
- throw err;
440
+ staticSkip++;
441
+ handled = true;
442
+ break;
346
443
  }
347
444
  }
348
445
  if (!handled) {
349
- console.warn(
350
- `[rsc-router] No router could render static handler "${name}"`,
351
- );
446
+ console.warn(`[rango] No router could render static handler "${name}"`);
352
447
  }
353
448
  }
354
449
  }
355
450
 
356
451
  const totalStaticElapsed = (performance.now() - startStatic).toFixed(0);
357
452
  if (staticDone > 0) {
358
- state.staticCollectedData = collected;
453
+ state.staticManifestEntries = manifestEntries;
359
454
  }
360
455
  const staticParts = [`${staticDone} done`];
361
456
  if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
362
457
  console.log(
363
- `[rsc-router] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`,
458
+ `[rango] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`,
459
+ );
460
+ debug?.(
461
+ "renderStaticHandlers done: %d done, %d skipped, %sms (overall %sms)",
462
+ staticDone,
463
+ staticSkip,
464
+ totalStaticElapsed,
465
+ (performance.now() - overallStart).toFixed(1),
364
466
  );
365
467
  }