@rangojs/router 0.0.0-experimental.963406dd → 0.0.0-experimental.97

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 (114) hide show
  1. package/README.md +112 -17
  2. package/dist/vite/index.js +1337 -463
  3. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  4. package/package.json +18 -17
  5. package/skills/breadcrumbs/SKILL.md +3 -1
  6. package/skills/handler-use/SKILL.md +362 -0
  7. package/skills/hooks/SKILL.md +33 -20
  8. package/skills/intercept/SKILL.md +20 -0
  9. package/skills/layout/SKILL.md +22 -0
  10. package/skills/links/SKILL.md +90 -16
  11. package/skills/loader/SKILL.md +70 -3
  12. package/skills/middleware/SKILL.md +34 -3
  13. package/skills/migrate-nextjs/SKILL.md +562 -0
  14. package/skills/migrate-react-router/SKILL.md +769 -0
  15. package/skills/parallel/SKILL.md +66 -0
  16. package/skills/rango/SKILL.md +25 -22
  17. package/skills/response-routes/SKILL.md +8 -0
  18. package/skills/route/SKILL.md +24 -0
  19. package/skills/server-actions/SKILL.md +739 -0
  20. package/skills/streams-and-websockets/SKILL.md +283 -0
  21. package/skills/typesafety/SKILL.md +3 -1
  22. package/src/browser/app-shell.ts +52 -0
  23. package/src/browser/event-controller.ts +44 -4
  24. package/src/browser/navigation-bridge.ts +72 -4
  25. package/src/browser/navigation-client.ts +64 -13
  26. package/src/browser/navigation-store.ts +25 -1
  27. package/src/browser/partial-update.ts +34 -3
  28. package/src/browser/prefetch/cache.ts +129 -21
  29. package/src/browser/prefetch/fetch.ts +148 -16
  30. package/src/browser/prefetch/queue.ts +36 -5
  31. package/src/browser/rango-state.ts +53 -13
  32. package/src/browser/react/Link.tsx +30 -2
  33. package/src/browser/react/NavigationProvider.tsx +70 -18
  34. package/src/browser/react/filter-segment-order.ts +51 -7
  35. package/src/browser/react/use-navigation.ts +22 -2
  36. package/src/browser/react/use-params.ts +11 -1
  37. package/src/browser/react/use-router.ts +8 -1
  38. package/src/browser/react/use-segments.ts +11 -8
  39. package/src/browser/rsc-router.tsx +34 -6
  40. package/src/browser/segment-reconciler.ts +36 -14
  41. package/src/browser/types.ts +19 -0
  42. package/src/build/route-trie.ts +50 -24
  43. package/src/cache/cf/cf-cache-store.ts +5 -7
  44. package/src/client.tsx +82 -174
  45. package/src/index.rsc.ts +3 -0
  46. package/src/index.ts +40 -9
  47. package/src/outlet-context.ts +1 -1
  48. package/src/response-utils.ts +28 -0
  49. package/src/reverse.ts +7 -3
  50. package/src/route-definition/dsl-helpers.ts +175 -23
  51. package/src/route-definition/helpers-types.ts +63 -14
  52. package/src/route-definition/resolve-handler-use.ts +6 -0
  53. package/src/route-types.ts +7 -0
  54. package/src/router/handler-context.ts +24 -4
  55. package/src/router/lazy-includes.ts +6 -6
  56. package/src/router/loader-resolution.ts +3 -0
  57. package/src/router/manifest.ts +22 -13
  58. package/src/router/match-api.ts +4 -3
  59. package/src/router/match-handlers.ts +1 -0
  60. package/src/router/match-middleware/cache-lookup.ts +2 -1
  61. package/src/router/match-result.ts +101 -4
  62. package/src/router/middleware-types.ts +2 -22
  63. package/src/router/middleware.ts +54 -7
  64. package/src/router/pattern-matching.ts +60 -9
  65. package/src/router/revalidation.ts +15 -1
  66. package/src/router/segment-resolution/fresh.ts +13 -0
  67. package/src/router/segment-resolution/revalidation.ts +135 -101
  68. package/src/router/trie-matching.ts +10 -4
  69. package/src/router/url-params.ts +49 -0
  70. package/src/router.ts +1 -2
  71. package/src/rsc/handler.ts +16 -8
  72. package/src/rsc/helpers.ts +69 -41
  73. package/src/rsc/progressive-enhancement.ts +4 -0
  74. package/src/rsc/response-route-handler.ts +14 -1
  75. package/src/rsc/rsc-rendering.ts +10 -0
  76. package/src/rsc/server-action.ts +4 -0
  77. package/src/rsc/types.ts +6 -0
  78. package/src/segment-content-promise.ts +67 -0
  79. package/src/segment-loader-promise.ts +122 -0
  80. package/src/segment-system.tsx +11 -61
  81. package/src/server/context.ts +26 -3
  82. package/src/server/request-context.ts +10 -42
  83. package/src/ssr/index.tsx +5 -1
  84. package/src/types/handler-context.ts +12 -39
  85. package/src/types/loader-types.ts +5 -6
  86. package/src/types/request-scope.ts +126 -0
  87. package/src/types/route-entry.ts +11 -0
  88. package/src/types/segments.ts +18 -1
  89. package/src/urls/include-helper.ts +24 -14
  90. package/src/urls/path-helper-types.ts +30 -4
  91. package/src/urls/response-types.ts +2 -10
  92. package/src/use-loader.tsx +4 -1
  93. package/src/vite/debug.ts +184 -0
  94. package/src/vite/discovery/discover-routers.ts +31 -3
  95. package/src/vite/discovery/gate-state.ts +171 -0
  96. package/src/vite/discovery/prerender-collection.ts +48 -1
  97. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  98. package/src/vite/plugins/cjs-to-esm.ts +5 -0
  99. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  100. package/src/vite/plugins/client-ref-hashing.ts +16 -4
  101. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  102. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  103. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  104. package/src/vite/plugins/expose-action-id.ts +52 -28
  105. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  106. package/src/vite/plugins/expose-internal-ids.ts +515 -480
  107. package/src/vite/plugins/performance-tracks.ts +17 -9
  108. package/src/vite/plugins/use-cache-transform.ts +56 -43
  109. package/src/vite/plugins/version-injector.ts +37 -11
  110. package/src/vite/rango.ts +49 -14
  111. package/src/vite/router-discovery.ts +558 -53
  112. package/src/vite/utils/banner.ts +1 -1
  113. package/src/vite/utils/package-resolution.ts +41 -1
  114. package/src/vite/utils/prerender-utils.ts +20 -6
@@ -191,7 +191,99 @@ function escapeRegExp(input) {
191
191
  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
192
192
  }
193
193
 
194
+ // src/vite/debug.ts
195
+ import debugFactory from "debug";
196
+ var NS = {
197
+ config: "rango:config",
198
+ discovery: "rango:discovery",
199
+ routes: "rango:routes",
200
+ prerender: "rango:prerender",
201
+ build: "rango:build",
202
+ dev: "rango:dev",
203
+ transform: "rango:transform"
204
+ };
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
+
194
285
  // src/vite/plugins/expose-action-id.ts
