@rangojs/router 0.0.0-experimental.d7eeaa75 → 0.0.0-experimental.d98a8e9d

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 (278) hide show
  1. package/README.md +120 -25
  2. package/dist/bin/rango.js +147 -57
  3. package/dist/testing/vitest.js +82 -0
  4. package/dist/vite/index.js +2154 -861
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +57 -11
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +3 -1
  9. package/skills/bundle-analysis/SKILL.md +159 -0
  10. package/skills/cache-guide/SKILL.md +220 -30
  11. package/skills/caching/SKILL.md +116 -8
  12. package/skills/composability/SKILL.md +27 -2
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +229 -20
  16. package/skills/host-router/SKILL.md +45 -20
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +46 -4
  19. package/skills/layout/SKILL.md +28 -7
  20. package/skills/links/SKILL.md +247 -17
  21. package/skills/loader/SKILL.md +219 -9
  22. package/skills/middleware/SKILL.md +47 -12
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +27 -0
  26. package/skills/observability/SKILL.md +137 -0
  27. package/skills/parallel/SKILL.md +71 -6
  28. package/skills/prerender/SKILL.md +14 -33
  29. package/skills/rango/SKILL.md +243 -22
  30. package/skills/react-compiler/SKILL.md +168 -0
  31. package/skills/response-routes/SKILL.md +122 -47
  32. package/skills/route/SKILL.md +57 -4
  33. package/skills/router-setup/SKILL.md +3 -3
  34. package/skills/server-actions/SKILL.md +751 -0
  35. package/skills/streams-and-websockets/SKILL.md +283 -0
  36. package/skills/testing/SKILL.md +128 -0
  37. package/skills/testing/bindings.md +89 -0
  38. package/skills/testing/cache-prerender.md +98 -0
  39. package/skills/testing/client-components.md +121 -0
  40. package/skills/testing/e2e-parity.md +124 -0
  41. package/skills/testing/flight.md +89 -0
  42. package/skills/testing/handles.md +127 -0
  43. package/skills/testing/loader.md +108 -0
  44. package/skills/testing/middleware.md +97 -0
  45. package/skills/testing/render-handler.md +102 -0
  46. package/skills/testing/response-routes.md +94 -0
  47. package/skills/testing/reverse-and-types.md +83 -0
  48. package/skills/testing/server-actions.md +89 -0
  49. package/skills/testing/server-tree.md +128 -0
  50. package/skills/testing/setup.md +120 -0
  51. package/skills/typesafety/SKILL.md +319 -27
  52. package/skills/use-cache/SKILL.md +34 -5
  53. package/skills/view-transitions/SKILL.md +294 -0
  54. package/src/__augment-tests__/augment.ts +81 -0
  55. package/src/__augment-tests__/augmented.check.ts +116 -0
  56. package/src/browser/action-coordinator.ts +53 -36
  57. package/src/browser/app-shell.ts +52 -0
  58. package/src/browser/event-controller.ts +86 -70
  59. package/src/browser/history-state.ts +21 -0
  60. package/src/browser/index.ts +3 -3
  61. package/src/browser/navigation-bridge.ts +84 -11
  62. package/src/browser/navigation-client.ts +104 -68
  63. package/src/browser/navigation-store.ts +32 -9
  64. package/src/browser/navigation-transaction.ts +10 -28
  65. package/src/browser/partial-update.ts +64 -26
  66. package/src/browser/prefetch/cache.ts +183 -44
  67. package/src/browser/prefetch/fetch.ts +228 -37
  68. package/src/browser/prefetch/queue.ts +36 -5
  69. package/src/browser/rango-state.ts +53 -13
  70. package/src/browser/react/Link.tsx +30 -2
  71. package/src/browser/react/NavigationProvider.tsx +72 -31
  72. package/src/browser/react/filter-segment-order.ts +51 -7
  73. package/src/browser/react/index.ts +3 -0
  74. package/src/browser/react/location-state-shared.ts +175 -4
  75. package/src/browser/react/location-state.ts +39 -13
  76. package/src/browser/react/use-handle.ts +17 -9
  77. package/src/browser/react/use-navigation.ts +22 -2
  78. package/src/browser/react/use-params.ts +20 -8
  79. package/src/browser/react/use-reverse.ts +106 -0
  80. package/src/browser/react/use-router.ts +22 -2
  81. package/src/browser/react/use-segments.ts +11 -8
  82. package/src/browser/response-adapter.ts +32 -1
  83. package/src/browser/rsc-router.tsx +69 -22
  84. package/src/browser/scroll-restoration.ts +22 -14
  85. package/src/browser/segment-reconciler.ts +36 -14
  86. package/src/browser/segment-structure-assert.ts +2 -2
  87. package/src/browser/server-action-bridge.ts +23 -30
  88. package/src/browser/types.ts +21 -0
  89. package/src/build/collect-fallback-refs.ts +107 -0
  90. package/src/build/generate-manifest.ts +60 -35
  91. package/src/build/generate-route-types.ts +2 -0
  92. package/src/build/index.ts +8 -1
  93. package/src/build/prefix-tree-utils.ts +123 -0
  94. package/src/build/route-trie.ts +95 -25
  95. package/src/build/route-types/codegen.ts +4 -4
  96. package/src/build/route-types/include-resolution.ts +1 -1
  97. package/src/build/route-types/per-module-writer.ts +7 -4
  98. package/src/build/route-types/router-processing.ts +55 -14
  99. package/src/build/route-types/scan-filter.ts +1 -1
  100. package/src/build/route-types/source-scan.ts +118 -0
  101. package/src/build/runtime-discovery.ts +9 -20
  102. package/src/cache/cache-scope.ts +28 -42
  103. package/src/cache/cf/cf-cache-store.ts +54 -13
  104. package/src/client.rsc.tsx +3 -0
  105. package/src/client.tsx +96 -205
  106. package/src/context-var.ts +5 -5
  107. package/src/decode-loader-results.ts +36 -0
  108. package/src/errors.ts +30 -4
  109. package/src/handle.ts +32 -14
  110. package/src/host/index.ts +2 -2
  111. package/src/host/router.ts +129 -57
  112. package/src/host/types.ts +31 -2
  113. package/src/host/utils.ts +1 -1
  114. package/src/href-client.ts +140 -21
  115. package/src/index.rsc.ts +10 -6
  116. package/src/index.ts +54 -17
  117. package/src/loader-store.ts +500 -0
  118. package/src/loader.rsc.ts +25 -7
  119. package/src/loader.ts +16 -9
  120. package/src/missing-id-error.ts +68 -0
  121. package/src/outlet-context.ts +1 -1
  122. package/src/prerender.ts +27 -6
  123. package/src/response-utils.ts +37 -0
  124. package/src/reverse.ts +65 -36
  125. package/src/route-content-wrapper.tsx +6 -28
  126. package/src/route-definition/dsl-helpers.ts +384 -257
  127. package/src/route-definition/helper-factories.ts +29 -139
  128. package/src/route-definition/helpers-types.ts +100 -28
  129. package/src/route-definition/resolve-handler-use.ts +6 -0
  130. package/src/route-definition/use-item-types.ts +32 -0
  131. package/src/route-types.ts +26 -41
  132. package/src/router/basename.ts +14 -0
  133. package/src/router/content-negotiation.ts +15 -2
  134. package/src/router/error-handling.ts +1 -1
  135. package/src/router/find-match.ts +54 -6
  136. package/src/router/handler-context.ts +21 -38
  137. package/src/router/intercept-resolution.ts +4 -18
  138. package/src/router/lazy-includes.ts +41 -22
  139. package/src/router/loader-resolution.ts +82 -36
  140. package/src/router/manifest.ts +41 -19
  141. package/src/router/match-api.ts +4 -3
  142. package/src/router/match-handlers.ts +63 -20
  143. package/src/router/match-middleware/cache-lookup.ts +44 -91
  144. package/src/router/match-middleware/cache-store.ts +3 -2
  145. package/src/router/match-result.ts +53 -32
  146. package/src/router/metrics.ts +1 -1
  147. package/src/router/middleware-types.ts +15 -26
  148. package/src/router/middleware.ts +99 -84
  149. package/src/router/pattern-matching.ts +116 -19
  150. package/src/router/prerender-match.ts +1 -1
  151. package/src/router/preview-match.ts +3 -1
  152. package/src/router/request-classification.ts +4 -28
  153. package/src/router/revalidation.ts +58 -2
  154. package/src/router/router-interfaces.ts +45 -28
  155. package/src/router/router-options.ts +40 -1
  156. package/src/router/router-registry.ts +2 -5
  157. package/src/router/segment-resolution/fresh.ts +27 -6
  158. package/src/router/segment-resolution/revalidation.ts +147 -106
  159. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  160. package/src/router/substitute-pattern-params.ts +56 -0
  161. package/src/router/telemetry.ts +99 -0
  162. package/src/router/trie-matching.ts +40 -16
  163. package/src/router/types.ts +8 -0
  164. package/src/router/url-params.ts +49 -0
  165. package/src/router.ts +52 -30
  166. package/src/rsc/handler-context.ts +2 -2
  167. package/src/rsc/handler.ts +28 -69
  168. package/src/rsc/helpers.ts +91 -43
  169. package/src/rsc/index.ts +1 -1
  170. package/src/rsc/manifest-init.ts +28 -41
  171. package/src/rsc/origin-guard.ts +28 -10
  172. package/src/rsc/progressive-enhancement.ts +4 -0
  173. package/src/rsc/response-error.ts +79 -12
  174. package/src/rsc/response-route-handler.ts +57 -61
  175. package/src/rsc/rsc-rendering.ts +35 -51
  176. package/src/rsc/runtime-warnings.ts +9 -10
  177. package/src/rsc/server-action.ts +17 -37
  178. package/src/rsc/ssr-setup.ts +16 -0
  179. package/src/rsc/types.ts +8 -2
  180. package/src/runtime-env.ts +18 -0
  181. package/src/search-params.ts +4 -4
  182. package/src/segment-content-promise.ts +67 -0
  183. package/src/segment-loader-promise.ts +122 -0
  184. package/src/segment-system.tsx +132 -116
  185. package/src/serialize.ts +243 -0
  186. package/src/server/context.ts +175 -53
  187. package/src/server/cookie-store.ts +28 -4
  188. package/src/server/request-context.ts +67 -51
  189. package/src/ssr/index.tsx +5 -1
  190. package/src/static-handler.ts +25 -3
  191. package/src/testing/cache-status.ts +166 -0
  192. package/src/testing/collect-handle.ts +63 -0
  193. package/src/testing/dispatch.ts +581 -0
  194. package/src/testing/dom.entry.ts +22 -0
  195. package/src/testing/e2e/fixture.ts +188 -0
  196. package/src/testing/e2e/index.ts +149 -0
  197. package/src/testing/e2e/matchers.ts +51 -0
  198. package/src/testing/e2e/page-helpers.ts +272 -0
  199. package/src/testing/e2e/parity.ts +326 -0
  200. package/src/testing/e2e/server.ts +195 -0
  201. package/src/testing/flight-matchers.ts +110 -0
  202. package/src/testing/flight-normalize.ts +38 -0
  203. package/src/testing/flight-runtime.d.ts +57 -0
  204. package/src/testing/flight-tree.ts +682 -0
  205. package/src/testing/flight.entry.ts +51 -0
  206. package/src/testing/flight.ts +234 -0
  207. package/src/testing/generated-routes.ts +223 -0
  208. package/src/testing/index.ts +106 -0
  209. package/src/testing/internal/context.ts +304 -0
  210. package/src/testing/internal/flight-client-globals.ts +30 -0
  211. package/src/testing/internal/seed-vars.ts +42 -0
  212. package/src/testing/render-handler.ts +323 -0
  213. package/src/testing/render-route.tsx +590 -0
  214. package/src/testing/run-loader.ts +363 -0
  215. package/src/testing/run-middleware.ts +205 -0
  216. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  217. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  218. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  219. package/src/testing/vitest-stubs/version.ts +5 -0
  220. package/src/testing/vitest.ts +285 -0
  221. package/src/types/global-namespace.ts +39 -26
  222. package/src/types/handler-context.ts +68 -50
  223. package/src/types/index.ts +1 -0
  224. package/src/types/loader-types.ts +11 -9
  225. package/src/types/request-scope.ts +126 -0
  226. package/src/types/route-entry.ts +11 -0
  227. package/src/types/segments.ts +35 -2
  228. package/src/urls/include-helper.ts +34 -67
  229. package/src/urls/index.ts +1 -5
  230. package/src/urls/path-helper-types.ts +41 -7
  231. package/src/urls/path-helper.ts +17 -52
  232. package/src/urls/pattern-types.ts +36 -19
  233. package/src/urls/response-types.ts +22 -29
  234. package/src/urls/type-extraction.ts +58 -139
  235. package/src/urls/urls-function.ts +1 -5
  236. package/src/use-loader.tsx +413 -42
  237. package/src/vite/debug.ts +185 -0
  238. package/src/vite/discovery/bundle-postprocess.ts +6 -6
  239. package/src/vite/discovery/discover-routers.ts +106 -75
  240. package/src/vite/discovery/discovery-errors.ts +194 -0
  241. package/src/vite/discovery/gate-state.ts +171 -0
  242. package/src/vite/discovery/prerender-collection.ts +67 -26
  243. package/src/vite/discovery/route-types-writer.ts +40 -84
  244. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  245. package/src/vite/discovery/state.ts +33 -0
  246. package/src/vite/discovery/virtual-module-codegen.ts +13 -23
  247. package/src/vite/index.ts +2 -0
  248. package/src/vite/plugin-types.ts +67 -0
  249. package/src/vite/plugins/cjs-to-esm.ts +8 -7
  250. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  251. package/src/vite/plugins/client-ref-hashing.ts +28 -5
  252. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  253. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  254. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  255. package/src/vite/plugins/expose-action-id.ts +54 -30
  256. package/src/vite/plugins/expose-id-utils.ts +12 -8
  257. package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
  258. package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
  259. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
  260. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  261. package/src/vite/plugins/expose-internal-ids.ts +496 -486
  262. package/src/vite/plugins/performance-tracks.ts +29 -25
  263. package/src/vite/plugins/use-cache-transform.ts +65 -50
  264. package/src/vite/plugins/version-injector.ts +39 -23
  265. package/src/vite/plugins/version-plugin.ts +59 -2
  266. package/src/vite/plugins/virtual-entries.ts +2 -2
  267. package/src/vite/rango.ts +116 -29
  268. package/src/vite/router-discovery.ts +750 -100
  269. package/src/vite/utils/ast-handler-extract.ts +15 -15
  270. package/src/vite/utils/banner.ts +1 -1
  271. package/src/vite/utils/bundle-analysis.ts +4 -2
  272. package/src/vite/utils/client-chunks.ts +190 -0
  273. package/src/vite/utils/forward-user-plugins.ts +193 -0
  274. package/src/vite/utils/manifest-utils.ts +8 -59
  275. package/src/vite/utils/package-resolution.ts +41 -1
  276. package/src/vite/utils/prerender-utils.ts +21 -6
  277. package/src/vite/utils/shared-utils.ts +107 -26
  278. package/src/browser/action-response-classifier.ts +0 -99
