@rangojs/router 0.0.0-experimental.66 → 0.0.0-experimental.66cdebe3

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 (123) hide show
  1. package/README.md +112 -17
  2. package/dist/vite/index.js +1462 -422
  3. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  4. package/package.json +7 -5
  5. package/skills/breadcrumbs/SKILL.md +3 -1
  6. package/skills/handler-use/SKILL.md +364 -0
  7. package/skills/hooks/SKILL.md +54 -20
  8. package/skills/i18n/SKILL.md +276 -0
  9. package/skills/intercept/SKILL.md +45 -0
  10. package/skills/layout/SKILL.md +24 -0
  11. package/skills/links/SKILL.md +234 -16
  12. package/skills/loader/SKILL.md +70 -3
  13. package/skills/middleware/SKILL.md +34 -3
  14. package/skills/migrate-nextjs/SKILL.md +562 -0
  15. package/skills/migrate-react-router/SKILL.md +769 -0
  16. package/skills/parallel/SKILL.md +68 -0
  17. package/skills/rango/SKILL.md +26 -22
  18. package/skills/response-routes/SKILL.md +8 -0
  19. package/skills/route/SKILL.md +48 -0
  20. package/skills/server-actions/SKILL.md +739 -0
  21. package/skills/streams-and-websockets/SKILL.md +283 -0
  22. package/skills/typesafety/SKILL.md +9 -1
  23. package/skills/view-transitions/SKILL.md +212 -0
  24. package/src/browser/app-shell.ts +52 -0
  25. package/src/browser/event-controller.ts +44 -4
  26. package/src/browser/navigation-bridge.ts +151 -9
  27. package/src/browser/navigation-client.ts +64 -13
  28. package/src/browser/navigation-store.ts +25 -1
  29. package/src/browser/partial-update.ts +58 -12
  30. package/src/browser/prefetch/cache.ts +129 -21
  31. package/src/browser/prefetch/fetch.ts +148 -16
  32. package/src/browser/prefetch/queue.ts +36 -5
  33. package/src/browser/rango-state.ts +53 -13
  34. package/src/browser/react/Link.tsx +30 -2
  35. package/src/browser/react/NavigationProvider.tsx +95 -44
  36. package/src/browser/react/filter-segment-order.ts +51 -7
  37. package/src/browser/react/index.ts +3 -0
  38. package/src/browser/react/use-navigation.ts +22 -2
  39. package/src/browser/react/use-params.ts +17 -4
  40. package/src/browser/react/use-reverse.ts +99 -0
  41. package/src/browser/react/use-router.ts +8 -1
  42. package/src/browser/react/use-segments.ts +11 -8
  43. package/src/browser/rsc-router.tsx +34 -6
  44. package/src/browser/scroll-restoration.ts +69 -28
  45. package/src/browser/segment-reconciler.ts +36 -14
  46. package/src/browser/types.ts +19 -0
  47. package/src/build/route-trie.ts +52 -25
  48. package/src/cache/cf/cf-cache-store.ts +5 -7
  49. package/src/client.rsc.tsx +3 -0
  50. package/src/client.tsx +87 -175
  51. package/src/href-client.ts +4 -1
  52. package/src/index.rsc.ts +3 -0
  53. package/src/index.ts +44 -9
  54. package/src/outlet-context.ts +1 -1
  55. package/src/response-utils.ts +28 -0
  56. package/src/reverse.ts +62 -36
  57. package/src/route-definition/dsl-helpers.ts +175 -23
  58. package/src/route-definition/helpers-types.ts +63 -14
  59. package/src/route-definition/resolve-handler-use.ts +6 -0
  60. package/src/route-types.ts +7 -0
  61. package/src/router/handler-context.ts +21 -38
  62. package/src/router/lazy-includes.ts +6 -6
  63. package/src/router/loader-resolution.ts +3 -0
  64. package/src/router/manifest.ts +22 -13
  65. package/src/router/match-api.ts +4 -3
  66. package/src/router/match-handlers.ts +1 -0
  67. package/src/router/match-middleware/cache-lookup.ts +2 -1
  68. package/src/router/match-result.ts +101 -4
  69. package/src/router/middleware-types.ts +14 -25
  70. package/src/router/middleware.ts +54 -7
  71. package/src/router/pattern-matching.ts +101 -17
  72. package/src/router/revalidation.ts +15 -1
  73. package/src/router/segment-resolution/fresh.ts +13 -0
  74. package/src/router/segment-resolution/revalidation.ts +135 -101
  75. package/src/router/substitute-pattern-params.ts +56 -0
  76. package/src/router/trie-matching.ts +18 -13
  77. package/src/router/url-params.ts +49 -0
  78. package/src/router.ts +1 -2
  79. package/src/rsc/handler.ts +16 -8
  80. package/src/rsc/helpers.ts +69 -41
  81. package/src/rsc/progressive-enhancement.ts +4 -0
  82. package/src/rsc/response-route-handler.ts +14 -1
  83. package/src/rsc/rsc-rendering.ts +10 -0
  84. package/src/rsc/server-action.ts +4 -0
  85. package/src/rsc/types.ts +6 -0
  86. package/src/segment-content-promise.ts +67 -0
  87. package/src/segment-loader-promise.ts +122 -0
  88. package/src/segment-system.tsx +71 -70
  89. package/src/server/context.ts +26 -3
  90. package/src/server/request-context.ts +10 -42
  91. package/src/ssr/index.tsx +5 -1
  92. package/src/types/handler-context.ts +12 -39
  93. package/src/types/loader-types.ts +5 -6
  94. package/src/types/request-scope.ts +126 -0
  95. package/src/types/route-entry.ts +11 -0
  96. package/src/types/segments.ts +18 -1
  97. package/src/urls/include-helper.ts +24 -14
  98. package/src/urls/path-helper-types.ts +30 -4
  99. package/src/urls/response-types.ts +2 -10
  100. package/src/use-loader.tsx +4 -1
  101. package/src/vite/debug.ts +184 -0
  102. package/src/vite/discovery/discover-routers.ts +31 -3
  103. package/src/vite/discovery/gate-state.ts +171 -0
  104. package/src/vite/discovery/prerender-collection.ts +172 -84
  105. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  106. package/src/vite/plugins/cjs-to-esm.ts +5 -0
  107. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  108. package/src/vite/plugins/client-ref-hashing.ts +16 -4
  109. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  110. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  111. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  112. package/src/vite/plugins/expose-action-id.ts +52 -28
  113. package/src/vite/plugins/expose-id-utils.ts +12 -0
  114. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  115. package/src/vite/plugins/expose-internal-ids.ts +545 -304
  116. package/src/vite/plugins/performance-tracks.ts +17 -9
  117. package/src/vite/plugins/use-cache-transform.ts +56 -43
  118. package/src/vite/plugins/version-injector.ts +37 -11
  119. package/src/vite/rango.ts +49 -14
  120. package/src/vite/router-discovery.ts +558 -53
  121. package/src/vite/utils/banner.ts +1 -1
  122. package/src/vite/utils/package-resolution.ts +41 -1
  123. package/src/vite/utils/prerender-utils.ts +21 -6
@@ -18,6 +18,9 @@ function hashId(filePath, exportName) {
18
18
  const hash = crypto.createHash("sha256").update(input).digest("hex");
19
19
  return `${hash.slice(0, 8)}#${exportName}`;
20
20
  }
21
+ function makeStubId(filePath, exportName, isBuild) {
22
+ return isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
23
+ }
21
24
  function hashInlineId(filePath, lineNumber, index) {
22
25
  const input = index !== void 0 && index > 0 ? `${filePath}:${lineNumber}:${index}` : `${filePath}:${lineNumber}`;
23
26
  return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
@@ -188,7 +191,99 @@ function escapeRegExp(input) {
188
191
  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
189
192
  }
190
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
+ };
205
+ if (process.env.INTERNAL_RANGO_DEBUG) {
206
+ const existing = debugFactory.disable();
207
+ debugFactory.enable(existing ? `${existing},rango:*` : "rango:*");
208
+ }
209
+ function createRangoDebugger(namespace) {
210
+ const primary = debugFactory(namespace);
211
+ const shadow = debugFactory(`vite:${namespace}`);
212
+ if (primary.enabled) return primary;
213
+ if (shadow.enabled) return shadow;
214
+ return void 0;
215
+ }
216
+ async function timed(debug11, label, fn) {
217
+ if (!debug11) return await fn();
218
+ const start = performance.now();
219
+ try {
220
+ return await fn();
221
+ } finally {
222
+ debug11("%s (%sms)", label, (performance.now() - start).toFixed(1));
223
+ }
224
+ }
225
+ function timedSync(debug11, label, fn) {
226
+ if (!debug11) return fn();
227
+ const start = performance.now();
228
+ try {
229
+ return fn();
230
+ } finally {
231
+ debug11("%s (%sms)", label, (performance.now() - start).toFixed(1));
232
+ }
233
+ }
234
+ function createCounter(debug11, label) {
235
+ if (!debug11) return void 0;
236
+ let n = 0;
237
+ let totalMs = 0;
238
+ let slowestMs = 0;
239
+ let slowestFile = "";
240
+ const record = (file, ms) => {
241
+ n++;
242
+ totalMs += ms;
243
+ if (ms > slowestMs) {
244
+ slowestMs = ms;
245
+ slowestFile = file;
246
+ }
247
+ };
248
+ return {
249
+ record,
250
+ time(file, fn) {
251
+ const start = performance.now();
252
+ let out;
253
+ try {
254
+ out = fn();
255
+ } catch (err) {
256
+ record(file, performance.now() - start);
257
+ throw err;
258
+ }
259
+ if (out && typeof out.then === "function") {
260
+ return out.finally(
261
+ () => record(file, performance.now() - start)
262
+ );
263
+ }
264
+ record(file, performance.now() - start);
265
+ return out;
266
+ },
267
+ flush() {
268
+ if (n === 0) return;
269
+ debug11(
270
+ "%s: %d files, %sms total, slowest %sms %s",
271
+ label,
272
+ n,
273
+ totalMs.toFixed(1),
274
+ slowestMs.toFixed(1),
275
+ slowestFile
276
+ );
277
+ n = 0;
278
+ totalMs = 0;
279
+ slowestMs = 0;
280
+ slowestFile = "";
281
+ }
282
+ };
283
+ }
284
+
191
285
  // src/vite/plugins/expose-action-id.ts
