@rangojs/router 0.0.0-experimental.ede38110 → 0.0.0-experimental.f2d1a2f1

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 (66) hide show
  1. package/README.md +50 -20
  2. package/dist/vite/index.js +353 -49
  3. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  4. package/package.json +5 -3
  5. package/skills/breadcrumbs/SKILL.md +3 -1
  6. package/skills/hooks/SKILL.md +28 -20
  7. package/skills/links/SKILL.md +88 -16
  8. package/skills/loader/SKILL.md +35 -2
  9. package/skills/migrate-react-router/SKILL.md +1 -0
  10. package/skills/response-routes/SKILL.md +8 -0
  11. package/skills/streams-and-websockets/SKILL.md +283 -0
  12. package/skills/typesafety/SKILL.md +3 -1
  13. package/src/browser/app-shell.ts +52 -0
  14. package/src/browser/navigation-bridge.ts +51 -2
  15. package/src/browser/navigation-client.ts +33 -10
  16. package/src/browser/navigation-store.ts +25 -1
  17. package/src/browser/partial-update.ts +20 -1
  18. package/src/browser/prefetch/cache.ts +124 -26
  19. package/src/browser/prefetch/fetch.ts +114 -38
  20. package/src/browser/prefetch/queue.ts +36 -5
  21. package/src/browser/rango-state.ts +53 -13
  22. package/src/browser/react/Link.tsx +18 -13
  23. package/src/browser/react/NavigationProvider.tsx +50 -11
  24. package/src/browser/react/use-navigation.ts +30 -11
  25. package/src/browser/react/use-params.ts +11 -1
  26. package/src/browser/react/use-router.ts +8 -1
  27. package/src/browser/rsc-router.tsx +34 -6
  28. package/src/browser/types.ts +13 -0
  29. package/src/cache/cf/cf-cache-store.ts +5 -7
  30. package/src/index.rsc.ts +3 -0
  31. package/src/index.ts +3 -0
  32. package/src/outlet-context.ts +1 -1
  33. package/src/response-utils.ts +28 -0
  34. package/src/reverse.ts +3 -2
  35. package/src/route-definition/dsl-helpers.ts +16 -3
  36. package/src/route-definition/resolve-handler-use.ts +6 -0
  37. package/src/router/handler-context.ts +20 -3
  38. package/src/router/lazy-includes.ts +1 -1
  39. package/src/router/loader-resolution.ts +3 -0
  40. package/src/router/match-api.ts +3 -3
  41. package/src/router/middleware-types.ts +2 -22
  42. package/src/router/middleware.ts +32 -4
  43. package/src/router/pattern-matching.ts +60 -9
  44. package/src/router/trie-matching.ts +10 -4
  45. package/src/router/url-params.ts +49 -0
  46. package/src/router.ts +1 -2
  47. package/src/rsc/handler.ts +8 -4
  48. package/src/rsc/helpers.ts +69 -41
  49. package/src/rsc/progressive-enhancement.ts +2 -0
  50. package/src/rsc/response-route-handler.ts +14 -1
  51. package/src/rsc/rsc-rendering.ts +7 -0
  52. package/src/rsc/server-action.ts +2 -0
  53. package/src/server/request-context.ts +10 -42
  54. package/src/types/handler-context.ts +2 -34
  55. package/src/types/loader-types.ts +5 -6
  56. package/src/types/request-scope.ts +126 -0
  57. package/src/urls/response-types.ts +2 -10
  58. package/src/vite/debug.ts +86 -0
  59. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  60. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  61. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  62. package/src/vite/plugins/performance-tracks.ts +4 -6
  63. package/src/vite/rango.ts +49 -14
  64. package/src/vite/router-discovery.ts +161 -23
  65. package/src/vite/utils/banner.ts +1 -1
  66. package/src/vite/utils/package-resolution.ts +41 -1
