@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bdaf10aa

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 (449) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +303 -741
  3. package/dist/bin/rango.js +730 -184
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +4344 -1335
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +86 -15
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +85 -6
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +251 -24
  13. package/skills/caching/SKILL.md +375 -17
  14. package/skills/catalog.json +271 -0
  15. package/skills/comparison/SKILL.md +50 -0
  16. package/skills/comparison/agents/openai.yaml +4 -0
  17. package/skills/comparison/references/framework-comparison.md +837 -0
  18. package/skills/composability/SKILL.md +110 -4
  19. package/skills/css/SKILL.md +76 -0
  20. package/skills/debug-manifest/SKILL.md +5 -3
  21. package/skills/defer-hydration/SKILL.md +235 -0
  22. package/skills/document-cache/SKILL.md +87 -56
  23. package/skills/fonts/SKILL.md +1 -1
  24. package/skills/handler-use/SKILL.md +364 -0
  25. package/skills/hooks/SKILL.md +73 -691
  26. package/skills/hooks/data.md +273 -0
  27. package/skills/hooks/handle-and-actions.md +103 -0
  28. package/skills/hooks/navigation.md +110 -0
  29. package/skills/hooks/outlets.md +41 -0
  30. package/skills/hooks/state.md +228 -0
  31. package/skills/hooks/urls.md +135 -0
  32. package/skills/host-router/SKILL.md +129 -27
  33. package/skills/i18n/SKILL.md +276 -0
  34. package/skills/intercept/SKILL.md +94 -18
  35. package/skills/layout/SKILL.md +62 -19
  36. package/skills/links/SKILL.md +249 -17
  37. package/skills/loader/SKILL.md +302 -54
  38. package/skills/middleware/SKILL.md +59 -16
  39. package/skills/migrate-nextjs/SKILL.md +745 -0
  40. package/skills/migrate-react-router/SKILL.md +153 -0
  41. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  42. package/skills/migrate-react-router/component-migration.md +196 -0
  43. package/skills/migrate-react-router/data-and-actions.md +225 -0
  44. package/skills/migrate-react-router/route-mapping.md +271 -0
  45. package/skills/mime-routes/SKILL.md +29 -2
  46. package/skills/observability/SKILL.md +202 -0
  47. package/skills/parallel/SKILL.md +225 -10
  48. package/skills/ppr/SKILL.md +622 -0
  49. package/skills/prerender/SKILL.md +178 -124
  50. package/skills/rango/SKILL.md +318 -24
  51. package/skills/react-compiler/SKILL.md +168 -0
  52. package/skills/response-routes/SKILL.md +138 -49
  53. package/skills/route/SKILL.md +172 -9
  54. package/skills/router-setup/SKILL.md +131 -11
  55. package/skills/scripts/SKILL.md +179 -0
  56. package/skills/server-actions/SKILL.md +776 -0
  57. package/skills/shell-manifest/SKILL.md +185 -0
  58. package/skills/streams-and-websockets/SKILL.md +283 -0
  59. package/skills/tailwind/SKILL.md +28 -4
  60. package/skills/testing/SKILL.md +130 -0
  61. package/skills/testing/bindings.md +103 -0
  62. package/skills/testing/cache-prerender.md +127 -0
  63. package/skills/testing/client-components.md +124 -0
  64. package/skills/testing/e2e-parity.md +125 -0
  65. package/skills/testing/flight.md +91 -0
  66. package/skills/testing/handles.md +131 -0
  67. package/skills/testing/loader.md +128 -0
  68. package/skills/testing/middleware.md +99 -0
  69. package/skills/testing/render-handler.md +122 -0
  70. package/skills/testing/response-routes.md +95 -0
  71. package/skills/testing/reverse-and-types.md +85 -0
  72. package/skills/testing/server-actions.md +107 -0
  73. package/skills/testing/server-tree.md +128 -0
  74. package/skills/testing/setup.md +123 -0
  75. package/skills/theme/SKILL.md +1 -1
  76. package/skills/typesafety/SKILL.md +45 -616
  77. package/skills/typesafety/env-and-bindings.md +254 -0
  78. package/skills/typesafety/generated-files-and-cli.md +335 -0
  79. package/skills/typesafety/params-and-search.md +153 -0
  80. package/skills/typesafety/route-types.md +209 -0
  81. package/skills/use-cache/SKILL.md +74 -15
  82. package/skills/vercel/SKILL.md +128 -0
  83. package/skills/view-transitions/SKILL.md +337 -0
  84. package/src/__augment-tests__/augment.ts +81 -0
  85. package/src/__augment-tests__/augmented.check.ts +116 -0
  86. package/src/__internal.ts +1 -66
  87. package/src/browser/action-coordinator.ts +53 -36
  88. package/src/browser/action-fence.ts +47 -0
  89. package/src/browser/app-shell.ts +39 -0
  90. package/src/browser/app-version.ts +14 -0
  91. package/src/browser/connection-warmup.ts +134 -0
  92. package/src/browser/cookie-name.ts +140 -0
  93. package/src/browser/event-controller.ts +257 -158
  94. package/src/browser/history-state.ts +21 -0
  95. package/src/browser/index.ts +3 -3
  96. package/src/browser/invalidate-client-cache.ts +52 -0
  97. package/src/browser/logging.ts +28 -0
  98. package/src/browser/merge-segment-loaders.ts +6 -4
  99. package/src/browser/navigation-bridge.ts +132 -33
  100. package/src/browser/navigation-client.ts +218 -68
  101. package/src/browser/navigation-store-handle.ts +38 -0
  102. package/src/browser/navigation-store.ts +203 -80
  103. package/src/browser/navigation-transaction.ts +18 -66
  104. package/src/browser/network-error-handler.ts +34 -7
  105. package/src/browser/partial-update.ts +241 -127
  106. package/src/browser/prefetch/cache.ts +271 -44
  107. package/src/browser/prefetch/fetch.ts +367 -40
  108. package/src/browser/prefetch/queue.ts +144 -23
  109. package/src/browser/prefetch/resource-ready.ts +77 -0
  110. package/src/browser/rango-state.ts +158 -76
  111. package/src/browser/react/Link.tsx +121 -16
  112. package/src/browser/react/NavigationProvider.tsx +240 -122
  113. package/src/browser/react/ScrollRestoration.tsx +10 -6
  114. package/src/browser/react/context.ts +7 -2
  115. package/src/browser/react/filter-segment-order.ts +66 -7
  116. package/src/browser/react/index.ts +0 -48
  117. package/src/browser/react/location-state-shared.ts +178 -8
  118. package/src/browser/react/location-state.ts +39 -14
  119. package/src/browser/react/use-action.ts +6 -15
  120. package/src/browser/react/use-handle.ts +23 -69
  121. package/src/browser/react/use-href.tsx +8 -1
  122. package/src/browser/react/use-link-status.ts +33 -8
  123. package/src/browser/react/use-navigation.ts +32 -7
  124. package/src/browser/react/use-params.ts +20 -10
  125. package/src/browser/react/use-reverse.ts +106 -0
  126. package/src/browser/react/use-router.ts +46 -11
  127. package/src/browser/react/use-search-params.ts +0 -5
  128. package/src/browser/react/use-segments.ts +11 -21
  129. package/src/browser/response-adapter.ts +99 -8
  130. package/src/browser/rsc-router.tsx +272 -80
  131. package/src/browser/scroll-restoration.ts +56 -22
  132. package/src/browser/segment-reconciler.ts +44 -7
  133. package/src/browser/segment-structure-assert.ts +2 -2
  134. package/src/browser/server-action-bridge.ts +244 -71
  135. package/src/browser/types.ts +136 -12
  136. package/src/browser/validate-redirect-origin.ts +43 -16
  137. package/src/build/collect-fallback-refs.ts +107 -0
  138. package/src/build/generate-manifest.ts +207 -158
  139. package/src/build/generate-route-types.ts +6 -1
  140. package/src/build/index.ts +11 -3
  141. package/src/build/prefix-tree-utils.ts +123 -0
  142. package/src/build/route-trie.ts +198 -41
  143. package/src/build/route-types/ast-route-extraction.ts +15 -8
  144. package/src/build/route-types/codegen.ts +16 -5
  145. package/src/build/route-types/include-resolution.ts +464 -63
  146. package/src/build/route-types/param-extraction.ts +6 -3
  147. package/src/build/route-types/per-module-writer.ts +22 -6
  148. package/src/build/route-types/router-processing.ts +336 -110
  149. package/src/build/route-types/scan-filter.ts +9 -2
  150. package/src/build/route-types/source-scan.ts +216 -0
  151. package/src/build/runtime-discovery.ts +13 -21
  152. package/src/cache/cache-error.ts +104 -0
  153. package/src/cache/cache-key-utils.ts +58 -13
  154. package/src/cache/cache-policy.ts +108 -34
  155. package/src/cache/cache-runtime.ts +454 -97
  156. package/src/cache/cache-scope.ts +235 -103
  157. package/src/cache/cache-tag.ts +149 -0
  158. package/src/cache/cf/cf-base64.ts +33 -0
  159. package/src/cache/cf/cf-cache-constants.ts +127 -0
  160. package/src/cache/cf/cf-cache-store.ts +2446 -170
  161. package/src/cache/cf/cf-cache-types.ts +349 -0
  162. package/src/cache/cf/cf-kv-utils.ts +46 -0
  163. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  164. package/src/cache/cf/index.ts +11 -17
  165. package/src/cache/document-cache.ts +144 -49
  166. package/src/cache/handle-snapshot.ts +70 -0
  167. package/src/cache/index.ts +24 -20
  168. package/src/cache/memory-segment-store.ts +243 -37
  169. package/src/cache/profile-registry.ts +46 -31
  170. package/src/cache/read-through-swr.ts +56 -12
  171. package/src/cache/segment-codec.ts +13 -21
  172. package/src/cache/shell-snapshot.ts +417 -0
  173. package/src/cache/tag-invalidation.ts +230 -0
  174. package/src/cache/taint.ts +55 -0
  175. package/src/cache/types.ts +194 -99
  176. package/src/cache/vercel/index.ts +11 -0
  177. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  178. package/src/client.rsc.tsx +41 -21
  179. package/src/client.tsx +116 -290
  180. package/src/cloudflare/index.ts +11 -0
  181. package/src/cloudflare/tracing.ts +108 -0
  182. package/src/component-utils.ts +19 -0
  183. package/src/components/DefaultDocument.tsx +8 -2
  184. package/src/context-var.ts +84 -2
  185. package/src/debug.ts +2 -2
  186. package/src/decode-loader-results.ts +52 -0
  187. package/src/defer.ts +185 -0
  188. package/src/deps/ssr.ts +0 -1
  189. package/src/encode-kv.ts +49 -0
  190. package/src/errors.ts +30 -4
  191. package/src/escape-script.ts +52 -0
  192. package/src/handle.ts +104 -34
  193. package/src/handles/MetaTags.tsx +24 -53
  194. package/src/handles/Scripts.tsx +183 -0
  195. package/src/handles/breadcrumbs.ts +35 -8
  196. package/src/handles/deferred-resolution.ts +127 -0
  197. package/src/handles/is-thenable.ts +18 -0
  198. package/src/handles/meta.ts +14 -40
  199. package/src/handles/script.ts +244 -0
  200. package/src/host/cookie-handler.ts +9 -60
  201. package/src/host/errors.ts +13 -22
  202. package/src/host/index.ts +9 -2
  203. package/src/host/pattern-matcher.ts +23 -52
  204. package/src/host/router.ts +107 -99
  205. package/src/host/testing.ts +40 -27
  206. package/src/host/types.ts +37 -4
  207. package/src/host/utils.ts +1 -1
  208. package/src/href-client.ts +137 -22
  209. package/src/index.rsc.ts +100 -13
  210. package/src/index.ts +143 -19
  211. package/src/internal-debug.ts +11 -10
  212. package/src/loader-store.ts +500 -0
  213. package/src/loader.rsc.ts +20 -13
  214. package/src/loader.ts +12 -11
  215. package/src/missing-id-error.ts +68 -0
  216. package/src/outlet-context.ts +1 -1
  217. package/src/outlet-provider.tsx +1 -5
  218. package/src/prerender/param-hash.ts +16 -16
  219. package/src/prerender/store.ts +37 -41
  220. package/src/prerender.ts +215 -86
  221. package/src/redirect-origin.ts +114 -0
  222. package/src/regex-escape.ts +8 -0
  223. package/src/render-error-thrower.tsx +20 -0
  224. package/src/response-utils.ts +62 -0
  225. package/src/reverse.ts +65 -15
  226. package/src/root-error-boundary.tsx +1 -19
  227. package/src/route-content-wrapper.tsx +19 -77
  228. package/src/route-definition/dsl-helpers.ts +485 -303
  229. package/src/route-definition/helper-factories.ts +28 -140
  230. package/src/route-definition/helpers-types.ts +153 -77
  231. package/src/route-definition/index.ts +4 -2
  232. package/src/route-definition/redirect.ts +53 -12
  233. package/src/route-definition/resolve-handler-use.ts +160 -0
  234. package/src/route-definition/use-item-types.ts +29 -0
  235. package/src/route-map-builder.ts +48 -21
  236. package/src/route-types.ts +37 -46
  237. package/src/router/basename.ts +14 -0
  238. package/src/router/content-negotiation.ts +164 -17
  239. package/src/router/error-handling.ts +45 -18
  240. package/src/router/find-match.ts +130 -29
  241. package/src/router/handler-context.ts +83 -39
  242. package/src/router/instrument.ts +355 -0
  243. package/src/router/intercept-resolution.ts +50 -24
  244. package/src/router/lazy-includes.ts +89 -63
  245. package/src/router/loader-resolution.ts +286 -56
  246. package/src/router/logging.ts +5 -8
  247. package/src/router/manifest.ts +105 -56
  248. package/src/router/match-api.ts +178 -218
  249. package/src/router/match-context.ts +0 -22
  250. package/src/router/match-handlers.ts +211 -165
  251. package/src/router/match-middleware/background-revalidation.ts +66 -22
  252. package/src/router/match-middleware/cache-lookup.ts +214 -263
  253. package/src/router/match-middleware/cache-store.ts +105 -50
  254. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  255. package/src/router/match-middleware/segment-resolution.ts +52 -18
  256. package/src/router/match-pipelines.ts +1 -42
  257. package/src/router/match-result.ts +128 -44
  258. package/src/router/metrics.ts +5 -34
  259. package/src/router/middleware-types.ts +13 -142
  260. package/src/router/middleware.ts +301 -177
  261. package/src/router/navigation-snapshot.ts +133 -0
  262. package/src/router/params-util.ts +23 -0
  263. package/src/router/parse-pattern.ts +115 -0
  264. package/src/router/pattern-matching.ts +181 -150
  265. package/src/router/prefetch-cache-ttl.ts +51 -0
  266. package/src/router/prefetch-limits.ts +37 -0
  267. package/src/router/prerender-match.ts +203 -58
  268. package/src/router/preview-match.ts +35 -103
  269. package/src/router/request-classification.ts +291 -0
  270. package/src/router/revalidation.ts +123 -73
  271. package/src/router/route-snapshot.ts +256 -0
  272. package/src/router/router-context.ts +11 -29
  273. package/src/router/router-interfaces.ts +146 -35
  274. package/src/router/router-options.ts +202 -15
  275. package/src/router/router-registry.ts +2 -5
  276. package/src/router/segment-resolution/fresh.ts +301 -78
  277. package/src/router/segment-resolution/helpers.ts +115 -30
  278. package/src/router/segment-resolution/loader-cache.ts +156 -39
  279. package/src/router/segment-resolution/loader-mask.ts +60 -0
  280. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  281. package/src/router/segment-resolution/mask-nested.ts +83 -0
  282. package/src/router/segment-resolution/revalidation.ts +477 -385
  283. package/src/router/segment-resolution/static-store.ts +19 -5
  284. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  285. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  286. package/src/router/segment-resolution.ts +5 -1
  287. package/src/router/segment-wrappers.ts +8 -5
  288. package/src/router/state-cookie-name.ts +33 -0
  289. package/src/router/substitute-pattern-params.ts +75 -0
  290. package/src/router/telemetry-otel.ts +160 -200
  291. package/src/router/telemetry.ts +105 -20
  292. package/src/router/timeout.ts +0 -20
  293. package/src/router/tracing.ts +215 -0
  294. package/src/router/trie-matching.ts +171 -59
  295. package/src/router/types.ts +10 -63
  296. package/src/router/url-params.ts +57 -0
  297. package/src/router.ts +210 -71
  298. package/src/rsc/full-payload.ts +70 -0
  299. package/src/rsc/handler-context.ts +3 -2
  300. package/src/rsc/handler.ts +682 -508
  301. package/src/rsc/helpers.ts +168 -46
  302. package/src/rsc/index.ts +2 -5
  303. package/src/rsc/json-route-result.ts +38 -0
  304. package/src/rsc/loader-fetch.ts +127 -31
  305. package/src/rsc/manifest-init.ts +33 -42
  306. package/src/rsc/nonce.ts +10 -1
  307. package/src/rsc/origin-guard.ts +39 -25
  308. package/src/rsc/progressive-enhancement.ts +138 -15
  309. package/src/rsc/redirect-guard.ts +100 -0
  310. package/src/rsc/response-cache-serve.ts +238 -0
  311. package/src/rsc/response-error.ts +79 -12
  312. package/src/rsc/response-route-handler.ts +99 -189
  313. package/src/rsc/rsc-rendering.ts +509 -73
  314. package/src/rsc/runtime-warnings.ts +23 -10
  315. package/src/rsc/server-action.ts +287 -113
  316. package/src/rsc/shell-capture.ts +1190 -0
  317. package/src/rsc/shell-serve.ts +181 -0
  318. package/src/rsc/ssr-setup.ts +18 -2
  319. package/src/rsc/transition-gate.ts +89 -0
  320. package/src/rsc/types.ts +62 -6
  321. package/src/runtime-env.ts +18 -0
  322. package/src/search-params.ts +35 -30
  323. package/src/segment-content-promise.ts +67 -0
  324. package/src/segment-loader-promise.ts +167 -0
  325. package/src/segment-system.tsx +449 -132
  326. package/src/serialize.ts +243 -0
  327. package/src/server/context.ts +367 -61
  328. package/src/server/cookie-parse.ts +32 -0
  329. package/src/server/cookie-store.ts +152 -5
  330. package/src/server/handle-store.ts +40 -38
  331. package/src/server/loader-registry.ts +38 -46
  332. package/src/server/request-context.ts +558 -173
  333. package/src/ssr/index.tsx +491 -174
  334. package/src/ssr/inject-rsc-eager.ts +167 -0
  335. package/src/ssr/ssr-root.tsx +228 -0
  336. package/src/static-handler.ts +27 -18
  337. package/src/testing/cache-status.ts +162 -0
  338. package/src/testing/collect-handle.ts +46 -0
  339. package/src/testing/dispatch.ts +813 -0
  340. package/src/testing/dom.entry.ts +22 -0
  341. package/src/testing/e2e/fixture.ts +188 -0
  342. package/src/testing/e2e/index.ts +128 -0
  343. package/src/testing/e2e/matchers.ts +35 -0
  344. package/src/testing/e2e/page-helpers.ts +272 -0
  345. package/src/testing/e2e/parity.ts +387 -0
  346. package/src/testing/e2e/server.ts +195 -0
  347. package/src/testing/flight-matchers.ts +97 -0
  348. package/src/testing/flight-normalize.ts +11 -0
  349. package/src/testing/flight-runtime.d.ts +57 -0
  350. package/src/testing/flight-tree.ts +682 -0
  351. package/src/testing/flight.entry.ts +52 -0
  352. package/src/testing/flight.ts +257 -0
  353. package/src/testing/generated-routes.ts +199 -0
  354. package/src/testing/index.ts +105 -0
  355. package/src/testing/internal/context.ts +371 -0
  356. package/src/testing/internal/flight-client-globals.ts +30 -0
  357. package/src/testing/internal/seed-vars.ts +54 -0
  358. package/src/testing/render-handler.ts +357 -0
  359. package/src/testing/render-route.tsx +584 -0
  360. package/src/testing/run-loader.ts +385 -0
  361. package/src/testing/run-middleware.ts +205 -0
  362. package/src/testing/run-transition-when.ts +164 -0
  363. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  364. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  365. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  366. package/src/testing/vitest-stubs/version.ts +5 -0
  367. package/src/testing/vitest.ts +305 -0
  368. package/src/theme/ThemeProvider.tsx +56 -84
  369. package/src/theme/ThemeScript.tsx +7 -9
  370. package/src/theme/constants.ts +52 -13
  371. package/src/theme/index.ts +0 -7
  372. package/src/theme/theme-context.ts +1 -5
  373. package/src/theme/theme-script.ts +22 -21
  374. package/src/theme/use-theme.ts +0 -3
  375. package/src/types/boundaries.ts +0 -35
  376. package/src/types/cache-types.ts +17 -8
  377. package/src/types/error-types.ts +30 -90
  378. package/src/types/global-namespace.ts +54 -41
  379. package/src/types/handler-context.ts +234 -82
  380. package/src/types/index.ts +3 -10
  381. package/src/types/loader-types.ts +44 -15
  382. package/src/types/request-scope.ts +112 -0
  383. package/src/types/route-config.ts +20 -52
  384. package/src/types/route-entry.ts +19 -7
  385. package/src/types/segments.ts +137 -14
  386. package/src/urls/include-helper.ts +40 -75
  387. package/src/urls/include-provider.ts +71 -0
  388. package/src/urls/index.ts +2 -11
  389. package/src/urls/path-helper-types.ts +102 -23
  390. package/src/urls/path-helper.ts +62 -111
  391. package/src/urls/pattern-types.ts +84 -19
  392. package/src/urls/response-types.ts +25 -22
  393. package/src/urls/type-extraction.ts +98 -154
  394. package/src/urls/urls-function.ts +1 -19
  395. package/src/use-loader.tsx +346 -89
  396. package/src/vercel/index.ts +11 -0
  397. package/src/vercel/tracing.ts +88 -0
  398. package/src/vite/debug.ts +185 -0
  399. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  400. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  401. package/src/vite/discovery/discover-routers.ts +130 -85
  402. package/src/vite/discovery/discovery-errors.ts +255 -0
  403. package/src/vite/discovery/gate-state.ts +171 -0
  404. package/src/vite/discovery/prerender-collection.ts +214 -132
  405. package/src/vite/discovery/route-types-writer.ts +40 -84
  406. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  407. package/src/vite/discovery/state.ts +57 -6
  408. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  409. package/src/vite/index.ts +15 -0
  410. package/src/vite/inject-client-debug.ts +88 -0
  411. package/src/vite/plugin-types.ts +234 -62
  412. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  413. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  414. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  415. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  416. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  417. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  418. package/src/vite/plugins/expose-action-id.ts +49 -98
  419. package/src/vite/plugins/expose-id-utils.ts +96 -51
  420. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  421. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  422. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  423. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  424. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  425. package/src/vite/plugins/performance-tracks.ts +89 -0
  426. package/src/vite/plugins/refresh-cmd.ts +89 -27
  427. package/src/vite/plugins/use-cache-transform.ts +73 -83
  428. package/src/vite/plugins/vercel-output.ts +384 -0
  429. package/src/vite/plugins/version-injector.ts +40 -29
  430. package/src/vite/plugins/version-plugin.ts +46 -37
  431. package/src/vite/plugins/virtual-entries.ts +138 -27
  432. package/src/vite/rango.ts +353 -303
  433. package/src/vite/router-discovery.ts +1090 -166
  434. package/src/vite/utils/ast-handler-extract.ts +26 -35
  435. package/src/vite/utils/banner.ts +4 -4
  436. package/src/vite/utils/bundle-analysis.ts +10 -15
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +4 -59
  441. package/src/vite/utils/package-resolution.ts +20 -52
  442. package/src/vite/utils/prerender-utils.ts +98 -38
  443. package/src/vite/utils/shared-utils.ts +144 -44
  444. package/src/browser/action-response-classifier.ts +0 -99
  445. package/src/browser/react/use-client-cache.ts +0 -58
  446. package/src/browser/shallow.ts +0 -40
  447. package/src/handles/index.ts +0 -7
  448. package/src/network-error-thrower.tsx +0 -23
  449. package/src/router/middleware-cookies.ts +0 -55
