@rangojs/router 0.0.0-experimental.1b930379 → 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 (400) hide show
  1. package/AGENTS.md +12 -0
  2. package/README.md +245 -49
  3. package/dist/bin/rango.js +441 -134
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3453 -1240
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +76 -21
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +64 -2
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +318 -15
  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 +250 -30
  19. package/skills/host-router/SKILL.md +83 -23
  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 +279 -53
  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 +153 -109
  32. package/skills/rango/SKILL.md +251 -22
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +101 -5
  36. package/skills/router-setup/SKILL.md +116 -8
  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 +129 -0
  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 +332 -29
  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 +1 -66
  62. package/src/browser/action-coordinator.ts +53 -36
  63. package/src/browser/action-fence.ts +47 -0
  64. package/src/browser/app-shell.ts +39 -0
  65. package/src/browser/app-version.ts +14 -0
  66. package/src/browser/connection-warmup.ts +134 -0
  67. package/src/browser/cookie-name.ts +140 -0
  68. package/src/browser/event-controller.ts +197 -150
  69. package/src/browser/history-state.ts +21 -0
  70. package/src/browser/index.ts +3 -3
  71. package/src/browser/invalidate-client-cache.ts +52 -0
  72. package/src/browser/navigation-bridge.ts +111 -31
  73. package/src/browser/navigation-client.ts +201 -67
  74. package/src/browser/navigation-store-handle.ts +38 -0
  75. package/src/browser/navigation-store.ts +76 -67
  76. package/src/browser/navigation-transaction.ts +18 -66
  77. package/src/browser/network-error-handler.ts +34 -7
  78. package/src/browser/partial-update.ts +187 -112
  79. package/src/browser/prefetch/cache.ts +230 -35
  80. package/src/browser/prefetch/fetch.ts +338 -39
  81. package/src/browser/prefetch/queue.ts +126 -20
  82. package/src/browser/prefetch/resource-ready.ts +77 -0
  83. package/src/browser/rango-state.ts +158 -76
  84. package/src/browser/react/Link.tsx +111 -16
  85. package/src/browser/react/NavigationProvider.tsx +135 -120
  86. package/src/browser/react/ScrollRestoration.tsx +10 -6
  87. package/src/browser/react/context.ts +7 -2
  88. package/src/browser/react/filter-segment-order.ts +66 -7
  89. package/src/browser/react/index.ts +0 -48
  90. package/src/browser/react/location-state-shared.ts +178 -8
  91. package/src/browser/react/location-state.ts +39 -14
  92. package/src/browser/react/use-action.ts +6 -15
  93. package/src/browser/react/use-handle.ts +23 -69
  94. package/src/browser/react/use-href.tsx +8 -1
  95. package/src/browser/react/use-link-status.ts +33 -8
  96. package/src/browser/react/use-navigation.ts +32 -7
  97. package/src/browser/react/use-params.ts +20 -10
  98. package/src/browser/react/use-reverse.ts +106 -0
  99. package/src/browser/react/use-router.ts +46 -11
  100. package/src/browser/react/use-search-params.ts +0 -5
  101. package/src/browser/react/use-segments.ts +11 -21
  102. package/src/browser/response-adapter.ts +80 -5
  103. package/src/browser/rsc-router.tsx +218 -76
  104. package/src/browser/scroll-restoration.ts +54 -42
  105. package/src/browser/segment-reconciler.ts +36 -9
  106. package/src/browser/segment-structure-assert.ts +2 -2
  107. package/src/browser/server-action-bridge.ts +222 -61
  108. package/src/browser/types.ts +91 -11
  109. package/src/browser/validate-redirect-origin.ts +43 -16
  110. package/src/build/collect-fallback-refs.ts +107 -0
  111. package/src/build/generate-manifest.ts +65 -40
  112. package/src/build/generate-route-types.ts +5 -1
  113. package/src/build/index.ts +8 -2
  114. package/src/build/prefix-tree-utils.ts +123 -0
  115. package/src/build/route-trie.ts +165 -36
  116. package/src/build/route-types/ast-route-extraction.ts +15 -8
  117. package/src/build/route-types/codegen.ts +16 -5
  118. package/src/build/route-types/include-resolution.ts +125 -24
  119. package/src/build/route-types/param-extraction.ts +6 -3
  120. package/src/build/route-types/per-module-writer.ts +22 -6
  121. package/src/build/route-types/router-processing.ts +272 -96
  122. package/src/build/route-types/scan-filter.ts +9 -2
  123. package/src/build/route-types/source-scan.ts +216 -0
  124. package/src/build/runtime-discovery.ts +9 -20
  125. package/src/cache/cache-error.ts +104 -0
  126. package/src/cache/cache-key-utils.ts +29 -13
  127. package/src/cache/cache-policy.ts +108 -34
  128. package/src/cache/cache-runtime.ts +214 -48
  129. package/src/cache/cache-scope.ts +236 -89
  130. package/src/cache/cache-tag.ts +103 -0
  131. package/src/cache/cf/cf-base64.ts +33 -0
  132. package/src/cache/cf/cf-cache-constants.ts +127 -0
  133. package/src/cache/cf/cf-cache-store.ts +2224 -171
  134. package/src/cache/cf/cf-cache-types.ts +349 -0
  135. package/src/cache/cf/cf-kv-utils.ts +46 -0
  136. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  137. package/src/cache/cf/index.ts +11 -17
  138. package/src/cache/document-cache.ts +89 -27
  139. package/src/cache/handle-snapshot.ts +70 -0
  140. package/src/cache/index.ts +11 -20
  141. package/src/cache/memory-segment-store.ts +136 -37
  142. package/src/cache/profile-registry.ts +31 -31
  143. package/src/cache/read-through-swr.ts +41 -11
  144. package/src/cache/segment-codec.ts +9 -17
  145. package/src/cache/tag-invalidation.ts +230 -0
  146. package/src/cache/taint.ts +55 -0
  147. package/src/cache/types.ts +37 -100
  148. package/src/client.rsc.tsx +44 -21
  149. package/src/client.tsx +119 -290
  150. package/src/cloudflare/index.ts +11 -0
  151. package/src/cloudflare/tracing.ts +109 -0
  152. package/src/component-utils.ts +19 -0
  153. package/src/components/DefaultDocument.tsx +8 -2
  154. package/src/context-var.ts +84 -2
  155. package/src/debug.ts +2 -2
  156. package/src/decode-loader-results.ts +52 -0
  157. package/src/defer.ts +196 -0
  158. package/src/deps/ssr.ts +0 -1
  159. package/src/encode-kv.ts +49 -0
  160. package/src/errors.ts +30 -4
  161. package/src/escape-script.ts +52 -0
  162. package/src/handle.ts +70 -22
  163. package/src/handles/MetaTags.tsx +56 -19
  164. package/src/handles/Scripts.tsx +183 -0
  165. package/src/handles/breadcrumbs.ts +37 -8
  166. package/src/handles/is-thenable.ts +19 -0
  167. package/src/handles/meta.ts +51 -40
  168. package/src/handles/script.ts +244 -0
  169. package/src/host/cookie-handler.ts +9 -60
  170. package/src/host/errors.ts +0 -24
  171. package/src/host/index.ts +8 -2
  172. package/src/host/pattern-matcher.ts +23 -52
  173. package/src/host/router.ts +107 -99
  174. package/src/host/testing.ts +40 -27
  175. package/src/host/types.ts +37 -4
  176. package/src/host/utils.ts +1 -1
  177. package/src/href-client.ts +137 -22
  178. package/src/index.rsc.ts +93 -12
  179. package/src/index.ts +133 -15
  180. package/src/internal-debug.ts +11 -10
  181. package/src/loader-store.ts +500 -0
  182. package/src/loader.rsc.ts +20 -13
  183. package/src/loader.ts +12 -11
  184. package/src/missing-id-error.ts +68 -0
  185. package/src/outlet-context.ts +1 -1
  186. package/src/outlet-provider.tsx +1 -5
  187. package/src/prerender/param-hash.ts +16 -16
  188. package/src/prerender/store.ts +37 -41
  189. package/src/prerender.ts +198 -82
  190. package/src/redirect-origin.ts +100 -0
  191. package/src/regex-escape.ts +8 -0
  192. package/src/render-error-thrower.tsx +20 -0
  193. package/src/response-utils.ts +62 -0
  194. package/src/reverse.ts +65 -15
  195. package/src/root-error-boundary.tsx +1 -19
  196. package/src/route-content-wrapper.tsx +7 -72
  197. package/src/route-definition/dsl-helpers.ts +469 -276
  198. package/src/route-definition/helper-factories.ts +29 -139
  199. package/src/route-definition/helpers-types.ts +113 -37
  200. package/src/route-definition/index.ts +3 -0
  201. package/src/route-definition/redirect.ts +53 -12
  202. package/src/route-definition/resolve-handler-use.ts +161 -0
  203. package/src/route-definition/use-item-types.ts +32 -0
  204. package/src/route-map-builder.ts +7 -17
  205. package/src/route-types.ts +37 -41
  206. package/src/router/basename.ts +14 -0
  207. package/src/router/content-negotiation.ts +164 -17
  208. package/src/router/error-handling.ts +45 -18
  209. package/src/router/find-match.ts +45 -22
  210. package/src/router/handler-context.ts +83 -39
  211. package/src/router/instrument.ts +350 -0
  212. package/src/router/intercept-resolution.ts +50 -24
  213. package/src/router/lazy-includes.ts +19 -53
  214. package/src/router/loader-resolution.ts +274 -56
  215. package/src/router/logging.ts +5 -8
  216. package/src/router/manifest.ts +49 -45
  217. package/src/router/match-api.ts +120 -204
  218. package/src/router/match-context.ts +0 -22
  219. package/src/router/match-handlers.ts +58 -58
  220. package/src/router/match-middleware/background-revalidation.ts +33 -6
  221. package/src/router/match-middleware/cache-lookup.ts +214 -263
  222. package/src/router/match-middleware/cache-store.ts +73 -33
  223. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  224. package/src/router/match-middleware/segment-resolution.ts +52 -18
  225. package/src/router/match-pipelines.ts +1 -42
  226. package/src/router/match-result.ts +104 -40
  227. package/src/router/metrics.ts +5 -34
  228. package/src/router/middleware-types.ts +13 -142
  229. package/src/router/middleware.ts +270 -172
  230. package/src/router/navigation-snapshot.ts +131 -0
  231. package/src/router/params-util.ts +23 -0
  232. package/src/router/pattern-matching.ts +132 -90
  233. package/src/router/prefetch-cache-ttl.ts +51 -0
  234. package/src/router/prerender-match.ts +195 -56
  235. package/src/router/preview-match.ts +32 -102
  236. package/src/router/request-classification.ts +276 -0
  237. package/src/router/revalidation.ts +123 -73
  238. package/src/router/route-snapshot.ts +244 -0
  239. package/src/router/router-context.ts +8 -28
  240. package/src/router/router-interfaces.ts +115 -35
  241. package/src/router/router-options.ts +172 -15
  242. package/src/router/router-registry.ts +2 -5
  243. package/src/router/segment-resolution/fresh.ts +264 -77
  244. package/src/router/segment-resolution/helpers.ts +115 -30
  245. package/src/router/segment-resolution/loader-cache.ts +63 -37
  246. package/src/router/segment-resolution/revalidation.ts +474 -385
  247. package/src/router/segment-resolution/static-store.ts +19 -5
  248. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  249. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  250. package/src/router/segment-resolution.ts +5 -1
  251. package/src/router/segment-wrappers.ts +8 -5
  252. package/src/router/state-cookie-name.ts +33 -0
  253. package/src/router/substitute-pattern-params.ts +56 -0
  254. package/src/router/telemetry-otel.ts +161 -199
  255. package/src/router/telemetry.ts +96 -19
  256. package/src/router/timeout.ts +0 -20
  257. package/src/router/tracing.ts +206 -0
  258. package/src/router/trie-matching.ts +163 -59
  259. package/src/router/types.ts +10 -63
  260. package/src/router/url-params.ts +44 -0
  261. package/src/router.ts +163 -55
  262. package/src/rsc/handler-context.ts +3 -2
  263. package/src/rsc/handler.ts +658 -511
  264. package/src/rsc/helpers.ts +168 -46
  265. package/src/rsc/index.ts +2 -5
  266. package/src/rsc/json-route-result.ts +38 -0
  267. package/src/rsc/loader-fetch.ts +127 -31
  268. package/src/rsc/manifest-init.ts +33 -42
  269. package/src/rsc/origin-guard.ts +39 -25
  270. package/src/rsc/progressive-enhancement.ts +77 -11
  271. package/src/rsc/redirect-guard.ts +99 -0
  272. package/src/rsc/response-cache-serve.ts +238 -0
  273. package/src/rsc/response-error.ts +79 -12
  274. package/src/rsc/response-route-handler.ts +99 -189
  275. package/src/rsc/rsc-rendering.ts +105 -72
  276. package/src/rsc/runtime-warnings.ts +23 -10
  277. package/src/rsc/server-action.ts +263 -112
  278. package/src/rsc/ssr-setup.ts +18 -2
  279. package/src/rsc/types.ts +32 -6
  280. package/src/runtime-env.ts +18 -0
  281. package/src/search-params.ts +35 -30
  282. package/src/segment-content-promise.ts +67 -0
  283. package/src/segment-loader-promise.ts +149 -0
  284. package/src/segment-system.tsx +281 -129
  285. package/src/serialize.ts +243 -0
  286. package/src/server/context.ts +309 -61
  287. package/src/server/cookie-parse.ts +32 -0
  288. package/src/server/cookie-store.ts +80 -5
  289. package/src/server/handle-store.ts +40 -38
  290. package/src/server/loader-registry.ts +26 -46
  291. package/src/server/request-context.ts +398 -172
  292. package/src/ssr/index.tsx +25 -16
  293. package/src/static-handler.ts +27 -18
  294. package/src/testing/cache-status.ts +162 -0
  295. package/src/testing/collect-handle.ts +40 -0
  296. package/src/testing/dispatch.ts +701 -0
  297. package/src/testing/dom.entry.ts +22 -0
  298. package/src/testing/e2e/fixture.ts +188 -0
  299. package/src/testing/e2e/index.ts +128 -0
  300. package/src/testing/e2e/matchers.ts +35 -0
  301. package/src/testing/e2e/page-helpers.ts +272 -0
  302. package/src/testing/e2e/parity.ts +387 -0
  303. package/src/testing/e2e/server.ts +195 -0
  304. package/src/testing/flight-matchers.ts +97 -0
  305. package/src/testing/flight-normalize.ts +11 -0
  306. package/src/testing/flight-runtime.d.ts +57 -0
  307. package/src/testing/flight-tree.ts +682 -0
  308. package/src/testing/flight.entry.ts +52 -0
  309. package/src/testing/flight.ts +257 -0
  310. package/src/testing/generated-routes.ts +183 -0
  311. package/src/testing/index.ts +99 -0
  312. package/src/testing/internal/context.ts +371 -0
  313. package/src/testing/internal/flight-client-globals.ts +30 -0
  314. package/src/testing/internal/seed-vars.ts +54 -0
  315. package/src/testing/render-handler.ts +343 -0
  316. package/src/testing/render-route.tsx +581 -0
  317. package/src/testing/run-loader.ts +385 -0
  318. package/src/testing/run-middleware.ts +205 -0
  319. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  320. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  321. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  322. package/src/testing/vitest-stubs/version.ts +5 -0
  323. package/src/testing/vitest.ts +305 -0
  324. package/src/theme/ThemeProvider.tsx +20 -58
  325. package/src/theme/ThemeScript.tsx +7 -9
  326. package/src/theme/constants.ts +52 -13
  327. package/src/theme/index.ts +0 -7
  328. package/src/theme/theme-context.ts +1 -5
  329. package/src/theme/theme-script.ts +22 -21
  330. package/src/theme/use-theme.ts +0 -3
  331. package/src/types/boundaries.ts +0 -35
  332. package/src/types/cache-types.ts +17 -8
  333. package/src/types/error-types.ts +30 -90
  334. package/src/types/global-namespace.ts +54 -41
  335. package/src/types/handler-context.ts +233 -81
  336. package/src/types/index.ts +1 -10
  337. package/src/types/loader-types.ts +44 -15
  338. package/src/types/request-scope.ts +112 -0
  339. package/src/types/route-config.ts +6 -50
  340. package/src/types/route-entry.ts +19 -7
  341. package/src/types/segments.ts +37 -14
  342. package/src/urls/include-helper.ts +33 -70
  343. package/src/urls/index.ts +1 -11
  344. package/src/urls/path-helper-types.ts +58 -11
  345. package/src/urls/path-helper.ts +57 -111
  346. package/src/urls/pattern-types.ts +48 -19
  347. package/src/urls/response-types.ts +25 -22
  348. package/src/urls/type-extraction.ts +58 -139
  349. package/src/urls/urls-function.ts +1 -18
  350. package/src/use-loader.tsx +346 -89
  351. package/src/vite/debug.ts +185 -0
  352. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  353. package/src/vite/discovery/discover-routers.ts +130 -85
  354. package/src/vite/discovery/discovery-errors.ts +194 -0
  355. package/src/vite/discovery/gate-state.ts +171 -0
  356. package/src/vite/discovery/prerender-collection.ts +214 -132
  357. package/src/vite/discovery/route-types-writer.ts +40 -84
  358. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  359. package/src/vite/discovery/state.ts +57 -6
  360. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  361. package/src/vite/index.ts +6 -0
  362. package/src/vite/inject-client-debug.ts +36 -0
  363. package/src/vite/plugin-types.ts +155 -65
  364. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  365. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  366. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  367. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  368. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  369. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  370. package/src/vite/plugins/expose-action-id.ts +49 -98
  371. package/src/vite/plugins/expose-id-utils.ts +96 -51
  372. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  373. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  374. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  375. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  376. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  377. package/src/vite/plugins/performance-tracks.ts +89 -0
  378. package/src/vite/plugins/refresh-cmd.ts +89 -27
  379. package/src/vite/plugins/use-cache-transform.ts +73 -83
  380. package/src/vite/plugins/version-injector.ts +21 -25
  381. package/src/vite/plugins/version-plugin.ts +46 -37
  382. package/src/vite/plugins/virtual-entries.ts +13 -18
  383. package/src/vite/rango.ts +238 -295
  384. package/src/vite/router-discovery.ts +940 -149
  385. package/src/vite/utils/ast-handler-extract.ts +26 -35
  386. package/src/vite/utils/banner.ts +4 -4
  387. package/src/vite/utils/bundle-analysis.ts +10 -15
  388. package/src/vite/utils/client-chunks.ts +184 -0
  389. package/src/vite/utils/directive-prologue.ts +40 -0
  390. package/src/vite/utils/forward-user-plugins.ts +171 -0
  391. package/src/vite/utils/manifest-utils.ts +4 -59
  392. package/src/vite/utils/package-resolution.ts +20 -52
  393. package/src/vite/utils/prerender-utils.ts +81 -34
  394. package/src/vite/utils/shared-utils.ts +92 -42
  395. package/src/browser/action-response-classifier.ts +0 -99
  396. package/src/browser/react/use-client-cache.ts +0 -58
  397. package/src/browser/shallow.ts +0 -40
  398. package/src/handles/index.ts +0 -7
  399. package/src/network-error-thrower.tsx +0 -23
  400. package/src/router/middleware-cookies.ts +0 -55