@@ -1859,12 +1859,13 @@ function getVirtualVersionContent(version) {
1859
1859
 
1860
1860
  // src/vite/utils/package-resolution.ts
1861
1861
  import { existsSync } from "node:fs";
1862
+ import { createRequire } from "node:module";
1862
1863
  import { resolve } from "node:path";
1863
1864
 
1864
1865
  // package.json
1865
1866
  var package_default = {
1866
1867
  name: "@rangojs/router",
1867
- version: "0.0.0-experimental.ede38110",
1868
+ version: "0.0.0-experimental.f2d1a2f1",
1868
1869
  description: "Django-inspired RSC router with composable URL patterns",
1869
1870
  keywords: [
1870
1871
  "react",
@@ -1997,9 +1998,9 @@ var package_default = {
1997
1998
  tag: "experimental"
1998
1999
  },
1999
2000
  scripts: {
2000
- build: "pnpm dlx esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && pnpm dlx esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
2001
+ 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",
2001
2002
  prepublishOnly: "pnpm build",
2002
- typecheck: "tsc --noEmit",
2003
+ typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit",
2003
2004
  test: "playwright test",
2004
2005
  "test:ui": "playwright test --ui",
2005
2006
  "test:unit": "vitest run",
@@ -2007,12 +2008,14 @@ var package_default = {
2007
2008
  },
2008
2009
  dependencies: {
2009
2010
  "@vitejs/plugin-rsc": "^0.5.23",
2011
+ debug: "^4.4.1",
2010
2012
  "magic-string": "^0.30.17",
2011
2013
  picomatch: "^4.0.3",
2012
2014
  "rsc-html-stream": "^0.0.7"
2013
2015
  },
2014
2016
  devDependencies: {
2015
2017
  "@playwright/test": "^1.49.1",
2018
+ "@types/debug": "^4.1.12",
2016
2019
  "@types/node": "^24.10.1",
2017
2020
  "@types/react": "catalog:",
2018
2021
  "@types/react-dom": "catalog:",
@@ -2041,6 +2044,7 @@ var package_default = {
2041
2044
  };
2042
2045
 
2043
2046
  // src/vite/utils/package-resolution.ts
2047
+ var require2 = createRequire(import.meta.url);
2044
2048
  var VIRTUAL_PACKAGE_NAME = "@rangojs/router";
2045
2049
  function getPublishedPackageName() {
2046
2050
  return package_default.name;
@@ -2081,6 +2085,20 @@ function getPackageAliases() {
2081
2085
  }
2082
2086
  return aliases;
2083
2087
  }
2088
+ function getVendorAliases() {
2089
+ const specs = [
2090
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
2091
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
2092
+ ];
2093
+ const aliases = {};
2094
+ for (const spec of specs) {
2095
+ try {
2096
+ aliases[spec] = require2.resolve(spec);
2097
+ } catch {
2098
+ }
2099
+ }
2100
+ return aliases;
2101
+ }
2084
2102
 
2085
2103
  // src/build/route-types/param-extraction.ts
2086
2104
  function extractParamsFromPattern(pattern) {
@@ -2978,6 +2996,41 @@ import * as Vite from "vite";
2978
2996
 
2979
2997
  // src/vite/plugins/performance-tracks.ts
2980
2998
  import { readFile } from "node:fs/promises";
2999
+
3000
+ // src/vite/debug.ts
3001
+ import debugFactory from "debug";
3002
+ if (process.env.INTERNAL_RANGO_DEBUG) {
3003
+ const existing = debugFactory.disable();
3004
+ debugFactory.enable(existing ? `${existing},rango:*` : "rango:*");
3005
+ }
3006
+ function createRangoDebugger(namespace) {
3007
+ const primary = debugFactory(namespace);
3008
+ const shadow = debugFactory(`vite:${namespace}`);
3009
+ if (primary.enabled) return primary;
3010
+ if (shadow.enabled) return shadow;
3011
+ return void 0;
3012
+ }
3013
+ async function timed(debug2, label, fn) {
3014
+ if (!debug2) return await fn();
3015
+ const start = performance.now();
3016
+ try {
3017
+ return await fn();
3018
+ } finally {
3019
+ debug2("%s (%sms)", label, (performance.now() - start).toFixed(1));
3020
+ }
3021
+ }
3022
+ function timedSync(debug2, label, fn) {
3023
+ if (!debug2) return fn();
3024
+ const start = performance.now();
3025
+ try {
3026
+ return fn();
3027
+ } finally {
3028
+ debug2("%s (%sms)", label, (performance.now() - start).toFixed(1));
3029
+ }
3030
+ }
3031
+
3032
+ // src/vite/plugins/performance-tracks.ts
3033
+ var debug = createRangoDebugger("rango:transform");
2981
3034
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
2982
3035
  function buildPatchReplacement(match, debugInfoVar) {
2983
3036
  return `${match}
@@ -3025,12 +3078,7 @@ function performanceTracksPlugin() {
3025
3078
  if (!id.includes("react-server-dom") || !id.includes("client")) return;
3026
3079
  const patched = patchRsdwClientDebugInfoRecovery(code);
3027
3080
  if (!patched.debugInfoVar) return;
3028
- if (process.env.INTERNAL_RANGO_DEBUG)
3029
- console.log(
3030
- "[perf-tracks] patched RSDW client (var:",
3031
- patched.debugInfoVar,
3032
- ")"
3033
- );
3081
+ debug?.("patched RSDW client (var: %s)", patched.debugInfoVar);
3034
3082
  return patched.code;
3035
3083
  }
3036
3084
  };
@@ -3260,7 +3308,7 @@ function createCjsToEsmPlugin() {
3260
3308
  import { createServer as createViteServer } from "vite";
3261
3309
  import { resolve as resolve8 } from "node:path";
3262
3310
  import { readFileSync as readFileSync6 } from "node:fs";
3263
- import { createRequire } from "node:module";
3311
+ import { createRequire as createRequire2, register } from "node:module";
3264
3312
  import { pathToFileURL } from "node:url";
3265
3313
 
3266
3314
  // src/vite/plugins/virtual-stub-plugin.ts
@@ -3287,6 +3335,113 @@ function createVirtualStubPlugin() {
3287
3335
  };
3288
3336
  }
3289
3337
 
3338
+ // src/vite/plugins/cloudflare-protocol-stub.ts
3339
+ var VIRTUAL_PREFIX = "virtual:rango-cloudflare-stub-";
3340
+ var NULL_PREFIX = "\0" + VIRTUAL_PREFIX;
3341
+ var CF_PREFIX = "cloudflare:";
3342
+ var BUILD_ENV_GLOBAL_KEY = "__rango_build_env__";
3343
+ var SOURCE_EXT_RE = /\.[mc]?[jt]sx?$/;
3344
+ var IMPORT_NODE_TYPES = /* @__PURE__ */ new Set([
3345
+ "ImportDeclaration",
3346
+ "ImportExpression",
3347
+ "ExportNamedDeclaration",
3348
+ "ExportAllDeclaration"
3349
+ ]);
3350
+ var STUBS = {
3351
+ "cloudflare:workers": `
3352
+ export class DurableObject { constructor(_ctx, _env) {} }
3353
+ export class WorkerEntrypoint { constructor(_ctx, _env) {} }
3354
+ export class WorkflowEntrypoint { constructor(_ctx, _env) {} }
3355
+ export class RpcTarget {}
3356
+ export const env = globalThis[${JSON.stringify(BUILD_ENV_GLOBAL_KEY)}] ?? {};
3357
+ export default {};
3358
+ `,
3359
+ "cloudflare:email": `
3360
+ export class EmailMessage { constructor(_from, _to, _raw) {} }
3361
+ export default {};
3362
+ `,
3363
+ "cloudflare:sockets": `
3364
+ export function connect() { return {}; }
3365
+ export default {};
3366
+ `,
3367
+ "cloudflare:workflows": `
3368
+ export class NonRetryableError extends Error {
3369
+ constructor(message, name) { super(message); this.name = name ?? "NonRetryableError"; }
3370
+ }
3371
+ export default {};
3372
+ `
3373
+ };
3374
+ var FALLBACK_STUB = `export default {};
3375
+ `;
3376
+ function createCloudflareProtocolStubPlugin() {
3377
+ return {
3378
+ name: "@rangojs/router:cloudflare-protocol-stub",
3379
+ transform(code, id) {
3380
+ const cleanId = id.split("?")[0] ?? id;
3381
+ if (!SOURCE_EXT_RE.test(cleanId)) return null;
3382
+ if (!code.includes(CF_PREFIX)) return null;
3383
+ let ast;
3384
+ try {
3385
+ ast = this.parse(code);
3386
+ } catch {
3387
+ return null;
3388
+ }
3389
+ const hits = [];
3390
+ walk(ast, (node) => {
3391
+ if (!IMPORT_NODE_TYPES.has(node.type)) return;
3392
+ const source = node.source;
3393
+ if (!source || source.type !== "Literal") return;
3394
+ if (typeof source.value !== "string") return;
3395
+ if (!source.value.startsWith(CF_PREFIX)) return;
3396
+ if (typeof source.start !== "number" || typeof source.end !== "number")
3397
+ return;
3398
+ hits.push({
3399
+ start: source.start,
3400
+ end: source.end,
3401
+ value: source.value
3402
+ });
3403
+ });
3404
+ if (hits.length === 0) return null;
3405
+ hits.sort((a, b) => b.start - a.start);
3406
+ let out = code;
3407
+ for (const hit of hits) {
3408
+ const submodule = hit.value.slice(CF_PREFIX.length);
3409
+ const quote = code[hit.start] === "'" ? "'" : '"';
3410
+ out = out.slice(0, hit.start) + quote + VIRTUAL_PREFIX + submodule + quote + out.slice(hit.end);
3411
+ }
3412
+ return { code: out, map: null };
3413
+ },
3414
+ resolveId(id) {
3415
+ if (id.startsWith(VIRTUAL_PREFIX)) {
3416
+ return "\0" + id;
3417
+ }
3418
+ return null;
3419
+ },
3420
+ load(id) {
3421
+ if (!id.startsWith(NULL_PREFIX)) return null;
3422
+ const submodule = id.slice(NULL_PREFIX.length);
3423
+ const specifier = CF_PREFIX + submodule;
3424
+ return STUBS[specifier] ?? FALLBACK_STUB;
3425
+ }
3426
+ };
3427
+ }
3428
+ function walk(node, visit) {
3429
+ if (!node || typeof node !== "object") return;
3430
+ if (Array.isArray(node)) {
3431
+ for (const child of node) walk(child, visit);
3432
+ return;
3433
+ }
3434
+ const n = node;
3435
+ if (typeof n.type !== "string") return;
3436
+ visit(n);
3437
+ for (const key in n) {
3438
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
3439
+ continue;
3440
+ }
3441
+ walk(n[key], visit);
3442
+ }
3443
+ }
3444
+
3290
3445
  // src/vite/plugins/client-ref-hashing.ts
3291
3446
  import { relative } from "node:path";
3292
3447
  import { createHash as createHash2 } from "node:crypto";
@@ -4682,7 +4837,24 @@ function postprocessBundle(state) {
4682
4837
  }
4683
4838
 
4684
4839
  // src/vite/router-discovery.ts
4840
+ var debugDiscovery = createRangoDebugger("rango:discovery");
4841
+ var debugRoutes = createRangoDebugger("rango:routes");
4842
+ var loaderHookRegistered = false;
4843
+ function ensureCloudflareProtocolLoaderRegistered() {
4844
+ if (loaderHookRegistered) return;
4845
+ loaderHookRegistered = true;
4846
+ try {
4847
+ register(
4848
+ new URL("./plugins/cloudflare-protocol-loader-hook.mjs", import.meta.url)
4849
+ );
4850
+ } catch (err) {
4851
+ console.warn(
4852
+ `[rsc-router] Could not register Node ESM loader hook for cloudflare:* imports (${err?.message ?? err}). Falling back to Vite transform only.`
4853
+ );
4854
+ }
4855
+ }
4685
4856
  async function createTempRscServer(state, options = {}) {
4857
+ ensureCloudflareProtocolLoaderRegistered();
4686
4858
  const { default: rsc } = await import("@vitejs/plugin-rsc");
4687
4859
  return createViteServer({
4688
4860
  root: state.projectRoot,
@@ -4705,6 +4877,7 @@ async function createTempRscServer(state, options = {}) {
4705
4877
  ...options.forceBuild ? [hashClientRefs(state.projectRoot)] : [],
4706
4878
  createVersionPlugin(),
4707
4879
  createVirtualStubPlugin(),
4880
+ createCloudflareProtocolStubPlugin(),
4708
4881
  // Dev prerender must use dev-mode IDs (path-based) to match the workerd
4709
4882
  // runtime. forceBuild produces hashed IDs for production bundle consistency.
4710
4883
  exposeInternalIds(options.forceBuild ? { forceBuild: true } : void 0),
@@ -4721,7 +4894,7 @@ async function resolveBuildEnv(option, factoryCtx) {
4721
4894
  );
4722
4895
  }
4723
4896
  try {
4724
- const userRequire = createRequire(
4897
+ const userRequire = createRequire2(
4725
4898
  resolve8(factoryCtx.root, "package.json")
4726
4899
  );
4727
4900
  const wranglerPath = userRequire.resolve("wrangler");
@@ -4756,6 +4929,7 @@ async function acquireBuildEnv(s, command, mode) {
4756
4929
  if (!result) return false;
4757
4930
  s.resolvedBuildEnv = result.env;
4758
4931
  s.buildEnvDispose = result.dispose ?? null;
4932
+ globalThis[BUILD_ENV_GLOBAL_KEY] = result.env;
4759
4933
  return true;
4760
4934
  }
4761
4935
  async function releaseBuildEnv(s) {
@@ -4768,6 +4942,7 @@ async function releaseBuildEnv(s) {
4768
4942
  s.buildEnvDispose = null;
4769
4943
  }
4770
4944
  s.resolvedBuildEnv = void 0;
4945
+ delete globalThis[BUILD_ENV_GLOBAL_KEY];
4771
4946
  }
4772
4947
  function createRouterDiscoveryPlugin(entryPath, opts) {
4773
4948
  const s = createDiscoveryState(entryPath, opts);
@@ -4860,15 +5035,33 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4860
5035
  return null;
4861
5036
  }
4862
5037
  const discover = async () => {
5038
+ const discoverStart = performance.now();
4863
5039
  const rscEnv = server.environments?.rsc;
4864
5040
  if (!rscEnv?.runner) {
5041
+ debugDiscovery?.("dev: no rsc runner (cloudflare path)");
4865
5042
  s.devServerOrigin = getDevServerOrigin();
4866
5043
  try {
4867
- await acquireBuildEnv(s, viteCommand, viteMode);
4868
- const tempRscEnv = await getOrCreateTempServer();
5044
+ await timed(
5045
+ debugDiscovery,
5046
+ "acquireBuildEnv",
5047
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5048
+ );
5049
+ const tempRscEnv = await timed(
5050
+ debugDiscovery,
5051
+ "getOrCreateTempServer",
5052
+ () => getOrCreateTempServer()
5053
+ );
4869
5054
  if (tempRscEnv) {
4870
- await discoverRouters(s, tempRscEnv);
4871
- writeRouteTypesFiles(s);
5055
+ await timed(
5056
+ debugDiscovery,
5057
+ "discoverRouters (cloudflare)",
5058
+ () => discoverRouters(s, tempRscEnv)
5059
+ );
5060
+ timedSync(
5061
+ debugDiscovery,
5062
+ "writeRouteTypesFiles",
5063
+ () => writeRouteTypesFiles(s)
5064
+ );
4872
5065
  }
4873
5066
  } catch (err) {
4874
5067
  console.warn(
@@ -4876,27 +5069,54 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
4876
5069
  ${err.stack}`
4877
5070
  );
4878
5071
  }
5072
+ debugDiscovery?.(
5073
+ "dev discovery done (%sms)",
5074
+ (performance.now() - discoverStart).toFixed(1)
5075
+ );
4879
5076
  resolveDiscovery();
4880
5077
  return;
4881
5078
  }
4882
5079
  try {
4883
- await acquireBuildEnv(s, viteCommand, viteMode);
4884
- const serverMod = await rscEnv.runner.import(
4885
- "@rangojs/router/server"
5080
+ debugDiscovery?.("dev: node path start");
5081
+ await timed(
5082
+ debugDiscovery,
5083
+ "acquireBuildEnv",
5084
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5085
+ );
5086
+ const serverMod = await timed(
5087
+ debugDiscovery,
5088
+ "import @rangojs/router/server",
5089
+ () => rscEnv.runner.import("@rangojs/router/server")
4886
5090
  );
4887
5091
  if (serverMod?.setManifestReadyPromise) {
4888
5092
  serverMod.setManifestReadyPromise(discoveryPromise);
4889
5093
  }
4890
- await discoverRouters(s, rscEnv);
5094
+ await timed(
5095
+ debugDiscovery,
5096
+ "discoverRouters",
5097
+ () => discoverRouters(s, rscEnv)
5098
+ );
4891
5099
  s.devServerOrigin = getDevServerOrigin();
4892
- writeRouteTypesFiles(s);
4893
- await propagateDiscoveryState(rscEnv);
5100
+ timedSync(
5101
+ debugDiscovery,
5102
+ "writeRouteTypesFiles",
5103
+ () => writeRouteTypesFiles(s)
5104
+ );
5105
+ await timed(
5106
+ debugDiscovery,
5107
+ "propagateDiscoveryState",
5108
+ () => propagateDiscoveryState(rscEnv)
5109
+ );
4894
5110
  } catch (err) {
4895
5111
  console.warn(
4896
5112
  `[rsc-router] Router discovery failed: ${err.message}
4897
5113
  ${err.stack}`
4898
5114
  );
4899
5115
  } finally {
5116
+ debugDiscovery?.(
5117
+ "dev discovery done (%sms)",
5118
+ (performance.now() - discoverStart).toFixed(1)
5119
+ );
4900
5120
  resolveDiscovery();
4901
5121
  }
4902
5122
  };
@@ -5041,15 +5261,32 @@ ${err.stack}`
5041
5261
  const rscEnv = server.environments?.rsc;
5042
5262
  if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
5043
5263
  runtimeRediscoveryInProgress = true;
5264
+ const hmrStart = performance.now();
5044
5265
  try {
5045
- await discoverRouters(s, rscEnv);
5046
- writeRouteTypesFiles(s);
5047
- await propagateDiscoveryState(rscEnv);
5266
+ await timed(
5267
+ debugDiscovery,
5268
+ "hmr discoverRouters",
5269
+ () => discoverRouters(s, rscEnv)
5270
+ );
5271
+ timedSync(
5272
+ debugDiscovery,
5273
+ "hmr writeRouteTypesFiles",
5274
+ () => writeRouteTypesFiles(s)
5275
+ );
5276
+ await timed(
5277
+ debugDiscovery,
5278
+ "hmr propagateDiscoveryState",
5279
+ () => propagateDiscoveryState(rscEnv)
5280
+ );
5048
5281
  } catch (err) {
5049
5282
  console.warn(
5050
5283
  `[rsc-router] Runtime re-discovery failed: ${err.message}`
5051
5284
  );
5052
5285
  } finally {
5286
+ debugDiscovery?.(
5287
+ "hmr re-discovery done (%sms)",
5288
+ (performance.now() - hmrStart).toFixed(1)
5289
+ );
5053
5290
  runtimeRediscoveryInProgress = false;
5054
5291
  }
5055
5292
  };
@@ -5122,14 +5359,24 @@ ${err.stack}`
5122
5359
  async buildStart() {
5123
5360
  if (!s.isBuildMode) return;
5124
5361
  if (s.mergedRouteManifest !== null) return;
5362
+ const buildStartTime = performance.now();
5363
+ debugDiscovery?.("build: start");
5125
5364
  resetStagedBuildAssets(s.projectRoot);
5126
5365
  s.prerenderManifestEntries = null;
5127
5366
  s.staticManifestEntries = null;
5128
- await acquireBuildEnv(s, viteCommand, viteMode);
5367
+ await timed(
5368
+ debugDiscovery,
5369
+ "build acquireBuildEnv",
5370
+ () => acquireBuildEnv(s, viteCommand, viteMode)
5371
+ );
5129
5372
  let tempServer = null;
5130
5373
  globalThis.__rscRouterDiscoveryActive = true;
5131
5374
  try {
5132
- tempServer = await createTempRscServer(s, { forceBuild: true });
5375
+ tempServer = await timed(
5376
+ debugDiscovery,
5377
+ "build createTempRscServer",
5378
+ () => createTempRscServer(s, { forceBuild: true })
5379
+ );
5133
5380
  const rscEnv = tempServer.environments?.rsc;
5134
5381
  if (!rscEnv?.runner) {
5135
5382
  console.warn(
@@ -5143,8 +5390,16 @@ ${err.stack}`
5143
5390
  if (tempIdsPlugin?.api?.staticHandlerModules) {
5144
5391
  s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
5145
5392
  }
5146
- await discoverRouters(s, rscEnv);
5147
- writeRouteTypesFiles(s);
5393
+ await timed(
5394
+ debugDiscovery,
5395
+ "build discoverRouters",
5396
+ () => discoverRouters(s, rscEnv)
5397
+ );
5398
+ timedSync(
5399
+ debugDiscovery,
5400
+ "build writeRouteTypesFiles",
5401
+ () => writeRouteTypesFiles(s)
5402
+ );
5148
5403
  } catch (err) {
5149
5404
  const sourceFile = err.stack?.split("\n").find(
5150
5405
  (line) => line.includes(s.projectRoot) && !line.includes("node_modules")
@@ -5163,9 +5418,17 @@ ${details}`
5163
5418
  } finally {
5164
5419
  delete globalThis.__rscRouterDiscoveryActive;
5165
5420
  if (tempServer) {
5166
- await tempServer.close();
5421
+ await timed(
5422
+ debugDiscovery,
5423
+ "build tempServer.close",
5424
+ () => tempServer.close()
5425
+ );
5167
5426
  }
5168
5427
  await releaseBuildEnv(s);
5428
+ debugDiscovery?.(
5429
+ "build discovery done (%sms)",
5430
+ (performance.now() - buildStartTime).toFixed(1)
5431
+ );
5169
5432
  }
5170
5433
  },
5171
5434
  // Virtual module: provides the pre-generated route manifest as a JS module
@@ -5182,17 +5445,36 @@ ${details}`
5182
5445
  async load(id) {
5183
5446
  if (id === "\0" + VIRTUAL_ROUTES_MANIFEST_ID) {
5184
5447
  if (s.discoveryDone) {
5185
- await s.discoveryDone;
5448
+ await timed(
5449
+ debugRoutes,
5450
+ "await discoveryDone (manifest)",
5451
+ () => Promise.resolve(s.discoveryDone)
5452
+ );
5186
5453
  }
5187
- return generateRoutesManifestModule(s);
5454
+ const code = await timed(
5455
+ debugRoutes,
5456
+ "generateRoutesManifestModule",
5457
+ () => generateRoutesManifestModule(s)
5458
+ );
5459
+ debugRoutes?.("manifest module emitted (%d bytes)", code?.length ?? 0);
5460
+ return code;
5188
5461
  }
5189
5462
  const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
5190
5463
  if (id.startsWith(perRouterPrefix)) {
5191
5464
  if (s.discoveryDone) {
5192
- await s.discoveryDone;
5465
+ await timed(
5466
+ debugRoutes,
5467
+ "await discoveryDone (per-router)",
5468
+ () => Promise.resolve(s.discoveryDone)
5469
+ );
5193
5470
  }
5194
5471
  const routerId = id.slice(perRouterPrefix.length);
5195
- return generatePerRouterModule(s, routerId);
5472
+ const code = await timed(
5473
+ debugRoutes,
5474
+ `generatePerRouterModule ${routerId}`,
5475
+ () => generatePerRouterModule(s, routerId)
5476
+ );
5477
+ return code;
5196
5478
  }
5197
5479
  return null;
5198
5480
  },
@@ -5268,22 +5550,30 @@ ${details}`
5268
5550
  }
5269
5551
 
5270
5552
  // src/vite/rango.ts
5553
+ var debugConfig = createRangoDebugger("rango:config");
5271
5554
  async function rango(options) {
5555
+ const rangoStart = performance.now();
5272
5556
  const resolvedOptions = options ?? { preset: "node" };
5273
5557
  const preset = resolvedOptions.preset ?? "node";
5274
5558
  const showBanner = resolvedOptions.banner ?? true;
5559
+ debugConfig?.("rango(%s) setup start", preset);
5275
5560
  const plugins = [];
5276
- const rangoAliases = getPackageAliases();
5561
+ const rangoAliases = { ...getPackageAliases(), ...getVendorAliases() };
5277
5562
  const excludeDeps = [
5278
5563
  ...getExcludeDeps(),
5279
- // The public browser entry re-exports the RSDW browser client.
5280
- // Excluding both keeps Vite from freezing the unpatched bundle into
5281
- // .vite/deps before our source transforms run.
5564
+ // plugin-rsc itself injects these into the client env's
5565
+ // optimizeDeps.include, which overrides exclude for the dep's own
5566
+ // pre-bundle entry. What exclude still controls is how *other*
5567
+ // pre-bundled deps treat imports of these specs (external vs inlined)
5568
+ // via esbuildCjsExternalPlugin. The cjs-to-esm transform in
5569
+ // plugins/cjs-to-esm.ts is the fallback for strict-pnpm consumers,
5570
+ // where client.browser's bare include fails to resolve and Vite ends up
5571
+ // serving the raw CJS file at dev-serve time.
5282
5572
  "@vitejs/plugin-rsc/browser",
5283
- // Keep the browser RSDW client out of Vite's dep optimizer so our
5284
- // cjs-to-esm transform can patch the real file.
5285
5573
  "@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
5286
5574
  ];
5575
+ const pkg = getPublishedPackageName();
5576
+ const nested = (spec) => `${pkg} > ${spec}`;
5287
5577
  const routerRef = { path: void 0 };
5288
5578
  const prerenderEnabled = true;
5289
5579
  if (preset === "cloudflare") {
@@ -5321,7 +5611,7 @@ async function rango(options) {
5321
5611
  // Pre-bundle rsc-html-stream to prevent discovery during first request
5322
5612
  // Exclude rsc-router modules to ensure same Context instance
5323
5613
  optimizeDeps: {
5324
- include: ["rsc-html-stream/client"],
5614
+ include: [nested("rsc-html-stream/client")],
5325
5615
  exclude: excludeDeps,
5326
5616
  esbuildOptions: sharedEsbuildOptions
5327
5617
  }
@@ -5346,8 +5636,10 @@ async function rango(options) {
5346
5636
  "react-dom/static.edge",
5347
5637
  "react/jsx-runtime",
5348
5638
  "react/jsx-dev-runtime",
5349
- "rsc-html-stream/server",
5350
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
5639
+ nested("rsc-html-stream/server"),
5640
+ nested(
5641
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
5642
+ )
5351
5643
  ],
5352
5644
  exclude: excludeDeps,
5353
5645
  esbuildOptions: sharedEsbuildOptions
@@ -5362,7 +5654,9 @@ async function rango(options) {
5362
5654
  "react",
5363
5655
  "react/jsx-runtime",
5364
5656
  "react/jsx-dev-runtime",
5365
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
5657
+ nested(
5658
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
5659
+ )
5366
5660
  ],
5367
5661
  exclude: excludeDeps,
5368
5662
  esbuildOptions: sharedEsbuildOptions
@@ -5443,7 +5737,7 @@ ${list}`);
5443
5737
  "react-dom",
5444
5738
  "react/jsx-runtime",
5445
5739
  "react/jsx-dev-runtime",
5446
- "rsc-html-stream/client"
5740
+ nested("rsc-html-stream/client")
5447
5741
  ],
5448
5742
  exclude: excludeDeps,
5449
5743
  esbuildOptions: sharedEsbuildOptions,
@@ -5460,7 +5754,9 @@ ${list}`);
5460
5754
  "react-dom/static.edge",
5461
5755
  "react/jsx-runtime",
5462
5756
  "react/jsx-dev-runtime",
5463
- "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
5757
+ nested(
5758
+ "@vitejs/plugin-rsc/vendor/react-server-dom/client.edge"
5759
+ )
5464
5760
  ],
5465
5761
  exclude: excludeDeps,
5466
5762
  esbuildOptions: sharedEsbuildOptions
@@ -5473,7 +5769,9 @@ ${list}`);
5473
5769
  "react",
5474
5770
  "react/jsx-runtime",
5475
5771
  "react/jsx-dev-runtime",
5476
- "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
5772
+ nested(
5773
+ "@vitejs/plugin-rsc/vendor/react-server-dom/server.edge"
5774
+ )
5477
5775
  ],
5478
5776
  esbuildOptions: sharedEsbuildOptions
5479
5777
  }
@@ -5543,6 +5841,12 @@ ${list}`);
5543
5841
  preset
5544
5842
  })
5545
5843
  );
5844
+ debugConfig?.(
5845
+ "rango(%s) setup done: %d plugin(s) (%sms)",
5846
+ preset,
5847
+ plugins.length,
5848
+ (performance.now() - rangoStart).toFixed(1)
5849
+ );
5546
5850
  return plugins;
5547
5851
  }
5548
5852
 
@@ -5553,7 +5857,7 @@ function poke() {
5553
5857
  apply: "serve",
5554
5858
  configureServer(server) {
5555
5859
  const stdin = process.stdin;
5556
- const debug = process.env.RANGO_POKE_DEBUG === "1";
5860
+ const debug2 = process.env.RANGO_POKE_DEBUG === "1";
5557
5861
  const triggerReload = (source) => {
5558
5862
  server.hot.send({ type: "full-reload", path: "*" });
5559
5863
  server.config.logger.info(` browser reload (${source})`, {
@@ -5586,7 +5890,7 @@ function poke() {
5586
5890
  lines.pop();
5587
5891
  return lines;
5588
5892
  };
5589
- if (debug) {
5893
+ if (debug2) {
5590
5894
  server.config.logger.info(
5591
5895
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
5592
5896
  { timestamp: true }
@@ -5599,7 +5903,7 @@ function poke() {
5599
5903
  );
5600
5904
  }
5601
5905
  const onData = (data) => {
5602
- if (debug) {
5906
+ if (debug2) {
5603
5907
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
5604
5908
  timestamp: true
5605
5909
  });