286
+ var debug = createRangoDebugger(NS.transform);
195
287
  function getRscPluginApi(config) {
196
288
  let plugin = config.plugins.find((p) => p.name === "rsc:minimal");
197
289
  if (!plugin) {
@@ -280,6 +372,8 @@ function exposeActionId() {
280
372
  let isBuild = false;
281
373
  let hashToFileMap;
282
374
  let rscPluginApi;
375
+ const counterTransform = createCounter(debug, "expose-action-id transform");
376
+ const counterRender = createCounter(debug, "expose-action-id renderChunk");
283
377
  return {
284
378
  name: "@rangojs/router:expose-action-id",
285
379
  // Run after all other plugins (including RSC plugin's transforms)
@@ -289,6 +383,10 @@ function exposeActionId() {
289
383
  isBuild = config.command === "build";
290
384
  rscPluginApi = getRscPluginApi(config);
291
385
  },
386
+ buildEnd() {
387
+ counterTransform?.flush();
388
+ counterRender?.flush();
389
+ },
292
390
  buildStart() {
293
391
  if (!rscPluginApi) {
294
392
  rscPluginApi = getRscPluginApi(config);
@@ -325,28 +423,42 @@ function exposeActionId() {
325
423
  if (id.includes("/node_modules/")) {
326
424
  return;
327
425
  }
328
- 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
+ }
329
432
  },
330
433
  // Build mode: renderChunk runs after all transforms and bundling complete
331
434
  renderChunk(code, chunk) {
332
- const isRscEnv = this.environment?.name === "rsc";
333
- const effectiveMap = isRscEnv ? hashToFileMap : void 0;
334
- if (isRscEnv && hashToFileMap) {
335
- const s = new MagicString(code);
336
- const changed1 = applyServerReferenceWrapping(code, s, effectiveMap);
337
- const changed2 = applyRegisterReferenceWrapping(code, s, hashToFileMap);
338
- if (changed1 || changed2) {
339
- return {
340
- code: s.toString(),
341
- map: s.generateMap({
342
- source: chunk.fileName,
343
- includeContent: true
344
- })
345
- };
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;
346
457
  }
347
- return null;
458
+ return transformServerReferences(code, chunk.fileName, effectiveMap);
459
+ } finally {
460
+ counterRender?.record(chunk.fileName, performance.now() - start);
348
461
  }
349
- return transformServerReferences(code, chunk.fileName, effectiveMap);
350
462
  }
351
463
  };
352
464
  }
@@ -953,6 +1065,7 @@ ${binding.localName}.$$id = "${handlerId}";`;
953
1065
  import MagicString3 from "magic-string";
954
1066
  import path3 from "node:path";
955
1067
  import { createHash } from "node:crypto";
1068
+ var debug2 = createRangoDebugger(NS.transform);
956
1069
  function transformRouter(code, filePath, routerFnNames, absolutePath) {
957
1070
  const pat = new RegExp(
958
1071
  `\\b(?:${routerFnNames.map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})\\s*(?:<[^>]*>)?\\s*\\(`,
@@ -995,11 +1108,15 @@ function transformRouter(code, filePath, routerFnNames, absolutePath) {
995
1108
  }
996
1109
  function exposeRouterId() {
997
1110
  let projectRoot = "";
1111
+ const counter = createCounter(debug2, "expose-router-id");
998
1112
  return {
999
1113
  name: "@rangojs/router:expose-router-id",
1000
1114
  configResolved(config) {
1001
1115
  projectRoot = config.root;
1002
1116
  },
1117
+ buildEnd() {
1118
+ counter?.flush();
1119
+ },
1003
1120
  transform(code, id) {
1004
1121
  if (!code.includes("createRouter")) return null;
1005
1122
  if (!/import\s*\{[^}]*\bcreateRouter\b[^}]*\}\s*from\s*["']@rangojs\/router(?:\/server)?["']/.test(
@@ -1008,14 +1125,25 @@ function exposeRouterId() {
1008
1125
  return null;
1009
1126
  }
1010
1127
  if (id.includes("node_modules")) return null;
1011
- const filePath = normalizePath(path3.relative(projectRoot, id));
1012
- const routerFnNames = getImportedFnNames(code, "createRouter");
1013
- return transformRouter(code, filePath, routerFnNames, normalizePath(id));
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
+ }
1014
1141
  }
1015
1142
  };
1016
1143
  }
1017
1144
 
1018
1145
  // src/vite/plugins/expose-internal-ids.ts
1146
+ var debug3 = createRangoDebugger(NS.transform);
1019
1147
  var VIRTUAL_LOADER_MANIFEST = "virtual:rsc-router/loader-manifest";
1020
1148
  var RESOLVED_VIRTUAL_LOADER_MANIFEST = "\0" + VIRTUAL_LOADER_MANIFEST;
1021
1149
  var VIRTUAL_HANDLER_PREFIX = "virtual:handler-extract:";
@@ -1028,9 +1156,13 @@ function exposeInternalIds(options) {
1028
1156
  const staticHandlerModules = /* @__PURE__ */ new Map();
1029
1157
  const virtualHandlers = /* @__PURE__ */ new Map();
1030
1158
  const unsupportedShapeWarnings = /* @__PURE__ */ new Set();
1159
+ const counter = createCounter(debug3, "expose-internal-ids");
1031
1160
  return {
1032
1161
  name: "@rangojs/router:expose-internal-ids",
1033
1162
  enforce: "post",
1163
+ buildEnd() {
1164
+ counter?.flush();
1165
+ },
1034
1166
  api: {
1035
1167
  prerenderHandlerModules,
1036
1168
  staticHandlerModules
@@ -1144,11 +1276,13 @@ ${lazyImports.join(",\n")}
1144
1276
  // --------------- Unified transform ---------------
1145
1277
  transform(code, id) {
1146
1278
  if (id.includes("/node_modules/")) return;
1147
- const filePath = normalizePath(path4.relative(projectRoot, id));
1148
- const isRscEnv = this.environment?.name === "rsc";
1149
- if (id.includes(".named-routes.gen.") && !isRscEnv && this.environment?.name === "client") {
1150
- this.warn(
1151
- `
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
+ `
1152
1286
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1153
1287
  !! !!
1154
1288
  !! WARNING: NamedRoutes imported in a CLIENT component! !!
@@ -1168,356 +1302,373 @@ ${lazyImports.join(",\n")}
1168
1302
  !! !!
1169
1303
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1170
1304
  `
1171
- );
1172
- }
1173
- if (!code.includes("@rangojs/router")) return;
1174
- const has = detectImports(code);
1175
- const hasLoaderCode = has.loader && code.includes("createLoader");
1176
- const hasHandleCode = has.handle && code.includes("createHandle");
1177
- const hasLocationStateCode = has.locationState && code.includes("createLocationState");
1178
- const hasPrerenderHandlerCode = has.prerenderHandler && code.includes("Prerender");
1179
- const hasStaticHandlerCode = has.staticHandler && code.includes("Static");
1180
- if (!hasLoaderCode && !hasHandleCode && !hasLocationStateCode && !hasPrerenderHandlerCode && !hasStaticHandlerCode) {
1181
- return;
1182
- }
1183
- const _fnNamesCache = /* @__PURE__ */ new Map();
1184
- const _bindingsCache = /* @__PURE__ */ new Map();
1185
- let _cachedAst;
1186
- let _astParseFailed = false;
1187
- let _astCodeRef = code;
1188
- const getFnNames = (canonicalName) => {
1189
- let result = _fnNamesCache.get(canonicalName);
1190
- if (!result) {
1191
- result = getImportedFnNames(code, canonicalName);
1192
- _fnNamesCache.set(canonicalName, result);
1305
+ );
1193
1306
  }
1194
- return result;
1195
- };
1196
- const lazyAst = () => {
1197
- if (code !== _astCodeRef) {
1198
- _cachedAst = void 0;
1199
- _astParseFailed = false;
1200
- _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;
1201
1316
  }
1202
- if (_cachedAst !== void 0 || _astParseFailed) return _cachedAst;
1203
- try {
1204
- _cachedAst = parseAst2(code, { jsx: true });
1205
- } catch {
1206
- _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));
1207
1368
  }
1208
- return _cachedAst;
1209
- };
1210
- const getBindings = (currentCode, fnNames) => {
1211
- const key = fnNames.join("\0");
1212
- let result = _bindingsCache.get(key);
1213
- if (!result) {
1214
- result = collectCreateExportBindings(currentCode, fnNames, lazyAst());
1215
- _bindingsCache.set(key, result);
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
+ }
1216
1380
  }
1217
- return result;
1218
- };
1219
- for (const cfg of STRICT_CREATE_CONFIGS) {
1220
- const hasCode = cfg.fnName === "createLoader" ? hasLoaderCode : cfg.fnName === "createHandle" ? hasHandleCode : hasLocationStateCode;
1221
- if (!hasCode) continue;
1222
- const fnNames = getFnNames(cfg.fnName);
1223
- const totalCalls = countCreateCallsForNames(code, fnNames);
1224
- const supportedBindings = getBindings(code, fnNames).length;
1225
- if (totalCalls <= supportedBindings) continue;
1226
- const warnKey = `${id}::${cfg.fnName}`;
1227
- if (unsupportedShapeWarnings.has(warnKey)) continue;
1228
- unsupportedShapeWarnings.add(warnKey);
1229
- this.warn(buildUnsupportedShapeWarning(filePath, cfg.fnName));
1230
- }
1231
- if (hasLoaderCode && isRscEnv) {
1232
- const fnNames = getFnNames("createLoader");
1233
- const bindings = getBindings(code, fnNames);
1234
- for (const binding of bindings) {
1235
- const exportName = binding.exportNames[0];
1236
- const hashedId = hashId(filePath, exportName);
1237
- loaderRegistry.set(hashedId, {
1381
+ if (hasLoaderCode && !isRscEnv) {
1382
+ const fnNames = getFnNames("createLoader");
1383
+ const bindings = getBindings(code, fnNames);
1384
+ const stubResult = generateClientLoaderStubs(
1385
+ bindings,
1386
+ code,
1238
1387
  filePath,
1239
- exportName
1240
- });
1241
- }
1242
- }
1243
- if (hasLoaderCode && !isRscEnv) {
1244
- const fnNames = getFnNames("createLoader");
1245
- const bindings = getBindings(code, fnNames);
1246
- const stubResult = generateClientLoaderStubs(
1247
- bindings,
1248
- code,
1249
- filePath,
1250
- isBuild
1251
- );
1252
- if (stubResult) return stubResult;
1253
- }
1254
- if (hasPrerenderHandlerCode && !isRscEnv) {
1255
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1256
- const bindings = getBindings(code, fnNames);
1257
- const wholeFile = generateWholeFileStubs(
1258
- PRERENDER_CONFIG,
1259
- bindings,
1260
- code,
1261
- filePath,
1262
- isBuild
1263
- );
1264
- if (wholeFile) return wholeFile;
1265
- }
1266
- if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
1267
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1268
- const exportNames = getBindings(code, fnNames).map(
1269
- (b) => b.exportNames[0]
1270
- );
1271
- if (exportNames.length > 0) {
1272
- prerenderHandlerModules.set(id, exportNames);
1388
+ isBuild
1389
+ );
1390
+ if (stubResult) return stubResult;
1273
1391
  }
1274
- }
1275
- let changed = false;
1276
- const handlerConfigs = [
1277
- hasStaticHandlerCode && STATIC_CONFIG,
1278
- hasPrerenderHandlerCode && PRERENDER_CONFIG
1279
- ].filter((c) => !!c).map((cfg) => {
1280
- const fnNames = getFnNames(cfg.fnName);
1281
- return { cfg, fnNames };
1282
- });
1283
- for (const { cfg, fnNames } of handlerConfigs) {
1284
- const totalCalls = countCreateCallsForNames(code, fnNames);
1285
- const supportedBindings = getBindings(code, fnNames).length;
1286
- if (totalCalls > supportedBindings) {
1287
- const iterS = new MagicString4(code);
1288
- const result = transformInlineHandlers(
1289
- cfg.fnName,
1290
- VIRTUAL_HANDLER_PREFIX,
1291
- iterS,
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,
1292
1398
  code,
1293
1399
  filePath,
1294
- virtualHandlers,
1295
- id,
1296
- parseAst2
1400
+ isBuild
1297
1401
  );
1298
- if (result) {
1299
- changed = true;
1300
- code = iterS.toString();
1301
- _bindingsCache.clear();
1302
- }
1402
+ if (wholeFile) return wholeFile;
1303
1403
  }
1304
- }
1305
- if (hasStaticHandlerCode && !isRscEnv) {
1306
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1307
- const bindings = getBindings(code, fnNames);
1308
- const wholeFile = generateWholeFileStubs(
1309
- STATIC_CONFIG,
1310
- bindings,
1311
- code,
1312
- filePath,
1313
- isBuild
1314
- );
1315
- if (wholeFile) return wholeFile;
1316
- }
1317
- if (!isRscEnv && (hasPrerenderHandlerCode || hasStaticHandlerCode)) {
1318
- const prerenderFnNames = hasPrerenderHandlerCode ? getFnNames(PRERENDER_CONFIG.fnName) : [];
1319
- const staticFnNames = hasStaticHandlerCode ? getFnNames(STATIC_CONFIG.fnName) : [];
1320
- const loaderFnNames = hasLoaderCode ? getFnNames("createLoader") : [];
1321
- const handleFnNames = hasHandleCode ? getFnNames("createHandle") : [];
1322
- const lsFnNames = hasLocationStateCode ? getFnNames("createLocationState") : [];
1323
- const allBindings = [];
1324
- for (const fnNames of [
1325
- prerenderFnNames,
1326
- staticFnNames,
1327
- loaderFnNames,
1328
- handleFnNames,
1329
- lsFnNames
1330
- ]) {
1331
- if (fnNames.length > 0) {
1332
- allBindings.push(...getBindings(code, fnNames));
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);
1333
1411
  }
1334
1412
  }