package/dist/bin/rango.js CHANGED
@@ -22,10 +22,10 @@ function extractParamsFromPattern(pattern) {
22
22
  function formatRouteEntry(key, pattern, _params, search) {
23
23
  const hasSearch = search && Object.keys(search).length > 0;
24
24
  if (!hasSearch) {
25
- return ` ${key}: "${pattern}",`;
25
+ return ` ${key}: ${JSON.stringify(pattern)},`;
26
26
  }
27
- const searchBody = Object.entries(search).map(([k, v]) => `${k}: "${v}"`).join(", ");
28
- return ` ${key}: { path: "${pattern}", search: { ${searchBody} } },`;
27
+ const searchBody = Object.entries(search).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
28
+ return ` ${key}: { path: ${JSON.stringify(pattern)}, search: { ${searchBody} } },`;
29
29
  }
30
30
  var init_param_extraction = __esm({
31
31
  "src/build/route-types/param-extraction.ts"() {
@@ -59,8 +59,8 @@ var init_ast_helpers = __esm({
59
59
 
60
60
  // src/build/route-types/ast-route-extraction.ts
61
61
  import ts2 from "typescript";
62
- function extractRoutesFromSource(code) {
63
- const sourceFile = ts2.createSourceFile(
62
+ function extractRoutesFromSource(code, sourceFileArg) {
63
+ const sourceFile = sourceFileArg ?? ts2.createSourceFile(
64
64
  "input.tsx",
65
65
  code,
66
66
  ts2.ScriptTarget.Latest,
@@ -139,7 +139,7 @@ function generatePerModuleTypesSource(routes) {
139
139
  const valid = routes.filter(({ name }) => {
140
140
  if (!name || /["'\\`\n\r]/.test(name)) {
141
141
  console.warn(
142
- `[rsc-router] Skipping route with invalid name: ${JSON.stringify(name)}`
142
+ `[rango] Skipping route with invalid name: ${JSON.stringify(name)}`
143
143
  );
144
144
  return false;
145
145
  }
@@ -149,7 +149,7 @@ function generatePerModuleTypesSource(routes) {
149
149
  for (const { name, pattern, params, search } of valid) {
150
150
  if (deduped.has(name)) {
151
151
  console.warn(
152
- `[rsc-router] Duplicate route name "${name}" \u2014 keeping first definition`
152
+ `[rango] Duplicate route name "${name}" \u2014 keeping first definition`
153
153
  );
154
154
  continue;
155
155
  }
@@ -185,9 +185,13 @@ export const NamedRoutes = {
185
185
  ${objectBody}
186
186
  } as const;
187
187
 
188
+ // Aliased so the augmentation below does not pay a homomorphic mapped-type
189
+ // instantiation per route; \`as const\` already makes the members readonly.
190
+ type NamedRoutesShape = typeof NamedRoutes;
191
+
188
192
  declare global {
189
- namespace RSCRouter {
190
- interface GeneratedRouteMap extends Readonly<typeof NamedRoutes> {}
193
+ namespace Rango {
194
+ interface GeneratedRouteMap extends NamedRoutesShape {}
191
195
  }
192
196
  }
193
197
  `;
@@ -211,14 +215,15 @@ function findTsFiles(dir, filter) {
211
215
  entries = readdirSync(dir, { withFileTypes: true });
212
216
  } catch (err) {
213
217
  console.warn(
214
- `[rsc-router] Failed to scan directory ${dir}: ${err.message}`
218
+ `[rango] Failed to scan directory ${dir}: ${err.message}`
215
219
  );
216
220
  return results;
217
221
  }
218
222
  for (const entry of entries) {
219
223
  const fullPath = join(dir, entry.name);
220
224
  if (entry.isDirectory()) {
221
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
225
+ if (entry.name === "node_modules" || entry.name.startsWith(".") || entry.name === "dist" || entry.name === "build" || entry.name === "coverage")
226
+ continue;
222
227
  results.push(...findTsFiles(fullPath, filter));
223
228
  } else if ((entry.name.endsWith(".ts") || entry.name.endsWith(".tsx") || entry.name.endsWith(".js") || entry.name.endsWith(".jsx")) && !entry.name.includes(".gen.")) {
224
229
  if (filter && !filter(fullPath)) continue;
@@ -237,6 +242,33 @@ var init_scan_filter = __esm({
237
242
  import { readFileSync, existsSync } from "node:fs";
238
243
  import { dirname, resolve } from "node:path";
239
244
  import ts3 from "typescript";
245
+ function createScanMemo() {
246
+ return { files: /* @__PURE__ */ new Map(), blockSourceFiles: /* @__PURE__ */ new Map() };
247
+ }
248
+ function parseBlock(memo, block) {
249
+ if (memo) {
250
+ const cached = memo.blockSourceFiles.get(block);
251
+ if (cached) return cached;
252
+ }
253
+ const sf = ts3.createSourceFile(
254
+ "input.tsx",
255
+ block,
256
+ ts3.ScriptTarget.Latest,
257
+ true,
258
+ ts3.ScriptKind.TSX
259
+ );
260
+ if (memo) memo.blockSourceFiles.set(block, sf);
261
+ return sf;
262
+ }
263
+ function readSourceMemoized(memo, realPath) {
264
+ if (memo) {
265
+ const cached = memo.files.get(realPath);
266
+ if (cached !== void 0) return cached;
267
+ }
268
+ const source = readFileSync(realPath, "utf-8");
269
+ if (memo) memo.files.set(realPath, source);
270
+ return source;
271
+ }
240
272
  function extractNamePrefixFromInclude(node) {
241
273
  if (node.arguments.length >= 3) {
242
274
  const thirdArg = node.arguments[2];
@@ -252,8 +284,8 @@ function extractNamePrefixFromInclude(node) {
252
284
  }
253
285
  return null;
254
286
  }
255
- function extractIncludesWithDiagnostics(code) {
256
- const sourceFile = ts3.createSourceFile(
287
+ function extractIncludesWithDiagnostics(code, sourceFileArg) {
288
+ const sourceFile = sourceFileArg ?? ts3.createSourceFile(
257
289
  "input.tsx",
258
290
  code,
259
291
  ts3.ScriptTarget.Latest,
@@ -307,7 +339,7 @@ function extractIncludesWithDiagnostics(code) {
307
339
  return { resolved, unresolvable };
308
340
  }
309
341
  function resolveImportedVariable(code, localName) {
310
- const importRegex = /import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
342
+ const importRegex = /import\s*(?:[\w$]+\s*,\s*)?\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
311
343
  let match;
312
344
  while ((match = importRegex.exec(code)) !== null) {
313
345
  const imports = match[1];
@@ -344,8 +376,8 @@ function resolveImportPath(importSpec, fromFile) {
344
376
  }
345
377
  return null;
346
378
  }
347
- function extractUrlsBlockForVariable(code, varName) {
348
- const sourceFile = ts3.createSourceFile(
379
+ function extractUrlsBlockForVariable(code, varName, sourceFileArg) {
380
+ const sourceFile = sourceFileArg ?? ts3.createSourceFile(
349
381
  "input.tsx",
350
382
  code,
351
383
  ts3.ScriptTarget.Latest,
@@ -367,16 +399,20 @@ function extractUrlsBlockForVariable(code, varName) {
367
399
  visit(sourceFile);
368
400
  return result;
369
401
  }
370
- function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut) {
402
+ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSchemasOut, diagnosticsOut, memo) {
371
403
  const routeMap = {};
372
- const localRoutes = extractRoutesFromSource(block);
404
+ const blockSourceFile = parseBlock(memo, block);
405
+ const localRoutes = extractRoutesFromSource(block, blockSourceFile);
373
406
  for (const { name, pattern, search } of localRoutes) {
374
407
  routeMap[name] = pattern;
375
408
  if (search && searchSchemasOut) {
376
409
  searchSchemasOut[name] = search;
377
410
  }
378
411
  }
379
- const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(block);
412
+ const { resolved: includes, unresolvable } = extractIncludesWithDiagnostics(
413
+ block,
414
+ blockSourceFile
415
+ );
380
416
  if (diagnosticsOut) {
381
417
  for (const entry of unresolvable) {
382
418
  diagnosticsOut.push({ ...entry, sourceFile: filePath });
@@ -403,12 +439,15 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
403
439
  targetFile,
404
440
  imported.exportedName,
405
441
  visited,
406
- diagnosticsOut
442
+ diagnosticsOut,
443
+ void 0,
444
+ memo
407
445
  );
408
446
  } else {
409
447
  const sameFileBlock = extractUrlsBlockForVariable(
410
448
  fullSource,
411
- variableName
449
+ variableName,
450
+ parseBlock(memo, fullSource)
412
451
  );
413
452
  if (!sameFileBlock) {
414
453
  if (diagnosticsOut) {
@@ -426,7 +465,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
426
465
  filePath,
427
466
  variableName,
428
467
  visited,
429
- diagnosticsOut
468
+ diagnosticsOut,
469
+ void 0,
470
+ memo
430
471
  );
431
472
  }
432
473
  if (namePrefix === null) {
@@ -450,24 +491,31 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
450
491
  }
451
492
  return routeMap;
452
493
  }
453
- function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut) {
494
+ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock, memo) {
454
495
  visited = visited ?? /* @__PURE__ */ new Set();
496
+ memo = memo ?? createScanMemo();
455
497
  const realPath = resolve(filePath);
456
498
  const key = variableName ? `${realPath}:${variableName}` : realPath;
457
499
  if (visited.has(key)) {
458
- console.warn(`[rsc-router] Circular include detected, skipping: ${key}`);
500
+ console.warn(`[rango] Circular include detected, skipping: ${key}`);
459
501
  return { routes: {}, searchSchemas: {} };
460
502
  }
461
503
  visited.add(key);
462
504
  let source;
463
505
  try {
464
- source = readFileSync(realPath, "utf-8");
506
+ source = readSourceMemoized(memo, realPath);
465
507
  } catch {
466
508
  return { routes: {}, searchSchemas: {} };
467
509
  }
468
510
  let block;
469
- if (variableName) {
470
- const extracted = extractUrlsBlockForVariable(source, variableName);
511
+ if (inlineBlock) {
512
+ block = inlineBlock;
513
+ } else if (variableName) {
514
+ const extracted = extractUrlsBlockForVariable(
515
+ source,
516
+ variableName,
517
+ parseBlock(memo, source)
518
+ );
471
519
  if (!extracted) return { routes: {}, searchSchemas: {} };
472
520
  block = extracted;
473
521
  } else {
@@ -480,7 +528,8 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
480
528
  realPath,
481
529
  visited,
482
530
  searchSchemas,
483
- diagnosticsOut
531
+ diagnosticsOut,
532
+ memo
484
533
  );
485
534
  visited.delete(key);
486
535
  return { routes, searchSchemas };
@@ -525,8 +574,16 @@ function writePerModuleRouteTypesForFile(filePath) {
525
574
  let routes;
526
575
  if (varNames.length > 0) {
527
576
  routes = [];
577
+ const memo = createScanMemo();
528
578
  for (const varName of varNames) {
529
- const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(filePath, varName);
579
+ const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
580
+ filePath,
581
+ varName,
582
+ void 0,
583
+ void 0,
584
+ void 0,
585
+ memo
586
+ );
530
587
  for (const [name, pattern] of Object.entries(routeMap)) {
531
588
  const params = extractParamsFromPattern(pattern);
532
589
  routes.push({
@@ -540,12 +597,12 @@ function writePerModuleRouteTypesForFile(filePath) {
540
597
  } else {
541
598
  routes = extractRoutesFromSource(source);
542
599
  }
543
- const genPath = filePath.replace(/\.(tsx?)$/, ".gen.ts");
600
+ const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
544
601
  if (routes.length === 0) {
545
602
  if (varNames.length > 0 && !existsSync2(genPath)) {
546
603
  writeFileSync(genPath, generatePerModuleTypesSource([]));
547
604
  console.log(
548
- `[rsc-router] Generated route types (placeholder) -> ${genPath}`
605
+ `[rango] Generated route types (placeholder) -> ${genPath}`
549
606
  );
550
607
  }
551
608
  return;
@@ -554,11 +611,11 @@ function writePerModuleRouteTypesForFile(filePath) {
554
611
  const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
555
612
  if (existing !== genSource) {
556
613
  writeFileSync(genPath, genSource);
557
- console.log(`[rsc-router] Generated route types -> ${genPath}`);
614
+ console.log(`[rango] Generated route types -> ${genPath}`);
558
615
  }
559
616
  } catch (err) {
560
617
  console.warn(
561
- `[rsc-router] Failed to generate route types for ${filePath}: ${err.message}`
618
+ `[rango] Failed to generate route types for ${filePath}: ${err.message}`
562
619
  );
563
620
  }
564
621
  }
@@ -573,6 +630,131 @@ var init_per_module_writer = __esm({
573
630
  }
574
631
  });
575
632
 
633
+ // src/build/route-types/source-scan.ts
634
+ function isLineTerminator(ch) {
635
+ const c = ch.charCodeAt(0);
636
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
637
+ }
638
+ function isRegexPositionAt(code, slashPos, prevChar) {
639
+ if (prevChar === void 0) return true;
640
+ if (prevChar === ")" || prevChar === "]" || prevChar === "}") return false;
641
+ if (!/[\w$]/.test(prevChar)) return true;
642
+ let k = slashPos - 1;
643
+ while (k >= 0 && /\s/.test(code[k])) k--;
644
+ const wordEnd = k + 1;
645
+ while (k >= 0 && /[\w$]/.test(code[k])) k--;
646
+ return REGEX_PRECEDING_KEYWORDS.has(code.slice(k + 1, wordEnd));
647
+ }
648
+ function makeCodeClassifier(code) {
649
+ const n = code.length;
650
+ let i = 0;
651
+ let skipStart = -1;
652
+ let skipEnd = -1;
653
+ let lastSig;
654
+ return (q) => {
655
+ if (q >= skipStart && q < skipEnd) return false;
656
+ while (i < n && i <= q) {
657
+ const c = code[i];
658
+ const d = i + 1 < n ? code[i + 1] : "";
659
+ let end = -1;
660
+ let transparent = false;
661
+ if (c === "/" && d === "/") {
662
+ let j = i + 2;
663
+ while (j < n && !isLineTerminator(code[j])) j++;
664
+ end = j;
665
+ transparent = true;
666
+ } else if (c === "/" && d === "*") {
667
+ let j = i + 2;
668
+ while (j < n && !(code[j] === "*" && code[j + 1] === "/")) j++;
669
+ end = Math.min(n, j + 2);
670
+ transparent = true;
671
+ } else if (c === '"' || c === "'" || c === "`") {
672
+ let j = i + 1;
673
+ while (j < n) {
674
+ if (code[j] === "\\") {
675
+ j += 2;
676
+ continue;
677
+ }
678
+ if (code[j] === c) {
679
+ j++;
680
+ break;
681
+ }
682
+ j++;
683
+ }
684
+ end = j;
685
+ } else if (c === "/" && d !== "/" && d !== "*" && isRegexPositionAt(code, i, lastSig)) {
686
+ let j = i + 1;
687
+ let inClass = false;
688
+ let closed = false;
689
+ while (j < n && !isLineTerminator(code[j])) {
690
+ const r = code[j];
691
+ if (r === "\\") {
692
+ j += 2;
693
+ continue;
694
+ }
695
+ if (r === "[") inClass = true;
696
+ else if (r === "]") inClass = false;
697
+ else if (r === "/" && !inClass) {
698
+ j++;
699
+ closed = true;
700
+ break;
701
+ }
702
+ j++;
703
+ }
704
+ if (closed) {
705
+ while (j < n && /[a-z]/.test(code[j])) j++;
706
+ end = j;
707
+ }
708
+ }
709
+ if (end >= 0) {
710
+ if (q < end) {
711
+ skipStart = i;
712
+ skipEnd = end;
713
+ return false;
714
+ }
715
+ i = end;
716
+ if (!transparent) lastSig = "x";
717
+ } else {
718
+ if (!/\s/.test(c)) lastSig = c;
719
+ i++;
720
+ }
721
+ }
722
+ return true;
723
+ };
724
+ }
725
+ function firstCodeMatchIndex(code, pattern) {
726
+ const inCode = makeCodeClassifier(code);
727
+ pattern.lastIndex = 0;
728
+ let m;
729
+ while ((m = pattern.exec(code)) !== null) {
730
+ if (inCode(m.index)) return m.index;
731
+ if (pattern.lastIndex <= m.index) pattern.lastIndex = m.index + 1;
732
+ }
733
+ return -1;
734
+ }
735
+ var REGEX_PRECEDING_KEYWORDS;
736
+ var init_source_scan = __esm({
737
+ "src/build/route-types/source-scan.ts"() {
738
+ "use strict";
739
+ REGEX_PRECEDING_KEYWORDS = /* @__PURE__ */ new Set([
740
+ "return",
741
+ "typeof",
742
+ "instanceof",
743
+ "in",
744
+ "of",
745
+ "new",
746
+ "delete",
747
+ "void",
748
+ "do",
749
+ "else",
750
+ "yield",
751
+ "await",
752
+ "case",
753
+ "throw"
754
+ ]);
755
+ }
756
+ });
757
+
576
758
  // src/build/route-types/router-processing.ts
577
759
  import {
578
760
  readFileSync as readFileSync3,
@@ -601,7 +783,7 @@ function countPublicRouteEntries(source) {
601
783
  return count;
602
784
  }
603
785
  function isRoutableSourceFile(name) {
604
- return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.");
786
+ return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.") && !name.includes(".test.") && !name.includes(".spec.");
605
787
  }
606
788
  function findRouterFilesRecursive(dir, filter, results) {
607
789
  let entries;
@@ -609,7 +791,7 @@ function findRouterFilesRecursive(dir, filter, results) {
609
791
  entries = readdirSync2(dir, { withFileTypes: true });
610
792
  } catch (err) {
611
793
  console.warn(
612
- `[rsc-router] Failed to scan directory ${dir}: ${err.message}`
794
+ `[rango] Failed to scan directory ${dir}: ${err.message}`
613
795
  );
614
796
  return;
615
797
  }
@@ -618,7 +800,8 @@ function findRouterFilesRecursive(dir, filter, results) {
618
800
  for (const entry of entries) {
619
801
  const fullPath = join2(dir, entry.name);
620
802
  if (entry.isDirectory()) {
621
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
803
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "coverage" || entry.name === "__tests__" || entry.name === "__mocks__" || entry.name.startsWith("."))
804
+ continue;
622
805
  childDirs.push(fullPath);
623
806
  continue;
624
807
  }
@@ -626,7 +809,7 @@ function findRouterFilesRecursive(dir, filter, results) {
626
809
  if (filter && !filter(fullPath)) continue;
627
810
  try {
628
811
  const source = readFileSync3(fullPath, "utf-8");
629
- if (ROUTER_CALL_PATTERN.test(source)) {
812
+ if (ROUTER_CALL_PATTERN.test(source) && firstCodeMatchIndex(source, ROUTER_CALL_PATTERN_G) >= 0) {
630
813
  routerFilesInDir.push(fullPath);
631
814
  }
632
815
  } catch {
@@ -664,13 +847,13 @@ function findNestedRouterConflict(routerFiles) {
664
847
  }
665
848
  return null;
666
849
  }
667
- function formatNestedRouterConflictError(conflict, prefix = "[rsc-router]") {
850
+ function formatNestedRouterConflictError(conflict, prefix = "[rango]") {
668
851
  return `${prefix} Nested router roots are not supported.
669
852
  Router root: ${conflict.ancestor}
670
853
  Nested router: ${conflict.nested}
671
854
  Move the nested router into a sibling directory or configure it as a separate app root.`;
672
855
  }
673
- function extractUrlsVariableFromRouter(code) {
856
+ function extractUrlsFromRouter(code) {
674
857
  const sourceFile = ts5.createSourceFile(
675
858
  "router.tsx",
676
859
  code,
@@ -684,24 +867,70 @@ function extractUrlsVariableFromRouter(code) {
684
867
  const callee = node.expression;
685
868
  return ts5.isIdentifier(callee) && callee.text === "createRouter";
686
869
  }
870
+ function isInlineBuilder(node) {
871
+ return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
872
+ }
873
+ function isRoutesOnCreateRouter(node) {
874
+ if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
875
+ return false;
876
+ let inner = node.expression.expression;
877
+ while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
878
+ inner = inner.expression.expression;
879
+ }
880
+ return isCreateRouterCall(inner);
881
+ }
687
882
  function visit(node) {
688
883
  if (result) return;
689
- if (ts5.isCallExpression(node) && ts5.isPropertyAccessExpression(node.expression) && node.expression.name.text === "routes" && node.arguments.length >= 1 && ts5.isIdentifier(node.arguments[0])) {
690
- let inner = node.expression.expression;
691
- while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
692
- inner = inner.expression.expression;
693
- }
694
- if (isCreateRouterCall(inner)) {
695
- result = node.arguments[0].text;
696
- return;
884
+ if (ts5.isCallExpression(node) && node.arguments.length >= 1 && isRoutesOnCreateRouter(node)) {
885
+ const arg = node.arguments[0];
886
+ if (ts5.isIdentifier(arg)) {
887
+ result = { kind: "variable", name: arg.text };
888
+ } else if (isInlineBuilder(arg)) {
889
+ result = { kind: "inline", block: arg.getText(sourceFile) };
697
890
  }
891
+ return;
698
892
  }
699
893
  if (isCreateRouterCall(node)) {
700
894
  const callExpr = node;
701
- for (const arg of callExpr.arguments) {
895
+ for (const callArg of callExpr.arguments) {
896
+ if (ts5.isObjectLiteralExpression(callArg)) {
897
+ for (const prop of callArg.properties) {
898
+ if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls") {
899
+ if (ts5.isIdentifier(prop.initializer)) {
900
+ result = { kind: "variable", name: prop.initializer.text };
901
+ } else if (isInlineBuilder(prop.initializer)) {
902
+ result = {
903
+ kind: "inline",
904
+ block: prop.initializer.getText(sourceFile)
905
+ };
906
+ }
907
+ return;
908
+ }
909
+ }
910
+ }
911
+ }
912
+ }
913
+ ts5.forEachChild(node, visit);
914
+ }
915
+ visit(sourceFile);
916
+ return result;
917
+ }
918
+ function extractBasenameFromRouter(code) {
919
+ const sourceFile = ts5.createSourceFile(
920
+ "router.tsx",
921
+ code,
922
+ ts5.ScriptTarget.Latest,
923
+ true,
924
+ ts5.ScriptKind.TSX
925
+ );
926
+ let result;
927
+ function visit(node) {
928
+ if (result !== void 0) return;
929
+ if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "createRouter") {
930
+ for (const arg of node.arguments) {
702
931
  if (ts5.isObjectLiteralExpression(arg)) {
703
932
  for (const prop of arg.properties) {
704
- if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls" && ts5.isIdentifier(prop.initializer)) {
933
+ if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "basename" && ts5.isStringLiteral(prop.initializer)) {
705
934
  result = prop.initializer.text;
706
935
  return;
707
936
  }
@@ -714,6 +943,38 @@ function extractUrlsVariableFromRouter(code) {
714
943
  visit(sourceFile);
715
944
  return result;
716
945
  }
946
+ function applyBasenameToRoutes(result, basename) {
947
+ const prefixed = {};
948
+ for (const [name, pattern] of Object.entries(result.routes)) {
949
+ if (pattern === "/") {
950
+ prefixed[name] = basename;
951
+ } else if (basename.endsWith("/") && pattern.startsWith("/")) {
952
+ prefixed[name] = basename + pattern.slice(1);
953
+ } else {
954
+ prefixed[name] = basename + pattern;
955
+ }
956
+ }
957
+ return { routes: prefixed, searchSchemas: result.searchSchemas };
958
+ }
959
+ function genFileTsPath(sourceFile) {
960
+ const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
961
+ return join2(dirname2(sourceFile), `${base}.named-routes.gen.ts`);
962
+ }
963
+ function resolveSearchSchemas(publicRouteNames, runtimeSchemas, sourceFile) {
964
+ if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
965
+ return runtimeSchemas;
966
+ }
967
+ const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
968
+ if (Object.keys(staticParsed.searchSchemas).length === 0) {
969
+ return runtimeSchemas;
970
+ }
971
+ const filtered = {};
972
+ for (const name of publicRouteNames) {
973
+ const schema = staticParsed.searchSchemas[name];
974
+ if (schema) filtered[name] = schema;
975
+ }
976
+ return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
977
+ }
717
978
  function buildCombinedRouteMapForRouterFile(routerFilePath) {
718
979
  let routerSource;
719
980
  try {
@@ -721,19 +982,40 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
721
982
  } catch {
722
983
  return { routes: {}, searchSchemas: {} };
723
984
  }
724
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
725
- if (!urlsVarName) {
985
+ const extraction = extractUrlsFromRouter(routerSource);
986
+ if (!extraction) {
726
987
  return { routes: {}, searchSchemas: {} };
727
988
  }
728
- const imported = resolveImportedVariable(routerSource, urlsVarName);
729
- if (imported) {
730
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
731
- if (!targetFile) {
732
- return { routes: {}, searchSchemas: {} };
989
+ const rawBasename = extractBasenameFromRouter(routerSource);
990
+ const basename = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
991
+ let result;
992
+ if (extraction.kind === "inline") {
993
+ result = buildCombinedRouteMapWithSearch(
994
+ routerFilePath,
995
+ void 0,
996
+ void 0,
997
+ void 0,
998
+ extraction.block
999
+ );
1000
+ } else {
1001
+ const imported = resolveImportedVariable(routerSource, extraction.name);
1002
+ if (imported) {
1003
+ const targetFile = resolveImportPath(imported.specifier, routerFilePath);
1004
+ if (!targetFile) {
1005
+ return { routes: {}, searchSchemas: {} };
1006
+ }
1007
+ result = buildCombinedRouteMapWithSearch(
1008
+ targetFile,
1009
+ imported.exportedName
1010
+ );
1011
+ } else {
1012
+ result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
733
1013
  }
734
- return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
735
1014
  }
736
- return buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
1015
+ if (basename) {
1016
+ result = applyBasenameToRoutes(result, basename);
1017
+ }
1018
+ return result;
737
1019
  }
738
1020
  function detectUnresolvableIncludes(routerFilePath) {
739
1021
  const realPath = resolve2(routerFilePath);
@@ -743,9 +1025,20 @@ function detectUnresolvableIncludes(routerFilePath) {
743
1025
  } catch {
744
1026
  return [];
745
1027
  }
746
- const urlsVarName = extractUrlsVariableFromRouter(source);
747
- if (!urlsVarName) return [];
748
- const imported = resolveImportedVariable(source, urlsVarName);
1028
+ const extraction = extractUrlsFromRouter(source);
1029
+ if (!extraction) return [];
1030
+ const diagnostics = [];
1031
+ if (extraction.kind === "inline") {
1032
+ buildCombinedRouteMapWithSearch(
1033
+ realPath,
1034
+ void 0,
1035
+ /* @__PURE__ */ new Set(),
1036
+ diagnostics,
1037
+ extraction.block
1038
+ );
1039
+ return diagnostics;
1040
+ }
1041
+ const imported = resolveImportedVariable(source, extraction.name);
749
1042
  let targetFile;
750
1043
  let exportedName;
751
1044
  if (imported) {
@@ -765,9 +1058,8 @@ function detectUnresolvableIncludes(routerFilePath) {
765
1058
  exportedName = imported.exportedName;
766
1059
  } else {
767
1060
  targetFile = realPath;
768
- exportedName = urlsVarName;
1061
+ exportedName = extraction.name;
769
1062
  }
770
- const diagnostics = [];
771
1063
  buildCombinedRouteMapWithSearch(
772
1064
  targetFile,
773
1065
  exportedName,
@@ -803,7 +1095,7 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
803
1095
  if (existsSync3(oldCombinedPath)) {
804
1096
  unlinkSync(oldCombinedPath);
805
1097
  console.log(
806
- `[rsc-router] Removed stale combined route types: ${oldCombinedPath}`
1098
+ `[rango] Removed stale combined route types: ${oldCombinedPath}`
807
1099
  );
808
1100
  }
809
1101
  } catch {
@@ -815,38 +1107,22 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
815
1107
  throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
816
1108
  }
817
1109
  for (const routerFilePath of routerFilePaths) {
818
- let routerSource;
819
- try {
820
- routerSource = readFileSync3(routerFilePath, "utf-8");
821
- } catch {
822
- continue;
823
- }
824
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
825
- if (!urlsVarName) continue;
826
- let result;
827
- const imported = resolveImportedVariable(routerSource, urlsVarName);
828
- if (imported) {
829
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
830
- if (!targetFile) continue;
831
- result = buildCombinedRouteMapWithSearch(
832
- targetFile,
833
- imported.exportedName
834
- );
835
- } else {
836
- result = buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
1110
+ const result = buildCombinedRouteMapForRouterFile(routerFilePath);
1111
+ if (Object.keys(result.routes).length === 0 && Object.keys(result.searchSchemas).length === 0) {
1112
+ let routerSource;
1113
+ try {
1114
+ routerSource = readFileSync3(routerFilePath, "utf-8");
1115
+ } catch {
1116
+ continue;
1117
+ }
1118
+ if (!extractUrlsFromRouter(routerSource)) continue;
837
1119
  }
838
- const routerBasename = pathBasename(routerFilePath).replace(
839
- /\.(tsx?|jsx?)$/,
840
- ""
841
- );
842
- const outPath = join2(
843
- dirname2(routerFilePath),
844
- `${routerBasename}.named-routes.gen.ts`
845
- );
1120
+ const outPath = genFileTsPath(routerFilePath);
846
1121
  const existing = existsSync3(outPath) ? readFileSync3(outPath, "utf-8") : null;
847
1122
  if (Object.keys(result.routes).length === 0) {
848
1123
  if (!existing) {
849
1124
  const emptySource = generateRouteTypesSource({});
1125
+ opts?.onWrite?.(outPath, emptySource);
850
1126
  writeFileSync2(outPath, emptySource);
851
1127
  }
852
1128
  continue;
@@ -866,22 +1142,25 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
866
1142
  continue;
867
1143
  }
868
1144
  }
1145
+ opts?.onWrite?.(outPath, source);
869
1146
  writeFileSync2(outPath, source);
870
1147
  console.log(
871
- `[rsc-router] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
1148
+ `[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
872
1149
  );
873
1150
  }
874
1151
  }
875
1152
  }
876
- var ROUTER_CALL_PATTERN;
1153
+ var ROUTER_CALL_PATTERN, ROUTER_CALL_PATTERN_G;
877
1154
  var init_router_processing = __esm({
878
1155
  "src/build/route-types/router-processing.ts"() {
879
1156
  "use strict";
880
1157
  init_codegen();
1158
+ init_source_scan();
881
1159
  init_include_resolution();
882
1160
  init_per_module_writer();
883
1161
  init_route_name();
884
1162
  ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
1163
+ ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
885
1164
  }
886
1165
  });
887
1166
 
@@ -919,7 +1198,7 @@ import {
919
1198
  import { createElement, StrictMode } from "react";
920
1199
  import { hydrateRoot } from "react-dom/client";
921
1200
  import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
922
- import { initBrowserApp, RSCRouter } from "@rangojs/router/browser";
1201
+ import { initBrowserApp, Rango } from "@rangojs/router/browser";
923
1202
 
924
1203
  async function initializeApp() {
925
1204
  const deps = {
@@ -930,11 +1209,16 @@ async function initializeApp() {
930
1209
  createTemporaryReferenceSet,
931
1210
  };
932
1211
 
933
- await initBrowserApp({ rscStream, deps });
1212
+ // initBrowserApp resolves the initial payload and returns the browser app
1213
+ // context, including strictMode (default true) from createRouter. StrictMode
1214
+ // is the default; createRouter({ strictMode: false }) ships the opt-out in the
1215
+ // payload metadata. StrictMode emits no DOM, so toggling never changes markup.
1216
+ const { strictMode } = await initBrowserApp({ rscStream, deps });
934
1217
 
1218
+ const app = createElement(Rango);
935
1219
  hydrateRoot(
936
1220
  document,
937
- createElement(StrictMode, null, createElement(RSCRouter))
1221
+ strictMode === false ? app : createElement(StrictMode, null, app)
938
1222
  );
939
1223
  }
940
1224
 
@@ -963,12 +1247,37 @@ export const renderHTML = createSSRHandler({
963
1247
  }
964
1248
  });
965
1249
 
1250
+ // src/vite/utils/directive-prologue.ts
1251
+ import { parseAst } from "vite";
1252
+ function hasUseClientDirective(source) {
1253
+ let program;
1254
+ try {
1255
+ program = parseAst(source, { lang: "tsx" });
1256
+ } catch {
1257
+ return false;
1258
+ }
1259
+ for (const node of program.body ?? []) {
1260
+ if (node?.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
1261
+ if (node.expression.value === "use client") return true;
1262
+ continue;
1263
+ }
1264
+ break;
1265
+ }
1266
+ return false;
1267
+ }
1268
+ var init_directive_prologue = __esm({
1269
+ "src/vite/utils/directive-prologue.ts"() {
1270
+ "use strict";
1271
+ }
1272
+ });
1273
+
966
1274
  // src/vite/plugins/version-plugin.ts
967
1275
  var version_plugin_exports = {};
968
1276
  __export(version_plugin_exports, {
969
- createVersionPlugin: () => createVersionPlugin
1277
+ createVersionPlugin: () => createVersionPlugin,
1278
+ isViteDepCachePath: () => isViteDepCachePath
970
1279
  });
971
- import { parseAst } from "vite";
1280
+ import { parseAst as parseAst2 } from "vite";
972
1281
  function isCodeModule(id) {
973
1282
  return /\.(tsx?|jsx?)($|\?)/.test(id);
974
1283
  }
@@ -976,23 +1285,13 @@ function normalizeModuleId(id) {
976
1285
  return id.split("?", 1)[0];
977
1286
  }
978
1287
  function getClientModuleSignature(source) {
1288
+ if (!hasUseClientDirective(source)) return void 0;
979
1289
  let program;
980
1290
  try {
981
- program = parseAst(source, { jsx: true });
1291
+ program = parseAst2(source, { lang: "tsx" });
982
1292
  } catch {
983
1293
  return void 0;
984
1294
  }
985
- let isUseClient = false;
986
- for (const node of program.body ?? []) {
987
- if (node?.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
988
- if (node.expression.value === "use client") {
989
- isUseClient = true;
990
- }
991
- continue;
992
- }
993
- break;
994
- }
995
- if (!isUseClient) return void 0;
996
1295
  const exports = /* @__PURE__ */ new Set();
997
1296
  let hasDefault = false;
998
1297
  let hasExportAll = false;
@@ -1061,10 +1360,12 @@ function createVersionPlugin() {
1061
1360
  let currentVersion = buildVersion;
1062
1361
  let isDev = false;
1063
1362
  let server = null;
1363
+ let resolvedCacheDir;
1064
1364
  const clientModuleSignatures = /* @__PURE__ */ new Map();
1365
+ let versionCounter = 0;
1065
1366
  const bumpVersion = (reason) => {
1066
- currentVersion = Date.now().toString(16);
1067
- console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
1367
+ currentVersion = Date.now().toString(16) + String(++versionCounter);
1368
+ console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
1068
1369
  const rscEnv = server?.environments?.rsc;
1069
1370
  const versionMod = rscEnv?.moduleGraph?.getModuleById(
1070
1371
  "\0" + VIRTUAL_IDS.version
@@ -1078,6 +1379,7 @@ function createVersionPlugin() {
1078
1379
  enforce: "pre",
1079
1380
  configResolved(config) {
1080
1381
  isDev = config.command === "serve";
1382
+ resolvedCacheDir = config.cacheDir ? String(config.cacheDir).replace(/\\/g, "/") : void 0;
1081
1383
  },
1082
1384
  configureServer(devServer) {
1083
1385
  server = devServer;
@@ -1114,11 +1416,14 @@ function createVersionPlugin() {
1114
1416
  }
1115
1417
  return null;
1116
1418
  },
1117
- // Track RSC module changes and update version
1118
1419
  async hotUpdate(ctx) {
1119
1420
  if (!isDev) return;
1120
1421
  const isRscModule = this.environment?.name === "rsc";
1121
1422
  if (!isRscModule) return;
1423
+ if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
1424
+ if (ctx.modules.length === 1 && ctx.modules[0].id === "\0" + VIRTUAL_IDS.version) {
1425
+ return;
1426
+ }
1122
1427
  if (isCodeModule(ctx.file)) {
1123
1428
  const filePath = normalizeModuleId(ctx.file);
1124
1429
  const previousSignature = clientModuleSignatures.get(filePath);
@@ -1145,10 +1450,22 @@ function createVersionPlugin() {
1145
1450
  }
1146
1451
  };
1147
1452
  }
1453
+ function isViteDepCachePath(filePath, cacheDir) {
1454
+ if (!filePath) return false;
1455
+ const normalized = filePath.replace(/\\/g, "/");
1456
+ if (cacheDir) {
1457
+ const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
1458
+ if (normalized === normalizedCacheDir || normalized.startsWith(normalizedCacheDir + "/")) {
1459
+ return true;
1460
+ }
1461
+ }
1462
+ return /\/node_modules\/\.vite[^/]*\//.test(normalized) || normalized.includes("/.vite-isolated/");
1463
+ }
1148
1464
  var init_version_plugin = __esm({
1149
1465
  "src/vite/plugins/version-plugin.ts"() {
1150
1466
  "use strict";
1151
1467
  init_virtual_entries();
1468
+ init_directive_prologue();
1152
1469
  }
1153
1470
  });
1154
1471
 
@@ -1190,7 +1507,7 @@ var runtime_discovery_exports = {};
1190
1507
  __export(runtime_discovery_exports, {
1191
1508
  discoverAndWriteRouteTypes: () => discoverAndWriteRouteTypes
1192
1509
  });
1193
- import { dirname as dirname3, join as join3, basename, resolve as resolve3 } from "node:path";
1510
+ import { resolve as resolve3 } from "node:path";
1194
1511
  import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
1195
1512
  async function discoverAndWriteRouteTypes(opts) {
1196
1513
  let createViteServer;
@@ -1300,22 +1617,12 @@ This means createRouter() stack trace parsing matched an internal frame.
1300
1617
  Set an explicit \`id\` on createRouter() or check the call site.`
1301
1618
  );
1302
1619
  }
1303
- if (!routeSearchSchemas || Object.keys(routeSearchSchemas).length === 0) {
1304
- const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
1305
- if (Object.keys(staticParsed.searchSchemas).length > 0) {
1306
- const filtered = {};
1307
- for (const name of Object.keys(routeManifest)) {
1308
- const schema = staticParsed.searchSchemas[name];
1309
- if (schema) filtered[name] = schema;
1310
- }
1311
- if (Object.keys(filtered).length > 0) {
1312
- routeSearchSchemas = filtered;
1313
- }
1314
- }
1315
- }
1316
- const routerDir = dirname3(sourceFile);
1317
- const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
1318
- const outPath = join3(routerDir, `${routerBasename}.named-routes.gen.ts`);
1620
+ routeSearchSchemas = resolveSearchSchemas(
1621
+ Object.keys(routeManifest),
1622
+ routeSearchSchemas,
1623
+ sourceFile
1624
+ );
1625
+ const outPath = genFileTsPath(sourceFile);
1319
1626
  const source = generateRouteTypesSource(
1320
1627
  routeManifest,
1321
1628
  routeSearchSchemas && Object.keys(routeSearchSchemas).length > 0 ? routeSearchSchemas : void 0
@@ -1352,7 +1659,7 @@ var init_runtime_discovery = __esm({
1352
1659
 
1353
1660
  // src/bin/rango.ts
1354
1661
  init_generate_route_types();
1355
- import { resolve as resolve4, dirname as dirname4 } from "node:path";
1662
+ import { resolve as resolve4, dirname as dirname3 } from "node:path";
1356
1663
  import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "node:fs";
1357
1664
  var [command, ...rawArgs] = process.argv.slice(2);
1358
1665
  if (command === "generate") {
@@ -1423,12 +1730,12 @@ Examples:
1423
1730
  );
1424
1731
  }
1425
1732
  function findProjectRoot(fromPath) {
1426
- let dir = dirname4(resolve4(fromPath));
1427
- while (dir !== dirname4(dir)) {
1733
+ let dir = dirname3(resolve4(fromPath));
1734
+ while (dir !== dirname3(dir)) {
1428
1735
  if (existsSync5(resolve4(dir, "package.json")) || existsSync5(resolve4(dir, "vite.config.ts")) || existsSync5(resolve4(dir, "vite.config.js"))) {
1429
1736
  return dir;
1430
1737
  }
1431
- dir = dirname4(dir);
1738
+ dir = dirname3(dir);
1432
1739
  }
1433
1740
  return process.cwd();
1434
1741
  }