286
+ var debug = createRangoDebugger(NS.transform);
192
287
  function getRscPluginApi(config) {
193
288
  let plugin = config.plugins.find((p) => p.name === "rsc:minimal");
194
289
  if (!plugin) {
@@ -277,6 +372,8 @@ function exposeActionId() {
277
372
  let isBuild = false;
278
373
  let hashToFileMap;
279
374
  let rscPluginApi;
375
+ const counterTransform = createCounter(debug, "expose-action-id transform");
376
+ const counterRender = createCounter(debug, "expose-action-id renderChunk");
280
377
  return {
281
378
  name: "@rangojs/router:expose-action-id",
282
379
  // Run after all other plugins (including RSC plugin's transforms)
@@ -286,6 +383,10 @@ function exposeActionId() {
286
383
  isBuild = config.command === "build";
287
384
  rscPluginApi = getRscPluginApi(config);
288
385
  },
386
+ buildEnd() {
387
+ counterTransform?.flush();
388
+ counterRender?.flush();
389
+ },
289
390
  buildStart() {
290
391
  if (!rscPluginApi) {
291
392
  rscPluginApi = getRscPluginApi(config);
@@ -322,28 +423,42 @@ function exposeActionId() {
322
423
  if (id.includes("/node_modules/")) {
323
424
  return;
324
425
  }
325
- return transformServerReferences(code, id);
426
+ const start = counterTransform ? performance.now() : 0;
427
+ try {
428
+ return transformServerReferences(code, id);
429
+ } finally {
430
+ counterTransform?.record(id, performance.now() - start);
431
+ }
326
432
  },
327
433
  // Build mode: renderChunk runs after all transforms and bundling complete
328
434
  renderChunk(code, chunk) {
329
- const isRscEnv = this.environment?.name === "rsc";
330
- const effectiveMap = isRscEnv ? hashToFileMap : void 0;
331
- if (isRscEnv && hashToFileMap) {
332
- const s = new MagicString(code);
333
- const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
334
- const changed2 = applyRegisterReferenceWrapping(code, s, hashToFileMap);
335
- if (changed1 || changed2) {
336
- return {
337
- code: s.toString(),
338
- map: s.generateMap({
339
- source: chunk.fileName,
340
- includeContent: true
341
- })
342
- };
435
+ const start = counterRender ? performance.now() : 0;
436
+ try {
437
+ const isRscEnv = this.environment?.name === "rsc";
438
+ const effectiveMap = isRscEnv ? hashToFileMap : void 0;
439
+ if (isRscEnv && hashToFileMap) {
440
+ const s = new MagicString(code);
441
+ const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
442
+ const changed2 = applyRegisterReferenceWrapping(
443
+ code,
444
+ s,
445
+ hashToFileMap
446
+ );
447
+ if (changed1 || changed2) {
448
+ return {
449
+ code: s.toString(),
450
+ map: s.generateMap({
451
+ source: chunk.fileName,
452
+ includeContent: true
453
+ })
454
+ };
455
+ }
456
+ return null;
343
457
  }
344
- return null;
458
+ return transformServerReferences(code, chunk.fileName, effectiveMap);
459
+ } finally {
460
+ counterRender?.record(chunk.fileName, performance.now() - start);
345
461
  }
346
- return transformServerReferences(code, chunk.fileName, effectiveMap);
347
462
  }
348
463
  };
349
464
  }
@@ -950,6 +1065,7 @@ ${binding.localName}.$$id = "${handlerId}";`;
950
1065
  import MagicString3 from "magic-string";
951
1066
  import path3 from "node:path";
952
1067
  import { createHash } from "node:crypto";
1068
+ var debug2 = createRangoDebugger(NS.transform);
953
1069
  function transformRouter(code, filePath, routerFnNames, absolutePath) {
954
1070
  const pat = new RegExp(
955
1071
  `\\b(?:${routerFnNames.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\s*(?:<[^>]*>)?\\s*\\(`,
@@ -992,11 +1108,15 @@ function transformRouter(code, filePath, routerFnNames, absolutePath) {
992
1108
  }
993
1109
  function exposeRouterId() {
994
1110
  let projectRoot = "";
1111
+ const counter = createCounter(debug2, "expose-router-id");
995
1112
  return {
996
1113
  name: "@rangojs/router:expose-router-id",
997
1114
  configResolved(config) {
998
1115
  projectRoot = config.root;
999
1116
  },
1117
+ buildEnd() {
1118
+ counter?.flush();
1119
+ },
1000
1120
  transform(code, id) {
1001
1121
  if (!code.includes("createRouter")) return null;
1002
1122
  if (!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
@@ -1005,14 +1125,25 @@ function exposeRouterId() {
1005
1125
  return null;
1006
1126
  }
1007
1127
  if (id.includes("node_modules")) return null;
1008
- const filePath = normalizePath(path3.relative(projectRoot, id));
1009
- const routerFnNames = getImportedFnNames(code, "createRouter");
1010
- return transformRouter(code, filePath, routerFnNames, normalizePath(id));
1128
+ const start = counter ? performance.now() : 0;
1129
+ try {
1130
+ const filePath = normalizePath(path3.relative(projectRoot, id));
1131
+ const routerFnNames = getImportedFnNames(code, "createRouter");
1132
+ return transformRouter(
1133
+ code,
1134
+ filePath,
1135
+ routerFnNames,
1136
+ normalizePath(id)
1137
+ );
1138
+ } finally {
1139
+ counter?.record(id, performance.now() - start);
1140
+ }
1011
1141
  }
1012
1142
  };
1013
1143
  }
1014
1144
 
1015
1145
  // src/vite/plugins/expose-internal-ids.ts
1146
+ var debug3 = createRangoDebugger(NS.transform);
1016
1147
  var VIRTUAL_LOADER_MANIFEST = "virtual:rsc-router/loader-manifest";
1017
1148
  var RESOLVED_VIRTUAL_LOADER_MANIFEST = "\0" + VIRTUAL_LOADER_MANIFEST;
1018
1149
  var VIRTUAL_HANDLER_PREFIX = "virtual:handler-extract:";
@@ -1025,9 +1156,13 @@ function exposeInternalIds(options) {
1025
1156
  const staticHandlerModules = /* @__PURE__ */ new Map();
1026
1157
  const virtualHandlers = /* @__PURE__ */ new Map();
1027
1158
  const unsupportedShapeWarnings = /* @__PURE__ */ new Set();
1159
+ const counter = createCounter(debug3, "expose-internal-ids");
1028
1160
  return {
1029
1161
  name: "@rangojs/router:expose-internal-ids",
1030
1162
  enforce: "post",
1163
+ buildEnd() {
1164
+ counter?.flush();
1165
+ },
1031
1166
  api: {
1032
1167
  prerenderHandlerModules,
1033
1168
  staticHandlerModules
@@ -1141,11 +1276,13 @@ ${lazyImports.join(",\n")}
1141
1276
  // --------------- Unified transform ---------------
1142
1277
  transform(code, id) {
1143
1278
  if (id.includes("/node_modules/")) return;
1144
- const filePath = normalizePath(path4.relative(projectRoot, id));
1145
- const isRscEnv = this.environment?.name === "rsc";
1146
- if (id.includes(".named-routes.gen.") && !isRscEnv && this.environment?.name === "client") {
1147
- this.warn(
1148
- `
1279
+ const __t0 = counter ? performance.now() : 0;
1280
+ try {
1281
+ const filePath = normalizePath(path4.relative(projectRoot, id));
1282
+ const isRscEnv = this.environment?.name === "rsc";
1283
+ if (id.includes(".named-routes.gen.") && !isRscEnv && this.environment?.name === "client") {
1284
+ this.warn(
1285
+ `
1149
1286
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1150
1287
  !! !!
1151
1288
  !! WARNING: NamedRoutes imported in a CLIENT component! !!
@@ -1165,226 +1302,373 @@ ${lazyImports.join(",\n")}
1165
1302
  !! !!
1166
1303
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1167
1304
  `
1168
- );
1169
- }
1170
- if (!code.includes("@rangojs/router")) return;
1171
- const has = detectImports(code);
1172
- const hasLoaderCode = has.loader && code.includes("createLoader");
1173
- const hasHandleCode = has.handle && code.includes("createHandle");
1174
- const hasLocationStateCode = has.locationState && code.includes("createLocationState");
1175
- const hasPrerenderHandlerCode = has.prerenderHandler && code.includes("Prerender");
1176
- const hasStaticHandlerCode = has.staticHandler && code.includes("Static");
1177
- if (!hasLoaderCode && !hasHandleCode && !hasLocationStateCode && !hasPrerenderHandlerCode && !hasStaticHandlerCode) {
1178
- return;
1179
- }
1180
- const _fnNamesCache = /* @__PURE__ */ new Map();
1181
- const _bindingsCache = /* @__PURE__ */ new Map();
1182
- let _cachedAst;
1183
- let _astParseFailed = false;
1184
- let _astCodeRef = code;
1185
- const getFnNames = (canonicalName) => {
1186
- let result = _fnNamesCache.get(canonicalName);
1187
- if (!result) {
1188
- result = getImportedFnNames(code, canonicalName);
1189
- _fnNamesCache.set(canonicalName, result);
1305
+ );
1190
1306
  }
1191
- return result;
1192
- };
1193
- const lazyAst = () => {
1194
- if (code !== _astCodeRef) {
1195
- _cachedAst = void 0;
1196
- _astParseFailed = false;
1197
- _astCodeRef = code;
1307
+ if (!code.includes("@rangojs/router")) return;
1308
+ const has = detectImports(code);
1309
+ const hasLoaderCode = has.loader && code.includes("createLoader");
1310
+ const hasHandleCode = has.handle && code.includes("createHandle");
1311
+ const hasLocationStateCode = has.locationState && code.includes("createLocationState");
1312
+ const hasPrerenderHandlerCode = has.prerenderHandler && code.includes("Prerender");
1313
+ const hasStaticHandlerCode = has.staticHandler && code.includes("Static");
1314
+ if (!hasLoaderCode && !hasHandleCode && !hasLocationStateCode && !hasPrerenderHandlerCode && !hasStaticHandlerCode) {
1315
+ return;
1198
1316
  }
1199
- if (_cachedAst !== void 0 || _astParseFailed) return _cachedAst;
1200
- try {
1201
- _cachedAst = parseAst2(code, { jsx: true });
1202
- } catch {
1203
- _astParseFailed = true;
1317
+ const _fnNamesCache = /* @__PURE__ */ new Map();
1318
+ const _bindingsCache = /* @__PURE__ */ new Map();
1319
+ let _cachedAst;
1320
+ let _astParseFailed = false;
1321
+ let _astCodeRef = code;
1322
+ const getFnNames = (canonicalName) => {
1323
+ let result = _fnNamesCache.get(canonicalName);
1324
+ if (!result) {
1325
+ result = getImportedFnNames(code, canonicalName);
1326
+ _fnNamesCache.set(canonicalName, result);
1327
+ }
1328
+ return result;
1329
+ };
1330
+ const lazyAst = () => {
1331
+ if (code !== _astCodeRef) {
1332
+ _cachedAst = void 0;
1333
+ _astParseFailed = false;
1334
+ _astCodeRef = code;
1335
+ }
1336
+ if (_cachedAst !== void 0 || _astParseFailed) return _cachedAst;
1337
+ try {
1338
+ _cachedAst = parseAst2(code, { jsx: true });
1339
+ } catch {
1340
+ _astParseFailed = true;
1341
+ }
1342
+ return _cachedAst;
1343
+ };
1344
+ const getBindings = (currentCode, fnNames) => {
1345
+ const key = fnNames.join("\0");
1346
+ let result = _bindingsCache.get(key);
1347
+ if (!result) {
1348
+ result = collectCreateExportBindings(
1349
+ currentCode,
1350
+ fnNames,
1351
+ lazyAst()
1352
+ );
1353
+ _bindingsCache.set(key, result);
1354
+ }
1355
+ return result;
1356
+ };
1357
+ for (const cfg of STRICT_CREATE_CONFIGS) {
1358
+ const hasCode = cfg.fnName === "createLoader" ? hasLoaderCode : cfg.fnName === "createHandle" ? hasHandleCode : hasLocationStateCode;
1359
+ if (!hasCode) continue;
1360
+ const fnNames = getFnNames(cfg.fnName);
1361
+ const totalCalls = countCreateCallsForNames(code, fnNames);
1362
+ const supportedBindings = getBindings(code, fnNames).length;
1363
+ if (totalCalls <= supportedBindings) continue;
1364
+ const warnKey = `${id}::${cfg.fnName}`;
1365
+ if (unsupportedShapeWarnings.has(warnKey)) continue;
1366
+ unsupportedShapeWarnings.add(warnKey);
1367
+ this.warn(buildUnsupportedShapeWarning(filePath, cfg.fnName));
1204
1368
  }
1205
- return _cachedAst;
1206
- };
1207
- const getBindings = (currentCode, fnNames) => {
1208
- const key = fnNames.join("\0");
1209
- let result = _bindingsCache.get(key);
1210
- if (!result) {
1211
- result = collectCreateExportBindings(currentCode, fnNames, lazyAst());
1212
- _bindingsCache.set(key, result);
1369
+ if (hasLoaderCode && isRscEnv) {
1370
+ const fnNames = getFnNames("createLoader");
1371
+ const bindings = getBindings(code, fnNames);
1372
+ for (const binding of bindings) {
1373
+ const exportName = binding.exportNames[0];
1374
+ const hashedId = hashId(filePath, exportName);
1375
+ loaderRegistry.set(hashedId, {
1376
+ filePath,
1377
+ exportName
1378
+ });
1379
+ }
1213
1380
  }
1214
- return result;
1215
- };
1216
- for (const cfg of STRICT_CREATE_CONFIGS) {
1217
- const hasCode = cfg.fnName === "createLoader" ? hasLoaderCode : cfg.fnName === "createHandle" ? hasHandleCode : hasLocationStateCode;
1218
- if (!hasCode) continue;
1219
- const fnNames = getFnNames(cfg.fnName);
1220
- const totalCalls = countCreateCallsForNames(code, fnNames);
1221
- const supportedBindings = getBindings(code, fnNames).length;
1222
- if (totalCalls <= supportedBindings) continue;
1223
- const warnKey = `${id}::${cfg.fnName}`;
1224
- if (unsupportedShapeWarnings.has(warnKey)) continue;
1225
- unsupportedShapeWarnings.add(warnKey);
1226
- this.warn(buildUnsupportedShapeWarning(filePath, cfg.fnName));
1227
- }
1228
- if (hasLoaderCode && isRscEnv) {
1229
- const fnNames = getFnNames("createLoader");
1230
- const bindings = getBindings(code, fnNames);
1231
- for (const binding of bindings) {
1232
- const exportName = binding.exportNames[0];
1233
- const hashedId = hashId(filePath, exportName);
1234
- loaderRegistry.set(hashedId, {
1381
+ if (hasLoaderCode && !isRscEnv) {
1382
+ const fnNames = getFnNames("createLoader");
1383
+ const bindings = getBindings(code, fnNames);
1384
+ const stubResult = generateClientLoaderStubs(
1385
+ bindings,
1386
+ code,
1235
1387
  filePath,
1236
- exportName
1237
- });
1238
- }
1239
- }
1240
- if (hasLoaderCode && !isRscEnv) {
1241
- const fnNames = getFnNames("createLoader");
1242
- const bindings = getBindings(code, fnNames);
1243
- const stubResult = generateClientLoaderStubs(
1244
- bindings,
1245
- code,
1246
- filePath,
1247
- isBuild
1248
- );
1249
- if (stubResult) return stubResult;
1250
- }
1251
- if (hasPrerenderHandlerCode && !isRscEnv) {
1252
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1253
- const bindings = getBindings(code, fnNames);
1254
- const wholeFile = generateWholeFileStubs(
1255
- PRERENDER_CONFIG,
1256
- bindings,
1257
- code,
1258
- filePath,
1259
- isBuild
1260
- );
1261
- if (wholeFile) return wholeFile;
1262
- }
1263
- if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
1264
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1265
- const exportNames = getBindings(code, fnNames).map(
1266
- (b) => b.exportNames[0]
1267
- );
1268
- if (exportNames.length > 0) {
1269
- prerenderHandlerModules.set(id, exportNames);
1388
+ isBuild
1389
+ );
1390
+ if (stubResult) return stubResult;
1270
1391
  }
1271
- }
1272
- let changed = false;
1273
- const handlerConfigs = [
1274
- hasStaticHandlerCode && STATIC_CONFIG,
1275
- hasPrerenderHandlerCode && PRERENDER_CONFIG
1276
- ].filter((c) => !!c).map((cfg) => {
1277
- const fnNames = getFnNames(cfg.fnName);
1278
- return { cfg, fnNames };
1279
- });
1280
- for (const { cfg, fnNames } of handlerConfigs) {
1281
- const totalCalls = countCreateCallsForNames(code, fnNames);
1282
- const supportedBindings = getBindings(code, fnNames).length;
1283
- if (totalCalls > supportedBindings) {
1284
- const iterS = new MagicString4(code);
1285
- const result = transformInlineHandlers(
1286
- cfg.fnName,
1287
- VIRTUAL_HANDLER_PREFIX,
1288
- iterS,
1392
+ if (hasPrerenderHandlerCode && !isRscEnv) {
1393
+ const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1394
+ const bindings = getBindings(code, fnNames);
1395
+ const wholeFile = generateWholeFileStubs(
1396
+ PRERENDER_CONFIG,
1397
+ bindings,
1289
1398
  code,
1290
1399
  filePath,
1291
- virtualHandlers,
1292
- id,
1293
- parseAst2
1400
+ isBuild
1294
1401
  );
1295
- if (result) {
1296
- changed = true;
1297
- code = iterS.toString();
1298
- _bindingsCache.clear();
1402
+ if (wholeFile) return wholeFile;
1403
+ }
1404
+ if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
1405
+ const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1406
+ const exportNames = getBindings(code, fnNames).map(
1407
+ (b) => b.exportNames[0]
1408
+ );
1409
+ if (exportNames.length > 0) {
1410
+ prerenderHandlerModules.set(id, exportNames);
1299
1411
  }
1300
1412
  }
1301
- }
1302
- if (hasStaticHandlerCode && !isRscEnv) {
1303
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1304
- const bindings = getBindings(code, fnNames);
1305
- const wholeFile = generateWholeFileStubs(
1306
- STATIC_CONFIG,
1307
- bindings,
1308
- code,
1309
- filePath,
1310
- isBuild
1311
- );
1312
- if (wholeFile) return wholeFile;
1313
- }
1314
- if (hasStaticHandlerCode && isRscEnv && isBuild) {
1315
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1316
- const exportNames = getBindings(code, fnNames).map(
1317
- (b) => b.exportNames[0]
1318
- );
1319
- if (exportNames.length > 0) {
1320
- staticHandlerModules.set(id, exportNames);
1413
+ let changed = false;
1414
+ const handlerConfigs = [
1415
+ hasStaticHandlerCode && STATIC_CONFIG,
1416
+ hasPrerenderHandlerCode && PRERENDER_CONFIG
1417
+ ].filter((c) => !!c).map((cfg) => {
1418
+ const fnNames = getFnNames(cfg.fnName);
1419
+ return { cfg, fnNames };
1420
+ });
1421
+ for (const { cfg, fnNames } of handlerConfigs) {
1422
+ const totalCalls = countCreateCallsForNames(code, fnNames);
1423
+ const supportedBindings = getBindings(code, fnNames).length;
1424
+ if (totalCalls > supportedBindings) {
1425
+ const iterS = new MagicString4(code);
1426
+ const result = transformInlineHandlers(
1427
+ cfg.fnName,
1428
+ VIRTUAL_HANDLER_PREFIX,
1429
+ iterS,
1430
+ code,
1431
+ filePath,
1432
+ virtualHandlers,
1433
+ id,
1434
+ parseAst2
1435
+ );
1436
+ if (result) {
1437
+ changed = true;
1438
+ code = iterS.toString();
1439
+ _bindingsCache.clear();
1440
+ }
1441
+ }
1321
1442
  }
1322
- }
1323
- const s = new MagicString4(code);
1324
- if (hasLoaderCode) {
1325
- const fnNames = getFnNames("createLoader");
1326
- changed = transformLoaders(getBindings(code, fnNames), s, filePath, isBuild) || changed;
1327
- }
1328
- if (hasHandleCode) {
1329
- const fnNames = getFnNames("createHandle");
1330
- changed = transformHandles(
1331
- getBindings(code, fnNames),
1332
- s,
1333
- code,
1334
- filePath,
1335
- isBuild
1336
- ) || changed;
1337
- }
1338
- if (hasLocationStateCode) {
1339
- const fnNames = getFnNames("createLocationState");
1340
- changed = transformLocationState(
1341
- getBindings(code, fnNames),
1342
- s,
1343
- filePath,
1344
- isBuild
1345
- ) || changed;
1346
- }
1347
- if (hasPrerenderHandlerCode) {
1348
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1349
- const bindings = getBindings(code, fnNames);
1350
- if (isRscEnv) {
1351
- changed = transformHandlerIds(
1352
- PRERENDER_CONFIG,
1443
+ if (hasStaticHandlerCode && !isRscEnv) {
1444
+ const fnNames = getFnNames(STATIC_CONFIG.fnName);
1445
+ const bindings = getBindings(code, fnNames);
1446
+ const wholeFile = generateWholeFileStubs(
1447
+ STATIC_CONFIG,
1353
1448
  bindings,
1449
+ code,
1450
+ filePath,
1451
+ isBuild
1452
+ );
1453
+ if (wholeFile) return wholeFile;
1454
+ }
1455
+ if (!isRscEnv && (hasPrerenderHandlerCode || hasStaticHandlerCode)) {
1456
+ const prerenderFnNames = hasPrerenderHandlerCode ? getFnNames(PRERENDER_CONFIG.fnName) : [];
1457
+ const staticFnNames = hasStaticHandlerCode ? getFnNames(STATIC_CONFIG.fnName) : [];
1458
+ const loaderFnNames = hasLoaderCode ? getFnNames("createLoader") : [];
1459
+ const handleFnNames = hasHandleCode ? getFnNames("createHandle") : [];
1460
+ const lsFnNames = hasLocationStateCode ? getFnNames("createLocationState") : [];
1461
+ const allBindings = [];
1462
+ for (const fnNames of [
1463
+ prerenderFnNames,
1464
+ staticFnNames,
1465
+ loaderFnNames,
1466
+ handleFnNames,
1467
+ lsFnNames
1468
+ ]) {
1469
+ if (fnNames.length > 0) {
1470
+ allBindings.push(...getBindings(code, fnNames));
1471
+ }
1472
+ }
1473
+ let canStubWholeFile = allBindings.length > 0 && isExportOnlyFile(code, allBindings);
1474
+ if (canStubWholeFile && (handleFnNames.length > 0 || lsFnNames.length > 0)) {
1475
+ const exportedLocals = new Set(allBindings.map((b) => b.localName));
1476
+ const strippedBindings = [];
1477
+ const localDeclPattern = /(?:^|;|\n)\s*(?:const|let|var|function)\s+(\w+)/g;
1478
+ let declMatch;
1479
+ while ((declMatch = localDeclPattern.exec(code)) !== null) {
1480
+ const name = declMatch[1];
1481
+ if (!exportedLocals.has(name) && !/^_c\d*$/.test(name)) {
1482
+ strippedBindings.push(name);
1483
+ }
1484
+ }
1485
+ const importPattern = /import\s*\{([^}]*)\}\s*from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1486
+ let importMatch;
1487
+ while ((importMatch = importPattern.exec(code)) !== null) {
1488
+ for (const spec of importMatch[1].split(",")) {
1489
+ const m = spec.trim().match(/^[A-Za-z_$][\w$]*(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
1490
+ if (m)
1491
+ strippedBindings.push(m[1] || m[0].trim().split(/\s/)[0]);
1492
+ }
1493
+ }
1494
+ const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1495
+ while ((importMatch = defaultImportPattern.exec(code)) !== null) {
1496
+ strippedBindings.push(importMatch[1]);
1497
+ }
1498
+ const nsImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1499
+ while ((importMatch = nsImportPattern.exec(code)) !== null) {
1500
+ strippedBindings.push(importMatch[1]);
1501
+ }
1502
+ if (strippedBindings.length > 0) {
1503
+ const preservedBindings = allBindings.filter((b) => {
1504
+ const fc = code.slice(b.callExprStart, b.callOpenParenPos + 1);
1505
+ return handleFnNames.some((n) => fc.includes(n)) || lsFnNames.some((n) => fc.includes(n));
1506
+ });
1507
+ const strippedRe = new RegExp(
1508
+ `\\b(?:${strippedBindings.join("|")})\\b`
1509
+ );
1510
+ canStubWholeFile = !preservedBindings.some((b) => {
1511
+ const expr = code.slice(
1512
+ b.callExprStart,
1513
+ b.callCloseParenPos + 1
1514
+ );
1515
+ return strippedRe.test(expr);
1516
+ });
1517
+ }
1518
+ }
1519
+ if (canStubWholeFile) {
1520
+ const lines = [];
1521
+ const neededImports = [];
1522
+ if (handleFnNames.length > 0) neededImports.push("createHandle");
1523
+ if (lsFnNames.length > 0) neededImports.push("createLocationState");
1524
+ if (neededImports.length > 0) {
1525
+ lines.push(
1526
+ `import { ${neededImports.join(", ")} } from "@rangojs/router";`
1527
+ );
1528
+ }
1529
+ for (const binding of allBindings) {
1530
+ const fnCall = code.slice(
1531
+ binding.callExprStart,
1532
+ binding.callOpenParenPos + 1
1533
+ );
1534
+ const isHandle = handleFnNames.some((n) => fnCall.includes(n));
1535
+ const isLocationState = lsFnNames.some((n) => fnCall.includes(n));
1536
+ const primaryName = binding.exportNames[0];
1537
+ const stubId = makeStubId(filePath, primaryName, isBuild);
1538
+ if (isHandle || isLocationState) {
1539
+ const rawArgs = code.slice(
1540
+ binding.callOpenParenPos + 1,
1541
+ binding.callCloseParenPos
1542
+ ).replace(/\b_c\d*\s*=\s*/g, "");
1543
+ const canonicalName = isHandle ? "createHandle" : "createLocationState";
1544
+ const activeFnNames = isHandle ? handleFnNames : lsFnNames;
1545
+ let rawCallee = code.slice(
1546
+ binding.callExprStart,
1547
+ binding.callOpenParenPos
1548
+ );
1549
+ for (const alias of activeFnNames) {
1550
+ if (alias !== canonicalName && rawCallee.startsWith(alias)) {
1551
+ rawCallee = canonicalName + rawCallee.slice(alias.length);
1552
+ break;
1553
+ }
1554
+ }
1555
+ if (isHandle) {
1556
+ const idParam = binding.argCount === 0 ? `undefined, "${stubId}"` : `, "${stubId}"`;
1557
+ lines.push(
1558
+ `export const ${primaryName} = ${rawCallee}(${rawArgs}${idParam});`
1559
+ );
1560
+ lines.push(`${primaryName}.$$id = "${stubId}";`);
1561
+ } else {
1562
+ lines.push(
1563
+ `export const ${primaryName} = ${rawCallee}(${rawArgs});`
1564
+ );
1565
+ lines.push(
1566
+ `${primaryName}.__rsc_ls_key = "__rsc_ls_${stubId}";`
1567
+ );
1568
+ }
1569
+ for (const name of binding.exportNames.slice(1)) {
1570
+ lines.push(`export const ${name} = ${primaryName};`);
1571
+ }
1572
+ } else {
1573
+ let brand = "loader";
1574
+ if (prerenderFnNames.some((n) => fnCall.includes(n))) {
1575
+ brand = PRERENDER_CONFIG.brand;
1576
+ } else if (staticFnNames.some((n) => fnCall.includes(n))) {
1577
+ brand = STATIC_CONFIG.brand;
1578
+ }
1579
+ lines.push(
1580
+ `export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
1581
+ );
1582
+ for (const name of binding.exportNames.slice(1)) {
1583
+ lines.push(`export const ${name} = ${primaryName};`);
1584
+ }
1585
+ }
1586
+ }
1587
+ return { code: lines.join("\n") + "\n", map: null };
1588
+ }
1589
+ }
1590
+ if (hasStaticHandlerCode && isRscEnv && isBuild) {
1591
+ const fnNames = getFnNames(STATIC_CONFIG.fnName);
1592
+ const exportNames = getBindings(code, fnNames).map(
1593
+ (b) => b.exportNames[0]
1594
+ );
1595
+ if (exportNames.length > 0) {
1596
+ staticHandlerModules.set(id, exportNames);
1597
+ }
1598
+ }
1599
+ const s = new MagicString4(code);
1600
+ if (hasLoaderCode) {
1601
+ const fnNames = getFnNames("createLoader");
1602
+ changed = transformLoaders(
1603
+ getBindings(code, fnNames),
1354
1604
  s,
1355
1605
  filePath,
1356
1606
  isBuild
1357
1607
  ) || changed;
1358
- } else {
1359
- changed = stubHandlerExprs(
1360
- PRERENDER_CONFIG,
1361
- bindings,
1608
+ }
1609
+ if (hasHandleCode) {
1610
+ const fnNames = getFnNames("createHandle");
1611
+ changed = transformHandles(
1612
+ getBindings(code, fnNames),
1362
1613
  s,
1614
+ code,
1363
1615
  filePath,
1364
1616
  isBuild
1365
1617
  ) || changed;
1366
1618
  }
1367
- }
1368
- if (hasStaticHandlerCode) {
1369
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1370
- const bindings = getBindings(code, fnNames);
1371
- if (isRscEnv) {
1372
- changed = transformHandlerIds(
1373
- STATIC_CONFIG,
1374
- bindings,
1619
+ if (hasLocationStateCode) {
1620
+ const fnNames = getFnNames("createLocationState");
1621
+ changed = transformLocationState(
1622
+ getBindings(code, fnNames),
1375
1623
  s,
1376
1624
  filePath,
1377
1625
  isBuild
1378
1626
  ) || changed;
1379
- } else {
1380
- changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
1381
1627
  }
1628
+ if (hasPrerenderHandlerCode) {
1629
+ const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1630
+ const bindings = getBindings(code, fnNames);
1631
+ if (isRscEnv) {
1632
+ changed = transformHandlerIds(
1633
+ PRERENDER_CONFIG,
1634
+ bindings,
1635
+ s,
1636
+ filePath,
1637
+ isBuild
1638
+ ) || changed;
1639
+ } else {
1640
+ changed = stubHandlerExprs(
1641
+ PRERENDER_CONFIG,
1642
+ bindings,
1643
+ s,
1644
+ filePath,
1645
+ isBuild
1646
+ ) || changed;
1647
+ }
1648
+ }
1649
+ if (hasStaticHandlerCode) {
1650
+ const fnNames = getFnNames(STATIC_CONFIG.fnName);
1651
+ const bindings = getBindings(code, fnNames);
1652
+ if (isRscEnv) {
1653
+ changed = transformHandlerIds(
1654
+ STATIC_CONFIG,
1655
+ bindings,
1656
+ s,
1657
+ filePath,
1658
+ isBuild
1659
+ ) || changed;
1660
+ } else {
1661
+ changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
1662
+ }
1663
+ }
1664
+ if (!changed) return;
1665
+ return {
1666
+ code: s.toString(),
1667
+ map: s.generateMap({ source: id, includeContent: true })
1668
+ };
1669
+ } finally {
1670
+ counter?.record(id, performance.now() - __t0);
1382
1671
  }
1383
- if (!changed) return;
1384
- return {
1385
- code: s.toString(),
1386
- map: s.generateMap({ source: id, includeContent: true })
1387
- };
1388
1672
  }
1389
1673
  };
1390
1674
  }
@@ -1392,12 +1676,14 @@ ${lazyImports.join(",\n")}
1392
1676
  // src/vite/plugins/use-cache-transform.ts
1393
1677
  import path5 from "node:path";
1394
1678
  import MagicString5 from "magic-string";
1679
+ var debug4 = createRangoDebugger(NS.transform);
1395
1680
  var CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
1396
1681
  var LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
1397
1682
  function useCacheTransform() {
1398
1683
  let projectRoot = "";
1399
1684
  let isBuild = false;
1400
1685
  let rscTransforms = null;
1686
+ const counter = createCounter(debug4, "use-cache");
1401
1687
  return {
1402
1688
  name: "@rangojs/router:use-cache",
1403
1689
  enforce: "post",
@@ -1405,53 +1691,61 @@ function useCacheTransform() {
1405
1691
  projectRoot = config.root;
1406
1692
  isBuild = config.command === "build";
1407
1693
  },
1694
+ buildEnd() {
1695
+ counter?.flush();
1696
+ },
1408
1697
  async transform(code, id) {
1409
1698
  if (this.environment?.name !== "rsc") return;
1410
1699
  if (!code.includes("use cache")) return;
1411
1700
  if (id.includes("/node_modules/") || id.startsWith("\0")) return;
1412
1701
  if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
1413
- if (!rscTransforms) {
1702
+ const start = counter ? performance.now() : 0;
1703
+ try {
1704
+ if (!rscTransforms) {
1705
+ try {
1706
+ rscTransforms = await import("@vitejs/plugin-rsc/transforms");
1707
+ } catch {
1708
+ return;
1709
+ }
1710
+ }
1711
+ const {
1712
+ hasDirective,
1713
+ transformWrapExport,
1714
+ transformHoistInlineDirective
1715
+ } = rscTransforms;
1716
+ let ast;
1414
1717
  try {
1415
- rscTransforms = await import("@vitejs/plugin-rsc/transforms");
1718
+ const { parseAst: parseAst4 } = await import("vite");
1719
+ ast = parseAst4(code);
1416
1720
  } catch {
1417
1721
  return;
1418
1722
  }
1419
- }
1420
- const {
1421
- hasDirective,
1422
- transformWrapExport,
1423
- transformHoistInlineDirective
1424
- } = rscTransforms;
1425
- let ast;
1426
- try {
1427
- const { parseAst: parseAst4 } = await import("vite");
1428
- ast = parseAst4(code);
1429
- } catch {
1430
- return;
1431
- }
1432
- const filePath = normalizePath(path5.relative(projectRoot, id));
1433
- const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
1434
- if (hasDirective(ast.body, "use cache")) {
1435
- return transformFileLevelUseCache(
1723
+ const filePath = normalizePath(path5.relative(projectRoot, id));
1724
+ const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
1725
+ if (hasDirective(ast.body, "use cache")) {
1726
+ return transformFileLevelUseCache(
1727
+ code,
1728
+ ast,
1729
+ filePath,
1730
+ id,
1731
+ isBuild,
1732
+ isLayoutOrTemplate,
1733
+ transformWrapExport
1734
+ );
1735
+ }
1736
+ const functionResult = transformFunctionLevelUseCache(
1436
1737
  code,
1437
1738
  ast,
1438
1739
  filePath,
1439
1740
  id,
1440
1741
  isBuild,
1441
- isLayoutOrTemplate,
1442
- transformWrapExport
1742
+ transformHoistInlineDirective
1443
1743
  );
1744
+ warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1745
+ if (functionResult) return functionResult;
1746
+ } finally {
1747
+ counter?.record(id, performance.now() - start);
1444
1748
  }
1445
- const functionResult = transformFunctionLevelUseCache(
1446
- code,
1447
- ast,
1448
- filePath,
1449
- id,
1450
- isBuild,
1451
- transformHoistInlineDirective
1452
- );
1453
- warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1454
- if (functionResult) return functionResult;
1455
1749
  }
1456
1750
  };
1457
1751
  }
@@ -1574,6 +1868,7 @@ function warnOnNearMissDirectives(ast, fileId, warn) {
1574
1868
  }
1575
1869
 
1576
1870
  // src/vite/plugins/client-ref-dedup.ts
1871
+ var debug5 = createRangoDebugger(NS.transform);
1577
1872
  var CLIENT_IN_SERVER_PROXY_PREFIX = "virtual:vite-rsc/client-in-server-package-proxy/";
1578
1873
  function extractPackageName(absolutePath) {
1579
1874
  const marker = "/node_modules/";
@@ -1590,6 +1885,7 @@ function extractPackageName(absolutePath) {
1590
1885
  }
1591
1886
  function clientRefDedup() {
1592
1887
  let clientExclude = [];
1888
+ const dedupedPackages = /* @__PURE__ */ new Set();
1593
1889
  return {
1594
1890
  name: "@rangojs/router:client-ref-dedup",
1595
1891
  enforce: "pre",
@@ -1598,6 +1894,15 @@ function clientRefDedup() {
1598
1894
  const clientEnv = config.environments?.["client"];
1599
1895
  clientExclude = clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
1600
1896
  },
1897
+ buildEnd() {
1898
+ if (debug5 && dedupedPackages.size > 0) {
1899
+ debug5(
1900
+ "client-ref-dedup: redirected %d package(s) (%s)",
1901
+ dedupedPackages.size,
1902
+ [...dedupedPackages].join(",")
1903
+ );
1904
+ }
1905
+ },
1601
1906
  resolveId(source, importer, options) {
1602
1907
  if (this.environment?.name !== "client") return;
1603
1908
  if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
@@ -1606,6 +1911,7 @@ function clientRefDedup() {
1606
1911
  const packageName = extractPackageName(source);
1607
1912
  if (!packageName) return;
1608
1913
  if (clientExclude.includes(packageName)) return;
1914
+ if (debug5) dedupedPackages.add(packageName);
1609
1915
  return `\0rango:dedup/${packageName}`;
1610
1916
  },
1611
1917
  load(id) {
@@ -1728,12 +2034,13 @@ function getVirtualVersionContent(version) {
1728
2034
 
1729
2035
  // src/vite/utils/package-resolution.ts
1730
2036
  import { existsSync } from "node:fs";
2037
+ import { createRequire } from "node:module";
1731
2038
  import { resolve } from "node:path";
1732
2039
 
1733
2040
  // package.json
1734
2041
  var package_default = {
1735
2042
  name: "@rangojs/router",
1736
- version: "0.0.0-experimental.66",
2043
+ version: "0.0.0-experimental.66cdebe3",
1737
2044
  description: "Django-inspired RSC router with composable URL patterns",
1738
2045
  keywords: [
1739
2046
  "react",
@@ -1866,16 +2173,18 @@ var package_default = {
1866
2173
  tag: "experimental"
1867
2174
  },
1868
2175
  scripts: {
1869
- 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",
2176
+ 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/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",
1870
2177
  prepublishOnly: "pnpm build",
1871
- typecheck: "tsc --noEmit",
2178
+ typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit",
1872
2179
  test: "playwright test",
1873
2180
  "test:ui": "playwright test --ui",
1874
2181
  "test:unit": "vitest run",
1875
2182
  "test:unit:watch": "vitest"
1876
2183
  },
1877
2184
  dependencies: {
1878
- "@vitejs/plugin-rsc": "^0.5.19",
2185
+ "@types/debug": "^4.1.12",
2186
+ "@vitejs/plugin-rsc": "^0.5.23",
2187
+ debug: "^4.4.1",
1879
2188
  "magic-string": "^0.30.17",
1880
2189
  picomatch: "^4.0.3",
1881
2190
  "rsc-html-stream": "^0.0.7"
@@ -1895,7 +2204,7 @@ var package_default = {
1895
2204
  },
1896
2205
  peerDependencies: {
1897
2206
  "@cloudflare/vite-plugin": "^1.25.0",
1898
- "@vitejs/plugin-rsc": "^0.5.14",
2207
+ "@vitejs/plugin-rsc": "^0.5.23",
1899
2208
  react: "^18.0.0 || ^19.0.0",
1900
2209
  vite: "^7.3.0"
1901
2210
  },
@@ -1910,6 +2219,7 @@ var package_default = {
1910
2219
  };
1911
2220
 
1912
2221
  // src/vite/utils/package-resolution.ts
2222
+ var require2 = createRequire(import.meta.url);
1913
2223
  var VIRTUAL_PACKAGE_NAME = "@rangojs/router";
1914
2224
  function getPublishedPackageName() {
1915
2225
  return package_default.name;
@@ -1950,6 +2260,20 @@ function getPackageAliases() {
1950
2260
  }
1951
2261
  return aliases;
1952
2262
  }
2263
+ function getVendorAliases() {
2264
+ const specs = [
2265
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
2266
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
2267
+ ];
2268
+ const aliases = {};
2269
+ for (const spec of specs) {
2270
+ try {
2271
+ aliases[spec] = require2.resolve(spec);
2272
+ } catch {
2273
+ }
2274
+ }
2275
+ return aliases;
2276
+ }
1953
2277
 
1954
2278
  // src/build/route-types/param-extraction.ts
1955
2279
  function extractParamsFromPattern(pattern) {
@@ -2847,6 +3171,7 @@ import * as Vite from "vite";
2847
3171
 
2848
3172
  // src/vite/plugins/performance-tracks.ts
2849
3173
  import { readFile } from "node:fs/promises";
3174
+ var debug6 = createRangoDebugger(NS.transform);
2850
3175
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
2851
3176
  function buildPatchReplacement(match, debugInfoVar) {
2852
3177
  return `${match}
@@ -2888,19 +3213,23 @@ function performanceTracksOptimizeDepsPlugin() {
2888
3213
  };
2889
3214
  }
2890
3215
  function performanceTracksPlugin() {
3216
+ const counter = createCounter(debug6, "performance-tracks");
2891
3217
  return {
2892
3218
  name: "@rangojs/router:performance-tracks",
3219
+ buildEnd() {
3220
+ counter?.flush();
3221
+ },
2893
3222
  transform(code, id) {
2894
3223
  if (!id.includes("react-server-dom") || !id.includes("client")) return;
2895
- const patched = patchRsdwClientDebugInfoRecovery(code);
2896
- if (!patched.debugInfoVar) return;
2897
- if (process.env.INTERNAL_RANGO_DEBUG)
2898
- console.log(
2899
- "[perf-tracks] patched RSDW client (var:",
2900
- patched.debugInfoVar,
2901
- ")"
2902
- );
2903
- return patched.code;
3224
+ const start = counter ? performance.now() : 0;
3225
+ try {
3226
+ const patched = patchRsdwClientDebugInfoRecovery(code);
3227
+ if (!patched.debugInfoVar) return;
3228
+ debug6?.("patched RSDW client (var: %s)", patched.debugInfoVar);
3229
+ return patched.code;
3230
+ } finally {
3231
+ counter?.record(id, performance.now() - start);
3232
+ }
2904
3233
  }
2905
3234
  };
2906
3235
  }
@@ -3047,11 +3376,10 @@ function createVersionInjectorPlugin(rscEntryPath) {
3047
3376
  if (normalizedId !== normalizedEntry) {
3048
3377
  return null;
3049
3378
  }
3050
- const prepend = [];
3379
+ const prepend = [
3380
+ `import "virtual:rsc-router/routes-manifest";`
3381
+ ];
3051
3382
  let newCode = code;
3052
- if (!code.includes("virtual:rsc-router/routes-manifest")) {
3053
- prepend.push(`import "virtual:rsc-router/routes-manifest";`);
3054
- }
3055
3383
  const needsVersion = code.includes("createRSCHandler") && !code.includes("@rangojs/router:version") && /createRSCHandler\s*\(\s*\{/.test(code);
3056
3384
  if (needsVersion) {
3057
3385
  prepend.push(`import { VERSION } from "@rangojs/router:version";`);
@@ -3060,8 +3388,21 @@ function createVersionInjectorPlugin(rscEntryPath) {
3060
3388
  "createRSCHandler({\n version: VERSION,"
3061
3389
  );
3062
3390
  }
3063
- if (prepend.length === 0 && newCode === code) return null;
3064
- newCode = prepend.join("\n") + (prepend.length > 0 ? "\n" : "") + newCode;
3391
+ const lines = newCode.split("\n");
3392
+ let insertAt = 0;
3393
+ while (insertAt < lines.length) {
3394
+ const trimmed = lines[insertAt].trim();
3395
+ if (trimmed === "" || /^\/\/\/\s*<reference\b/.test(trimmed)) {
3396
+ insertAt++;
3397
+ } else {
3398
+ break;
3399
+ }
3400
+ }
3401
+ newCode = [
3402
+ ...lines.slice(0, insertAt),
3403
+ ...prepend,
3404
+ ...lines.slice(insertAt)
3405
+ ].join("\n");
3065
3406
  return {
3066
3407
  code: newCode,
3067
3408
  map: null
@@ -3071,6 +3412,7 @@ function createVersionInjectorPlugin(rscEntryPath) {
3071
3412
  }
3072
3413
 
3073
3414
  // src/vite/plugins/cjs-to-esm.ts
3415
+ var debug7 = createRangoDebugger(NS.transform);
3074
3416
  function createCjsToEsmPlugin() {
3075
3417
  return {
3076
3418
  name: "@rangojs/router:cjs-to-esm",
@@ -3080,6 +3422,7 @@ function createCjsToEsmPlugin() {
3080
3422
  if (cleanId.includes("vendor/react-server-dom/client.browser.js") || cleanId.includes("vendor\\react-server-dom\\client.browser.js")) {
3081
3423
  const isProd = process.env.NODE_ENV === "production";
3082
3424
  const cjsFile = isProd ? "./cjs/react-server-dom-webpack-client.browser.production.js" : "./cjs/react-server-dom-webpack-client.browser.development.js";
3425
+ debug7?.("cjs-to-esm entry redirect %s", id);
3083
3426
  return {
3084
3427
  code: `export * from "${cjsFile}";`,
3085
3428
  map: null
@@ -3115,6 +3458,7 @@ function createCjsToEsmPlugin() {
3115
3458
  "export const $1 ="
3116
3459
  );
3117
3460
  transformed = license + "\n" + transformed;
3461
+ debug7?.("cjs-to-esm body rewrite %s", id);
3118
3462
  return {
3119
3463
  code: transformed,
3120
3464
  map: null
@@ -3129,7 +3473,7 @@ function createCjsToEsmPlugin() {
3129
3473
  import { createServer as createViteServer } from "vite";
3130
3474
  import { resolve as resolve8 } from "node:path";
3131
3475
  import { readFileSync as readFileSync6 } from "node:fs";
3132
- import { createRequire } from "node:module";
3476
+ import { createRequire as createRequire2, register } from "node:module";
3133
3477
  import { pathToFileURL } from "node:url";
3134
3478
 
3135
3479
  // src/vite/plugins/virtual-stub-plugin.ts
@@ -3156,9 +3500,117 @@ function createVirtualStubPlugin() {
3156
3500
  };
3157
3501
  }
3158
3502
 
3503
+ // src/vite/plugins/cloudflare-protocol-stub.ts
3504
+ var VIRTUAL_PREFIX = "virtual:rango-cloudflare-stub-";
3505
+ var NULL_PREFIX = "\0" + VIRTUAL_PREFIX;
3506
+ var CF_PREFIX = "cloudflare:";
3507
+ var BUILD_ENV_GLOBAL_KEY = "__rango_build_env__";
3508
+ var SOURCE_EXT_RE = /\.[mc]?[jt]sx?$/;
3509
+ var IMPORT_NODE_TYPES = /* @__PURE__ */ new Set([
3510
+ "ImportDeclaration",
3511
+ "ImportExpression",
3512
+ "ExportNamedDeclaration",
3513
+ "ExportAllDeclaration"
3514
+ ]);
3515
+ var STUBS = {
3516
+ "cloudflare:workers": `
3517
+ export class DurableObject { constructor(_ctx, _env) {} }
3518
+ export class WorkerEntrypoint { constructor(_ctx, _env) {} }
3519
+ export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
3520
+ export class RpcTarget {}
3521
+ export const env = globalThis[${JSON.stringify(BUILD_ENV_GLOBAL_KEY)}] ?? {};
3522
+ export default {};
3523
+ `,
3524
+ "cloudflare:email": `
3525
+ export class EmailMessage { constructor(_from, _to, _raw) {} }
3526
+ export default {};
3527
+ `,
3528
+ "cloudflare:sockets": `
3529
+ export function connect() { return {}; }
3530
+ export default {};
3531
+ `,
3532
+ "cloudflare:workflows": `
3533
+ export class NonRetryableError extends Error {
3534
+ constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
3535
+ }
3536
+ export default {};
3537
+ `
3538
+ };
3539
+ var FALLBACK_STUB = `export default {};
3540
+ `;
3541
+ function createCloudflareProtocolStubPlugin() {
3542
+ return {
3543
+ name: "@rangojs/router:cloudflare-protocol-stub",
3544
+ transform(code, id) {
3545
+ const cleanId = id.split("?")[0] ?? id;
3546
+ if (!SOURCE_EXT_RE.test(cleanId)) return null;
3547
+ if (!code.includes(CF_PREFIX)) return null;
3548
+ let ast;
3549
+ try {
3550
+ ast = this.parse(code);
3551
+ } catch {
3552
+ return null;
3553
+ }
3554
+ const hits = [];
3555
+ walk(ast, (node) => {
3556
+ if (!IMPORT_NODE_TYPES.has(node.type)) return;
3557
+ const source = node.source;
3558
+ if (!source || source.type !== "Literal") return;
3559
+ if (typeof source.value !== "string") return;
3560
+ if (!source.value.startsWith(CF_PREFIX)) return;
3561
+ if (typeof source.start !== "number" || typeof source.end !== "number")
3562
+ return;
3563
+ hits.push({
3564
+ start: source.start,
3565
+ end: source.end,
3566
+ value: source.value
3567
+ });
3568
+ });
3569
+ if (hits.length === 0) return null;
3570
+ hits.sort((a, b) => b.start - a.start);
3571
+ let out = code;
3572
+ for (const hit of hits) {
3573
+ const submodule = hit.value.slice(CF_PREFIX.length);
3574
+ const quote = code[hit.start] === "'" ? "'" : '"';
3575
+ out = out.slice(0, hit.start) + quote + VIRTUAL_PREFIX + submodule + quote + out.slice(hit.end);
3576
+ }
3577
+ return { code: out, map: null };
3578
+ },
3579
+ resolveId(id) {
3580
+ if (id.startsWith(VIRTUAL_PREFIX)) {
3581
+ return "\0" + id;
3582
+ }
3583
+ return null;
3584
+ },
3585
+ load(id) {
3586
+ if (!id.startsWith(NULL_PREFIX)) return null;
3587
+ const submodule = id.slice(NULL_PREFIX.length);
3588
+ const specifier = CF_PREFIX + submodule;
3589
+ return STUBS[specifier] ?? FALLBACK_STUB;
3590
+ }
3591
+ };
3592
+ }
3593
+ function walk(node, visit) {
3594
+ if (!node || typeof node !== "object") return;
3595
+ if (Array.isArray(node)) {
3596
+ for (const child of node) walk(child, visit);
3597
+ return;
3598
+ }
3599
+ const n = node;
3600
+ if (typeof n.type !== "string") return;
3601
+ visit(n);
3602
+ for (const key in n) {
3603
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
3604
+ continue;
3605
+ }
3606
+ walk(n[key], visit);
3607
+ }
3608
+ }
3609
+
3159
3610
  // src/vite/plugins/client-ref-hashing.ts
3160
3611
  import { relative } from "node:path";
3161
3612
  import { createHash as createHash2 } from "node:crypto";
3613
+ var debug8 = createRangoDebugger(NS.transform);
3162
3614
  var CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
3163
3615
  var CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
3164
3616
  var FS_PREFIX = "/@fs/";
@@ -3197,6 +3649,7 @@ function transformClientRefs(code, projectRoot) {
3197
3649
  return hasReplacement ? result : null;
3198
3650
  }
3199
3651
  function hashClientRefs(projectRoot) {
3652
+ const counter = createCounter(debug8, "hash-client-refs");
3200
3653
  return {
3201
3654
  name: "@rangojs/router:hash-client-refs",
3202
3655
  // Run after the RSC plugin's transform (default enforce is normal)
@@ -3204,10 +3657,18 @@ function hashClientRefs(projectRoot) {
3204
3657
  applyToEnvironment(env) {
3205
3658
  return env.name === "rsc";
3206
3659
  },
3207
- transform(code, _id) {
3208
- const result = transformClientRefs(code, projectRoot);
3209
- if (result === null) return;
3210
- return { code: result, map: null };
3660
+ buildEnd() {
3661
+ counter?.flush();
3662
+ },
3663
+ transform(code, id) {
3664
+ const start = counter ? performance.now() : 0;
3665
+ try {
3666
+ const result = transformClientRefs(code, projectRoot);
3667
+ if (result === null) return;
3668
+ return { code: result, map: null };
3669
+ } finally {
3670
+ counter?.record(id, performance.now() - start);
3671
+ }
3211
3672
  }
3212
3673
  };
3213
3674
  }
@@ -3335,6 +3796,12 @@ function markSelfGenWrite(state, filePath, content) {
3335
3796
  state.selfWrittenGenFiles.set(filePath, { at: Date.now(), hash });
3336
3797
  }
3337
3798
  function consumeSelfGenWrite(state, filePath) {
3799
+ return checkSelfGenWrite(state, filePath, true);
3800
+ }
3801
+ function peekSelfGenWrite(state, filePath) {
3802
+ return checkSelfGenWrite(state, filePath, false);
3803
+ }
3804
+ function checkSelfGenWrite(state, filePath, consume) {
3338
3805
  const info = state.selfWrittenGenFiles.get(filePath);
3339
3806
  if (!info) return false;
3340
3807
  if (Date.now() - info.at > state.SELF_WRITE_WINDOW_MS) {
@@ -3345,7 +3812,7 @@ function consumeSelfGenWrite(state, filePath) {
3345
3812
  const current = readFileSync3(filePath, "utf-8");
3346
3813
  const currentHash = createHash3("sha256").update(current).digest("hex");
3347
3814
  if (currentHash === info.hash) {
3348
- state.selfWrittenGenFiles.delete(filePath);
3815
+ if (consume) state.selfWrittenGenFiles.delete(filePath);
3349
3816
  return true;
3350
3817
  }
3351
3818
  return false;
@@ -3449,11 +3916,19 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
3449
3916
  let hadOmittedOptional = false;
3450
3917
  for (const [key, value] of Object.entries(params)) {
3451
3918
  const escaped = escapeRegExp2(key);
3452
- result = result.replace(
3453
- new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
3454
- encode(value)
3455
- );
3456
- result = result.replace(`*${key}`, encode(value));
3919
+ if (value === "") {
3920
+ result = result.replace(
3921
+ new RegExp(`:${escaped}(\\([^)]*\\))?(?!\\?)`),
3922
+ ""
3923
+ );
3924
+ result = result.replace(`*${key}`, "");
3925
+ } else {
3926
+ result = result.replace(
3927
+ new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
3928
+ encode(value)
3929
+ );
3930
+ result = result.replace(`*${key}`, encode(value));
3931
+ }
3457
3932
  }
3458
3933
  result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
3459
3934
  hadOmittedOptional = true;
@@ -3559,8 +4034,14 @@ function copyStagedBuildAssets(projectRoot, fileNames) {
3559
4034
  }
3560
4035
 
3561
4036
  // src/vite/discovery/prerender-collection.ts
4037
+ var debug9 = createRangoDebugger(NS.prerender);
3562
4038
  async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3563
4039
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode) return;
4040
+ const overallStart = debug9 ? performance.now() : 0;
4041
+ debug9?.(
4042
+ "expandPrerenderRoutes: start (%d router manifest(s))",
4043
+ allManifests.length
4044
+ );
3564
4045
  const entries = [];
3565
4046
  const allRoutes = {};
3566
4047
  for (const { manifest: m } of allManifests) {
@@ -3572,100 +4053,151 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3572
4053
  if (!params) return pattern;
3573
4054
  return substituteRouteParams(pattern, params);
3574
4055
  };
4056
+ let resolvedRoutes = 0;
4057
+ let totalDynamic = 0;
3575
4058
  for (const { manifest } of allManifests) {
3576
4059
  if (!manifest.prerenderRoutes) continue;
3577
- const defs = manifest._prerenderDefs || {};
3578
- const passthroughSet = new Set(manifest.passthroughRoutes || []);
3579
4060
  for (const routeName of manifest.prerenderRoutes) {
3580
4061
  const pattern = manifest.routeManifest[routeName];
3581
- if (!pattern) continue;
3582
- const def = defs[routeName];
3583
- const isPassthroughRoute = passthroughSet.has(routeName);
3584
- const hasDynamic = pattern.includes(":") || pattern.includes("*");
3585
- if (!hasDynamic) {
3586
- entries.push({
3587
- urlPath: pattern.replace(/\/$/, "") || "/",
3588
- routeName,
3589
- concurrency: 1,
3590
- isPassthroughRoute
3591
- });
3592
- } else {
3593
- if (def?.getParams) {
3594
- try {
3595
- const buildVars = {};
3596
- const buildEnv = state.resolvedBuildEnv;
3597
- const getParamsCtx = {
3598
- build: true,
3599
- dev: !state.isBuildMode,
3600
- set: ((keyOrVar, value) => {
3601
- contextSet(buildVars, keyOrVar, value);
3602
- }),
3603
- reverse: getParamsReverse,
3604
- get env() {
3605
- if (buildEnv !== void 0) return buildEnv;
3606
- throw new Error(
3607
- "[rsc-router] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
3608
- );
3609
- }
3610
- };
3611
- const paramsList = await def.getParams(getParamsCtx);
3612
- const concurrency = def.options?.concurrency ?? 1;
3613
- const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
3614
- for (const params of paramsList) {
3615
- let url = substituteRouteParams(
3616
- pattern,
3617
- params,
3618
- encodePathParam
3619
- );
3620
- if (url.includes("*")) {
3621
- const wildcardValue = params["*"] ?? params.splat;
3622
- if (wildcardValue !== void 0) {
3623
- url = url.replace(/\*[^/]*$/, encodePathParam(wildcardValue));
4062
+ if (pattern && (pattern.includes(":") || pattern.includes("*"))) {
4063
+ totalDynamic++;
4064
+ }
4065
+ }
4066
+ }
4067
+ const paramsStart = performance.now();
4068
+ const progressInterval = totalDynamic > 0 ? setInterval(() => {
4069
+ const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
4070
+ console.log(
4071
+ `[rsc-router] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
4072
+ );
4073
+ }, 5e3) : void 0;
4074
+ try {
4075
+ for (const { manifest } of allManifests) {
4076
+ if (!manifest.prerenderRoutes) continue;
4077
+ const defs = manifest._prerenderDefs || {};
4078
+ const passthroughSet = new Set(manifest.passthroughRoutes || []);
4079
+ for (const routeName of manifest.prerenderRoutes) {
4080
+ const pattern = manifest.routeManifest[routeName];
4081
+ if (!pattern) continue;
4082
+ const def = defs[routeName];
4083
+ const isPassthroughRoute = passthroughSet.has(routeName);
4084
+ const hasDynamic = pattern.includes(":") || pattern.includes("*");
4085
+ if (!hasDynamic) {
4086
+ entries.push({
4087
+ urlPath: pattern.replace(/\/$/, "") || "/",
4088
+ routeName,
4089
+ concurrency: 1,
4090
+ isPassthroughRoute
4091
+ });
4092
+ } else {
4093
+ if (def?.getParams) {
4094
+ const getParamsStart = debug9 ? performance.now() : 0;
4095
+ try {
4096
+ const buildVars = {};
4097
+ const buildEnv = state.resolvedBuildEnv;
4098
+ const getParamsCtx = {
4099
+ build: true,
4100
+ dev: !state.isBuildMode,
4101
+ set: ((keyOrVar, value) => {
4102
+ contextSet(buildVars, keyOrVar, value);
4103
+ }),
4104
+ reverse: getParamsReverse,
4105
+ get env() {
4106
+ if (buildEnv !== void 0) return buildEnv;
4107
+ throw new Error(
4108
+ "[rsc-router] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
4109
+ );
3624
4110
  }
3625
- }
3626
- entries.push({
3627
- urlPath: url.replace(/\/$/, "") || "/",
4111
+ };
4112
+ const paramsList = await def.getParams(getParamsCtx);
4113
+ debug9?.(
4114
+ "getParams %s -> %d params (%sms)",
3628
4115
  routeName,
3629
- concurrency,
3630
- ...hasBuildVars ? { buildVars } : {},
3631
- isPassthroughRoute
3632
- });
3633
- }
3634
- } catch (err) {
3635
- if (err.name === "Skip") {
3636
- console.log(
3637
- `[rsc-router] SKIP route "${routeName}" - ${err.message}`
4116
+ paramsList.length,
4117
+ (performance.now() - getParamsStart).toFixed(1)
3638
4118
  );
3639
- notifyOnError(
3640
- registry,
3641
- err,
3642
- "prerender",
3643
- routeName,
3644
- void 0,
3645
- true
4119
+ const concurrency = def.options?.concurrency ?? 1;
4120
+ const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
4121
+ for (const params of paramsList) {
4122
+ let url = substituteRouteParams(
4123
+ pattern,
4124
+ params,
4125
+ encodePathParam
4126
+ );
4127
+ if (url.includes("*")) {
4128
+ const wildcardValue = params["*"] ?? params.splat;
4129
+ if (wildcardValue !== void 0) {
4130
+ url = url.replace(
4131
+ /\*[^/]*$/,
4132
+ encodePathParam(wildcardValue)
4133
+ );
4134
+ }
4135
+ }
4136
+ entries.push({
4137
+ urlPath: url.replace(/\/$/, "") || "/",
4138
+ routeName,
4139
+ concurrency,
4140
+ ...hasBuildVars ? { buildVars } : {},
4141
+ isPassthroughRoute
4142
+ });
4143
+ }
4144
+ resolvedRoutes++;
4145
+ } catch (err) {
4146
+ resolvedRoutes++;
4147
+ if (err.name === "Skip") {
4148
+ console.log(
4149
+ `[rsc-router] SKIP route "${routeName}" - ${err.message}`
4150
+ );
4151
+ notifyOnError(
4152
+ registry,
4153
+ err,
4154
+ "prerender",
4155
+ routeName,
4156
+ void 0,
4157
+ true
4158
+ );
4159
+ continue;
4160
+ }
4161
+ console.error(
4162
+ `[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`
3646
4163
  );
3647
- continue;
4164
+ notifyOnError(registry, err, "prerender", routeName);
4165
+ throw err;
3648
4166
  }
3649
- console.error(
3650
- `[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`
4167
+ } else {
4168
+ console.warn(
4169
+ `[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
3651
4170
  );
3652
- notifyOnError(registry, err, "prerender", routeName);
3653
- throw err;
3654
4171
  }
3655
- } else {
3656
- console.warn(
3657
- `[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
3658
- );
3659
4172
  }
3660
4173
  }
3661
4174
  }
4175
+ } finally {
4176
+ if (progressInterval) {
4177
+ clearInterval(progressInterval);
4178
+ const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
4179
+ console.log(
4180
+ `[rsc-router] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
4181
+ );
4182
+ }
4183
+ }
4184
+ if (entries.length === 0) {
4185
+ debug9?.(
4186
+ "no prerender entries (done in %sms)",
4187
+ (performance.now() - overallStart).toFixed(1)
4188
+ );
4189
+ return;
3662
4190
  }
3663
- if (entries.length === 0) return;
3664
4191
  const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
3665
4192
  const concurrencyNote = maxConcurrency > 1 ? ` (concurrency: ${maxConcurrency})` : "";
3666
4193
  console.log(
3667
4194
  `[rsc-router] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
3668
4195
  );
4196
+ debug9?.(
4197
+ "prerender loop: %d entries, max concurrency %d",
4198
+ entries.length,
4199
+ maxConcurrency
4200
+ );
3669
4201
  const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
3670
4202
  const manifestEntries = {};
3671
4203
  let doneCount = 0;
@@ -3772,10 +4304,22 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3772
4304
  console.log(
3773
4305
  `[rsc-router] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`
3774
4306
  );
4307
+ debug9?.(
4308
+ "expandPrerenderRoutes done: %d done, %d skipped, %sms (overall %sms)",
4309
+ doneCount,
4310
+ skipCount,
4311
+ totalElapsed,
4312
+ (performance.now() - overallStart).toFixed(1)
4313
+ );
3775
4314
  }
3776
4315
  async function renderStaticHandlers(state, rscEnv, registry) {
3777
4316
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode || !state.resolvedStaticModules?.size)
3778
4317
  return;
4318
+ const overallStart = debug9 ? performance.now() : 0;
4319
+ debug9?.(
4320
+ "renderStaticHandlers: start (%d static module(s))",
4321
+ state.resolvedStaticModules.size
4322
+ );
3779
4323
  const manifestEntries = {};
3780
4324
  let staticDone = 0;
3781
4325
  let staticSkip = 0;
@@ -3865,13 +4409,29 @@ async function renderStaticHandlers(state, rscEnv, registry) {
3865
4409
  console.log(
3866
4410
  `[rsc-router] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`
3867
4411
  );
4412
+ debug9?.(
4413
+ "renderStaticHandlers done: %d done, %d skipped, %sms (overall %sms)",
4414
+ staticDone,
4415
+ staticSkip,
4416
+ totalStaticElapsed,
4417
+ (performance.now() - overallStart).toFixed(1)
4418
+ );
3868
4419
  }
3869
4420
 
3870
4421
  // src/vite/discovery/discover-routers.ts
4422
+ var debug10 = createRangoDebugger(NS.discovery);
3871
4423
  async function discoverRouters(state, rscEnv) {
3872
4424
  if (!state.resolvedEntryPath) return;
3873
- await rscEnv.runner.import(state.resolvedEntryPath);
3874
- const serverMod = await rscEnv.runner.import("@rangojs/router/server");
4425
+ await timed(
4426
+ debug10,
4427
+ "inner: import entry",
4428
+ () => rscEnv.runner.import(state.resolvedEntryPath)
4429
+ );
4430
+ const serverMod = await timed(
4431
+ debug10,
4432
+ "inner: import @rangojs/router/server",
4433
+ () => rscEnv.runner.import("@rangojs/router/server")
4434
+ );
3875
4435
  let registry = serverMod.RouterRegistry;
3876
4436
  if (!registry || registry.size === 0) {
3877
4437
  try {
@@ -3913,8 +4473,13 @@ async function discoverRouters(state, rscEnv) {
3913
4473
  );
3914
4474
  }
3915
4475
  }
3916
- const buildMod = await rscEnv.runner.import("@rangojs/router/build");
4476
+ const buildMod = await timed(
4477
+ debug10,
4478
+ "inner: import @rangojs/router/build",
4479
+ () => rscEnv.runner.import("@rangojs/router/build")
4480
+ );
3917
4481
  const generateManifestFull = buildMod.generateManifestFull;
4482
+ debug10?.("inner: found %d router(s) in registry", registry.size);
3918
4483
  const nestedRouterConflict = findNestedRouterConflict(
3919
4484
  [...registry.values()].map((router) => router.__sourceFile).filter(
3920
4485
  (sourceFile) => typeof sourceFile === "string"
@@ -3933,6 +4498,7 @@ async function discoverRouters(state, rscEnv) {
3933
4498
  let mergedRouteTrailingSlash = {};
3934
4499
  let routerMountIndex = 0;
3935
4500
  const allManifests = [];
4501
+ const manifestGenStart = debug10 ? performance.now() : 0;
3936
4502
  for (const [id, router] of registry) {
3937
4503
  if (!router.urlpatterns || !generateManifestFull) {
3938
4504
  continue;
@@ -4007,7 +4573,13 @@ async function discoverRouters(state, rscEnv) {
4007
4573
  );
4008
4574
  }
4009
4575
  }
4576
+ debug10?.(
4577
+ "inner: generated manifests for %d router(s) (%sms)",
4578
+ allManifests.length,
4579
+ (performance.now() - manifestGenStart).toFixed(1)
4580
+ );
4010
4581
  let newMergedRouteTrie = null;
4582
+ const trieStart = debug10 ? performance.now() : 0;
4011
4583
  if (Object.keys(newMergedRouteManifest).length > 0) {
4012
4584
  const buildRouteTrie = buildMod.buildRouteTrie;
4013
4585
  if (buildRouteTrie && mergedRouteAncestry) {
@@ -4070,6 +4642,10 @@ async function discoverRouters(state, rscEnv) {
4070
4642
  }
4071
4643
  }
4072
4644
  }
4645
+ debug10?.(
4646
+ "inner: trie build done (%sms)",
4647
+ (performance.now() - trieStart).toFixed(1)
4648
+ );
4073
4649
  state.mergedRouteManifest = newMergedRouteManifest;
4074
4650
  state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
4075
4651
  state.perRouterManifests = newPerRouterManifests;
@@ -4509,8 +5085,101 @@ function postprocessBundle(state) {
4509
5085
  }
4510
5086
  }
4511
5087
 
5088
+ // src/vite/discovery/gate-state.ts
5089
+ function createDiscoveryGate(s, debug11) {
5090
+ let gatePending = false;
5091
+ let gateResolver = () => {
5092
+ };
5093
+ let inProgress = false;
5094
+ let queued = false;
5095
+ let pendingEvents = false;
5096
+ const beginGate = () => {
5097
+ if (gatePending) return;
5098
+ s.discoveryDone = new Promise((resolve10) => {
5099
+ gateResolver = resolve10;
5100
+ });
5101
+ gatePending = true;
5102
+ };
5103
+ const resolveGate = () => {
5104
+ if (!gatePending) return;
5105
+ if (inProgress || queued || pendingEvents) {
5106
+ debug11?.(
5107
+ "hmr: resolveGate deferred \u2014 work in flight (inProgress=%s queued=%s pendingEvents=%s)",
5108
+ inProgress,
5109
+ queued,
5110
+ pendingEvents
5111
+ );
5112
+ return;
5113
+ }
5114
+ gatePending = false;
5115
+ debug11?.("hmr: discoveryDone resolved");
5116
+ gateResolver();
5117
+ };
5118
+ const noteRouteEvent = () => {
5119
+ pendingEvents = true;
5120
+ beginGate();
5121
+ };
5122
+ const runRefreshCycle = async (work) => {
5123
+ if (inProgress) {
5124
+ queued = true;
5125
+ debug11?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
5126
+ return;
5127
+ }
5128
+ pendingEvents = false;
5129
+ inProgress = true;
5130
+ try {
5131
+ await work();
5132
+ } finally {
5133
+ inProgress = false;
5134
+ if (queued) {
5135
+ queued = false;
5136
+ debug11?.("hmr: consuming queued rediscovery");
5137
+ runRefreshCycle(work).catch((err) => {
5138
+ debug11?.(
5139
+ "hmr: queued cycle rejected \u2014 releasing gate (%s)",
5140
+ err instanceof Error ? err.message : String(err)
5141
+ );
5142
+ resolveGate();
5143
+ });
5144
+ } else if (pendingEvents) {
5145
+ debug11?.(
5146
+ "hmr: holding gate for pending events (debounce not yet fired)"
5147
+ );
5148
+ } else {
5149
+ resolveGate();
5150
+ }
5151
+ }
5152
+ };
5153
+ return {
5154
+ beginGate,
5155
+ resolveGate,
5156
+ noteRouteEvent,
5157
+ runRefreshCycle,
5158
+ state: () => ({ gatePending, inProgress, queued, pendingEvents })
5159
+ };
5160
+ }
5161
+
4512
5162
  // src/vite/router-discovery.ts
5163
+ var debugDiscovery = createRangoDebugger(NS.discovery);
5164
+ var debugRoutes = createRangoDebugger(NS.routes);
5165
+ var debugBuild = createRangoDebugger(NS.build);
5166
+ var debugDev = createRangoDebugger(NS.dev);
5167
+ var loaderHookRegistered = false;
5168
+ function ensureCloudflareProtocolLoaderRegistered() {
5169
+ if (loaderHookRegistered) return;
5170
+ loaderHookRegistered = true;
5171
+ try {
5172
+ register(
5173
+ new URL("./plugins/cloudflare-protocol-loader-hook.mjs", import.meta.url)
5174
+ );
5175
+ } catch (err) {
5176
+ console.warn(
5177
+ `[rsc-router] Could not register Node ESM loader hook for cloudflare:* imports (${err?.message ?? err}). Falling back to Vite transform only.`
5178
+ );
5179
+ }
5180
+ }
4513
5181
  async function createTempRscServer(state, options = {}) {
5182
+ ensureCloudflareProtocolLoaderRegistered();
4514
5183
  const { default: rsc } = await import("@vitejs/plugin-rsc");
4515
5184
  return createViteServer({
4516
5185
  root: state.projectRoot,
@@ -4533,6 +5202,7 @@ async function createTempRscServer(state, options = {}) {
4533
5202
  ...options.forceBuild ? [hashClientRefs(state.projectRoot)] : [],
4534
5203
  createVersionPlugin(),
4535
5204
  createVirtualStubPlugin(),
5205
+ createCloudflareProtocolStubPlugin(),
4536
5206
  // Dev prerender must use dev-mode IDs (path-based) to match the workerd
4537
5207
  // runtime. forceBuild produces hashed IDs for production bundle consistency.
4538
5208
  exposeInternalIds(options.forceBuild ? { forceBuild: true } : void 0),
@@ -4549,7 +5219,7 @@ async function resolveBuildEnv(option, factoryCtx) {
4549
5219
  );
4550
5220
  }
4551
5221
  try {
4552
- const userRequire = createRequire(
5222
+ const userRequire = createRequire2(
4553
5223
  resolve8(factoryCtx.root, "package.json")
4554
5224
  );
4555
5225
  const wranglerPath = userRequire.resolve("wrangler");
@@ -4584,6 +5254,7 @@ async function acquireBuildEnv(s, command, mode) {
4584
5254
  if (!result) return false;
4585
5255
  s.resolvedBuildEnv = result.env;
4586
5256
  s.buildEnvDispose = result.dispose ?? null;
5257
+ globalThis[BUILD_ENV_GLOBAL_KEY] = result.env;
4587
5258
  return true;
4588
5259
  }
4589
5260
  async function releaseBuildEnv(s) {
@@ -4596,6 +5267,7 @@ async function releaseBuildEnv(s) {
4596
5267
  s.buildEnvDispose = null;
4597
5268
  }
4598
5269
  s.resolvedBuildEnv = void 0;
5270
+ delete globalThis[BUILD_ENV_GLOBAL_KEY];
4599
5271
  }
4600
5272
  function createRouterDiscoveryPlugin(entryPath, opts) {
4601
5273
  const s = createDiscoveryState(entryPath, opts);
@@ -4651,6 +5323,9 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4651
5323
  const discoveryPromise = new Promise((resolve10) => {
4652
5324
  resolveDiscovery = resolve10;
4653
5325
  });
5326
+ const gate = createDiscoveryGate(s, debugDiscovery);
5327
+ const beginDiscoveryGate = gate.beginGate;
5328
+ const resolveDiscoveryGate = gate.resolveGate;
4654
5329
  const getDevServerOrigin = () => server.resolvedUrls?.local?.[0]?.replace(/\/$/, "") || `http://localhost:${server.config.server.port || 5173}`;
4655
5330
  let prerenderTempServer = null;
4656
5331
  let prerenderNodeRegistry = null;
@@ -4663,40 +5338,180 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4663
5338
  releaseBuildEnv(s).catch(() => {
4664
5339
  });
4665
5340
  });
5341
+ async function importEntryAndRegistry(tempRscEnv) {
5342
+ const flagAlreadySet = !!globalThis.__rscRouterDiscoveryActive;
5343
+ if (!flagAlreadySet) {
5344
+ globalThis.__rscRouterDiscoveryActive = true;
5345
+ }
5346
+ try {
5347
+ debugDiscovery?.(
5348
+ "importEntryAndRegistry: importing entry (flag=%s)",
5349
+ globalThis.__rscRouterDiscoveryActive ?? false
5350
+ );
5351
+ await tempRscEnv.runner.import(s.resolvedEntryPath);
5352
+ debugDiscovery?.(
5353
+ "importEntryAndRegistry: entry import OK, fetching RouterRegistry"
5354
+ );
5355
+ const serverMod = await tempRscEnv.runner.import(
5356
+ "@rangojs/router/server"
5357
+ );
5358
+ prerenderNodeRegistry = serverMod.RouterRegistry;
5359
+ debugDiscovery?.(
5360
+ "importEntryAndRegistry: registry size=%d",
5361
+ prerenderNodeRegistry?.size ?? 0
5362
+ );
5363
+ } finally {
5364
+ if (!flagAlreadySet) {
5365
+ delete globalThis.__rscRouterDiscoveryActive;
5366
+ debugDiscovery?.(
5367
+ "importEntryAndRegistry: cleared __rscRouterDiscoveryActive"
5368
+ );
5369
+ }
5370
+ }
5371
+ }
4666
5372
  async function getOrCreateTempServer() {
4667
- if (prerenderNodeRegistry) {
4668
- return prerenderTempServer.environments?.rsc ?? null;
5373
+ if (prerenderTempServer) {
5374
+ const existingEnv = prerenderTempServer.environments?.rsc;
5375
+ if (existingEnv?.runner) {
5376
+ if (prerenderNodeRegistry) {
5377
+ debugDiscovery?.(
5378
+ "getOrCreateTempServer: cached temp runner reused"
5379
+ );
5380
+ return existingEnv;
5381
+ }
5382
+ debugDiscovery?.(
5383
+ "getOrCreateTempServer: server alive but registry missing \u2014 re-importing"
5384
+ );
5385
+ try {
5386
+ await importEntryAndRegistry(existingEnv);
5387
+ return existingEnv;
5388
+ } catch (err) {
5389
+ debugDiscovery?.(
5390
+ "getOrCreateTempServer: reuse import failed (%s) \u2014 closing orphan and creating fresh",
5391
+ err?.message ?? String(err)
5392
+ );
5393
+ await prerenderTempServer.close().catch(() => {
5394
+ });
5395
+ prerenderTempServer = null;
5396
+ prerenderNodeRegistry = null;
5397
+ }
5398
+ } else {
5399
+ debugDiscovery?.(
5400
+ "getOrCreateTempServer: existing server has no rsc.runner \u2014 closing and recreating"
5401
+ );
5402
+ await prerenderTempServer.close().catch(() => {
5403
+ });
5404
+ prerenderTempServer = null;
5405
+ prerenderNodeRegistry = null;
5406
+ }
4669
5407
  }
5408
+ debugDiscovery?.(
5409
+ "getOrCreateTempServer: creating new temp server, entry=%s",
5410
+ s.resolvedEntryPath ?? "(unset)"
5411
+ );
4670
5412
  try {
4671
5413
  prerenderTempServer = await createTempRscServer(s, {
4672
5414
  cacheDir: "node_modules/.vite_prerender"
4673
5415
  });
4674
5416
  const tempRscEnv = prerenderTempServer.environments?.rsc;
4675
5417
  if (tempRscEnv?.runner) {
4676
- await tempRscEnv.runner.import(s.resolvedEntryPath);
4677
- const serverMod = await tempRscEnv.runner.import(
4678
- "@rangojs/router/server"
4679
- );
4680
- prerenderNodeRegistry = serverMod.RouterRegistry;
5418
+ await importEntryAndRegistry(tempRscEnv);
4681
5419
  return tempRscEnv;
4682
5420
  }
5421
+ debugDiscovery?.(
5422
+ "getOrCreateTempServer: tempRscEnv.runner unavailable"
5423
+ );
4683
5424
  } catch (err) {
5425
+ debugDiscovery?.(
5426
+ "getOrCreateTempServer: FAILED message=%s",
5427
+ err.message
5428
+ );
4684
5429
  console.warn(
4685
5430
  `[rsc-router] Failed to create temp runner: ${err.message}`
4686
5431
  );
4687
5432
  }
4688
5433
  return null;
4689
5434
  }
5435
+ async function clearTempRegistries(tempRscEnv) {
5436
+ try {
5437
+ const serverMod = await tempRscEnv.runner.import(
5438
+ "@rangojs/router/server"
5439
+ );
5440
+ if (typeof serverMod?.RouterRegistry?.clear === "function") {
5441
+ serverMod.RouterRegistry.clear();
5442
+ }
5443
+ if (typeof serverMod?.HostRouterRegistry?.clear === "function") {
5444
+ serverMod.HostRouterRegistry.clear();
5445
+ }
5446
+ debugDiscovery?.(
5447
+ "clearTempRegistries: cleared RouterRegistry + HostRouterRegistry"
5448
+ );
5449
+ } catch (err) {
5450
+ debugDiscovery?.(
5451
+ "clearTempRegistries: import @rangojs/router/server failed (%s)",
5452
+ err?.message ?? String(err)
5453
+ );
5454
+ }
5455
+ }
5456
+ async function refreshTempRscEnv() {
5457
+ let tempRscEnv = await getOrCreateTempServer();
5458
+ if (!tempRscEnv) return null;
5459
+ const envGraph = tempRscEnv.moduleGraph;
5460
+ const serverGraph = prerenderTempServer?.moduleGraph;
5461
+ const target = envGraph?.invalidateAll ? envGraph : serverGraph?.invalidateAll ? serverGraph : null;
5462
+ if (!target) {
5463
+ debugDiscovery?.(
5464
+ "refreshTempRscEnv: invalidateAll unavailable on env+server graphs, falling back to close+recreate"
5465
+ );
5466
+ if (prerenderTempServer) {
5467
+ await prerenderTempServer.close().catch(() => {
5468
+ });
5469
+ prerenderTempServer = null;
5470
+ prerenderNodeRegistry = null;
5471
+ }
5472
+ return await getOrCreateTempServer();
5473
+ }
5474
+ debugDiscovery?.(
5475
+ "refreshTempRscEnv: invalidating module graph (%s)",
5476
+ envGraph?.invalidateAll ? "env" : "server"
5477
+ );
5478
+ target.invalidateAll();
5479
+ prerenderNodeRegistry = null;
5480
+ await clearTempRegistries(tempRscEnv);
5481
+ await importEntryAndRegistry(tempRscEnv);
5482
+ return tempRscEnv;
5483
+ }
4690
5484
  const discover = async () => {
5485
+ const discoverStart = performance.now();
4691
5486
  const rscEnv = server.environments?.rsc;
4692
5487
  if (!rscEnv?.runner) {
5488
+ debugDiscovery?.(
5489
+ "dev: cloudflare path start, __rscRouterDiscoveryActive=%s",
5490
+ globalThis.__rscRouterDiscoveryActive ?? false
5491
+ );
4693
5492
  s.devServerOrigin = getDevServerOrigin();
4694
5493
  try {
4695
- await acquireBuildEnv(s, viteCommand, viteMode);
4696
- const tempRscEnv = await getOrCreateTempServer();
5494
+ await timed(
5495
+ debugDiscovery,
5496
+ "acquireBuildEnv",
5497
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5498
+ );
5499
+ const tempRscEnv = await timed(
5500
+ debugDiscovery,
5501
+ "getOrCreateTempServer",
5502
+ () => getOrCreateTempServer()
5503
+ );
4697
5504
  if (tempRscEnv) {
4698
- await discoverRouters(s, tempRscEnv);
4699
- writeRouteTypesFiles(s);
5505
+ await timed(
5506
+ debugDiscovery,
5507
+ "discoverRouters (cloudflare)",
5508
+ () => discoverRouters(s, tempRscEnv)
5509
+ );
5510
+ timedSync(
5511
+ debugDiscovery,
5512
+ "writeRouteTypesFiles",
5513
+ () => writeRouteTypesFiles(s)
5514
+ );
4700
5515
  }
4701
5516
  } catch (err) {
4702
5517
  console.warn(
@@ -4704,33 +5519,62 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4704
5519
  ${err.stack}`
4705
5520
  );
4706
5521
  }
5522
+ debugDiscovery?.(
5523
+ "dev discovery done (%sms)",
5524
+ (performance.now() - discoverStart).toFixed(1)
5525
+ );
4707
5526
  resolveDiscovery();
4708
5527
  return;
4709
5528
  }
4710
5529
  try {
4711
- await acquireBuildEnv(s, viteCommand, viteMode);
4712
- const serverMod = await rscEnv.runner.import(
4713
- "@rangojs/router/server"
5530
+ debugDiscovery?.("dev: node path start");
5531
+ await timed(
5532
+ debugDiscovery,
5533
+ "acquireBuildEnv",
5534
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5535
+ );
5536
+ const serverMod = await timed(
5537
+ debugDiscovery,
5538
+ "import @rangojs/router/server",
5539
+ () => rscEnv.runner.import("@rangojs/router/server")
4714
5540
  );
4715
5541
  if (serverMod?.setManifestReadyPromise) {
4716
5542
  serverMod.setManifestReadyPromise(discoveryPromise);
4717
5543
  }
4718
- await discoverRouters(s, rscEnv);
5544
+ await timed(
5545
+ debugDiscovery,
5546
+ "discoverRouters",
5547
+ () => discoverRouters(s, rscEnv)
5548
+ );
4719
5549
  s.devServerOrigin = getDevServerOrigin();
4720
- writeRouteTypesFiles(s);
4721
- await propagateDiscoveryState(rscEnv);
5550
+ timedSync(
5551
+ debugDiscovery,
5552
+ "writeRouteTypesFiles",
5553
+ () => writeRouteTypesFiles(s)
5554
+ );
5555
+ await timed(
5556
+ debugDiscovery,
5557
+ "propagateDiscoveryState",
5558
+ () => propagateDiscoveryState(rscEnv)
5559
+ );
4722
5560
  } catch (err) {
4723
5561
  console.warn(
4724
5562
  `[rsc-router] Router discovery failed: ${err.message}
4725
5563
  ${err.stack}`
4726
5564
  );
4727
5565
  } finally {
5566
+ debugDiscovery?.(
5567
+ "dev discovery done (%sms)",
5568
+ (performance.now() - discoverStart).toFixed(1)
5569
+ );
4728
5570
  resolveDiscovery();
4729
5571
  }
4730
5572
  };
4731
- s.discoveryDone = new Promise((resolve10) => {
4732
- setTimeout(() => discover().then(resolve10, resolve10), 0);
4733
- });
5573
+ beginDiscoveryGate();
5574
+ setTimeout(
5575
+ () => discover().then(resolveDiscoveryGate, resolveDiscoveryGate),
5576
+ 0
5577
+ );
4734
5578
  let mainRegistry = null;
4735
5579
  const propagateDiscoveryState = async (rscEnv) => {
4736
5580
  const serverMod = await rscEnv.runner.import("@rangojs/router/server");
@@ -4765,12 +5609,23 @@ ${err.stack}`
4765
5609
  }
4766
5610
  };
4767
5611
  server.middlewares.use("/__rsc_prerender", async (req, res) => {
5612
+ const reqStart = debugDev ? performance.now() : 0;
5613
+ const logResult = (status, note) => {
5614
+ debugDev?.(
5615
+ "/__rsc_prerender %s -> %d %s (%sms)",
5616
+ req.url,
5617
+ status,
5618
+ note,
5619
+ (performance.now() - reqStart).toFixed(1)
5620
+ );
5621
+ };
4768
5622
  if (s.discoveryDone) await s.discoveryDone;
4769
5623
  const url = new URL(req.url || "/", "http://localhost");
4770
5624
  const pathname = url.searchParams.get("pathname");
4771
5625
  if (!pathname) {
4772
5626
  res.statusCode = 400;
4773
5627
  res.end("Missing pathname");
5628
+ logResult(400, "missing pathname");
4774
5629
  return;
4775
5630
  }
4776
5631
  const rscEnv = server.environments?.rsc;
@@ -4788,6 +5643,7 @@ ${err.stack}`
4788
5643
  );
4789
5644
  res.statusCode = 500;
4790
5645
  res.end(`Prerender handler error: ${err.message}`);
5646
+ logResult(500, "module refresh failed");
4791
5647
  return;
4792
5648
  }
4793
5649
  } else {
@@ -4802,6 +5658,7 @@ ${err.stack}`
4802
5658
  if (!registry || registry.size === 0) {
4803
5659
  res.statusCode = 503;
4804
5660
  res.end("Prerender runner not available");
5661
+ logResult(503, "no registry");
4805
5662
  return;
4806
5663
  }
4807
5664
  const wantIntercept = url.searchParams.get("intercept") === "1";
@@ -4836,6 +5693,7 @@ ${err.stack}`
4836
5693
  payload = { segments: result.segments, handles: result.handles };
4837
5694
  }
4838
5695
  res.end(JSON.stringify(payload));
5696
+ logResult(200, `match ${result.routeName}`);
4839
5697
  return;
4840
5698
  } catch (err) {
4841
5699
  console.warn(
@@ -4845,6 +5703,7 @@ ${err.stack}`
4845
5703
  }
4846
5704
  res.statusCode = 404;
4847
5705
  res.end("No prerender match");
5706
+ logResult(404, "no match");
4848
5707
  });
4849
5708
  if (opts?.staticRouteTypesGeneration !== false) {
4850
5709
  const isGeneratedRouteFile = (filePath) => filePath.endsWith(".gen.ts") && (filePath.includes("named-routes.gen.ts") || filePath.includes("urls.gen.ts"));
@@ -4864,42 +5723,96 @@ ${err.stack}`
4864
5723
  return true;
4865
5724
  };
4866
5725
  let routeChangeTimer;
4867
- let runtimeRediscoveryInProgress = false;
4868
5726
  const refreshRuntimeDiscovery = async () => {
4869
5727
  const rscEnv = server.environments?.rsc;
4870
- if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
4871
- runtimeRediscoveryInProgress = true;
4872
- try {
4873
- await discoverRouters(s, rscEnv);
4874
- writeRouteTypesFiles(s);
4875
- await propagateDiscoveryState(rscEnv);
4876
- } catch (err) {
4877
- console.warn(
4878
- `[rsc-router] Runtime re-discovery failed: ${err.message}`
4879
- );
4880
- } finally {
4881
- runtimeRediscoveryInProgress = false;
4882
- }
5728
+ const hasMainRunner = !!rscEnv?.runner;
5729
+ if (!hasMainRunner && s.perRouterManifests.length === 0) return;
5730
+ await gate.runRefreshCycle(async () => {
5731
+ const hmrStart = performance.now();
5732
+ try {
5733
+ if (hasMainRunner) {
5734
+ await timed(
5735
+ debugDiscovery,
5736
+ "hmr discoverRouters",
5737
+ () => discoverRouters(s, rscEnv)
5738
+ );
5739
+ timedSync(
5740
+ debugDiscovery,
5741
+ "hmr writeRouteTypesFiles",
5742
+ () => writeRouteTypesFiles(s)
5743
+ );
5744
+ await timed(
5745
+ debugDiscovery,
5746
+ "hmr propagateDiscoveryState",
5747
+ () => propagateDiscoveryState(rscEnv)
5748
+ );
5749
+ } else {
5750
+ const tempRscEnv = await timed(
5751
+ debugDiscovery,
5752
+ "hmr refreshTempRscEnv (cloudflare)",
5753
+ () => refreshTempRscEnv()
5754
+ );
5755
+ if (!tempRscEnv) {
5756
+ throw new Error(
5757
+ "temp runner unavailable for cloudflare HMR rediscovery"
5758
+ );
5759
+ }
5760
+ await timed(
5761
+ debugDiscovery,
5762
+ "hmr discoverRouters (cloudflare)",
5763
+ () => discoverRouters(s, tempRscEnv)
5764
+ );
5765
+ timedSync(
5766
+ debugDiscovery,
5767
+ "hmr writeRouteTypesFiles",
5768
+ () => writeRouteTypesFiles(s)
5769
+ );
5770
+ }
5771
+ } catch (err) {
5772
+ console.warn(
5773
+ `[rsc-router] Runtime re-discovery failed: ${err.message}`
5774
+ );
5775
+ } finally {
5776
+ debugDiscovery?.(
5777
+ "hmr re-discovery done (%sms)",
5778
+ (performance.now() - hmrStart).toFixed(1)
5779
+ );
5780
+ }
5781
+ });
4883
5782
  };
4884
5783
  const scheduleRouteRegeneration = () => {
4885
5784
  clearTimeout(routeChangeTimer);
4886
5785
  routeChangeTimer = setTimeout(() => {
4887
5786
  routeChangeTimer = void 0;
5787
+ const regenStart = debugDiscovery ? performance.now() : 0;
5788
+ const rscEnv = server.environments?.rsc;
5789
+ const skipStaticWrite = !rscEnv?.runner && s.perRouterManifests.length > 0;
4888
5790
  try {
4889
- writeCombinedRouteTypesWithTracking(s);
4890
- if (s.perRouterManifests.length > 0) {
4891
- supplementGenFilesWithRuntimeRoutes(s);
5791
+ if (skipStaticWrite) {
5792
+ debugDiscovery?.(
5793
+ "watcher: skipping static write (cloudflare HMR \u2014 runtime rediscovery owns gen file)"
5794
+ );
5795
+ } else {
5796
+ writeCombinedRouteTypesWithTracking(s);
5797
+ if (s.perRouterManifests.length > 0) {
5798
+ supplementGenFilesWithRuntimeRoutes(s);
5799
+ }
4892
5800
  }
4893
5801
  } catch (err) {
4894
5802
  console.error(
4895
5803
  `[rsc-router] Route regeneration error: ${err.message}`
4896
5804
  );
4897
5805
  }
5806
+ debugDiscovery?.(
5807
+ "watcher: regenerated gen files (%sms)",
5808
+ (performance.now() - regenStart).toFixed(1)
5809
+ );
4898
5810
  if (s.perRouterManifests.length > 0) {
4899
5811
  refreshRuntimeDiscovery().catch((err) => {
4900
5812
  console.warn(
4901
5813
  `[rsc-router] Runtime re-discovery error: ${err.message}`
4902
5814
  );
5815
+ resolveDiscoveryGate();
4903
5816
  });
4904
5817
  }
4905
5818
  }, 100);
@@ -4917,6 +5830,12 @@ ${err.stack}`
4917
5830
  const hasUrls = source.includes("urls(");
4918
5831
  const hasCreateRouter = /\bcreateRouter\s*[<(]/.test(source);
4919
5832
  if (!hasUrls && !hasCreateRouter) return;
5833
+ debugDiscovery?.(
5834
+ "watcher: %s matches (urls=%s, router=%s)",
5835
+ filePath,
5836
+ hasUrls,
5837
+ hasCreateRouter
5838
+ );
4920
5839
  if (hasCreateRouter) {
4921
5840
  const nestedRouterConflict = findNestedRouterConflict([
4922
5841
  ...s.cachedRouterFiles ?? [],
@@ -4930,6 +5849,9 @@ ${err.stack}`
4930
5849
  }
4931
5850
  s.cachedRouterFiles = void 0;
4932
5851
  }
5852
+ if (s.perRouterManifests.length > 0) {
5853
+ gate.noteRouteEvent();
5854
+ }
4933
5855
  scheduleRouteRegeneration();
4934
5856
  } catch {
4935
5857
  }
@@ -4949,15 +5871,31 @@ ${err.stack}`
4949
5871
  // The manifest data is stored for the virtual module's load hook.
4950
5872
  async buildStart() {
4951
5873
  if (!s.isBuildMode) return;
4952
- if (s.mergedRouteManifest !== null) return;
5874
+ if (s.mergedRouteManifest !== null) {
5875
+ debugDiscovery?.(
5876
+ "build: skip (already discovered, env=%s)",
5877
+ this.environment?.name ?? "?"
5878
+ );
5879
+ return;
5880
+ }
5881
+ const buildStartTime = performance.now();
5882
+ debugDiscovery?.("build: start (env=%s)", this.environment?.name ?? "?");
4953
5883
  resetStagedBuildAssets(s.projectRoot);
4954
5884
  s.prerenderManifestEntries = null;
4955
5885
  s.staticManifestEntries = null;
4956
- await acquireBuildEnv(s, viteCommand, viteMode);
5886
+ await timed(
5887
+ debugDiscovery,
5888
+ "build acquireBuildEnv",
5889
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5890
+ );
4957
5891
  let tempServer = null;
4958
5892
  globalThis.__rscRouterDiscoveryActive = true;
4959
5893
  try {
4960
- tempServer = await createTempRscServer(s, { forceBuild: true });
5894
+ tempServer = await timed(
5895
+ debugDiscovery,
5896
+ "build createTempRscServer",
5897
+ () => createTempRscServer(s, { forceBuild: true })
5898
+ );
4961
5899
  const rscEnv = tempServer.environments?.rsc;
4962
5900
  if (!rscEnv?.runner) {
4963
5901
  console.warn(
@@ -4971,8 +5909,16 @@ ${err.stack}`
4971
5909
  if (tempIdsPlugin?.api?.staticHandlerModules) {
4972
5910
  s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
4973
5911
  }
4974
- await discoverRouters(s, rscEnv);
4975
- writeRouteTypesFiles(s);
5912
+ await timed(
5913
+ debugDiscovery,
5914
+ "build discoverRouters",
5915
+ () => discoverRouters(s, rscEnv)
5916
+ );
5917
+ timedSync(
5918
+ debugDiscovery,
5919
+ "build writeRouteTypesFiles",
5920
+ () => writeRouteTypesFiles(s)
5921
+ );
4976
5922
  } catch (err) {
4977
5923
  const sourceFile = err.stack?.split("\n").find(
4978
5924
  (line) => line.includes(s.projectRoot) && !line.includes("node_modules")
@@ -4991,9 +5937,45 @@ ${details}`
4991
5937
  } finally {
4992
5938
  delete globalThis.__rscRouterDiscoveryActive;
4993
5939
  if (tempServer) {
4994
- await tempServer.close();
5940
+ await timed(
5941
+ debugDiscovery,
5942
+ "build tempServer.close",
5943
+ () => tempServer.close()
5944
+ );
4995
5945
  }
4996
5946
  await releaseBuildEnv(s);
5947
+ debugDiscovery?.(
5948
+ "build discovery done (%sms)",
5949
+ (performance.now() - buildStartTime).toFixed(1)
5950
+ );
5951
+ }
5952
+ },
5953
+ // Suppress vite's HMR cascade for our own gen-file writes.
5954
+ //
5955
+ // After every cf HMR cycle, refreshTempRscEnv → writeRouteTypesFiles
5956
+ // writes the configured gen files (default `router.named-routes.gen.ts`,
5957
+ // but the source filenames and gen suffix are user-configurable). The
5958
+ // chokidar watcher then fires twice independently: our
5959
+ // `handleRouteFileChange` (already short-circuited by
5960
+ // `consumeSelfGenWrite` inside `maybeHandleGeneratedRouteFileMutation`),
5961
+ // AND vite's own HMR pipeline (which invalidates the gen file's
5962
+ // importers and triggers a second workerd full reload — visible to the
5963
+ // user as a duplicate "[RSCRouter] HMR: version changed" on the client).
5964
+ //
5965
+ // `peekSelfGenWrite` is the authoritative filter: its map only contains
5966
+ // paths that `markSelfGenWrite` has registered, so it natively works
5967
+ // for any configured gen-file name. It is non-consuming so the chokidar
5968
+ // handler that fires later can still consume the same entry. Returning
5969
+ // [] tells vite "no modules invalidated by this change" — safe because
5970
+ // `s.perRouterManifests` is already up-to-date (the write that just
5971
+ // happened is the consequence of our just-completed rediscovery).
5972
+ handleHotUpdate(ctx) {
5973
+ if (peekSelfGenWrite(s, ctx.file)) {
5974
+ debugDiscovery?.(
5975
+ "handleHotUpdate: suppressing self-write HMR cascade for %s",
5976
+ ctx.file
5977
+ );
5978
+ return [];
4997
5979
  }
4998
5980
  },
4999
5981
  // Virtual module: provides the pre-generated route manifest as a JS module
@@ -5010,17 +5992,36 @@ ${details}`
5010
5992
  async load(id) {
5011
5993
  if (id === "\0" + VIRTUAL_ROUTES_MANIFEST_ID) {
5012
5994
  if (s.discoveryDone) {
5013
- await s.discoveryDone;
5995
+ await timed(
5996
+ debugRoutes,
5997
+ "await discoveryDone (manifest)",
5998
+ () => s.discoveryDone
5999
+ );
5014
6000
  }
5015
- return generateRoutesManifestModule(s);
6001
+ const code = await timed(
6002
+ debugRoutes,
6003
+ "generateRoutesManifestModule",
6004
+ () => generateRoutesManifestModule(s)
6005
+ );
6006
+ debugRoutes?.("manifest module emitted (%d bytes)", code?.length ?? 0);
6007
+ return code;
5016
6008
  }
5017
6009
  const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
5018
6010
  if (id.startsWith(perRouterPrefix)) {
5019
6011
  if (s.discoveryDone) {
5020
- await s.discoveryDone;
6012
+ await timed(
6013
+ debugRoutes,
6014
+ "await discoveryDone (per-router)",
6015
+ () => s.discoveryDone
6016
+ );
5021
6017
  }
5022
6018
  const routerId = id.slice(perRouterPrefix.length);
5023
- return generatePerRouterModule(s, routerId);
6019
+ const code = await timed(
6020
+ debugRoutes,
6021
+ `generatePerRouterModule ${routerId}`,
6022
+ () => generatePerRouterModule(s, routerId)
6023
+ );
6024
+ return code;
5024
6025
  }
5025
6026
  return null;
5026
6027
  },
@@ -5028,14 +6029,20 @@ ${details}`
5028
6029
  // Used by closeBundle for handler code eviction and prerender data injection.
5029
6030
  generateBundle(_options, bundle) {
5030
6031
  if (this.environment?.name !== "rsc") return;
6032
+ const genStart = debugBuild ? performance.now() : 0;
5031
6033
  for (const [fileName, chunk] of Object.entries(bundle)) {
5032
6034
  if (chunk.type === "chunk" && chunk.isEntry) {
5033
6035
  s.rscEntryFileName = fileName;
5034
6036
  break;
5035
6037
  }
5036
6038
  }
5037
- if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size)
6039
+ if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size) {
6040
+ debugBuild?.(
6041
+ "generateBundle (rsc): no handlers to scan (%sms)",
6042
+ (performance.now() - genStart).toFixed(1)
6043
+ );
5038
6044
  return;
6045
+ }
5039
6046
  s.handlerChunkInfoMap.clear();
5040
6047
  s.staticHandlerChunkInfoMap.clear();
5041
6048
  for (const [fileName, chunk] of Object.entries(bundle)) {
@@ -5079,6 +6086,13 @@ ${details}`
5079
6086
  }
5080
6087
  }
5081
6088
  }
6089
+ debugBuild?.(
6090
+ "generateBundle (rsc): scanned %d chunks, %d prerender chunk(s), %d static chunk(s) (%sms)",
6091
+ Object.keys(bundle).length,
6092
+ s.handlerChunkInfoMap.size,
6093
+ s.staticHandlerChunkInfoMap.size,
6094
+ (performance.now() - genStart).toFixed(1)
6095
+ );
5082
6096
  },
5083
6097
  // Build-time pre-rendering: evict handler code and inject collected prerender data.
5084
6098
  // Collection now happens in-process during discoverRouters() via RSC runner.
@@ -5089,29 +6103,41 @@ ${details}`
5089
6103
  async handler() {
5090
6104
  if (!s.isBuildMode) return;
5091
6105
  if (this.environment && this.environment.name !== "rsc") return;
5092
- postprocessBundle(s);
6106
+ timedSync(
6107
+ debugBuild,
6108
+ "closeBundle postprocessBundle",
6109
+ () => postprocessBundle(s)
6110
+ );
5093
6111
  }
5094
6112
  }
5095
6113
  };
5096
6114
  }
5097
6115
 
5098
6116
  // src/vite/rango.ts
6117
+ var debugConfig = createRangoDebugger(NS.config);
5099
6118
  async function rango(options) {
6119
+ const rangoStart = performance.now();
5100
6120
  const resolvedOptions = options ?? { preset: "node" };
5101
6121
  const preset = resolvedOptions.preset ?? "node";
5102
6122
  const showBanner = resolvedOptions.banner ?? true;
6123
+ debugConfig?.("rango(%s) setup start", preset);
5103
6124
  const plugins = [];
5104
- const rangoAliases = getPackageAliases();
6125
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
5105
6126
  const excludeDeps = [
5106
6127
  ...getExcludeDeps(),
5107
- // The public browser entry re-exports the RSDW browser client.
5108
- // Excluding both keeps Vite from freezing the unpatched bundle into
5109
- // .vite/deps before our source transforms run.
6128
+ // plugin-rsc itself injects these into the client env's
6129
+ // optimizeDeps.include, which overrides exclude for the dep's own
6130
+ // pre-bundle entry. What exclude still controls is how *other*
6131
+ // pre-bundled deps treat imports of these specs (external vs inlined)
6132
+ // via esbuildCjsExternalPlugin. The cjs-to-esm transform in
6133
+ // plugins/cjs-to-esm.ts is the fallback for strict-pnpm consumers,
6134
+ // where client.browser's bare include fails to resolve and Vite ends up
6135
+ // serving the raw CJS file at dev-serve time.
5110
6136
  "@vitejs/plugin-rsc/browser",
5111
- // Keep the browser RSDW client out of Vite's dep optimizer so our
5112
- // cjs-to-esm transform can patch the real file.
5113
6137
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
5114
6138
  ];
6139
+ const pkg = getPublishedPackageName();
6140
+ const nested = (spec) => `${pkg} > ${spec}`;
5115
6141
  const routerRef = { path: void 0 };
5116
6142
  const prerenderEnabled = true;
5117
6143
  if (preset === "cloudflare") {
@@ -5149,7 +6175,7 @@ async function rango(options) {
5149
6175
  // Pre-bundle rsc-html-stream to prevent discovery during first request
5150
6176
  // Exclude rsc-router modules to ensure same Context instance
5151
6177
  optimizeDeps: {
5152
- include: ["rsc-html-stream/client"],
6178
+ include: [nested("rsc-html-stream/client")],
5153
6179
  exclude: excludeDeps,
5154
6180
  esbuildOptions: sharedEsbuildOptions
5155
6181
  }
@@ -5174,8 +6200,10 @@ async function rango(options) {
5174
6200
  "react-dom/static.edge",
5175
6201
  "react/jsx-runtime",
5176
6202
  "react/jsx-dev-runtime",
5177
- "rsc-html-stream/server",
5178
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6203
+ nested("rsc-html-stream/server"),
6204
+ nested(
6205
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6206
+ )
5179
6207
  ],
5180
6208
  exclude: excludeDeps,
5181
6209
  esbuildOptions: sharedEsbuildOptions
@@ -5190,7 +6218,9 @@ async function rango(options) {
5190
6218
  "react",
5191
6219
  "react/jsx-runtime",
5192
6220
  "react/jsx-dev-runtime",
5193
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6221
+ nested(
6222
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6223
+ )
5194
6224
  ],
5195
6225
  exclude: excludeDeps,
5196
6226
  esbuildOptions: sharedEsbuildOptions
@@ -5271,7 +6301,7 @@ ${list}`);
5271
6301
  "react-dom",
5272
6302
  "react/jsx-runtime",
5273
6303
  "react/jsx-dev-runtime",
5274
- "rsc-html-stream/client"
6304
+ nested("rsc-html-stream/client")
5275
6305
  ],
5276
6306
  exclude: excludeDeps,
5277
6307
  esbuildOptions: sharedEsbuildOptions,
@@ -5288,7 +6318,9 @@ ${list}`);
5288
6318
  "react-dom/static.edge",
5289
6319
  "react/jsx-runtime",
5290
6320
  "react/jsx-dev-runtime",
5291
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6321
+ nested(
6322
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6323
+ )
5292
6324
  ],
5293
6325
  exclude: excludeDeps,
5294
6326
  esbuildOptions: sharedEsbuildOptions
@@ -5301,7 +6333,9 @@ ${list}`);
5301
6333
  "react",
5302
6334
  "react/jsx-runtime",
5303
6335
  "react/jsx-dev-runtime",
5304
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6336
+ nested(
6337
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6338
+ )
5305
6339
  ],
5306
6340
  esbuildOptions: sharedEsbuildOptions
5307
6341
  }
@@ -5371,6 +6405,12 @@ ${list}`);
5371
6405
  preset
5372
6406
  })
5373
6407
  );
6408
+ debugConfig?.(
6409
+ "rango(%s) setup done: %d plugin(s) (%sms)",
6410
+ preset,
6411
+ plugins.length,
6412
+ (performance.now() - rangoStart).toFixed(1)
6413
+ );
5374
6414
  return plugins;
5375
6415
  }
5376
6416
 
@@ -5381,7 +6421,7 @@ function poke() {
5381
6421
  apply: "serve",
5382
6422
  configureServer(server) {
5383
6423
  const stdin = process.stdin;
5384
- const debug = process.env.RANGO_POKE_DEBUG === "1";
6424
+ const debug11 = process.env.RANGO_POKE_DEBUG === "1";
5385
6425
  const triggerReload = (source) => {
5386
6426
  server.hot.send({ type: "full-reload", path: "*" });
5387
6427
  server.config.logger.info(` browser reload (${source})`, {
@@ -5414,7 +6454,7 @@ function poke() {
5414
6454
  lines.pop();
5415
6455
  return lines;
5416
6456
  };
5417
- if (debug) {
6457
+ if (debug11) {
5418
6458
  server.config.logger.info(
5419
6459
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
5420
6460
  { timestamp: true }
@@ -5427,7 +6467,7 @@ function poke() {
5427
6467
  );
5428
6468
  }
5429
6469
  const onData = (data) => {
5430
- if (debug) {
6470
+ if (debug11) {
5431
6471
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
5432
6472
  timestamp: true
5433
6473
  });