1335
- let canStubWholeFile = allBindings.length > 0 && isExportOnlyFile(code, allBindings);
1336
- if (canStubWholeFile && (handleFnNames.length > 0 || lsFnNames.length > 0)) {
1337
- const exportedLocals = new Set(allBindings.map((b) => b.localName));
1338
- const strippedBindings = [];
1339
- const localDeclPattern = /(?:^|;|\n)\s*(?:const|let|var|function)\s+(\w+)/g;
1340
- let declMatch;
1341
- while ((declMatch = localDeclPattern.exec(code)) !== null) {
1342
- if (!exportedLocals.has(declMatch[1])) {
1343
- strippedBindings.push(declMatch[1]);
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();
1344
1440
  }
1345
1441
  }
1346
- const importPattern = /import\s*\{([^}]*)\}\s*from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1347
- let importMatch;
1348
- while ((importMatch = importPattern.exec(code)) !== null) {
1349
- for (const spec of importMatch[1].split(",")) {
1350
- const m = spec.trim().match(/^[A-Za-z_$][\w$]*(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
1351
- if (m) strippedBindings.push(m[1] || m[0].trim().split(/\s/)[0]);
1442
+ }
1443
+ if (hasStaticHandlerCode && !isRscEnv) {
1444
+ const fnNames = getFnNames(STATIC_CONFIG.fnName);
1445
+ const bindings = getBindings(code, fnNames);
1446
+ const wholeFile = generateWholeFileStubs(
1447
+ STATIC_CONFIG,
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));
1352
1471
  }
1353
1472
  }
1354
- const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1355
- while ((importMatch = defaultImportPattern.exec(code)) !== null) {
1356
- strippedBindings.push(importMatch[1]);
1357
- }
1358
- const nsImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
1359
- while ((importMatch = nsImportPattern.exec(code)) !== null) {
1360
- strippedBindings.push(importMatch[1]);
1361
- }
1362
- if (strippedBindings.length > 0) {
1363
- const preservedBindings = allBindings.filter((b) => {
1364
- const fc = code.slice(b.callExprStart, b.callOpenParenPos + 1);
1365
- return handleFnNames.some((n) => fc.includes(n)) || lsFnNames.some((n) => fc.includes(n));
1366
- });
1367
- const strippedRe = new RegExp(
1368
- `\\b(?:${strippedBindings.join("|")})\\b`
1369
- );
1370
- canStubWholeFile = !preservedBindings.some((b) => {
1371
- const expr = code.slice(b.callExprStart, b.callCloseParenPos + 1);
1372
- return strippedRe.test(expr);
1373
- });
1374
- }
1375
- }
1376
- if (canStubWholeFile) {
1377
- const lines = [];
1378
- const neededImports = [];
1379
- if (handleFnNames.length > 0) neededImports.push("createHandle");
1380
- if (lsFnNames.length > 0) neededImports.push("createLocationState");
1381
- if (neededImports.length > 0) {
1382
- lines.push(
1383
- `import { ${neededImports.join(", ")} } from "@rangojs/router";`
1384
- );
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
+ }
1385
1518
  }
1386
- for (const binding of allBindings) {
1387
- const fnCall = code.slice(
1388
- binding.callExprStart,
1389
- binding.callOpenParenPos + 1
1390
- );
1391
- const isHandle = handleFnNames.some((n) => fnCall.includes(n));
1392
- const isLocationState = lsFnNames.some((n) => fnCall.includes(n));
1393
- const primaryName = binding.exportNames[0];
1394
- const stubId = makeStubId(filePath, primaryName, isBuild);
1395
- if (isHandle || isLocationState) {
1396
- const rawArgs = code.slice(
1397
- binding.callOpenParenPos + 1,
1398
- binding.callCloseParenPos
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";`
1399
1527
  );
1400
- const canonicalName = isHandle ? "createHandle" : "createLocationState";
1401
- const activeFnNames = isHandle ? handleFnNames : lsFnNames;
1402
- let rawCallee = code.slice(
1528
+ }
1529
+ for (const binding of allBindings) {
1530
+ const fnCall = code.slice(
1403
1531
  binding.callExprStart,
1404
- binding.callOpenParenPos
1532
+ binding.callOpenParenPos + 1
1405
1533
  );
1406
- for (const alias of activeFnNames) {
1407
- if (alias !== canonicalName && rawCallee.startsWith(alias)) {
1408
- rawCallee = canonicalName + rawCallee.slice(alias.length);
1409
- break;
1410
- }
1411
- }
1412
- if (isHandle) {
1413
- const idParam = binding.argCount === 0 ? `undefined, "${stubId}"` : `, "${stubId}"`;
1414
- lines.push(
1415
- `export const ${primaryName} = ${rawCallee}(${rawArgs}${idParam});`
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
1416
1548
  );
1417
- lines.push(`${primaryName}.$$id = "${stubId}";`);
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
+ }
1418
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
+ }
1419
1579
  lines.push(
1420
- `export const ${primaryName} = ${rawCallee}(${rawArgs});`
1421
- );
1422
- lines.push(
1423
- `${primaryName}.__rsc_ls_key = "__rsc_ls_${stubId}";`
1580
+ `export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
1424
1581
  );
1425
- }
1426
- for (const name of binding.exportNames.slice(1)) {
1427
- lines.push(`export const ${name} = ${primaryName};`);
1428
- }
1429
- } else {
1430
- let brand = "loader";
1431
- if (prerenderFnNames.some((n) => fnCall.includes(n))) {
1432
- brand = PRERENDER_CONFIG.brand;
1433
- } else if (staticFnNames.some((n) => fnCall.includes(n))) {
1434
- brand = STATIC_CONFIG.brand;
1435
- }
1436
- lines.push(
1437
- `export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
1438
- );
1439
- for (const name of binding.exportNames.slice(1)) {
1440
- lines.push(`export const ${name} = ${primaryName};`);
1582
+ for (const name of binding.exportNames.slice(1)) {
1583
+ lines.push(`export const ${name} = ${primaryName};`);
1584
+ }
1441
1585
  }
1442
1586
  }
1587
+ return { code: lines.join("\n") + "\n", map: null };
1443
1588
  }
1444
- return { code: lines.join("\n") + "\n", map: null };
1445
1589
  }
1446
- }
1447
- if (hasStaticHandlerCode && isRscEnv && isBuild) {
1448
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1449
- const exportNames = getBindings(code, fnNames).map(
1450
- (b) => b.exportNames[0]
1451
- );
1452
- if (exportNames.length > 0) {
1453
- staticHandlerModules.set(id, exportNames);
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
+ }
1454
1598
  }
