@rangojs/router 0.0.0-experimental.a769fbe7 → 0.0.0-experimental.ad1a365c
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.
- package/README.md +76 -18
- package/dist/bin/rango.js +130 -47
- package/dist/vite/index.js +703 -372
- package/dist/vite/index.js.bak +5448 -0
- package/package.json +3 -3
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +764 -0
- package/skills/parallel/SKILL.md +59 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/route/SKILL.md +24 -0
- package/skills/router-setup/SKILL.md +35 -0
- package/src/__internal.ts +1 -1
- package/src/browser/app-version.ts +14 -0
- package/src/browser/navigation-bridge.ts +37 -5
- package/src/browser/navigation-client.ts +98 -70
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/partial-update.ts +41 -7
- package/src/browser/prefetch/cache.ts +16 -6
- package/src/browser/prefetch/fetch.ts +68 -6
- package/src/browser/react/Link.tsx +67 -8
- package/src/browser/react/NavigationProvider.tsx +8 -1
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +26 -3
- package/src/browser/scroll-restoration.ts +10 -8
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/server-action-bridge.ts +8 -18
- package/src/browser/types.ts +20 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +50 -24
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +211 -72
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/client.tsx +84 -230
- package/src/deps/browser.ts +0 -1
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +3 -1
- package/src/index.ts +46 -6
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +25 -1
- package/src/route-definition/dsl-helpers.ts +194 -32
- package/src/route-definition/helpers-types.ts +61 -14
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +9 -1
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/handler-context.ts +51 -15
- package/src/router/intercept-resolution.ts +9 -4
- package/src/router/lazy-includes.ts +5 -5
- package/src/router/loader-resolution.ts +150 -21
- package/src/router/manifest.ts +17 -9
- package/src/router/match-api.ts +124 -189
- package/src/router/match-middleware/cache-lookup.ts +28 -8
- package/src/router/match-middleware/segment-resolution.ts +53 -0
- package/src/router/match-result.ts +82 -4
- package/src/router/middleware-types.ts +0 -6
- package/src/router/middleware.ts +0 -3
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +70 -5
- package/src/router/segment-resolution/revalidation.ts +87 -9
- package/src/router.ts +53 -5
- package/src/rsc/handler.ts +472 -398
- package/src/rsc/loader-fetch.ts +18 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +12 -3
- package/src/rsc/rsc-rendering.ts +8 -2
- package/src/rsc/server-action.ts +8 -2
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +6 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +11 -61
- package/src/server/context.ts +65 -5
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +132 -13
- package/src/ssr/index.tsx +3 -0
- package/src/static-handler.ts +18 -6
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +17 -11
- package/src/types/loader-types.ts +32 -5
- package/src/types/route-entry.ts +12 -1
- package/src/types/segments.ts +1 -1
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +47 -12
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +16 -6
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/bundle-postprocess.ts +30 -33
- package/src/vite/discovery/discover-routers.ts +5 -1
- package/src/vite/discovery/prerender-collection.ts +128 -74
- package/src/vite/discovery/state.ts +13 -4
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +60 -5
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +64 -207
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/rango.ts +18 -5
- package/src/vite/router-discovery.ts +178 -37
- package/src/vite/utils/prerender-utils.ts +37 -5
- package/src/vite/utils/shared-utils.ts +3 -2
- package/src/browser/debug-channel.ts +0 -93
package/dist/vite/index.js
CHANGED
|
@@ -9,18 +9,21 @@ import fs from "node:fs";
|
|
|
9
9
|
|
|
10
10
|
// src/vite/plugins/expose-id-utils.ts
|
|
11
11
|
import path from "node:path";
|
|
12
|
-
import
|
|
12
|
+
import crypto from "node:crypto";
|
|
13
13
|
function normalizePath(p) {
|
|
14
14
|
return p.split(path.sep).join("/");
|
|
15
15
|
}
|
|
16
16
|
function hashId(filePath, exportName) {
|
|
17
17
|
const input = `${filePath}#${exportName}`;
|
|
18
|
-
const hash =
|
|
18
|
+
const hash = crypto.createHash("sha256").update(input).digest("hex");
|
|
19
19
|
return `${hash.slice(0, 8)}#${exportName}`;
|
|
20
20
|
}
|
|
21
|
+
function makeStubId(filePath, exportName, isBuild) {
|
|
22
|
+
return isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
|
|
23
|
+
}
|
|
21
24
|
function hashInlineId(filePath, lineNumber, index) {
|
|
22
25
|
const input = index !== void 0 && index > 0 ? `${filePath}:${lineNumber}:${index}` : `${filePath}:${lineNumber}`;
|
|
23
|
-
return
|
|
26
|
+
return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
|
|
24
27
|
}
|
|
25
28
|
function buildExportMap(program) {
|
|
26
29
|
const exportMap = /* @__PURE__ */ new Map();
|
|
@@ -910,9 +913,7 @@ function generateWholeFileStubs(cfg, bindings, code, filePath, isBuild) {
|
|
|
910
913
|
});
|
|
911
914
|
return { code: stubs.join("\n") + "\n", map: null };
|
|
912
915
|
}
|
|
913
|
-
function
|
|
914
|
-
if (bindings.length === 0) return null;
|
|
915
|
-
const s = new MagicString2(code);
|
|
916
|
+
function stubHandlerExprs(cfg, bindings, s, filePath, isBuild) {
|
|
916
917
|
let hasChanges = false;
|
|
917
918
|
for (const binding of bindings) {
|
|
918
919
|
const exportName = binding.exportNames[0];
|
|
@@ -924,15 +925,7 @@ function generateExprStubs(cfg, bindings, code, filePath, sourceId, isBuild) {
|
|
|
924
925
|
);
|
|
925
926
|
hasChanges = true;
|
|
926
927
|
}
|
|
927
|
-
|
|
928
|
-
return {
|
|
929
|
-
code: s.toString(),
|
|
930
|
-
map: s.generateMap({
|
|
931
|
-
source: sourceId,
|
|
932
|
-
includeContent: true,
|
|
933
|
-
hires: "boundary"
|
|
934
|
-
})
|
|
935
|
-
};
|
|
928
|
+
return hasChanges;
|
|
936
929
|
}
|
|
937
930
|
function transformHandlerIds(cfg, bindings, s, filePath, isBuild) {
|
|
938
931
|
let hasChanges = false;
|
|
@@ -1269,15 +1262,6 @@ ${lazyImports.join(",\n")}
|
|
|
1269
1262
|
isBuild
|
|
1270
1263
|
);
|
|
1271
1264
|
if (wholeFile) return wholeFile;
|
|
1272
|
-
const exprStubs = generateExprStubs(
|
|
1273
|
-
PRERENDER_CONFIG,
|
|
1274
|
-
bindings,
|
|
1275
|
-
code,
|
|
1276
|
-
filePath,
|
|
1277
|
-
id,
|
|
1278
|
-
isBuild
|
|
1279
|
-
);
|
|
1280
|
-
if (exprStubs) return exprStubs;
|
|
1281
1265
|
}
|
|
1282
1266
|
if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
|
|
1283
1267
|
const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
|
|
@@ -1329,15 +1313,134 @@ ${lazyImports.join(",\n")}
|
|
|
1329
1313
|
isBuild
|
|
1330
1314
|
);
|
|
1331
1315
|
if (wholeFile) return wholeFile;
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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));
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
let canStubWholeFile = allBindings.length > 0 && isExportOnlyFile(code, allBindings);
|
|
1336
|
+
if (canStubWholeFile && (handleFnNames.length > 0 || lsFnNames.length > 0)) {
|
|
1337
|
+
const exportedLocals = new Set(allBindings.map((b) => b.localName));
|
|
1338
|
+
const strippedBindings = [];
|
|
1339
|
+
const localDeclPattern = /(?:^|;|\n)\s*(?:const|let|var|function)\s+(\w+)/g;
|
|
1340
|
+
let declMatch;
|
|
1341
|
+
while ((declMatch = localDeclPattern.exec(code)) !== null) {
|
|
1342
|
+
const name = declMatch[1];
|
|
1343
|
+
if (!exportedLocals.has(name) && !/^_c\d*$/.test(name)) {
|
|
1344
|
+
strippedBindings.push(name);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const importPattern = /import\s*\{([^}]*)\}\s*from\s*["'](?!@rangojs\/router)[^"']*["']/g;
|
|
1348
|
+
let importMatch;
|
|
1349
|
+
while ((importMatch = importPattern.exec(code)) !== null) {
|
|
1350
|
+
for (const spec of importMatch[1].split(",")) {
|
|
1351
|
+
const m = spec.trim().match(/^[A-Za-z_$][\w$]*(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
|
|
1352
|
+
if (m) strippedBindings.push(m[1] || m[0].trim().split(/\s/)[0]);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const defaultImportPattern = /import\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
|
|
1356
|
+
while ((importMatch = defaultImportPattern.exec(code)) !== null) {
|
|
1357
|
+
strippedBindings.push(importMatch[1]);
|
|
1358
|
+
}
|
|
1359
|
+
const nsImportPattern = /import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\s*["'](?!@rangojs\/router)[^"']*["']/g;
|
|
1360
|
+
while ((importMatch = nsImportPattern.exec(code)) !== null) {
|
|
1361
|
+
strippedBindings.push(importMatch[1]);
|
|
1362
|
+
}
|
|
1363
|
+
if (strippedBindings.length > 0) {
|
|
1364
|
+
const preservedBindings = allBindings.filter((b) => {
|
|
1365
|
+
const fc = code.slice(b.callExprStart, b.callOpenParenPos + 1);
|
|
1366
|
+
return handleFnNames.some((n) => fc.includes(n)) || lsFnNames.some((n) => fc.includes(n));
|
|
1367
|
+
});
|
|
1368
|
+
const strippedRe = new RegExp(
|
|
1369
|
+
`\\b(?:${strippedBindings.join("|")})\\b`
|
|
1370
|
+
);
|
|
1371
|
+
canStubWholeFile = !preservedBindings.some((b) => {
|
|
1372
|
+
const expr = code.slice(b.callExprStart, b.callCloseParenPos + 1);
|
|
1373
|
+
return strippedRe.test(expr);
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (canStubWholeFile) {
|
|
1378
|
+
const lines = [];
|
|
1379
|
+
const neededImports = [];
|
|
1380
|
+
if (handleFnNames.length > 0) neededImports.push("createHandle");
|
|
1381
|
+
if (lsFnNames.length > 0) neededImports.push("createLocationState");
|
|
1382
|
+
if (neededImports.length > 0) {
|
|
1383
|
+
lines.push(
|
|
1384
|
+
`import { ${neededImports.join(", ")} } from "@rangojs/router";`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
for (const binding of allBindings) {
|
|
1388
|
+
const fnCall = code.slice(
|
|
1389
|
+
binding.callExprStart,
|
|
1390
|
+
binding.callOpenParenPos + 1
|
|
1391
|
+
);
|
|
1392
|
+
const isHandle = handleFnNames.some((n) => fnCall.includes(n));
|
|
1393
|
+
const isLocationState = lsFnNames.some((n) => fnCall.includes(n));
|
|
1394
|
+
const primaryName = binding.exportNames[0];
|
|
1395
|
+
const stubId = makeStubId(filePath, primaryName, isBuild);
|
|
1396
|
+
if (isHandle || isLocationState) {
|
|
1397
|
+
const rawArgs = code.slice(binding.callOpenParenPos + 1, binding.callCloseParenPos).replace(/\b_c\d*\s*=\s*/g, "");
|
|
1398
|
+
const canonicalName = isHandle ? "createHandle" : "createLocationState";
|
|
1399
|
+
const activeFnNames = isHandle ? handleFnNames : lsFnNames;
|
|
1400
|
+
let rawCallee = code.slice(
|
|
1401
|
+
binding.callExprStart,
|
|
1402
|
+
binding.callOpenParenPos
|
|
1403
|
+
);
|
|
1404
|
+
for (const alias of activeFnNames) {
|
|
1405
|
+
if (alias !== canonicalName && rawCallee.startsWith(alias)) {
|
|
1406
|
+
rawCallee = canonicalName + rawCallee.slice(alias.length);
|
|
1407
|
+
break;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (isHandle) {
|
|
1411
|
+
const idParam = binding.argCount === 0 ? `undefined, "${stubId}"` : `, "${stubId}"`;
|
|
1412
|
+
lines.push(
|
|
1413
|
+
`export const ${primaryName} = ${rawCallee}(${rawArgs}${idParam});`
|
|
1414
|
+
);
|
|
1415
|
+
lines.push(`${primaryName}.$$id = "${stubId}";`);
|
|
1416
|
+
} else {
|
|
1417
|
+
lines.push(
|
|
1418
|
+
`export const ${primaryName} = ${rawCallee}(${rawArgs});`
|
|
1419
|
+
);
|
|
1420
|
+
lines.push(
|
|
1421
|
+
`${primaryName}.__rsc_ls_key = "__rsc_ls_${stubId}";`
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
for (const name of binding.exportNames.slice(1)) {
|
|
1425
|
+
lines.push(`export const ${name} = ${primaryName};`);
|
|
1426
|
+
}
|
|
1427
|
+
} else {
|
|
1428
|
+
let brand = "loader";
|
|
1429
|
+
if (prerenderFnNames.some((n) => fnCall.includes(n))) {
|
|
1430
|
+
brand = PRERENDER_CONFIG.brand;
|
|
1431
|
+
} else if (staticFnNames.some((n) => fnCall.includes(n))) {
|
|
1432
|
+
brand = STATIC_CONFIG.brand;
|
|
1433
|
+
}
|
|
1434
|
+
lines.push(
|
|
1435
|
+
`export const ${primaryName} = { __brand: "${brand}", $$id: "${stubId}" };`
|
|
1436
|
+
);
|
|
1437
|
+
for (const name of binding.exportNames.slice(1)) {
|
|
1438
|
+
lines.push(`export const ${name} = ${primaryName};`);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { code: lines.join("\n") + "\n", map: null };
|
|
1443
|
+
}
|
|
1341
1444
|
}
|
|
1342
1445
|
if (hasStaticHandlerCode && isRscEnv && isBuild) {
|
|
1343
1446
|
const fnNames = getFnNames(STATIC_CONFIG.fnName);
|
|
@@ -1372,25 +1475,41 @@ ${lazyImports.join(",\n")}
|
|
|
1372
1475
|
isBuild
|
|
1373
1476
|
) || changed;
|
|
1374
1477
|
}
|
|
1375
|
-
if (hasPrerenderHandlerCode
|
|
1478
|
+
if (hasPrerenderHandlerCode) {
|
|
1376
1479
|
const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1480
|
+
const bindings = getBindings(code, fnNames);
|
|
1481
|
+
if (isRscEnv) {
|
|
1482
|
+
changed = transformHandlerIds(
|
|
1483
|
+
PRERENDER_CONFIG,
|
|
1484
|
+
bindings,
|
|
1485
|
+
s,
|
|
1486
|
+
filePath,
|
|
1487
|
+
isBuild
|
|
1488
|
+
) || changed;
|
|
1489
|
+
} else {
|
|
1490
|
+
changed = stubHandlerExprs(
|
|
1491
|
+
PRERENDER_CONFIG,
|
|
1492
|
+
bindings,
|
|
1493
|
+
s,
|
|
1494
|
+
filePath,
|
|
1495
|
+
isBuild
|
|
1496
|
+
) || changed;
|
|
1497
|
+
}
|
|
1384
1498
|
}
|
|
1385
|
-
if (hasStaticHandlerCode
|
|
1499
|
+
if (hasStaticHandlerCode) {
|
|
1386
1500
|
const fnNames = getFnNames(STATIC_CONFIG.fnName);
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1501
|
+
const bindings = getBindings(code, fnNames);
|
|
1502
|
+
if (isRscEnv) {
|
|
1503
|
+
changed = transformHandlerIds(
|
|
1504
|
+
STATIC_CONFIG,
|
|
1505
|
+
bindings,
|
|
1506
|
+
s,
|
|
1507
|
+
filePath,
|
|
1508
|
+
isBuild
|
|
1509
|
+
) || changed;
|
|
1510
|
+
} else {
|
|
1511
|
+
changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
|
|
1512
|
+
}
|
|
1394
1513
|
}
|
|
1395
1514
|
if (!changed) return;
|
|
1396
1515
|
return {
|
|
@@ -1745,7 +1864,7 @@ import { resolve } from "node:path";
|
|
|
1745
1864
|
// package.json
|
|
1746
1865
|
var package_default = {
|
|
1747
1866
|
name: "@rangojs/router",
|
|
1748
|
-
version: "0.0.0-experimental.
|
|
1867
|
+
version: "0.0.0-experimental.ad1a365c",
|
|
1749
1868
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1750
1869
|
keywords: [
|
|
1751
1870
|
"react",
|
|
@@ -1887,7 +2006,7 @@ var package_default = {
|
|
|
1887
2006
|
"test:unit:watch": "vitest"
|
|
1888
2007
|
},
|
|
1889
2008
|
dependencies: {
|
|
1890
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
2009
|
+
"@vitejs/plugin-rsc": "^0.5.23",
|
|
1891
2010
|
"magic-string": "^0.30.17",
|
|
1892
2011
|
picomatch: "^4.0.3",
|
|
1893
2012
|
"rsc-html-stream": "^0.0.7"
|
|
@@ -1907,7 +2026,7 @@ var package_default = {
|
|
|
1907
2026
|
},
|
|
1908
2027
|
peerDependencies: {
|
|
1909
2028
|
"@cloudflare/vite-plugin": "^1.25.0",
|
|
1910
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
2029
|
+
"@vitejs/plugin-rsc": "^0.5.23",
|
|
1911
2030
|
react: "^18.0.0 || ^19.0.0",
|
|
1912
2031
|
vite: "^7.3.0"
|
|
1913
2032
|
},
|
|
@@ -2317,7 +2436,7 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2317
2436
|
}
|
|
2318
2437
|
return routeMap;
|
|
2319
2438
|
}
|
|
2320
|
-
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut) {
|
|
2439
|
+
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock) {
|
|
2321
2440
|
visited = visited ?? /* @__PURE__ */ new Set();
|
|
2322
2441
|
const realPath = resolve2(filePath);
|
|
2323
2442
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
@@ -2333,7 +2452,9 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2333
2452
|
return { routes: {}, searchSchemas: {} };
|
|
2334
2453
|
}
|
|
2335
2454
|
let block;
|
|
2336
|
-
if (
|
|
2455
|
+
if (inlineBlock) {
|
|
2456
|
+
block = inlineBlock;
|
|
2457
|
+
} else if (variableName) {
|
|
2337
2458
|
const extracted = extractUrlsBlockForVariable(source, variableName);
|
|
2338
2459
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
2339
2460
|
block = extracted;
|
|
@@ -2452,7 +2573,7 @@ Router root: ${conflict.ancestor}
|
|
|
2452
2573
|
Nested router: ${conflict.nested}
|
|
2453
2574
|
Move the nested router into a sibling directory or configure it as a separate app root.`;
|
|
2454
2575
|
}
|
|
2455
|
-
function
|
|
2576
|
+
function extractUrlsFromRouter(code) {
|
|
2456
2577
|
const sourceFile = ts5.createSourceFile(
|
|
2457
2578
|
"router.tsx",
|
|
2458
2579
|
code,
|
|
@@ -2466,24 +2587,70 @@ function extractUrlsVariableFromRouter(code) {
|
|
|
2466
2587
|
const callee = node.expression;
|
|
2467
2588
|
return ts5.isIdentifier(callee) && callee.text === "createRouter";
|
|
2468
2589
|
}
|
|
2590
|
+
function isInlineBuilder(node) {
|
|
2591
|
+
return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
|
|
2592
|
+
}
|
|
2593
|
+
function isRoutesOnCreateRouter(node) {
|
|
2594
|
+
if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
|
|
2595
|
+
return false;
|
|
2596
|
+
let inner = node.expression.expression;
|
|
2597
|
+
while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
|
|
2598
|
+
inner = inner.expression.expression;
|
|
2599
|
+
}
|
|
2600
|
+
return isCreateRouterCall(inner);
|
|
2601
|
+
}
|
|
2469
2602
|
function visit(node) {
|
|
2470
2603
|
if (result) return;
|
|
2471
|
-
if (ts5.isCallExpression(node) &&
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
result = node.arguments[0].text;
|
|
2478
|
-
return;
|
|
2604
|
+
if (ts5.isCallExpression(node) && node.arguments.length >= 1 && isRoutesOnCreateRouter(node)) {
|
|
2605
|
+
const arg = node.arguments[0];
|
|
2606
|
+
if (ts5.isIdentifier(arg)) {
|
|
2607
|
+
result = { kind: "variable", name: arg.text };
|
|
2608
|
+
} else if (isInlineBuilder(arg)) {
|
|
2609
|
+
result = { kind: "inline", block: arg.getText(sourceFile) };
|
|
2479
2610
|
}
|
|
2611
|
+
return;
|
|
2480
2612
|
}
|
|
2481
2613
|
if (isCreateRouterCall(node)) {
|
|
2482
2614
|
const callExpr = node;
|
|
2483
|
-
for (const
|
|
2615
|
+
for (const callArg of callExpr.arguments) {
|
|
2616
|
+
if (ts5.isObjectLiteralExpression(callArg)) {
|
|
2617
|
+
for (const prop of callArg.properties) {
|
|
2618
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls") {
|
|
2619
|
+
if (ts5.isIdentifier(prop.initializer)) {
|
|
2620
|
+
result = { kind: "variable", name: prop.initializer.text };
|
|
2621
|
+
} else if (isInlineBuilder(prop.initializer)) {
|
|
2622
|
+
result = {
|
|
2623
|
+
kind: "inline",
|
|
2624
|
+
block: prop.initializer.getText(sourceFile)
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
ts5.forEachChild(node, visit);
|
|
2634
|
+
}
|
|
2635
|
+
visit(sourceFile);
|
|
2636
|
+
return result;
|
|
2637
|
+
}
|
|
2638
|
+
function extractBasenameFromRouter(code) {
|
|
2639
|
+
const sourceFile = ts5.createSourceFile(
|
|
2640
|
+
"router.tsx",
|
|
2641
|
+
code,
|
|
2642
|
+
ts5.ScriptTarget.Latest,
|
|
2643
|
+
true,
|
|
2644
|
+
ts5.ScriptKind.TSX
|
|
2645
|
+
);
|
|
2646
|
+
let result;
|
|
2647
|
+
function visit(node) {
|
|
2648
|
+
if (result !== void 0) return;
|
|
2649
|
+
if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "createRouter") {
|
|
2650
|
+
for (const arg of node.arguments) {
|
|
2484
2651
|
if (ts5.isObjectLiteralExpression(arg)) {
|
|
2485
2652
|
for (const prop of arg.properties) {
|
|
2486
|
-
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "
|
|
2653
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "basename" && ts5.isStringLiteral(prop.initializer)) {
|
|
2487
2654
|
result = prop.initializer.text;
|
|
2488
2655
|
return;
|
|
2489
2656
|
}
|
|
@@ -2496,6 +2663,19 @@ function extractUrlsVariableFromRouter(code) {
|
|
|
2496
2663
|
visit(sourceFile);
|
|
2497
2664
|
return result;
|
|
2498
2665
|
}
|
|
2666
|
+
function applyBasenameToRoutes(result, basename3) {
|
|
2667
|
+
const prefixed = {};
|
|
2668
|
+
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
2669
|
+
if (pattern === "/") {
|
|
2670
|
+
prefixed[name] = basename3;
|
|
2671
|
+
} else if (basename3.endsWith("/") && pattern.startsWith("/")) {
|
|
2672
|
+
prefixed[name] = basename3 + pattern.slice(1);
|
|
2673
|
+
} else {
|
|
2674
|
+
prefixed[name] = basename3 + pattern;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
2678
|
+
}
|
|
2499
2679
|
function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
2500
2680
|
let routerSource;
|
|
2501
2681
|
try {
|
|
@@ -2503,19 +2683,40 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
2503
2683
|
} catch {
|
|
2504
2684
|
return { routes: {}, searchSchemas: {} };
|
|
2505
2685
|
}
|
|
2506
|
-
const
|
|
2507
|
-
if (!
|
|
2686
|
+
const extraction = extractUrlsFromRouter(routerSource);
|
|
2687
|
+
if (!extraction) {
|
|
2508
2688
|
return { routes: {}, searchSchemas: {} };
|
|
2509
2689
|
}
|
|
2510
|
-
const
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2690
|
+
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
2691
|
+
const basename3 = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
|
|
2692
|
+
let result;
|
|
2693
|
+
if (extraction.kind === "inline") {
|
|
2694
|
+
result = buildCombinedRouteMapWithSearch(
|
|
2695
|
+
routerFilePath,
|
|
2696
|
+
void 0,
|
|
2697
|
+
void 0,
|
|
2698
|
+
void 0,
|
|
2699
|
+
extraction.block
|
|
2700
|
+
);
|
|
2701
|
+
} else {
|
|
2702
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
2703
|
+
if (imported) {
|
|
2704
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
2705
|
+
if (!targetFile) {
|
|
2706
|
+
return { routes: {}, searchSchemas: {} };
|
|
2707
|
+
}
|
|
2708
|
+
result = buildCombinedRouteMapWithSearch(
|
|
2709
|
+
targetFile,
|
|
2710
|
+
imported.exportedName
|
|
2711
|
+
);
|
|
2712
|
+
} else {
|
|
2713
|
+
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
2515
2714
|
}
|
|
2516
|
-
return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
|
|
2517
2715
|
}
|
|
2518
|
-
|
|
2716
|
+
if (basename3) {
|
|
2717
|
+
result = applyBasenameToRoutes(result, basename3);
|
|
2718
|
+
}
|
|
2719
|
+
return result;
|
|
2519
2720
|
}
|
|
2520
2721
|
function findRouterFiles(root, filter) {
|
|
2521
2722
|
const result = [];
|
|
@@ -2540,25 +2741,15 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
2540
2741
|
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
2541
2742
|
}
|
|
2542
2743
|
for (const routerFilePath of routerFilePaths) {
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
routerSource
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
const imported = resolveImportedVariable(routerSource, urlsVarName);
|
|
2553
|
-
if (imported) {
|
|
2554
|
-
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
2555
|
-
if (!targetFile) continue;
|
|
2556
|
-
result = buildCombinedRouteMapWithSearch(
|
|
2557
|
-
targetFile,
|
|
2558
|
-
imported.exportedName
|
|
2559
|
-
);
|
|
2560
|
-
} else {
|
|
2561
|
-
result = buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
|
|
2744
|
+
const result = buildCombinedRouteMapForRouterFile(routerFilePath);
|
|
2745
|
+
if (Object.keys(result.routes).length === 0 && Object.keys(result.searchSchemas).length === 0) {
|
|
2746
|
+
let routerSource;
|
|
2747
|
+
try {
|
|
2748
|
+
routerSource = readFileSync2(routerFilePath, "utf-8");
|
|
2749
|
+
} catch {
|
|
2750
|
+
continue;
|
|
2751
|
+
}
|
|
2752
|
+
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
2562
2753
|
}
|
|
2563
2754
|
const routerBasename = pathBasename(routerFilePath).replace(
|
|
2564
2755
|
/\.(tsx?|jsx?)$/,
|
|
@@ -2784,6 +2975,68 @@ function createVersionPlugin() {
|
|
|
2784
2975
|
|
|
2785
2976
|
// src/vite/utils/shared-utils.ts
|
|
2786
2977
|
import * as Vite from "vite";
|
|
2978
|
+
|
|
2979
|
+
// src/vite/plugins/performance-tracks.ts
|
|
2980
|
+
import { readFile } from "node:fs/promises";
|
|
2981
|
+
var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
|
|
2982
|
+
function buildPatchReplacement(match, debugInfoVar) {
|
|
2983
|
+
return `${match}
|
|
2984
|
+
if (${debugInfoVar} && 0 === ${debugInfoVar}.length && "fulfilled" === root.status) {
|
|
2985
|
+
var _resolved = "function" === typeof resolveLazy ? resolveLazy(root.value) : root.value;
|
|
2986
|
+
if ("object" === typeof _resolved && null !== _resolved && isArrayImpl(_resolved._debugInfo)) {
|
|
2987
|
+
${debugInfoVar} = _resolved._debugInfo;
|
|
2988
|
+
}
|
|
2989
|
+
}`;
|
|
2990
|
+
}
|
|
2991
|
+
function patchRsdwClientDebugInfoRecovery(code) {
|
|
2992
|
+
const match = code.match(RSDW_PATCH_RE);
|
|
2993
|
+
if (!match) {
|
|
2994
|
+
return { code, debugInfoVar: null };
|
|
2995
|
+
}
|
|
2996
|
+
return {
|
|
2997
|
+
code: code.replace(match[1], buildPatchReplacement(match[1], match[2])),
|
|
2998
|
+
debugInfoVar: match[2]
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
function performanceTracksOptimizeDepsPlugin() {
|
|
3002
|
+
return {
|
|
3003
|
+
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
3004
|
+
setup(build) {
|
|
3005
|
+
build.onLoad(
|
|
3006
|
+
{
|
|
3007
|
+
filter: /react-server-dom-webpack-client\.browser\.(development|production)\.js$/
|
|
3008
|
+
},
|
|
3009
|
+
async (args) => {
|
|
3010
|
+
const code = await readFile(args.path, "utf8");
|
|
3011
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
3012
|
+
return {
|
|
3013
|
+
contents: patched.code,
|
|
3014
|
+
loader: "js"
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
};
|
|
3020
|
+
}
|
|
3021
|
+
function performanceTracksPlugin() {
|
|
3022
|
+
return {
|
|
3023
|
+
name: "@rangojs/router:performance-tracks",
|
|
3024
|
+
transform(code, id) {
|
|
3025
|
+
if (!id.includes("react-server-dom") || !id.includes("client")) return;
|
|
3026
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
3027
|
+
if (!patched.debugInfoVar) return;
|
|
3028
|
+
if (process.env.INTERNAL_RANGO_DEBUG)
|
|
3029
|
+
console.log(
|
|
3030
|
+
"[perf-tracks] patched RSDW client (var:",
|
|
3031
|
+
patched.debugInfoVar,
|
|
3032
|
+
")"
|
|
3033
|
+
);
|
|
3034
|
+
return patched.code;
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
// src/vite/utils/shared-utils.ts
|
|
2787
3040
|
var versionEsbuildPlugin = {
|
|
2788
3041
|
name: "@rangojs/router-version",
|
|
2789
3042
|
setup(build) {
|
|
@@ -2801,7 +3054,7 @@ var versionEsbuildPlugin = {
|
|
|
2801
3054
|
}
|
|
2802
3055
|
};
|
|
2803
3056
|
var sharedEsbuildOptions = {
|
|
2804
|
-
plugins: [versionEsbuildPlugin]
|
|
3057
|
+
plugins: [versionEsbuildPlugin, performanceTracksOptimizeDepsPlugin()]
|
|
2805
3058
|
};
|
|
2806
3059
|
function createVirtualEntriesPlugin(entries, routerPathRef) {
|
|
2807
3060
|
const virtualModules = {};
|
|
@@ -3007,6 +3260,8 @@ function createCjsToEsmPlugin() {
|
|
|
3007
3260
|
import { createServer as createViteServer } from "vite";
|
|
3008
3261
|
import { resolve as resolve8 } from "node:path";
|
|
3009
3262
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3263
|
+
import { createRequire } from "node:module";
|
|
3264
|
+
import { pathToFileURL } from "node:url";
|
|
3010
3265
|
|
|
3011
3266
|
// src/vite/plugins/virtual-stub-plugin.ts
|
|
3012
3267
|
function createVirtualStubPlugin() {
|
|
@@ -3190,8 +3445,8 @@ function createDiscoveryState(entryPath, opts) {
|
|
|
3190
3445
|
perRouterManifestDataMap: /* @__PURE__ */ new Map(),
|
|
3191
3446
|
prerenderManifestEntries: null,
|
|
3192
3447
|
staticManifestEntries: null,
|
|
3193
|
-
|
|
3194
|
-
|
|
3448
|
+
handlerChunkInfoMap: /* @__PURE__ */ new Map(),
|
|
3449
|
+
staticHandlerChunkInfoMap: /* @__PURE__ */ new Map(),
|
|
3195
3450
|
rscEntryFileName: null,
|
|
3196
3451
|
resolvedPrerenderModules: void 0,
|
|
3197
3452
|
resolvedStaticModules: void 0,
|
|
@@ -3322,13 +3577,31 @@ function encodePathParam(value) {
|
|
|
3322
3577
|
}
|
|
3323
3578
|
function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
3324
3579
|
let result = pattern;
|
|
3580
|
+
let hadOmittedOptional = false;
|
|
3325
3581
|
for (const [key, value] of Object.entries(params)) {
|
|
3326
3582
|
const escaped = escapeRegExp2(key);
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3583
|
+
if (value === "") {
|
|
3584
|
+
result = result.replace(
|
|
3585
|
+
new RegExp(`:${escaped}(\\([^)]*\\))?(?!\\?)`),
|
|
3586
|
+
""
|
|
3587
|
+
);
|
|
3588
|
+
result = result.replace(`*${key}`, "");
|
|
3589
|
+
} else {
|
|
3590
|
+
result = result.replace(
|
|
3591
|
+
new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
|
|
3592
|
+
encode(value)
|
|
3593
|
+
);
|
|
3594
|
+
result = result.replace(`*${key}`, encode(value));
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
|
|
3598
|
+
hadOmittedOptional = true;
|
|
3599
|
+
return "";
|
|
3600
|
+
});
|
|
3601
|
+
if (hadOmittedOptional) {
|
|
3602
|
+
const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
|
|
3603
|
+
result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
|
|
3604
|
+
if (hadTrailingSlash && !result.endsWith("/")) result += "/";
|
|
3332
3605
|
}
|
|
3333
3606
|
return result;
|
|
3334
3607
|
}
|
|
@@ -3438,84 +3711,126 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3438
3711
|
if (!params) return pattern;
|
|
3439
3712
|
return substituteRouteParams(pattern, params);
|
|
3440
3713
|
};
|
|
3714
|
+
let resolvedRoutes = 0;
|
|
3715
|
+
let totalDynamic = 0;
|
|
3441
3716
|
for (const { manifest } of allManifests) {
|
|
3442
3717
|
if (!manifest.prerenderRoutes) continue;
|
|
3443
|
-
const defs = manifest._prerenderDefs || {};
|
|
3444
3718
|
for (const routeName of manifest.prerenderRoutes) {
|
|
3445
3719
|
const pattern = manifest.routeManifest[routeName];
|
|
3446
|
-
if (
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3720
|
+
if (pattern && (pattern.includes(":") || pattern.includes("*"))) {
|
|
3721
|
+
totalDynamic++;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
const paramsStart = performance.now();
|
|
3726
|
+
const progressInterval = totalDynamic > 0 ? setInterval(() => {
|
|
3727
|
+
const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
|
|
3728
|
+
console.log(
|
|
3729
|
+
`[rsc-router] Resolving prerender params... ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
|
|
3730
|
+
);
|
|
3731
|
+
}, 5e3) : void 0;
|
|
3732
|
+
try {
|
|
3733
|
+
for (const { manifest } of allManifests) {
|
|
3734
|
+
if (!manifest.prerenderRoutes) continue;
|
|
3735
|
+
const defs = manifest._prerenderDefs || {};
|
|
3736
|
+
const passthroughSet = new Set(manifest.passthroughRoutes || []);
|
|
3737
|
+
for (const routeName of manifest.prerenderRoutes) {
|
|
3738
|
+
const pattern = manifest.routeManifest[routeName];
|
|
3739
|
+
if (!pattern) continue;
|
|
3740
|
+
const def = defs[routeName];
|
|
3741
|
+
const isPassthroughRoute = passthroughSet.has(routeName);
|
|
3742
|
+
const hasDynamic = pattern.includes(":") || pattern.includes("*");
|
|
3743
|
+
if (!hasDynamic) {
|
|
3744
|
+
entries.push({
|
|
3745
|
+
urlPath: pattern.replace(/\/$/, "") || "/",
|
|
3746
|
+
routeName,
|
|
3747
|
+
concurrency: 1,
|
|
3748
|
+
isPassthroughRoute
|
|
3749
|
+
});
|
|
3750
|
+
} else {
|
|
3751
|
+
if (def?.getParams) {
|
|
3752
|
+
try {
|
|
3753
|
+
const buildVars = {};
|
|
3754
|
+
const buildEnv = state.resolvedBuildEnv;
|
|
3755
|
+
const getParamsCtx = {
|
|
3756
|
+
build: true,
|
|
3757
|
+
dev: !state.isBuildMode,
|
|
3758
|
+
set: ((keyOrVar, value) => {
|
|
3759
|
+
contextSet(buildVars, keyOrVar, value);
|
|
3760
|
+
}),
|
|
3761
|
+
reverse: getParamsReverse,
|
|
3762
|
+
get env() {
|
|
3763
|
+
if (buildEnv !== void 0) return buildEnv;
|
|
3764
|
+
throw new Error(
|
|
3765
|
+
"[rsc-router] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
|
|
3766
|
+
);
|
|
3481
3767
|
}
|
|
3768
|
+
};
|
|
3769
|
+
const paramsList = await def.getParams(getParamsCtx);
|
|
3770
|
+
const concurrency = def.options?.concurrency ?? 1;
|
|
3771
|
+
const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
|
|
3772
|
+
for (const params of paramsList) {
|
|
3773
|
+
let url = substituteRouteParams(
|
|
3774
|
+
pattern,
|
|
3775
|
+
params,
|
|
3776
|
+
encodePathParam
|
|
3777
|
+
);
|
|
3778
|
+
if (url.includes("*")) {
|
|
3779
|
+
const wildcardValue = params["*"] ?? params.splat;
|
|
3780
|
+
if (wildcardValue !== void 0) {
|
|
3781
|
+
url = url.replace(
|
|
3782
|
+
/\*[^/]*$/,
|
|
3783
|
+
encodePathParam(wildcardValue)
|
|
3784
|
+
);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
entries.push({
|
|
3788
|
+
urlPath: url.replace(/\/$/, "") || "/",
|
|
3789
|
+
routeName,
|
|
3790
|
+
concurrency,
|
|
3791
|
+
...hasBuildVars ? { buildVars } : {},
|
|
3792
|
+
isPassthroughRoute
|
|
3793
|
+
});
|
|
3482
3794
|
}
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
true
|
|
3795
|
+
resolvedRoutes++;
|
|
3796
|
+
} catch (err) {
|
|
3797
|
+
resolvedRoutes++;
|
|
3798
|
+
if (err.name === "Skip") {
|
|
3799
|
+
console.log(
|
|
3800
|
+
`[rsc-router] SKIP route "${routeName}" - ${err.message}`
|
|
3801
|
+
);
|
|
3802
|
+
notifyOnError(
|
|
3803
|
+
registry,
|
|
3804
|
+
err,
|
|
3805
|
+
"prerender",
|
|
3806
|
+
routeName,
|
|
3807
|
+
void 0,
|
|
3808
|
+
true
|
|
3809
|
+
);
|
|
3810
|
+
continue;
|
|
3811
|
+
}
|
|
3812
|
+
console.error(
|
|
3813
|
+
`[rsc-router] Failed to get params for prerender route "${routeName}": ${err.message}`
|
|
3503
3814
|
);
|
|
3504
|
-
|
|
3815
|
+
notifyOnError(registry, err, "prerender", routeName);
|
|
3816
|
+
throw err;
|
|
3505
3817
|
}
|
|
3506
|
-
|
|
3507
|
-
|
|
3818
|
+
} else {
|
|
3819
|
+
console.warn(
|
|
3820
|
+
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
|
|
3508
3821
|
);
|
|
3509
|
-
notifyOnError(registry, err, "prerender", routeName);
|
|
3510
|
-
throw err;
|
|
3511
3822
|
}
|
|
3512
|
-
} else {
|
|
3513
|
-
console.warn(
|
|
3514
|
-
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
|
|
3515
|
-
);
|
|
3516
3823
|
}
|
|
3517
3824
|
}
|
|
3518
3825
|
}
|
|
3826
|
+
} finally {
|
|
3827
|
+
if (progressInterval) {
|
|
3828
|
+
clearInterval(progressInterval);
|
|
3829
|
+
const elapsed = ((performance.now() - paramsStart) / 1e3).toFixed(1);
|
|
3830
|
+
console.log(
|
|
3831
|
+
`[rsc-router] Resolved prerender params: ${resolvedRoutes}/${totalDynamic} routes (${elapsed}s)`
|
|
3832
|
+
);
|
|
3833
|
+
}
|
|
3519
3834
|
}
|
|
3520
3835
|
if (entries.length === 0) return;
|
|
3521
3836
|
const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
|
|
@@ -3542,7 +3857,8 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3542
3857
|
entry.urlPath,
|
|
3543
3858
|
{},
|
|
3544
3859
|
entry.buildVars,
|
|
3545
|
-
entry.isPassthroughRoute
|
|
3860
|
+
entry.isPassthroughRoute,
|
|
3861
|
+
state.resolvedBuildEnv
|
|
3546
3862
|
);
|
|
3547
3863
|
if (!result) continue;
|
|
3548
3864
|
if (result.passthrough) {
|
|
@@ -3666,7 +3982,9 @@ async function renderStaticHandlers(state, rscEnv, registry) {
|
|
|
3666
3982
|
const result = await routerInstance.renderStaticSegment(
|
|
3667
3983
|
def.handler,
|
|
3668
3984
|
def.$$id,
|
|
3669
|
-
def.$$routePrefix
|
|
3985
|
+
def.$$routePrefix,
|
|
3986
|
+
state.resolvedBuildEnv,
|
|
3987
|
+
!state.isBuildMode
|
|
3670
3988
|
);
|
|
3671
3989
|
if (result) {
|
|
3672
3990
|
const hasHandles = Object.keys(result.handles).length > 0;
|
|
@@ -3791,7 +4109,11 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3791
4109
|
if (!router.urlpatterns || !generateManifestFull) {
|
|
3792
4110
|
continue;
|
|
3793
4111
|
}
|
|
3794
|
-
const manifest = generateManifestFull(
|
|
4112
|
+
const manifest = generateManifestFull(
|
|
4113
|
+
router.urlpatterns,
|
|
4114
|
+
routerMountIndex,
|
|
4115
|
+
router.__basename ? { urlPrefix: router.__basename } : void 0
|
|
4116
|
+
);
|
|
3795
4117
|
routerMountIndex++;
|
|
3796
4118
|
allManifests.push({ id, manifest });
|
|
3797
4119
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
@@ -4240,48 +4562,45 @@ function postprocessBundle(state) {
|
|
|
4240
4562
|
);
|
|
4241
4563
|
const evictionTargets = [
|
|
4242
4564
|
{
|
|
4243
|
-
|
|
4565
|
+
infos: state.handlerChunkInfoMap.values(),
|
|
4244
4566
|
fnName: "Prerender",
|
|
4245
4567
|
brand: "prerenderHandler",
|
|
4246
4568
|
label: "handler code from RSC bundle"
|
|
4247
4569
|
},
|
|
4248
4570
|
{
|
|
4249
|
-
|
|
4571
|
+
infos: state.staticHandlerChunkInfoMap.values(),
|
|
4250
4572
|
fnName: "Static",
|
|
4251
4573
|
brand: "staticHandler",
|
|
4252
4574
|
label: "static handler code"
|
|
4253
4575
|
}
|
|
4254
4576
|
];
|
|
4255
4577
|
for (const target of evictionTargets) {
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4578
|
+
for (const info of target.infos) {
|
|
4579
|
+
const chunkPath = resolve7(state.projectRoot, "dist/rsc", info.fileName);
|
|
4580
|
+
try {
|
|
4581
|
+
const code = readFileSync5(chunkPath, "utf-8");
|
|
4582
|
+
const result = evictHandlerCode(
|
|
4583
|
+
code,
|
|
4584
|
+
info.exports,
|
|
4585
|
+
target.fnName,
|
|
4586
|
+
target.brand
|
|
4587
|
+
);
|
|
4588
|
+
if (result) {
|
|
4589
|
+
writeFileSync4(chunkPath, result.code);
|
|
4590
|
+
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
4591
|
+
console.log(
|
|
4592
|
+
`[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`
|
|
4593
|
+
);
|
|
4594
|
+
}
|
|
4595
|
+
} catch (replaceErr) {
|
|
4596
|
+
console.warn(
|
|
4597
|
+
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`
|
|
4275
4598
|
);
|
|
4276
4599
|
}
|
|
4277
|
-
} catch (replaceErr) {
|
|
4278
|
-
console.warn(
|
|
4279
|
-
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`
|
|
4280
|
-
);
|
|
4281
4600
|
}
|
|
4282
4601
|
}
|
|
4283
|
-
state.
|
|
4284
|
-
state.
|
|
4602
|
+
state.handlerChunkInfoMap.clear();
|
|
4603
|
+
state.staticHandlerChunkInfoMap.clear();
|
|
4285
4604
|
if (hasPrerenderData && existsSync6(rscEntryPath)) {
|
|
4286
4605
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4287
4606
|
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
@@ -4324,7 +4643,7 @@ function postprocessBundle(state) {
|
|
|
4324
4643
|
}
|
|
4325
4644
|
if (hasStaticData && existsSync6(rscEntryPath)) {
|
|
4326
4645
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4327
|
-
if (!rscCode.includes("
|
|
4646
|
+
if (!rscCode.includes("__static-manifest.js")) {
|
|
4328
4647
|
try {
|
|
4329
4648
|
const manifestEntries = [];
|
|
4330
4649
|
let totalBytes = copyStagedBuildAssets(
|
|
@@ -4393,8 +4712,67 @@ async function createTempRscServer(state, options = {}) {
|
|
|
4393
4712
|
]
|
|
4394
4713
|
});
|
|
4395
4714
|
}
|
|
4715
|
+
async function resolveBuildEnv(option, factoryCtx) {
|
|
4716
|
+
if (!option) return null;
|
|
4717
|
+
if (option === "auto") {
|
|
4718
|
+
if (factoryCtx.preset !== "cloudflare") {
|
|
4719
|
+
throw new Error(
|
|
4720
|
+
'[rsc-router] buildEnv: "auto" is only supported with preset: "cloudflare". Use a factory function or plain object for other presets.'
|
|
4721
|
+
);
|
|
4722
|
+
}
|
|
4723
|
+
try {
|
|
4724
|
+
const userRequire = createRequire(
|
|
4725
|
+
resolve8(factoryCtx.root, "package.json")
|
|
4726
|
+
);
|
|
4727
|
+
const wranglerPath = userRequire.resolve("wrangler");
|
|
4728
|
+
const { getPlatformProxy } = await import(pathToFileURL(wranglerPath).href);
|
|
4729
|
+
const proxy = await getPlatformProxy();
|
|
4730
|
+
return {
|
|
4731
|
+
env: proxy.env,
|
|
4732
|
+
dispose: proxy.dispose
|
|
4733
|
+
};
|
|
4734
|
+
} catch (err) {
|
|
4735
|
+
throw new Error(
|
|
4736
|
+
`[rsc-router] buildEnv: "auto" requires wrangler to be installed.
|
|
4737
|
+
Install it with: pnpm add -D wrangler
|
|
4738
|
+
${err.message}`
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
if (typeof option === "function") {
|
|
4743
|
+
return await option(factoryCtx);
|
|
4744
|
+
}
|
|
4745
|
+
return { env: option };
|
|
4746
|
+
}
|
|
4747
|
+
async function acquireBuildEnv(s, command, mode) {
|
|
4748
|
+
const option = s.opts?.buildEnv;
|
|
4749
|
+
if (!option) return false;
|
|
4750
|
+
const result = await resolveBuildEnv(option, {
|
|
4751
|
+
root: s.projectRoot,
|
|
4752
|
+
mode,
|
|
4753
|
+
command,
|
|
4754
|
+
preset: s.opts?.preset ?? "node"
|
|
4755
|
+
});
|
|
4756
|
+
if (!result) return false;
|
|
4757
|
+
s.resolvedBuildEnv = result.env;
|
|
4758
|
+
s.buildEnvDispose = result.dispose ?? null;
|
|
4759
|
+
return true;
|
|
4760
|
+
}
|
|
4761
|
+
async function releaseBuildEnv(s) {
|
|
4762
|
+
if (s.buildEnvDispose) {
|
|
4763
|
+
try {
|
|
4764
|
+
await s.buildEnvDispose();
|
|
4765
|
+
} catch (err) {
|
|
4766
|
+
console.warn(`[rsc-router] buildEnv dispose failed: ${err.message}`);
|
|
4767
|
+
}
|
|
4768
|
+
s.buildEnvDispose = null;
|
|
4769
|
+
}
|
|
4770
|
+
s.resolvedBuildEnv = void 0;
|
|
4771
|
+
}
|
|
4396
4772
|
function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
4397
4773
|
const s = createDiscoveryState(entryPath, opts);
|
|
4774
|
+
let viteCommand = "build";
|
|
4775
|
+
let viteMode = "production";
|
|
4398
4776
|
return {
|
|
4399
4777
|
name: "@rangojs/router:discovery",
|
|
4400
4778
|
config() {
|
|
@@ -4403,31 +4781,13 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4403
4781
|
__RANGO_DEBUG__: JSON.stringify(!!process.env.INTERNAL_RANGO_DEBUG)
|
|
4404
4782
|
}
|
|
4405
4783
|
};
|
|
4406
|
-
if (opts?.enableBuildPrerender) {
|
|
4407
|
-
config.environments = {
|
|
4408
|
-
rsc: {
|
|
4409
|
-
build: {
|
|
4410
|
-
rollupOptions: {
|
|
4411
|
-
output: {
|
|
4412
|
-
manualChunks(id) {
|
|
4413
|
-
if (s.resolvedPrerenderModules?.has(id)) {
|
|
4414
|
-
return "__prerender-handlers";
|
|
4415
|
-
}
|
|
4416
|
-
if (s.resolvedStaticModules?.has(id)) {
|
|
4417
|
-
return "__static-handlers";
|
|
4418
|
-
}
|
|
4419
|
-
}
|
|
4420
|
-
}
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
}
|
|
4424
|
-
};
|
|
4425
|
-
}
|
|
4426
4784
|
return config;
|
|
4427
4785
|
},
|
|
4428
4786
|
configResolved(config) {
|
|
4429
4787
|
s.projectRoot = config.root;
|
|
4430
4788
|
s.isBuildMode = config.command === "build";
|
|
4789
|
+
viteCommand = config.command;
|
|
4790
|
+
viteMode = config.mode;
|
|
4431
4791
|
s.userResolveAlias = config.resolve.alias;
|
|
4432
4792
|
if (!s.resolvedEntryPath && opts?.routerPathRef?.path) {
|
|
4433
4793
|
s.resolvedEntryPath = opts.routerPathRef.path;
|
|
@@ -4472,6 +4832,8 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4472
4832
|
});
|
|
4473
4833
|
prerenderTempServer = null;
|
|
4474
4834
|
}
|
|
4835
|
+
releaseBuildEnv(s).catch(() => {
|
|
4836
|
+
});
|
|
4475
4837
|
});
|
|
4476
4838
|
async function getOrCreateTempServer() {
|
|
4477
4839
|
if (prerenderNodeRegistry) {
|
|
@@ -4502,6 +4864,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4502
4864
|
if (!rscEnv?.runner) {
|
|
4503
4865
|
s.devServerOrigin = getDevServerOrigin();
|
|
4504
4866
|
try {
|
|
4867
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4505
4868
|
const tempRscEnv = await getOrCreateTempServer();
|
|
4506
4869
|
if (tempRscEnv) {
|
|
4507
4870
|
await discoverRouters(s, tempRscEnv);
|
|
@@ -4517,6 +4880,7 @@ ${err.stack}`
|
|
|
4517
4880
|
return;
|
|
4518
4881
|
}
|
|
4519
4882
|
try {
|
|
4883
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4520
4884
|
const serverMod = await rscEnv.runner.import(
|
|
4521
4885
|
"@rangojs/router/server"
|
|
4522
4886
|
);
|
|
@@ -4581,7 +4945,26 @@ ${err.stack}`
|
|
|
4581
4945
|
res.end("Missing pathname");
|
|
4582
4946
|
return;
|
|
4583
4947
|
}
|
|
4584
|
-
|
|
4948
|
+
const rscEnv = server.environments?.rsc;
|
|
4949
|
+
let registry = null;
|
|
4950
|
+
if (rscEnv?.runner && s.resolvedEntryPath) {
|
|
4951
|
+
try {
|
|
4952
|
+
await rscEnv.runner.import(s.resolvedEntryPath);
|
|
4953
|
+
const serverMod = await rscEnv.runner.import(
|
|
4954
|
+
"@rangojs/router/server"
|
|
4955
|
+
);
|
|
4956
|
+
registry = serverMod.RouterRegistry ?? null;
|
|
4957
|
+
} catch (err) {
|
|
4958
|
+
console.warn(
|
|
4959
|
+
`[rsc-router] Dev prerender module refresh failed: ${err.message}`
|
|
4960
|
+
);
|
|
4961
|
+
res.statusCode = 500;
|
|
4962
|
+
res.end(`Prerender handler error: ${err.message}`);
|
|
4963
|
+
return;
|
|
4964
|
+
}
|
|
4965
|
+
} else {
|
|
4966
|
+
registry = mainRegistry;
|
|
4967
|
+
}
|
|
4585
4968
|
if (!registry) {
|
|
4586
4969
|
if (!prerenderNodeRegistry) {
|
|
4587
4970
|
await getOrCreateTempServer();
|
|
@@ -4603,7 +4986,10 @@ ${err.stack}`
|
|
|
4603
4986
|
pathname,
|
|
4604
4987
|
{},
|
|
4605
4988
|
void 0,
|
|
4606
|
-
wantPassthrough
|
|
4989
|
+
wantPassthrough,
|
|
4990
|
+
s.resolvedBuildEnv,
|
|
4991
|
+
true
|
|
4992
|
+
// devMode: check getParams for passthrough routes
|
|
4607
4993
|
);
|
|
4608
4994
|
if (!result) continue;
|
|
4609
4995
|
if (result.passthrough) continue;
|
|
@@ -4739,6 +5125,7 @@ ${err.stack}`
|
|
|
4739
5125
|
resetStagedBuildAssets(s.projectRoot);
|
|
4740
5126
|
s.prerenderManifestEntries = null;
|
|
4741
5127
|
s.staticManifestEntries = null;
|
|
5128
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4742
5129
|
let tempServer = null;
|
|
4743
5130
|
globalThis.__rscRouterDiscoveryActive = true;
|
|
4744
5131
|
try {
|
|
@@ -4778,6 +5165,7 @@ ${details}`
|
|
|
4778
5165
|
if (tempServer) {
|
|
4779
5166
|
await tempServer.close();
|
|
4780
5167
|
}
|
|
5168
|
+
await releaseBuildEnv(s);
|
|
4781
5169
|
}
|
|
4782
5170
|
},
|
|
4783
5171
|
// Virtual module: provides the pre-generated route manifest as a JS module
|
|
@@ -4820,20 +5208,30 @@ ${details}`
|
|
|
4820
5208
|
}
|
|
4821
5209
|
if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size)
|
|
4822
5210
|
return;
|
|
5211
|
+
s.handlerChunkInfoMap.clear();
|
|
5212
|
+
s.staticHandlerChunkInfoMap.clear();
|
|
4823
5213
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
4824
5214
|
if (chunk.type !== "chunk") continue;
|
|
4825
|
-
if (
|
|
5215
|
+
if (s.resolvedPrerenderModules?.size) {
|
|
4826
5216
|
const handlers = extractHandlerExportsFromChunk(
|
|
4827
5217
|
chunk.code,
|
|
4828
5218
|
s.resolvedPrerenderModules,
|
|
4829
5219
|
"Prerender",
|
|
4830
|
-
|
|
5220
|
+
false
|
|
4831
5221
|
);
|
|
4832
5222
|
if (handlers.length > 0) {
|
|
4833
|
-
|
|
5223
|
+
const existing = s.handlerChunkInfoMap.get(fileName);
|
|
5224
|
+
if (existing) {
|
|
5225
|
+
existing.exports.push(...handlers);
|
|
5226
|
+
} else {
|
|
5227
|
+
s.handlerChunkInfoMap.set(fileName, {
|
|
5228
|
+
fileName,
|
|
5229
|
+
exports: handlers
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
4834
5232
|
}
|
|
4835
5233
|
}
|
|
4836
|
-
if (
|
|
5234
|
+
if (s.resolvedStaticModules?.size) {
|
|
4837
5235
|
const handlers = extractHandlerExportsFromChunk(
|
|
4838
5236
|
chunk.code,
|
|
4839
5237
|
s.resolvedStaticModules,
|
|
@@ -4841,7 +5239,15 @@ ${details}`
|
|
|
4841
5239
|
false
|
|
4842
5240
|
);
|
|
4843
5241
|
if (handlers.length > 0) {
|
|
4844
|
-
|
|
5242
|
+
const existing = s.staticHandlerChunkInfoMap.get(fileName);
|
|
5243
|
+
if (existing) {
|
|
5244
|
+
existing.exports.push(...handlers);
|
|
5245
|
+
} else {
|
|
5246
|
+
s.staticHandlerChunkInfoMap.set(fileName, {
|
|
5247
|
+
fileName,
|
|
5248
|
+
exports: handlers
|
|
5249
|
+
});
|
|
5250
|
+
}
|
|
4845
5251
|
}
|
|
4846
5252
|
}
|
|
4847
5253
|
}
|
|
@@ -4861,138 +5267,6 @@ ${details}`
|
|
|
4861
5267
|
};
|
|
4862
5268
|
}
|
|
4863
5269
|
|
|
4864
|
-
// src/vite/plugins/performance-tracks.ts
|
|
4865
|
-
var DEBUG_ID_HEADER = "X-RSC-Debug-Id";
|
|
4866
|
-
var DEBUG_S2C_EVENT = "rango:perf-s2c";
|
|
4867
|
-
var DEBUG_C2S_EVENT = "rango:perf-c2s";
|
|
4868
|
-
var GLOBAL_KEY = "__RANGO_DEBUG_CHANNELS__";
|
|
4869
|
-
function getRegistry() {
|
|
4870
|
-
return globalThis[GLOBAL_KEY] ??= {
|
|
4871
|
-
channels: /* @__PURE__ */ new Map(),
|
|
4872
|
-
sessions: /* @__PURE__ */ new Map()
|
|
4873
|
-
};
|
|
4874
|
-
}
|
|
4875
|
-
var bytesToBase64 = (bytes) => Buffer.from(bytes).toString("base64");
|
|
4876
|
-
var base64ToBytes = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
|
|
4877
|
-
function performanceTracksPlugin() {
|
|
4878
|
-
return {
|
|
4879
|
-
name: "@rangojs/router:performance-tracks",
|
|
4880
|
-
apply: "serve",
|
|
4881
|
-
enforce: "pre",
|
|
4882
|
-
configureServer(server) {
|
|
4883
|
-
const hot = server.environments.client.hot;
|
|
4884
|
-
const registry = getRegistry();
|
|
4885
|
-
const sessions = registry.sessions;
|
|
4886
|
-
const sendChunk = (debugId, chunk) => {
|
|
4887
|
-
hot.send(DEBUG_S2C_EVENT, {
|
|
4888
|
-
i: debugId,
|
|
4889
|
-
b: bytesToBase64(chunk)
|
|
4890
|
-
});
|
|
4891
|
-
};
|
|
4892
|
-
const cleanupIfEnded = (debugId, session) => {
|
|
4893
|
-
if (session.pendingChunks || !session.ended) return;
|
|
4894
|
-
sessions.delete(debugId);
|
|
4895
|
-
hot.send(DEBUG_S2C_EVENT, {
|
|
4896
|
-
i: debugId,
|
|
4897
|
-
d: true
|
|
4898
|
-
});
|
|
4899
|
-
};
|
|
4900
|
-
const registerDebugChannel = (debugId) => {
|
|
4901
|
-
let session = sessions.get(debugId);
|
|
4902
|
-
if (!session) {
|
|
4903
|
-
session = { pendingChunks: [], ended: false };
|
|
4904
|
-
sessions.set(debugId, session);
|
|
4905
|
-
}
|
|
4906
|
-
const readable = new ReadableStream({
|
|
4907
|
-
start(controller) {
|
|
4908
|
-
session.cmdController = controller;
|
|
4909
|
-
},
|
|
4910
|
-
cancel() {
|
|
4911
|
-
delete session.cmdController;
|
|
4912
|
-
}
|
|
4913
|
-
});
|
|
4914
|
-
const writable = new WritableStream({
|
|
4915
|
-
write(chunk) {
|
|
4916
|
-
if (session.pendingChunks) {
|
|
4917
|
-
session.pendingChunks.push(chunk);
|
|
4918
|
-
} else {
|
|
4919
|
-
sendChunk(debugId, chunk);
|
|
4920
|
-
}
|
|
4921
|
-
},
|
|
4922
|
-
close() {
|
|
4923
|
-
session.ended = true;
|
|
4924
|
-
cleanupIfEnded(debugId, session);
|
|
4925
|
-
},
|
|
4926
|
-
abort() {
|
|
4927
|
-
session.ended = true;
|
|
4928
|
-
cleanupIfEnded(debugId, session);
|
|
4929
|
-
}
|
|
4930
|
-
});
|
|
4931
|
-
registry.channels.set(debugId, { readable, writable });
|
|
4932
|
-
};
|
|
4933
|
-
hot.on(DEBUG_C2S_EVENT, (raw) => {
|
|
4934
|
-
const payload = raw;
|
|
4935
|
-
const session = sessions.get(payload.i);
|
|
4936
|
-
if (payload.d) {
|
|
4937
|
-
if (session?.cmdController) {
|
|
4938
|
-
try {
|
|
4939
|
-
session.cmdController.close();
|
|
4940
|
-
} catch {
|
|
4941
|
-
}
|
|
4942
|
-
delete session.cmdController;
|
|
4943
|
-
}
|
|
4944
|
-
return;
|
|
4945
|
-
}
|
|
4946
|
-
if (payload.b) {
|
|
4947
|
-
if (session?.cmdController) {
|
|
4948
|
-
try {
|
|
4949
|
-
session.cmdController.enqueue(base64ToBytes(payload.b));
|
|
4950
|
-
} catch {
|
|
4951
|
-
delete session.cmdController;
|
|
4952
|
-
}
|
|
4953
|
-
}
|
|
4954
|
-
return;
|
|
4955
|
-
}
|
|
4956
|
-
if (session) {
|
|
4957
|
-
if (session.pendingChunks) {
|
|
4958
|
-
for (const chunk of session.pendingChunks) {
|
|
4959
|
-
sendChunk(payload.i, chunk);
|
|
4960
|
-
}
|
|
4961
|
-
delete session.pendingChunks;
|
|
4962
|
-
}
|
|
4963
|
-
cleanupIfEnded(payload.i, session);
|
|
4964
|
-
} else {
|
|
4965
|
-
sessions.set(payload.i, { ended: false });
|
|
4966
|
-
}
|
|
4967
|
-
});
|
|
4968
|
-
server.middlewares.use((req, _res, next) => {
|
|
4969
|
-
const existingId = req.headers[DEBUG_ID_HEADER.toLowerCase()];
|
|
4970
|
-
const isHtml = req.headers.accept?.includes("text/html");
|
|
4971
|
-
if (!existingId && !isHtml) {
|
|
4972
|
-
next();
|
|
4973
|
-
return;
|
|
4974
|
-
}
|
|
4975
|
-
const debugId = existingId || crypto.randomUUID();
|
|
4976
|
-
if (!existingId) {
|
|
4977
|
-
const lowerName = DEBUG_ID_HEADER.toLowerCase();
|
|
4978
|
-
req.headers[lowerName] = debugId;
|
|
4979
|
-
if (req.rawHeaders) {
|
|
4980
|
-
req.rawHeaders.push(DEBUG_ID_HEADER, debugId);
|
|
4981
|
-
}
|
|
4982
|
-
}
|
|
4983
|
-
registerDebugChannel(debugId);
|
|
4984
|
-
console.log(
|
|
4985
|
-
"[perf-tracks] middleware: created channel for",
|
|
4986
|
-
debugId,
|
|
4987
|
-
"from",
|
|
4988
|
-
existingId ? "client header" : "SSR inject"
|
|
4989
|
-
);
|
|
4990
|
-
next();
|
|
4991
|
-
});
|
|
4992
|
-
}
|
|
4993
|
-
};
|
|
4994
|
-
}
|
|
4995
|
-
|
|
4996
5270
|
// src/vite/rango.ts
|
|
4997
5271
|
async function rango(options) {
|
|
4998
5272
|
const resolvedOptions = options ?? { preset: "node" };
|
|
@@ -5000,7 +5274,16 @@ async function rango(options) {
|
|
|
5000
5274
|
const showBanner = resolvedOptions.banner ?? true;
|
|
5001
5275
|
const plugins = [];
|
|
5002
5276
|
const rangoAliases = getPackageAliases();
|
|
5003
|
-
const excludeDeps =
|
|
5277
|
+
const excludeDeps = [
|
|
5278
|
+
...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.
|
|
5282
|
+
"@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
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
|
|
5286
|
+
];
|
|
5004
5287
|
const routerRef = { path: void 0 };
|
|
5005
5288
|
const prerenderEnabled = true;
|
|
5006
5289
|
if (preset === "cloudflare") {
|
|
@@ -5096,6 +5379,7 @@ async function rango(options) {
|
|
|
5096
5379
|
}
|
|
5097
5380
|
});
|
|
5098
5381
|
plugins.push(createVirtualEntriesPlugin(finalEntries));
|
|
5382
|
+
plugins.push(performanceTracksPlugin());
|
|
5099
5383
|
plugins.push(
|
|
5100
5384
|
rsc({
|
|
5101
5385
|
entries: finalEntries,
|
|
@@ -5214,6 +5498,7 @@ ${list}`);
|
|
|
5214
5498
|
}
|
|
5215
5499
|
});
|
|
5216
5500
|
plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
|
|
5501
|
+
plugins.push(performanceTracksPlugin());
|
|
5217
5502
|
plugins.push(
|
|
5218
5503
|
rsc({
|
|
5219
5504
|
entries: finalEntries
|
|
@@ -5254,10 +5539,10 @@ ${list}`);
|
|
|
5254
5539
|
createRouterDiscoveryPlugin(discoveryEntryPath, {
|
|
5255
5540
|
routerPathRef: discoveryRouterRef,
|
|
5256
5541
|
enableBuildPrerender: prerenderEnabled,
|
|
5257
|
-
|
|
5542
|
+
buildEnv: options?.buildEnv,
|
|
5543
|
+
preset
|
|
5258
5544
|
})
|
|
5259
5545
|
);
|
|
5260
|
-
plugins.push(performanceTracksPlugin());
|
|
5261
5546
|
return plugins;
|
|
5262
5547
|
}
|
|
5263
5548
|
|
|
@@ -5268,29 +5553,75 @@ function poke() {
|
|
|
5268
5553
|
apply: "serve",
|
|
5269
5554
|
configureServer(server) {
|
|
5270
5555
|
const stdin = process.stdin;
|
|
5271
|
-
const
|
|
5556
|
+
const debug = process.env.RANGO_POKE_DEBUG === "1";
|
|
5557
|
+
const triggerReload = (source) => {
|
|
5558
|
+
server.hot.send({ type: "full-reload", path: "*" });
|
|
5559
|
+
server.config.logger.info(` browser reload (${source})`, {
|
|
5560
|
+
timestamp: true
|
|
5561
|
+
});
|
|
5562
|
+
};
|
|
5563
|
+
const toBuffer = (chunk) => {
|
|
5564
|
+
return typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
5565
|
+
};
|
|
5566
|
+
const formatChunk = (chunk) => {
|
|
5567
|
+
const data = toBuffer(chunk);
|
|
5568
|
+
const hex = Array.from(data).map((byte) => `0x${byte.toString(16).padStart(2, "0")}`).join(" ");
|
|
5569
|
+
const ascii = Array.from(data).map((byte) => {
|
|
5570
|
+
if (byte >= 32 && byte <= 126) return String.fromCharCode(byte);
|
|
5571
|
+
if (byte === 10) return "\\n";
|
|
5572
|
+
if (byte === 13) return "\\r";
|
|
5573
|
+
if (byte === 9) return "\\t";
|
|
5574
|
+
return ".";
|
|
5575
|
+
}).join("");
|
|
5576
|
+
return `len=${data.length} hex=[${hex}] ascii="${ascii}"`;
|
|
5577
|
+
};
|
|
5578
|
+
const readCtrlR = (chunk) => {
|
|
5579
|
+
const data = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
5580
|
+
return data.length === 1 && data[0] === 18;
|
|
5581
|
+
};
|
|
5582
|
+
const readSubmittedCommands = (chunk) => {
|
|
5583
|
+
const text = toBuffer(chunk).toString("utf8").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
5584
|
+
if (!text.includes("\n")) return [];
|
|
5585
|
+
const lines = text.split("\n");
|
|
5586
|
+
lines.pop();
|
|
5587
|
+
return lines;
|
|
5588
|
+
};
|
|
5589
|
+
if (debug) {
|
|
5590
|
+
server.config.logger.info(
|
|
5591
|
+
` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
|
|
5592
|
+
{ timestamp: true }
|
|
5593
|
+
);
|
|
5594
|
+
}
|
|
5272
5595
|
if (stdin.isTTY) {
|
|
5273
|
-
|
|
5596
|
+
server.config.logger.info(
|
|
5597
|
+
" poke ready: press e + enter to reload browser (ctrl+r also works when available)",
|
|
5598
|
+
{ timestamp: true }
|
|
5599
|
+
);
|
|
5274
5600
|
}
|
|
5275
5601
|
const onData = (data) => {
|
|
5276
|
-
if (
|
|
5277
|
-
|
|
5278
|
-
process.emit("SIGINT", "SIGINT");
|
|
5279
|
-
return;
|
|
5280
|
-
}
|
|
5281
|
-
if (data[0] === 18) {
|
|
5282
|
-
server.hot.send({ type: "full-reload", path: "*" });
|
|
5283
|
-
server.config.logger.info(" browser reload (ctrl+r)", {
|
|
5602
|
+
if (debug) {
|
|
5603
|
+
server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
|
|
5284
5604
|
timestamp: true
|
|
5285
5605
|
});
|
|
5286
5606
|
}
|
|
5607
|
+
if (readCtrlR(data)) {
|
|
5608
|
+
triggerReload("ctrl+r");
|
|
5609
|
+
return;
|
|
5610
|
+
}
|
|
5611
|
+
for (const command of readSubmittedCommands(data)) {
|
|
5612
|
+
if (command === "e") {
|
|
5613
|
+
triggerReload("e+enter");
|
|
5614
|
+
return;
|
|
5615
|
+
}
|
|
5616
|
+
if (command === "\x1Br") {
|
|
5617
|
+
triggerReload("option+r+enter");
|
|
5618
|
+
return;
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5287
5621
|
};
|
|
5288
5622
|
stdin.on("data", onData);
|
|
5289
5623
|
server.httpServer?.on("close", () => {
|
|
5290
5624
|
stdin.off("data", onData);
|
|
5291
|
-
if (stdin.isTTY && previousRawMode !== null) {
|
|
5292
|
-
stdin.setRawMode(previousRawMode);
|
|
5293
|
-
}
|
|
5294
5625
|
});
|
|
5295
5626
|
}
|
|
5296
5627
|
};
|