@@ -21,8 +21,8 @@ function hashId(filePath, exportName) {
21
21
  function makeStubId(filePath, exportName, isBuild) {
22
22
  return isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
23
23
  }
24
- function hashInlineId(filePath, lineNumber, index) {
25
- const input = index !== void 0 && index > 0 ? `${filePath}:${lineNumber}:${index}` : `${filePath}:${lineNumber}`;
24
+ function hashInlineId(filePath, fnName, index) {
25
+ const input = `${filePath}:${fnName}:${index}`;
26
26
  return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
27
27
  }
28
28
  function buildExportMap(program) {
@@ -191,7 +191,100 @@ function escapeRegExp(input) {
191
191
  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
192
192
  }
193
193
 
194
+ // src/vite/debug.ts
195
+ import debugFactory from "debug";
196
+ var NS = {
197
+ config: "rango:config",
198
+ discovery: "rango:discovery",
199
+ routes: "rango:routes",
200
+ prerender: "rango:prerender",
201
+ build: "rango:build",
202
+ dev: "rango:dev",
203
+ transform: "rango:transform",
204
+ chunks: "rango:chunks"
205
+ };
206
+ if (process.env.INTERNAL_RANGO_DEBUG) {
207
+ const existing = debugFactory.disable();
208
+ debugFactory.enable(existing ? `${existing},rango:*` : "rango:*");
209
+ }
210
+ function createRangoDebugger(namespace) {
211
+ const primary = debugFactory(namespace);
212
+ const shadow = debugFactory(`vite:${namespace}`);
213
+ if (primary.enabled) return primary;
214
+ if (shadow.enabled) return shadow;
215
+ return void 0;
216
+ }
217
+ async function timed(debug11, label, fn) {
218
+ if (!debug11) return await fn();
219
+ const start = performance.now();
220
+ try {
221
+ return await fn();
222
+ } finally {
223
+ debug11("%s (%sms)", label, (performance.now() - start).toFixed(1));
224
+ }
225
+ }
226
+ function timedSync(debug11, label, fn) {
227
+ if (!debug11) return fn();
228
+ const start = performance.now();
229
+ try {
230
+ return fn();
231
+ } finally {
232
+ debug11("%s (%sms)", label, (performance.now() - start).toFixed(1));
233
+ }
234
+ }
235
+ function createCounter(debug11, label) {
236
+ if (!debug11) return void 0;
237
+ let n = 0;
238
+ let totalMs = 0;
239
+ let slowestMs = 0;
240
+ let slowestFile = "";
241
+ const record = (file, ms) => {
242
+ n++;
243
+ totalMs += ms;
244
+ if (ms > slowestMs) {
245
+ slowestMs = ms;
246
+ slowestFile = file;
247
+ }
248
+ };
249
+ return {
250
+ record,
251
+ time(file, fn) {
252
+ const start = performance.now();
253
+ let out;
254
+ try {
255
+ out = fn();
256
+ } catch (err) {
257
+ record(file, performance.now() - start);
258
+ throw err;
259
+ }
260
+ if (out && typeof out.then === "function") {
261
+ return out.finally(
262
+ () => record(file, performance.now() - start)
263
+ );
264
+ }
265
+ record(file, performance.now() - start);
266
+ return out;
267
+ },
268
+ flush() {
269
+ if (n === 0) return;
270
+ debug11(
271
+ "%s: %d files, %sms total, slowest %sms %s",
272
+ label,
273
+ n,
274
+ totalMs.toFixed(1),
275
+ slowestMs.toFixed(1),
276
+ slowestFile
277
+ );
278
+ n = 0;
279
+ totalMs = 0;
280
+ slowestMs = 0;
281
+ slowestFile = "";
282
+ }
283
+ };
284
+ }
285
+
194
286
  // src/vite/plugins/expose-action-id.ts
287
+ var debug = createRangoDebugger(NS.transform);
195
288
  function getRscPluginApi(config) {
196
289
  let plugin = config.plugins.find((p) => p.name === "rsc:minimal");
197
290
  if (!plugin) {
@@ -200,7 +293,7 @@ function getRscPluginApi(config) {
200
293
  );
201
294
  if (plugin) {
202
295
  console.warn(
203
- `[rsc-router:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). Consider updating the name lookup if the plugin was renamed.`
296
+ `[rango:expose-action-id] RSC plugin found by API structure (name: "${plugin.name}"). Consider updating the name lookup if the plugin was renamed.`
204
297
  );
205
298
  }
206
299
  }
@@ -280,6 +373,8 @@ function exposeActionId() {
280
373
  let isBuild = false;
281
374
  let hashToFileMap;
282
375
  let rscPluginApi;
376
+ const counterTransform = createCounter(debug, "expose-action-id transform");
377
+ const counterRender = createCounter(debug, "expose-action-id renderChunk");
283
378
  return {
284
379
  name: "@rangojs/router:expose-action-id",
285
380
  // Run after all other plugins (including RSC plugin's transforms)
@@ -289,13 +384,17 @@ function exposeActionId() {
289
384
  isBuild = config.command === "build";
290
385
  rscPluginApi = getRscPluginApi(config);
291
386
  },
387
+ buildEnd() {
388
+ counterTransform?.flush();
389
+ counterRender?.flush();
390
+ },
292
391
  buildStart() {
293
392
  if (!rscPluginApi) {
294
393
  rscPluginApi = getRscPluginApi(config);
295
394
  }
296
395
  if (!rscPluginApi) {
297
396
  throw new Error(
298
- "[rsc-router] Could not find @vitejs/plugin-rsc. @rangojs/router requires the Vite RSC plugin, which is included automatically by rango()."
397
+ "[rango] Could not find @vitejs/plugin-rsc. @rangojs/router requires the Vite RSC plugin, which is included automatically by rango()."
299
398
  );
300
399
  }
301
400
  if (!isBuild) return;
@@ -325,35 +424,49 @@ function exposeActionId() {
325
424
  if (id.includes("/node_modules/")) {
326
425
  return;
327
426
  }
328
- return transformServerReferences(code, id);
427
+ const start = counterTransform ? performance.now() : 0;
428
+ try {
429
+ return transformServerReferences(code, id);
430
+ } finally {
431
+ counterTransform?.record(id, performance.now() - start);
432
+ }
329
433
  },
330
434
  // Build mode: renderChunk runs after all transforms and bundling complete
331
435
  renderChunk(code, chunk) {
332
- const isRscEnv = this.environment?.name === "rsc";
333
- const effectiveMap = isRscEnv ? hashToFileMap : void 0;
334
- if (isRscEnv && hashToFileMap) {
335
- const s = new MagicString(code);
336
- const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
337
- const changed2 = applyRegisterReferenceWrapping(code, s, hashToFileMap);
338
- if (changed1 || changed2) {
339
- return {
340
- code: s.toString(),
341
- map: s.generateMap({
342
- source: chunk.fileName,
343
- includeContent: true
344
- })
345
- };
436
+ const start = counterRender ? performance.now() : 0;
437
+ try {
438
+ const isRscEnv = this.environment?.name === "rsc";
439
+ const effectiveMap = isRscEnv ? hashToFileMap : void 0;
440
+ if (isRscEnv && hashToFileMap) {
441
+ const s = new MagicString(code);
442
+ const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
443
+ const changed2 = applyRegisterReferenceWrapping(
444
+ code,
445
+ s,
446
+ hashToFileMap
447
+ );
448
+ if (changed1 || changed2) {
449
+ return {
450
+ code: s.toString(),
451
+ map: s.generateMap({
452
+ source: chunk.fileName,
453
+ includeContent: true
454
+ })
455
+ };
456
+ }
457
+ return null;
346
458
  }
347
- return null;
459
+ return transformServerReferences(code, chunk.fileName, effectiveMap);
460
+ } finally {
461
+ counterRender?.record(chunk.fileName, performance.now() - start);
348
462
  }
349
- return transformServerReferences(code, chunk.fileName, effectiveMap);
350
463
  }
351
464
  };
352
465
  }
353
466
 
354
467
  // src/vite/plugins/expose-internal-ids.ts
355
468
  import { parseAst as parseAst2 } from "vite";
356
- import MagicString4 from "magic-string";
469
+ import MagicString3 from "magic-string";
357
470
  import path4 from "node:path";
358
471
 
359
472
  // src/vite/utils/ast-handler-extract.ts
@@ -363,7 +476,7 @@ function isDirectivePrologueStatement(node) {
363
476
  function findImportInsertionPos(code, parseAst4) {
364
477
  let program;
365
478
  try {
366
- program = parseAst4(code, { jsx: true });
479
+ program = parseAst4(code, { lang: "tsx" });
367
480
  } catch {
368
481
  return 0;
369
482
  }
@@ -403,7 +516,7 @@ function walkNode(node, parent, ancestors, enter) {
403
516
  function findHandlerCalls(code, fnName, parseAst4) {
404
517
  let program;
405
518
  try {
406
- program = parseAst4(code, { jsx: true });
519
+ program = parseAst4(code, { lang: "tsx" });
407
520
  } catch {
408
521
  return [];
409
522
  }
@@ -477,7 +590,7 @@ function getImportedLocalNamesFromProgram(program, importedName) {
477
590
  }
478
591
  function getImportedLocalNames(code, importedName, parseAst4) {
479
592
  try {
480
- const program = parseAst4(code, { jsx: true });
593
+ const program = parseAst4(code, { lang: "tsx" });
481
594
  return getImportedLocalNamesFromProgram(program, importedName);
482
595
  } catch {
483
596
  return /* @__PURE__ */ new Set();
@@ -486,7 +599,7 @@ function getImportedLocalNames(code, importedName, parseAst4) {
486
599
  function extractImportDeclarations(code, parseAst4) {
487
600
  let program;
488
601
  try {
489
- program = parseAst4(code, { jsx: true });
602
+ program = parseAst4(code, { lang: "tsx" });
490
603
  } catch {
491
604
  return [];
492
605
  }
@@ -541,7 +654,7 @@ function isSafeVariableDeclaration(node, handlerNames) {
541
654
  function extractModuleLevelDeclarations(code, parseAst4, handlerNames) {
542
655
  let program;
543
656
  try {
544
- program = parseAst4(code, { jsx: true });
657
+ program = parseAst4(code, { lang: "tsx" });
545
658
  } catch {
546
659
  return [];
547
660
  }
@@ -585,14 +698,12 @@ function transformInlineHandlers(fnName, virtualPrefix, s, code, filePath, virtu
585
698
  parseAst4,
586
699
  handlerNames
587
700
  );
588
- const lineCounts = /* @__PURE__ */ new Map();
589
701
  const importStatements = [];
590
- for (const site of inlineSites) {
591
- const lineCount = lineCounts.get(site.lineNumber) ?? 0;
592
- lineCounts.set(site.lineNumber, lineCount + 1);
593
- const hash = hashInlineId(filePath, site.lineNumber, lineCount);
702
+ for (const [siteIndex, site] of inlineSites.entries()) {
703
+ const hash = hashInlineId(filePath, fnName, siteIndex);
594
704
  const exportName = `__sh_${hash}`;
595
- const virtualId = `\0${virtualPrefix}${filePath}:${site.lineNumber}${lineCount > 0 ? `:${lineCount}` : ""}`;
705
+ const idSuffix = `${filePath}:${fnName}:${siteIndex}`;
706
+ const virtualId = `\0${virtualPrefix}${idSuffix}`;
596
707
  const handlerCode = code.slice(site.callStart, site.callEnd);
597
708
  virtualRegistry.set(virtualId, {
598
709
  originalModuleId: moduleId,
@@ -602,7 +713,7 @@ function transformInlineHandlers(fnName, virtualPrefix, s, code, filePath, virtu
602
713
  exportName
603
714
  });
604
715
  s.overwrite(site.callStart, site.callEnd, exportName);
605
- const importId = `${virtualPrefix}${filePath}:${site.lineNumber}${lineCount > 0 ? `:${lineCount}` : ""}`;
716
+ const importId = `${virtualPrefix}${idSuffix}`;
606
717
  importStatements.push(`import { ${exportName} } from "${importId}";`);
607
718
  }
608
719
  if (importStatements.length > 0) {
@@ -634,6 +745,83 @@ var STRICT_CREATE_CONFIGS = [
634
745
 
635
746
  // src/vite/plugins/expose-ids/export-analysis.ts
636
747
  import { parseAst } from "vite";
748
+
749
+ // src/build/route-types/source-scan.ts
750
+ function isLineTerminator(ch) {
751
+ const c = ch.charCodeAt(0);
752
+ return c === 10 || c === 13 || c === 8232 || c === 8233;
753
+ }
754
+ function makeCodeClassifier(code) {
755
+ const n = code.length;
756
+ let i = 0;
757
+ let skipStart = -1;
758
+ let skipEnd = -1;
759
+ return (q) => {
760
+ if (q >= skipStart && q < skipEnd) return false;
761
+ while (i < n && i <= q) {
762
+ const c = code[i];
763
+ const d = i + 1 < n ? code[i + 1] : "";
764
+ let end = -1;
765
+ if (c === "/" && d === "/") {
766
+ let j = i + 2;
767
+ while (j < n && !isLineTerminator(code[j])) j++;
768
+ end = j;
769
+ } else if (c === "/" && d === "*") {
770
+ let j = i + 2;
771
+ while (j < n && !(code[j] === "*" && code[j + 1] === "/")) j++;
772
+ end = Math.min(n, j + 2);
773
+ } else if (c === '"' || c === "'" || c === "`") {
774
+ let j = i + 1;
775
+ while (j < n) {
776
+ if (code[j] === "\\") {
777
+ j += 2;
778
+ continue;
779
+ }
780
+ if (code[j] === c) {
781
+ j++;
782
+ break;
783
+ }
784
+ j++;
785
+ }
786
+ end = j;
787
+ }
788
+ if (end >= 0) {
789
+ if (q < end) {
790
+ skipStart = i;
791
+ skipEnd = end;
792
+ return false;
793
+ }
794
+ i = end;
795
+ } else {
796
+ i++;
797
+ }
798
+ }
799
+ return true;
800
+ };
801
+ }
802
+ function firstCodeMatchIndex(code, pattern) {
803
+ const inCode = makeCodeClassifier(code);
804
+ pattern.lastIndex = 0;
805
+ let m;
806
+ while ((m = pattern.exec(code)) !== null) {
807
+ if (inCode(m.index)) return m.index;
808
+ if (pattern.lastIndex <= m.index) pattern.lastIndex = m.index + 1;
809
+ }
810
+ return -1;
811
+ }
812
+ function codeMatchIndices(code, pattern) {
813
+ const inCode = makeCodeClassifier(code);
814
+ const indices = [];
815
+ pattern.lastIndex = 0;
816
+ let m;
817
+ while ((m = pattern.exec(code)) !== null) {
818
+ if (inCode(m.index)) indices.push(m.index);
819
+ if (pattern.lastIndex <= m.index) pattern.lastIndex = m.index + 1;
820
+ }
821
+ return indices;
822
+ }
823
+
824
+ // src/vite/plugins/expose-ids/export-analysis.ts
637
825
  function isExportOnlyFile(code, bindings) {
638
826
  if (bindings.length === 0) return false;
639
827
  const knownLocals = /* @__PURE__ */ new Set();
@@ -662,12 +850,30 @@ function isExportOnlyFile(code, bindings) {
662
850
  }
663
851
  return true;
664
852
  }
665
- function countCreateCallsForNames(code, fnNames) {
666
- const pattern = new RegExp(
853
+ function createCallPattern(fnNames) {
854
+ return new RegExp(
667
855
  `\\b(?:${fnNames.map(escapeRegExp).join("|")})\\s*(?:<[^>]*>\\s*)?\\(`,
668
856
  "g"
669
857
  );
670
- return (code.match(pattern) || []).length;
858
+ }
859
+ function countCreateCallsForNames(code, fnNames) {
860
+ return codeMatchIndices(code, createCallPattern(fnNames)).length;
861
+ }
862
+ function offsetToLineColumn(code, index) {
863
+ let line = 1;
864
+ let lineStart = 0;
865
+ const end = Math.min(index, code.length);
866
+ for (let i = 0; i < end; i++) {
867
+ if (code[i] === "\n") {
868
+ line++;
869
+ lineStart = i + 1;
870
+ }
871
+ }
872
+ return { line, column: index - lineStart + 1 };
873
+ }
874
+ function findUnsupportedCreateCallSites(code, fnNames, supportedBindings) {
875
+ const supported = new Set(supportedBindings.map((b) => b.callExprStart));
876
+ return codeMatchIndices(code, createCallPattern(fnNames)).filter((index) => !supported.has(index)).map((index) => offsetToLineColumn(code, index));
671
877
  }
672
878
  function getImportedFnNames(code, importedName) {
673
879
  const importPattern = /import\s*\{([^}]*)\}\s*from\s*["']@rangojs\/router(?:\/[^"']*)?["']/g;
@@ -698,6 +904,17 @@ function getCalledIdentifierFromCall(callExpr) {
698
904
  }
699
905
  return null;
700
906
  }
907
+ function unwrapSignatureWrappedCall(init, fnNameSet) {
908
+ if (init?.type !== "CallExpression") return init;
909
+ const directId = getCalledIdentifierFromCall(init);
910
+ if (directId && fnNameSet.has(directId)) return init;
911
+ const firstArg = init.arguments?.[0];
912
+ if (firstArg?.type === "CallExpression") {
913
+ const innerId = getCalledIdentifierFromCall(firstArg);
914
+ if (innerId && fnNameSet.has(innerId)) return firstArg;
915
+ }
916
+ return init;
917
+ }
701
918
  function collectCreateExportBindingsFallback(code, fnNames) {
702
919
  const alternation = fnNames.map(escapeRegExp).join("|");
703
920
  const exportConstPattern = new RegExp(
@@ -757,7 +974,7 @@ function collectCreateExportBindingsFallback(code, fnNames) {
757
974
  function collectCreateExportBindings(code, fnNames, program) {
758
975
  if (!program) {
759
976
  try {
760
- program = parseAst(code, { jsx: true });
977
+ program = parseAst(code, { lang: "tsx" });
761
978
  } catch {
762
979
  return collectCreateExportBindingsFallback(code, fnNames);
763
980
  }
@@ -770,16 +987,16 @@ function collectCreateExportBindings(code, fnNames, program) {
770
987
  return;
771
988
  }
772
989
  for (const decl of varDecl.declarations ?? []) {
773
- const calledIdentifier = getCalledIdentifierFromCall(decl?.init);
774
- if (decl?.id?.type !== "Identifier" || decl?.init?.type !== "CallExpression" || !calledIdentifier || !fnNameSet.has(calledIdentifier)) {
990
+ const callExpr = unwrapSignatureWrappedCall(decl?.init, fnNameSet);
991
+ const calledIdentifier = getCalledIdentifierFromCall(callExpr);
992
+ if (decl?.id?.type !== "Identifier" || callExpr?.type !== "CallExpression" || !calledIdentifier || !fnNameSet.has(calledIdentifier)) {
775
993
  continue;
776
994
  }
777
995
  const localName = decl.id.name;
778
996
  const exportNames = exportMap.get(localName) ?? [];
779
997
  if (exportNames.length === 0) continue;
780
- const callStart = decl.init.start;
781
- const callEnd = decl.init.end;
782
- const calleeEnd = decl.init.callee.end;
998
+ const callEnd = callExpr.end;
999
+ const calleeEnd = callExpr.callee.end;
783
1000
  let openParenPos = -1;
784
1001
  for (let i = calleeEnd; i < callEnd; i++) {
785
1002
  if (code[i] === "(") {
@@ -793,10 +1010,10 @@ function collectCreateExportBindings(code, fnNames, program) {
793
1010
  bindings.push({
794
1011
  localName,
795
1012
  exportNames,
796
- callExprStart: decl.init.start,
1013
+ callExprStart: callExpr.start,
797
1014
  callOpenParenPos: openParenPos,
798
1015
  callCloseParenPos: closeParenPos,
799
- argCount: decl.init.arguments?.length ?? 0,
1016
+ argCount: callExpr.arguments?.length ?? 0,
800
1017
  statementEnd
801
1018
  });
802
1019
  }
@@ -815,9 +1032,20 @@ function collectCreateExportBindings(code, fnNames, program) {
815
1032
  }
816
1033
  return bindings;
817
1034
  }
818
- function buildUnsupportedShapeWarning(filePath, fnName) {
819
- return [
820
- `[rsc-router] Unsupported ${fnName} shape in "${filePath}".`,
1035
+ function buildUnsupportedShapeWarning(filePath, fnName, sites = []) {
1036
+ const lines = [`[rango] Unsupported ${fnName} shape in "${filePath}".`];
1037
+ if (sites.length === 1) {
1038
+ const s = sites[0];
1039
+ lines.push(
1040
+ `The ${fnName}(...) call at ${filePath}:${s.line}:${s.column} has no stable $$id injected \u2014 it is not in a supported shape.`
1041
+ );
1042
+ } else if (sites.length > 1) {
1043
+ lines.push(
1044
+ `These ${fnName}(...) calls have no stable $$id injected \u2014 they are not in a supported shape:`
1045
+ );
1046
+ for (const s of sites) lines.push(` - ${filePath}:${s.line}:${s.column}`);
1047
+ }
1048
+ lines.push(
821
1049
  `Supported shapes are:`,
822
1050
  ` - export const X = ${fnName}(...)`,
823
1051
  ` - const X = ${fnName}(...); export { X }`,
@@ -825,7 +1053,8 @@ function buildUnsupportedShapeWarning(filePath, fnName) {
825
1053
  `Potentially unsupported forms include:`,
826
1054
  ` - export let/var X = ${fnName}(...)`,
827
1055
  ` - inline ${fnName}(...) calls`
828
- ].join("\n");
1056
+ );
1057
+ return lines.join("\n");
829
1058
  }
830
1059
 
831
1060
  // src/vite/plugins/expose-ids/loader-transform.ts
@@ -839,7 +1068,7 @@ function generateClientLoaderStubs(bindings, code, filePath, isBuild) {
839
1068
  const lines = [];
840
1069
  for (const binding of bindings) {
841
1070
  for (const name of binding.exportNames) {
842
- const loaderId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
1071
+ const loaderId = makeStubId(filePath, name, isBuild);
843
1072
  lines.push(
844
1073
  `export const ${name} = { __brand: "loader", $$id: "${loaderId}" };`
845
1074
  );
@@ -851,7 +1080,7 @@ function transformLoaders(bindings, s, filePath, isBuild) {
851
1080
  let hasChanges = false;
852
1081
  for (const binding of bindings) {
853
1082
  const exportName = binding.exportNames[0];
854
- const loaderId = isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
1083
+ const loaderId = makeStubId(filePath, exportName, isBuild);
855
1084
  const paramInjection = binding.argCount === 1 ? `, undefined, "${loaderId}"` : `, "${loaderId}"`;
856
1085
  s.appendLeft(binding.callCloseParenPos, paramInjection);
857
1086
  const propInjection = `
@@ -863,7 +1092,6 @@ ${binding.localName}.$$id = "${loaderId}";`;
863
1092
  }
864
1093
 
865
1094
  // src/vite/plugins/expose-ids/handler-transform.ts
866
- import MagicString2 from "magic-string";
867
1095
  function analyzeCreateHandleArgs(code, startPos, endPos) {
868
1096
  const content = code.slice(startPos, endPos).trim();
869
1097
  return { hasArgs: content.length > 0 };
@@ -877,7 +1105,7 @@ function transformHandles(bindings, s, code, filePath, isBuild) {
877
1105
  binding.callOpenParenPos + 1,
878
1106
  binding.callCloseParenPos
879
1107
  );
880
- const handleId = isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
1108
+ const handleId = makeStubId(filePath, exportName, isBuild);
881
1109
  let paramInjection;
882
1110
  if (!args.hasArgs) {
883
1111
  paramInjection = `undefined, "${handleId}"`;
@@ -896,7 +1124,7 @@ function transformLocationState(bindings, s, filePath, isBuild) {
896
1124
  let hasChanges = false;
897
1125
  for (const binding of bindings) {
898
1126
  const exportName = binding.exportNames[0];
899
- const stateKey = isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
1127
+ const stateKey = makeStubId(filePath, exportName, isBuild);
900
1128
  const propInjection = `
901
1129
  ${binding.localName}.__rsc_ls_key = "__rsc_ls_${stateKey}";`;
902
1130
  s.appendRight(binding.statementEnd, propInjection);
@@ -908,7 +1136,7 @@ function generateWholeFileStubs(cfg, bindings, code, filePath, isBuild) {
908
1136
  if (!isExportOnlyFile(code, bindings)) return null;
909
1137
  const exportNames = bindings.flatMap((b) => b.exportNames);
910
1138
  const stubs = exportNames.map((name) => {
911
- const handlerId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
1139
+ const handlerId = makeStubId(filePath, name, isBuild);
912
1140
  return `export const ${name} = { __brand: "${cfg.brand}", $$id: "${handlerId}" };`;
913
1141
  });
914
1142
  return { code: stubs.join("\n") + "\n", map: null };
@@ -917,7 +1145,7 @@ function stubHandlerExprs(cfg, bindings, s, filePath, isBuild) {
917
1145
  let hasChanges = false;
918
1146
  for (const binding of bindings) {
919
1147
  const exportName = binding.exportNames[0];
920
- const handlerId = isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
1148
+ const handlerId = makeStubId(filePath, exportName, isBuild);
921
1149
  s.overwrite(
922
1150
  binding.callExprStart,
923
1151
  binding.callCloseParenPos + 1,
@@ -931,7 +1159,7 @@ function transformHandlerIds(cfg, bindings, s, filePath, isBuild) {
931
1159
  let hasChanges = false;
932
1160
  for (const binding of bindings) {
933
1161
  const exportName = binding.exportNames[0];
934
- const handlerId = isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
1162
+ const handlerId = makeStubId(filePath, exportName, isBuild);
935
1163
  let paramInjection;
936
1164
  if (binding.argCount === 0) {
937
1165
  paramInjection = `undefined, "${handlerId}"`;
@@ -950,19 +1178,20 @@ ${binding.localName}.$$id = "${handlerId}";`;
950
1178
  }
951
1179
 
952
1180
  // src/vite/plugins/expose-ids/router-transform.ts
953
- import MagicString3 from "magic-string";
1181
+ import MagicString2 from "magic-string";
954
1182
  import path3 from "node:path";
955
1183
  import { createHash } from "node:crypto";
1184
+ var debug2 = createRangoDebugger(NS.transform);
956
1185
  function transformRouter(code, filePath, routerFnNames, absolutePath) {
957
1186
  const pat = new RegExp(
958
1187
  `\\b(?:${routerFnNames.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\s*(?:<[^>]*>)?\\s*\\(`,
959
1188
  "g"
960
1189
  );
961
1190
  let match;
962
- const s = new MagicString3(code);
1191
+ const s = new MagicString2(code);
963
1192
  let changed = false;
964
- const basename3 = path3.basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
965
- const routeNamesImport = `./${basename3}.named-routes.gen.js`;
1193
+ const basename2 = path3.basename(filePath).replace(/\.(tsx?|jsx?)$/, "");
1194
+ const routeNamesImport = `./${basename2}.named-routes.gen.js`;
966
1195
  const routeNamesVar = `__rsc_rn`;
967
1196
  while ((match = pat.exec(code)) !== null) {
968
1197
  const callStart = match.index;
@@ -995,11 +1224,15 @@ function transformRouter(code, filePath, routerFnNames, absolutePath) {
995
1224
  }
996
1225
  function exposeRouterId() {
997
1226
  let projectRoot = "";
1227
+ const counter = createCounter(debug2, "expose-router-id");
998
1228
  return {
999
1229
  name: "@rangojs/router:expose-router-id",
1000
1230
  configResolved(config) {
1001
1231
  projectRoot = config.root;
1002
1232
  },
1233
+ buildEnd() {
1234
+ counter?.flush();
1235
+ },
1003
1236
  transform(code, id) {
1004
1237
  if (!code.includes("createRouter")) return null;
1005
1238
  if (!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
@@ -1008,14 +1241,25 @@ function exposeRouterId() {
1008
1241
  return null;
1009
1242
  }
1010
1243
  if (id.includes("node_modules")) return null;
1011
- const filePath = normalizePath(path3.relative(projectRoot, id));
1012
- const routerFnNames = getImportedFnNames(code, "createRouter");
1013
- return transformRouter(code, filePath, routerFnNames, normalizePath(id));
1244
+ const start = counter ? performance.now() : 0;
1245
+ try {
1246
+ const filePath = normalizePath(path3.relative(projectRoot, id));
1247
+ const routerFnNames = getImportedFnNames(code, "createRouter");
1248
+ return transformRouter(
1249
+ code,
1250
+ filePath,
1251
+ routerFnNames,
1252
+ normalizePath(id)
1253
+ );
1254
+ } finally {
1255
+ counter?.record(id, performance.now() - start);
1256
+ }
1014
1257
  }
1015
1258
  };
1016
1259
  }
1017
1260
 
1018
1261
  // src/vite/plugins/expose-internal-ids.ts
1262
+ var debug3 = createRangoDebugger(NS.transform);
1019
1263
  var VIRTUAL_LOADER_MANIFEST = "virtual:rsc-router/loader-manifest";
1020
1264
  var RESOLVED_VIRTUAL_LOADER_MANIFEST = "\0" + VIRTUAL_LOADER_MANIFEST;
1021
1265
  var VIRTUAL_HANDLER_PREFIX = "virtual:handler-extract:";
@@ -1028,9 +1272,13 @@ function exposeInternalIds(options) {
1028
1272
  const staticHandlerModules = /* @__PURE__ */ new Map();
1029
1273
  const virtualHandlers = /* @__PURE__ */ new Map();
1030
1274
  const unsupportedShapeWarnings = /* @__PURE__ */ new Set();
1275
+ const counter = createCounter(debug3, "expose-internal-ids");
1031
1276
  return {
1032
1277
  name: "@rangojs/router:expose-internal-ids",
1033
1278
  enforce: "post",
1279
+ buildEnd() {
1280
+ counter?.flush();
1281
+ },
1034
1282
  api: {
1035
1283
  prerenderHandlerModules,
1036
1284
  staticHandlerModules
@@ -1144,11 +1392,13 @@ ${lazyImports.join(",\n")}
1144
1392
  // --------------- Unified transform ---------------
1145
1393
  transform(code, id) {
1146
1394
  if (id.includes("/node_modules/")) return;
1147
- const filePath = normalizePath(path4.relative(projectRoot, id));
1148
- const isRscEnv = this.environment?.name === "rsc";
1149
- if (id.includes(".named-routes.gen.") && !isRscEnv && this.environment?.name === "client") {
1150
- this.warn(
1151
- `
1395
+ const __t0 = counter ? performance.now() : 0;
1396
+ try {
1397
+ const filePath = normalizePath(path4.relative(projectRoot, id));
1398
+ const isRscEnv = this.environment?.name === "rsc";
1399
+ if (id.includes(".named-routes.gen.") && !isRscEnv && this.environment?.name === "client") {
1400
+ this.warn(
1401
+ `
1152
1402
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1153
1403
  !! !!
1154
1404
  !! WARNING: NamedRoutes imported in a CLIENT component! !!
@@ -1168,367 +1418,363 @@ ${lazyImports.join(",\n")}
1168
1418
  !! !!
1169
1419
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1170
1420
  `
1171
- );
1172
- }
1173
- if (!code.includes("@rangojs/router")) return;
1174
- const has = detectImports(code);
1175
- const hasLoaderCode = has.loader && code.includes("createLoader");
1176
- const hasHandleCode = has.handle && code.includes("createHandle");
1177
- const hasLocationStateCode = has.locationState && code.includes("createLocationState");
1178
- const hasPrerenderHandlerCode = has.prerenderHandler && code.includes("Prerender");
1179
- const hasStaticHandlerCode = has.staticHandler && code.includes("Static");
1180
- if (!hasLoaderCode && !hasHandleCode && !hasLocationStateCode && !hasPrerenderHandlerCode && !hasStaticHandlerCode) {
1181
- return;
1182
- }
1183
- const _fnNamesCache = /* @__PURE__ */ new Map();
1184
- const _bindingsCache = /* @__PURE__ */ new Map();
1185
- let _cachedAst;
1186
- let _astParseFailed = false;
1187
- let _astCodeRef = code;
1188
- const getFnNames = (canonicalName) => {
1189
- let result = _fnNamesCache.get(canonicalName);
1190
- if (!result) {
1191
- result = getImportedFnNames(code, canonicalName);
1192
- _fnNamesCache.set(canonicalName, result);
1421
+ );
1193
1422
  }
1194
- return result;
1195
- };
1196
- const lazyAst = () => {
1197
- if (code !== _astCodeRef) {
1198
- _cachedAst = void 0;
1199
- _astParseFailed = false;
1200
- _astCodeRef = code;
1423
+ if (!code.includes("@rangojs/router")) return;
1424
+ const has = detectImports(code);
1425
+ const hasLoaderCode = has.loader && code.includes("createLoader");
1426
+ const hasHandleCode = has.handle && code.includes("createHandle");
1427
+ const hasLocationStateCode = has.locationState && code.includes("createLocationState");
1428
+ const hasPrerenderHandlerCode = has.prerenderHandler && code.includes("Prerender");
1429
+ const hasStaticHandlerCode = has.staticHandler && code.includes("Static");
1430
+ if (!hasLoaderCode && !hasHandleCode && !hasLocationStateCode && !hasPrerenderHandlerCode && !hasStaticHandlerCode) {
1431
+ return;
1201
1432
  }
1202
- if (_cachedAst !== void 0 || _astParseFailed) return _cachedAst;
1203
- try {
1204
- _cachedAst = parseAst2(code, { jsx: true });
1205
- } catch {
1206
- _astParseFailed = true;
1433
+ const _fnNamesCache = /* @__PURE__ */ new Map();
1434
+ const _bindingsCache = /* @__PURE__ */ new Map();
1435
+ let _cachedAst;
1436
+ let _astParseFailed = false;
1437
+ let _astCodeRef = code;
1438
+ const getFnNames = (canonicalName) => {
1439
+ let result = _fnNamesCache.get(canonicalName);
1440
+ if (!result) {
1441
+ result = getImportedFnNames(code, canonicalName);
1442
+ _fnNamesCache.set(canonicalName, result);
1443
+ }
1444
+ return result;
1445
+ };
1446
+ const lazyAst = () => {
1447
+ if (code !== _astCodeRef) {
1448
+ _cachedAst = void 0;
1449
+ _astParseFailed = false;
1450
+ _astCodeRef = code;
1451
+ }
1452
+ if (_cachedAst !== void 0 || _astParseFailed) return _cachedAst;
1453
+ try {
1454
+ _cachedAst = parseAst2(code, { lang: "tsx" });
1455
+ } catch {
1456
+ _astParseFailed = true;
1457
+ }
1458
+ return _cachedAst;
1459
+ };
1460
+ const getBindings = (currentCode, fnNames) => {
1461
+ const key = fnNames.join("\0");
1462
+ let result = _bindingsCache.get(key);
1463
+ if (!result) {
1464
+ result = collectCreateExportBindings(
1465
+ currentCode,
1466
+ fnNames,
1467
+ lazyAst()
1468
+ );
1469
+ _bindingsCache.set(key, result);
1470
+ }
1471
+ return result;
1472
+ };
1473
+ for (const cfg of STRICT_CREATE_CONFIGS) {
1474
+ const hasCode = cfg.fnName === "createLoader" ? hasLoaderCode : cfg.fnName === "createHandle" ? hasHandleCode : hasLocationStateCode;
1475
+ if (!hasCode) continue;
1476
+ const fnNames = getFnNames(cfg.fnName);
1477
+ const sites = findUnsupportedCreateCallSites(
1478
+ code,
1479
+ fnNames,
1480
+ getBindings(code, fnNames)
1481
+ );
1482
+ if (sites.length === 0) continue;
1483
+ const warnKey = `${id}::${cfg.fnName}`;
1484
+ if (unsupportedShapeWarnings.has(warnKey)) continue;
1485
+ unsupportedShapeWarnings.add(warnKey);
1486
+ this.warn(buildUnsupportedShapeWarning(filePath, cfg.fnName, sites));
1207
1487
  }
1208
- return _cachedAst;
1209
- };
1210
- const getBindings = (currentCode, fnNames) => {
1211
- const key = fnNames.join("\0");
1212
- let result = _bindingsCache.get(key);
1213
- if (!result) {
1214
- result = collectCreateExportBindings(currentCode, fnNames, lazyAst());
1215
- _bindingsCache.set(key, result);
1488
+ if (hasLoaderCode && isRscEnv) {
1489
+ const fnNames = getFnNames("createLoader");
1490
+ const bindings = getBindings(code, fnNames);
1491
+ for (const binding of bindings) {
1492
+ const exportName = binding.exportNames[0];
1493
+ const hashedId = hashId(filePath, exportName);
1494
+ loaderRegistry.set(hashedId, {
1495
+ filePath,
1496
+ exportName
1497
+ });
1498
+ }
1216
1499
  }
1217
- return result;
1218
- };
1219
- for (const cfg of STRICT_CREATE_CONFIGS) {
1220
- const hasCode = cfg.fnName === "createLoader" ? hasLoaderCode : cfg.fnName === "createHandle" ? hasHandleCode : hasLocationStateCode;
1221
- if (!hasCode) continue;
1222
- const fnNames = getFnNames(cfg.fnName);
1223
- const totalCalls = countCreateCallsForNames(code, fnNames);
1224
- const supportedBindings = getBindings(code, fnNames).length;
1225
- if (totalCalls <= supportedBindings) continue;
1226
- const warnKey = `${id}::${cfg.fnName}`;
1227
- if (unsupportedShapeWarnings.has(warnKey)) continue;
1228
- unsupportedShapeWarnings.add(warnKey);
1229
- this.warn(buildUnsupportedShapeWarning(filePath, cfg.fnName));
1230
- }
1231
- if (hasLoaderCode && isRscEnv) {
1232
- const fnNames = getFnNames("createLoader");
1233
- const bindings = getBindings(code, fnNames);
1234
- for (const binding of bindings) {
1235
- const exportName = binding.exportNames[0];
1236
- const hashedId = hashId(filePath, exportName);
1237
- loaderRegistry.set(hashedId, {
1500
+ if (hasLoaderCode && !isRscEnv) {
1501
+ const fnNames = getFnNames("createLoader");
1502
+ const bindings = getBindings(code, fnNames);
1503
+ const stubResult = generateClientLoaderStubs(
1504
+ bindings,
1505
+ code,
1238
1506
  filePath,
1239
- exportName
1240
- });
1241
- }
1242
- }
1243
- if (hasLoaderCode && !isRscEnv) {
1244
- const fnNames = getFnNames("createLoader");
1245
- const bindings = getBindings(code, fnNames);
1246
- const stubResult = generateClientLoaderStubs(
1247
- bindings,
1248
- code,
1249
- filePath,
1250
- isBuild
1251
- );
1252
- if (stubResult) return stubResult;
1253
- }
1254
- if (hasPrerenderHandlerCode && !isRscEnv) {
1255
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1256
- const bindings = getBindings(code, fnNames);
1257
- const wholeFile = generateWholeFileStubs(
1258
- PRERENDER_CONFIG,
1259
- bindings,
1260
- code,
1261
- filePath,
1262
- isBuild
1263
- );
1264
- if (wholeFile) return wholeFile;
1265
- }
1266
- if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
1267
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1268
- const exportNames = getBindings(code, fnNames).map(
1269
- (b) => b.exportNames[0]
1270
- );
1271
- if (exportNames.length > 0) {
1272
- prerenderHandlerModules.set(id, exportNames);
1507
+ isBuild
1508
+ );
1509
+ if (stubResult) return stubResult;
1273
1510
  }
1274
- }
1275
- let changed = false;
1276
- const handlerConfigs = [
1277
- hasStaticHandlerCode && STATIC_CONFIG,
1278
- hasPrerenderHandlerCode && PRERENDER_CONFIG
1279
- ].filter((c) => !!c).map((cfg) => {
1280
- const fnNames = getFnNames(cfg.fnName);
1281
- return { cfg, fnNames };
1282
- });
1283
- for (const { cfg, fnNames } of handlerConfigs) {
1284
- const totalCalls = countCreateCallsForNames(code, fnNames);
1285
- const supportedBindings = getBindings(code, fnNames).length;
1286
- if (totalCalls > supportedBindings) {
1287
- const iterS = new MagicString4(code);
1288
- const result = transformInlineHandlers(
1289
- cfg.fnName,
1290
- VIRTUAL_HANDLER_PREFIX,
1291
- iterS,
1511
+ if (hasPrerenderHandlerCode && !isRscEnv) {
1512
+ const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1513
+ const bindings = getBindings(code, fnNames);
1514
+ const wholeFile = generateWholeFileStubs(
1515
+ PRERENDER_CONFIG,
1516
+ bindings,
1292
1517
  code,
1293
1518
  filePath,
1294
- virtualHandlers,
1295
- id,
1296
- parseAst2
1519
+ isBuild
1297
1520
  );
1298
- if (result) {
1299
- changed = true;
1300
- code = iterS.toString();
1301
- _bindingsCache.clear();
1302
- }
1521
+ if (wholeFile) return wholeFile;
1303
1522
  }
1304
- }
1305
- if (hasStaticHandlerCode && !isRscEnv) {
1306
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1307
- const bindings = getBindings(code, fnNames);
1308
- const wholeFile = generateWholeFileStubs(
1309
- STATIC_CONFIG,
1310
- bindings,
1311
- code,
1312
- filePath,
1313
- isBuild
1314
- );
1315
- if (wholeFile) return wholeFile;
1316
- }
1317
- if (!isRscEnv && (hasPrerenderHandlerCode || hasStaticHandlerCode)) {
1318
- const prerenderFnNames = hasPrerenderHandlerCode ? getFnNames(PRERENDER_CONFIG.fnName) : [];
1319
- const staticFnNames = hasStaticHandlerCode ? getFnNames(STATIC_CONFIG.fnName) : [];
1320
- const loaderFnNames = hasLoaderCode ? getFnNames("createLoader") : [];
1321
- const handleFnNames = hasHandleCode ? getFnNames("createHandle") : [];
1322
- const lsFnNames = hasLocationStateCode ? getFnNames("createLocationState") : [];
1323
- const allBindings = [];
1324
- for (const fnNames of [
1325
- prerenderFnNames,
1326
- staticFnNames,
1327
- loaderFnNames,
1328
- handleFnNames,
1329
- lsFnNames
1330
- ]) {
1331
- if (fnNames.length > 0) {
1332
- allBindings.push(...getBindings(code, fnNames));
1523
+ let changed = false;
1524
+ const handlerConfigs = [
1525
+ hasStaticHandlerCode && STATIC_CONFIG,
1526
+ hasPrerenderHandlerCode && PRERENDER_CONFIG
1527
+ ].filter((c) => !!c).map((cfg) => {
1528
+ const fnNames = getFnNames(cfg.fnName);
1529
+ return { cfg, fnNames };
1530
+ });
1531
+ for (const { cfg, fnNames } of handlerConfigs) {
1532
+ const totalCalls = countCreateCallsForNames(code, fnNames);
1533
+ const supportedBindings = getBindings(code, fnNames).length;
1534
+ if (totalCalls > supportedBindings) {
1535
+ const iterS = new MagicString3(code);
1536
+ const result = transformInlineHandlers(
1537
+ cfg.fnName,
1538
+ VIRTUAL_HANDLER_PREFIX,
1539
+ iterS,
1540
+ code,
1541
+ filePath,
1542
+ virtualHandlers,
1543
+ id,
1544
+ parseAst2
1545
+ );
1546
+ if (result) {
1547
+ changed = true;
1548
+ code = iterS.toString();
1549
+ _bindingsCache.clear();
1550
+ }
1333
1551
  }
1334
1552
  }
1335
- let canStubWholeFile = allBindings.length > 0 && isExportOnlyFile(code, allBindings);
1336
- if (canStubWholeFile && (handleFnNames.length > 0 || lsFnNames.length > 0)) {
1337
- const exportedLocals = new Set(allBindings.map((b) => b.localName));
1338
- const strippedBindings = [];
1339
- const localDeclPattern = /(?:^|;|\n)\s*(?:const|let|var|function)\s+(\w+)/g;
1340
- let declMatch;
1341
- while ((declMatch = localDeclPattern.exec(code)) !== null) {
1342
- const name = declMatch[1];
1343
- if (!exportedLocals.has(name) && !/^_c\d*$/.test(name)) {
1344
- strippedBindings.push(name);
1553
+ if (hasStaticHandlerCode && !isRscEnv) {
1554
+ const fnNames = getFnNames(STATIC_CONFIG.fnName);
1555
+ const bindings = getBindings(code, fnNames);
1556
+ const wholeFile = generateWholeFileStubs(
1557
+ STATIC_CONFIG,
1558
+ bindings,
1559
+ code,
1560
+ filePath,
1561
+ isBuild
1562
+ );
1563
+ if (wholeFile) return wholeFile;
1564
+ }
1565
+ if (!isRscEnv && (hasPrerenderHandlerCode || hasStaticHandlerCode)) {
1566
+ const prerenderFnNames = hasPrerenderHandlerCode ? getFnNames(PRERENDER_CONFIG.fnName) : [];
1567
+ const staticFnNames = hasStaticHandlerCode ? getFnNames(STATIC_CONFIG.fnName) : [];
1568
+ const loaderFnNames = hasLoaderCode ? getFnNames("createLoader") : [];
1569
+ const handleFnNames = hasHandleCode ? getFnNames("createHandle") : [];
1570
+ const lsFnNames = hasLocationStateCode ? getFnNames("createLocationState") : [];
1571
+ const allBindings = [];
1572
+ for (const fnNames of [
1573
+ prerenderFnNames,
1574
+ staticFnNames,
1575
+ loaderFnNames,
1576
+ handleFnNames,
1577
+ lsFnNames
1578
+ ]) {
1579
+ if (fnNames.length > 0) {
1580
+ allBindings.push(...getBindings(code, fnNames));
1345
1581
  }
1346
1582
  }
1347
- const importPattern = /import\s*\{([^}]*)\}\s*from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1348
- let importMatch;
1349
- while ((importMatch = importPattern.exec(code)) !== null) {
1350
- for (const spec of importMatch[1].split(",")) {
1351
- const m = spec.trim().match(/^[A-Za-z_$][\w$]*(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
1352
- if (m) strippedBindings.push(m[1] || m[0].trim().split(/\s/)[0]);
1583
+ let canStubWholeFile = allBindings.length > 0 && isExportOnlyFile(code, allBindings);
1584
+ if (canStubWholeFile && (handleFnNames.length > 0 || lsFnNames.length > 0)) {
1585
+ const exportedLocals = new Set(allBindings.map((b) => b.localName));
1586
+ const strippedBindings = [];
1587
+ const localDeclPattern = /(?:^|;|\n)\s*(?:const|let|var|function)\s+(\w+)/g;
1588
+ let declMatch;
1589
+ while ((declMatch = localDeclPattern.exec(code)) !== null) {
1590
+ const name = declMatch[1];
1591
+ if (!exportedLocals.has(name) && !/^_c\d*$/.test(name)) {
1592
+ strippedBindings.push(name);
1593
+ }
1594
+ }
1595
+ const importPattern = /import\s*\{([^}]*)\}\s*from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1596
+ let importMatch;
1597
+ while ((importMatch = importPattern.exec(code)) !== null) {
1598
+ for (const spec of importMatch[1].split(",")) {
1599
+ const m = spec.trim().match(/^[A-Za-z_$][\w$]*(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
1600
+ if (m)
1601
+ strippedBindings.push(m[1] || m[0].trim().split(/\s/)[0]);
1602
+ }
1603
+ }
1604
+ const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1605
+ while ((importMatch = defaultImportPattern.exec(code)) !== null) {
1606
+ strippedBindings.push(importMatch[1]);
1607
+ }
1608
+ const nsImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1609
+ while ((importMatch = nsImportPattern.exec(code)) !== null) {
1610
+ strippedBindings.push(importMatch[1]);
1611
+ }
1612
+ if (strippedBindings.length > 0) {
1613
+ const preservedBindings = allBindings.filter((b) => {
1614
+ const fc = code.slice(b.callExprStart, b.callOpenParenPos + 1);
1615
+ return handleFnNames.some((n) => fc.includes(n)) || lsFnNames.some((n) => fc.includes(n));
1616
+ });
1617
+ const strippedRe = new RegExp(
1618
+ `\\b(?:${strippedBindings.join("|")})\\b`
1619
+ );
1620
+ canStubWholeFile = !preservedBindings.some((b) => {
1621
+ const expr = code.slice(
1622
+ b.callExprStart,
1623
+ b.callCloseParenPos + 1
1624
+ );
1625
+ return strippedRe.test(expr);
1626
+ });
1353
1627
  }
1354
1628
  }
1355
- const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1356
- while ((importMatch = defaultImportPattern.exec(code)) !== null) {
1357
- strippedBindings.push(importMatch[1]);
1358
- }
1359
- const nsImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1360
- while ((importMatch = nsImportPattern.exec(code)) !== null) {
1361
- strippedBindings.push(importMatch[1]);
1362
- }
1363
- if (strippedBindings.length > 0) {
1364
- const preservedBindings = allBindings.filter((b) => {
1365
- const fc = code.slice(b.callExprStart, b.callOpenParenPos + 1);
1366
- return handleFnNames.some((n) => fc.includes(n)) || lsFnNames.some((n) => fc.includes(n));
1367
- });
1368
- const strippedRe = new RegExp(
1369
- `\\b(?:${strippedBindings.join("|")})\\b`
1370
- );
1371
- canStubWholeFile = !preservedBindings.some((b) => {
1372
- const expr = code.slice(b.callExprStart, b.callCloseParenPos + 1);
1373
- return strippedRe.test(expr);
1374
- });
1375
- }
1376
- }
1377
- if (canStubWholeFile) {
1378
- const lines = [];
1379
- const neededImports = [];
1380
- if (handleFnNames.length > 0) neededImports.push("createHandle");
1381
- if (lsFnNames.length > 0) neededImports.push("createLocationState");
1382
- if (neededImports.length > 0) {
1383
- lines.push(
1384
- `import { ${neededImports.join(", ")} } from "@rangojs/router";`
1385
- );
1386
- }
1387
- for (const binding of allBindings) {
1388
- const fnCall = code.slice(
1389
- binding.callExprStart,
1390
- binding.callOpenParenPos + 1
1391
- );
1392
- const isHandle = handleFnNames.some((n) => fnCall.includes(n));
1393
- const isLocationState = lsFnNames.some((n) => fnCall.includes(n));
1394
- const primaryName = binding.exportNames[0];
1395
- const stubId = makeStubId(filePath, primaryName, isBuild);
1396
- if (isHandle || isLocationState) {
1397
- const rawArgs = code.slice(binding.callOpenParenPos + 1, binding.callCloseParenPos).replace(/\b_c\d*\s*=\s*/g, "");
1398
- const canonicalName = isHandle ? "createHandle" : "createLocationState";
1399
- const activeFnNames = isHandle ? handleFnNames : lsFnNames;
1400
- let rawCallee = code.slice(
1629
+ if (canStubWholeFile) {
1630
+ const lines = [];
1631
+ const neededImports = [];
1632
+ if (handleFnNames.length > 0) neededImports.push("createHandle");
1633
+ if (lsFnNames.length > 0) neededImports.push("createLocationState");
1634
+ if (neededImports.length > 0) {
1635
+ lines.push(
1636
+ `import { ${neededImports.join(", ")} } from "@rangojs/router";`
1637
+ );
1638
+ }
1639
+ for (const binding of allBindings) {
1640
+ const fnCall = code.slice(
1401
1641
  binding.callExprStart,
1402
- binding.callOpenParenPos
1642
+ binding.callOpenParenPos + 1
1403
1643
  );
1404
- for (const alias of activeFnNames) {
1405
- if (alias !== canonicalName && rawCallee.startsWith(alias)) {
1406
- rawCallee = canonicalName + rawCallee.slice(alias.length);
1407
- break;
1408
- }
1409
- }
1410
- if (isHandle) {
1411
- const idParam = binding.argCount === 0 ? `undefined, "${stubId}"` : `, "${stubId}"`;
1412
- lines.push(
1413
- `export const ${primaryName} = ${rawCallee}(${rawArgs}${idParam});`
1644
+ const isHandle = handleFnNames.some((n) => fnCall.includes(n));
1645
+ const isLocationState = lsFnNames.some((n) => fnCall.includes(n));
1646
+ const primaryName = binding.exportNames[0];
1647
+ const stubId = makeStubId(filePath, primaryName, isBuild);
1648
+ if (isHandle || isLocationState) {
1649
+ const rawArgs = code.slice(
1650
+ binding.callOpenParenPos + 1,
1651
+ binding.callCloseParenPos
1652
+ ).replace(/\b_c\d*\s*=\s*/g, "");
1653
+ const canonicalName = isHandle ? "createHandle" : "createLocationState";
1654
+ const activeFnNames = isHandle ? handleFnNames : lsFnNames;
1655
+ let rawCallee = code.slice(
1656
+ binding.callExprStart,
1657
+ binding.callOpenParenPos
1414
1658
  );
1415
- lines.push(`${primaryName}.$$id = "${stubId}";`);
1659
+ for (const alias of activeFnNames) {
1660
+ if (alias !== canonicalName && rawCallee.startsWith(alias)) {
1661
+ rawCallee = canonicalName + rawCallee.slice(alias.length);
1662
+ break;
1663
+ }
1664
+ }
1665
+ if (isHandle) {
1666
+ const idParam = binding.argCount === 0 ? `undefined, "${stubId}"` : `, "${stubId}"`;
1667
+ lines.push(
1668
+ `export const ${primaryName} = ${rawCallee}(${rawArgs}${idParam});`
1669
+ );
1670
+ lines.push(`${primaryName}.$$id = "${stubId}";`);
1671
+ } else {
1672
+ lines.push(
1673
+ `export const ${primaryName} = ${rawCallee}(${rawArgs});`
1674
+ );
1675
+ lines.push(
1676
+ `${primaryName}.__rsc_ls_key = "__rsc_ls_${stubId}";`
1677
+ );
1678
+ }
1679
+ for (const name of binding.exportNames.slice(1)) {
1680
+ lines.push(`export const ${name} = ${primaryName};`);
1681
+ }
1416
1682
  } else {
1683
+ let brand = "loader";
1684
+ if (prerenderFnNames.some((n) => fnCall.includes(n))) {
1685
+ brand = PRERENDER_CONFIG.brand;
1686
+ } else if (staticFnNames.some((n) => fnCall.includes(n))) {
1687
+ brand = STATIC_CONFIG.brand;
1688
+ }
1417
1689
  lines.push(
1418
- `export const ${primaryName} = ${rawCallee}(${rawArgs});`
1419
- );
1420
- lines.push(
1421
- `${primaryName}.__rsc_ls_key = "__rsc_ls_${stubId}";`
1690
+ `export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
1422
1691
  );
1423
- }
1424
- for (const name of binding.exportNames.slice(1)) {
1425
- lines.push(`export const ${name} = ${primaryName};`);
1426
- }
1427
- } else {
1428
- let brand = "loader";
1429
- if (prerenderFnNames.some((n) => fnCall.includes(n))) {
1430
- brand = PRERENDER_CONFIG.brand;
1431
- } else if (staticFnNames.some((n) => fnCall.includes(n))) {
1432
- brand = STATIC_CONFIG.brand;
1433
- }
1434
- lines.push(
1435
- `export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
1436
- );
1437
- for (const name of binding.exportNames.slice(1)) {
1438
- lines.push(`export const ${name} = ${primaryName};`);
1692
+ for (const name of binding.exportNames.slice(1)) {
1693
+ lines.push(`export const ${name} = ${primaryName};`);
1694
+ }
1439
1695
  }
1440
1696
  }
1697
+ return { code: lines.join("\n") + "\n", map: null };
1441
1698
  }
1442
- return { code: lines.join("\n") + "\n", map: null };
1443
1699
  }
1444
- }
1445
- if (hasStaticHandlerCode && isRscEnv && isBuild) {
1446
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1447
- const exportNames = getBindings(code, fnNames).map(
1448
- (b) => b.exportNames[0]
1449
- );
1450
- if (exportNames.length > 0) {
1451
- staticHandlerModules.set(id, exportNames);
1700
+ if (isRscEnv && isBuild) {
1701
+ const trackTypes = [
1702
+ [
1703
+ hasPrerenderHandlerCode,
1704
+ PRERENDER_CONFIG,
1705
+ prerenderHandlerModules
1706
+ ],
1707
+ [hasStaticHandlerCode, STATIC_CONFIG, staticHandlerModules]
1708
+ ];
1709
+ for (const [has2, cfg, trackMap] of trackTypes) {
1710
+ if (!has2) continue;
1711
+ const exportNames = getBindings(code, getFnNames(cfg.fnName)).map(
1712
+ (b) => b.exportNames[0]
1713
+ );
1714
+ if (exportNames.length > 0) trackMap.set(id, exportNames);
1715
+ }
1452
1716
  }
1453
- }
1454
- const s = new MagicString4(code);
1455
- if (hasLoaderCode) {
1456
- const fnNames = getFnNames("createLoader");
1457
- changed = transformLoaders(getBindings(code, fnNames), s, filePath, isBuild) || changed;
1458
- }
1459
- if (hasHandleCode) {
1460
- const fnNames = getFnNames("createHandle");
1461
- changed = transformHandles(
1462
- getBindings(code, fnNames),
1463
- s,
1464
- code,
1465
- filePath,
1466
- isBuild
1467
- ) || changed;
1468
- }
1469
- if (hasLocationStateCode) {
1470
- const fnNames = getFnNames("createLocationState");
1471
- changed = transformLocationState(
1472
- getBindings(code, fnNames),
1473
- s,
1474
- filePath,
1475
- isBuild
1476
- ) || changed;
1477
- }
1478
- if (hasPrerenderHandlerCode) {
1479
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1480
- const bindings = getBindings(code, fnNames);
1481
- if (isRscEnv) {
1482
- changed = transformHandlerIds(
1483
- PRERENDER_CONFIG,
1484
- bindings,
1717
+ const s = new MagicString3(code);
1718
+ if (hasLoaderCode) {
1719
+ const fnNames = getFnNames("createLoader");
1720
+ changed = transformLoaders(
1721
+ getBindings(code, fnNames),
1485
1722
  s,
1486
1723
  filePath,
1487
1724
  isBuild
1488
1725
  ) || changed;
1489
- } else {
1490
- changed = stubHandlerExprs(
1491
- PRERENDER_CONFIG,
1492
- bindings,
1726
+ }
1727
+ if (hasHandleCode) {
1728
+ const fnNames = getFnNames("createHandle");
1729
+ changed = transformHandles(
1730
+ getBindings(code, fnNames),
1493
1731
  s,
1732
+ code,
1494
1733
  filePath,
1495
1734
  isBuild
1496
1735
  ) || changed;
1497
1736
  }
1498
- }
1499
- if (hasStaticHandlerCode) {
1500
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1501
- const bindings = getBindings(code, fnNames);
1502
- if (isRscEnv) {
1503
- changed = transformHandlerIds(
1504
- STATIC_CONFIG,
1505
- bindings,
1737
+ if (hasLocationStateCode) {
1738
+ const fnNames = getFnNames("createLocationState");
1739
+ changed = transformLocationState(
1740
+ getBindings(code, fnNames),
1506
1741
  s,
1507
1742
  filePath,
1508
1743
  isBuild
1509
1744
  ) || changed;
1510
- } else {
1511
- changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
1512
1745
  }
1746
+ const finalHandlerConfigs = [
1747
+ hasPrerenderHandlerCode && PRERENDER_CONFIG,
1748
+ hasStaticHandlerCode && STATIC_CONFIG
1749
+ ].filter((c) => !!c);
1750
+ for (const cfg of finalHandlerConfigs) {
1751
+ const bindings = getBindings(code, getFnNames(cfg.fnName));
1752
+ changed = (isRscEnv ? transformHandlerIds(cfg, bindings, s, filePath, isBuild) : stubHandlerExprs(cfg, bindings, s, filePath, isBuild)) || changed;
1753
+ }
1754
+ if (!changed) return;
1755
+ return {
1756
+ code: s.toString(),
1757
+ map: s.generateMap({ source: id, includeContent: true })
1758
+ };
1759
+ } finally {
1760
+ counter?.record(id, performance.now() - __t0);
1513
1761
  }
1514
- if (!changed) return;
1515
- return {
1516
- code: s.toString(),
1517
- map: s.generateMap({ source: id, includeContent: true })
1518
- };
1519
1762
  }
1520
1763
  };
1521
1764
  }
1522
1765
 
1523
1766
  // src/vite/plugins/use-cache-transform.ts
1524
1767
  import path5 from "node:path";
1525
- import MagicString5 from "magic-string";
1768
+ import MagicString4 from "magic-string";
1769
+ var debug4 = createRangoDebugger(NS.transform);
1526
1770
  var CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
1527
1771
  var LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
1772
+ var USE_CACHE_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
1528
1773
  function useCacheTransform() {
1529
1774
  let projectRoot = "";
1530
1775
  let isBuild = false;
1531
1776
  let rscTransforms = null;
1777
+ const counter = createCounter(debug4, "use-cache");
1532
1778
  return {
1533
1779
  name: "@rangojs/router:use-cache",
1534
1780
  enforce: "post",
@@ -1536,53 +1782,61 @@ function useCacheTransform() {
1536
1782
  projectRoot = config.root;
1537
1783
  isBuild = config.command === "build";
1538
1784
  },
1785
+ buildEnd() {
1786
+ counter?.flush();
1787
+ },
1539
1788
  async transform(code, id) {
1540
1789
  if (this.environment?.name !== "rsc") return;
1541
1790
  if (!code.includes("use cache")) return;
1542
1791
  if (id.includes("/node_modules/") || id.startsWith("\0")) return;
1543
1792
  if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
1544
- if (!rscTransforms) {
1793
+ const start = counter ? performance.now() : 0;
1794
+ try {
1795
+ if (!rscTransforms) {
1796
+ try {
1797
+ rscTransforms = await import("@vitejs/plugin-rsc/transforms");
1798
+ } catch {
1799
+ return;
1800
+ }
1801
+ }
1802
+ const {
1803
+ hasDirective,
1804
+ transformWrapExport,
1805
+ transformHoistInlineDirective
1806
+ } = rscTransforms;
1807
+ let ast;
1545
1808
  try {
1546
- rscTransforms = await import("@vitejs/plugin-rsc/transforms");
1809
+ const { parseAst: parseAst4 } = await import("vite");
1810
+ ast = parseAst4(code, { lang: "tsx" });
1547
1811
  } catch {
1548
1812
  return;
1549
1813
  }
1550
- }
1551
- const {
1552
- hasDirective,
1553
- transformWrapExport,
1554
- transformHoistInlineDirective
1555
- } = rscTransforms;
1556
- let ast;
1557
- try {
1558
- const { parseAst: parseAst4 } = await import("vite");
1559
- ast = parseAst4(code);
1560
- } catch {
1561
- return;
1562
- }
1563
- const filePath = normalizePath(path5.relative(projectRoot, id));
1564
- const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
1565
- if (hasDirective(ast.body, "use cache")) {
1566
- return transformFileLevelUseCache(
1814
+ const filePath = normalizePath(path5.relative(projectRoot, id));
1815
+ const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
1816
+ if (hasDirective(ast.body, "use cache")) {
1817
+ return transformFileLevelUseCache(
1818
+ code,
1819
+ ast,
1820
+ filePath,
1821
+ id,
1822
+ isBuild,
1823
+ isLayoutOrTemplate,
1824
+ transformWrapExport
1825
+ );
1826
+ }
1827
+ const functionResult = transformFunctionLevelUseCache(
1567
1828
  code,
1568
1829
  ast,
1569
1830
  filePath,
1570
1831
  id,
1571
1832
  isBuild,
1572
- isLayoutOrTemplate,
1573
- transformWrapExport
1833
+ transformHoistInlineDirective
1574
1834
  );
1835
+ warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1836
+ if (functionResult) return functionResult;
1837
+ } finally {
1838
+ counter?.record(id, performance.now() - start);
1575
1839
  }
1576
- const functionResult = transformFunctionLevelUseCache(
1577
- code,
1578
- ast,
1579
- filePath,
1580
- id,
1581
- isBuild,
1582
- transformHoistInlineDirective
1583
- );
1584
- warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1585
- if (functionResult) return functionResult;
1586
1840
  }
1587
1841
  };
1588
1842
  }
@@ -1609,7 +1863,7 @@ function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLa
1609
1863
  );
1610
1864
  }
1611
1865
  if (exportNames.length === 0) {
1612
- const s = new MagicString5(code);
1866
+ const s = new MagicString4(code);
1613
1867
  const directive2 = findFileLevelDirective(ast);
1614
1868
  if (directive2) {
1615
1869
  s.overwrite(
@@ -1644,7 +1898,7 @@ function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLa
1644
1898
  function transformFunctionLevelUseCache(code, ast, filePath, sourceId, isBuild, transformHoistInlineDirective) {
1645
1899
  try {
1646
1900
  const { output, names } = transformHoistInlineDirective(code, ast, {
1647
- directive: /^use cache(:\s*[\w-]+)?$/,
1901
+ directive: USE_CACHE_DIRECTIVE_RE,
1648
1902
  runtime: (value, name, meta) => {
1649
1903
  const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
1650
1904
  const profileMatch = meta.directiveMatch[1];
@@ -1674,14 +1928,13 @@ function findFileLevelDirective(ast) {
1674
1928
  }
1675
1929
  return null;
1676
1930
  }
1677
- var VALID_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
1678
1931
  var NEAR_MISS_RE = /^use cache:\s*.+$/;
1679
1932
  function warnOnNearMissDirectives(ast, fileId, warn) {
1680
1933
  const visit = (node) => {
1681
1934
  if (!node || typeof node !== "object") return;
1682
1935
  if (node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
1683
1936
  const value = node.expression.value;
1684
- if (value.startsWith("use cache") && NEAR_MISS_RE.test(value) && !VALID_DIRECTIVE_RE.test(value)) {
1937
+ if (value.startsWith("use cache") && NEAR_MISS_RE.test(value) && !USE_CACHE_DIRECTIVE_RE.test(value)) {
1685
1938
  const profilePart = value.slice("use cache:".length).trim();
1686
1939
  warn(
1687
1940
  `[rango:use-cache] "${value}" in ${fileId} has an invalid profile name "${profilePart}". Profile names must match [a-zA-Z0-9_-]+. This directive will be ignored.`
@@ -1705,6 +1958,7 @@ function warnOnNearMissDirectives(ast, fileId, warn) {
1705
1958
  }
1706
1959
 
1707
1960
  // src/vite/plugins/client-ref-dedup.ts
1961
+ var debug5 = createRangoDebugger(NS.transform);
1708
1962
  var CLIENT_IN_SERVER_PROXY_PREFIX = "virtual:vite-rsc/client-in-server-package-proxy/";
1709
1963
  function extractPackageName(absolutePath) {
1710
1964
  const marker = "/node_modules/";
@@ -1721,6 +1975,7 @@ function extractPackageName(absolutePath) {
1721
1975
  }
1722
1976
  function clientRefDedup() {
1723
1977
  let clientExclude = [];
1978
+ const dedupedPackages = /* @__PURE__ */ new Set();
1724
1979
  return {
1725
1980
  name: "@rangojs/router:client-ref-dedup",
1726
1981
  enforce: "pre",
@@ -1729,6 +1984,15 @@ function clientRefDedup() {
1729
1984
  const clientEnv = config.environments?.["client"];
1730
1985
  clientExclude = clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
1731
1986
  },
1987
+ buildEnd() {
1988
+ if (debug5 && dedupedPackages.size > 0) {
1989
+ debug5(
1990
+ "client-ref-dedup: redirected %d package(s) (%s)",
1991
+ dedupedPackages.size,
1992
+ [...dedupedPackages].join(",")
1993
+ );
1994
+ }
1995
+ },
1732
1996
  resolveId(source, importer, options) {
1733
1997
  if (this.environment?.name !== "client") return;
1734
1998
  if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
@@ -1737,6 +2001,7 @@ function clientRefDedup() {
1737
2001
  const packageName = extractPackageName(source);
1738
2002
  if (!packageName) return;
1739
2003
  if (clientExclude.includes(packageName)) return;
2004
+ if (debug5) dedupedPackages.add(packageName);
1740
2005
  return `\0rango:dedup/${packageName}`;
1741
2006
  },
1742
2007
  load(id) {
@@ -1763,7 +2028,7 @@ import {
1763
2028
  import { createElement, StrictMode } from "react";
1764
2029
  import { hydrateRoot } from "react-dom/client";
1765
2030
  import { rscStream } from "@rangojs/router/internal/deps/html-stream-client";
1766
- import { initBrowserApp, RSCRouter } from "@rangojs/router/browser";
2031
+ import { initBrowserApp, Rango } from "@rangojs/router/browser";
1767
2032
 
1768
2033
  async function initializeApp() {
1769
2034
  const deps = {
@@ -1778,7 +2043,7 @@ async function initializeApp() {
1778
2043
 
1779
2044
  hydrateRoot(
1780
2045
  document,
1781
- createElement(StrictMode, null, createElement(RSCRouter))
2046
+ createElement(StrictMode, null, createElement(Rango))
1782
2047
  );
1783
2048
  }
1784
2049
 
@@ -1859,12 +2124,13 @@ function getVirtualVersionContent(version) {
1859
2124
 
1860
2125
  // src/vite/utils/package-resolution.ts
1861
2126
  import { existsSync } from "node:fs";
2127
+ import { createRequire } from "node:module";
1862
2128
  import { resolve } from "node:path";
1863
2129
 
1864
2130
  // package.json
1865
2131
  var package_default = {
1866
2132
  name: "@rangojs/router",
1867
- version: "0.0.0-experimental.d7eeaa75",
2133
+ version: "0.0.0-experimental.d98a8e9d",
1868
2134
  description: "Django-inspired RSC router with composable URL patterns",
1869
2135
  keywords: [
1870
2136
  "react",
@@ -1990,6 +2256,31 @@ var package_default = {
1990
2256
  "./host/testing": {
1991
2257
  types: "./src/host/testing.ts",
1992
2258
  default: "./src/host/testing.ts"
2259
+ },
2260
+ "./testing": {
2261
+ types: "./src/testing/index.ts",
2262
+ default: "./src/testing/index.ts"
2263
+ },
2264
+ "./testing/vitest": {
2265
+ types: "./src/testing/vitest.ts",
2266
+ default: "./dist/testing/vitest.js"
2267
+ },
2268
+ "./testing/dom": {
2269
+ types: "./src/testing/dom.entry.ts",
2270
+ default: "./src/testing/dom.entry.ts"
2271
+ },
2272
+ "./testing/e2e": {
2273
+ types: "./src/testing/e2e/index.ts",
2274
+ default: "./src/testing/e2e/index.ts"
2275
+ },
2276
+ "./testing/flight": {
2277
+ types: "./src/testing/flight.entry.ts",
2278
+ "react-server": "./src/testing/flight.entry.ts",
2279
+ default: "./src/testing/flight.entry.ts"
2280
+ },
2281
+ "./testing/flight-matchers": {
2282
+ types: "./src/testing/flight-matchers.ts",
2283
+ default: "./src/testing/flight-matchers.ts"
1993
2284
  }
1994
2285
  },
1995
2286
  publishConfig: {
@@ -1997,50 +2288,72 @@ var package_default = {
1997
2288
  tag: "experimental"
1998
2289
  },
1999
2290
  scripts: {
2000
- build: "pnpm dlx esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && pnpm dlx esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
2291
+ build: "pnpm dlx esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm dlx esbuild src/testing/vitest.ts --bundle --format=esm --outfile=dist/testing/vitest.js --platform=node --packages=external && pnpm dlx esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
2001
2292
  prepublishOnly: "pnpm build",
2002
- typecheck: "tsc --noEmit",
2293
+ typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
2003
2294
  test: "playwright test",
2004
2295
  "test:ui": "playwright test --ui",
2296
+ "test:hmr-local": "playwright test --project=dev-warmup --project=hmr-routes --project=hmr-basename --project=hmr-prerender --no-deps --workers=1",
2005
2297
  "test:unit": "vitest run",
2006
- "test:unit:watch": "vitest"
2298
+ "test:unit:watch": "vitest",
2299
+ "test:unit:rsc": "vitest run --config vitest.rsc.config.ts"
2007
2300
  },
2008
2301
  dependencies: {
2009
- "@vitejs/plugin-rsc": "^0.5.19",
2302
+ "@types/debug": "^4.1.12",
2303
+ "@vitejs/plugin-rsc": "^0.5.26",
2304
+ debug: "^4.4.1",
2010
2305
  "magic-string": "^0.30.17",
2011
2306
  picomatch: "^4.0.3",
2012
- "rsc-html-stream": "^0.0.7"
2307
+ "rsc-html-stream": "^0.0.7",
2308
+ tinyexec: "^0.3.2"
2013
2309
  },
2014
2310
  devDependencies: {
2015
2311
  "@playwright/test": "^1.49.1",
2312
+ "@shared/e2e": "workspace:*",
2313
+ "@testing-library/dom": "^10.4.1",
2314
+ "@testing-library/react": "^16.3.2",
2016
2315
  "@types/node": "^24.10.1",
2017
2316
  "@types/react": "catalog:",
2018
2317
  "@types/react-dom": "catalog:",
2019
2318
  esbuild: "^0.27.0",
2319
+ "happy-dom": "^20.10.1",
2020
2320
  jiti: "^2.6.1",
2021
2321
  react: "catalog:",
2022
2322
  "react-dom": "catalog:",
2023
- tinyexec: "^0.3.2",
2024
2323
  typescript: "^5.3.0",
2025
2324
  vitest: "^4.0.0"
2026
2325
  },
2027
2326
  peerDependencies: {
2028
- "@cloudflare/vite-plugin": "^1.25.0",
2029
- "@vitejs/plugin-rsc": "^0.5.14",
2030
- react: "^18.0.0 || ^19.0.0",
2031
- vite: "^7.3.0"
2327
+ "@cloudflare/vite-plugin": "^1.38.0",
2328
+ "@playwright/test": "^1.49.1",
2329
+ "@testing-library/react": ">=16",
2330
+ "@vitejs/plugin-rsc": "^0.5.26",
2331
+ react: ">=19.2.6 <20",
2332
+ "react-dom": ">=19.2.6 <20",
2333
+ vite: "^8.0.0",
2334
+ vitest: ">=3"
2032
2335
  },
2033
2336
  peerDependenciesMeta: {
2034
2337
  "@cloudflare/vite-plugin": {
2035
2338
  optional: true
2036
2339
  },
2037
- vite: {
2340
+ "@playwright/test": {
2341
+ optional: true
2342
+ },
2343
+ "@testing-library/react": {
2344
+ optional: true
2345
+ },
2346
+ vite: {
2347
+ optional: true
2348
+ },
2349
+ vitest: {
2038
2350
  optional: true
2039
2351
  }
2040
2352
  }
2041
2353
  };
2042
2354
 
2043
2355
  // src/vite/utils/package-resolution.ts
2356
+ var require2 = createRequire(import.meta.url);
2044
2357
  var VIRTUAL_PACKAGE_NAME = "@rangojs/router";
2045
2358
  function getPublishedPackageName() {
2046
2359
  return package_default.name;
@@ -2081,6 +2394,20 @@ function getPackageAliases() {
2081
2394
  }
2082
2395
  return aliases;
2083
2396
  }
2397
+ function getVendorAliases() {
2398
+ const specs = [
2399
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
2400
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
2401
+ ];
2402
+ const aliases = {};
2403
+ for (const spec of specs) {
2404
+ try {
2405
+ aliases[spec] = require2.resolve(spec);
2406
+ } catch {
2407
+ }
2408
+ }
2409
+ return aliases;
2410
+ }
2084
2411
 
2085
2412
  // src/build/route-types/param-extraction.ts
2086
2413
  function extractParamsFromPattern(pattern) {
@@ -2206,7 +2533,7 @@ ${objectBody}
2206
2533
  } as const;
2207
2534
 
2208
2535
  declare global {
2209
- namespace RSCRouter {
2536
+ namespace Rango {
2210
2537
  interface GeneratedRouteMap extends Readonly<typeof NamedRoutes> {}
2211
2538
  }
2212
2539
  }
@@ -2441,7 +2768,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
2441
2768
  const realPath = resolve2(filePath);
2442
2769
  const key = variableName ? `${realPath}:${variableName}` : realPath;
2443
2770
  if (visited.has(key)) {
2444
- console.warn(`[rsc-router] Circular include detected, skipping: ${key}`);
2771
+ console.warn(`[rango] Circular include detected, skipping: ${key}`);
2445
2772
  return { routes: {}, searchSchemas: {} };
2446
2773
  }
2447
2774
  visited.add(key);
@@ -2502,6 +2829,7 @@ function countPublicRouteEntries(source) {
2502
2829
  return count;
2503
2830
  }
2504
2831
  var ROUTER_CALL_PATTERN = /\bcreateRouter\s*[<(]/;
2832
+ var ROUTER_CALL_PATTERN_G = /\bcreateRouter\s*[<(]/g;
2505
2833
  function isRoutableSourceFile(name) {
2506
2834
  return (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js") || name.endsWith(".jsx")) && !name.includes(".gen.") && !name.includes(".test.") && !name.includes(".spec.");
2507
2835
  }
@@ -2511,7 +2839,7 @@ function findRouterFilesRecursive(dir, filter, results) {
2511
2839
  entries = readdirSync(dir, { withFileTypes: true });
2512
2840
  } catch (err) {
2513
2841
  console.warn(
2514
- `[rsc-router] Failed to scan directory ${dir}: ${err.message}`
2842
+ `[rango] Failed to scan directory ${dir}: ${err.message}`
2515
2843
  );
2516
2844
  return;
2517
2845
  }
@@ -2529,7 +2857,7 @@ function findRouterFilesRecursive(dir, filter, results) {
2529
2857
  if (filter && !filter(fullPath)) continue;
2530
2858
  try {
2531
2859
  const source = readFileSync2(fullPath, "utf-8");
2532
- if (ROUTER_CALL_PATTERN.test(source)) {
2860
+ if (ROUTER_CALL_PATTERN.test(source) && firstCodeMatchIndex(source, ROUTER_CALL_PATTERN_G) >= 0) {
2533
2861
  routerFilesInDir.push(fullPath);
2534
2862
  }
2535
2863
  } catch {
@@ -2567,7 +2895,7 @@ function findNestedRouterConflict(routerFiles) {
2567
2895
  }
2568
2896
  return null;
2569
2897
  }
2570
- function formatNestedRouterConflictError(conflict, prefix = "[rsc-router]") {
2898
+ function formatNestedRouterConflictError(conflict, prefix = "[rango]") {
2571
2899
  return `${prefix} Nested router roots are not supported.
2572
2900
  Router root: ${conflict.ancestor}
2573
2901
  Nested router: ${conflict.nested}
@@ -2663,19 +2991,38 @@ function extractBasenameFromRouter(code) {
2663
2991
  visit(sourceFile);
2664
2992
  return result;
2665
2993
  }
2666
- function applyBasenameToRoutes(result, basename3) {
2994
+ function applyBasenameToRoutes(result, basename2) {
2667
2995
  const prefixed = {};
2668
2996
  for (const [name, pattern] of Object.entries(result.routes)) {
2669
2997
  if (pattern === "/") {
2670
- prefixed[name] = basename3;
2671
- } else if (basename3.endsWith("/") && pattern.startsWith("/")) {
2672
- prefixed[name] = basename3 + pattern.slice(1);
2998
+ prefixed[name] = basename2;
2999
+ } else if (basename2.endsWith("/") && pattern.startsWith("/")) {
3000
+ prefixed[name] = basename2 + pattern.slice(1);
2673
3001
  } else {
2674
- prefixed[name] = basename3 + pattern;
3002
+ prefixed[name] = basename2 + pattern;
2675
3003
  }
2676
3004
  }
2677
3005
  return { routes: prefixed, searchSchemas: result.searchSchemas };
2678
3006
  }
3007
+ function genFileTsPath(sourceFile) {
3008
+ const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
3009
+ return join(dirname2(sourceFile), `${base}.named-routes.gen.ts`);
3010
+ }
3011
+ function resolveSearchSchemas(publicRouteNames, runtimeSchemas, sourceFile) {
3012
+ if (runtimeSchemas && Object.keys(runtimeSchemas).length > 0) {
3013
+ return runtimeSchemas;
3014
+ }
3015
+ const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
3016
+ if (Object.keys(staticParsed.searchSchemas).length === 0) {
3017
+ return runtimeSchemas;
3018
+ }
3019
+ const filtered = {};
3020
+ for (const name of publicRouteNames) {
3021
+ const schema = staticParsed.searchSchemas[name];
3022
+ if (schema) filtered[name] = schema;
3023
+ }
3024
+ return Object.keys(filtered).length > 0 ? filtered : runtimeSchemas;
3025
+ }
2679
3026
  function buildCombinedRouteMapForRouterFile(routerFilePath) {
2680
3027
  let routerSource;
2681
3028
  try {
@@ -2688,7 +3035,7 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
2688
3035
  return { routes: {}, searchSchemas: {} };
2689
3036
  }
2690
3037
  const rawBasename = extractBasenameFromRouter(routerSource);
2691
- const basename3 = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
3038
+ const basename2 = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
2692
3039
  let result;
2693
3040
  if (extraction.kind === "inline") {
2694
3041
  result = buildCombinedRouteMapWithSearch(
@@ -2713,8 +3060,8 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
2713
3060
  result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
2714
3061
  }
2715
3062
  }
2716
- if (basename3) {
2717
- result = applyBasenameToRoutes(result, basename3);
3063
+ if (basename2) {
3064
+ result = applyBasenameToRoutes(result, basename2);
2718
3065
  }
2719
3066
  return result;
2720
3067
  }
@@ -2729,7 +3076,7 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
2729
3076
  if (existsSync3(oldCombinedPath)) {
2730
3077
  unlinkSync(oldCombinedPath);
2731
3078
  console.log(
2732
- `[rsc-router] Removed stale combined route types: ${oldCombinedPath}`
3079
+ `[rango] Removed stale combined route types: ${oldCombinedPath}`
2733
3080
  );
2734
3081
  }
2735
3082
  } catch {
@@ -2751,18 +3098,12 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
2751
3098
  }
2752
3099
  if (!extractUrlsFromRouter(routerSource)) continue;
2753
3100
  }
2754
- const routerBasename = pathBasename(routerFilePath).replace(
2755
- /\.(tsx?|jsx?)$/,
2756
- ""
2757
- );
2758
- const outPath = join(
2759
- dirname2(routerFilePath),
2760
- `${routerBasename}.named-routes.gen.ts`
2761
- );
3101
+ const outPath = genFileTsPath(routerFilePath);
2762
3102
  const existing = existsSync3(outPath) ? readFileSync2(outPath, "utf-8") : null;
2763
3103
  if (Object.keys(result.routes).length === 0) {
2764
3104
  if (!existing) {
2765
3105
  const emptySource = generateRouteTypesSource({});
3106
+ opts?.onWrite?.(outPath, emptySource);
2766
3107
  writeFileSync(outPath, emptySource);
2767
3108
  }
2768
3109
  continue;
@@ -2782,9 +3123,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
2782
3123
  continue;
2783
3124
  }
2784
3125
  }
3126
+ opts?.onWrite?.(outPath, source);
2785
3127
  writeFileSync(outPath, source);
2786
3128
  console.log(
2787
- `[rsc-router] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
3129
+ `[rango] Generated route types (${Object.keys(result.routes).length} routes) -> ${outPath}`
2788
3130
  );
2789
3131
  }
2790
3132
  }
@@ -2801,7 +3143,7 @@ function normalizeModuleId(id) {
2801
3143
  function getClientModuleSignature(source) {
2802
3144
  let program;
2803
3145
  try {
2804
- program = parseAst3(source, { jsx: true });
3146
+ program = parseAst3(source, { lang: "tsx" });
2805
3147
  } catch {
2806
3148
  return void 0;
2807
3149
  }
@@ -2884,11 +3226,12 @@ function createVersionPlugin() {
2884
3226
  let currentVersion = buildVersion;
2885
3227
  let isDev = false;
2886
3228
  let server = null;
3229
+ let resolvedCacheDir;
2887
3230
  const clientModuleSignatures = /* @__PURE__ */ new Map();
2888
3231
  let versionCounter = 0;
2889
3232
  const bumpVersion = (reason) => {
2890
3233
  currentVersion = Date.now().toString(16) + String(++versionCounter);
2891
- console.log(`[rsc-router] ${reason}, version updated: ${currentVersion}`);
3234
+ console.log(`[rango] ${reason}, version updated: ${currentVersion}`);
2892
3235
  const rscEnv = server?.environments?.rsc;
2893
3236
  const versionMod = rscEnv?.moduleGraph?.getModuleById(
2894
3237
  "\0" + VIRTUAL_IDS.version
@@ -2902,6 +3245,7 @@ function createVersionPlugin() {
2902
3245
  enforce: "pre",
2903
3246
  configResolved(config) {
2904
3247
  isDev = config.command === "serve";
3248
+ resolvedCacheDir = config.cacheDir ? String(config.cacheDir).replace(/\\/g, "/") : void 0;
2905
3249
  },
2906
3250
  configureServer(devServer) {
2907
3251
  server = devServer;
@@ -2943,6 +3287,7 @@ function createVersionPlugin() {
2943
3287
  if (!isDev) return;
2944
3288
  const isRscModule = this.environment?.name === "rsc";
2945
3289
  if (!isRscModule) return;
3290
+ if (isViteDepCachePath(ctx.file, resolvedCacheDir)) return;
2946
3291
  if (ctx.modules.length === 1 && ctx.modules[0].id === "\0" + VIRTUAL_IDS.version) {
2947
3292
  return;
2948
3293
  }
@@ -2972,12 +3317,24 @@ function createVersionPlugin() {
2972
3317
  }
2973
3318
  };
2974
3319
  }
3320
+ function isViteDepCachePath(filePath, cacheDir) {
3321
+ if (!filePath) return false;
3322
+ const normalized = filePath.replace(/\\/g, "/");
3323
+ if (cacheDir) {
3324
+ const normalizedCacheDir = cacheDir.replace(/\\/g, "/").replace(/\/+$/, "");
3325
+ if (normalized === normalizedCacheDir || normalized.startsWith(normalizedCacheDir + "/")) {
3326
+ return true;
3327
+ }
3328
+ }
3329
+ return /\/node_modules\/\.vite[^/]*\//.test(normalized) || normalized.includes("/.vite-isolated/");
3330
+ }
2975
3331
 
2976
3332
  // src/vite/utils/shared-utils.ts
2977
3333
  import * as Vite from "vite";
2978
3334
 
2979
3335
  // src/vite/plugins/performance-tracks.ts
2980
3336
  import { readFile } from "node:fs/promises";
3337
+ var debug6 = createRangoDebugger(NS.transform);
2981
3338
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
2982
3339
  function buildPatchReplacement(match, debugInfoVar) {
2983
3340
  return `${match}
@@ -2999,62 +3356,65 @@ function patchRsdwClientDebugInfoRecovery(code) {
2999
3356
  };
3000
3357
  }
3001
3358
  function performanceTracksOptimizeDepsPlugin() {
3359
+ const RSDW_CLIENT_RE = /react-server-dom-webpack-client\.browser\.(development|production)\.js$/;
3002
3360
  return {
3003
3361
  name: "@rangojs/router:performance-tracks-optimize-deps",
3004
- setup(build) {
3005
- build.onLoad(
3006
- {
3007
- filter: /react-server-dom-webpack-client\.browser\.(development|production)\.js$/
3008
- },
3009
- async (args) => {
3010
- const code = await readFile(args.path, "utf8");
3011
- const patched = patchRsdwClientDebugInfoRecovery(code);
3012
- return {
3013
- contents: patched.code,
3014
- loader: "js"
3015
- };
3016
- }
3017
- );
3362
+ // Vite 8 optimizes deps with Rolldown (Rollup-style plugin pipeline), so the
3363
+ // pre-bundled RSDW client is patched via load() rather than esbuild's onLoad.
3364
+ // Returning code overrides Rolldown's default filesystem read for the module.
3365
+ async load(id) {
3366
+ const cleanId = id.split("?")[0] ?? id;
3367
+ if (!RSDW_CLIENT_RE.test(cleanId)) return null;
3368
+ const code = await readFile(cleanId, "utf8");
3369
+ const patched = patchRsdwClientDebugInfoRecovery(code);
3370
+ return { code: patched.code };
3018
3371
  }
3019
3372
  };
3020
3373
  }
3021
3374
  function performanceTracksPlugin() {
3375
+ const counter = createCounter(debug6, "performance-tracks");
3022
3376
  return {
3023
3377
  name: "@rangojs/router:performance-tracks",
3378
+ buildEnd() {
3379
+ counter?.flush();
3380
+ },
3024
3381
  transform(code, id) {
3025
3382
  if (!id.includes("react-server-dom") || !id.includes("client")) return;
3026
- const patched = patchRsdwClientDebugInfoRecovery(code);
3027
- if (!patched.debugInfoVar) return;
3028
- if (process.env.INTERNAL_RANGO_DEBUG)
3029
- console.log(
3030
- "[perf-tracks] patched RSDW client (var:",
3031
- patched.debugInfoVar,
3032
- ")"
3033
- );
3034
- return patched.code;
3383
+ const start = counter ? performance.now() : 0;
3384
+ try {
3385
+ const patched = patchRsdwClientDebugInfoRecovery(code);
3386
+ if (!patched.debugInfoVar) return;
3387
+ debug6?.("patched RSDW client (var: %s)", patched.debugInfoVar);
3388
+ return patched.code;
3389
+ } finally {
3390
+ counter?.record(id, performance.now() - start);
3391
+ }
3035
3392
  }
3036
3393
  };
3037
3394
  }
3038
3395
 
3039
3396
  // src/vite/utils/shared-utils.ts
3040
- var versionEsbuildPlugin = {
3397
+ function resolveRscEntryFromConfig(config) {
3398
+ const entries = config.environments?.["rsc"]?.optimizeDeps?.entries;
3399
+ if (typeof entries === "string") return entries;
3400
+ if (Array.isArray(entries) && entries.length > 0) return entries[0];
3401
+ return void 0;
3402
+ }
3403
+ var versionRolldownPlugin = {
3041
3404
  name: "@rangojs/router-version",
3042
- setup(build) {
3043
- build.onResolve({ filter: /^rsc-router:version$/ }, (args) => ({
3044
- path: args.path,
3045
- namespace: "@rangojs/router-virtual"
3046
- }));
3047
- build.onLoad(
3048
- { filter: /.*/, namespace: "@rangojs/router-virtual" },
3049
- () => ({
3050
- contents: `export const VERSION = "dev";`,
3051
- loader: "js"
3052
- })
3053
- );
3405
+ resolveId(id) {
3406
+ if (id === VIRTUAL_IDS.version) return "\0" + VIRTUAL_IDS.version;
3407
+ return void 0;
3408
+ },
3409
+ load(id) {
3410
+ if (id === "\0" + VIRTUAL_IDS.version) {
3411
+ return getVirtualVersionContent("dev");
3412
+ }
3413
+ return void 0;
3054
3414
  }
3055
3415
  };
3056
- var sharedEsbuildOptions = {
3057
- plugins: [versionEsbuildPlugin, performanceTracksOptimizeDepsPlugin()]
3416
+ var sharedRolldownOptions = {
3417
+ plugins: [versionRolldownPlugin, performanceTracksOptimizeDepsPlugin()]
3058
3418
  };
3059
3419
  function createVirtualEntriesPlugin(entries, routerPathRef) {
3060
3420
  const virtualModules = {};
@@ -3096,8 +3456,29 @@ function createVirtualEntriesPlugin(entries, routerPathRef) {
3096
3456
  }
3097
3457
  };
3098
3458
  }
3459
+ function isContentHashedAssetConflict(message) {
3460
+ if (!message) return false;
3461
+ const match = /The emitted file "?([^"\s]+)"? overwrites a previously emitted file/.exec(
3462
+ message
3463
+ );
3464
+ if (!match) return false;
3465
+ const fileName = match[1];
3466
+ const base = fileName.slice(fileName.lastIndexOf("/") + 1);
3467
+ const dot = base.lastIndexOf(".");
3468
+ if (dot <= 0) return false;
3469
+ const stem = base.slice(0, dot);
3470
+ const HASH_LEN = 8;
3471
+ if (stem.length < HASH_LEN + 1 || stem[stem.length - HASH_LEN - 1] !== "-") {
3472
+ return false;
3473
+ }
3474
+ const hash = stem.slice(-HASH_LEN);
3475
+ return /^[A-Za-z0-9_-]+$/.test(hash) && /[A-Z0-9]/.test(hash);
3476
+ }
3099
3477
  function onwarn(warning, defaultHandler) {
3100
- if (warning.code === "MODULE_LEVEL_DIRECTIVE" || warning.code === "SOURCEMAP_ERROR" || warning.code === "EMPTY_BUNDLE") {
3478
+ if (warning.code === "MODULE_LEVEL_DIRECTIVE" || warning.code === "SOURCEMAP_ERROR" || warning.code === "EMPTY_BUNDLE" || warning.code === "INEFFECTIVE_DYNAMIC_IMPORT") {
3479
+ return;
3480
+ }
3481
+ if (warning.code === "FILE_NAME_CONFLICT" && isContentHashedAssetConflict(warning.message)) {
3101
3482
  return;
3102
3483
  }
3103
3484
  if (warning.message?.includes("Sourcemap is likely to be incorrect")) {
@@ -3116,12 +3497,138 @@ function getManualChunks(id) {
3116
3497
  return "react";
3117
3498
  }
3118
3499
  const packageName = getPublishedPackageName();
3119
- if (normalized.includes(`node_modules/${packageName}/`) || normalized.includes("packages/rsc-router/") || normalized.includes("packages/rangojs-router/")) {
3500
+ if (normalized.includes(`node_modules/${packageName}/`) || /\/packages\/(rsc-router|rangojs-router)\/(src|dist)\//.test(normalized)) {
3120
3501
  return "router";
3121
3502
  }
3122
3503
  return void 0;
3123
3504
  }
3124
3505
 
3506
+ // src/vite/plugins/client-ref-hashing.ts
3507
+ import { relative } from "node:path";
3508
+ import { createHash as createHash2 } from "node:crypto";
3509
+ var debug7 = createRangoDebugger(NS.transform);
3510
+ var CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
3511
+ var CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
3512
+ var FS_PREFIX = "/@fs/";
3513
+ function hashRefKey(relativeId) {
3514
+ return createHash2("sha256").update(relativeId).digest("hex").slice(0, 12);
3515
+ }
3516
+ function computeProductionHash(projectRoot, refKey) {
3517
+ let toHash;
3518
+ if (refKey.startsWith(CLIENT_PKG_PROXY_PREFIX)) {
3519
+ toHash = refKey.slice(CLIENT_PKG_PROXY_PREFIX.length);
3520
+ } else if (refKey.startsWith(CLIENT_IN_SERVER_PKG_PROXY_PREFIX)) {
3521
+ const absPath = decodeURIComponent(
3522
+ refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length)
3523
+ );
3524
+ toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
3525
+ } else if (refKey.startsWith(FS_PREFIX)) {
3526
+ const absPath = refKey.slice(FS_PREFIX.length - 1);
3527
+ toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
3528
+ } else if (refKey.startsWith("/")) {
3529
+ toHash = refKey.slice(1);
3530
+ } else {
3531
+ return refKey;
3532
+ }
3533
+ return hashRefKey(toHash);
3534
+ }
3535
+ var REGISTER_CLIENT_REF_RE = /registerClientReference\(\s*(?:(?:\([^)]*\))|(?:\(\)[\s\S]*?\}))\s*,\s*"([^"]+)"\s*,\s*"[^"]+"\s*\)/g;
3536
+ function transformClientRefs(code, projectRoot) {
3537
+ if (!code.includes("registerClientReference")) return null;
3538
+ let hasReplacement = false;
3539
+ const result = code.replace(
3540
+ REGISTER_CLIENT_REF_RE,
3541
+ (match, refKey) => {
3542
+ const hash = computeProductionHash(projectRoot, refKey);
3543
+ if (hash === refKey) return match;
3544
+ hasReplacement = true;
3545
+ return match.replace(`"${refKey}"`, `"${hash}"`);
3546
+ }
3547
+ );
3548
+ return hasReplacement ? result : null;
3549
+ }
3550
+ function hashClientRefs(projectRoot) {
3551
+ const counter = createCounter(debug7, "hash-client-refs");
3552
+ return {
3553
+ name: "@rangojs/router:hash-client-refs",
3554
+ // Run after the RSC plugin's transform (default enforce is normal)
3555
+ enforce: "post",
3556
+ applyToEnvironment(env) {
3557
+ return env.name === "rsc";
3558
+ },
3559
+ buildEnd() {
3560
+ counter?.flush();
3561
+ },
3562
+ transform(code, id) {
3563
+ const start = counter ? performance.now() : 0;
3564
+ try {
3565
+ const result = transformClientRefs(code, projectRoot);
3566
+ if (result === null) return;
3567
+ return { code: result, map: null };
3568
+ } finally {
3569
+ counter?.record(id, performance.now() - start);
3570
+ }
3571
+ }
3572
+ };
3573
+ }
3574
+
3575
+ // src/vite/utils/client-chunks.ts
3576
+ var debugChunks = createRangoDebugger(NS.chunks);
3577
+ function isSharedRuntime(meta) {
3578
+ return [meta.id, meta.normalizedId].some(
3579
+ (path6) => path6.includes("/node_modules/") || /\/@rangojs\/router\//.test(path6) || /\/packages\/(rangojs-router|rsc-router)\/(src|dist)\//.test(path6)
3580
+ );
3581
+ }
3582
+ function sanitizeGroup(name) {
3583
+ return name.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "") || "app";
3584
+ }
3585
+ var ROUTE_ROOT_DIRS = /* @__PURE__ */ new Set([
3586
+ "routes",
3587
+ "route",
3588
+ "pages",
3589
+ "page",
3590
+ "app",
3591
+ "features",
3592
+ "feature",
3593
+ "views",
3594
+ "view",
3595
+ "handlers",
3596
+ "urls",
3597
+ "modules",
3598
+ "screens",
3599
+ "sections"
3600
+ ]);
3601
+ function directoryClientChunks(meta, ctx) {
3602
+ if (isSharedRuntime(meta)) {
3603
+ return void 0;
3604
+ }
3605
+ if (ctx?.fallbackRefs.size && ctx.fallbackRefs.has(hashRefKey(meta.normalizedId))) {
3606
+ debugChunks?.("fallback %s -> app-fallback", meta.normalizedId);
3607
+ return "app-fallback";
3608
+ }
3609
+ const segments = meta.normalizedId.split("/").filter(Boolean);
3610
+ const dirCount = segments.length - 1;
3611
+ if (dirCount >= 1) {
3612
+ for (let i = 0; i < dirCount - 1; i++) {
3613
+ if (ROUTE_ROOT_DIRS.has(segments[i].toLowerCase())) {
3614
+ const group = `app-${sanitizeGroup(segments[i + 1])}`;
3615
+ debugChunks?.("split %s -> %s", meta.normalizedId, group);
3616
+ return group;
3617
+ }
3618
+ }
3619
+ }
3620
+ debugChunks?.(
3621
+ "shared %s (no route-root marker; inherits default grouping)",
3622
+ meta.normalizedId
3623
+ );
3624
+ return void 0;
3625
+ }
3626
+ function resolveClientChunks(option, ctx) {
3627
+ if (!option) return void 0;
3628
+ if (option === true) return (meta) => directoryClientChunks(meta, ctx);
3629
+ return option;
3630
+ }
3631
+
3125
3632
  // src/vite/utils/banner.ts
3126
3633
  var rangoVersion = package_default.version;
3127
3634
  var _bannerPrinted = false;
@@ -3158,15 +3665,7 @@ function createVersionInjectorPlugin(rscEntryPath) {
3158
3665
  enforce: "pre",
3159
3666
  configResolved(config) {
3160
3667
  let entryPath = rscEntryPath;
3161
- if (!entryPath) {
3162
- const rscEnvConfig = config.environments?.["rsc"];
3163
- const entries = rscEnvConfig?.optimizeDeps?.entries;
3164
- if (typeof entries === "string") {
3165
- entryPath = entries;
3166
- } else if (Array.isArray(entries) && entries.length > 0) {
3167
- entryPath = entries[0];
3168
- }
3169
- }
3668
+ if (!entryPath) entryPath = resolveRscEntryFromConfig(config);
3170
3669
  if (entryPath) {
3171
3670
  resolvedEntryPath = resolve4(config.root, entryPath);
3172
3671
  }
@@ -3178,11 +3677,10 @@ function createVersionInjectorPlugin(rscEntryPath) {
3178
3677
  if (normalizedId !== normalizedEntry) {
3179
3678
  return null;
3180
3679
  }
3181
- const prepend = [];
3680
+ const prepend = [
3681
+ `import "virtual:rsc-router/routes-manifest";`
3682
+ ];
3182
3683
  let newCode = code;
3183
- if (!code.includes("virtual:rsc-router/routes-manifest")) {
3184
- prepend.push(`import "virtual:rsc-router/routes-manifest";`);
3185
- }
3186
3684
  const needsVersion = code.includes("createRSCHandler") && !code.includes("@rangojs/router:version") && /createRSCHandler\s*\(\s*\{/.test(code);
3187
3685
  if (needsVersion) {
3188
3686
  prepend.push(`import { VERSION } from "@rangojs/router:version";`);
@@ -3191,8 +3689,21 @@ function createVersionInjectorPlugin(rscEntryPath) {
3191
3689
  "createRSCHandler({\n version: VERSION,"
3192
3690
  );
3193
3691
  }
3194
- if (prepend.length === 0 && newCode === code) return null;
3195
- newCode = prepend.join("\n") + (prepend.length > 0 ? "\n" : "") + newCode;
3692
+ const lines = newCode.split("\n");
3693
+ let insertAt = 0;
3694
+ while (insertAt < lines.length) {
3695
+ const trimmed = lines[insertAt].trim();
3696
+ if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
3697
+ insertAt++;
3698
+ } else {
3699
+ break;
3700
+ }
3701
+ }
3702
+ newCode = [
3703
+ ...lines.slice(0, insertAt),
3704
+ ...prepend,
3705
+ ...lines.slice(insertAt)
3706
+ ].join("\n");
3196
3707
  return {
3197
3708
  code: newCode,
3198
3709
  map: null
@@ -3202,21 +3713,23 @@ function createVersionInjectorPlugin(rscEntryPath) {
3202
3713
  }
3203
3714
 
3204
3715
  // src/vite/plugins/cjs-to-esm.ts
3716
+ var debug8 = createRangoDebugger(NS.transform);
3205
3717
  function createCjsToEsmPlugin() {
3206
3718
  return {
3207
3719
  name: "@rangojs/router:cjs-to-esm",
3208
3720
  enforce: "pre",
3209
3721
  transform(code, id) {
3210
- const cleanId = id.split("?")[0];
3211
- if (cleanId.includes("vendor/react-server-dom/client.browser.js") || cleanId.includes("vendor\\react-server-dom\\client.browser.js")) {
3722
+ const cleanId = id.split("?")[0].replaceAll("\\", "/");
3723
+ if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
3212
3724
  const isProd = process.env.NODE_ENV === "production";
3213
3725
  const cjsFile = isProd ? "./cjs/react-server-dom-webpack-client.browser.production.js" : "./cjs/react-server-dom-webpack-client.browser.development.js";
3726
+ debug8?.("cjs-to-esm entry redirect %s", id);
3214
3727
  return {
3215
3728
  code: `export * from "${cjsFile}";`,
3216
3729
  map: null
3217
3730
  };
3218
3731
  }
3219
- if ((cleanId.includes("vendor/react-server-dom/cjs/") || cleanId.includes("vendor\\react-server-dom\\cjs\\")) && cleanId.includes("client.browser")) {
3732
+ if (cleanId.includes("vendor/react-server-dom/cjs/") && cleanId.includes("client.browser")) {
3220
3733
  let transformed = code;
3221
3734
  const licenseMatch = transformed.match(/^\/\*\*[\s\S]*?\*\//);
3222
3735
  const license = licenseMatch ? licenseMatch[0] : "";
@@ -3246,6 +3759,7 @@ function createCjsToEsmPlugin() {
3246
3759
  "export const $1 ="
3247
3760
  );
3248
3761
  transformed = license + "\n" + transformed;
3762
+ debug8?.("cjs-to-esm body rewrite %s", id);
3249
3763
  return {
3250
3764
  code: transformed,
3251
3765
  map: null
@@ -3260,7 +3774,7 @@ function createCjsToEsmPlugin() {
3260
3774
  import { createServer as createViteServer } from "vite";
3261
3775
  import { resolve as resolve8 } from "node:path";
3262
3776
  import { readFileSync as readFileSync6 } from "node:fs";
3263
- import { createRequire } from "node:module";
3777
+ import { createRequire as createRequire2, register } from "node:module";
3264
3778
  import { pathToFileURL } from "node:url";
3265
3779
 
3266
3780
  // src/vite/plugins/virtual-stub-plugin.ts
@@ -3287,61 +3801,112 @@ function createVirtualStubPlugin() {
3287
3801
  };
3288
3802
  }
3289
3803
 
3290
- // src/vite/plugins/client-ref-hashing.ts
3291
- import { relative } from "node:path";
3292
- import { createHash as createHash2 } from "node:crypto";
3293
- var CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
3294
- var CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
3295
- var FS_PREFIX = "/@fs/";
3296
- function computeProductionHash(projectRoot, refKey) {
3297
- let toHash;
3298
- if (refKey.startsWith(CLIENT_PKG_PROXY_PREFIX)) {
3299
- toHash = refKey.slice(CLIENT_PKG_PROXY_PREFIX.length);
3300
- } else if (refKey.startsWith(CLIENT_IN_SERVER_PKG_PROXY_PREFIX)) {
3301
- const absPath = decodeURIComponent(
3302
- refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length)
3303
- );
3304
- toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
3305
- } else if (refKey.startsWith(FS_PREFIX)) {
3306
- const absPath = refKey.slice(FS_PREFIX.length - 1);
3307
- toHash = relative(projectRoot, absPath).replaceAll("\\", "/");
3308
- } else if (refKey.startsWith("/")) {
3309
- toHash = refKey.slice(1);
3310
- } else {
3311
- return refKey;
3312
- }
3313
- return createHash2("sha256").update(toHash).digest("hex").slice(0, 12);
3314
- }
3315
- var REGISTER_CLIENT_REF_RE = /registerClientReference\(\s*(?:(?:\([^)]*\))|(?:\(\)[\s\S]*?\}))\s*,\s*"([^"]+)"\s*,\s*"[^"]+"\s*\)/g;
3316
- function transformClientRefs(code, projectRoot) {
3317
- if (!code.includes("registerClientReference")) return null;
3318
- let hasReplacement = false;
3319
- const result = code.replace(
3320
- REGISTER_CLIENT_REF_RE,
3321
- (match, refKey) => {
3322
- const hash = computeProductionHash(projectRoot, refKey);
3323
- if (hash === refKey) return match;
3324
- hasReplacement = true;
3325
- return match.replace(`"${refKey}"`, `"${hash}"`);
3326
- }
3327
- );
3328
- return hasReplacement ? result : null;
3329
- }
3330
- function hashClientRefs(projectRoot) {
3804
+ // src/vite/plugins/cloudflare-protocol-stub.ts
3805
+ var VIRTUAL_PREFIX = "virtual:rango-cloudflare-stub-";
3806
+ var NULL_PREFIX = "\0" + VIRTUAL_PREFIX;
3807
+ var CF_PREFIX = "cloudflare:";
3808
+ var BUILD_ENV_GLOBAL_KEY = "__rango_build_env__";
3809
+ var SOURCE_EXT_RE = /\.[mc]?[jt]sx?$/;
3810
+ var IMPORT_NODE_TYPES = /* @__PURE__ */ new Set([
3811
+ "ImportDeclaration",
3812
+ "ImportExpression",
3813
+ "ExportNamedDeclaration",
3814
+ "ExportAllDeclaration"
3815
+ ]);
3816
+ var STUBS = {
3817
+ "cloudflare:workers": `
3818
+ export class DurableObject { constructor(_ctx, _env) {} }
3819
+ export class WorkerEntrypoint { constructor(_ctx, _env) {} }
3820
+ export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
3821
+ export class RpcTarget {}
3822
+ export const env = globalThis[${JSON.stringify(BUILD_ENV_GLOBAL_KEY)}] ?? {};
3823
+ export default {};
3824
+ `,
3825
+ "cloudflare:email": `
3826
+ export class EmailMessage { constructor(_from, _to, _raw) {} }
3827
+ export default {};
3828
+ `,
3829
+ "cloudflare:sockets": `
3830
+ export function connect() { return {}; }
3831
+ export default {};
3832
+ `,
3833
+ "cloudflare:workflows": `
3834
+ export class NonRetryableError extends Error {
3835
+ constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
3836
+ }
3837
+ export default {};
3838
+ `
3839
+ };
3840
+ var FALLBACK_STUB = `export default {};
3841
+ `;
3842
+ function createCloudflareProtocolStubPlugin() {
3331
3843
  return {
3332
- name: "@rangojs/router:hash-client-refs",
3333
- // Run after the RSC plugin's transform (default enforce is normal)
3334
- enforce: "post",
3335
- applyToEnvironment(env) {
3336
- return env.name === "rsc";
3844
+ name: "@rangojs/router:cloudflare-protocol-stub",
3845
+ transform(code, id) {
3846
+ const cleanId = id.split("?")[0] ?? id;
3847
+ if (!SOURCE_EXT_RE.test(cleanId)) return null;
3848
+ if (!code.includes(CF_PREFIX)) return null;
3849
+ let ast;
3850
+ try {
3851
+ ast = this.parse(code, { lang: "tsx" });
3852
+ } catch {
3853
+ return null;
3854
+ }
3855
+ const hits = [];
3856
+ walk(ast, (node) => {
3857
+ if (!IMPORT_NODE_TYPES.has(node.type)) return;
3858
+ const source = node.source;
3859
+ if (!source || source.type !== "Literal") return;
3860
+ if (typeof source.value !== "string") return;
3861
+ if (!source.value.startsWith(CF_PREFIX)) return;
3862
+ if (typeof source.start !== "number" || typeof source.end !== "number")
3863
+ return;
3864
+ hits.push({
3865
+ start: source.start,
3866
+ end: source.end,
3867
+ value: source.value
3868
+ });
3869
+ });
3870
+ if (hits.length === 0) return null;
3871
+ hits.sort((a, b) => b.start - a.start);
3872
+ let out = code;
3873
+ for (const hit of hits) {
3874
+ const submodule = hit.value.slice(CF_PREFIX.length);
3875
+ const quote = code[hit.start] === "'" ? "'" : '"';
3876
+ out = out.slice(0, hit.start) + quote + VIRTUAL_PREFIX + submodule + quote + out.slice(hit.end);
3877
+ }
3878
+ return { code: out, map: null };
3337
3879
  },
3338
- transform(code, _id) {
3339
- const result = transformClientRefs(code, projectRoot);
3340
- if (result === null) return;
3341
- return { code: result, map: null };
3880
+ resolveId(id) {
3881
+ if (id.startsWith(VIRTUAL_PREFIX)) {
3882
+ return "\0" + id;
3883
+ }
3884
+ return null;
3885
+ },
3886
+ load(id) {
3887
+ if (!id.startsWith(NULL_PREFIX)) return null;
3888
+ const submodule = id.slice(NULL_PREFIX.length);
3889
+ const specifier = CF_PREFIX + submodule;
3890
+ return STUBS[specifier] ?? FALLBACK_STUB;
3342
3891
  }
3343
3892
  };
3344
3893
  }
3894
+ function walk(node, visit) {
3895
+ if (!node || typeof node !== "object") return;
3896
+ if (Array.isArray(node)) {
3897
+ for (const child of node) walk(child, visit);
3898
+ return;
3899
+ }
3900
+ const n = node;
3901
+ if (typeof n.type !== "string") return;
3902
+ visit(n);
3903
+ for (const key in n) {
3904
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
3905
+ continue;
3906
+ }
3907
+ walk(n[key], visit);
3908
+ }
3909
+ }
3345
3910
 
3346
3911
  // src/vite/utils/bundle-analysis.ts
3347
3912
  function findMatchingParenInBundle(code, openParenPos) {
@@ -3373,7 +3938,7 @@ function extractHandlerExportsFromChunk(chunkCode, handlerModules, fnName, detec
3373
3938
  if (detectPassthrough) {
3374
3939
  const eFnName = escapeRegExp(fnName);
3375
3940
  const callStartRe = new RegExp(
3376
- `const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
3941
+ `(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
3377
3942
  );
3378
3943
  const callStart = callStartRe.exec(chunkCode);
3379
3944
  if (callStart) {
@@ -3398,7 +3963,7 @@ function evictHandlerCode(code, exports, fnName, brand) {
3398
3963
  if (passthrough) continue;
3399
3964
  const eName = escapeRegExp(name);
3400
3965
  const callStartRe = new RegExp(
3401
- `const\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
3966
+ `(?:const|let|var)\\s+${eName}\\s*=\\s*${eFnName}\\s*(?:<[^>]*>)?\\s*\\(`
3402
3967
  );
3403
3968
  const startMatch = callStartRe.exec(modified);
3404
3969
  if (!startMatch) continue;
@@ -3433,6 +3998,8 @@ function createDiscoveryState(entryPath, opts) {
3433
3998
  projectRoot: "",
3434
3999
  isBuildMode: false,
3435
4000
  userResolveAlias: void 0,
4001
+ userRunnerConfig: void 0,
4002
+ userResolvePlugins: [],
3436
4003
  scanFilter: void 0,
3437
4004
  cachedRouterFiles: void 0,
3438
4005
  opts,
@@ -3454,7 +4021,8 @@ function createDiscoveryState(entryPath, opts) {
3454
4021
  devServerOrigin: null,
3455
4022
  devServer: null,
3456
4023
  selfWrittenGenFiles: /* @__PURE__ */ new Map(),
3457
- SELF_WRITE_WINDOW_MS: 5e3
4024
+ SELF_WRITE_WINDOW_MS: 5e3,
4025
+ lastDiscoveryError: null
3458
4026
  };
3459
4027
  }
3460
4028
 
@@ -3466,6 +4034,12 @@ function markSelfGenWrite(state, filePath, content) {
3466
4034
  state.selfWrittenGenFiles.set(filePath, { at: Date.now(), hash });
3467
4035
  }
3468
4036
  function consumeSelfGenWrite(state, filePath) {
4037
+ return checkSelfGenWrite(state, filePath, true);
4038
+ }
4039
+ function peekSelfGenWrite(state, filePath) {
4040
+ return checkSelfGenWrite(state, filePath, false);
4041
+ }
4042
+ function checkSelfGenWrite(state, filePath, consume) {
3469
4043
  const info = state.selfWrittenGenFiles.get(filePath);
3470
4044
  if (!info) return false;
3471
4045
  if (Date.now() - info.at > state.SELF_WRITE_WINDOW_MS) {
@@ -3476,7 +4050,7 @@ function consumeSelfGenWrite(state, filePath) {
3476
4050
  const current = readFileSync3(filePath, "utf-8");
3477
4051
  const currentHash = createHash3("sha256").update(current).digest("hex");
3478
4052
  if (currentHash === info.hash) {
3479
- state.selfWrittenGenFiles.delete(filePath);
4053
+ if (consume) state.selfWrittenGenFiles.delete(filePath);
3480
4054
  return true;
3481
4055
  }
3482
4056
  return false;
@@ -3486,11 +4060,14 @@ function consumeSelfGenWrite(state, filePath) {
3486
4060
  }
3487
4061
  }
3488
4062
 
3489
- // src/vite/utils/manifest-utils.ts
4063
+ // src/build/prefix-tree-utils.ts
3490
4064
  function flattenLeafEntries(prefixTree, routeManifest, result) {
3491
- function visit(node) {
4065
+ function visit(node, ancestorStaticPrefixes) {
3492
4066
  const children = node.children || {};
3493
4067
  if (Object.keys(children).length === 0 && node.routes && node.routes.length > 0) {
4068
+ if (ancestorStaticPrefixes.has(node.staticPrefix)) {
4069
+ return;
4070
+ }
3494
4071
  const routes = {};
3495
4072
  for (const name of node.routes) {
3496
4073
  if (name in routeManifest) {
@@ -3499,13 +4076,15 @@ function flattenLeafEntries(prefixTree, routeManifest, result) {
3499
4076
  }
3500
4077
  result.push({ staticPrefix: node.staticPrefix, routes });
3501
4078
  } else {
4079
+ const nextAncestors = new Set(ancestorStaticPrefixes);
4080
+ nextAncestors.add(node.staticPrefix);
3502
4081
  for (const child of Object.values(children)) {
3503
- visit(child);
4082
+ visit(child, nextAncestors);
3504
4083
  }
3505
4084
  }
3506
4085
  }
3507
4086
  for (const node of Object.values(prefixTree)) {
3508
- visit(node);
4087
+ visit(node, /* @__PURE__ */ new Set());
3509
4088
  }
3510
4089
  }
3511
4090
  function buildRouteToStaticPrefix(prefixTree, result) {
@@ -3522,6 +4101,8 @@ function buildRouteToStaticPrefix(prefixTree, result) {
3522
4101
  visit(node);
3523
4102
  }
3524
4103
  }
4104
+
4105
+ // src/vite/utils/manifest-utils.ts
3525
4106
  function jsonParseExpression(value) {
3526
4107
  const json = JSON.stringify(value);
3527
4108
  const escaped = json.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
@@ -3580,11 +4161,19 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
3580
4161
  let hadOmittedOptional = false;
3581
4162
  for (const [key, value] of Object.entries(params)) {
3582
4163
  const escaped = escapeRegExp2(key);
3583
- result = result.replace(
3584
- new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
3585
- encode(value)
3586
- );
3587
- result = result.replace(`*${key}`, encode(value));
4164
+ if (value === "") {
4165
+ result = result.replace(
4166
+ new RegExp(`:${escaped}(\\([^)]*\\))?(?!\\?)`),
4167
+ ""
4168
+ );
4169
+ result = result.replace(`*${key}`, "");
4170
+ } else {
4171
+ result = result.replace(
4172
+ new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
4173
+ encode(value)
4174
+ );
4175
+ result = result.replace(`*${key}`, encode(value));
4176
+ }
3588
4177
  }
3589
4178
  result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
3590
4179
  hadOmittedOptional = true;
@@ -3690,8 +4279,14 @@ function copyStagedBuildAssets(projectRoot, fileNames) {
3690
4279
  }
3691
4280
 
3692
4281
  // src/vite/discovery/prerender-collection.ts
4282
+ var debug9 = createRangoDebugger(NS.prerender);
3693
4283
  async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3694
4284
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode) return;
4285
+ const overallStart = debug9 ? performance.now() : 0;
4286
+ debug9?.(
4287
+ "expandPrerenderRoutes: start (%d router manifest(s))",
4288
+ allManifests.length
4289
+ );
3695
4290
  const entries = [];
3696
4291
  const allRoutes = {};
3697
4292
  for (const { manifest: m } of allManifests) {
@@ -3718,7 +4313,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3718
4313
  const progressInterval = totalDynamic > 0 ? setInterval(() => {
3719
4314
  const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
3720
4315
  console.log(
3721
- `[rsc-router] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
4316
+ `[rango] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
3722
4317
  );
3723
4318
  }, 5e3) : void 0;
3724
4319
  try {
@@ -3741,6 +4336,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3741
4336
  });
3742
4337
  } else {
3743
4338
  if (def?.getParams) {
4339
+ const getParamsStart = debug9 ? performance.now() : 0;
3744
4340
  try {
3745
4341
  const buildVars = {};
3746
4342
  const buildEnv = state.resolvedBuildEnv;
@@ -3754,11 +4350,17 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3754
4350
  get env() {
3755
4351
  if (buildEnv !== void 0) return buildEnv;
3756
4352
  throw new Error(
3757
- "[rsc-router] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
4353
+ "[rango] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
3758
4354
  );
3759
4355
  }
3760
4356
  };
3761
4357
  const paramsList = await def.getParams(getParamsCtx);
4358
+ debug9?.(
4359
+ "getParams %s -> %d params (%sms)",
4360
+ routeName,
4361
+ paramsList.length,
4362
+ (performance.now() - getParamsStart).toFixed(1)
4363
+ );
3762
4364
  const concurrency = def.options?.concurrency ?? 1;
3763
4365
  const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
3764
4366
  for (const params of paramsList) {
@@ -3789,7 +4391,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3789
4391
  resolvedRoutes++;
3790
4392
  if (err.name === "Skip") {
3791
4393
  console.log(
3792
- `[rsc-router] SKIP route "${routeName}" - ${err.message}`
4394
+ `[rango] SKIP route "${routeName}" - ${err.message}`
3793
4395
  );
3794
4396
  notifyOnError(
3795
4397
  registry,
@@ -3802,14 +4404,14 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3802
4404
  continue;
3803
4405
  }
3804
4406
  console.error(
3805
- `[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`
4407
+ `[rango] Failed to get params for prerender route "${routeName}": ${err.message}`
3806
4408
  );
3807
4409
  notifyOnError(registry, err, "prerender", routeName);
3808
4410
  throw err;
3809
4411
  }
3810
4412
  } else {
3811
4413
  console.warn(
3812
- `[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
4414
+ `[rango] Dynamic prerender route "${routeName}" has no getParams(), skipping`
3813
4415
  );
3814
4416
  }
3815
4417
  }
@@ -3820,15 +4422,26 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3820
4422
  clearInterval(progressInterval);
3821
4423
  const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
3822
4424
  console.log(
3823
- `[rsc-router] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
4425
+ `[rango] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
3824
4426
  );
3825
4427
  }
3826
4428
  }
3827
- if (entries.length === 0) return;
4429
+ if (entries.length === 0) {
4430
+ debug9?.(
4431
+ "no prerender entries (done in %sms)",
4432
+ (performance.now() - overallStart).toFixed(1)
4433
+ );
4434
+ return;
4435
+ }
3828
4436
  const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
3829
4437
  const concurrencyNote = maxConcurrency > 1 ? ` (concurrency: ${maxConcurrency})` : "";
3830
4438
  console.log(
3831
- `[rsc-router] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
4439
+ `[rango] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
4440
+ );
4441
+ debug9?.(
4442
+ "prerender loop: %d entries, max concurrency %d",
4443
+ entries.length,
4444
+ maxConcurrency
3832
4445
  );
3833
4446
  const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
3834
4447
  const manifestEntries = {};
@@ -3856,7 +4469,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3856
4469
  if (result.passthrough) {
3857
4470
  const elapsed2 = (performance.now() - startUrl).toFixed(0);
3858
4471
  console.log(
3859
- `[rsc-router] PASS ${entry.urlPath.padEnd(40)} (${elapsed2}ms) - live fallback`
4472
+ `[rango] PASS ${entry.urlPath.padEnd(40)} (${elapsed2}ms) - live fallback`
3860
4473
  );
3861
4474
  doneCount++;
3862
4475
  break;
@@ -3889,7 +4502,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3889
4502
  }
3890
4503
  const elapsed = (performance.now() - startUrl).toFixed(0);
3891
4504
  console.log(
3892
- `[rsc-router] OK ${entry.urlPath.padEnd(40)} (${elapsed}ms)`
4505
+ `[rango] OK ${entry.urlPath.padEnd(40)} (${elapsed}ms)`
3893
4506
  );
3894
4507
  doneCount++;
3895
4508
  break;
@@ -3897,7 +4510,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3897
4510
  if (err.name === "Skip") {
3898
4511
  const elapsed2 = (performance.now() - startUrl).toFixed(0);
3899
4512
  console.log(
3900
- `[rsc-router] SKIP ${entry.urlPath.padEnd(40)} (${elapsed2}ms) - ${err.message}`
4513
+ `[rango] SKIP ${entry.urlPath.padEnd(40)} (${elapsed2}ms) - ${err.message}`
3901
4514
  );
3902
4515
  skipCount++;
3903
4516
  notifyOnError(
@@ -3912,7 +4525,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3912
4525
  }
3913
4526
  const elapsed = (performance.now() - startUrl).toFixed(0);
3914
4527
  console.error(
3915
- `[rsc-router] FAIL ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`
4528
+ `[rango] FAIL ${entry.urlPath.padEnd(40)} (${elapsed}ms) - ${err.message}`
3916
4529
  );
3917
4530
  notifyOnError(
3918
4531
  registry,
@@ -3934,12 +4547,24 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3934
4547
  const parts = [`${doneCount} done`];
3935
4548
  if (skipCount > 0) parts.push(`${skipCount} skipped`);
3936
4549
  console.log(
3937
- `[rsc-router] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`
4550
+ `[rango] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`
4551
+ );
4552
+ debug9?.(
4553
+ "expandPrerenderRoutes done: %d done, %d skipped, %sms (overall %sms)",
4554
+ doneCount,
4555
+ skipCount,
4556
+ totalElapsed,
4557
+ (performance.now() - overallStart).toFixed(1)
3938
4558
  );
3939
4559
  }
3940
4560
  async function renderStaticHandlers(state, rscEnv, registry) {
3941
4561
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode || !state.resolvedStaticModules?.size)
3942
4562
  return;
4563
+ const overallStart = debug9 ? performance.now() : 0;
4564
+ debug9?.(
4565
+ "renderStaticHandlers: start (%d static module(s))",
4566
+ state.resolvedStaticModules.size
4567
+ );
3943
4568
  const manifestEntries = {};
3944
4569
  let staticDone = 0;
3945
4570
  let staticSkip = 0;
@@ -3948,16 +4573,14 @@ async function renderStaticHandlers(state, rscEnv, registry) {
3948
4573
  totalStaticCount += exportNames.length;
3949
4574
  }
3950
4575
  const startStatic = performance.now();
3951
- console.log(
3952
- `[rsc-router] Rendering ${totalStaticCount} static handler(s)...`
3953
- );
4576
+ console.log(`[rango] Rendering ${totalStaticCount} static handler(s)...`);
3954
4577
  for (const [moduleId, exportNames] of state.resolvedStaticModules) {
3955
4578
  let mod;
3956
4579
  try {
3957
4580
  mod = await rscEnv.runner.import(moduleId);
3958
4581
  } catch (err) {
3959
4582
  console.error(
3960
- `[rsc-router] Failed to import static module ${moduleId}: ${err.message}`
4583
+ `[rango] Failed to import static module ${moduleId}: ${err.message}`
3961
4584
  );
3962
4585
  notifyOnError(registry, err, "static");
3963
4586
  throw err;
@@ -3987,9 +4610,7 @@ async function renderStaticHandlers(state, rscEnv, registry) {
3987
4610
  exportValue
3988
4611
  );
3989
4612
  const elapsed = (performance.now() - startHandler).toFixed(0);
3990
- console.log(
3991
- `[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`
3992
- );
4613
+ console.log(`[rango] OK ${name.padEnd(40)} (${elapsed}ms)`);
3993
4614
  staticDone++;
3994
4615
  handled = true;
3995
4616
  break;
@@ -3998,7 +4619,7 @@ async function renderStaticHandlers(state, rscEnv, registry) {
3998
4619
  if (err.name === "Skip") {
3999
4620
  const elapsed2 = (performance.now() - startHandler).toFixed(0);
4000
4621
  console.log(
4001
- `[rsc-router] SKIP ${name.padEnd(40)} (${elapsed2}ms) - ${err.message}`
4622
+ `[rango] SKIP ${name.padEnd(40)} (${elapsed2}ms) - ${err.message}`
4002
4623
  );
4003
4624
  staticSkip++;
4004
4625
  notifyOnError(registry, err, "static", void 0, void 0, true);
@@ -4007,16 +4628,14 @@ async function renderStaticHandlers(state, rscEnv, registry) {
4007
4628
  }
4008
4629
  const elapsed = (performance.now() - startHandler).toFixed(0);
4009
4630
  console.error(
4010
- `[rsc-router] FAIL ${name.padEnd(40)} (${elapsed}ms) - ${err.message}`
4631
+ `[rango] FAIL ${name.padEnd(40)} (${elapsed}ms) - ${err.message}`
4011
4632
  );
4012
4633
  notifyOnError(registry, err, "static");
4013
4634
  throw err;
4014
4635
  }
4015
4636
  }
4016
4637
  if (!handled) {
4017
- console.warn(
4018
- `[rsc-router] No router could render static handler "${name}"`
4019
- );
4638
+ console.warn(`[rango] No router could render static handler "${name}"`);
4020
4639
  }
4021
4640
  }
4022
4641
  }
@@ -4027,38 +4646,118 @@ async function renderStaticHandlers(state, rscEnv, registry) {
4027
4646
  const staticParts = [`${staticDone} done`];
4028
4647
  if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
4029
4648
  console.log(
4030
- `[rsc-router] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`
4649
+ `[rango] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`
4650
+ );
4651
+ debug9?.(
4652
+ "renderStaticHandlers done: %d done, %d skipped, %sms (overall %sms)",
4653
+ staticDone,
4654
+ staticSkip,
4655
+ totalStaticElapsed,
4656
+ (performance.now() - overallStart).toFixed(1)
4031
4657
  );
4032
4658
  }
4033
4659
 
4660
+ // src/vite/discovery/discovery-errors.ts
4661
+ function indent(text, pad) {
4662
+ return text.split("\n").map((line) => line.length > 0 ? pad + line : line).join("\n");
4663
+ }
4664
+ async function invokeLazyMount(loader, context, errors) {
4665
+ try {
4666
+ await loader();
4667
+ } catch (error) {
4668
+ errors.push({ context, error });
4669
+ }
4670
+ }
4671
+ function isLazyMount(route) {
4672
+ return !!route && route.kind === "lazy" && typeof route.handler === "function";
4673
+ }
4674
+ async function resolveHostRouterHandlers(hostRegistry) {
4675
+ const errors = [];
4676
+ for (const [hostId, entry] of hostRegistry) {
4677
+ for (const route of entry.routes) {
4678
+ if (isLazyMount(route)) {
4679
+ await invokeLazyMount(
4680
+ route.handler,
4681
+ `host "${hostId}" route handler`,
4682
+ errors
4683
+ );
4684
+ }
4685
+ }
4686
+ if (isLazyMount(entry.fallback)) {
4687
+ await invokeLazyMount(
4688
+ entry.fallback.handler,
4689
+ `host "${hostId}" fallback handler`,
4690
+ errors
4691
+ );
4692
+ }
4693
+ }
4694
+ return errors;
4695
+ }
4696
+ function formatNoRoutersError(entryPath, errors) {
4697
+ const base = `[rango] No routers found in registry after importing ${entryPath}`;
4698
+ if (errors.length === 0) {
4699
+ return base;
4700
+ }
4701
+ const formatted = errors.map(({ context, error }) => {
4702
+ const err = error instanceof Error ? error : new Error(String(error));
4703
+ const detail = err.stack ?? err.message;
4704
+ return ` - while resolving ${context}:
4705
+ ${indent(detail, " ")}`;
4706
+ }).join("\n");
4707
+ return `${base}
4708
+
4709
+ ${errors.length} error(s) were caught during host-router discovery and likely explain why no routers were registered:
4710
+ ${formatted}`;
4711
+ }
4712
+ function toCause(errors) {
4713
+ if (errors.length === 0) return void 0;
4714
+ if (errors.length === 1) return errors[0].error;
4715
+ return new AggregateError(
4716
+ errors.map((e) => e.error),
4717
+ "Multiple host-router handlers failed during discovery"
4718
+ );
4719
+ }
4720
+ var DiscoveryError = class _DiscoveryError extends Error {
4721
+ constructor(entryPath, caught) {
4722
+ super(formatNoRoutersError(entryPath, caught));
4723
+ const cause = toCause(caught);
4724
+ if (cause !== void 0) {
4725
+ this.cause = cause;
4726
+ }
4727
+ this.name = "DiscoveryError";
4728
+ this.entryPath = entryPath;
4729
+ this.caught = caught;
4730
+ Object.setPrototypeOf(this, _DiscoveryError.prototype);
4731
+ }
4732
+ };
4733
+
4034
4734
  // src/vite/discovery/discover-routers.ts
4735
+ var debug10 = createRangoDebugger(NS.discovery);
4035
4736
  async function discoverRouters(state, rscEnv) {
4036
4737
  if (!state.resolvedEntryPath) return;
4037
- await rscEnv.runner.import(state.resolvedEntryPath);
4038
- const serverMod = await rscEnv.runner.import("@rangojs/router/server");
4738
+ await timed(
4739
+ debug10,
4740
+ "inner: import entry",
4741
+ () => rscEnv.runner.import(state.resolvedEntryPath)
4742
+ );
4743
+ const serverMod = await timed(
4744
+ debug10,
4745
+ "inner: import @rangojs/router/server",
4746
+ () => rscEnv.runner.import("@rangojs/router/server")
4747
+ );
4039
4748
  let registry = serverMod.RouterRegistry;
4040
4749
  if (!registry || registry.size === 0) {
4750
+ const discoveryErrors = [];
4041
4751
  try {
4042
4752
  const hostRegistry = serverMod.HostRouterRegistry;
4043
4753
  if (hostRegistry && hostRegistry.size > 0) {
4044
4754
  console.log(
4045
- `[rsc-router] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`
4755
+ `[rango] Found ${hostRegistry.size} host router(s), resolving lazy handlers...`
4046
4756
  );
4047
- for (const [, entry] of hostRegistry) {
4048
- for (const route of entry.routes) {
4049
- if (typeof route.handler === "function") {
4050
- try {
4051
- await route.handler();
4052
- } catch {
4053
- }
4054
- }
4055
- }
4056
- if (entry.fallback && typeof entry.fallback.handler === "function") {
4057
- try {
4058
- await entry.fallback.handler();
4059
- } catch {
4060
- }
4061
- }
4757
+ const handlerErrors = await resolveHostRouterHandlers(hostRegistry);
4758
+ discoveryErrors.push(...handlerErrors);
4759
+ for (const { context, error } of handlerErrors) {
4760
+ debug10?.("caught error while resolving %s: %O", context, error);
4062
4761
  }
4063
4762
  const freshServerMod = await rscEnv.runner.import(
4064
4763
  "@rangojs/router/server"
@@ -4069,16 +4768,20 @@ async function discoverRouters(state, rscEnv) {
4069
4768
  registry = freshRegistry;
4070
4769
  }
4071
4770
  }
4072
- } catch {
4771
+ } catch (error) {
4772
+ discoveryErrors.push({ context: "host-router discovery", error });
4073
4773
  }
4074
4774
  if (!registry || registry.size === 0) {
4075
- throw new Error(
4076
- `[rsc-router] No routers found in registry after importing ${state.resolvedEntryPath}`
4077
- );
4775
+ throw new DiscoveryError(state.resolvedEntryPath, discoveryErrors);
4078
4776
  }
4079
4777
  }
4080
- const buildMod = await rscEnv.runner.import("@rangojs/router/build");
4778
+ const buildMod = await timed(
4779
+ debug10,
4780
+ "inner: import @rangojs/router/build",
4781
+ () => rscEnv.runner.import("@rangojs/router/build")
4782
+ );
4081
4783
  const generateManifestFull = buildMod.generateManifestFull;
4784
+ debug10?.("inner: found %d router(s) in registry", registry.size);
4082
4785
  const nestedRouterConflict = findNestedRouterConflict(
4083
4786
  [...registry.values()].map((router) => router.__sourceFile).filter(
4084
4787
  (sourceFile) => typeof sourceFile === "string"
@@ -4097,6 +4800,16 @@ async function discoverRouters(state, rscEnv) {
4097
4800
  let mergedRouteTrailingSlash = {};
4098
4801
  let routerMountIndex = 0;
4099
4802
  const allManifests = [];
4803
+ const clientChunkCtx = state.opts?.clientChunkCtx;
4804
+ const collectClientFallbackRef = clientChunkCtx ? (refKey) => clientChunkCtx.fallbackRefs.add(
4805
+ computeProductionHash(state.projectRoot, refKey)
4806
+ ) : void 0;
4807
+ const collectFromBoundaryNode = (node) => {
4808
+ if (collectClientFallbackRef && buildMod.collectFallbackClientRefs) {
4809
+ buildMod.collectFallbackClientRefs(node, collectClientFallbackRef);
4810
+ }
4811
+ };
4812
+ const manifestGenStart = debug10 ? performance.now() : 0;
4100
4813
  for (const [id, router] of registry) {
4101
4814
  if (!router.urlpatterns || !generateManifestFull) {
4102
4815
  continue;
@@ -4104,10 +4817,18 @@ async function discoverRouters(state, rscEnv) {
4104
4817
  const manifest = generateManifestFull(
4105
4818
  router.urlpatterns,
4106
4819
  routerMountIndex,
4107
- router.__basename ? { urlPrefix: router.__basename } : void 0
4820
+ {
4821
+ ...router.__basename ? { urlPrefix: router.__basename } : {},
4822
+ ...collectClientFallbackRef ? { collectClientFallbackRef } : {}
4823
+ }
4108
4824
  );
4109
4825
  routerMountIndex++;
4110
4826
  allManifests.push({ id, manifest });
4827
+ if (collectClientFallbackRef) {
4828
+ collectFromBoundaryNode(router.__defaultErrorBoundary);
4829
+ collectFromBoundaryNode(router.__defaultNotFoundBoundary);
4830
+ collectFromBoundaryNode(router.__notFound);
4831
+ }
4111
4832
  const routeCount = Object.keys(manifest.routeManifest).length;
4112
4833
  const staticRoutes = Object.values(manifest.routeManifest).filter(
4113
4834
  (p) => !p.includes(":") && !p.includes("*")
@@ -4158,7 +4879,7 @@ async function discoverRouters(state, rscEnv) {
4158
4879
  );
4159
4880
  newPerRouterPrecomputedMap.set(id, routerPrecomputed);
4160
4881
  console.log(
4161
- `[rsc-router] Router "${id}" -> ${routeCount} routes (${staticRoutes} static, ${dynamicRoutes} dynamic)`
4882
+ `[rango] Router "${id}" -> ${routeCount} routes (${staticRoutes} static, ${dynamicRoutes} dynamic)`
4162
4883
  );
4163
4884
  }
4164
4885
  if (registry.size > 1) {
@@ -4167,11 +4888,17 @@ async function discoverRouters(state, rscEnv) {
4167
4888
  );
4168
4889
  if (autoIds.length > 1) {
4169
4890
  console.warn(
4170
- `[rsc-router] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). In multi-router setups, each createRouter() must have an explicit \`id\` option to ensure per-router manifest data is matched correctly at runtime. Example: createRouter({ id: "site", ... })`
4891
+ `[rango] WARNING: ${autoIds.length} routers use auto-generated IDs (${autoIds.join(", ")}). In multi-router setups, each createRouter() must have an explicit \`id\` option to ensure per-router manifest data is matched correctly at runtime. Example: createRouter({ id: "site", ... })`
4171
4892
  );
4172
4893
  }
4173
4894
  }
4895
+ debug10?.(
4896
+ "inner: generated manifests for %d router(s) (%sms)",
4897
+ allManifests.length,
4898
+ (performance.now() - manifestGenStart).toFixed(1)
4899
+ );
4174
4900
  let newMergedRouteTrie = null;
4901
+ const trieStart = debug10 ? performance.now() : 0;
4175
4902
  if (Object.keys(newMergedRouteManifest).length > 0) {
4176
4903
  const buildRouteTrie = buildMod.buildRouteTrie;
4177
4904
  if (buildRouteTrie && mergedRouteAncestry) {
@@ -4206,34 +4933,24 @@ async function discoverRouters(state, rscEnv) {
4206
4933
  newMergedRouteManifest,
4207
4934
  mergedRouteAncestry,
4208
4935
  routeToStaticPrefix,
4209
- Object.keys(mergedRouteTrailingSlash).length > 0 ? mergedRouteTrailingSlash : void 0,
4210
- prerenderRouteNames.size > 0 ? prerenderRouteNames : void 0,
4211
- passthroughRouteNames.size > 0 ? passthroughRouteNames : void 0,
4212
- Object.keys(mergedResponseTypeRoutes).length > 0 ? mergedResponseTypeRoutes : void 0
4936
+ mergedRouteTrailingSlash,
4937
+ prerenderRouteNames,
4938
+ passthroughRouteNames,
4939
+ mergedResponseTypeRoutes
4213
4940
  );
4941
+ const buildPerRouterTrie = buildMod.buildPerRouterTrie;
4214
4942
  for (const { id, manifest } of allManifests) {
4215
- if (!manifest._routeAncestry || Object.keys(manifest._routeAncestry).length === 0)
4216
- continue;
4217
- const perRouterStaticPrefix = {};
4218
- for (const name of Object.keys(manifest.routeManifest)) {
4219
- perRouterStaticPrefix[name] = "";
4943
+ const perRouterTrie = buildPerRouterTrie ? buildPerRouterTrie(manifest) : null;
4944
+ if (perRouterTrie) {
4945
+ newPerRouterTrieMap.set(id, perRouterTrie);
4220
4946
  }
4221
- buildRouteToStaticPrefix(manifest.prefixTree, perRouterStaticPrefix);
4222
- const perRouterPrerenderNames = manifest.prerenderRoutes ? new Set(manifest.prerenderRoutes) : void 0;
4223
- const perRouterPassthroughNames = manifest.passthroughRoutes ? new Set(manifest.passthroughRoutes) : void 0;
4224
- const perRouterTrie = buildRouteTrie(
4225
- manifest.routeManifest,
4226
- manifest._routeAncestry,
4227
- perRouterStaticPrefix,
4228
- manifest.routeTrailingSlash && Object.keys(manifest.routeTrailingSlash).length > 0 ? manifest.routeTrailingSlash : void 0,
4229
- perRouterPrerenderNames && perRouterPrerenderNames.size > 0 ? perRouterPrerenderNames : void 0,
4230
- perRouterPassthroughNames && perRouterPassthroughNames.size > 0 ? perRouterPassthroughNames : void 0,
4231
- manifest.responseTypeRoutes && Object.keys(manifest.responseTypeRoutes).length > 0 ? manifest.responseTypeRoutes : void 0
4232
- );
4233
- newPerRouterTrieMap.set(id, perRouterTrie);
4234
4947
  }
4235
4948
  }
4236
4949
  }
4950
+ debug10?.(
4951
+ "inner: trie build done (%sms)",
4952
+ (performance.now() - trieStart).toFixed(1)
4953
+ );
4237
4954
  state.mergedRouteManifest = newMergedRouteManifest;
4238
4955
  state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
4239
4956
  state.perRouterManifests = newPerRouterManifests;
@@ -4247,7 +4964,7 @@ async function discoverRouters(state, rscEnv) {
4247
4964
  }
4248
4965
 
4249
4966
  // src/vite/discovery/route-types-writer.ts
4250
- import { dirname as dirname3, basename, join as join2, resolve as resolve6 } from "node:path";
4967
+ import { dirname as dirname3, join as join2, resolve as resolve6 } from "node:path";
4251
4968
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, unlinkSync as unlinkSync2 } from "node:fs";
4252
4969
  function filterUserNamedRoutes(manifest) {
4253
4970
  const filtered = {};
@@ -4258,39 +4975,20 @@ function filterUserNamedRoutes(manifest) {
4258
4975
  }
4259
4976
  return filtered;
4260
4977
  }
4978
+ function writeGenFileIfChanged(state, outPath, source, opts) {
4979
+ const existing = existsSync5(outPath) ? readFileSync4(outPath, "utf-8") : null;
4980
+ if (existing === source) return;
4981
+ markSelfGenWrite(state, outPath, source);
4982
+ writeFileSync3(outPath, source);
4983
+ if (opts?.log) console.log(`[rango] Generated route types -> ${outPath}`);
4984
+ }
4261
4985
  function writeCombinedRouteTypesWithTracking(state, opts) {
4262
4986
  const routerFiles = state.cachedRouterFiles ?? findRouterFiles(state.projectRoot, state.scanFilter);
4263
4987
  state.cachedRouterFiles = routerFiles;
4264
- const preContent = /* @__PURE__ */ new Map();
4265
- for (const routerFilePath of routerFiles) {
4266
- const routerDir = dirname3(routerFilePath);
4267
- const routerBasename = basename(routerFilePath).replace(
4268
- /\.(tsx?|jsx?)$/,
4269
- ""
4270
- );
4271
- const outPath = join2(routerDir, `${routerBasename}.named-routes.gen.ts`);
4272
- try {
4273
- preContent.set(outPath, readFileSync4(outPath, "utf-8"));
4274
- } catch {
4275
- }
4276
- }
4277
- writeCombinedRouteTypes(state.projectRoot, routerFiles, opts);
4278
- for (const routerFilePath of routerFiles) {
4279
- const routerDir = dirname3(routerFilePath);
4280
- const routerBasename = basename(routerFilePath).replace(
4281
- /\.(tsx?|jsx?)$/,
4282
- ""
4283
- );
4284
- const outPath = join2(routerDir, `${routerBasename}.named-routes.gen.ts`);
4285
- if (!existsSync5(outPath)) continue;
4286
- try {
4287
- const content = readFileSync4(outPath, "utf-8");
4288
- if (content !== preContent.get(outPath)) {
4289
- markSelfGenWrite(state, outPath, content);
4290
- }
4291
- } catch {
4292
- }
4293
- }
4988
+ writeCombinedRouteTypes(state.projectRoot, routerFiles, {
4989
+ ...opts,
4990
+ onWrite: (outPath, content) => markSelfGenWrite(state, outPath, content)
4991
+ });
4294
4992
  }
4295
4993
  function writeRouteTypesFiles(state) {
4296
4994
  if (state.perRouterManifests.length === 0) return;
@@ -4302,7 +5000,7 @@ function writeRouteTypesFiles(state) {
4302
5000
  if (existsSync5(oldCombinedPath)) {
4303
5001
  unlinkSync2(oldCombinedPath);
4304
5002
  console.log(
4305
- `[rsc-router] Removed stale combined route types: ${oldCombinedPath}`
5003
+ `[rango] Removed stale combined route types: ${oldCombinedPath}`
4306
5004
  );
4307
5005
  }
4308
5006
  } catch {
@@ -4316,39 +5014,23 @@ function writeRouteTypesFiles(state) {
4316
5014
  if (!sourceFile) continue;
4317
5015
  if (sourceFile.includes("node_modules")) {
4318
5016
  throw new Error(
4319
- `[rsc-router] Router "${id}" has sourceFile inside node_modules: ${sourceFile}
5017
+ `[rango] Router "${id}" has sourceFile inside node_modules: ${sourceFile}
4320
5018
  This means createRouter() stack trace parsing matched a Vite internal frame.
4321
5019
  Set an explicit \`id\` on createRouter() or check the call site.`
4322
5020
  );
4323
5021
  }
4324
- const routerDir = dirname3(sourceFile);
4325
- const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
4326
- const outPath = join2(routerDir, `${routerBasename}.named-routes.gen.ts`);
5022
+ const outPath = genFileTsPath(sourceFile);
4327
5023
  const userRoutes = filterUserNamedRoutes(routeManifest);
4328
- let effectiveSearchSchemas = routeSearchSchemas;
4329
- if ((!effectiveSearchSchemas || Object.keys(effectiveSearchSchemas).length === 0) && sourceFile) {
4330
- const staticParsed = buildCombinedRouteMapForRouterFile(sourceFile);
4331
- if (Object.keys(staticParsed.searchSchemas).length > 0) {
4332
- const filtered = {};
4333
- for (const name of Object.keys(userRoutes)) {
4334
- const schema = staticParsed.searchSchemas[name];
4335
- if (schema) filtered[name] = schema;
4336
- }
4337
- if (Object.keys(filtered).length > 0) {
4338
- effectiveSearchSchemas = filtered;
4339
- }
4340
- }
4341
- }
5024
+ const effectiveSearchSchemas = resolveSearchSchemas(
5025
+ Object.keys(userRoutes),
5026
+ routeSearchSchemas,
5027
+ sourceFile
5028
+ );
4342
5029
  const source = generateRouteTypesSource(
4343
5030
  userRoutes,
4344
5031
  effectiveSearchSchemas && Object.keys(effectiveSearchSchemas).length > 0 ? effectiveSearchSchemas : void 0
4345
5032
  );
4346
- const existing = existsSync5(outPath) ? readFileSync4(outPath, "utf-8") : null;
4347
- if (existing !== source) {
4348
- markSelfGenWrite(state, outPath, source);
4349
- writeFileSync3(outPath, source);
4350
- console.log(`[rsc-router] Generated route types -> ${outPath}`);
4351
- }
5033
+ writeGenFileIfChanged(state, outPath, source, { log: true });
4352
5034
  }
4353
5035
  }
4354
5036
  function supplementGenFilesWithRuntimeRoutes(state) {
@@ -4386,23 +5068,17 @@ function supplementGenFilesWithRuntimeRoutes(state) {
4386
5068
  }
4387
5069
  }
4388
5070
  }
4389
- const routerDir = dirname3(sourceFile);
4390
- const routerBasename = basename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
4391
- const outPath = join2(routerDir, `${routerBasename}.named-routes.gen.ts`);
5071
+ const outPath = genFileTsPath(sourceFile);
4392
5072
  const source = generateRouteTypesSource(
4393
5073
  mergedRoutes,
4394
5074
  Object.keys(mergedSearchSchemas).length > 0 ? mergedSearchSchemas : void 0
4395
5075
  );
4396
- const existing = existsSync5(outPath) ? readFileSync4(outPath, "utf-8") : null;
4397
- if (existing !== source) {
4398
- markSelfGenWrite(state, outPath, source);
4399
- writeFileSync3(outPath, source);
4400
- }
5076
+ writeGenFileIfChanged(state, outPath, source);
4401
5077
  }
4402
5078
  }
4403
5079
 
4404
5080
  // src/vite/discovery/virtual-module-codegen.ts
4405
- import { dirname as dirname4, basename as basename2, join as join3 } from "node:path";
5081
+ import { dirname as dirname4, basename, join as join3 } from "node:path";
4406
5082
  function generateRoutesManifestModule(state) {
4407
5083
  const hasManifest = state.mergedRouteManifest && Object.keys(state.mergedRouteManifest).length > 0;
4408
5084
  if (hasManifest) {
@@ -4413,7 +5089,7 @@ function generateRoutesManifestModule(state) {
4413
5089
  for (const entry of state.perRouterManifests) {
4414
5090
  if (entry.sourceFile) {
4415
5091
  const routerDir = dirname4(entry.sourceFile);
4416
- const routerBasename = basename2(entry.sourceFile).replace(
5092
+ const routerBasename = basename(entry.sourceFile).replace(
4417
5093
  /\.(tsx?|jsx?)$/,
4418
5094
  ""
4419
5095
  );
@@ -4434,7 +5110,7 @@ function generateRoutesManifestModule(state) {
4434
5110
  }
4435
5111
  }
4436
5112
  const lines = [
4437
- `import { setCachedManifest, setPrecomputedEntries, setRouteTrie, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
5113
+ `import { setCachedManifest, setRouterManifest, registerRouterManifestLoader, clearAllRouterData } from "@rangojs/router/server";`,
4438
5114
  ...genFileImports,
4439
5115
  // Clear stale per-router cached data (manifest, trie, precomputed entries)
4440
5116
  // before re-populating. In Cloudflare dev mode, program reloads re-evaluate
@@ -4470,18 +5146,6 @@ function generateRoutesManifestModule(state) {
4470
5146
  );
4471
5147
  }
4472
5148
  }
4473
- if (state.isBuildMode) {
4474
- if (state.mergedPrecomputedEntries && state.mergedPrecomputedEntries.length > 0) {
4475
- lines.push(
4476
- `setPrecomputedEntries(${jsonParseExpression(state.mergedPrecomputedEntries)});`
4477
- );
4478
- }
4479
- if (state.mergedRouteTrie) {
4480
- lines.push(
4481
- `setRouteTrie(${jsonParseExpression(state.mergedRouteTrie)});`
4482
- );
4483
- }
4484
- }
4485
5149
  for (const routerId of state.perRouterManifestDataMap.keys()) {
4486
5150
  lines.push(
4487
5151
  `registerRouterManifestLoader(${JSON.stringify(routerId)}, () => import(${JSON.stringify(VIRTUAL_ROUTES_MANIFEST_ID + "/" + routerId)}));`
@@ -4510,7 +5174,7 @@ function generatePerRouterModule(state, routerId) {
4510
5174
  const lines = [];
4511
5175
  if (routerEntry?.sourceFile) {
4512
5176
  const routerDir = dirname4(routerEntry.sourceFile);
4513
- const routerBasename = basename2(routerEntry.sourceFile).replace(
5177
+ const routerBasename = basename(routerEntry.sourceFile).replace(
4514
5178
  /\.(tsx?|jsx?)$/,
4515
5179
  ""
4516
5180
  );
@@ -4581,12 +5245,12 @@ function postprocessBundle(state) {
4581
5245
  writeFileSync4(chunkPath, result.code);
4582
5246
  const savedKB = (result.savedBytes / 1024).toFixed(1);
4583
5247
  console.log(
4584
- `[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`
5248
+ `[rango] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`
4585
5249
  );
4586
5250
  }
4587
5251
  } catch (replaceErr) {
4588
5252
  console.warn(
4589
- `[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`
5253
+ `[rango] Failed to evict ${target.label}: ${replaceErr.message}`
4590
5254
  );
4591
5255
  }
4592
5256
  }
@@ -4624,11 +5288,11 @@ function postprocessBundle(state) {
4624
5288
  writeFileSync4(rscEntryPath, injection + rscCode);
4625
5289
  const totalKB = (totalBytes / 1024).toFixed(1);
4626
5290
  console.log(
4627
- `[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries).length} entries)`
5291
+ `[rango] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries).length} entries)`
4628
5292
  );
4629
5293
  } catch (err) {
4630
5294
  throw new Error(
4631
- `[rsc-router] Failed to write prerender assets: ${err.message}`
5295
+ `[rango] Failed to write prerender assets: ${err.message}`
4632
5296
  );
4633
5297
  }
4634
5298
  }
@@ -4662,28 +5326,180 @@ function postprocessBundle(state) {
4662
5326
  writeFileSync4(rscEntryPath, injection + rscCode);
4663
5327
  const totalKB = (totalBytes / 1024).toFixed(1);
4664
5328
  console.log(
4665
- `[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries).length} entries)`
5329
+ `[rango] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries).length} entries)`
4666
5330
  );
4667
5331
  } catch (err) {
4668
5332
  throw new Error(
4669
- `[rsc-router] Failed to write static assets: ${err.message}`
5333
+ `[rango] Failed to write static assets: ${err.message}`
4670
5334
  );
4671
5335
  }
4672
5336
  }
4673
5337
  }
4674
5338
  }
4675
5339
 
5340
+ // src/vite/discovery/gate-state.ts
5341
+ function createDiscoveryGate(s, debug11) {
5342
+ let gatePending = false;
5343
+ let gateResolver = () => {
5344
+ };
5345
+ let inProgress = false;
5346
+ let queued = false;
5347
+ let pendingEvents = false;
5348
+ const beginGate = () => {
5349
+ if (gatePending) return;
5350
+ s.discoveryDone = new Promise((resolve10) => {
5351
+ gateResolver = resolve10;
5352
+ });
5353
+ gatePending = true;
5354
+ };
5355
+ const resolveGate = () => {
5356
+ if (!gatePending) return;
5357
+ if (inProgress || queued || pendingEvents) {
5358
+ debug11?.(
5359
+ "hmr: resolveGate deferred \u2014 work in flight (inProgress=%s queued=%s pendingEvents=%s)",
5360
+ inProgress,
5361
+ queued,
5362
+ pendingEvents
5363
+ );
5364
+ return;
5365
+ }
5366
+ gatePending = false;
5367
+ debug11?.("hmr: discoveryDone resolved");
5368
+ gateResolver();
5369
+ };
5370
+ const noteRouteEvent = () => {
5371
+ pendingEvents = true;
5372
+ beginGate();
5373
+ };
5374
+ const runRefreshCycle = async (work) => {
5375
+ if (inProgress) {
5376
+ queued = true;
5377
+ debug11?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
5378
+ return;
5379
+ }
5380
+ pendingEvents = false;
5381
+ inProgress = true;
5382
+ try {
5383
+ await work();
5384
+ } finally {
5385
+ inProgress = false;
5386
+ if (queued) {
5387
+ queued = false;
5388
+ debug11?.("hmr: consuming queued rediscovery");
5389
+ runRefreshCycle(work).catch((err) => {
5390
+ debug11?.(
5391
+ "hmr: queued cycle rejected \u2014 releasing gate (%s)",
5392
+ err instanceof Error ? err.message : String(err)
5393
+ );
5394
+ resolveGate();
5395
+ });
5396
+ } else if (pendingEvents) {
5397
+ debug11?.(
5398
+ "hmr: holding gate for pending events (debounce not yet fired)"
5399
+ );
5400
+ } else {
5401
+ resolveGate();
5402
+ }
5403
+ }
5404
+ };
5405
+ return {
5406
+ beginGate,
5407
+ resolveGate,
5408
+ noteRouteEvent,
5409
+ runRefreshCycle,
5410
+ state: () => ({ gatePending, inProgress, queued, pendingEvents })
5411
+ };
5412
+ }
5413
+
5414
+ // src/vite/utils/forward-user-plugins.ts
5415
+ function isDenied(name) {
5416
+ return name.startsWith("vite:") || name === "rsc" || name.startsWith("rsc:") || name.startsWith("@rangojs/router") || name.startsWith("@cloudflare/vite-plugin") || name.startsWith("vite-plugin-cloudflare");
5417
+ }
5418
+ function hasResolutionHooks(p) {
5419
+ return Boolean(p.resolveId || p.load);
5420
+ }
5421
+ function stripToResolutionHooks(p) {
5422
+ const stripped = { name: p.name };
5423
+ if (p.enforce) stripped.enforce = p.enforce;
5424
+ if (p.applyToEnvironment)
5425
+ stripped.applyToEnvironment = p.applyToEnvironment;
5426
+ if (p.resolveId) stripped.resolveId = p.resolveId;
5427
+ if (p.load) stripped.load = p.load;
5428
+ return stripped;
5429
+ }
5430
+ function selectForwardableResolvePlugins(plugins) {
5431
+ if (!plugins) return [];
5432
+ const forwarded = [];
5433
+ for (const p of plugins) {
5434
+ const name = p?.name;
5435
+ if (!name || isDenied(name)) continue;
5436
+ if (!hasResolutionHooks(p)) continue;
5437
+ forwarded.push(stripToResolutionHooks(p));
5438
+ }
5439
+ return forwarded;
5440
+ }
5441
+ function pickForwardedRunnerConfig(config) {
5442
+ const r = config.resolve ?? {};
5443
+ const resolve10 = {};
5444
+ if (r.alias !== void 0) resolve10.alias = r.alias;
5445
+ if (r.dedupe !== void 0) resolve10.dedupe = r.dedupe;
5446
+ if (r.conditions !== void 0) resolve10.conditions = r.conditions;
5447
+ if (r.mainFields !== void 0) resolve10.mainFields = r.mainFields;
5448
+ if (r.extensions !== void 0) resolve10.extensions = r.extensions;
5449
+ if (r.preserveSymlinks !== void 0)
5450
+ resolve10.preserveSymlinks = r.preserveSymlinks;
5451
+ if (r.tsconfigPaths !== void 0) resolve10.tsconfigPaths = r.tsconfigPaths;
5452
+ const userOxc = config.oxc;
5453
+ const userJsx = userOxc && typeof userOxc === "object" && typeof userOxc.jsx === "object" && userOxc.jsx !== null ? userOxc.jsx : {};
5454
+ const oxc = userOxc && typeof userOxc === "object" ? {
5455
+ ...userOxc,
5456
+ jsx: { ...userJsx, runtime: "automatic", importSource: "react" }
5457
+ } : { jsx: { runtime: "automatic", importSource: "react" } };
5458
+ return {
5459
+ resolve: resolve10,
5460
+ define: config.define,
5461
+ oxc
5462
+ };
5463
+ }
5464
+
4676
5465
  // src/vite/router-discovery.ts
5466
+ var debugDiscovery = createRangoDebugger(NS.discovery);
5467
+ var debugRoutes = createRangoDebugger(NS.routes);
5468
+ var debugBuild = createRangoDebugger(NS.build);
5469
+ var debugDev = createRangoDebugger(NS.dev);
5470
+ var loaderHookRegistered = false;
5471
+ function ensureCloudflareProtocolLoaderRegistered() {
5472
+ if (loaderHookRegistered) return;
5473
+ loaderHookRegistered = true;
5474
+ try {
5475
+ register(
5476
+ new URL("./plugins/cloudflare-protocol-loader-hook.mjs", import.meta.url)
5477
+ );
5478
+ } catch (err) {
5479
+ console.warn(
5480
+ `[rango] Could not register Node ESM loader hook for cloudflare:* imports (${err?.message ?? err}). Falling back to Vite transform only.`
5481
+ );
5482
+ }
5483
+ }
4677
5484
  async function createTempRscServer(state, options = {}) {
5485
+ ensureCloudflareProtocolLoaderRegistered();
4678
5486
  const { default: rsc } = await import("@vitejs/plugin-rsc");
5487
+ const runnerConfig = state.userRunnerConfig;
5488
+ const resolveConfig = runnerConfig?.resolve ?? {
5489
+ alias: state.userResolveAlias
5490
+ };
5491
+ const oxcConfig = runnerConfig?.oxc ?? {
5492
+ jsx: { runtime: "automatic", importSource: "react" }
5493
+ };
4679
5494
  return createViteServer({
4680
5495
  root: state.projectRoot,
4681
5496
  configFile: false,
4682
5497
  server: { middlewareMode: true },
4683
5498
  appType: "custom",
4684
5499
  logLevel: "silent",
4685
- resolve: { alias: state.userResolveAlias },
4686
- esbuild: { jsx: "automatic", jsxImportSource: "react" },
5500
+ resolve: resolveConfig,
5501
+ ...runnerConfig?.define ? { define: runnerConfig.define } : {},
5502
+ oxc: oxcConfig,
4687
5503
  ...options.cacheDir && { cacheDir: options.cacheDir },
4688
5504
  plugins: [
4689
5505
  rsc({
@@ -4697,10 +5513,15 @@ async function createTempRscServer(state, options = {}) {
4697
5513
  ...options.forceBuild ? [hashClientRefs(state.projectRoot)] : [],
4698
5514
  createVersionPlugin(),
4699
5515
  createVirtualStubPlugin(),
5516
+ createCloudflareProtocolStubPlugin(),
4700
5517
  // Dev prerender must use dev-mode IDs (path-based) to match the workerd
4701
5518
  // runtime. forceBuild produces hashed IDs for production bundle consistency.
4702
5519
  exposeInternalIds(options.forceBuild ? { forceBuild: true } : void 0),
4703
- exposeRouterId()
5520
+ exposeRouterId(),
5521
+ // Forwarded user resolution plugins (e.g. vite-tsconfig-paths). Stripped
5522
+ // to resolveId/load and placed last so framework resolution runs first;
5523
+ // Vite re-sorts by `enforce`, so `enforce: "pre"` resolvers still lead.
5524
+ ...state.userResolvePlugins
4704
5525
  ]
4705
5526
  });
4706
5527
  }
@@ -4709,11 +5530,11 @@ async function resolveBuildEnv(option, factoryCtx) {
4709
5530
  if (option === "auto") {
4710
5531
  if (factoryCtx.preset !== "cloudflare") {
4711
5532
  throw new Error(
4712
- '[rsc-router] buildEnv: "auto" is only supported with preset: "cloudflare". Use a factory function or plain object for other presets.'
5533
+ '[rango] buildEnv: "auto" is only supported with preset: "cloudflare". Use a factory function or plain object for other presets.'
4713
5534
  );
4714
5535
  }
4715
5536
  try {
4716
- const userRequire = createRequire(
5537
+ const userRequire = createRequire2(
4717
5538
  resolve8(factoryCtx.root, "package.json")
4718
5539
  );
4719
5540
  const wranglerPath = userRequire.resolve("wrangler");
@@ -4725,7 +5546,7 @@ async function resolveBuildEnv(option, factoryCtx) {
4725
5546
  };
4726
5547
  } catch (err) {
4727
5548
  throw new Error(
4728
- `[rsc-router] buildEnv: "auto" requires wrangler to be installed.
5549
+ `[rango] buildEnv: "auto" requires wrangler to be installed.
4729
5550
  Install it with: pnpm add -D wrangler
4730
5551
  ${err.message}`
4731
5552
  );
@@ -4748,6 +5569,7 @@ async function acquireBuildEnv(s, command, mode) {
4748
5569
  if (!result) return false;
4749
5570
  s.resolvedBuildEnv = result.env;
4750
5571
  s.buildEnvDispose = result.dispose ?? null;
5572
+ globalThis[BUILD_ENV_GLOBAL_KEY] = result.env;
4751
5573
  return true;
4752
5574
  }
4753
5575
  async function releaseBuildEnv(s) {
@@ -4755,11 +5577,12 @@ async function releaseBuildEnv(s) {
4755
5577
  try {
4756
5578
  await s.buildEnvDispose();
4757
5579
  } catch (err) {
4758
- console.warn(`[rsc-router] buildEnv dispose failed: ${err.message}`);
5580
+ console.warn(`[rango] buildEnv dispose failed: ${err.message}`);
4759
5581
  }
4760
5582
  s.buildEnvDispose = null;
4761
5583
  }
4762
5584
  s.resolvedBuildEnv = void 0;
5585
+ delete globalThis[BUILD_ENV_GLOBAL_KEY];
4763
5586
  }
4764
5587
  function createRouterDiscoveryPlugin(entryPath, opts) {
4765
5588
  const s = createDiscoveryState(entryPath, opts);
@@ -4781,17 +5604,16 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4781
5604
  viteCommand = config.command;
4782
5605
  viteMode = config.mode;
4783
5606
  s.userResolveAlias = config.resolve.alias;
5607
+ s.userRunnerConfig = pickForwardedRunnerConfig(config);
5608
+ s.userResolvePlugins = selectForwardableResolvePlugins(
5609
+ config.plugins
5610
+ );
4784
5611
  if (!s.resolvedEntryPath && opts?.routerPathRef?.path) {
4785
5612
  s.resolvedEntryPath = opts.routerPathRef.path;
4786
5613
  }
4787
5614
  if (!s.resolvedEntryPath) {
4788
- const rscEnvConfig = config.environments?.["rsc"];
4789
- const entries = rscEnvConfig?.optimizeDeps?.entries;
4790
- if (typeof entries === "string") {
4791
- s.resolvedEntryPath = entries;
4792
- } else if (Array.isArray(entries) && entries.length > 0) {
4793
- s.resolvedEntryPath = entries[0];
4794
- }
5615
+ const entry = resolveRscEntryFromConfig(config);
5616
+ if (entry) s.resolvedEntryPath = entry;
4795
5617
  }
4796
5618
  if (opts?.staticRouteTypesGeneration !== false) {
4797
5619
  s.cachedRouterFiles = findRouterFiles(s.projectRoot, s.scanFilter);
@@ -4815,6 +5637,9 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4815
5637
  const discoveryPromise = new Promise((resolve10) => {
4816
5638
  resolveDiscovery = resolve10;
4817
5639
  });
5640
+ const gate = createDiscoveryGate(s, debugDiscovery);
5641
+ const beginDiscoveryGate = gate.beginGate;
5642
+ const resolveDiscoveryGate = gate.resolveGate;
4818
5643
  const getDevServerOrigin = () => server.resolvedUrls?.local?.[0]?.replace(/\/$/, "") || `http://localhost:${server.config.server.port || 5173}`;
4819
5644
  let prerenderTempServer = null;
4820
5645
  let prerenderNodeRegistry = null;
@@ -4827,74 +5652,241 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4827
5652
  releaseBuildEnv(s).catch(() => {
4828
5653
  });
4829
5654
  });
5655
+ async function importEntryAndRegistry(tempRscEnv) {
5656
+ const flagAlreadySet = !!globalThis.__rscRouterDiscoveryActive;
5657
+ if (!flagAlreadySet) {
5658
+ globalThis.__rscRouterDiscoveryActive = true;
5659
+ }
5660
+ try {
5661
+ debugDiscovery?.(
5662
+ "importEntryAndRegistry: importing entry (flag=%s)",
5663
+ globalThis.__rscRouterDiscoveryActive ?? false
5664
+ );
5665
+ await tempRscEnv.runner.import(s.resolvedEntryPath);
5666
+ debugDiscovery?.(
5667
+ "importEntryAndRegistry: entry import OK, fetching RouterRegistry"
5668
+ );
5669
+ const serverMod = await tempRscEnv.runner.import(
5670
+ "@rangojs/router/server"
5671
+ );
5672
+ prerenderNodeRegistry = serverMod.RouterRegistry;
5673
+ debugDiscovery?.(
5674
+ "importEntryAndRegistry: registry size=%d",
5675
+ prerenderNodeRegistry?.size ?? 0
5676
+ );
5677
+ } finally {
5678
+ if (!flagAlreadySet) {
5679
+ delete globalThis.__rscRouterDiscoveryActive;
5680
+ debugDiscovery?.(
5681
+ "importEntryAndRegistry: cleared __rscRouterDiscoveryActive"
5682
+ );
5683
+ }
5684
+ }
5685
+ }
4830
5686
  async function getOrCreateTempServer() {
4831
- if (prerenderNodeRegistry) {
4832
- return prerenderTempServer.environments?.rsc ?? null;
5687
+ if (prerenderTempServer) {
5688
+ const existingEnv = prerenderTempServer.environments?.rsc;
5689
+ if (existingEnv?.runner) {
5690
+ if (prerenderNodeRegistry) {
5691
+ debugDiscovery?.(
5692
+ "getOrCreateTempServer: cached temp runner reused"
5693
+ );
5694
+ return existingEnv;
5695
+ }
5696
+ debugDiscovery?.(
5697
+ "getOrCreateTempServer: server alive but registry missing \u2014 re-importing"
5698
+ );
5699
+ try {
5700
+ await importEntryAndRegistry(existingEnv);
5701
+ return existingEnv;
5702
+ } catch (err) {
5703
+ debugDiscovery?.(
5704
+ "getOrCreateTempServer: reuse import failed (%s) \u2014 closing orphan and creating fresh",
5705
+ err?.message ?? String(err)
5706
+ );
5707
+ await prerenderTempServer.close().catch(() => {
5708
+ });
5709
+ prerenderTempServer = null;
5710
+ prerenderNodeRegistry = null;
5711
+ }
5712
+ } else {
5713
+ debugDiscovery?.(
5714
+ "getOrCreateTempServer: existing server has no rsc.runner \u2014 closing and recreating"
5715
+ );
5716
+ await prerenderTempServer.close().catch(() => {
5717
+ });
5718
+ prerenderTempServer = null;
5719
+ prerenderNodeRegistry = null;
5720
+ }
4833
5721
  }
5722
+ debugDiscovery?.(
5723
+ "getOrCreateTempServer: creating new temp server, entry=%s",
5724
+ s.resolvedEntryPath ?? "(unset)"
5725
+ );
4834
5726
  try {
4835
5727
  prerenderTempServer = await createTempRscServer(s, {
4836
5728
  cacheDir: "node_modules/.vite_prerender"
4837
5729
  });
4838
5730
  const tempRscEnv = prerenderTempServer.environments?.rsc;
4839
5731
  if (tempRscEnv?.runner) {
4840
- await tempRscEnv.runner.import(s.resolvedEntryPath);
4841
- const serverMod = await tempRscEnv.runner.import(
4842
- "@rangojs/router/server"
4843
- );
4844
- prerenderNodeRegistry = serverMod.RouterRegistry;
5732
+ await importEntryAndRegistry(tempRscEnv);
4845
5733
  return tempRscEnv;
4846
5734
  }
5735
+ debugDiscovery?.(
5736
+ "getOrCreateTempServer: tempRscEnv.runner unavailable"
5737
+ );
4847
5738
  } catch (err) {
4848
- console.warn(
4849
- `[rsc-router] Failed to create temp runner: ${err.message}`
5739
+ debugDiscovery?.(
5740
+ "getOrCreateTempServer: FAILED message=%s",
5741
+ err.message
4850
5742
  );
5743
+ console.warn(`[rango] Failed to create temp runner: ${err.message}`);
4851
5744
  }
4852
5745
  return null;
4853
5746
  }
5747
+ async function clearTempRegistries(tempRscEnv) {
5748
+ try {
5749
+ const serverMod = await tempRscEnv.runner.import(
5750
+ "@rangojs/router/server"
5751
+ );
5752
+ if (typeof serverMod?.RouterRegistry?.clear === "function") {
5753
+ serverMod.RouterRegistry.clear();
5754
+ }
5755
+ if (typeof serverMod?.HostRouterRegistry?.clear === "function") {
5756
+ serverMod.HostRouterRegistry.clear();
5757
+ }
5758
+ debugDiscovery?.(
5759
+ "clearTempRegistries: cleared RouterRegistry + HostRouterRegistry"
5760
+ );
5761
+ } catch (err) {
5762
+ debugDiscovery?.(
5763
+ "clearTempRegistries: import @rangojs/router/server failed (%s)",
5764
+ err?.message ?? String(err)
5765
+ );
5766
+ }
5767
+ }
5768
+ async function refreshTempRscEnv() {
5769
+ let tempRscEnv = await getOrCreateTempServer();
5770
+ if (!tempRscEnv) return null;
5771
+ const envGraph = tempRscEnv.moduleGraph;
5772
+ const serverGraph = prerenderTempServer?.moduleGraph;
5773
+ const target = envGraph?.invalidateAll ? envGraph : serverGraph?.invalidateAll ? serverGraph : null;
5774
+ if (!target) {
5775
+ debugDiscovery?.(
5776
+ "refreshTempRscEnv: invalidateAll unavailable on env+server graphs, falling back to close+recreate"
5777
+ );
5778
+ if (prerenderTempServer) {
5779
+ await prerenderTempServer.close().catch(() => {
5780
+ });
5781
+ prerenderTempServer = null;
5782
+ prerenderNodeRegistry = null;
5783
+ }
5784
+ return await getOrCreateTempServer();
5785
+ }
5786
+ debugDiscovery?.(
5787
+ "refreshTempRscEnv: invalidating module graph (%s)",
5788
+ envGraph?.invalidateAll ? "env" : "server"
5789
+ );
5790
+ target.invalidateAll();
5791
+ prerenderNodeRegistry = null;
5792
+ await clearTempRegistries(tempRscEnv);
5793
+ await importEntryAndRegistry(tempRscEnv);
5794
+ return tempRscEnv;
5795
+ }
4854
5796
  const discover = async () => {
5797
+ const discoverStart = performance.now();
4855
5798
  const rscEnv = server.environments?.rsc;
4856
5799
  if (!rscEnv?.runner) {
5800
+ debugDiscovery?.(
5801
+ "dev: cloudflare path start, __rscRouterDiscoveryActive=%s",
5802
+ globalThis.__rscRouterDiscoveryActive ?? false
5803
+ );
4857
5804
  s.devServerOrigin = getDevServerOrigin();
4858
5805
  try {
4859
- await acquireBuildEnv(s, viteCommand, viteMode);
4860
- const tempRscEnv = await getOrCreateTempServer();
5806
+ await timed(
5807
+ debugDiscovery,
5808
+ "acquireBuildEnv",
5809
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5810
+ );
5811
+ const tempRscEnv = await timed(
5812
+ debugDiscovery,
5813
+ "getOrCreateTempServer",
5814
+ () => getOrCreateTempServer()
5815
+ );
4861
5816
  if (tempRscEnv) {
4862
- await discoverRouters(s, tempRscEnv);
4863
- writeRouteTypesFiles(s);
5817
+ await timed(
5818
+ debugDiscovery,
5819
+ "discoverRouters (cloudflare)",
5820
+ () => discoverRouters(s, tempRscEnv)
5821
+ );
5822
+ timedSync(
5823
+ debugDiscovery,
5824
+ "writeRouteTypesFiles",
5825
+ () => writeRouteTypesFiles(s)
5826
+ );
4864
5827
  }
4865
5828
  } catch (err) {
4866
5829
  console.warn(
4867
- `[rsc-router] Cloudflare dev discovery failed: ${err.message}
5830
+ `[rango] Cloudflare dev discovery failed: ${err.message}
4868
5831
  ${err.stack}`
4869
5832
  );
4870
5833
  }
5834
+ debugDiscovery?.(
5835
+ "dev discovery done (%sms)",
5836
+ (performance.now() - discoverStart).toFixed(1)
5837
+ );
4871
5838
  resolveDiscovery();
4872
5839
  return;
4873
5840
  }
4874
5841
  try {
4875
- await acquireBuildEnv(s, viteCommand, viteMode);
4876
- const serverMod = await rscEnv.runner.import(
4877
- "@rangojs/router/server"
5842
+ debugDiscovery?.("dev: node path start");
5843
+ await timed(
5844
+ debugDiscovery,
5845
+ "acquireBuildEnv",
5846
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5847
+ );
5848
+ const serverMod = await timed(
5849
+ debugDiscovery,
5850
+ "import @rangojs/router/server",
5851
+ () => rscEnv.runner.import("@rangojs/router/server")
4878
5852
  );
4879
5853
  if (serverMod?.setManifestReadyPromise) {
4880
5854
  serverMod.setManifestReadyPromise(discoveryPromise);
4881
5855
  }
4882
- await discoverRouters(s, rscEnv);
5856
+ await timed(
5857
+ debugDiscovery,
5858
+ "discoverRouters",
5859
+ () => discoverRouters(s, rscEnv)
5860
+ );
4883
5861
  s.devServerOrigin = getDevServerOrigin();
4884
- writeRouteTypesFiles(s);
4885
- await propagateDiscoveryState(rscEnv);
5862
+ timedSync(
5863
+ debugDiscovery,
5864
+ "writeRouteTypesFiles",
5865
+ () => writeRouteTypesFiles(s)
5866
+ );
5867
+ await timed(
5868
+ debugDiscovery,
5869
+ "propagateDiscoveryState",
5870
+ () => propagateDiscoveryState(rscEnv)
5871
+ );
4886
5872
  } catch (err) {
4887
5873
  console.warn(
4888
- `[rsc-router] Router discovery failed: ${err.message}
5874
+ `[rango] Router discovery failed: ${err.message}
4889
5875
  ${err.stack}`
4890
5876
  );
4891
5877
  } finally {
5878
+ debugDiscovery?.(
5879
+ "dev discovery done (%sms)",
5880
+ (performance.now() - discoverStart).toFixed(1)
5881
+ );
4892
5882
  resolveDiscovery();
4893
5883
  }
4894
5884
  };
4895
- s.discoveryDone = new Promise((resolve10) => {
4896
- setTimeout(() => discover().then(resolve10, resolve10), 0);
4897
- });
5885
+ beginDiscoveryGate();
5886
+ setTimeout(
5887
+ () => discover().then(resolveDiscoveryGate, resolveDiscoveryGate),
5888
+ 0
5889
+ );
4898
5890
  let mainRegistry = null;
4899
5891
  const propagateDiscoveryState = async (rscEnv) => {
4900
5892
  const serverMod = await rscEnv.runner.import("@rangojs/router/server");
@@ -4912,29 +5904,35 @@ ${err.stack}`
4912
5904
  if (s.mergedRouteTrie && serverMod.setRouteTrie) {
4913
5905
  serverMod.setRouteTrie(s.mergedRouteTrie);
4914
5906
  }
4915
- if (serverMod.setRouterManifest) {
4916
- for (const [routerId, manifest] of s.perRouterManifestDataMap) {
4917
- serverMod.setRouterManifest(routerId, manifest);
4918
- }
4919
- }
4920
- if (serverMod.setRouterTrie) {
4921
- for (const [routerId, trie] of s.perRouterTrieMap) {
4922
- serverMod.setRouterTrie(routerId, trie);
4923
- }
4924
- }
4925
- if (serverMod.setRouterPrecomputedEntries) {
4926
- for (const [routerId, entries] of s.perRouterPrecomputedMap) {
4927
- serverMod.setRouterPrecomputedEntries(routerId, entries);
4928
- }
5907
+ const perRouterSetters = [
5908
+ [s.perRouterManifestDataMap, "setRouterManifest"],
5909
+ [s.perRouterTrieMap, "setRouterTrie"],
5910
+ [s.perRouterPrecomputedMap, "setRouterPrecomputedEntries"]
5911
+ ];
5912
+ for (const [map, fn] of perRouterSetters) {
5913
+ const setter = serverMod[fn];
5914
+ if (typeof setter !== "function") continue;
5915
+ for (const [routerId, value] of map) setter(routerId, value);
4929
5916
  }
4930
5917
  };
4931
5918
  server.middlewares.use("/__rsc_prerender", async (req, res) => {
5919
+ const reqStart = debugDev ? performance.now() : 0;
5920
+ const logResult = (status, note) => {
5921
+ debugDev?.(
5922
+ "/__rsc_prerender %s -> %d %s (%sms)",
5923
+ req.url,
5924
+ status,
5925
+ note,
5926
+ (performance.now() - reqStart).toFixed(1)
5927
+ );
5928
+ };
4932
5929
  if (s.discoveryDone) await s.discoveryDone;
4933
5930
  const url = new URL(req.url || "/", "http://localhost");
4934
5931
  const pathname = url.searchParams.get("pathname");
4935
5932
  if (!pathname) {
4936
5933
  res.statusCode = 400;
4937
5934
  res.end("Missing pathname");
5935
+ logResult(400, "missing pathname");
4938
5936
  return;
4939
5937
  }
4940
5938
  const rscEnv = server.environments?.rsc;
@@ -4948,10 +5946,11 @@ ${err.stack}`
4948
5946
  registry = serverMod.RouterRegistry ?? null;
4949
5947
  } catch (err) {
4950
5948
  console.warn(
4951
- `[rsc-router] Dev prerender module refresh failed: ${err.message}`
5949
+ `[rango] Dev prerender module refresh failed: ${err.message}`
4952
5950
  );
4953
5951
  res.statusCode = 500;
4954
5952
  res.end(`Prerender handler error: ${err.message}`);
5953
+ logResult(500, "module refresh failed");
4955
5954
  return;
4956
5955
  }
4957
5956
  } else {
@@ -4966,6 +5965,7 @@ ${err.stack}`
4966
5965
  if (!registry || registry.size === 0) {
4967
5966
  res.statusCode = 503;
4968
5967
  res.end("Prerender runner not available");
5968
+ logResult(503, "no registry");
4969
5969
  return;
4970
5970
  }
4971
5971
  const wantIntercept = url.searchParams.get("intercept") === "1";
@@ -5000,15 +6000,17 @@ ${err.stack}`
5000
6000
  payload = { segments: result.segments, handles: result.handles };
5001
6001
  }
5002
6002
  res.end(JSON.stringify(payload));
6003
+ logResult(200, `match ${result.routeName}`);
5003
6004
  return;
5004
6005
  } catch (err) {
5005
6006
  console.warn(
5006
- `[rsc-router] Dev prerender failed for ${pathname}: ${err.message}`
6007
+ `[rango] Dev prerender failed for ${pathname}: ${err.message}`
5007
6008
  );
5008
6009
  }
5009
6010
  }
5010
6011
  res.statusCode = 404;
5011
6012
  res.end("No prerender match");
6013
+ logResult(404, "no match");
5012
6014
  });
5013
6015
  if (opts?.staticRouteTypesGeneration !== false) {
5014
6016
  const isGeneratedRouteFile = (filePath) => filePath.endsWith(".gen.ts") && (filePath.includes("named-routes.gen.ts") || filePath.includes("urls.gen.ts"));
@@ -5028,59 +6030,185 @@ ${err.stack}`
5028
6030
  return true;
5029
6031
  };
5030
6032
  let routeChangeTimer;
5031
- let runtimeRediscoveryInProgress = false;
5032
6033
  const refreshRuntimeDiscovery = async () => {
5033
6034
  const rscEnv = server.environments?.rsc;
5034
- if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
5035
- runtimeRediscoveryInProgress = true;
6035
+ const hasMainRunner = !!rscEnv?.runner;
6036
+ if (!hasMainRunner && s.perRouterManifests.length === 0) return;
6037
+ await gate.runRefreshCycle(async () => {
6038
+ const hmrStart = performance.now();
6039
+ try {
6040
+ if (hasMainRunner) {
6041
+ await timed(
6042
+ debugDiscovery,
6043
+ "hmr discoverRouters",
6044
+ () => discoverRouters(s, rscEnv)
6045
+ );
6046
+ timedSync(
6047
+ debugDiscovery,
6048
+ "hmr writeRouteTypesFiles",
6049
+ () => writeRouteTypesFiles(s)
6050
+ );
6051
+ await timed(
6052
+ debugDiscovery,
6053
+ "hmr propagateDiscoveryState",
6054
+ () => propagateDiscoveryState(rscEnv)
6055
+ );
6056
+ } else {
6057
+ const tempRscEnv = await timed(
6058
+ debugDiscovery,
6059
+ "hmr refreshTempRscEnv (cloudflare)",
6060
+ () => refreshTempRscEnv()
6061
+ );
6062
+ if (!tempRscEnv) {
6063
+ throw new Error(
6064
+ "temp runner unavailable for cloudflare HMR rediscovery"
6065
+ );
6066
+ }
6067
+ await timed(
6068
+ debugDiscovery,
6069
+ "hmr discoverRouters (cloudflare)",
6070
+ () => discoverRouters(s, tempRscEnv)
6071
+ );
6072
+ timedSync(
6073
+ debugDiscovery,
6074
+ "hmr writeRouteTypesFiles",
6075
+ () => writeRouteTypesFiles(s)
6076
+ );
6077
+ }
6078
+ if (s.lastDiscoveryError) {
6079
+ debugDiscovery?.(
6080
+ "hmr: cleared lastDiscoveryError (%s) after successful rediscovery",
6081
+ s.lastDiscoveryError.message
6082
+ );
6083
+ s.lastDiscoveryError = null;
6084
+ }
6085
+ if (rscEnv && !rscEnv.runner) forceCloudflareWorkerReload(rscEnv);
6086
+ } catch (err) {
6087
+ s.lastDiscoveryError = {
6088
+ message: err?.message ?? String(err),
6089
+ at: Date.now()
6090
+ };
6091
+ console.warn(
6092
+ `[rango] Runtime re-discovery failed: ${err.message}`
6093
+ );
6094
+ debugDiscovery?.(
6095
+ "hmr: lastDiscoveryError set (%s) \u2014 manifest preserved at last-good; recovery mode active (any in-scan source change will trigger rediscovery)",
6096
+ err?.message
6097
+ );
6098
+ } finally {
6099
+ debugDiscovery?.(
6100
+ "hmr re-discovery done (%sms)",
6101
+ (performance.now() - hmrStart).toFixed(1)
6102
+ );
6103
+ }
6104
+ });
6105
+ };
6106
+ const forceCloudflareWorkerReload = (rscEnv) => {
6107
+ if (!rscEnv?.hot) return;
5036
6108
  try {
5037
- await discoverRouters(s, rscEnv);
5038
- writeRouteTypesFiles(s);
5039
- await propagateDiscoveryState(rscEnv);
6109
+ const graph = rscEnv.moduleGraph;
6110
+ if (graph?.invalidateAll) {
6111
+ graph.invalidateAll();
6112
+ debugDiscovery?.("hmr: invalidated workerd rsc module graph");
6113
+ }
6114
+ rscEnv.hot.send({ type: "full-reload" });
6115
+ debugDiscovery?.(
6116
+ "hmr: forced workerd rsc env reload (full-reload)"
6117
+ );
5040
6118
  } catch (err) {
5041
- console.warn(
5042
- `[rsc-router] Runtime re-discovery failed: ${err.message}`
6119
+ debugDiscovery?.(
6120
+ "hmr: workerd reload failed: %s",
6121
+ err?.message ?? err
5043
6122
  );
5044
- } finally {
5045
- runtimeRediscoveryInProgress = false;
5046
6123
  }
5047
6124
  };
5048
6125
  const scheduleRouteRegeneration = () => {
5049
6126
  clearTimeout(routeChangeTimer);
5050
6127
  routeChangeTimer = setTimeout(() => {
5051
6128
  routeChangeTimer = void 0;
6129
+ const regenStart = debugDiscovery ? performance.now() : 0;
6130
+ const rscEnv = server.environments?.rsc;
6131
+ const skipStaticWrite = !rscEnv?.runner && s.perRouterManifests.length > 0;
5052
6132
  try {
5053
- writeCombinedRouteTypesWithTracking(s);
5054
- if (s.perRouterManifests.length > 0) {
5055
- supplementGenFilesWithRuntimeRoutes(s);
6133
+ if (skipStaticWrite) {
6134
+ debugDiscovery?.(
6135
+ "watcher: skipping static write (cloudflare HMR \u2014 runtime rediscovery owns gen file)"
6136
+ );
6137
+ } else {
6138
+ writeCombinedRouteTypesWithTracking(s);
6139
+ if (s.perRouterManifests.length > 0) {
6140
+ supplementGenFilesWithRuntimeRoutes(s);
6141
+ }
5056
6142
  }
5057
6143
  } catch (err) {
5058
- console.error(
5059
- `[rsc-router] Route regeneration error: ${err.message}`
5060
- );
6144
+ console.error(`[rango] Route regeneration error: ${err.message}`);
5061
6145
  }
6146
+ debugDiscovery?.(
6147
+ "watcher: regenerated gen files (%sms)",
6148
+ (performance.now() - regenStart).toFixed(1)
6149
+ );
5062
6150
  if (s.perRouterManifests.length > 0) {
5063
6151
  refreshRuntimeDiscovery().catch((err) => {
5064
6152
  console.warn(
5065
- `[rsc-router] Runtime re-discovery error: ${err.message}`
6153
+ `[rango] Runtime re-discovery error: ${err.message}`
5066
6154
  );
6155
+ resolveDiscoveryGate();
5067
6156
  });
5068
6157
  }
5069
6158
  }, 100);
5070
6159
  };
5071
6160
  const handleRouteFileChange = (filePath) => {
5072
6161
  if (maybeHandleGeneratedRouteFileMutation(filePath)) return;
5073
- if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx") && !filePath.endsWith(".js") && !filePath.endsWith(".jsx"))
6162
+ if (!filePath.endsWith(".ts") && !filePath.endsWith(".tsx") && !filePath.endsWith(".js") && !filePath.endsWith(".jsx")) {
6163
+ if (s.lastDiscoveryError) {
6164
+ debugDiscovery?.(
6165
+ "watcher: skip non-source %s [LASTERR %s]",
6166
+ filePath,
6167
+ s.lastDiscoveryError.message
6168
+ );
6169
+ }
5074
6170
  return;
5075
- if (s.scanFilter && !s.scanFilter(filePath)) return;
6171
+ }
6172
+ if (s.scanFilter && !s.scanFilter(filePath)) {
6173
+ if (s.lastDiscoveryError) {
6174
+ debugDiscovery?.(
6175
+ "watcher: skip scan-filter %s [LASTERR %s]",
6176
+ filePath,
6177
+ s.lastDiscoveryError.message
6178
+ );
6179
+ }
6180
+ return;
6181
+ }
6182
+ const inRecoveryMode = !!s.lastDiscoveryError;
5076
6183
  try {
5077
6184
  const source = readFileSync6(filePath, "utf-8");
5078
6185
  const trimmed = source.trimStart();
5079
- if (trimmed.startsWith('"use client"') || trimmed.startsWith("'use client'"))
5080
- return;
5081
- const hasUrls = source.includes("urls(");
5082
- const hasCreateRouter = /\bcreateRouter\s*[<(]/.test(source);
5083
- if (!hasUrls && !hasCreateRouter) return;
6186
+ const isUseClient = trimmed.startsWith('"use client"') || trimmed.startsWith("'use client'");
6187
+ if (!inRecoveryMode && isUseClient) return;
6188
+ let hasUrls = source.includes("urls(");
6189
+ let hasCreateRouter = /\bcreateRouter\s*[<(]/.test(source);
6190
+ if (hasUrls) hasUrls = firstCodeMatchIndex(source, /urls\(/g) >= 0;
6191
+ if (hasCreateRouter) {
6192
+ hasCreateRouter = firstCodeMatchIndex(source, /\bcreateRouter\s*[<(]/g) >= 0;
6193
+ }
6194
+ if (!inRecoveryMode && !hasUrls && !hasCreateRouter) return;
6195
+ if (inRecoveryMode) {
6196
+ debugDiscovery?.(
6197
+ "watcher: recovery rediscovery for %s (urls=%s, router=%s, useClient=%s) [LASTERR %s]",
6198
+ filePath,
6199
+ hasUrls,
6200
+ hasCreateRouter,
6201
+ isUseClient,
6202
+ s.lastDiscoveryError.message
6203
+ );
6204
+ } else {
6205
+ debugDiscovery?.(
6206
+ "watcher: %s matches (urls=%s, router=%s)",
6207
+ filePath,
6208
+ hasUrls,
6209
+ hasCreateRouter
6210
+ );
6211
+ }
5084
6212
  if (hasCreateRouter) {
5085
6213
  const nestedRouterConflict = findNestedRouterConflict([
5086
6214
  ...s.cachedRouterFiles ?? [],
@@ -5094,8 +6222,19 @@ ${err.stack}`
5094
6222
  }
5095
6223
  s.cachedRouterFiles = void 0;
5096
6224
  }
6225
+ if (s.perRouterManifests.length > 0) {
6226
+ gate.noteRouteEvent();
6227
+ }
5097
6228
  scheduleRouteRegeneration();
5098
- } catch {
6229
+ } catch (readErr) {
6230
+ if (s.lastDiscoveryError) {
6231
+ debugDiscovery?.(
6232
+ "watcher: read error %s: %s [LASTERR %s]",
6233
+ filePath,
6234
+ readErr?.message,
6235
+ s.lastDiscoveryError.message
6236
+ );
6237
+ }
5099
6238
  }
5100
6239
  };
5101
6240
  server.watcher.on("add", handleRouteFileChange);
@@ -5113,19 +6252,35 @@ ${err.stack}`
5113
6252
  // The manifest data is stored for the virtual module's load hook.
5114
6253
  async buildStart() {
5115
6254
  if (!s.isBuildMode) return;
5116
- if (s.mergedRouteManifest !== null) return;
6255
+ if (s.mergedRouteManifest !== null) {
6256
+ debugDiscovery?.(
6257
+ "build: skip (already discovered, env=%s)",
6258
+ this.environment?.name ?? "?"
6259
+ );
6260
+ return;
6261
+ }
6262
+ const buildStartTime = performance.now();
6263
+ debugDiscovery?.("build: start (env=%s)", this.environment?.name ?? "?");
5117
6264
  resetStagedBuildAssets(s.projectRoot);
5118
6265
  s.prerenderManifestEntries = null;
5119
6266
  s.staticManifestEntries = null;
5120
- await acquireBuildEnv(s, viteCommand, viteMode);
6267
+ await timed(
6268
+ debugDiscovery,
6269
+ "build acquireBuildEnv",
6270
+ () => acquireBuildEnv(s, viteCommand, viteMode)
6271
+ );
5121
6272
  let tempServer = null;
5122
6273
  globalThis.__rscRouterDiscoveryActive = true;
5123
6274
  try {
5124
- tempServer = await createTempRscServer(s, { forceBuild: true });
6275
+ tempServer = await timed(
6276
+ debugDiscovery,
6277
+ "build createTempRscServer",
6278
+ () => createTempRscServer(s, { forceBuild: true })
6279
+ );
5125
6280
  const rscEnv = tempServer.environments?.rsc;
5126
6281
  if (!rscEnv?.runner) {
5127
6282
  console.warn(
5128
- "[rsc-router] RSC environment runner not available during build, skipping manifest generation"
6283
+ "[rango] RSC environment runner not available during build, skipping manifest generation"
5129
6284
  );
5130
6285
  return;
5131
6286
  }
@@ -5135,8 +6290,16 @@ ${err.stack}`
5135
6290
  if (tempIdsPlugin?.api?.staticHandlerModules) {
5136
6291
  s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
5137
6292
  }
5138
- await discoverRouters(s, rscEnv);
5139
- writeRouteTypesFiles(s);
6293
+ await timed(
6294
+ debugDiscovery,
6295
+ "build discoverRouters",
6296
+ () => discoverRouters(s, rscEnv)
6297
+ );
6298
+ timedSync(
6299
+ debugDiscovery,
6300
+ "build writeRouteTypesFiles",
6301
+ () => writeRouteTypesFiles(s)
6302
+ );
5140
6303
  } catch (err) {
5141
6304
  const sourceFile = err.stack?.split("\n").find(
5142
6305
  (line) => line.includes(s.projectRoot) && !line.includes("node_modules")
@@ -5149,15 +6312,52 @@ ${err.stack}`
5149
6312
  ${err.stack}` : null
5150
6313
  ].filter(Boolean).join("\n");
5151
6314
  throw new Error(
5152
- `[rsc-router] Build-time router discovery failed:
5153
- ${details}`
6315
+ `[rango] Build-time router discovery failed:
6316
+ ${details}`,
6317
+ { cause: err }
5154
6318
  );
5155
6319
  } finally {
5156
6320
  delete globalThis.__rscRouterDiscoveryActive;
5157
6321
  if (tempServer) {
5158
- await tempServer.close();
6322
+ await timed(
6323
+ debugDiscovery,
6324
+ "build tempServer.close",
6325
+ () => tempServer.close()
6326
+ );
5159
6327
  }
5160
6328
  await releaseBuildEnv(s);
6329
+ debugDiscovery?.(
6330
+ "build discovery done (%sms)",
6331
+ (performance.now() - buildStartTime).toFixed(1)
6332
+ );
6333
+ }
6334
+ },
6335
+ // Suppress vite's HMR cascade for our own gen-file writes.
6336
+ //
6337
+ // After every cf HMR cycle, refreshTempRscEnv → writeRouteTypesFiles
6338
+ // writes the configured gen files (default `router.named-routes.gen.ts`,
6339
+ // but the source filenames and gen suffix are user-configurable). The
6340
+ // chokidar watcher then fires twice independently: our
6341
+ // `handleRouteFileChange` (already short-circuited by
6342
+ // `consumeSelfGenWrite` inside `maybeHandleGeneratedRouteFileMutation`),
6343
+ // AND vite's own HMR pipeline (which invalidates the gen file's
6344
+ // importers and triggers a second workerd full reload — visible to the
6345
+ // user as a duplicate "[Rango] HMR: version changed" on the client).
6346
+ //
6347
+ // `peekSelfGenWrite` is the authoritative filter: its map only contains
6348
+ // paths that `markSelfGenWrite` has registered, so it natively works
6349
+ // for any configured gen-file name. It is non-consuming so the chokidar
6350
+ // handler that fires later can still consume the same entry. Returning
6351
+ // [] tells vite "no modules invalidated by this change" — safe because
6352
+ // `s.perRouterManifests` is already up-to-date (the write that just
6353
+ // happened is the consequence of our just-completed rediscovery).
6354
+ handleHotUpdate(ctx) {
6355
+ if (peekSelfGenWrite(s, ctx.file)) {
6356
+ debugDiscovery?.(
6357
+ "handleHotUpdate: suppressing self-write HMR cascade for %s",
6358
+ ctx.file
6359
+ );
6360
+ return [];
5161
6361
  }
5162
6362
  },
5163
6363
  // Virtual module: provides the pre-generated route manifest as a JS module
@@ -5174,17 +6374,36 @@ ${details}`
5174
6374
  async load(id) {
5175
6375
  if (id === "\0" + VIRTUAL_ROUTES_MANIFEST_ID) {
5176
6376
  if (s.discoveryDone) {
5177
- await s.discoveryDone;
6377
+ await timed(
6378
+ debugRoutes,
6379
+ "await discoveryDone (manifest)",
6380
+ () => s.discoveryDone
6381
+ );
5178
6382
  }
5179
- return generateRoutesManifestModule(s);
6383
+ const code = await timed(
6384
+ debugRoutes,
6385
+ "generateRoutesManifestModule",
6386
+ () => generateRoutesManifestModule(s)
6387
+ );
6388
+ debugRoutes?.("manifest module emitted (%d bytes)", code?.length ?? 0);
6389
+ return code;
5180
6390
  }
5181
6391
  const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
5182
6392
  if (id.startsWith(perRouterPrefix)) {
5183
6393
  if (s.discoveryDone) {
5184
- await s.discoveryDone;
6394
+ await timed(
6395
+ debugRoutes,
6396
+ "await discoveryDone (per-router)",
6397
+ () => s.discoveryDone
6398
+ );
5185
6399
  }
5186
6400
  const routerId = id.slice(perRouterPrefix.length);
5187
- return generatePerRouterModule(s, routerId);
6401
+ const code = await timed(
6402
+ debugRoutes,
6403
+ `generatePerRouterModule ${routerId}`,
6404
+ () => generatePerRouterModule(s, routerId)
6405
+ );
6406
+ return code;
5188
6407
  }
5189
6408
  return null;
5190
6409
  },
@@ -5192,14 +6411,20 @@ ${details}`
5192
6411
  // Used by closeBundle for handler code eviction and prerender data injection.
5193
6412
  generateBundle(_options, bundle) {
5194
6413
  if (this.environment?.name !== "rsc") return;
6414
+ const genStart = debugBuild ? performance.now() : 0;
5195
6415
  for (const [fileName, chunk] of Object.entries(bundle)) {
5196
6416
  if (chunk.type === "chunk" && chunk.isEntry) {
5197
6417
  s.rscEntryFileName = fileName;
5198
6418
  break;
5199
6419
  }
5200
6420
  }
5201
- if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size)
6421
+ if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size) {
6422
+ debugBuild?.(
6423
+ "generateBundle (rsc): no handlers to scan (%sms)",
6424
+ (performance.now() - genStart).toFixed(1)
6425
+ );
5202
6426
  return;
6427
+ }
5203
6428
  s.handlerChunkInfoMap.clear();
5204
6429
  s.staticHandlerChunkInfoMap.clear();
5205
6430
  for (const [fileName, chunk] of Object.entries(bundle)) {
@@ -5243,6 +6468,13 @@ ${details}`
5243
6468
  }
5244
6469
  }
5245
6470
  }
6471
+ debugBuild?.(
6472
+ "generateBundle (rsc): scanned %d chunks, %d prerender chunk(s), %d static chunk(s) (%sms)",
6473
+ Object.keys(bundle).length,
6474
+ s.handlerChunkInfoMap.size,
6475
+ s.staticHandlerChunkInfoMap.size,
6476
+ (performance.now() - genStart).toFixed(1)
6477
+ );
5246
6478
  },
5247
6479
  // Build-time pre-rendering: evict handler code and inject collected prerender data.
5248
6480
  // Collection now happens in-process during discoverRouters() via RSC runner.
@@ -5253,29 +6485,45 @@ ${details}`
5253
6485
  async handler() {
5254
6486
  if (!s.isBuildMode) return;
5255
6487
  if (this.environment && this.environment.name !== "rsc") return;
5256
- postprocessBundle(s);
6488
+ timedSync(
6489
+ debugBuild,
6490
+ "closeBundle postprocessBundle",
6491
+ () => postprocessBundle(s)
6492
+ );
5257
6493
  }
5258
6494
  }
5259
6495
  };
5260
6496
  }
5261
6497
 
5262
6498
  // src/vite/rango.ts
6499
+ var debugConfig = createRangoDebugger(NS.config);
5263
6500
  async function rango(options) {
6501
+ const rangoStart = performance.now();
5264
6502
  const resolvedOptions = options ?? { preset: "node" };
5265
6503
  const preset = resolvedOptions.preset ?? "node";
5266
6504
  const showBanner = resolvedOptions.banner ?? true;
6505
+ const clientChunksOption = resolvedOptions.clientChunks ?? true;
6506
+ const useBuiltInClientChunks = clientChunksOption === true;
6507
+ const clientChunkCtx = useBuiltInClientChunks ? { fallbackRefs: /* @__PURE__ */ new Set() } : void 0;
6508
+ const clientChunks = resolveClientChunks(clientChunksOption, clientChunkCtx);
6509
+ debugConfig?.("rango(%s) setup start", preset);
5267
6510
  const plugins = [];
5268
- const rangoAliases = getPackageAliases();
6511
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
5269
6512
  const excludeDeps = [
5270
6513
  ...getExcludeDeps(),
5271
- // The public browser entry re-exports the RSDW browser client.
5272
- // Excluding both keeps Vite from freezing the unpatched bundle into
5273
- // .vite/deps before our source transforms run.
6514
+ // plugin-rsc itself injects these into the client env's
6515
+ // optimizeDeps.include, which overrides exclude for the dep's own
6516
+ // pre-bundle entry. What exclude still controls is how *other*
6517
+ // pre-bundled deps treat imports of these specs (external vs inlined)
6518
+ // via esbuildCjsExternalPlugin. The cjs-to-esm transform in
6519
+ // plugins/cjs-to-esm.ts is the fallback for strict-pnpm consumers,
6520
+ // where client.browser's bare include fails to resolve and Vite ends up
6521
+ // serving the raw CJS file at dev-serve time.
5274
6522
  "@vitejs/plugin-rsc/browser",
5275
- // Keep the browser RSDW client out of Vite's dep optimizer so our
5276
- // cjs-to-esm transform can patch the real file.
5277
6523
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
5278
6524
  ];
6525
+ const pkg = getPublishedPackageName();
6526
+ const nested = (spec) => `${pkg} > ${spec}`;
5279
6527
  const routerRef = { path: void 0 };
5280
6528
  const prerenderEnabled = true;
5281
6529
  if (preset === "cloudflare") {
@@ -5293,10 +6541,18 @@ async function rango(options) {
5293
6541
  // This ensures the same Context instance is used by both browser entry and RSC proxy modules
5294
6542
  optimizeDeps: {
5295
6543
  exclude: excludeDeps,
5296
- esbuildOptions: sharedEsbuildOptions
6544
+ rolldownOptions: sharedRolldownOptions
5297
6545
  },
5298
6546
  resolve: {
5299
- alias: rangoAliases
6547
+ alias: rangoAliases,
6548
+ // Force a single React/React-DOM copy across all three RSC
6549
+ // environments. RSC requires exactly one react/react-dom instance
6550
+ // per environment runtime; consumer install topologies (pnpm
6551
+ // strict layout, experimental React pins, third-party "use client"
6552
+ // packages) can otherwise resolve duplicate copies, causing
6553
+ // "Invalid hook call" / lost context. Child environments inherit
6554
+ // this root dedupe, and Vite merges it with any consumer dedupe.
6555
+ dedupe: ["react", "react-dom"]
5300
6556
  },
5301
6557
  build: {
5302
6558
  rollupOptions: { onwarn }
@@ -5305,6 +6561,14 @@ async function rango(options) {
5305
6561
  client: {
5306
6562
  build: {
5307
6563
  rollupOptions: {
6564
+ // FILE_NAME_CONFLICT (and any other client-build warning) is
6565
+ // emitted by the CLIENT environment build, which consults THIS
6566
+ // env's onwarn -- Vite 8's environment builds do NOT propagate
6567
+ // the top-level build.rollupOptions.onwarn into the client env.
6568
+ // Wire it here so the suppression runs where the conflicts
6569
+ // originate (the top-level handler is invoked 0x for these; the
6570
+ // client-env handler is invoked for all of them).
6571
+ onwarn,
5308
6572
  output: {
5309
6573
  manualChunks: getManualChunks
5310
6574
  }
@@ -5313,9 +6577,9 @@ async function rango(options) {
5313
6577
  // Pre-bundle rsc-html-stream to prevent discovery during first request
5314
6578
  // Exclude rsc-router modules to ensure same Context instance
5315
6579
  optimizeDeps: {
5316
- include: ["rsc-html-stream/client"],
6580
+ include: [nested("rsc-html-stream/client")],
5317
6581
  exclude: excludeDeps,
5318
- esbuildOptions: sharedEsbuildOptions
6582
+ rolldownOptions: sharedRolldownOptions
5319
6583
  }
5320
6584
  },
5321
6585
  ssr: {
@@ -5323,10 +6587,6 @@ async function rango(options) {
5323
6587
  build: {
5324
6588
  outDir: "./dist/rsc/ssr"
5325
6589
  },
5326
- resolve: {
5327
- // Ensure single React instance in SSR child environment
5328
- dedupe: ["react", "react-dom"]
5329
- },
5330
6590
  // Pre-bundle SSR entry and React for proper module linking with childEnvironments
5331
6591
  // All deps must be listed to avoid late discovery triggering ERR_OUTDATED_OPTIMIZED_DEP
5332
6592
  optimizeDeps: {
@@ -5338,11 +6598,13 @@ async function rango(options) {
5338
6598
  "react-dom/static.edge",
5339
6599
  "react/jsx-runtime",
5340
6600
  "react/jsx-dev-runtime",
5341
- "rsc-html-stream/server",
5342
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6601
+ nested("rsc-html-stream/server"),
6602
+ nested(
6603
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6604
+ )
5343
6605
  ],
5344
6606
  exclude: excludeDeps,
5345
- esbuildOptions: sharedEsbuildOptions
6607
+ rolldownOptions: sharedRolldownOptions
5346
6608
  }
5347
6609
  },
5348
6610
  rsc: {
@@ -5354,10 +6616,12 @@ async function rango(options) {
5354
6616
  "react",
5355
6617
  "react/jsx-runtime",
5356
6618
  "react/jsx-dev-runtime",
5357
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6619
+ nested(
6620
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6621
+ )
5358
6622
  ],
5359
6623
  exclude: excludeDeps,
5360
- esbuildOptions: sharedEsbuildOptions
6624
+ rolldownOptions: sharedRolldownOptions
5361
6625
  }
5362
6626
  }
5363
6627
  }
@@ -5375,7 +6639,8 @@ async function rango(options) {
5375
6639
  plugins.push(
5376
6640
  rsc({
5377
6641
  entries: finalEntries,
5378
- serverHandler: false
6642
+ serverHandler: false,
6643
+ clientChunks
5379
6644
  })
5380
6645
  );
5381
6646
  plugins.push(clientRefDedup());
@@ -5393,7 +6658,7 @@ async function rango(options) {
5393
6658
  const list = candidates.map(
5394
6659
  (f) => " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f)
5395
6660
  ).join("\n");
5396
- throw new Error(`[rsc-router] Multiple routers found:
6661
+ throw new Error(`[rango] Multiple routers found:
5397
6662
  ${list}`);
5398
6663
  }
5399
6664
  }
@@ -5412,18 +6677,34 @@ ${list}`);
5412
6677
  return {
5413
6678
  optimizeDeps: {
5414
6679
  exclude: excludeDeps,
5415
- esbuildOptions: sharedEsbuildOptions
6680
+ rolldownOptions: sharedRolldownOptions
5416
6681
  },
5417
6682
  build: {
5418
6683
  rollupOptions: { onwarn }
5419
6684
  },
5420
6685
  resolve: {
5421
- alias: rangoAliases
6686
+ alias: rangoAliases,
6687
+ // Force a single React/React-DOM copy across all three RSC
6688
+ // environments. RSC requires exactly one react/react-dom instance
6689
+ // per environment runtime; consumer install topologies (pnpm
6690
+ // strict layout, experimental React pins, third-party "use client"
6691
+ // packages) can otherwise resolve duplicate copies, causing
6692
+ // "Invalid hook call" / lost context. Child environments inherit
6693
+ // this root dedupe, and Vite merges it with any consumer dedupe.
6694
+ dedupe: ["react", "react-dom"]
5422
6695
  },
5423
6696
  environments: {
5424
6697
  client: {
5425
6698
  build: {
5426
6699
  rollupOptions: {
6700
+ // FILE_NAME_CONFLICT (and any other client-build warning) is
6701
+ // emitted by the CLIENT environment build, which consults THIS
6702
+ // env's onwarn -- Vite 8's environment builds do NOT propagate
6703
+ // the top-level build.rollupOptions.onwarn into the client env.
6704
+ // Wire it here so the suppression runs where the conflicts
6705
+ // originate (the top-level handler is invoked 0x for these; the
6706
+ // client-env handler is invoked for all of them).
6707
+ onwarn,
5427
6708
  output: {
5428
6709
  manualChunks: getManualChunks
5429
6710
  }
@@ -5435,10 +6716,10 @@ ${list}`);
5435
6716
  "react-dom",
5436
6717
  "react/jsx-runtime",
5437
6718
  "react/jsx-dev-runtime",
5438
- "rsc-html-stream/client"
6719
+ nested("rsc-html-stream/client")
5439
6720
  ],
5440
6721
  exclude: excludeDeps,
5441
- esbuildOptions: sharedEsbuildOptions,
6722
+ rolldownOptions: sharedRolldownOptions,
5442
6723
  entries: [VIRTUAL_IDS.browser]
5443
6724
  }
5444
6725
  },
@@ -5452,10 +6733,12 @@ ${list}`);
5452
6733
  "react-dom/static.edge",
5453
6734
  "react/jsx-runtime",
5454
6735
  "react/jsx-dev-runtime",
5455
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6736
+ nested(
6737
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6738
+ )
5456
6739
  ],
5457
6740
  exclude: excludeDeps,
5458
- esbuildOptions: sharedEsbuildOptions
6741
+ rolldownOptions: sharedRolldownOptions
5459
6742
  }
5460
6743
  },
5461
6744
  rsc: {
@@ -5465,9 +6748,11 @@ ${list}`);
5465
6748
  "react",
5466
6749
  "react/jsx-runtime",
5467
6750
  "react/jsx-dev-runtime",
5468
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6751
+ nested(
6752
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6753
+ )
5469
6754
  ],
5470
- esbuildOptions: sharedEsbuildOptions
6755
+ rolldownOptions: sharedRolldownOptions
5471
6756
  }
5472
6757
  }
5473
6758
  }
@@ -5484,7 +6769,7 @@ ${list}`);
5484
6769
  if (rscMinimalCount > 1 && !hasWarnedDuplicate) {
5485
6770
  hasWarnedDuplicate = true;
5486
6771
  console.warn(
5487
- "[rsc-router] Duplicate @vitejs/plugin-rsc detected. Remove rsc() from your vite config \u2014 rango() includes it automatically."
6772
+ "[rango] Duplicate @vitejs/plugin-rsc detected. Remove rsc() from your vite config \u2014 rango() includes it automatically."
5488
6773
  );
5489
6774
  }
5490
6775
  }
@@ -5493,7 +6778,8 @@ ${list}`);
5493
6778
  plugins.push(performanceTracksPlugin());
5494
6779
  plugins.push(
5495
6780
  rsc({
5496
- entries: finalEntries
6781
+ entries: finalEntries,
6782
+ clientChunks
5497
6783
  })
5498
6784
  );
5499
6785
  plugins.push(clientRefDedup());
@@ -5532,9 +6818,16 @@ ${list}`);
5532
6818
  routerPathRef: discoveryRouterRef,
5533
6819
  enableBuildPrerender: prerenderEnabled,
5534
6820
  buildEnv: options?.buildEnv,
5535
- preset
6821
+ preset,
6822
+ clientChunkCtx
5536
6823
  })
5537
6824
  );
6825
+ debugConfig?.(
6826
+ "rango(%s) setup done: %d plugin(s) (%sms)",
6827
+ preset,
6828
+ plugins.length,
6829
+ (performance.now() - rangoStart).toFixed(1)
6830
+ );
5538
6831
  return plugins;
5539
6832
  }
5540
6833
 
@@ -5545,7 +6838,7 @@ function poke() {
5545
6838
  apply: "serve",
5546
6839
  configureServer(server) {
5547
6840
  const stdin = process.stdin;
5548
- const debug = process.env.RANGO_POKE_DEBUG === "1";
6841
+ const debug11 = process.env.RANGO_POKE_DEBUG === "1";
5549
6842
  const triggerReload = (source) => {
5550
6843
  server.hot.send({ type: "full-reload", path: "*" });
5551
6844
  server.config.logger.info(` browser reload (${source})`, {
@@ -5578,7 +6871,7 @@ function poke() {
5578
6871
  lines.pop();
5579
6872
  return lines;
5580
6873
  };
5581
- if (debug) {
6874
+ if (debug11) {
5582
6875
  server.config.logger.info(
5583
6876
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
5584
6877
  { timestamp: true }
@@ -5591,7 +6884,7 @@ function poke() {
5591
6884
  );
5592
6885
  }
5593
6886
  const onData = (data) => {
5594
- if (debug) {
6887
+ if (debug11) {
5595
6888
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
5596
6889
  timestamp: true
5597
6890
  });