@rangojs/router 0.0.0-experimental.88a3b2f7 → 0.0.0-experimental.8bd1b239
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/dist/vite/index.js +307 -128
- 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/middleware/SKILL.md +32 -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/rango/SKILL.md +24 -22
- package/skills/route/SKILL.md +24 -0
- package/src/browser/navigation-bridge.ts +21 -2
- package/src/browser/navigation-client.ts +34 -6
- package/src/browser/partial-update.ts +14 -2
- package/src/browser/prefetch/cache.ts +16 -6
- package/src/browser/prefetch/fetch.ts +60 -4
- package/src/browser/react/Link.tsx +25 -2
- package/src/browser/react/use-navigation.ts +11 -10
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/build/route-trie.ts +50 -24
- package/src/client.tsx +84 -230
- package/src/index.ts +41 -9
- package/src/reverse.ts +4 -1
- package/src/route-definition/dsl-helpers.ts +164 -21
- package/src/route-definition/helpers-types.ts +61 -14
- package/src/router/handler-context.ts +4 -1
- package/src/router/loader-resolution.ts +70 -46
- package/src/router/match-middleware/cache-lookup.ts +10 -5
- package/src/router/match-middleware/segment-resolution.ts +1 -1
- package/src/router/match-result.ts +82 -4
- package/src/router/segment-resolution/fresh.ts +52 -0
- package/src/router/segment-resolution/revalidation.ts +69 -1
- package/src/rsc/handler.ts +13 -5
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/progressive-enhancement.ts +10 -2
- package/src/rsc/rsc-rendering.ts +5 -1
- package/src/rsc/server-action.ts +6 -0
- package/src/rsc/types.ts +1 -0
- 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/handle-store.ts +19 -0
- package/src/server/request-context.ts +54 -14
- package/src/types/segments.ts +1 -1
- package/src/urls/path-helper-types.ts +34 -5
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/prerender-collection.ts +124 -83
- 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/router-discovery.ts +25 -3
- package/src/vite/utils/prerender-utils.ts +20 -6
package/dist/vite/index.js
CHANGED
|
@@ -18,6 +18,9 @@ function hashId(filePath, exportName) {
|
|
|
18
18
|
const hash = crypto.createHash("sha256").update(input).digest("hex");
|
|
19
19
|
return `${hash.slice(0, 8)}#${exportName}`;
|
|
20
20
|
}
|
|
21
|
+
function makeStubId(filePath, exportName, isBuild) {
|
|
22
|
+
return isBuild ? hashId(filePath, exportName) : `${filePath}#${exportName}`;
|
|
23
|
+
}
|
|
21
24
|
function hashInlineId(filePath, lineNumber, index) {
|
|
22
25
|
const input = index !== void 0 && index > 0 ? `${filePath}:${lineNumber}:${index}` : `${filePath}:${lineNumber}`;
|
|
23
26
|
return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
|
|
@@ -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.8bd1b239",
|
|
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
|
},
|
|
@@ -3461,11 +3580,19 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
|
3461
3580
|
let hadOmittedOptional = false;
|
|
3462
3581
|
for (const [key, value] of Object.entries(params)) {
|
|
3463
3582
|
const escaped = escapeRegExp2(key);
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
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
|
+
}
|
|
3469
3596
|
}
|
|
3470
3597
|
result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
|
|
3471
3598
|
hadOmittedOptional = true;
|
|
@@ -3584,93 +3711,126 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3584
3711
|
if (!params) return pattern;
|
|
3585
3712
|
return substituteRouteParams(pattern, params);
|
|
3586
3713
|
};
|
|
3714
|
+
let resolvedRoutes = 0;
|
|
3715
|
+
let totalDynamic = 0;
|
|
3587
3716
|
for (const { manifest } of allManifests) {
|
|
3588
3717
|
if (!manifest.prerenderRoutes) continue;
|
|
3589
|
-
const defs = manifest._prerenderDefs || {};
|
|
3590
|
-
const passthroughSet = new Set(manifest.passthroughRoutes || []);
|
|
3591
3718
|
for (const routeName of manifest.prerenderRoutes) {
|
|
3592
3719
|
const pattern = manifest.routeManifest[routeName];
|
|
3593
|
-
if (
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
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
|
+
);
|
|
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
|
|
3620
3777
|
);
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
params,
|
|
3630
|
-
encodePathParam
|
|
3631
|
-
);
|
|
3632
|
-
if (url.includes("*")) {
|
|
3633
|
-
const wildcardValue = params["*"] ?? params.splat;
|
|
3634
|
-
if (wildcardValue !== void 0) {
|
|
3635
|
-
url = url.replace(/\*[^/]*$/, encodePathParam(wildcardValue));
|
|
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
|
+
}
|
|
3636
3786
|
}
|
|
3787
|
+
entries.push({
|
|
3788
|
+
urlPath: url.replace(/\/$/, "") || "/",
|
|
3789
|
+
routeName,
|
|
3790
|
+
concurrency,
|
|
3791
|
+
...hasBuildVars ? { buildVars } : {},
|
|
3792
|
+
isPassthroughRoute
|
|
3793
|
+
});
|
|
3637
3794
|
}
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
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}`
|
|
3658
3814
|
);
|
|
3659
|
-
|
|
3815
|
+
notifyOnError(registry, err, "prerender", routeName);
|
|
3816
|
+
throw err;
|
|
3660
3817
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3818
|
+
} else {
|
|
3819
|
+
console.warn(
|
|
3820
|
+
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
|
|
3663
3821
|
);
|
|
3664
|
-
notifyOnError(registry, err, "prerender", routeName);
|
|
3665
|
-
throw err;
|
|
3666
3822
|
}
|
|
3667
|
-
} else {
|
|
3668
|
-
console.warn(
|
|
3669
|
-
`[rsc-router] Dynamic prerender route "${routeName}" has no getParams(), skipping`
|
|
3670
|
-
);
|
|
3671
3823
|
}
|
|
3672
3824
|
}
|
|
3673
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
|
+
}
|
|
3674
3834
|
}
|
|
3675
3835
|
if (entries.length === 0) return;
|
|
3676
3836
|
const maxConcurrency = Math.max(...entries.map((e) => e.concurrency));
|
|
@@ -4785,7 +4945,26 @@ ${err.stack}`
|
|
|
4785
4945
|
res.end("Missing pathname");
|
|
4786
4946
|
return;
|
|
4787
4947
|
}
|
|
4788
|
-
|
|
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
|
+
}
|
|
4789
4968
|
if (!registry) {
|
|
4790
4969
|
if (!prerenderNodeRegistry) {
|
|
4791
4970
|
await getOrCreateTempServer();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rangojs/router",
|
|
3
|
-
"version": "0.0.0-experimental.
|
|
3
|
+
"version": "0.0.0-experimental.8bd1b239",
|
|
4
4
|
"description": "Django-inspired RSC router with composable URL patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"test:unit:watch": "vitest"
|
|
143
143
|
},
|
|
144
144
|
"dependencies": {
|
|
145
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
145
|
+
"@vitejs/plugin-rsc": "^0.5.23",
|
|
146
146
|
"magic-string": "^0.30.17",
|
|
147
147
|
"picomatch": "^4.0.3",
|
|
148
148
|
"rsc-html-stream": "^0.0.7"
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
},
|
|
163
163
|
"peerDependencies": {
|
|
164
164
|
"@cloudflare/vite-plugin": "^1.25.0",
|
|
165
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
165
|
+
"@vitejs/plugin-rsc": "^0.5.23",
|
|
166
166
|
"react": "^18.0.0 || ^19.0.0",
|
|
167
167
|
"vite": "^7.3.0"
|
|
168
168
|
},
|