@rangojs/router 0.0.0-experimental.fa8a383a → 0.0.0-experimental.fad716ff
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 +526 -168
- package/dist/vite/index.js.bak +5448 -0
- package/package.json +2 -2
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +8 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +53 -43
- package/skills/middleware/SKILL.md +2 -0
- package/skills/parallel/SKILL.md +67 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/route/SKILL.md +31 -0
- package/skills/router-setup/SKILL.md +87 -2
- package/skills/typesafety/SKILL.md +10 -0
- package/src/__internal.ts +1 -1
- package/src/browser/app-version.ts +14 -0
- package/src/browser/navigation-bridge.ts +16 -3
- package/src/browser/navigation-client.ts +64 -40
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/partial-update.ts +37 -4
- package/src/browser/prefetch/fetch.ts +8 -2
- package/src/browser/prefetch/queue.ts +61 -29
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +44 -8
- package/src/browser/react/NavigationProvider.tsx +13 -4
- 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/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +27 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- 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/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +46 -5
- package/src/cache/taint.ts +55 -0
- package/src/client.tsx +2 -56
- package/src/context-var.ts +72 -2
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +3 -1
- package/src/index.ts +12 -0
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +22 -1
- package/src/route-definition/dsl-helpers.ts +42 -19
- package/src/route-definition/helpers-types.ts +10 -6
- 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 +11 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/handler-context.ts +79 -23
- package/src/router/intercept-resolution.ts +9 -4
- package/src/router/loader-resolution.ts +156 -21
- package/src/router/match-api.ts +124 -189
- package/src/router/match-middleware/background-revalidation.ts +12 -1
- package/src/router/match-middleware/cache-lookup.ts +72 -13
- package/src/router/match-middleware/cache-store.ts +21 -4
- package/src/router/match-middleware/segment-resolution.ts +53 -0
- package/src/router/match-result.ts +11 -5
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +6 -8
- package/src/router/middleware.ts +2 -5
- 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-context.ts +1 -0
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +101 -18
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/revalidation.ts +122 -26
- package/src/router/types.ts +1 -0
- package/src/router.ts +54 -5
- package/src/rsc/handler.ts +464 -377
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +14 -2
- package/src/rsc/rsc-rendering.ts +10 -1
- package/src/rsc/server-action.ts +8 -0
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +9 -1
- package/src/server/context.ts +50 -1
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +175 -15
- 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 +137 -33
- package/src/types/loader-types.ts +36 -9
- package/src/types/route-entry.ts +1 -1
- package/src/urls/path-helper-types.ts +9 -2
- 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 +73 -4
- 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 +14 -1
- 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-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +118 -39
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/rango.ts +19 -2
- package/src/vite/router-discovery.ts +178 -37
- package/src/vite/utils/prerender-utils.ts +18 -0
- package/src/vite/utils/shared-utils.ts +3 -2
package/dist/vite/index.js
CHANGED
|
@@ -910,9 +910,7 @@ function generateWholeFileStubs(cfg, bindings, code, filePath, isBuild) {
|
|
|
910
910
|
});
|
|
911
911
|
return { code: stubs.join("\n") + "\n", map: null };
|
|
912
912
|
}
|
|
913
|
-
function
|
|
914
|
-
if (bindings.length === 0) return null;
|
|
915
|
-
const s = new MagicString2(code);
|
|
913
|
+
function stubHandlerExprs(cfg, bindings, s, filePath, isBuild) {
|
|
916
914
|
let hasChanges = false;
|
|
917
915
|
for (const binding of bindings) {
|
|
918
916
|
const exportName = binding.exportNames[0];
|
|
@@ -924,15 +922,7 @@ function generateExprStubs(cfg, bindings, code, filePath, sourceId, isBuild) {
|
|
|
924
922
|
);
|
|
925
923
|
hasChanges = true;
|
|
926
924
|
}
|
|
927
|
-
|
|
928
|
-
return {
|
|
929
|
-
code: s.toString(),
|
|
930
|
-
map: s.generateMap({
|
|
931
|
-
source: sourceId,
|
|
932
|
-
includeContent: true,
|
|
933
|
-
hires: "boundary"
|
|
934
|
-
})
|
|
935
|
-
};
|
|
925
|
+
return hasChanges;
|
|
936
926
|
}
|
|
937
927
|
function transformHandlerIds(cfg, bindings, s, filePath, isBuild) {
|
|
938
928
|
let hasChanges = false;
|
|
@@ -1269,15 +1259,6 @@ ${lazyImports.join(",\n")}
|
|
|
1269
1259
|
isBuild
|
|
1270
1260
|
);
|
|
1271
1261
|
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
1262
|
}
|
|
1282
1263
|
if (hasPrerenderHandlerCode && isRscEnv && isBuild) {
|
|
1283
1264
|
const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
|
|
@@ -1329,15 +1310,57 @@ ${lazyImports.join(",\n")}
|
|
|
1329
1310
|
isBuild
|
|
1330
1311
|
);
|
|
1331
1312
|
if (wholeFile) return wholeFile;
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1313
|
+
}
|
|
1314
|
+
if (!isRscEnv) {
|
|
1315
|
+
const allBindings = [];
|
|
1316
|
+
if (hasLoaderCode) {
|
|
1317
|
+
allBindings.push(...getBindings(code, getFnNames("createLoader")));
|
|
1318
|
+
}
|
|
1319
|
+
if (hasHandleCode) {
|
|
1320
|
+
allBindings.push(...getBindings(code, getFnNames("createHandle")));
|
|
1321
|
+
}
|
|
1322
|
+
if (hasLocationStateCode) {
|
|
1323
|
+
allBindings.push(
|
|
1324
|
+
...getBindings(code, getFnNames("createLocationState"))
|
|
1325
|
+
);
|
|
1326
|
+
}
|
|
1327
|
+
if (hasPrerenderHandlerCode) {
|
|
1328
|
+
allBindings.push(
|
|
1329
|
+
...getBindings(code, getFnNames(PRERENDER_CONFIG.fnName))
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
if (hasStaticHandlerCode) {
|
|
1333
|
+
allBindings.push(
|
|
1334
|
+
...getBindings(code, getFnNames(STATIC_CONFIG.fnName))
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
if (allBindings.length > 0 && isExportOnlyFile(code, allBindings)) {
|
|
1338
|
+
const stubs = [];
|
|
1339
|
+
for (const binding of allBindings) {
|
|
1340
|
+
const name = binding.exportNames[0];
|
|
1341
|
+
const stubId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
1342
|
+
const fnCall = code.slice(
|
|
1343
|
+
binding.callExprStart,
|
|
1344
|
+
binding.callOpenParenPos + 1
|
|
1345
|
+
);
|
|
1346
|
+
let brand = "loader";
|
|
1347
|
+
if (hasPrerenderHandlerCode && getFnNames(PRERENDER_CONFIG.fnName).some(
|
|
1348
|
+
(n) => fnCall.includes(n)
|
|
1349
|
+
)) {
|
|
1350
|
+
brand = PRERENDER_CONFIG.brand;
|
|
1351
|
+
} else if (hasStaticHandlerCode && getFnNames(STATIC_CONFIG.fnName).some((n) => fnCall.includes(n))) {
|
|
1352
|
+
brand = STATIC_CONFIG.brand;
|
|
1353
|
+
} else if (hasHandleCode && getFnNames("createHandle").some((n) => fnCall.includes(n))) {
|
|
1354
|
+
brand = "handle";
|
|
1355
|
+
} else if (hasLocationStateCode && getFnNames("createLocationState").some((n) => fnCall.includes(n))) {
|
|
1356
|
+
brand = "locationState";
|
|
1357
|
+
}
|
|
1358
|
+
stubs.push(
|
|
1359
|
+
`export const ${name} = { __brand: "${brand}", $$id: "${stubId}" };`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
return { code: stubs.join("\n") + "\n", map: null };
|
|
1363
|
+
}
|
|
1341
1364
|
}
|
|
1342
1365
|
if (hasStaticHandlerCode && isRscEnv && isBuild) {
|
|
1343
1366
|
const fnNames = getFnNames(STATIC_CONFIG.fnName);
|
|
@@ -1372,25 +1395,41 @@ ${lazyImports.join(",\n")}
|
|
|
1372
1395
|
isBuild
|
|
1373
1396
|
) || changed;
|
|
1374
1397
|
}
|
|
1375
|
-
if (hasPrerenderHandlerCode
|
|
1398
|
+
if (hasPrerenderHandlerCode) {
|
|
1376
1399
|
const fnNames = getFnNames(PRERENDER_CONFIG.fnName);
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1400
|
+
const bindings = getBindings(code, fnNames);
|
|
1401
|
+
if (isRscEnv) {
|
|
1402
|
+
changed = transformHandlerIds(
|
|
1403
|
+
PRERENDER_CONFIG,
|
|
1404
|
+
bindings,
|
|
1405
|
+
s,
|
|
1406
|
+
filePath,
|
|
1407
|
+
isBuild
|
|
1408
|
+
) || changed;
|
|
1409
|
+
} else {
|
|
1410
|
+
changed = stubHandlerExprs(
|
|
1411
|
+
PRERENDER_CONFIG,
|
|
1412
|
+
bindings,
|
|
1413
|
+
s,
|
|
1414
|
+
filePath,
|
|
1415
|
+
isBuild
|
|
1416
|
+
) || changed;
|
|
1417
|
+
}
|
|
1384
1418
|
}
|
|
1385
|
-
if (hasStaticHandlerCode
|
|
1419
|
+
if (hasStaticHandlerCode) {
|
|
1386
1420
|
const fnNames = getFnNames(STATIC_CONFIG.fnName);
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1421
|
+
const bindings = getBindings(code, fnNames);
|
|
1422
|
+
if (isRscEnv) {
|
|
1423
|
+
changed = transformHandlerIds(
|
|
1424
|
+
STATIC_CONFIG,
|
|
1425
|
+
bindings,
|
|
1426
|
+
s,
|
|
1427
|
+
filePath,
|
|
1428
|
+
isBuild
|
|
1429
|
+
) || changed;
|
|
1430
|
+
} else {
|
|
1431
|
+
changed = stubHandlerExprs(STATIC_CONFIG, bindings, s, filePath, isBuild) || changed;
|
|
1432
|
+
}
|
|
1394
1433
|
}
|
|
1395
1434
|
if (!changed) return;
|
|
1396
1435
|
return {
|
|
@@ -1745,7 +1784,7 @@ import { resolve } from "node:path";
|
|
|
1745
1784
|
// package.json
|
|
1746
1785
|
var package_default = {
|
|
1747
1786
|
name: "@rangojs/router",
|
|
1748
|
-
version: "0.0.0-experimental.
|
|
1787
|
+
version: "0.0.0-experimental.fad716ff",
|
|
1749
1788
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1750
1789
|
keywords: [
|
|
1751
1790
|
"react",
|
|
@@ -1887,7 +1926,7 @@ var package_default = {
|
|
|
1887
1926
|
"test:unit:watch": "vitest"
|
|
1888
1927
|
},
|
|
1889
1928
|
dependencies: {
|
|
1890
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
1929
|
+
"@vitejs/plugin-rsc": "^0.5.19",
|
|
1891
1930
|
"magic-string": "^0.30.17",
|
|
1892
1931
|
picomatch: "^4.0.3",
|
|
1893
1932
|
"rsc-html-stream": "^0.0.7"
|
|
@@ -2317,7 +2356,7 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2317
2356
|
}
|
|
2318
2357
|
return routeMap;
|
|
2319
2358
|
}
|
|
2320
|
-
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut) {
|
|
2359
|
+
function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock) {
|
|
2321
2360
|
visited = visited ?? /* @__PURE__ */ new Set();
|
|
2322
2361
|
const realPath = resolve2(filePath);
|
|
2323
2362
|
const key = variableName ? `${realPath}:${variableName}` : realPath;
|
|
@@ -2333,7 +2372,9 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2333
2372
|
return { routes: {}, searchSchemas: {} };
|
|
2334
2373
|
}
|
|
2335
2374
|
let block;
|
|
2336
|
-
if (
|
|
2375
|
+
if (inlineBlock) {
|
|
2376
|
+
block = inlineBlock;
|
|
2377
|
+
} else if (variableName) {
|
|
2337
2378
|
const extracted = extractUrlsBlockForVariable(source, variableName);
|
|
2338
2379
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
2339
2380
|
block = extracted;
|
|
@@ -2452,7 +2493,7 @@ Router root: ${conflict.ancestor}
|
|
|
2452
2493
|
Nested router: ${conflict.nested}
|
|
2453
2494
|
Move the nested router into a sibling directory or configure it as a separate app root.`;
|
|
2454
2495
|
}
|
|
2455
|
-
function
|
|
2496
|
+
function extractUrlsFromRouter(code) {
|
|
2456
2497
|
const sourceFile = ts5.createSourceFile(
|
|
2457
2498
|
"router.tsx",
|
|
2458
2499
|
code,
|
|
@@ -2466,24 +2507,70 @@ function extractUrlsVariableFromRouter(code) {
|
|
|
2466
2507
|
const callee = node.expression;
|
|
2467
2508
|
return ts5.isIdentifier(callee) && callee.text === "createRouter";
|
|
2468
2509
|
}
|
|
2510
|
+
function isInlineBuilder(node) {
|
|
2511
|
+
return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
|
|
2512
|
+
}
|
|
2513
|
+
function isRoutesOnCreateRouter(node) {
|
|
2514
|
+
if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
|
|
2515
|
+
return false;
|
|
2516
|
+
let inner = node.expression.expression;
|
|
2517
|
+
while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
|
|
2518
|
+
inner = inner.expression.expression;
|
|
2519
|
+
}
|
|
2520
|
+
return isCreateRouterCall(inner);
|
|
2521
|
+
}
|
|
2469
2522
|
function visit(node) {
|
|
2470
2523
|
if (result) return;
|
|
2471
|
-
if (ts5.isCallExpression(node) &&
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
result = node.arguments[0].text;
|
|
2478
|
-
return;
|
|
2524
|
+
if (ts5.isCallExpression(node) && node.arguments.length >= 1 && isRoutesOnCreateRouter(node)) {
|
|
2525
|
+
const arg = node.arguments[0];
|
|
2526
|
+
if (ts5.isIdentifier(arg)) {
|
|
2527
|
+
result = { kind: "variable", name: arg.text };
|
|
2528
|
+
} else if (isInlineBuilder(arg)) {
|
|
2529
|
+
result = { kind: "inline", block: arg.getText(sourceFile) };
|
|
2479
2530
|
}
|
|
2531
|
+
return;
|
|
2480
2532
|
}
|
|
2481
2533
|
if (isCreateRouterCall(node)) {
|
|
2482
2534
|
const callExpr = node;
|
|
2483
|
-
for (const
|
|
2535
|
+
for (const callArg of callExpr.arguments) {
|
|
2536
|
+
if (ts5.isObjectLiteralExpression(callArg)) {
|
|
2537
|
+
for (const prop of callArg.properties) {
|
|
2538
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls") {
|
|
2539
|
+
if (ts5.isIdentifier(prop.initializer)) {
|
|
2540
|
+
result = { kind: "variable", name: prop.initializer.text };
|
|
2541
|
+
} else if (isInlineBuilder(prop.initializer)) {
|
|
2542
|
+
result = {
|
|
2543
|
+
kind: "inline",
|
|
2544
|
+
block: prop.initializer.getText(sourceFile)
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
ts5.forEachChild(node, visit);
|
|
2554
|
+
}
|
|
2555
|
+
visit(sourceFile);
|
|
2556
|
+
return result;
|
|
2557
|
+
}
|
|
2558
|
+
function extractBasenameFromRouter(code) {
|
|
2559
|
+
const sourceFile = ts5.createSourceFile(
|
|
2560
|
+
"router.tsx",
|
|
2561
|
+
code,
|
|
2562
|
+
ts5.ScriptTarget.Latest,
|
|
2563
|
+
true,
|
|
2564
|
+
ts5.ScriptKind.TSX
|
|
2565
|
+
);
|
|
2566
|
+
let result;
|
|
2567
|
+
function visit(node) {
|
|
2568
|
+
if (result !== void 0) return;
|
|
2569
|
+
if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "createRouter") {
|
|
2570
|
+
for (const arg of node.arguments) {
|
|
2484
2571
|
if (ts5.isObjectLiteralExpression(arg)) {
|
|
2485
2572
|
for (const prop of arg.properties) {
|
|
2486
|
-
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "
|
|
2573
|
+
if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "basename" && ts5.isStringLiteral(prop.initializer)) {
|
|
2487
2574
|
result = prop.initializer.text;
|
|
2488
2575
|
return;
|
|
2489
2576
|
}
|
|
@@ -2496,6 +2583,19 @@ function extractUrlsVariableFromRouter(code) {
|
|
|
2496
2583
|
visit(sourceFile);
|
|
2497
2584
|
return result;
|
|
2498
2585
|
}
|
|
2586
|
+
function applyBasenameToRoutes(result, basename3) {
|
|
2587
|
+
const prefixed = {};
|
|
2588
|
+
for (const [name, pattern] of Object.entries(result.routes)) {
|
|
2589
|
+
if (pattern === "/") {
|
|
2590
|
+
prefixed[name] = basename3;
|
|
2591
|
+
} else if (basename3.endsWith("/") && pattern.startsWith("/")) {
|
|
2592
|
+
prefixed[name] = basename3 + pattern.slice(1);
|
|
2593
|
+
} else {
|
|
2594
|
+
prefixed[name] = basename3 + pattern;
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
2598
|
+
}
|
|
2499
2599
|
function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
2500
2600
|
let routerSource;
|
|
2501
2601
|
try {
|
|
@@ -2503,19 +2603,40 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
2503
2603
|
} catch {
|
|
2504
2604
|
return { routes: {}, searchSchemas: {} };
|
|
2505
2605
|
}
|
|
2506
|
-
const
|
|
2507
|
-
if (!
|
|
2606
|
+
const extraction = extractUrlsFromRouter(routerSource);
|
|
2607
|
+
if (!extraction) {
|
|
2508
2608
|
return { routes: {}, searchSchemas: {} };
|
|
2509
2609
|
}
|
|
2510
|
-
const
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2610
|
+
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
2611
|
+
const basename3 = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
|
|
2612
|
+
let result;
|
|
2613
|
+
if (extraction.kind === "inline") {
|
|
2614
|
+
result = buildCombinedRouteMapWithSearch(
|
|
2615
|
+
routerFilePath,
|
|
2616
|
+
void 0,
|
|
2617
|
+
void 0,
|
|
2618
|
+
void 0,
|
|
2619
|
+
extraction.block
|
|
2620
|
+
);
|
|
2621
|
+
} else {
|
|
2622
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
2623
|
+
if (imported) {
|
|
2624
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
2625
|
+
if (!targetFile) {
|
|
2626
|
+
return { routes: {}, searchSchemas: {} };
|
|
2627
|
+
}
|
|
2628
|
+
result = buildCombinedRouteMapWithSearch(
|
|
2629
|
+
targetFile,
|
|
2630
|
+
imported.exportedName
|
|
2631
|
+
);
|
|
2632
|
+
} else {
|
|
2633
|
+
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
2515
2634
|
}
|
|
2516
|
-
return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
|
|
2517
2635
|
}
|
|
2518
|
-
|
|
2636
|
+
if (basename3) {
|
|
2637
|
+
result = applyBasenameToRoutes(result, basename3);
|
|
2638
|
+
}
|
|
2639
|
+
return result;
|
|
2519
2640
|
}
|
|
2520
2641
|
function findRouterFiles(root, filter) {
|
|
2521
2642
|
const result = [];
|
|
@@ -2540,25 +2661,15 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
2540
2661
|
throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
|
|
2541
2662
|
}
|
|
2542
2663
|
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);
|
|
2664
|
+
const result = buildCombinedRouteMapForRouterFile(routerFilePath);
|
|
2665
|
+
if (Object.keys(result.routes).length === 0 && Object.keys(result.searchSchemas).length === 0) {
|
|
2666
|
+
let routerSource;
|
|
2667
|
+
try {
|
|
2668
|
+
routerSource = readFileSync2(routerFilePath, "utf-8");
|
|
2669
|
+
} catch {
|
|
2670
|
+
continue;
|
|
2671
|
+
}
|
|
2672
|
+
if (!extractUrlsFromRouter(routerSource)) continue;
|
|
2562
2673
|
}
|
|
2563
2674
|
const routerBasename = pathBasename(routerFilePath).replace(
|
|
2564
2675
|
/\.(tsx?|jsx?)$/,
|
|
@@ -2784,6 +2895,68 @@ function createVersionPlugin() {
|
|
|
2784
2895
|
|
|
2785
2896
|
// src/vite/utils/shared-utils.ts
|
|
2786
2897
|
import * as Vite from "vite";
|
|
2898
|
+
|
|
2899
|
+
// src/vite/plugins/performance-tracks.ts
|
|
2900
|
+
import { readFile } from "node:fs/promises";
|
|
2901
|
+
var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
|
|
2902
|
+
function buildPatchReplacement(match, debugInfoVar) {
|
|
2903
|
+
return `${match}
|
|
2904
|
+
if (${debugInfoVar} && 0 === ${debugInfoVar}.length && "fulfilled" === root.status) {
|
|
2905
|
+
var _resolved = "function" === typeof resolveLazy ? resolveLazy(root.value) : root.value;
|
|
2906
|
+
if ("object" === typeof _resolved && null !== _resolved && isArrayImpl(_resolved._debugInfo)) {
|
|
2907
|
+
${debugInfoVar} = _resolved._debugInfo;
|
|
2908
|
+
}
|
|
2909
|
+
}`;
|
|
2910
|
+
}
|
|
2911
|
+
function patchRsdwClientDebugInfoRecovery(code) {
|
|
2912
|
+
const match = code.match(RSDW_PATCH_RE);
|
|
2913
|
+
if (!match) {
|
|
2914
|
+
return { code, debugInfoVar: null };
|
|
2915
|
+
}
|
|
2916
|
+
return {
|
|
2917
|
+
code: code.replace(match[1], buildPatchReplacement(match[1], match[2])),
|
|
2918
|
+
debugInfoVar: match[2]
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
function performanceTracksOptimizeDepsPlugin() {
|
|
2922
|
+
return {
|
|
2923
|
+
name: "@rangojs/router:performance-tracks-optimize-deps",
|
|
2924
|
+
setup(build) {
|
|
2925
|
+
build.onLoad(
|
|
2926
|
+
{
|
|
2927
|
+
filter: /react-server-dom-webpack-client\.browser\.(development|production)\.js$/
|
|
2928
|
+
},
|
|
2929
|
+
async (args) => {
|
|
2930
|
+
const code = await readFile(args.path, "utf8");
|
|
2931
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
2932
|
+
return {
|
|
2933
|
+
contents: patched.code,
|
|
2934
|
+
loader: "js"
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
);
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
function performanceTracksPlugin() {
|
|
2942
|
+
return {
|
|
2943
|
+
name: "@rangojs/router:performance-tracks",
|
|
2944
|
+
transform(code, id) {
|
|
2945
|
+
if (!id.includes("react-server-dom") || !id.includes("client")) return;
|
|
2946
|
+
const patched = patchRsdwClientDebugInfoRecovery(code);
|
|
2947
|
+
if (!patched.debugInfoVar) return;
|
|
2948
|
+
if (process.env.INTERNAL_RANGO_DEBUG)
|
|
2949
|
+
console.log(
|
|
2950
|
+
"[perf-tracks] patched RSDW client (var:",
|
|
2951
|
+
patched.debugInfoVar,
|
|
2952
|
+
")"
|
|
2953
|
+
);
|
|
2954
|
+
return patched.code;
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
// src/vite/utils/shared-utils.ts
|
|
2787
2960
|
var versionEsbuildPlugin = {
|
|
2788
2961
|
name: "@rangojs/router-version",
|
|
2789
2962
|
setup(build) {
|
|
@@ -2801,7 +2974,7 @@ var versionEsbuildPlugin = {
|
|
|
2801
2974
|
}
|
|
2802
2975
|
};
|
|
2803
2976
|
var sharedEsbuildOptions = {
|
|
2804
|
-
plugins: [versionEsbuildPlugin]
|
|
2977
|
+
plugins: [versionEsbuildPlugin, performanceTracksOptimizeDepsPlugin()]
|
|
2805
2978
|
};
|
|
2806
2979
|
function createVirtualEntriesPlugin(entries, routerPathRef) {
|
|
2807
2980
|
const virtualModules = {};
|
|
@@ -3007,6 +3180,8 @@ function createCjsToEsmPlugin() {
|
|
|
3007
3180
|
import { createServer as createViteServer } from "vite";
|
|
3008
3181
|
import { resolve as resolve8 } from "node:path";
|
|
3009
3182
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3183
|
+
import { createRequire } from "node:module";
|
|
3184
|
+
import { pathToFileURL } from "node:url";
|
|
3010
3185
|
|
|
3011
3186
|
// src/vite/plugins/virtual-stub-plugin.ts
|
|
3012
3187
|
function createVirtualStubPlugin() {
|
|
@@ -3190,8 +3365,8 @@ function createDiscoveryState(entryPath, opts) {
|
|
|
3190
3365
|
perRouterManifestDataMap: /* @__PURE__ */ new Map(),
|
|
3191
3366
|
prerenderManifestEntries: null,
|
|
3192
3367
|
staticManifestEntries: null,
|
|
3193
|
-
|
|
3194
|
-
|
|
3368
|
+
handlerChunkInfoMap: /* @__PURE__ */ new Map(),
|
|
3369
|
+
staticHandlerChunkInfoMap: /* @__PURE__ */ new Map(),
|
|
3195
3370
|
rscEntryFileName: null,
|
|
3196
3371
|
resolvedPrerenderModules: void 0,
|
|
3197
3372
|
resolvedStaticModules: void 0,
|
|
@@ -3274,8 +3449,17 @@ function jsonParseExpression(value) {
|
|
|
3274
3449
|
}
|
|
3275
3450
|
|
|
3276
3451
|
// src/context-var.ts
|
|
3452
|
+
var NON_CACHEABLE_KEYS = /* @__PURE__ */ Symbol.for(
|
|
3453
|
+
"rango:non-cacheable-keys"
|
|
3454
|
+
);
|
|
3455
|
+
function getNonCacheableKeys(variables) {
|
|
3456
|
+
if (!variables[NON_CACHEABLE_KEYS]) {
|
|
3457
|
+
variables[NON_CACHEABLE_KEYS] = /* @__PURE__ */ new Set();
|
|
3458
|
+
}
|
|
3459
|
+
return variables[NON_CACHEABLE_KEYS];
|
|
3460
|
+
}
|
|
3277
3461
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3278
|
-
function contextSet(variables, keyOrVar, value) {
|
|
3462
|
+
function contextSet(variables, keyOrVar, value, options) {
|
|
3279
3463
|
if (typeof keyOrVar === "string") {
|
|
3280
3464
|
if (FORBIDDEN_KEYS.has(keyOrVar)) {
|
|
3281
3465
|
throw new Error(
|
|
@@ -3283,8 +3467,14 @@ function contextSet(variables, keyOrVar, value) {
|
|
|
3283
3467
|
);
|
|
3284
3468
|
}
|
|
3285
3469
|
variables[keyOrVar] = value;
|
|
3470
|
+
if (options?.cache === false) {
|
|
3471
|
+
getNonCacheableKeys(variables).add(keyOrVar);
|
|
3472
|
+
}
|
|
3286
3473
|
} else {
|
|
3287
3474
|
variables[keyOrVar.key] = value;
|
|
3475
|
+
if (options?.cache === false) {
|
|
3476
|
+
getNonCacheableKeys(variables).add(keyOrVar.key);
|
|
3477
|
+
}
|
|
3288
3478
|
}
|
|
3289
3479
|
}
|
|
3290
3480
|
|
|
@@ -3307,6 +3497,7 @@ function encodePathParam(value) {
|
|
|
3307
3497
|
}
|
|
3308
3498
|
function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
3309
3499
|
let result = pattern;
|
|
3500
|
+
let hadOmittedOptional = false;
|
|
3310
3501
|
for (const [key, value] of Object.entries(params)) {
|
|
3311
3502
|
const escaped = escapeRegExp2(key);
|
|
3312
3503
|
result = result.replace(
|
|
@@ -3315,6 +3506,15 @@ function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
|
3315
3506
|
);
|
|
3316
3507
|
result = result.replace(`*${key}`, encode(value));
|
|
3317
3508
|
}
|
|
3509
|
+
result = result.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\?/g, () => {
|
|
3510
|
+
hadOmittedOptional = true;
|
|
3511
|
+
return "";
|
|
3512
|
+
});
|
|
3513
|
+
if (hadOmittedOptional) {
|
|
3514
|
+
const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
|
|
3515
|
+
result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
|
|
3516
|
+
if (hadTrailingSlash && !result.endsWith("/")) result += "/";
|
|
3517
|
+
}
|
|
3318
3518
|
return result;
|
|
3319
3519
|
}
|
|
3320
3520
|
async function runWithConcurrency(items, concurrency, fn) {
|
|
@@ -3426,11 +3626,12 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3426
3626
|
for (const { manifest } of allManifests) {
|
|
3427
3627
|
if (!manifest.prerenderRoutes) continue;
|
|
3428
3628
|
const defs = manifest._prerenderDefs || {};
|
|
3629
|
+
const passthroughSet = new Set(manifest.passthroughRoutes || []);
|
|
3429
3630
|
for (const routeName of manifest.prerenderRoutes) {
|
|
3430
3631
|
const pattern = manifest.routeManifest[routeName];
|
|
3431
3632
|
if (!pattern) continue;
|
|
3432
3633
|
const def = defs[routeName];
|
|
3433
|
-
const isPassthroughRoute =
|
|
3634
|
+
const isPassthroughRoute = passthroughSet.has(routeName);
|
|
3434
3635
|
const hasDynamic = pattern.includes(":") || pattern.includes("*");
|
|
3435
3636
|
if (!hasDynamic) {
|
|
3436
3637
|
entries.push({
|
|
@@ -3443,12 +3644,20 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3443
3644
|
if (def?.getParams) {
|
|
3444
3645
|
try {
|
|
3445
3646
|
const buildVars = {};
|
|
3647
|
+
const buildEnv = state.resolvedBuildEnv;
|
|
3446
3648
|
const getParamsCtx = {
|
|
3447
3649
|
build: true,
|
|
3650
|
+
dev: !state.isBuildMode,
|
|
3448
3651
|
set: ((keyOrVar, value) => {
|
|
3449
3652
|
contextSet(buildVars, keyOrVar, value);
|
|
3450
3653
|
}),
|
|
3451
|
-
reverse: getParamsReverse
|
|
3654
|
+
reverse: getParamsReverse,
|
|
3655
|
+
get env() {
|
|
3656
|
+
if (buildEnv !== void 0) return buildEnv;
|
|
3657
|
+
throw new Error(
|
|
3658
|
+
"[rsc-router] ctx.env is not available during build-time getParams(). Configure buildEnv in your rango() plugin options to enable build-time env access."
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3452
3661
|
};
|
|
3453
3662
|
const paramsList = await def.getParams(getParamsCtx);
|
|
3454
3663
|
const concurrency = def.options?.concurrency ?? 1;
|
|
@@ -3527,7 +3736,8 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3527
3736
|
entry.urlPath,
|
|
3528
3737
|
{},
|
|
3529
3738
|
entry.buildVars,
|
|
3530
|
-
entry.isPassthroughRoute
|
|
3739
|
+
entry.isPassthroughRoute,
|
|
3740
|
+
state.resolvedBuildEnv
|
|
3531
3741
|
);
|
|
3532
3742
|
if (!result) continue;
|
|
3533
3743
|
if (result.passthrough) {
|
|
@@ -3651,7 +3861,9 @@ async function renderStaticHandlers(state, rscEnv, registry) {
|
|
|
3651
3861
|
const result = await routerInstance.renderStaticSegment(
|
|
3652
3862
|
def.handler,
|
|
3653
3863
|
def.$$id,
|
|
3654
|
-
def.$$routePrefix
|
|
3864
|
+
def.$$routePrefix,
|
|
3865
|
+
state.resolvedBuildEnv,
|
|
3866
|
+
!state.isBuildMode
|
|
3655
3867
|
);
|
|
3656
3868
|
if (result) {
|
|
3657
3869
|
const hasHandles = Object.keys(result.handles).length > 0;
|
|
@@ -3776,7 +3988,11 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3776
3988
|
if (!router.urlpatterns || !generateManifestFull) {
|
|
3777
3989
|
continue;
|
|
3778
3990
|
}
|
|
3779
|
-
const manifest = generateManifestFull(
|
|
3991
|
+
const manifest = generateManifestFull(
|
|
3992
|
+
router.urlpatterns,
|
|
3993
|
+
routerMountIndex,
|
|
3994
|
+
router.__basename ? { urlPrefix: router.__basename } : void 0
|
|
3995
|
+
);
|
|
3780
3996
|
routerMountIndex++;
|
|
3781
3997
|
allManifests.push({ id, manifest });
|
|
3782
3998
|
const routeCount = Object.keys(manifest.routeManifest).length;
|
|
@@ -4225,48 +4441,45 @@ function postprocessBundle(state) {
|
|
|
4225
4441
|
);
|
|
4226
4442
|
const evictionTargets = [
|
|
4227
4443
|
{
|
|
4228
|
-
|
|
4444
|
+
infos: state.handlerChunkInfoMap.values(),
|
|
4229
4445
|
fnName: "Prerender",
|
|
4230
4446
|
brand: "prerenderHandler",
|
|
4231
4447
|
label: "handler code from RSC bundle"
|
|
4232
4448
|
},
|
|
4233
4449
|
{
|
|
4234
|
-
|
|
4450
|
+
infos: state.staticHandlerChunkInfoMap.values(),
|
|
4235
4451
|
fnName: "Static",
|
|
4236
4452
|
brand: "staticHandler",
|
|
4237
4453
|
label: "static handler code"
|
|
4238
4454
|
}
|
|
4239
4455
|
];
|
|
4240
4456
|
for (const target of evictionTargets) {
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4457
|
+
for (const info of target.infos) {
|
|
4458
|
+
const chunkPath = resolve7(state.projectRoot, "dist/rsc", info.fileName);
|
|
4459
|
+
try {
|
|
4460
|
+
const code = readFileSync5(chunkPath, "utf-8");
|
|
4461
|
+
const result = evictHandlerCode(
|
|
4462
|
+
code,
|
|
4463
|
+
info.exports,
|
|
4464
|
+
target.fnName,
|
|
4465
|
+
target.brand
|
|
4466
|
+
);
|
|
4467
|
+
if (result) {
|
|
4468
|
+
writeFileSync4(chunkPath, result.code);
|
|
4469
|
+
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
4470
|
+
console.log(
|
|
4471
|
+
`[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${info.fileName}`
|
|
4472
|
+
);
|
|
4473
|
+
}
|
|
4474
|
+
} catch (replaceErr) {
|
|
4475
|
+
console.warn(
|
|
4476
|
+
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`
|
|
4260
4477
|
);
|
|
4261
4478
|
}
|
|
4262
|
-
} catch (replaceErr) {
|
|
4263
|
-
console.warn(
|
|
4264
|
-
`[rsc-router] Failed to evict ${target.label}: ${replaceErr.message}`
|
|
4265
|
-
);
|
|
4266
4479
|
}
|
|
4267
4480
|
}
|
|
4268
|
-
state.
|
|
4269
|
-
state.
|
|
4481
|
+
state.handlerChunkInfoMap.clear();
|
|
4482
|
+
state.staticHandlerChunkInfoMap.clear();
|
|
4270
4483
|
if (hasPrerenderData && existsSync6(rscEntryPath)) {
|
|
4271
4484
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4272
4485
|
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
@@ -4309,7 +4522,7 @@ function postprocessBundle(state) {
|
|
|
4309
4522
|
}
|
|
4310
4523
|
if (hasStaticData && existsSync6(rscEntryPath)) {
|
|
4311
4524
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4312
|
-
if (!rscCode.includes("
|
|
4525
|
+
if (!rscCode.includes("__static-manifest.js")) {
|
|
4313
4526
|
try {
|
|
4314
4527
|
const manifestEntries = [];
|
|
4315
4528
|
let totalBytes = copyStagedBuildAssets(
|
|
@@ -4378,8 +4591,67 @@ async function createTempRscServer(state, options = {}) {
|
|
|
4378
4591
|
]
|
|
4379
4592
|
});
|
|
4380
4593
|
}
|
|
4594
|
+
async function resolveBuildEnv(option, factoryCtx) {
|
|
4595
|
+
if (!option) return null;
|
|
4596
|
+
if (option === "auto") {
|
|
4597
|
+
if (factoryCtx.preset !== "cloudflare") {
|
|
4598
|
+
throw new Error(
|
|
4599
|
+
'[rsc-router] buildEnv: "auto" is only supported with preset: "cloudflare". Use a factory function or plain object for other presets.'
|
|
4600
|
+
);
|
|
4601
|
+
}
|
|
4602
|
+
try {
|
|
4603
|
+
const userRequire = createRequire(
|
|
4604
|
+
resolve8(factoryCtx.root, "package.json")
|
|
4605
|
+
);
|
|
4606
|
+
const wranglerPath = userRequire.resolve("wrangler");
|
|
4607
|
+
const { getPlatformProxy } = await import(pathToFileURL(wranglerPath).href);
|
|
4608
|
+
const proxy = await getPlatformProxy();
|
|
4609
|
+
return {
|
|
4610
|
+
env: proxy.env,
|
|
4611
|
+
dispose: proxy.dispose
|
|
4612
|
+
};
|
|
4613
|
+
} catch (err) {
|
|
4614
|
+
throw new Error(
|
|
4615
|
+
`[rsc-router] buildEnv: "auto" requires wrangler to be installed.
|
|
4616
|
+
Install it with: pnpm add -D wrangler
|
|
4617
|
+
${err.message}`
|
|
4618
|
+
);
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
if (typeof option === "function") {
|
|
4622
|
+
return await option(factoryCtx);
|
|
4623
|
+
}
|
|
4624
|
+
return { env: option };
|
|
4625
|
+
}
|
|
4626
|
+
async function acquireBuildEnv(s, command, mode) {
|
|
4627
|
+
const option = s.opts?.buildEnv;
|
|
4628
|
+
if (!option) return false;
|
|
4629
|
+
const result = await resolveBuildEnv(option, {
|
|
4630
|
+
root: s.projectRoot,
|
|
4631
|
+
mode,
|
|
4632
|
+
command,
|
|
4633
|
+
preset: s.opts?.preset ?? "node"
|
|
4634
|
+
});
|
|
4635
|
+
if (!result) return false;
|
|
4636
|
+
s.resolvedBuildEnv = result.env;
|
|
4637
|
+
s.buildEnvDispose = result.dispose ?? null;
|
|
4638
|
+
return true;
|
|
4639
|
+
}
|
|
4640
|
+
async function releaseBuildEnv(s) {
|
|
4641
|
+
if (s.buildEnvDispose) {
|
|
4642
|
+
try {
|
|
4643
|
+
await s.buildEnvDispose();
|
|
4644
|
+
} catch (err) {
|
|
4645
|
+
console.warn(`[rsc-router] buildEnv dispose failed: ${err.message}`);
|
|
4646
|
+
}
|
|
4647
|
+
s.buildEnvDispose = null;
|
|
4648
|
+
}
|
|
4649
|
+
s.resolvedBuildEnv = void 0;
|
|
4650
|
+
}
|
|
4381
4651
|
function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
4382
4652
|
const s = createDiscoveryState(entryPath, opts);
|
|
4653
|
+
let viteCommand = "build";
|
|
4654
|
+
let viteMode = "production";
|
|
4383
4655
|
return {
|
|
4384
4656
|
name: "@rangojs/router:discovery",
|
|
4385
4657
|
config() {
|
|
@@ -4388,31 +4660,13 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4388
4660
|
__RANGO_DEBUG__: JSON.stringify(!!process.env.INTERNAL_RANGO_DEBUG)
|
|
4389
4661
|
}
|
|
4390
4662
|
};
|
|
4391
|
-
if (opts?.enableBuildPrerender) {
|
|
4392
|
-
config.environments = {
|
|
4393
|
-
rsc: {
|
|
4394
|
-
build: {
|
|
4395
|
-
rollupOptions: {
|
|
4396
|
-
output: {
|
|
4397
|
-
manualChunks(id) {
|
|
4398
|
-
if (s.resolvedPrerenderModules?.has(id)) {
|
|
4399
|
-
return "__prerender-handlers";
|
|
4400
|
-
}
|
|
4401
|
-
if (s.resolvedStaticModules?.has(id)) {
|
|
4402
|
-
return "__static-handlers";
|
|
4403
|
-
}
|
|
4404
|
-
}
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
}
|
|
4408
|
-
}
|
|
4409
|
-
};
|
|
4410
|
-
}
|
|
4411
4663
|
return config;
|
|
4412
4664
|
},
|
|
4413
4665
|
configResolved(config) {
|
|
4414
4666
|
s.projectRoot = config.root;
|
|
4415
4667
|
s.isBuildMode = config.command === "build";
|
|
4668
|
+
viteCommand = config.command;
|
|
4669
|
+
viteMode = config.mode;
|
|
4416
4670
|
s.userResolveAlias = config.resolve.alias;
|
|
4417
4671
|
if (!s.resolvedEntryPath && opts?.routerPathRef?.path) {
|
|
4418
4672
|
s.resolvedEntryPath = opts.routerPathRef.path;
|
|
@@ -4457,6 +4711,8 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4457
4711
|
});
|
|
4458
4712
|
prerenderTempServer = null;
|
|
4459
4713
|
}
|
|
4714
|
+
releaseBuildEnv(s).catch(() => {
|
|
4715
|
+
});
|
|
4460
4716
|
});
|
|
4461
4717
|
async function getOrCreateTempServer() {
|
|
4462
4718
|
if (prerenderNodeRegistry) {
|
|
@@ -4487,6 +4743,7 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4487
4743
|
if (!rscEnv?.runner) {
|
|
4488
4744
|
s.devServerOrigin = getDevServerOrigin();
|
|
4489
4745
|
try {
|
|
4746
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4490
4747
|
const tempRscEnv = await getOrCreateTempServer();
|
|
4491
4748
|
if (tempRscEnv) {
|
|
4492
4749
|
await discoverRouters(s, tempRscEnv);
|
|
@@ -4502,6 +4759,7 @@ ${err.stack}`
|
|
|
4502
4759
|
return;
|
|
4503
4760
|
}
|
|
4504
4761
|
try {
|
|
4762
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4505
4763
|
const serverMod = await rscEnv.runner.import(
|
|
4506
4764
|
"@rangojs/router/server"
|
|
4507
4765
|
);
|
|
@@ -4566,7 +4824,26 @@ ${err.stack}`
|
|
|
4566
4824
|
res.end("Missing pathname");
|
|
4567
4825
|
return;
|
|
4568
4826
|
}
|
|
4569
|
-
|
|
4827
|
+
const rscEnv = server.environments?.rsc;
|
|
4828
|
+
let registry = null;
|
|
4829
|
+
if (rscEnv?.runner && s.resolvedEntryPath) {
|
|
4830
|
+
try {
|
|
4831
|
+
await rscEnv.runner.import(s.resolvedEntryPath);
|
|
4832
|
+
const serverMod = await rscEnv.runner.import(
|
|
4833
|
+
"@rangojs/router/server"
|
|
4834
|
+
);
|
|
4835
|
+
registry = serverMod.RouterRegistry ?? null;
|
|
4836
|
+
} catch (err) {
|
|
4837
|
+
console.warn(
|
|
4838
|
+
`[rsc-router] Dev prerender module refresh failed: ${err.message}`
|
|
4839
|
+
);
|
|
4840
|
+
res.statusCode = 500;
|
|
4841
|
+
res.end(`Prerender handler error: ${err.message}`);
|
|
4842
|
+
return;
|
|
4843
|
+
}
|
|
4844
|
+
} else {
|
|
4845
|
+
registry = mainRegistry;
|
|
4846
|
+
}
|
|
4570
4847
|
if (!registry) {
|
|
4571
4848
|
if (!prerenderNodeRegistry) {
|
|
4572
4849
|
await getOrCreateTempServer();
|
|
@@ -4588,7 +4865,10 @@ ${err.stack}`
|
|
|
4588
4865
|
pathname,
|
|
4589
4866
|
{},
|
|
4590
4867
|
void 0,
|
|
4591
|
-
wantPassthrough
|
|
4868
|
+
wantPassthrough,
|
|
4869
|
+
s.resolvedBuildEnv,
|
|
4870
|
+
true
|
|
4871
|
+
// devMode: check getParams for passthrough routes
|
|
4592
4872
|
);
|
|
4593
4873
|
if (!result) continue;
|
|
4594
4874
|
if (result.passthrough) continue;
|
|
@@ -4724,6 +5004,7 @@ ${err.stack}`
|
|
|
4724
5004
|
resetStagedBuildAssets(s.projectRoot);
|
|
4725
5005
|
s.prerenderManifestEntries = null;
|
|
4726
5006
|
s.staticManifestEntries = null;
|
|
5007
|
+
await acquireBuildEnv(s, viteCommand, viteMode);
|
|
4727
5008
|
let tempServer = null;
|
|
4728
5009
|
globalThis.__rscRouterDiscoveryActive = true;
|
|
4729
5010
|
try {
|
|
@@ -4763,6 +5044,7 @@ ${details}`
|
|
|
4763
5044
|
if (tempServer) {
|
|
4764
5045
|
await tempServer.close();
|
|
4765
5046
|
}
|
|
5047
|
+
await releaseBuildEnv(s);
|
|
4766
5048
|
}
|
|
4767
5049
|
},
|
|
4768
5050
|
// Virtual module: provides the pre-generated route manifest as a JS module
|
|
@@ -4805,20 +5087,30 @@ ${details}`
|
|
|
4805
5087
|
}
|
|
4806
5088
|
if (!s.resolvedPrerenderModules?.size && !s.resolvedStaticModules?.size)
|
|
4807
5089
|
return;
|
|
5090
|
+
s.handlerChunkInfoMap.clear();
|
|
5091
|
+
s.staticHandlerChunkInfoMap.clear();
|
|
4808
5092
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
4809
5093
|
if (chunk.type !== "chunk") continue;
|
|
4810
|
-
if (
|
|
5094
|
+
if (s.resolvedPrerenderModules?.size) {
|
|
4811
5095
|
const handlers = extractHandlerExportsFromChunk(
|
|
4812
5096
|
chunk.code,
|
|
4813
5097
|
s.resolvedPrerenderModules,
|
|
4814
5098
|
"Prerender",
|
|
4815
|
-
|
|
5099
|
+
false
|
|
4816
5100
|
);
|
|
4817
5101
|
if (handlers.length > 0) {
|
|
4818
|
-
|
|
5102
|
+
const existing = s.handlerChunkInfoMap.get(fileName);
|
|
5103
|
+
if (existing) {
|
|
5104
|
+
existing.exports.push(...handlers);
|
|
5105
|
+
} else {
|
|
5106
|
+
s.handlerChunkInfoMap.set(fileName, {
|
|
5107
|
+
fileName,
|
|
5108
|
+
exports: handlers
|
|
5109
|
+
});
|
|
5110
|
+
}
|
|
4819
5111
|
}
|
|
4820
5112
|
}
|
|
4821
|
-
if (
|
|
5113
|
+
if (s.resolvedStaticModules?.size) {
|
|
4822
5114
|
const handlers = extractHandlerExportsFromChunk(
|
|
4823
5115
|
chunk.code,
|
|
4824
5116
|
s.resolvedStaticModules,
|
|
@@ -4826,7 +5118,15 @@ ${details}`
|
|
|
4826
5118
|
false
|
|
4827
5119
|
);
|
|
4828
5120
|
if (handlers.length > 0) {
|
|
4829
|
-
|
|
5121
|
+
const existing = s.staticHandlerChunkInfoMap.get(fileName);
|
|
5122
|
+
if (existing) {
|
|
5123
|
+
existing.exports.push(...handlers);
|
|
5124
|
+
} else {
|
|
5125
|
+
s.staticHandlerChunkInfoMap.set(fileName, {
|
|
5126
|
+
fileName,
|
|
5127
|
+
exports: handlers
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
4830
5130
|
}
|
|
4831
5131
|
}
|
|
4832
5132
|
}
|
|
@@ -4853,7 +5153,16 @@ async function rango(options) {
|
|
|
4853
5153
|
const showBanner = resolvedOptions.banner ?? true;
|
|
4854
5154
|
const plugins = [];
|
|
4855
5155
|
const rangoAliases = getPackageAliases();
|
|
4856
|
-
const excludeDeps =
|
|
5156
|
+
const excludeDeps = [
|
|
5157
|
+
...getExcludeDeps(),
|
|
5158
|
+
// The public browser entry re-exports the RSDW browser client.
|
|
5159
|
+
// Excluding both keeps Vite from freezing the unpatched bundle into
|
|
5160
|
+
// .vite/deps before our source transforms run.
|
|
5161
|
+
"@vitejs/plugin-rsc/browser",
|
|
5162
|
+
// Keep the browser RSDW client out of Vite's dep optimizer so our
|
|
5163
|
+
// cjs-to-esm transform can patch the real file.
|
|
5164
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.browser"
|
|
5165
|
+
];
|
|
4857
5166
|
const routerRef = { path: void 0 };
|
|
4858
5167
|
const prerenderEnabled = true;
|
|
4859
5168
|
if (preset === "cloudflare") {
|
|
@@ -4949,6 +5258,7 @@ async function rango(options) {
|
|
|
4949
5258
|
}
|
|
4950
5259
|
});
|
|
4951
5260
|
plugins.push(createVirtualEntriesPlugin(finalEntries));
|
|
5261
|
+
plugins.push(performanceTracksPlugin());
|
|
4952
5262
|
plugins.push(
|
|
4953
5263
|
rsc({
|
|
4954
5264
|
entries: finalEntries,
|
|
@@ -5067,6 +5377,7 @@ ${list}`);
|
|
|
5067
5377
|
}
|
|
5068
5378
|
});
|
|
5069
5379
|
plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
|
|
5380
|
+
plugins.push(performanceTracksPlugin());
|
|
5070
5381
|
plugins.push(
|
|
5071
5382
|
rsc({
|
|
5072
5383
|
entries: finalEntries
|
|
@@ -5107,7 +5418,8 @@ ${list}`);
|
|
|
5107
5418
|
createRouterDiscoveryPlugin(discoveryEntryPath, {
|
|
5108
5419
|
routerPathRef: discoveryRouterRef,
|
|
5109
5420
|
enableBuildPrerender: prerenderEnabled,
|
|
5110
|
-
|
|
5421
|
+
buildEnv: options?.buildEnv,
|
|
5422
|
+
preset
|
|
5111
5423
|
})
|
|
5112
5424
|
);
|
|
5113
5425
|
return plugins;
|
|
@@ -5120,29 +5432,75 @@ function poke() {
|
|
|
5120
5432
|
apply: "serve",
|
|
5121
5433
|
configureServer(server) {
|
|
5122
5434
|
const stdin = process.stdin;
|
|
5123
|
-
const
|
|
5435
|
+
const debug = process.env.RANGO_POKE_DEBUG === "1";
|
|
5436
|
+
const triggerReload = (source) => {
|
|
5437
|
+
server.hot.send({ type: "full-reload", path: "*" });
|
|
5438
|
+
server.config.logger.info(` browser reload (${source})`, {
|
|
5439
|
+
timestamp: true
|
|
5440
|
+
});
|
|
5441
|
+
};
|
|
5442
|
+
const toBuffer = (chunk) => {
|
|
5443
|
+
return typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
5444
|
+
};
|
|
5445
|
+
const formatChunk = (chunk) => {
|
|
5446
|
+
const data = toBuffer(chunk);
|
|
5447
|
+
const hex = Array.from(data).map((byte) => `0x${byte.toString(16).padStart(2, "0")}`).join(" ");
|
|
5448
|
+
const ascii = Array.from(data).map((byte) => {
|
|
5449
|
+
if (byte >= 32 && byte <= 126) return String.fromCharCode(byte);
|
|
5450
|
+
if (byte === 10) return "\\n";
|
|
5451
|
+
if (byte === 13) return "\\r";
|
|
5452
|
+
if (byte === 9) return "\\t";
|
|
5453
|
+
return ".";
|
|
5454
|
+
}).join("");
|
|
5455
|
+
return `len=${data.length} hex=[${hex}] ascii="${ascii}"`;
|
|
5456
|
+
};
|
|
5457
|
+
const readCtrlR = (chunk) => {
|
|
5458
|
+
const data = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
5459
|
+
return data.length === 1 && data[0] === 18;
|
|
5460
|
+
};
|
|
5461
|
+
const readSubmittedCommands = (chunk) => {
|
|
5462
|
+
const text = toBuffer(chunk).toString("utf8").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
5463
|
+
if (!text.includes("\n")) return [];
|
|
5464
|
+
const lines = text.split("\n");
|
|
5465
|
+
lines.pop();
|
|
5466
|
+
return lines;
|
|
5467
|
+
};
|
|
5468
|
+
if (debug) {
|
|
5469
|
+
server.config.logger.info(
|
|
5470
|
+
` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
|
|
5471
|
+
{ timestamp: true }
|
|
5472
|
+
);
|
|
5473
|
+
}
|
|
5124
5474
|
if (stdin.isTTY) {
|
|
5125
|
-
|
|
5475
|
+
server.config.logger.info(
|
|
5476
|
+
" poke ready: press e + enter to reload browser (ctrl+r also works when available)",
|
|
5477
|
+
{ timestamp: true }
|
|
5478
|
+
);
|
|
5126
5479
|
}
|
|
5127
5480
|
const onData = (data) => {
|
|
5128
|
-
if (
|
|
5129
|
-
|
|
5130
|
-
process.emit("SIGINT", "SIGINT");
|
|
5131
|
-
return;
|
|
5132
|
-
}
|
|
5133
|
-
if (data[0] === 18) {
|
|
5134
|
-
server.hot.send({ type: "full-reload", path: "*" });
|
|
5135
|
-
server.config.logger.info(" browser reload (ctrl+r)", {
|
|
5481
|
+
if (debug) {
|
|
5482
|
+
server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
|
|
5136
5483
|
timestamp: true
|
|
5137
5484
|
});
|
|
5138
5485
|
}
|
|
5486
|
+
if (readCtrlR(data)) {
|
|
5487
|
+
triggerReload("ctrl+r");
|
|
5488
|
+
return;
|
|
5489
|
+
}
|
|
5490
|
+
for (const command of readSubmittedCommands(data)) {
|
|
5491
|
+
if (command === "e") {
|
|
5492
|
+
triggerReload("e+enter");
|
|
5493
|
+
return;
|
|
5494
|
+
}
|
|
5495
|
+
if (command === "\x1Br") {
|
|
5496
|
+
triggerReload("option+r+enter");
|
|
5497
|
+
return;
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5139
5500
|
};
|
|
5140
5501
|
stdin.on("data", onData);
|
|
5141
5502
|
server.httpServer?.on("close", () => {
|
|
5142
5503
|
stdin.off("data", onData);
|
|
5143
|
-
if (stdin.isTTY && previousRawMode !== null) {
|
|
5144
|
-
stdin.setRawMode(previousRawMode);
|
|
5145
|
-
}
|
|
5146
5504
|
});
|
|
5147
5505
|
}
|
|
5148
5506
|
};
|