1455
- }
1456
- const s = new MagicString4(code);
1457
- if (hasLoaderCode) {
1458
- const fnNames = getFnNames("createLoader");
1459
- changed = transformLoaders(getBindings(code, fnNames), s, filePath, isBuild) || changed;
1460
- }
1461
- if (hasHandleCode) {
1462
- const fnNames = getFnNames("createHandle");
1463
- changed = transformHandles(
1464
- getBindings(code, fnNames),
1465
- s,
1466
- code,
1467
- filePath,
1468
- isBuild
1469
- ) || changed;
1470
- }
1471
- if (hasLocationStateCode) {
1472
- const fnNames = getFnNames("createLocationState");
1473
- changed = transformLocationState(
1474
- getBindings(code, fnNames),
1475
- s,
1476
- filePath,
1477
- isBuild
1478
- ) || changed;
1479
- }
1480
- if (hasPrerenderHandlerCode) {
1481
- const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
1482
- const bindings = getBindings(code, fnNames);
1483
- if (isRscEnv) {
1484
- changed = transformHandlerIds(
1485
- PRERENDER_CONFIG,
1486
- bindings,
1599
+ const s = new MagicString4(code);
1600
+ if (hasLoaderCode) {
1601
+ const fnNames = getFnNames("createLoader");
1602
+ changed = transformLoaders(
1603
+ getBindings(code, fnNames),
1487
1604
  s,
1488
1605
  filePath,
1489
1606
  isBuild
1490
1607
  ) || changed;
1491
- } else {
1492
- changed = stubHandlerExprs(
1493
- PRERENDER_CONFIG,
1494
- bindings,
1608
+ }
1609
+ if (hasHandleCode) {
1610
+ const fnNames = getFnNames("createHandle");
1611
+ changed = transformHandles(
1612
+ getBindings(code, fnNames),
1495
1613
  s,
1614
+ code,
1496
1615
  filePath,
1497
1616
  isBuild
1498
1617
  ) || changed;
1499
1618
  }
1500
- }
1501
- if (hasStaticHandlerCode) {
1502
- const fnNames = getFnNames(STATIC_CONFIG.fnName);
1503
- const bindings = getBindings(code, fnNames);
1504
- if (isRscEnv) {
1505
- changed = transformHandlerIds(
1506
- STATIC_CONFIG,
1507
- bindings,
1619
+ if (hasLocationStateCode) {
1620
+ const fnNames = getFnNames("createLocationState");
1621
+ changed = transformLocationState(
1622
+ getBindings(code, fnNames),
1508
1623
  s,
1509
1624
  filePath,
1510
1625
  isBuild
1511
1626
  ) || changed;
1512
- } else {
1513
- changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
1514
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);
1515
1671
  }
1516
- if (!changed) return;
1517
- return {
1518
- code: s.toString(),
1519
- map: s.generateMap({ source: id, includeContent: true })
1520
- };
1521
1672
  }
1522
1673
  };
1523
1674
  }
@@ -1525,12 +1676,14 @@ ${lazyImports.join(",\n")}
1525
1676
  // src/vite/plugins/use-cache-transform.ts
1526
1677
  import path5 from "node:path";
1527
1678
  import MagicString5 from "magic-string";
1679
+ var debug4 = createRangoDebugger(NS.transform);
1528
1680
  var CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
1529
1681
  var LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
1530
1682
  function useCacheTransform() {
1531
1683
  let projectRoot = "";
1532
1684
  let isBuild = false;
1533
1685
  let rscTransforms = null;
1686
+ const counter = createCounter(debug4, "use-cache");
1534
1687
  return {
1535
1688
  name: "@rangojs/router:use-cache",
1536
1689
  enforce: "post",
@@ -1538,53 +1691,61 @@ function useCacheTransform() {
1538
1691
  projectRoot = config.root;
1539
1692
  isBuild = config.command === "build";
1540
1693
  },
1694
+ buildEnd() {
1695
+ counter?.flush();
1696
+ },
1541
1697
  async transform(code, id) {
1542
1698
  if (this.environment?.name !== "rsc") return;
1543
1699
  if (!code.includes("use cache")) return;
1544
1700
  if (id.includes("/node_modules/") || id.startsWith("\0")) return;
1545
1701
  if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
1546
- 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;
1547
1717
  try {
1548
- rscTransforms = await import("@vitejs/plugin-rsc/transforms");
1718
+ const { parseAst: parseAst4 } = await import("vite");
1719
+ ast = parseAst4(code);
1549
1720
  } catch {
1550
1721
  return;
1551
1722
  }
1552
- }
1553
- const {
1554
- hasDirective,
1555
- transformWrapExport,
1556
- transformHoistInlineDirective
1557
- } = rscTransforms;
1558
- let ast;
1559
- try {
1560
- const { parseAst: parseAst4 } = await import("vite");
1561
- ast = parseAst4(code);
1562
- } catch {
1563
- return;
1564
- }
1565
- const filePath = normalizePath(path5.relative(projectRoot, id));
1566
- const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
1567
- if (hasDirective(ast.body, "use cache")) {
1568
- 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(
1569
1737
  code,
1570
1738
  ast,
1571
1739
  filePath,
1572
1740
  id,
1573
1741
  isBuild,
1574
- isLayoutOrTemplate,
1575
- transformWrapExport
1742
+ transformHoistInlineDirective
1576
1743
  );
1744
+ warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1745
+ if (functionResult) return functionResult;
1746
+ } finally {
1747
+ counter?.record(id, performance.now() - start);
1577
1748
  }
1578
- const functionResult = transformFunctionLevelUseCache(
1579
- code,
1580
- ast,
1581
- filePath,
1582
- id,
1583
- isBuild,
1584
- transformHoistInlineDirective
1585
- );
1586
- warnOnNearMissDirectives(ast, id, this.warn.bind(this));
1587
- if (functionResult) return functionResult;
1588
1749
  }
1589
1750
  };
1590
1751
  }
@@ -1707,6 +1868,7 @@ function warnOnNearMissDirectives(ast, fileId, warn) {
1707
1868
  }
1708
1869
 
1709
1870
  // src/vite/plugins/client-ref-dedup.ts
1871
+ var debug5 = createRangoDebugger(NS.transform);
1710
1872
  var CLIENT_IN_SERVER_PROXY_PREFIX = "virtual:vite-rsc/client-in-server-package-proxy/";
1711
1873
  function extractPackageName(absolutePath) {
1712
1874
  const marker = "/node_modules/";
@@ -1723,6 +1885,7 @@ function extractPackageName(absolutePath) {
1723
1885
  }
1724
1886
  function clientRefDedup() {
1725
1887
  let clientExclude = [];
1888
+ const dedupedPackages = /* @__PURE__ */ new Set();
1726
1889
  return {
1727
1890
  name: "@rangojs/router:client-ref-dedup",
1728
1891
  enforce: "pre",
@@ -1731,6 +1894,15 @@ function clientRefDedup() {
1731
1894
  const clientEnv = config.environments?.["client"];
1732
1895
  clientExclude = clientEnv?.optimizeDeps?.exclude ?? config.optimizeDeps?.exclude ?? [];
1733
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
+ },
1734
1906
  resolveId(source, importer, options) {
1735
1907
  if (this.environment?.name !== "client") return;
1736
1908
  if (!importer?.includes(CLIENT_IN_SERVER_PROXY_PREFIX)) return;
@@ -1739,6 +1911,7 @@ function clientRefDedup() {
1739
1911
  const packageName = extractPackageName(source);
1740
1912
  if (!packageName) return;
1741
1913
  if (clientExclude.includes(packageName)) return;
1914
+ if (debug5) dedupedPackages.add(packageName);
1742
1915
  return `\0rango:dedup/${packageName}`;
1743
1916
  },
1744
1917
  load(id) {
@@ -1861,12 +2034,13 @@ function getVirtualVersionContent(version) {
1861
2034
 
1862
2035
  // src/vite/utils/package-resolution.ts
1863
2036
  import { existsSync } from "node:fs";
2037
+ import { createRequire } from "node:module";
1864
2038
  import { resolve } from "node:path";
1865
2039
 
1866
2040
  // package.json
1867
2041
  var package_default = {
1868
2042
  name: "@rangojs/router",
1869
- version: "0.0.0-experimental.963406dd",
2043
+ version: "0.0.0-experimental.97",
1870
2044
  description: "Django-inspired RSC router with composable URL patterns",
1871
2045
  keywords: [
1872
2046
  "react",
@@ -1999,16 +2173,18 @@ var package_default = {
1999
2173
  tag: "experimental"
2000
2174
  },
2001
2175
  scripts: {
2002
- 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",
2003
2177
  prepublishOnly: "pnpm build",
2004
- typecheck: "tsc --noEmit",
2178
+ typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit",
2005
2179
  test: "playwright test",
2006
2180
  "test:ui": "playwright test --ui",
2007
2181
  "test:unit": "vitest run",
2008
2182
  "test:unit:watch": "vitest"
2009
2183
  },
2010
2184
  dependencies: {
2011
- "@vitejs/plugin-rsc": "^0.5.19",
2185
+ "@types/debug": "^4.1.12",
2186
+ "@vitejs/plugin-rsc": "^0.5.23",
2187
+ debug: "^4.4.1",
2012
2188
  "magic-string": "^0.30.17",
2013
2189
  picomatch: "^4.0.3",
2014
2190
  "rsc-html-stream": "^0.0.7"
@@ -2028,7 +2204,7 @@ var package_default = {
2028
2204
  },
2029
2205
  peerDependencies: {
2030
2206
  "@cloudflare/vite-plugin": "^1.25.0",
2031
- "@vitejs/plugin-rsc": "^0.5.14",
2207
+ "@vitejs/plugin-rsc": "^0.5.23",
2032
2208
  react: "^18.0.0 || ^19.0.0",
2033
2209
  vite: "^7.3.0"
2034
2210
  },
@@ -2043,6 +2219,7 @@ var package_default = {
2043
2219
  };
2044
2220
 
2045
2221
  // src/vite/utils/package-resolution.ts
2222
+ var require2 = createRequire(import.meta.url);
2046
2223
  var VIRTUAL_PACKAGE_NAME = "@rangojs/router";
2047
2224
  function getPublishedPackageName() {
2048
2225
  return package_default.name;
@@ -2083,6 +2260,20 @@ function getPackageAliases() {
2083
2260
  }
2084
2261
  return aliases;
2085
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
+ }
2086
2277
 
2087
2278
  // src/build/route-types/param-extraction.ts
2088
2279
  function extractParamsFromPattern(pattern) {
@@ -2980,6 +3171,7 @@ import * as Vite from "vite";
2980
3171
 
2981
3172
  // src/vite/plugins/performance-tracks.ts
2982
3173
  import { readFile } from "node:fs/promises";
3174
+ var debug6 = createRangoDebugger(NS.transform);
2983
3175
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
2984
3176
  function buildPatchReplacement(match, debugInfoVar) {
2985
3177
  return `${match}
@@ -3021,19 +3213,23 @@ function performanceTracksOptimizeDepsPlugin() {
3021
3213
  };
3022
3214
  }
3023
3215
  function performanceTracksPlugin() {
3216
+ const counter = createCounter(debug6, "performance-tracks");
3024
3217
  return {
3025
3218
  name: "@rangojs/router:performance-tracks",
3219
+ buildEnd() {
3220
+ counter?.flush();
3221
+ },
3026
3222
  transform(code, id) {
3027
3223
  if (!id.includes("react-server-dom") || !id.includes("client")) return;
3028
- const patched = patchRsdwClientDebugInfoRecovery(code);
3029
- if (!patched.debugInfoVar) return;
3030
- if (process.env.INTERNAL_RANGO_DEBUG)
3031
- console.log(
3032
- "[perf-tracks] patched RSDW client (var:",
3033
- patched.debugInfoVar,
3034
- ")"
3035
- );
3036
- 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
+ }
3037
3233
  }
3038
3234
  };
3039
3235
  }
@@ -3180,11 +3376,10 @@ function createVersionInjectorPlugin(rscEntryPath) {
3180
3376
  if (normalizedId !== normalizedEntry) {
3181
3377
  return null;
3182
3378
  }
3183
- const prepend = [];
3379
+ const prepend = [
3380
+ `import "virtual:rsc-router/routes-manifest";`
3381
+ ];
3184
3382
  let newCode = code;
3185
- if (!code.includes("virtual:rsc-router/routes-manifest")) {
3186
- prepend.push(`import "virtual:rsc-router/routes-manifest";`);
3187
- }
3188
3383
  const needsVersion = code.includes("createRSCHandler") && !code.includes("@rangojs/router:version") && /createRSCHandler\s*\(\s*\{/.test(code);
3189
3384
  if (needsVersion) {
3190
3385
  prepend.push(`import { VERSION } from "@rangojs/router:version";`);
@@ -3193,8 +3388,21 @@ function createVersionInjectorPlugin(rscEntryPath) {
3193
3388
  "createRSCHandler({\n version: VERSION,"
3194
3389
  );
3195
3390
  }
3196
- if (prepend.length === 0 && newCode === code) return null;
3197
- 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");
3198
3406
  return {
3199
3407
  code: newCode,
3200
3408
  map: null
@@ -3204,6 +3412,7 @@ function createVersionInjectorPlugin(rscEntryPath) {
3204
3412
  }
3205
3413
 
3206
3414
  // src/vite/plugins/cjs-to-esm.ts
3415
+ var debug7 = createRangoDebugger(NS.transform);
3207
3416
  function createCjsToEsmPlugin() {
3208
3417
  return {
3209
3418
  name: "@rangojs/router:cjs-to-esm",
@@ -3213,6 +3422,7 @@ function createCjsToEsmPlugin() {
3213
3422
  if (cleanId.includes("vendor/react-server-dom/client.browser.js") || cleanId.includes("vendor\\react-server-dom\\client.browser.js")) {
3214
3423
  const isProd = process.env.NODE_ENV === "production";
3215
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);
3216
3426
  return {
3217
3427
  code: `export * from "${cjsFile}";`,
3218
3428
  map: null
@@ -3248,6 +3458,7 @@ function createCjsToEsmPlugin() {
3248
3458
  "export const $1 ="
3249
3459
  );
3250
3460
  transformed = license + "\n" + transformed;
3461
+ debug7?.("cjs-to-esm body rewrite %s", id);
3251
3462
  return {
3252
3463
  code: transformed,
3253
3464
  map: null
@@ -3262,7 +3473,7 @@ function createCjsToEsmPlugin() {
3262
3473
  import { createServer as createViteServer } from "vite";
3263
3474
  import { resolve as resolve8 } from "node:path";
3264
3475
  import { readFileSync as readFileSync6 } from "node:fs";
3265
- import { createRequire } from "node:module";
3476
+ import { createRequire as createRequire2, register } from "node:module";
3266
3477
  import { pathToFileURL } from "node:url";
3267
3478
 
3268
3479
  // src/vite/plugins/virtual-stub-plugin.ts
@@ -3289,9 +3500,117 @@ function createVirtualStubPlugin() {
3289
3500
  };
3290
3501
  }
3291
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
+
3292
3610
  // src/vite/plugins/client-ref-hashing.ts
3293
3611
  import { relative } from "node:path";
3294
3612
  import { createHash as createHash2 } from "node:crypto";
3613
+ var debug8 = createRangoDebugger(NS.transform);
3295
3614
  var CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
3296
3615
  var CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
3297
3616
  var FS_PREFIX = "/@fs/";
@@ -3330,6 +3649,7 @@ function transformClientRefs(code, projectRoot) {
3330
3649
  return hasReplacement ? result : null;
3331
3650
  }
3332
3651
  function hashClientRefs(projectRoot) {
3652
+ const counter = createCounter(debug8, "hash-client-refs");
3333
3653
  return {
3334
3654
  name: "@rangojs/router:hash-client-refs",
3335
3655
  // Run after the RSC plugin's transform (default enforce is normal)
@@ -3337,10 +3657,18 @@ function hashClientRefs(projectRoot) {
3337
3657
  applyToEnvironment(env) {
3338
3658
  return env.name === "rsc";
3339
3659
  },
3340
- transform(code, _id) {
3341
- const result = transformClientRefs(code, projectRoot);
3342
- if (result === null) return;
3343
- 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
+ }
3344
3672
  }
3345
3673
  };
3346
3674
  }
@@ -3468,6 +3796,12 @@ function markSelfGenWrite(state, filePath, content) {
3468
3796
  state.selfWrittenGenFiles.set(filePath, { at: Date.now(), hash });
3469
3797
  }
3470
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) {
3471
3805
  const info = state.selfWrittenGenFiles.get(filePath);
3472
3806
  if (!info) return false;
3473
3807
  if (Date.now() - info.at > state.SELF_WRITE_WINDOW_MS) {
@@ -3478,7 +3812,7 @@ function consumeSelfGenWrite(state, filePath) {
3478
3812
  const current = readFileSync3(filePath, "utf-8");
3479
3813
  const currentHash = createHash3("sha256").update(current).digest("hex");
3480
3814
  if (currentHash === info.hash) {
3481
- state.selfWrittenGenFiles.delete(filePath);
3815
+ if (consume) state.selfWrittenGenFiles.delete(filePath);
3482
3816
  return true;
3483
3817
  }
3484
3818
  return false;
@@ -3582,11 +3916,19 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
3582
3916
  let hadOmittedOptional = false;
3583
3917
  for (const [key, value] of Object.entries(params)) {
3584
3918
  const escaped = escapeRegExp2(key);
3585
- result = result.replace(
3586
- new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
3587
- encode(value)
3588
- );
3589
- 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
+ }
3590
3932
  }
3591
3933
  result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
3592
3934
  hadOmittedOptional = true;
@@ -3692,8 +4034,14 @@ function copyStagedBuildAssets(projectRoot, fileNames) {
3692
4034
  }
3693
4035
 
3694
4036
  // src/vite/discovery/prerender-collection.ts
4037
+ var debug9 = createRangoDebugger(NS.prerender);
3695
4038
  async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3696
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
+ );
3697
4045
  const entries = [];
3698
4046
  const allRoutes = {};
3699
4047
  for (const { manifest: m } of allManifests) {
@@ -3743,6 +4091,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3743
4091
  });
3744
4092
  } else {
3745
4093
  if (def?.getParams) {
4094
+ const getParamsStart = debug9 ? performance.now() : 0;
3746
4095
  try {
3747
4096
  const buildVars = {};
3748
4097
  const buildEnv = state.resolvedBuildEnv;
@@ -3761,6 +4110,12 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3761
4110
  }
3762
4111
  };
3763
4112
  const paramsList = await def.getParams(getParamsCtx);
4113
+ debug9?.(
4114
+ "getParams %s -> %d params (%sms)",
4115
+ routeName,
4116
+ paramsList.length,
4117
+ (performance.now() - getParamsStart).toFixed(1)
4118
+ );
3764
4119
  const concurrency = def.options?.concurrency ?? 1;
3765
4120
  const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
3766
4121
  for (const params of paramsList) {
@@ -3826,12 +4181,23 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3826
4181
  );
3827
4182
  }
3828
4183
  }
3829
- if (entries.length === 0) return;
4184
+ if (entries.length === 0) {
4185
+ debug9?.(
4186
+ "no prerender entries (done in %sms)",
4187
+ (performance.now() - overallStart).toFixed(1)
4188
+ );
4189
+ return;
4190
+ }
3830
4191
  const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
3831
4192
  const concurrencyNote = maxConcurrency > 1 ? ` (concurrency: ${maxConcurrency})` : "";
3832
4193
  console.log(
3833
4194
  `[rsc-router] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
3834
4195
  );
4196
+ debug9?.(
4197
+ "prerender loop: %d entries, max concurrency %d",
4198
+ entries.length,
4199
+ maxConcurrency
4200
+ );
3835
4201
  const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
3836
4202
  const manifestEntries = {};
3837
4203
  let doneCount = 0;
@@ -3938,10 +4304,22 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
3938
4304
  console.log(
3939
4305
  `[rsc-router] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`
3940
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
+ );
3941
4314
  }
3942
4315
  async function renderStaticHandlers(state, rscEnv, registry) {
3943
4316
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode || !state.resolvedStaticModules?.size)
3944
4317
  return;
4318
+ const overallStart = debug9 ? performance.now() : 0;
4319
+ debug9?.(
4320
+ "renderStaticHandlers: start (%d static module(s))",
4321
+ state.resolvedStaticModules.size
4322
+ );
3945
4323
  const manifestEntries = {};
3946
4324
  let staticDone = 0;
3947
4325
  let staticSkip = 0;
@@ -4031,13 +4409,29 @@ async function renderStaticHandlers(state, rscEnv, registry) {
4031
4409
  console.log(
4032
4410
  `[rsc-router] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`
4033
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
+ );
4034
4419
  }
4035
4420
 
4036
4421
  // src/vite/discovery/discover-routers.ts
4422
+ var debug10 = createRangoDebugger(NS.discovery);
4037
4423
  async function discoverRouters(state, rscEnv) {
4038
4424
  if (!state.resolvedEntryPath) return;
4039
- await rscEnv.runner.import(state.resolvedEntryPath);
4040
- 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
+ );
4041
4435
  let registry = serverMod.RouterRegistry;
4042
4436
  if (!registry || registry.size === 0) {
4043
4437
  try {
@@ -4079,8 +4473,13 @@ async function discoverRouters(state, rscEnv) {
4079
4473
  );
4080
4474
  }
4081
4475
  }
4082
- 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
+ );
4083
4481
  const generateManifestFull = buildMod.generateManifestFull;
4482
+ debug10?.("inner: found %d router(s) in registry", registry.size);
4084
4483
  const nestedRouterConflict = findNestedRouterConflict(
4085
4484
  [...registry.values()].map((router) => router.__sourceFile).filter(
4086
4485
  (sourceFile) => typeof sourceFile === "string"
@@ -4099,6 +4498,7 @@ async function discoverRouters(state, rscEnv) {
4099
4498
  let mergedRouteTrailingSlash = {};
4100
4499
  let routerMountIndex = 0;
4101
4500
  const allManifests = [];
4501
+ const manifestGenStart = debug10 ? performance.now() : 0;
4102
4502
  for (const [id, router] of registry) {
4103
4503
  if (!router.urlpatterns || !generateManifestFull) {
4104
4504
  continue;
@@ -4173,7 +4573,13 @@ async function discoverRouters(state, rscEnv) {
4173
4573
  );
4174
4574
  }
4175
4575
  }
4576
+ debug10?.(
4577
+ "inner: generated manifests for %d router(s) (%sms)",
4578
+ allManifests.length,
4579
+ (performance.now() - manifestGenStart).toFixed(1)
4580
+ );
4176
4581
  let newMergedRouteTrie = null;
4582
+ const trieStart = debug10 ? performance.now() : 0;
4177
4583
  if (Object.keys(newMergedRouteManifest).length > 0) {
4178
4584
  const buildRouteTrie = buildMod.buildRouteTrie;
4179
4585
  if (buildRouteTrie && mergedRouteAncestry) {
@@ -4236,6 +4642,10 @@ async function discoverRouters(state, rscEnv) {
4236
4642
  }
4237
4643
  }
4238
4644
  }
4645
+ debug10?.(
4646
+ "inner: trie build done (%sms)",
4647
+ (performance.now() - trieStart).toFixed(1)
4648
+ );
4239
4649
  state.mergedRouteManifest = newMergedRouteManifest;
4240
4650
  state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
4241
4651
  state.perRouterManifests = newPerRouterManifests;
@@ -4675,8 +5085,101 @@ function postprocessBundle(state) {
4675
5085
  }
4676
5086
  }
4677
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
+
4678
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
+ }
4679
5181
  async function createTempRscServer(state, options = {}) {
5182
+ ensureCloudflareProtocolLoaderRegistered();
4680
5183
  const { default: rsc } = await import("@vitejs/plugin-rsc");
4681
5184
  return createViteServer({
4682
5185
  root: state.projectRoot,
@@ -4699,6 +5202,7 @@ async function createTempRscServer(state, options = {}) {
4699
5202
  ...options.forceBuild ? [hashClientRefs(state.projectRoot)] : [],
4700
5203
  createVersionPlugin(),
4701
5204
  createVirtualStubPlugin(),
5205
+ createCloudflareProtocolStubPlugin(),
4702
5206
  // Dev prerender must use dev-mode IDs (path-based) to match the workerd
4703
5207
  // runtime. forceBuild produces hashed IDs for production bundle consistency.
4704
5208
  exposeInternalIds(options.forceBuild ? { forceBuild: true } : void 0),
@@ -4715,7 +5219,7 @@ async function resolveBuildEnv(option, factoryCtx) {
4715
5219
  );
4716
5220
  }
4717
5221
  try {
4718
- const userRequire = createRequire(
5222
+ const userRequire = createRequire2(
4719
5223
  resolve8(factoryCtx.root, "package.json")
4720
5224
  );
4721
5225
  const wranglerPath = userRequire.resolve("wrangler");
@@ -4750,6 +5254,7 @@ async function acquireBuildEnv(s, command, mode) {
4750
5254
  if (!result) return false;
4751
5255
  s.resolvedBuildEnv = result.env;
4752
5256
  s.buildEnvDispose = result.dispose ?? null;
5257
+ globalThis[BUILD_ENV_GLOBAL_KEY] = result.env;
4753
5258
  return true;
4754
5259
  }
4755
5260
  async function releaseBuildEnv(s) {
@@ -4762,6 +5267,7 @@ async function releaseBuildEnv(s) {
4762
5267
  s.buildEnvDispose = null;
4763
5268
  }
4764
5269
  s.resolvedBuildEnv = void 0;
5270
+ delete globalThis[BUILD_ENV_GLOBAL_KEY];
4765
5271
  }
4766
5272
  function createRouterDiscoveryPlugin(entryPath, opts) {
4767
5273
  const s = createDiscoveryState(entryPath, opts);
@@ -4817,6 +5323,9 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4817
5323
  const discoveryPromise = new Promise((resolve10) => {
4818
5324
  resolveDiscovery = resolve10;
4819
5325
  });
5326
+ const gate = createDiscoveryGate(s, debugDiscovery);
5327
+ const beginDiscoveryGate = gate.beginGate;
5328
+ const resolveDiscoveryGate = gate.resolveGate;
4820
5329
  const getDevServerOrigin = () => server.resolvedUrls?.local?.[0]?.replace(/\/$/, "") || `http://localhost:${server.config.server.port || 5173}`;
4821
5330
  let prerenderTempServer = null;
4822
5331
  let prerenderNodeRegistry = null;
@@ -4829,40 +5338,180 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4829
5338
  releaseBuildEnv(s).catch(() => {
4830
5339
  });
4831
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
+ }
4832
5372
  async function getOrCreateTempServer() {
4833
- if (prerenderNodeRegistry) {
4834
- 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
+ }
4835
5407
  }
5408
+ debugDiscovery?.(
5409
+ "getOrCreateTempServer: creating new temp server, entry=%s",
5410
+ s.resolvedEntryPath ?? "(unset)"
5411
+ );
4836
5412
  try {
4837
5413
  prerenderTempServer = await createTempRscServer(s, {
4838
5414
  cacheDir: "node_modules/.vite_prerender"
4839
5415
  });
4840
5416
  const tempRscEnv = prerenderTempServer.environments?.rsc;
4841
5417
  if (tempRscEnv?.runner) {
4842
- await tempRscEnv.runner.import(s.resolvedEntryPath);
4843
- const serverMod = await tempRscEnv.runner.import(
4844
- "@rangojs/router/server"
4845
- );
4846
- prerenderNodeRegistry = serverMod.RouterRegistry;
5418
+ await importEntryAndRegistry(tempRscEnv);
4847
5419
  return tempRscEnv;
4848
5420
  }
5421
+ debugDiscovery?.(
5422
+ "getOrCreateTempServer: tempRscEnv.runner unavailable"
5423
+ );
4849
5424
  } catch (err) {
5425
+ debugDiscovery?.(
5426
+ "getOrCreateTempServer: FAILED message=%s",
5427
+ err.message
5428
+ );
4850
5429
  console.warn(
4851
5430
  `[rsc-router] Failed to create temp runner: ${err.message}`
4852
5431
  );
4853
5432
  }
4854
5433
  return null;
4855
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
+ }
4856
5484
  const discover = async () => {
5485
+ const discoverStart = performance.now();
4857
5486
  const rscEnv = server.environments?.rsc;
4858
5487
  if (!rscEnv?.runner) {
5488
+ debugDiscovery?.(
5489
+ "dev: cloudflare path start, __rscRouterDiscoveryActive=%s",
5490
+ globalThis.__rscRouterDiscoveryActive ?? false
5491
+ );
4859
5492
  s.devServerOrigin = getDevServerOrigin();
4860
5493
  try {
4861
- await acquireBuildEnv(s, viteCommand, viteMode);
4862
- 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
+ );
4863
5504
  if (tempRscEnv) {
4864
- await discoverRouters(s, tempRscEnv);
4865
- 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
+ );
4866
5515
  }
4867
5516
  } catch (err) {
4868
5517
  console.warn(
@@ -4870,33 +5519,62 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4870
5519
  ${err.stack}`
4871
5520
  );
4872
5521
  }
5522
+ debugDiscovery?.(
5523
+ "dev discovery done (%sms)",
5524
+ (performance.now() - discoverStart).toFixed(1)
5525
+ );
4873
5526
  resolveDiscovery();
4874
5527
  return;
4875
5528
  }
4876
5529
  try {
4877
- await acquireBuildEnv(s, viteCommand, viteMode);
4878
- const serverMod = await rscEnv.runner.import(
4879
- "@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")
4880
5540
  );
4881
5541
  if (serverMod?.setManifestReadyPromise) {
4882
5542
  serverMod.setManifestReadyPromise(discoveryPromise);
4883
5543
  }
4884
- await discoverRouters(s, rscEnv);
5544
+ await timed(
5545
+ debugDiscovery,
5546
+ "discoverRouters",
5547
+ () => discoverRouters(s, rscEnv)
5548
+ );
4885
5549
  s.devServerOrigin = getDevServerOrigin();
4886
- writeRouteTypesFiles(s);
4887
- await propagateDiscoveryState(rscEnv);
5550
+ timedSync(
5551
+ debugDiscovery,
5552
+ "writeRouteTypesFiles",
5553
+ () => writeRouteTypesFiles(s)
5554
+ );
5555
+ await timed(
5556
+ debugDiscovery,
5557
+ "propagateDiscoveryState",
5558
+ () => propagateDiscoveryState(rscEnv)
5559
+ );
4888
5560
  } catch (err) {
4889
5561
  console.warn(
4890
5562
  `[rsc-router] Router discovery failed: ${err.message}
4891
5563
  ${err.stack}`
4892
5564
  );
4893
5565
  } finally {
5566
+ debugDiscovery?.(
5567
+ "dev discovery done (%sms)",
5568
+ (performance.now() - discoverStart).toFixed(1)
5569
+ );
4894
5570
  resolveDiscovery();
4895
5571
  }
4896
5572
  };
4897
- s.discoveryDone = new Promise((resolve10) => {
4898
- setTimeout(() => discover().then(resolve10, resolve10), 0);
4899
- });
5573
+ beginDiscoveryGate();
5574
+ setTimeout(
5575
+ () => discover().then(resolveDiscoveryGate, resolveDiscoveryGate),
5576
+ 0
5577
+ );
4900
5578
  let mainRegistry = null;
4901
5579
  const propagateDiscoveryState = async (rscEnv) => {
4902
5580
  const serverMod = await rscEnv.runner.import("@rangojs/router/server");
@@ -4931,12 +5609,23 @@ ${err.stack}`
4931
5609
  }
4932
5610
  };
4933
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
+ };
4934
5622
  if (s.discoveryDone) await s.discoveryDone;
4935
5623
  const url = new URL(req.url || "/", "http://localhost");
4936
5624
  const pathname = url.searchParams.get("pathname");
4937
5625
  if (!pathname) {
4938
5626
  res.statusCode = 400;
4939
5627
  res.end("Missing pathname");
5628
+ logResult(400, "missing pathname");
4940
5629
  return;
4941
5630
  }
4942
5631
  const rscEnv = server.environments?.rsc;
@@ -4954,6 +5643,7 @@ ${err.stack}`
4954
5643
  );
4955
5644
  res.statusCode = 500;
4956
5645
  res.end(`Prerender handler error: ${err.message}`);
5646
+ logResult(500, "module refresh failed");
4957
5647
  return;
4958
5648
  }
4959
5649
  } else {
@@ -4968,6 +5658,7 @@ ${err.stack}`
4968
5658
  if (!registry || registry.size === 0) {
4969
5659
  res.statusCode = 503;
4970
5660
  res.end("Prerender runner not available");
5661
+ logResult(503, "no registry");
4971
5662
  return;
4972
5663
  }
4973
5664
  const wantIntercept = url.searchParams.get("intercept") === "1";
@@ -5002,6 +5693,7 @@ ${err.stack}`
5002
5693
  payload = { segments: result.segments, handles: result.handles };
5003
5694
  }
5004
5695
  res.end(JSON.stringify(payload));
5696
+ logResult(200, `match ${result.routeName}`);
5005
5697
  return;
5006
5698
  } catch (err) {
5007
5699
  console.warn(
@@ -5011,6 +5703,7 @@ ${err.stack}`
5011
5703
  }
5012
5704
  res.statusCode = 404;
5013
5705
  res.end("No prerender match");
5706
+ logResult(404, "no match");
5014
5707
  });
5015
5708
  if (opts?.staticRouteTypesGeneration !== false) {
5016
5709
  const isGeneratedRouteFile = (filePath) => filePath.endsWith(".gen.ts") && (filePath.includes("named-routes.gen.ts") || filePath.includes("urls.gen.ts"));
@@ -5030,42 +5723,96 @@ ${err.stack}`
5030
5723
  return true;
5031
5724
  };
5032
5725
  let routeChangeTimer;
5033
- let runtimeRediscoveryInProgress = false;
5034
5726
  const refreshRuntimeDiscovery = async () => {
5035
5727
  const rscEnv = server.environments?.rsc;
5036
- if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
5037
- runtimeRediscoveryInProgress = true;
5038
- try {
5039
- await discoverRouters(s, rscEnv);
5040
- writeRouteTypesFiles(s);
5041
- await propagateDiscoveryState(rscEnv);
5042
- } catch (err) {
5043
- console.warn(
5044
- `[rsc-router] Runtime re-discovery failed: ${err.message}`
5045
- );
5046
- } finally {
5047
- runtimeRediscoveryInProgress = false;
5048
- }
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
+ });
5049
5782
  };
5050
5783
  const scheduleRouteRegeneration = () => {
5051
5784
  clearTimeout(routeChangeTimer);
5052
5785
  routeChangeTimer = setTimeout(() => {
5053
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;
5054
5790
  try {
5055
- writeCombinedRouteTypesWithTracking(s);
5056
- if (s.perRouterManifests.length > 0) {
5057
- 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
+ }
5058
5800
  }
5059
5801
  } catch (err) {
5060
5802
  console.error(
5061
5803
  `[rsc-router] Route regeneration error: ${err.message}`
5062
5804
  );
5063
5805
  }
5806
+ debugDiscovery?.(
5807
+ "watcher: regenerated gen files (%sms)",
5808
+ (performance.now() - regenStart).toFixed(1)
5809
+ );
5064
5810
  if (s.perRouterManifests.length > 0) {
5065
5811
  refreshRuntimeDiscovery().catch((err) => {
5066
5812
  console.warn(
5067
5813
  `[rsc-router] Runtime re-discovery error: ${err.message}`
5068
5814
  );
5815
+ resolveDiscoveryGate();
5069
5816
  });
5070
5817
  }
5071
5818
  }, 100);
@@ -5083,6 +5830,12 @@ ${err.stack}`
5083
5830
  const hasUrls = source.includes("urls(");
5084
5831
  const hasCreateRouter = /\bcreateRouter\s*[<(]/.test(source);
5085
5832
  if (!hasUrls && !hasCreateRouter) return;
5833
+ debugDiscovery?.(
5834
+ "watcher: %s matches (urls=%s, router=%s)",
5835
+ filePath,
5836
+ hasUrls,
5837
+ hasCreateRouter
5838
+ );
5086
5839
  if (hasCreateRouter) {
5087
5840
  const nestedRouterConflict = findNestedRouterConflict([
5088
5841
  ...s.cachedRouterFiles ?? [],
@@ -5096,6 +5849,9 @@ ${err.stack}`
5096
5849
  }
5097
5850
  s.cachedRouterFiles = void 0;
5098
5851
  }
5852
+ if (s.perRouterManifests.length > 0) {
5853
+ gate.noteRouteEvent();
5854
+ }
5099
5855
  scheduleRouteRegeneration();
5100
5856
  } catch {
5101
5857
  }
@@ -5115,15 +5871,31 @@ ${err.stack}`
5115
5871
  // The manifest data is stored for the virtual module's load hook.
5116
5872
  async buildStart() {
5117
5873
  if (!s.isBuildMode) return;
5118
- 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 ?? "?");
5119
5883
  resetStagedBuildAssets(s.projectRoot);
5120
5884
  s.prerenderManifestEntries = null;
5121
5885
  s.staticManifestEntries = null;
5122
- await acquireBuildEnv(s, viteCommand, viteMode);
5886
+ await timed(
5887
+ debugDiscovery,
5888
+ "build acquireBuildEnv",
5889
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5890
+ );
5123
5891
  let tempServer = null;
5124
5892
  globalThis.__rscRouterDiscoveryActive = true;
5125
5893
  try {
5126
- tempServer = await createTempRscServer(s, { forceBuild: true });
5894
+ tempServer = await timed(
5895
+ debugDiscovery,
5896
+ "build createTempRscServer",
5897
+ () => createTempRscServer(s, { forceBuild: true })
5898
+ );
5127
5899
  const rscEnv = tempServer.environments?.rsc;
5128
5900
  if (!rscEnv?.runner) {
5129
5901
  console.warn(
@@ -5137,8 +5909,16 @@ ${err.stack}`
5137
5909
  if (tempIdsPlugin?.api?.staticHandlerModules) {
5138
5910
  s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
5139
5911
  }
5140
- await discoverRouters(s, rscEnv);
5141
- 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
+ );
5142
5922
  } catch (err) {
5143
5923
  const sourceFile = err.stack?.split("\n").find(
5144
5924
  (line) => line.includes(s.projectRoot) && !line.includes("node_modules")
@@ -5157,9 +5937,45 @@ ${details}`
5157
5937
  } finally {
5158
5938
  delete globalThis.__rscRouterDiscoveryActive;
5159
5939
  if (tempServer) {
5160
- await tempServer.close();
5940
+ await timed(
5941
+ debugDiscovery,
5942
+ "build tempServer.close",
5943
+ () => tempServer.close()
5944
+ );
5161
5945
  }
5162
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 [];
5163
5979
  }
5164
5980
  },
5165
5981
  // Virtual module: provides the pre-generated route manifest as a JS module
@@ -5176,17 +5992,36 @@ ${details}`
5176
5992
  async load(id) {
5177
5993
  if (id === "\0" + VIRTUAL_ROUTES_MANIFEST_ID) {
5178
5994
  if (s.discoveryDone) {
5179
- await s.discoveryDone;
5995
+ await timed(
5996
+ debugRoutes,
5997
+ "await discoveryDone (manifest)",
5998
+ () => s.discoveryDone
5999
+ );
5180
6000
  }
5181
- 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;
5182
6008
  }
5183
6009
  const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
5184
6010
  if (id.startsWith(perRouterPrefix)) {
5185
6011
  if (s.discoveryDone) {
5186
- await s.discoveryDone;
6012
+ await timed(
6013
+ debugRoutes,
6014
+ "await discoveryDone (per-router)",
6015
+ () => s.discoveryDone
6016
+ );
5187
6017
  }
5188
6018
  const routerId = id.slice(perRouterPrefix.length);
5189
- return generatePerRouterModule(s, routerId);
6019
+ const code = await timed(
6020
+ debugRoutes,
6021
+ `generatePerRouterModule ${routerId}`,
6022
+ () => generatePerRouterModule(s, routerId)
6023
+ );
6024
+ return code;
5190
6025
  }
5191
6026
  return null;
5192
6027
  },
@@ -5194,14 +6029,20 @@ ${details}`
5194
6029
  // Used by closeBundle for handler code eviction and prerender data injection.
5195
6030
  generateBundle(_options, bundle) {
5196
6031
  if (this.environment?.name !== "rsc") return;
6032
+ const genStart = debugBuild ? performance.now() : 0;
5197
6033
  for (const [fileName, chunk] of Object.entries(bundle)) {
5198
6034
  if (chunk.type === "chunk" && chunk.isEntry) {
5199
6035
  s.rscEntryFileName = fileName;
5200
6036
  break;
5201
6037
  }
5202
6038
  }
5203
- 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
+ );
5204
6044
  return;
6045
+ }
5205
6046
  s.handlerChunkInfoMap.clear();
5206
6047
  s.staticHandlerChunkInfoMap.clear();
5207
6048
  for (const [fileName, chunk] of Object.entries(bundle)) {
@@ -5245,6 +6086,13 @@ ${details}`
5245
6086
  }
5246
6087
  }
5247
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
+ );
5248
6096
  },
5249
6097
  // Build-time pre-rendering: evict handler code and inject collected prerender data.
5250
6098
  // Collection now happens in-process during discoverRouters() via RSC runner.
@@ -5255,29 +6103,41 @@ ${details}`
5255
6103
  async handler() {
5256
6104
  if (!s.isBuildMode) return;
5257
6105
  if (this.environment && this.environment.name !== "rsc") return;
5258
- postprocessBundle(s);
6106
+ timedSync(
6107
+ debugBuild,
6108
+ "closeBundle postprocessBundle",
6109
+ () => postprocessBundle(s)
6110
+ );
5259
6111
  }
5260
6112
  }
5261
6113
  };
5262
6114
  }
5263
6115
 
5264
6116
  // src/vite/rango.ts
6117
+ var debugConfig = createRangoDebugger(NS.config);
5265
6118
  async function rango(options) {
6119
+ const rangoStart = performance.now();
5266
6120
  const resolvedOptions = options ?? { preset: "node" };
5267
6121
  const preset = resolvedOptions.preset ?? "node";
5268
6122
  const showBanner = resolvedOptions.banner ?? true;
6123
+ debugConfig?.("rango(%s) setup start", preset);
5269
6124
  const plugins = [];
5270
- const rangoAliases = getPackageAliases();
6125
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
5271
6126
  const excludeDeps = [
5272
6127
  ...getExcludeDeps(),
5273
- // The public browser entry re-exports the RSDW browser client.
5274
- // Excluding both keeps Vite from freezing the unpatched bundle into
5275
- // .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.
5276
6136
  "@vitejs/plugin-rsc/browser",
5277
- // Keep the browser RSDW client out of Vite's dep optimizer so our
5278
- // cjs-to-esm transform can patch the real file.
5279
6137
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
5280
6138
  ];
6139
+ const pkg = getPublishedPackageName();
6140
+ const nested = (spec) => `${pkg} > ${spec}`;
5281
6141
  const routerRef = { path: void 0 };
5282
6142
  const prerenderEnabled = true;
5283
6143
  if (preset === "cloudflare") {
@@ -5315,7 +6175,7 @@ async function rango(options) {
5315
6175
  // Pre-bundle rsc-html-stream to prevent discovery during first request
5316
6176
  // Exclude rsc-router modules to ensure same Context instance
5317
6177
  optimizeDeps: {
5318
- include: ["rsc-html-stream/client"],
6178
+ include: [nested("rsc-html-stream/client")],
5319
6179
  exclude: excludeDeps,
5320
6180
  esbuildOptions: sharedEsbuildOptions
5321
6181
  }
@@ -5340,8 +6200,10 @@ async function rango(options) {
5340
6200
  "react-dom/static.edge",
5341
6201
  "react/jsx-runtime",
5342
6202
  "react/jsx-dev-runtime",
5343
- "rsc-html-stream/server",
5344
- "@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
+ )
5345
6207
  ],
5346
6208
  exclude: excludeDeps,
5347
6209
  esbuildOptions: sharedEsbuildOptions
@@ -5356,7 +6218,9 @@ async function rango(options) {
5356
6218
  "react",
5357
6219
  "react/jsx-runtime",
5358
6220
  "react/jsx-dev-runtime",
5359
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6221
+ nested(
6222
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6223
+ )
5360
6224
  ],
5361
6225
  exclude: excludeDeps,
5362
6226
  esbuildOptions: sharedEsbuildOptions
@@ -5437,7 +6301,7 @@ ${list}`);
5437
6301
  "react-dom",
5438
6302
  "react/jsx-runtime",
5439
6303
  "react/jsx-dev-runtime",
5440
- "rsc-html-stream/client"
6304
+ nested("rsc-html-stream/client")
5441
6305
  ],
5442
6306
  exclude: excludeDeps,
5443
6307
  esbuildOptions: sharedEsbuildOptions,
@@ -5454,7 +6318,9 @@ ${list}`);
5454
6318
  "react-dom/static.edge",
5455
6319
  "react/jsx-runtime",
5456
6320
  "react/jsx-dev-runtime",
5457
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6321
+ nested(
6322
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
6323
+ )
5458
6324
  ],
5459
6325
  exclude: excludeDeps,
5460
6326
  esbuildOptions: sharedEsbuildOptions
@@ -5467,7 +6333,9 @@ ${list}`);
5467
6333
  "react",
5468
6334
  "react/jsx-runtime",
5469
6335
  "react/jsx-dev-runtime",
5470
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6336
+ nested(
6337
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
6338
+ )
5471
6339
  ],
5472
6340
  esbuildOptions: sharedEsbuildOptions
5473
6341
  }
@@ -5537,6 +6405,12 @@ ${list}`);
5537
6405
  preset
5538
6406
  })
5539
6407
  );
6408
+ debugConfig?.(
6409
+ "rango(%s) setup done: %d plugin(s) (%sms)",
6410
+ preset,
6411
+ plugins.length,
6412
+ (performance.now() - rangoStart).toFixed(1)
6413
+ );
5540
6414
  return plugins;
5541
6415
  }
5542
6416
 
@@ -5547,7 +6421,7 @@ function poke() {
5547
6421
  apply: "serve",
5548
6422
  configureServer(server) {
5549
6423
  const stdin = process.stdin;
5550
- const debug = process.env.RANGO_POKE_DEBUG === "1";
6424
+ const debug11 = process.env.RANGO_POKE_DEBUG === "1";
5551
6425
  const triggerReload = (source) => {
5552
6426
  server.hot.send({ type: "full-reload", path: "*" });
5553
6427
  server.config.logger.info(` browser reload (${source})`, {
@@ -5580,7 +6454,7 @@ function poke() {
5580
6454
  lines.pop();
5581
6455
  return lines;
5582
6456
  };
5583
- if (debug) {
6457
+ if (debug11) {
5584
6458
  server.config.logger.info(
5585
6459
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
5586
6460
  { timestamp: true }
@@ -5593,7 +6467,7 @@ function poke() {
5593
6467
  );
5594
6468
  }
5595
6469
  const onData = (data) => {
5596
- if (debug) {
6470
+ if (debug11) {
5597
6471
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
5598
6472
  timestamp: true
5599
6473
  });