@@ -15,6 +15,7 @@ import {
15
15
  import ts from "typescript";
16
16
  import { generateRouteTypesSource } from "./codegen.js";
17
17
  import type { ScanFilter } from "./scan-filter.js";
18
+ import { firstCodeMatchIndex } from "./source-scan.js";
18
19
  import {
19
20
  resolveImportedVariable,
20
21
  resolveImportPath,
@@ -38,6 +39,8 @@ function countPublicRouteEntries(source: string): number {
38
39
  }
39
40
 
40
41
  const ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
42
+ // Global variant for the code-region scan (firstCodeMatchIndex sets lastIndex).
43
+ const ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
41
44
 
42
45
  function isRoutableSourceFile(name: string): boolean {
43
46
  return (
@@ -45,12 +48,39 @@ function isRoutableSourceFile(name: string): boolean {
45
48
  name.endsWith(".tsx") ||
46
49
  name.endsWith(".js") ||
47
50
  name.endsWith(".jsx")) &&
48
- !name.includes(".gen.")
51
+ !name.includes(".gen.") &&
52
+ !name.includes(".test.") &&
53
+ !name.includes(".spec.")
49
54
  );
50
55
  }
51
56
 
52
- function findRouterFilesRecursive(
57
+ function isExcludedScanDir(name: string): boolean {
58
+ return (
59
+ name === "node_modules" ||
60
+ name === "dist" ||
61
+ name === "coverage" ||
62
+ name === "__tests__" ||
63
+ name === "__mocks__" ||
64
+ name.startsWith(".")
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Recursively collect source files whose code contains `pattern` (a comment- or
70
+ * string-only mention is ignored via firstCodeMatchIndex). Shared by createRouter
71
+ * and createHostRouter discovery, which differ only in the call pattern and
72
+ * `stopAtMatchDir`: createRouter treats a directory containing a match as a router
73
+ * root and stops descending it; createHostRouter descends the whole tree (the host
74
+ * entry sits above the sub-app router roots).
75
+ *
76
+ * `pattern` is the non-global tester (no lastIndex state); `patternG` is its global
77
+ * twin for the code-region scan.
78
+ */
79
+ function findCallSiteFilesRecursive(
53
80
  dir: string,
81
+ pattern: RegExp,
82
+ patternG: RegExp,
83
+ stopAtMatchDir: boolean,
54
84
  filter: ScanFilter | undefined,
55
85
  results: string[],
56
86
  ): void {
@@ -59,19 +89,18 @@ function findRouterFilesRecursive(
59
89
  entries = readdirSync(dir, { withFileTypes: true });
60
90
  } catch (err) {
61
91
  console.warn(
62
- `[rsc-router] Failed to scan directory ${dir}: ${(err as Error).message}`,
92
+ `[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
63
93
  );
64
94
  return;
65
95
  }
66
96
 
67
97
  const childDirs: string[] = [];
68
- const routerFilesInDir: string[] = [];
98
+ const matchesInDir: string[] = [];
69
99
 
70
100
  for (const entry of entries) {
71
101
  const fullPath = join(dir, entry.name);
72
102
  if (entry.isDirectory()) {
73
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
74
- childDirs.push(fullPath);
103
+ if (!isExcludedScanDir(entry.name)) childDirs.push(fullPath);
75
104
  continue;
76
105
  }
77
106
 
@@ -80,23 +109,36 @@ function findRouterFilesRecursive(
80
109
 
81
110
  try {
82
111
  const source = readFileSync(fullPath, "utf-8");
83
- if (ROUTER_CALL_PATTERN.test(source)) {
84
- routerFilesInDir.push(fullPath);
112
+ // Fast path: most files contain no `createRouter(` at all, so the cheap
113
+ // raw regex short-circuits before the code-region scan. Only a file that
114
+ // mentions the token (real call OR a comment/string mention) is rescanned
115
+ // over code regions — allocation-free, never building a stripped copy —
116
+ // so a mention inside a comment or string is not mistaken for a real
117
+ // router file (which previously triggered a spurious "Multiple routers
118
+ // found" error).
119
+ if (pattern.test(source) && firstCodeMatchIndex(source, patternG) >= 0) {
120
+ matchesInDir.push(fullPath);
85
121
  }
86
122
  } catch {
87
123
  continue;
88
124
  }
89
125
  }
90
126
 
91
- // A directory that contains a router file is treated as a router root.
92
- // Once found, deeper directories are skipped to avoid redundant scans.
93
- if (routerFilesInDir.length > 0) {
94
- results.push(...routerFilesInDir);
95
- return;
96
- }
97
-
98
- for (const childDir of childDirs) {
99
- findRouterFilesRecursive(childDir, filter, results);
127
+ results.push(...matchesInDir);
128
+
129
+ // createRouter (stopAtMatchDir): a directory that contains a match is a router
130
+ // root, so deeper directories are skipped. createHostRouter: always descend.
131
+ if (!stopAtMatchDir || matchesInDir.length === 0) {
132
+ for (const childDir of childDirs) {
133
+ findCallSiteFilesRecursive(
134
+ childDir,
135
+ pattern,
136
+ patternG,
137
+ stopAtMatchDir,
138
+ filter,
139
+ results,
140
+ );
141
+ }
100
142
  }
101
143
  }
102
144
 
@@ -132,7 +174,7 @@ export function findNestedRouterConflict(
132
174
 
133
175
  export function formatNestedRouterConflictError(
134
176
  conflict: { ancestor: string; nested: string },
135
- prefix = "[rsc-router]",
177
+ prefix = "[rango]",
136
178
  ): string {
137
179
  return (
138
180
  `${prefix} Nested router roots are not supported.\n` +
@@ -147,13 +189,26 @@ export function formatNestedRouterConflictError(
147
189
  // ---------------------------------------------------------------------------
148
190
 
149
191
  /**
150
- * Extract the url patterns variable from a router file using AST.
151
- * Detects two patterns:
192
+ * Result of extracting URL patterns from a router file.
193
+ * - "variable": a named variable reference (e.g., `.routes(patterns)` or `urls: patterns`)
194
+ * - "inline": an inline builder function (e.g., `.routes(({ path }) => [...])` or `urls: ({ path }) => [...]`)
195
+ */
196
+ export type UrlsExtractionResult =
197
+ | { kind: "variable"; name: string }
198
+ | { kind: "inline"; block: string };
199
+
200
+ /**
201
+ * Extract the url patterns from a router file using AST.
202
+ * Detects four patterns:
152
203
  * 1. createRouter(...).routes(variableName)
153
204
  * 2. createRouter({ urls: variableName, ... })
154
- * Returns the local variable name.
205
+ * 3. createRouter(...).routes(({ path, ... }) => [...])
206
+ * 4. createRouter({ urls: ({ path, ... }) => [...], ... })
207
+ * Returns either a variable name or an inline code block.
155
208
  */
156
- export function extractUrlsVariableFromRouter(code: string): string | null {
209
+ export function extractUrlsFromRouter(
210
+ code: string,
211
+ ): UrlsExtractionResult | null {
157
212
  const sourceFile = ts.createSourceFile(
158
213
  "router.tsx",
159
214
  code,
@@ -161,7 +216,7 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
161
216
  true,
162
217
  ts.ScriptKind.TSX,
163
218
  );
164
- let result: string | null = null;
219
+ let result: UrlsExtractionResult | null = null;
165
220
 
166
221
  function isCreateRouterCall(node: ts.Node): boolean {
167
222
  if (!ts.isCallExpression(node)) return false;
@@ -169,44 +224,108 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
169
224
  return ts.isIdentifier(callee) && callee.text === "createRouter";
170
225
  }
171
226
 
227
+ /** Check if a node is an arrow/function expression (inline builder). */
228
+ function isInlineBuilder(node: ts.Node): boolean {
229
+ return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
230
+ }
231
+
232
+ /** Check if a .routes() call chains from createRouter(). */
233
+ function isRoutesOnCreateRouter(node: ts.CallExpression): boolean {
234
+ if (
235
+ !ts.isPropertyAccessExpression(node.expression) ||
236
+ node.expression.name.text !== "routes"
237
+ )
238
+ return false;
239
+ let inner: ts.Expression = node.expression.expression;
240
+ while (
241
+ ts.isCallExpression(inner) &&
242
+ ts.isPropertyAccessExpression(inner.expression)
243
+ ) {
244
+ inner = inner.expression.expression;
245
+ }
246
+ return isCreateRouterCall(inner);
247
+ }
248
+
172
249
  function visit(node: ts.Node) {
173
250
  if (result) return;
174
251
 
175
- // Pattern 1: createRouter(...).routes(variableName)
176
- // The AST shape is CallExpression(.routes) -> PropertyAccessExpression -> CallExpression(createRouter)
252
+ // Pattern 1 & 3: createRouter(...).routes(variableName | builder)
177
253
  if (
178
254
  ts.isCallExpression(node) &&
179
- ts.isPropertyAccessExpression(node.expression) &&
180
- node.expression.name.text === "routes" &&
181
255
  node.arguments.length >= 1 &&
182
- ts.isIdentifier(node.arguments[0])
256
+ isRoutesOnCreateRouter(node)
183
257
  ) {
184
- // Walk up the chain: createRouter().middleware(...).routes(x) etc.
185
- // The innermost call should be createRouter(...)
186
- let inner: ts.Expression = node.expression.expression;
187
- while (
188
- ts.isCallExpression(inner) &&
189
- ts.isPropertyAccessExpression(inner.expression)
190
- ) {
191
- inner = inner.expression.expression;
192
- }
193
- if (isCreateRouterCall(inner)) {
194
- result = (node.arguments[0] as ts.Identifier).text;
195
- return;
258
+ const arg = node.arguments[0];
259
+ if (ts.isIdentifier(arg)) {
260
+ result = { kind: "variable", name: arg.text };
261
+ } else if (isInlineBuilder(arg)) {
262
+ result = { kind: "inline", block: arg.getText(sourceFile) };
196
263
  }
264
+ return;
197
265
  }
198
266
 
199
- // Pattern 2: createRouter({ urls: variableName, ... })
267
+ // Pattern 2 & 4: createRouter({ urls: variableName | builder, ... })
200
268
  if (isCreateRouterCall(node)) {
201
269
  const callExpr = node as ts.CallExpression;
202
- for (const arg of callExpr.arguments) {
270
+ for (const callArg of callExpr.arguments) {
271
+ if (ts.isObjectLiteralExpression(callArg)) {
272
+ for (const prop of callArg.properties) {
273
+ if (
274
+ ts.isPropertyAssignment(prop) &&
275
+ ts.isIdentifier(prop.name) &&
276
+ prop.name.text === "urls"
277
+ ) {
278
+ if (ts.isIdentifier(prop.initializer)) {
279
+ result = { kind: "variable", name: prop.initializer.text };
280
+ } else if (isInlineBuilder(prop.initializer)) {
281
+ result = {
282
+ kind: "inline",
283
+ block: prop.initializer.getText(sourceFile),
284
+ };
285
+ }
286
+ return;
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+
293
+ ts.forEachChild(node, visit);
294
+ }
295
+
296
+ visit(sourceFile);
297
+ return result;
298
+ }
299
+
300
+ /**
301
+ * Extract the `basename` string literal from createRouter({ basename: "..." }).
302
+ * Returns the basename value or undefined if not present.
303
+ */
304
+ export function extractBasenameFromRouter(code: string): string | undefined {
305
+ const sourceFile = ts.createSourceFile(
306
+ "router.tsx",
307
+ code,
308
+ ts.ScriptTarget.Latest,
309
+ true,
310
+ ts.ScriptKind.TSX,
311
+ );
312
+ let result: string | undefined;
313
+
314
+ function visit(node: ts.Node) {
315
+ if (result !== undefined) return;
316
+ if (
317
+ ts.isCallExpression(node) &&
318
+ ts.isIdentifier(node.expression) &&
319
+ node.expression.text === "createRouter"
320
+ ) {
321
+ for (const arg of node.arguments) {
203
322
  if (ts.isObjectLiteralExpression(arg)) {
204
323
  for (const prop of arg.properties) {
205
324
  if (
206
325
  ts.isPropertyAssignment(prop) &&
207
326
  ts.isIdentifier(prop.name) &&
208
- prop.name.text === "urls" &&
209
- ts.isIdentifier(prop.initializer)
327
+ prop.name.text === "basename" &&
328
+ ts.isStringLiteral(prop.initializer)
210
329
  ) {
211
330
  result = prop.initializer.text;
212
331
  return;
@@ -215,7 +334,6 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
215
334
  }
216
335
  }
217
336
  }
218
-
219
337
  ts.forEachChild(node, visit);
220
338
  }
221
339
 
@@ -223,9 +341,64 @@ export function extractUrlsVariableFromRouter(code: string): string | null {
223
341
  return result;
224
342
  }
225
343
 
344
+ /** Apply a basename prefix to all route patterns in a result set. */
345
+ function applyBasenameToRoutes(
346
+ result: {
347
+ routes: Record<string, string>;
348
+ searchSchemas: Record<string, Record<string, string>>;
349
+ },
350
+ basename: string,
351
+ ): {
352
+ routes: Record<string, string>;
353
+ searchSchemas: Record<string, Record<string, string>>;
354
+ } {
355
+ const prefixed: Record<string, string> = {};
356
+ for (const [name, pattern] of Object.entries(result.routes)) {
357
+ if (pattern === "/") {
358
+ prefixed[name] = basename;
359
+ } else if (basename.endsWith("/") && pattern.startsWith("/")) {
360
+ prefixed[name] = basename + pattern.slice(1);
361
+ } else {
362
+ prefixed[name] = basename + pattern;
363
+ }
364
+ }
365
+ return { routes: prefixed, searchSchemas: result.searchSchemas };
366
+ }
367
+
368
+ // Filesystem path of the generated route-types file for a router source file.
369
+ // Native separators — matches the self-gen-tracking Map key the watcher compares.
370
+ export function genFileTsPath(sourceFile: string): string {
371
+ const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
372
+ return join(dirname(sourceFile), `${base}.named-routes.gen.ts`);
373
+ }
374
+
375
+ // Search schemas for the gen file: prefer the runtime manifest's; when it omits
376
+ // them (some module-runner flows) fall back to static parsing filtered to the
377
+ // public route-name set. Returns the runtime value unchanged otherwise.
378
+ export function resolveSearchSchemas(
379
+ publicRouteNames: string[],
380
+ runtimeSchemas: Record<string, Record<string, string>> | undefined,
381
+ sourceFile: string,
382
+ ): Record<string, Record<string, string>> | undefined {
383
+ if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
384
+ return runtimeSchemas;
385
+ }
386
+ const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
387
+ if (Object.keys(staticParsed.searchSchemas).length === 0) {
388
+ return runtimeSchemas;
389
+ }
390
+ const filtered: Record<string, Record<string, string>> = {};
391
+ for (const name of publicRouteNames) {
392
+ const schema = staticParsed.searchSchemas[name];
393
+ if (schema) filtered[name] = schema;
394
+ }
395
+ return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
396
+ }
397
+
226
398
  /**
227
399
  * Resolve routes and search schemas from a router source file by following the
228
- * variable passed to `.routes(...)` or `urls: ...` in createRouter options.
400
+ * variable passed to `.routes(...)` or `urls: ...` in createRouter options,
401
+ * or by parsing an inline builder function directly.
229
402
  */
230
403
  export function buildCombinedRouteMapForRouterFile(routerFilePath: string): {
231
404
  routes: Record<string, string>;
@@ -238,21 +411,54 @@ export function buildCombinedRouteMapForRouterFile(routerFilePath: string): {
238
411
  return { routes: {}, searchSchemas: {} };
239
412
  }
240
413
 
241
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
242
- if (!urlsVarName) {
414
+ const extraction = extractUrlsFromRouter(routerSource);
415
+ if (!extraction) {
243
416
  return { routes: {}, searchSchemas: {} };
244
417
  }
245
418
 
246
- const imported = resolveImportedVariable(routerSource, urlsVarName);
247
- if (imported) {
248
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
249
- if (!targetFile) {
250
- return { routes: {}, searchSchemas: {} };
419
+ // Detect basename from createRouter({ basename: "..." })
420
+ const rawBasename = extractBasenameFromRouter(routerSource);
421
+ const basename = rawBasename
422
+ ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "")
423
+ : undefined;
424
+
425
+ let result: {
426
+ routes: Record<string, string>;
427
+ searchSchemas: Record<string, Record<string, string>>;
428
+ };
429
+
430
+ // Inline builder: extract routes directly from the function body
431
+ if (extraction.kind === "inline") {
432
+ result = buildCombinedRouteMapWithSearch(
433
+ routerFilePath,
434
+ undefined,
435
+ undefined,
436
+ undefined,
437
+ extraction.block,
438
+ );
439
+ } else {
440
+ // Variable reference: follow imports or same-file declaration
441
+ const imported = resolveImportedVariable(routerSource, extraction.name);
442
+ if (imported) {
443
+ const targetFile = resolveImportPath(imported.specifier, routerFilePath);
444
+ if (!targetFile) {
445
+ return { routes: {}, searchSchemas: {} };
446
+ }
447
+ result = buildCombinedRouteMapWithSearch(
448
+ targetFile,
449
+ imported.exportedName,
450
+ );
451
+ } else {
452
+ result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
251
453
  }
252
- return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
253
454
  }
254
455
 
255
- return buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
456
+ // Apply basename prefix to all extracted route patterns
457
+ if (basename) {
458
+ result = applyBasenameToRoutes(result, basename);
459
+ }
460
+
461
+ return result;
256
462
  }
257
463
 
258
464
  // ---------------------------------------------------------------------------
@@ -275,12 +481,26 @@ export function detectUnresolvableIncludes(
275
481
  return [];
276
482
  }
277
483
 
278
- // Extract the urls variable from the router file
279
- const urlsVarName = extractUrlsVariableFromRouter(source);
280
- if (!urlsVarName) return [];
484
+ // Extract the urls source from the router file
485
+ const extraction = extractUrlsFromRouter(source);
486
+ if (!extraction) return [];
487
+
488
+ const diagnostics: UnresolvableInclude[] = [];
281
489
 
282
- // Resolve where the urls variable comes from
283
- const imported = resolveImportedVariable(source, urlsVarName);
490
+ if (extraction.kind === "inline") {
491
+ // Inline builder: parse directly
492
+ buildCombinedRouteMapWithSearch(
493
+ realPath,
494
+ undefined,
495
+ new Set(),
496
+ diagnostics,
497
+ extraction.block,
498
+ );
499
+ return diagnostics;
500
+ }
501
+
502
+ // Variable reference: resolve where it comes from
503
+ const imported = resolveImportedVariable(source, extraction.name);
284
504
  let targetFile: string;
285
505
  let exportedName: string | undefined;
286
506
 
@@ -302,10 +522,9 @@ export function detectUnresolvableIncludes(
302
522
  } else {
303
523
  // Same-file urls() definition
304
524
  targetFile = realPath;
305
- exportedName = urlsVarName;
525
+ exportedName = extraction.name;
306
526
  }
307
527
 
308
- const diagnostics: UnresolvableInclude[] = [];
309
528
  buildCombinedRouteMapWithSearch(
310
529
  targetFile,
311
530
  exportedName,
@@ -352,7 +571,38 @@ export function detectUnresolvableIncludesForUrlsFile(
352
571
  */
353
572
  export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
354
573
  const result: string[] = [];
355
- findRouterFilesRecursive(root, filter, result);
574
+ findCallSiteFilesRecursive(
575
+ root,
576
+ ROUTER_CALL_PATTERN,
577
+ ROUTER_CALL_PATTERN_G,
578
+ true,
579
+ filter,
580
+ result,
581
+ );
582
+ return result;
583
+ }
584
+
585
+ const HOST_ROUTER_CALL_PATTERN = /\bcreateHostRouter\s*[<(]/;
586
+ const HOST_ROUTER_CALL_PATTERN_G = /\bcreateHostRouter\s*[<(]/g;
587
+
588
+ /**
589
+ * Scan for files containing createHostRouter() and return their paths. Unlike
590
+ * findRouterFiles, this does NOT stop at the first router-root directory -- a host
591
+ * entry typically sits above the sub-app router roots, so the whole tree is scanned.
592
+ */
593
+ export function findHostRouterFiles(
594
+ root: string,
595
+ filter?: ScanFilter,
596
+ ): string[] {
597
+ const result: string[] = [];
598
+ findCallSiteFilesRecursive(
599
+ root,
600
+ HOST_ROUTER_CALL_PATTERN,
601
+ HOST_ROUTER_CALL_PATTERN_G,
602
+ false,
603
+ filter,
604
+ result,
605
+ );
356
606
  return result;
357
607
  }
358
608
 
@@ -365,7 +615,10 @@ export function findRouterFiles(root: string, filter?: ScanFilter): string[] {
365
615
  export function writeCombinedRouteTypes(
366
616
  root: string,
367
617
  knownRouterFiles?: string[],
368
- opts?: { preserveIfLarger?: boolean },
618
+ opts?: {
619
+ preserveIfLarger?: boolean;
620
+ onWrite?: (outPath: string, content: string) => void;
621
+ },
369
622
  ): void {
370
623
  // Delete old combined named-routes.gen.ts if it exists (stale from older versions)
371
624
  try {
@@ -373,7 +626,7 @@ export function writeCombinedRouteTypes(
373
626
  if (existsSync(oldCombinedPath)) {
374
627
  unlinkSync(oldCombinedPath);
375
628
  console.log(
376
- `[rsc-router] Removed stale combined route types: ${oldCombinedPath}`,
629
+ `[rango] Removed stale combined route types: ${oldCombinedPath}`,
377
630
  );
378
631
  }
379
632
  } catch {}
@@ -387,54 +640,31 @@ export function writeCombinedRouteTypes(
387
640
  }
388
641
 
389
642
  for (const routerFilePath of routerFilePaths) {
390
- let routerSource: string;
391
- try {
392
- routerSource = readFileSync(routerFilePath, "utf-8");
393
- } catch {
394
- continue;
395
- }
396
- // Extract the urls variable name from .routes(varName) or urls: varName
397
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
398
- if (!urlsVarName) continue;
399
-
400
- // Resolve the variable to its source module
401
- let result: {
402
- routes: Record<string, string>;
403
- searchSchemas: Record<string, Record<string, string>>;
404
- };
405
-
406
- const imported = resolveImportedVariable(routerSource, urlsVarName);
407
- if (imported) {
408
- // Variable is imported from another module
409
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
410
- if (!targetFile) continue;
411
- result = buildCombinedRouteMapWithSearch(
412
- targetFile,
413
- imported.exportedName,
414
- );
415
- } else {
416
- // Variable is defined in the same file
417
- result = buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
643
+ const result = buildCombinedRouteMapForRouterFile(routerFilePath);
644
+ if (
645
+ Object.keys(result.routes).length === 0 &&
646
+ Object.keys(result.searchSchemas).length === 0
647
+ ) {
648
+ // Check if the file even has a createRouter call — if not, skip entirely.
649
+ // If it does, fall through to write an empty placeholder below.
650
+ let routerSource: string;
651
+ try {
652
+ routerSource = readFileSync(routerFilePath, "utf-8");
653
+ } catch {
654
+ continue;
655
+ }
656
+ if (!extractUrlsFromRouter(routerSource)) continue;
418
657
  }
419
658
 
420
- const routerBasename = pathBasename(routerFilePath).replace(
421
- /\.(tsx?|jsx?)$/,
422
- "",
423
- );
424
- const outPath = join(
425
- dirname(routerFilePath),
426
- `${routerBasename}.named-routes.gen.ts`,
427
- );
659
+ const outPath = genFileTsPath(routerFilePath);
428
660
  const existing = existsSync(outPath)
429
661
  ? readFileSync(outPath, "utf-8")
430
662
  : null;
431
663
 
432
- // When the static parser can't extract routes (e.g. callback-style urls()),
433
- // write an empty placeholder so the build-time transform's injected import
434
- // resolves. Runtime discovery will overwrite this with the real routes.
435
664
  if (Object.keys(result.routes).length === 0) {
436
665
  if (!existing) {
437
666
  const emptySource = generateRouteTypesSource({});
667
+ opts?.onWrite?.(outPath, emptySource);
438
668
  writeFileSync(outPath, emptySource);
439
669
  }
440
670
  continue;
@@ -446,11 +676,6 @@ export function writeCombinedRouteTypes(
446
676
  hasSearchSchemas ? result.searchSchemas : undefined,
447
677
  );
448
678
  if (existing !== source) {
449
- // On initial dev startup, don't overwrite a file from runtime discovery
450
- // (which has all dynamic routes) with a smaller set from the static
451
- // parser. The static parser can't see routes generated by Array.from()
452
- // or other dynamic code. During HMR (file watcher), always write so
453
- // newly added routes appear immediately.
454
679
  if (opts?.preserveIfLarger && existing) {
455
680
  const existingCount = countPublicRouteEntries(existing);
456
681
  const newCount = Object.keys(result.routes).filter(
@@ -460,9 +685,10 @@ export function writeCombinedRouteTypes(
460
685
  continue;
461
686
  }
462
687
  }
688
+ opts?.onWrite?.(outPath, source);
463
689
  writeFileSync(outPath, source);
464
690
  console.log(
465
- `[rsc-router] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`,
691
+ `[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`,
466
692
  );
467
693
  }
468
694
  }
@@ -54,14 +54,21 @@ export function findTsFiles(dir: string, filter?: ScanFilter): string[] {
54
54
  entries = readdirSync(dir, { withFileTypes: true });
55
55
  } catch (err) {
56
56
  console.warn(
57
- `[rsc-router] Failed to scan directory ${dir}: ${(err as Error).message}`,
57
+ `[rango] Failed to scan directory ${dir}: ${(err as Error).message}`,
58
58
  );
59
59
  return results;
60
60
  }
61
61
  for (const entry of entries) {
62
62
  const fullPath = join(dir, entry.name);
63
63
  if (entry.isDirectory()) {
64
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
64
+ if (
65
+ entry.name === "node_modules" ||
66
+ entry.name.startsWith(".") ||
67
+ entry.name === "dist" ||
68
+ entry.name === "build" ||
69
+ entry.name === "coverage"
70
+ )
71
+ continue;
65
72
  results.push(...findTsFiles(fullPath, filter));
66
73
  } else if (
67
74
  (entry.name.endsWith(".ts") ||