@rangojs/router 0.0.0-experimental.18 → 0.0.0-experimental.19
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 +46 -8
- package/dist/bin/rango.js +105 -18
- package/dist/vite/index.js +227 -93
- package/package.json +15 -14
- package/skills/hooks/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +94 -1
- package/skills/middleware/SKILL.md +81 -0
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +187 -17
- package/skills/route/SKILL.md +42 -1
- package/skills/router-setup/SKILL.md +77 -0
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +38 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +25 -27
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +0 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +46 -13
- package/src/browser/navigation-client.ts +32 -61
- package/src/browser/navigation-store.ts +1 -31
- package/src/browser/navigation-transaction.ts +46 -207
- package/src/browser/partial-update.ts +102 -150
- package/src/browser/{prefetch-cache.ts → prefetch/cache.ts} +23 -4
- package/src/browser/{prefetch-fetch.ts → prefetch/fetch.ts} +36 -8
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/{prefetch-queue.ts → prefetch/queue.ts} +10 -3
- package/src/browser/react/Link.tsx +28 -23
- package/src/browser/react/NavigationProvider.tsx +9 -1
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +1 -1
- package/src/browser/react/location-state.ts +2 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/use-action.ts +9 -1
- package/src/browser/react/use-handle.ts +3 -25
- package/src/browser/react/use-params.ts +2 -4
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +1 -1
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +7 -60
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +29 -23
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +115 -96
- package/src/browser/types.ts +1 -31
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +5 -0
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +45 -3
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +132 -96
- package/src/cache/cache-scope.ts +71 -73
- package/src/cache/cf/cf-cache-store.ts +9 -4
- package/src/cache/document-cache.ts +72 -47
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/memory-segment-store.ts +18 -7
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +101 -112
- package/src/cache/taint.ts +26 -0
- package/src/client.tsx +53 -30
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +33 -1
- package/src/index.ts +27 -0
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +4 -3
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +94 -15
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +1 -0
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +61 -7
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +69 -4
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/middleware-types.ts +7 -0
- package/src/router/middleware.ts +93 -8
- package/src/router/pattern-matching.ts +41 -5
- package/src/router/prerender-match.ts +34 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +34 -0
- package/src/router/router-options.ts +200 -0
- package/src/router/segment-resolution/fresh.ts +123 -30
- package/src/router/segment-resolution/helpers.ts +19 -0
- package/src/router/segment-resolution/loader-cache.ts +37 -146
- package/src/router/segment-resolution/revalidation.ts +358 -94
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/types.ts +7 -1
- package/src/router.ts +155 -11
- package/src/rsc/handler-context.ts +11 -0
- package/src/rsc/handler.ts +380 -88
- package/src/rsc/helpers.ts +25 -16
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +232 -19
- package/src/rsc/response-route-handler.ts +37 -26
- package/src/rsc/rsc-rendering.ts +12 -5
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +134 -58
- package/src/rsc/types.ts +8 -0
- package/src/search-params.ts +22 -10
- package/src/server/context.ts +53 -5
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +66 -9
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +90 -9
- package/src/ssr/index.tsx +63 -27
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +1 -6
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +5 -0
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +9 -0
- package/src/types/handler-context.ts +35 -13
- package/src/types/loader-types.ts +7 -0
- package/src/types/route-entry.ts +28 -0
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +27 -2
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +12 -4
- package/src/vite/discovery/bundle-postprocess.ts +12 -7
- package/src/vite/discovery/discover-routers.ts +30 -18
- package/src/vite/discovery/prerender-collection.ts +24 -27
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/rango.ts +3 -3
- package/src/vite/router-discovery.ts +99 -36
- package/src/vite/utils/prerender-utils.ts +21 -0
- package/src/vite/utils/shared-utils.ts +3 -1
- package/src/browser/request-controller.ts +0 -164
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/src/browser/{prefetch-observer.ts → prefetch/observer.ts} +0 -0
package/dist/vite/index.js
CHANGED
|
@@ -1454,7 +1454,7 @@ function useCacheTransform() {
|
|
|
1454
1454
|
transformWrapExport
|
|
1455
1455
|
);
|
|
1456
1456
|
}
|
|
1457
|
-
|
|
1457
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
1458
1458
|
code,
|
|
1459
1459
|
ast,
|
|
1460
1460
|
filePath,
|
|
@@ -1462,21 +1462,33 @@ function useCacheTransform() {
|
|
|
1462
1462
|
isBuild,
|
|
1463
1463
|
transformHoistInlineDirective
|
|
1464
1464
|
);
|
|
1465
|
+
warnOnNearMissDirectives(ast, id, this.warn.bind(this));
|
|
1466
|
+
if (functionResult) return functionResult;
|
|
1465
1467
|
}
|
|
1466
1468
|
};
|
|
1467
1469
|
}
|
|
1468
1470
|
function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLayoutOrTemplate, transformWrapExport) {
|
|
1471
|
+
const nonFunctionExports = [];
|
|
1469
1472
|
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
1470
1473
|
runtime: (value, name) => {
|
|
1471
1474
|
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
1472
1475
|
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, "default")`;
|
|
1473
1476
|
},
|
|
1474
1477
|
rejectNonAsyncFunction: false,
|
|
1475
|
-
filter: (name) => {
|
|
1478
|
+
filter: (name, meta) => {
|
|
1476
1479
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
1480
|
+
if (meta.isFunction === false) {
|
|
1481
|
+
nonFunctionExports.push(name);
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1477
1484
|
return true;
|
|
1478
1485
|
}
|
|
1479
1486
|
});
|
|
1487
|
+
if (nonFunctionExports.length > 0) {
|
|
1488
|
+
throw new Error(
|
|
1489
|
+
`[rango:use-cache] File-level "use cache" in ${sourceId} cannot wrap non-function export${nonFunctionExports.length > 1 ? "s" : ""}: ${nonFunctionExports.map((n) => `"${n}"`).join(", ")}. Only function exports can be cached. Either remove "use cache" from the file level and add it inside individual functions, or move the non-function exports to a separate module.`
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1480
1492
|
if (exportNames.length === 0) {
|
|
1481
1493
|
const s = new MagicString5(code);
|
|
1482
1494
|
const directive2 = findFileLevelDirective(ast);
|
|
@@ -1513,7 +1525,7 @@ function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLa
|
|
|
1513
1525
|
function transformFunctionLevelUseCache(code, ast, filePath, sourceId, isBuild, transformHoistInlineDirective) {
|
|
1514
1526
|
try {
|
|
1515
1527
|
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
1516
|
-
directive: /^use cache(:\s
|
|
1528
|
+
directive: /^use cache(:\s*[\w-]+)?$/,
|
|
1517
1529
|
runtime: (value, name, meta) => {
|
|
1518
1530
|
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
1519
1531
|
const profileMatch = meta.directiveMatch[1];
|
|
@@ -1543,6 +1555,35 @@ function findFileLevelDirective(ast) {
|
|
|
1543
1555
|
}
|
|
1544
1556
|
return null;
|
|
1545
1557
|
}
|
|
1558
|
+
var VALID_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
|
|
1559
|
+
var NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
1560
|
+
function warnOnNearMissDirectives(ast, fileId, warn) {
|
|
1561
|
+
const visit = (node) => {
|
|
1562
|
+
if (!node || typeof node !== "object") return;
|
|
1563
|
+
if (node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
|
|
1564
|
+
const value = node.expression.value;
|
|
1565
|
+
if (value.startsWith("use cache") && NEAR_MISS_RE.test(value) && !VALID_DIRECTIVE_RE.test(value)) {
|
|
1566
|
+
const profilePart = value.slice("use cache:".length).trim();
|
|
1567
|
+
warn(
|
|
1568
|
+
`[rango:use-cache] "${value}" in ${fileId} has an invalid profile name "${profilePart}". Profile names must match [a-zA-Z0-9_-]+. This directive will be ignored.`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
for (const key of Object.keys(node)) {
|
|
1573
|
+
const child = node[key];
|
|
1574
|
+
if (Array.isArray(child)) {
|
|
1575
|
+
for (const item of child) {
|
|
1576
|
+
visit(item);
|
|
1577
|
+
}
|
|
1578
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
1579
|
+
visit(child);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
for (const node of ast.body ?? []) {
|
|
1584
|
+
visit(node);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1546
1587
|
|
|
1547
1588
|
// src/vite/plugins/virtual-entries.ts
|
|
1548
1589
|
var VIRTUAL_ENTRY_BROWSER = `
|
|
@@ -1657,7 +1698,7 @@ import { resolve } from "node:path";
|
|
|
1657
1698
|
// package.json
|
|
1658
1699
|
var package_default = {
|
|
1659
1700
|
name: "@rangojs/router",
|
|
1660
|
-
version: "0.0.0-experimental.
|
|
1701
|
+
version: "0.0.0-experimental.19",
|
|
1661
1702
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1662
1703
|
keywords: [
|
|
1663
1704
|
"react",
|
|
@@ -1970,15 +2011,27 @@ function extractRouteFromCallExpression(node) {
|
|
|
1970
2011
|
};
|
|
1971
2012
|
}
|
|
1972
2013
|
|
|
2014
|
+
// src/route-name.ts
|
|
2015
|
+
var AUTO_GENERATED_ROUTE_PREFIX = "$path_";
|
|
2016
|
+
var INTERNAL_INCLUDE_SCOPE_PREFIX = "$prefix_";
|
|
2017
|
+
function isAutoGeneratedRouteName(name) {
|
|
2018
|
+
return name.split(".").some((segment) => {
|
|
2019
|
+
return segment.startsWith(AUTO_GENERATED_ROUTE_PREFIX) || segment.startsWith(INTERNAL_INCLUDE_SCOPE_PREFIX);
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
|
|
1973
2023
|
// src/build/route-types/codegen.ts
|
|
1974
2024
|
function generateRouteTypesSource(routeManifest, searchSchemas) {
|
|
1975
|
-
const entries = Object.entries(routeManifest).sort(
|
|
1976
|
-
|
|
1977
|
-
|
|
2025
|
+
const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b));
|
|
2026
|
+
const filteredSearchSchemas = searchSchemas ? Object.fromEntries(
|
|
2027
|
+
Object.entries(searchSchemas).filter(
|
|
2028
|
+
([name]) => !isAutoGeneratedRouteName(name)
|
|
2029
|
+
)
|
|
2030
|
+
) : void 0;
|
|
1978
2031
|
const objectBody = entries.map(([name, pattern]) => {
|
|
1979
2032
|
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
|
|
1980
2033
|
const params = extractParamsFromPattern(pattern);
|
|
1981
|
-
const search =
|
|
2034
|
+
const search = filteredSearchSchemas?.[name];
|
|
1982
2035
|
return formatRouteEntry(key, pattern, params, search);
|
|
1983
2036
|
}).join("\n");
|
|
1984
2037
|
return `// Auto-generated by @rangojs/router - do not edit
|
|
@@ -2244,6 +2297,9 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
|
|
|
2244
2297
|
diagnosticsOut
|
|
2245
2298
|
);
|
|
2246
2299
|
}
|
|
2300
|
+
if (namePrefix === null) {
|
|
2301
|
+
continue;
|
|
2302
|
+
}
|
|
2247
2303
|
for (const [name, pattern] of Object.entries(childResult.routes)) {
|
|
2248
2304
|
const prefixedName = namePrefix ? `${namePrefix}.${name}` : name;
|
|
2249
2305
|
let prefixedPattern;
|
|
@@ -2294,6 +2350,7 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2294
2350
|
searchSchemas,
|
|
2295
2351
|
diagnosticsOut
|
|
2296
2352
|
);
|
|
2353
|
+
visited.delete(key);
|
|
2297
2354
|
return { routes, searchSchemas };
|
|
2298
2355
|
}
|
|
2299
2356
|
|
|
@@ -2301,6 +2358,17 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
2301
2358
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, unlinkSync } from "node:fs";
|
|
2302
2359
|
import { join as join2, dirname as dirname2, resolve as resolve3, basename as pathBasename } from "node:path";
|
|
2303
2360
|
import ts5 from "typescript";
|
|
2361
|
+
function countPublicRouteEntries(source) {
|
|
2362
|
+
const matches = source.matchAll(/^\s+(?:"([^"]+)"|([a-zA-Z_$][^:]*)):\s*["{]/gm) ?? [];
|
|
2363
|
+
let count = 0;
|
|
2364
|
+
for (const match of matches) {
|
|
2365
|
+
const routeName = match[1] || match[2];
|
|
2366
|
+
if (routeName && !isAutoGeneratedRouteName(routeName.trim())) {
|
|
2367
|
+
count++;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
return count;
|
|
2371
|
+
}
|
|
2304
2372
|
function extractUrlsVariableFromRouter(code) {
|
|
2305
2373
|
const sourceFile = ts5.createSourceFile(
|
|
2306
2374
|
"router.tsx",
|
|
@@ -2439,8 +2507,10 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
|
|
|
2439
2507
|
);
|
|
2440
2508
|
if (existing !== source) {
|
|
2441
2509
|
if (opts?.preserveIfLarger && existing) {
|
|
2442
|
-
const existingCount = (existing
|
|
2443
|
-
const newCount = Object.keys(result.routes).
|
|
2510
|
+
const existingCount = countPublicRouteEntries(existing);
|
|
2511
|
+
const newCount = Object.keys(result.routes).filter(
|
|
2512
|
+
(name) => !isAutoGeneratedRouteName(name)
|
|
2513
|
+
).length;
|
|
2444
2514
|
if (existingCount > newCount) {
|
|
2445
2515
|
continue;
|
|
2446
2516
|
}
|
|
@@ -2684,7 +2754,8 @@ function createVirtualEntriesPlugin(entries, routerPathRef) {
|
|
|
2684
2754
|
return virtualModules[virtualId];
|
|
2685
2755
|
}
|
|
2686
2756
|
if (virtualId === VIRTUAL_IDS.rsc && routerPathRef?.path) {
|
|
2687
|
-
const
|
|
2757
|
+
const raw = routerPathRef.path.startsWith(".") ? "/" + routerPathRef.path.slice(2) : routerPathRef.path;
|
|
2758
|
+
const absoluteRouterPath = raw.replaceAll("\\", "/");
|
|
2688
2759
|
return getVirtualEntryRSC(absoluteRouterPath);
|
|
2689
2760
|
}
|
|
2690
2761
|
}
|
|
@@ -2881,7 +2952,7 @@ function createVirtualStubPlugin() {
|
|
|
2881
2952
|
}
|
|
2882
2953
|
|
|
2883
2954
|
// src/vite/plugins/client-ref-hashing.ts
|
|
2884
|
-
import { relative as relative2
|
|
2955
|
+
import { relative as relative2 } from "node:path";
|
|
2885
2956
|
import { createHash as createHash2 } from "node:crypto";
|
|
2886
2957
|
var CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
|
|
2887
2958
|
var CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
|
|
@@ -2894,10 +2965,10 @@ function computeProductionHash(projectRoot, refKey) {
|
|
|
2894
2965
|
const absPath = decodeURIComponent(
|
|
2895
2966
|
refKey.slice(CLIENT_IN_SERVER_PKG_PROXY_PREFIX.length)
|
|
2896
2967
|
);
|
|
2897
|
-
toHash =
|
|
2968
|
+
toHash = relative2(projectRoot, absPath).replaceAll("\\", "/");
|
|
2898
2969
|
} else if (refKey.startsWith(FS_PREFIX)) {
|
|
2899
2970
|
const absPath = refKey.slice(FS_PREFIX.length - 1);
|
|
2900
|
-
toHash =
|
|
2971
|
+
toHash = relative2(projectRoot, absPath).replaceAll("\\", "/");
|
|
2901
2972
|
} else if (refKey.startsWith("/")) {
|
|
2902
2973
|
toHash = refKey.slice(1);
|
|
2903
2974
|
} else {
|
|
@@ -3143,6 +3214,18 @@ function escapeRegExp2(str) {
|
|
|
3143
3214
|
function encodePathParam(value) {
|
|
3144
3215
|
return String(value).split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
3145
3216
|
}
|
|
3217
|
+
function substituteRouteParams(pattern, params, encode = encodeURIComponent) {
|
|
3218
|
+
let result = pattern;
|
|
3219
|
+
for (const [key, value] of Object.entries(params)) {
|
|
3220
|
+
const escaped = escapeRegExp2(key);
|
|
3221
|
+
result = result.replace(
|
|
3222
|
+
new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
|
|
3223
|
+
encode(value)
|
|
3224
|
+
);
|
|
3225
|
+
result = result.replace(`*${key}`, encode(value));
|
|
3226
|
+
}
|
|
3227
|
+
return result;
|
|
3228
|
+
}
|
|
3146
3229
|
async function runWithConcurrency(items, concurrency, fn) {
|
|
3147
3230
|
const limit = Math.max(1, Math.min(concurrency, items.length));
|
|
3148
3231
|
if (limit <= 1) {
|
|
@@ -3215,18 +3298,8 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3215
3298
|
const getParamsReverse = (name, params) => {
|
|
3216
3299
|
const pattern = allRoutes[name];
|
|
3217
3300
|
if (!pattern) throw new Error(`Unknown route: "${name}"`);
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
for (const [key, value] of Object.entries(params)) {
|
|
3221
|
-
const escaped = escapeRegExp2(key);
|
|
3222
|
-
result = result.replace(
|
|
3223
|
-
new RegExp(`:${escaped}(\\([^)]*\\))?`),
|
|
3224
|
-
encodeURIComponent(value)
|
|
3225
|
-
);
|
|
3226
|
-
result = result.replace(`*${key}`, encodeURIComponent(value));
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
return result;
|
|
3301
|
+
if (!params) return pattern;
|
|
3302
|
+
return substituteRouteParams(pattern, params);
|
|
3230
3303
|
};
|
|
3231
3304
|
for (const { manifest } of allManifests) {
|
|
3232
3305
|
if (!manifest.prerenderRoutes) continue;
|
|
@@ -3234,15 +3307,17 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3234
3307
|
for (const routeName of manifest.prerenderRoutes) {
|
|
3235
3308
|
const pattern = manifest.routeManifest[routeName];
|
|
3236
3309
|
if (!pattern) continue;
|
|
3310
|
+
const def = defs[routeName];
|
|
3311
|
+
const isPassthroughRoute = !!def?.options?.passthrough;
|
|
3237
3312
|
const hasDynamic = pattern.includes(":") || pattern.includes("*");
|
|
3238
3313
|
if (!hasDynamic) {
|
|
3239
3314
|
entries.push({
|
|
3240
3315
|
urlPath: pattern.replace(/\/$/, "") || "/",
|
|
3241
3316
|
routeName,
|
|
3242
|
-
concurrency: 1
|
|
3317
|
+
concurrency: 1,
|
|
3318
|
+
isPassthroughRoute
|
|
3243
3319
|
});
|
|
3244
3320
|
} else {
|
|
3245
|
-
const def = defs[routeName];
|
|
3246
3321
|
if (def?.getParams) {
|
|
3247
3322
|
try {
|
|
3248
3323
|
const buildVars = {};
|
|
@@ -3257,18 +3332,11 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3257
3332
|
const concurrency = def.options?.concurrency ?? 1;
|
|
3258
3333
|
const hasBuildVars = Object.keys(buildVars).length > 0 || Object.getOwnPropertySymbols(buildVars).length > 0;
|
|
3259
3334
|
for (const params of paramsList) {
|
|
3260
|
-
let url =
|
|
3261
|
-
|
|
3262
|
-
params
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
const escaped = escapeRegExp2(key);
|
|
3266
|
-
url = url.replace(
|
|
3267
|
-
new RegExp(`:${escaped}(\\([^)]*\\))?`),
|
|
3268
|
-
encoded
|
|
3269
|
-
);
|
|
3270
|
-
url = url.replace(`*${key}`, encoded);
|
|
3271
|
-
}
|
|
3335
|
+
let url = substituteRouteParams(
|
|
3336
|
+
pattern,
|
|
3337
|
+
params,
|
|
3338
|
+
encodePathParam
|
|
3339
|
+
);
|
|
3272
3340
|
if (url.includes("*")) {
|
|
3273
3341
|
const wildcardValue = params["*"] ?? params.splat;
|
|
3274
3342
|
if (wildcardValue !== void 0) {
|
|
@@ -3279,7 +3347,8 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3279
3347
|
urlPath: url.replace(/\/$/, "") || "/",
|
|
3280
3348
|
routeName,
|
|
3281
3349
|
concurrency,
|
|
3282
|
-
...hasBuildVars ? { buildVars } : {}
|
|
3350
|
+
...hasBuildVars ? { buildVars } : {},
|
|
3351
|
+
isPassthroughRoute
|
|
3283
3352
|
});
|
|
3284
3353
|
}
|
|
3285
3354
|
} catch (err) {
|
|
@@ -3335,9 +3404,18 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3335
3404
|
const result = await routerInstance.matchForPrerender(
|
|
3336
3405
|
entry.urlPath,
|
|
3337
3406
|
{},
|
|
3338
|
-
entry.buildVars
|
|
3407
|
+
entry.buildVars,
|
|
3408
|
+
entry.isPassthroughRoute
|
|
3339
3409
|
);
|
|
3340
3410
|
if (!result) continue;
|
|
3411
|
+
if (result.passthrough) {
|
|
3412
|
+
const elapsed2 = (performance.now() - startUrl).toFixed(0);
|
|
3413
|
+
console.log(
|
|
3414
|
+
`[rsc-router] PASS ${entry.urlPath.padEnd(40)} (${elapsed2}ms) - live fallback`
|
|
3415
|
+
);
|
|
3416
|
+
doneCount++;
|
|
3417
|
+
break;
|
|
3418
|
+
}
|
|
3341
3419
|
const paramHash = hashParams(result.params || {});
|
|
3342
3420
|
collectedData[`${result.routeName}/${paramHash}`] = {
|
|
3343
3421
|
segments: result.segments,
|
|
@@ -3537,12 +3615,12 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3537
3615
|
}
|
|
3538
3616
|
const buildMod = await rscEnv.runner.import("@rangojs/router/build");
|
|
3539
3617
|
const generateManifestFull = buildMod.generateManifestFull;
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3618
|
+
const newMergedRouteManifest = {};
|
|
3619
|
+
const newMergedPrecomputedEntries = [];
|
|
3620
|
+
const newPerRouterManifests = [];
|
|
3621
|
+
const newPerRouterManifestDataMap = /* @__PURE__ */ new Map();
|
|
3622
|
+
const newPerRouterPrecomputedMap = /* @__PURE__ */ new Map();
|
|
3623
|
+
const newPerRouterTrieMap = /* @__PURE__ */ new Map();
|
|
3546
3624
|
let mergedRouteAncestry = {};
|
|
3547
3625
|
let mergedRouteTrailingSlash = {};
|
|
3548
3626
|
let routerMountIndex = 0;
|
|
@@ -3559,7 +3637,7 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3559
3637
|
(p) => !p.includes(":") && !p.includes("*")
|
|
3560
3638
|
).length;
|
|
3561
3639
|
const dynamicRoutes = routeCount - staticRoutes;
|
|
3562
|
-
Object.assign(
|
|
3640
|
+
Object.assign(newMergedRouteManifest, manifest.routeManifest);
|
|
3563
3641
|
let factoryOnlyPrefixes;
|
|
3564
3642
|
if (router.__sourceFile) {
|
|
3565
3643
|
const staticParsed = buildCombinedRouteMapForRouterFile(
|
|
@@ -3577,7 +3655,7 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3577
3655
|
}
|
|
3578
3656
|
if (factoryOnlyPrefixes.size === 0) factoryOnlyPrefixes = void 0;
|
|
3579
3657
|
}
|
|
3580
|
-
|
|
3658
|
+
newPerRouterManifests.push({
|
|
3581
3659
|
id,
|
|
3582
3660
|
routeManifest: manifest.routeManifest,
|
|
3583
3661
|
routeSearchSchemas: manifest.routeSearchSchemas,
|
|
@@ -3593,16 +3671,16 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3593
3671
|
flattenLeafEntries(
|
|
3594
3672
|
manifest.prefixTree,
|
|
3595
3673
|
manifest.routeManifest,
|
|
3596
|
-
|
|
3674
|
+
newMergedPrecomputedEntries
|
|
3597
3675
|
);
|
|
3598
|
-
|
|
3676
|
+
newPerRouterManifestDataMap.set(id, manifest.routeManifest);
|
|
3599
3677
|
const routerPrecomputed = [];
|
|
3600
3678
|
flattenLeafEntries(
|
|
3601
3679
|
manifest.prefixTree,
|
|
3602
3680
|
manifest.routeManifest,
|
|
3603
3681
|
routerPrecomputed
|
|
3604
3682
|
);
|
|
3605
|
-
|
|
3683
|
+
newPerRouterPrecomputedMap.set(id, routerPrecomputed);
|
|
3606
3684
|
console.log(
|
|
3607
3685
|
`[rsc-router] Router "${id}" -> ${routeCount} routes (${staticRoutes} static, ${dynamicRoutes} dynamic)`
|
|
3608
3686
|
);
|
|
@@ -3617,7 +3695,8 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3617
3695
|
);
|
|
3618
3696
|
}
|
|
3619
3697
|
}
|
|
3620
|
-
|
|
3698
|
+
let newMergedRouteTrie = null;
|
|
3699
|
+
if (Object.keys(newMergedRouteManifest).length > 0) {
|
|
3621
3700
|
const buildRouteTrie = buildMod.buildRouteTrie;
|
|
3622
3701
|
if (buildRouteTrie && mergedRouteAncestry) {
|
|
3623
3702
|
const routeToStaticPrefix = {};
|
|
@@ -3647,8 +3726,8 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3647
3726
|
Object.assign(mergedResponseTypeRoutes, manifest.responseTypeRoutes);
|
|
3648
3727
|
}
|
|
3649
3728
|
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3729
|
+
newMergedRouteTrie = buildRouteTrie(
|
|
3730
|
+
newMergedRouteManifest,
|
|
3652
3731
|
mergedRouteAncestry,
|
|
3653
3732
|
routeToStaticPrefix,
|
|
3654
3733
|
Object.keys(mergedRouteTrailingSlash).length > 0 ? mergedRouteTrailingSlash : void 0,
|
|
@@ -3675,10 +3754,17 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3675
3754
|
perRouterPassthroughNames && perRouterPassthroughNames.size > 0 ? perRouterPassthroughNames : void 0,
|
|
3676
3755
|
manifest.responseTypeRoutes && Object.keys(manifest.responseTypeRoutes).length > 0 ? manifest.responseTypeRoutes : void 0
|
|
3677
3756
|
);
|
|
3678
|
-
|
|
3757
|
+
newPerRouterTrieMap.set(id, perRouterTrie);
|
|
3679
3758
|
}
|
|
3680
3759
|
}
|
|
3681
3760
|
}
|
|
3761
|
+
state.mergedRouteManifest = newMergedRouteManifest;
|
|
3762
|
+
state.mergedPrecomputedEntries = newMergedPrecomputedEntries;
|
|
3763
|
+
state.perRouterManifests = newPerRouterManifests;
|
|
3764
|
+
state.perRouterManifestDataMap = newPerRouterManifestDataMap;
|
|
3765
|
+
state.perRouterPrecomputedMap = newPerRouterPrecomputedMap;
|
|
3766
|
+
state.perRouterTrieMap = newPerRouterTrieMap;
|
|
3767
|
+
state.mergedRouteTrie = newMergedRouteTrie;
|
|
3682
3768
|
await expandPrerenderRoutes(state, rscEnv, registry, allManifests);
|
|
3683
3769
|
await renderStaticHandlers(state, rscEnv, registry);
|
|
3684
3770
|
return serverMod;
|
|
@@ -3690,7 +3776,7 @@ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsS
|
|
|
3690
3776
|
function filterUserNamedRoutes(manifest) {
|
|
3691
3777
|
const filtered = {};
|
|
3692
3778
|
for (const [name, pattern] of Object.entries(manifest)) {
|
|
3693
|
-
if (!name
|
|
3779
|
+
if (!isAutoGeneratedRouteName(name)) {
|
|
3694
3780
|
filtered[name] = pattern;
|
|
3695
3781
|
}
|
|
3696
3782
|
}
|
|
@@ -3813,7 +3899,7 @@ function supplementGenFilesWithRuntimeRoutes(state) {
|
|
|
3813
3899
|
...staticParsed.searchSchemas
|
|
3814
3900
|
};
|
|
3815
3901
|
for (const [name, pattern] of Object.entries(routeManifest)) {
|
|
3816
|
-
if (name
|
|
3902
|
+
if (isAutoGeneratedRouteName(name)) continue;
|
|
3817
3903
|
const dotIdx = name.indexOf(".");
|
|
3818
3904
|
if (dotIdx <= 0) continue;
|
|
3819
3905
|
const prefix = name.substring(0, dotIdx + 1);
|
|
@@ -3858,7 +3944,7 @@ function generateRoutesManifestModule(state) {
|
|
|
3858
3944
|
const genPath = join4(
|
|
3859
3945
|
routerDir,
|
|
3860
3946
|
`${routerBasename}.named-routes.gen.js`
|
|
3861
|
-
);
|
|
3947
|
+
).replaceAll("\\", "/");
|
|
3862
3948
|
const varName = `_r${varIdx++}`;
|
|
3863
3949
|
genFileImports.push(
|
|
3864
3950
|
`import { NamedRoutes as ${varName} } from ${JSON.stringify(genPath)};`
|
|
@@ -3952,7 +4038,10 @@ function generatePerRouterModule(state, routerId) {
|
|
|
3952
4038
|
/\.(tsx?|jsx?)$/,
|
|
3953
4039
|
""
|
|
3954
4040
|
);
|
|
3955
|
-
const genPath = join4(
|
|
4041
|
+
const genPath = join4(
|
|
4042
|
+
routerDir,
|
|
4043
|
+
`${routerBasename}.named-routes.gen.js`
|
|
4044
|
+
).replaceAll("\\", "/");
|
|
3956
4045
|
lines.push(`import { NamedRoutes as _r } from ${JSON.stringify(genPath)};`);
|
|
3957
4046
|
lines.push(
|
|
3958
4047
|
`function __flat(r) { const o = {}; for (const [k, v] of Object.entries(r)) o[k] = typeof v === "string" ? v : v.path; return o; }`
|
|
@@ -4034,7 +4123,7 @@ function postprocessBundle(state) {
|
|
|
4034
4123
|
state.staticHandlerChunkInfo = null;
|
|
4035
4124
|
if (hasPrerenderData && existsSync5(rscEntryPath)) {
|
|
4036
4125
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4037
|
-
if (!rscCode.includes("
|
|
4126
|
+
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
4038
4127
|
try {
|
|
4039
4128
|
const assetsDir = resolve6(state.projectRoot, "dist/rsc/assets");
|
|
4040
4129
|
mkdirSync(assetsDir, { recursive: true });
|
|
@@ -4089,11 +4178,11 @@ globalThis.__PRERENDER_MANIFEST = __pm;
|
|
|
4089
4178
|
for (const [handlerId, { encoded, handles }] of Object.entries(
|
|
4090
4179
|
state.staticCollectedData
|
|
4091
4180
|
)) {
|
|
4092
|
-
const contentHash = createHash4("sha256").update(encoded).digest("hex").slice(0, 8);
|
|
4093
|
-
const assetFileName = `__st-${contentHash}.js`;
|
|
4094
|
-
const assetPath = resolve6(assetsDir, assetFileName);
|
|
4095
4181
|
const hasHandles = Object.keys(handles).length > 0;
|
|
4096
4182
|
const exportValue = hasHandles ? JSON.stringify({ encoded, handles }) : JSON.stringify(encoded);
|
|
4183
|
+
const contentHash = createHash4("sha256").update(exportValue).digest("hex").slice(0, 8);
|
|
4184
|
+
const assetFileName = `__st-${contentHash}.js`;
|
|
4185
|
+
const assetPath = resolve6(assetsDir, assetFileName);
|
|
4097
4186
|
const assetCode = `export default ${exportValue};
|
|
4098
4187
|
`;
|
|
4099
4188
|
writeFileSync3(assetPath, assetCode);
|
|
@@ -4293,34 +4382,10 @@ ${err.stack}`
|
|
|
4293
4382
|
if (serverMod?.setManifestReadyPromise) {
|
|
4294
4383
|
serverMod.setManifestReadyPromise(discoveryPromise);
|
|
4295
4384
|
}
|
|
4296
|
-
|
|
4297
|
-
mainRegistry = serverModAfterDiscovery?.RouterRegistry ?? null;
|
|
4385
|
+
await discoverRouters(s, rscEnv);
|
|
4298
4386
|
s.devServerOrigin = getDevServerOrigin();
|
|
4299
4387
|
writeRouteTypesFiles(s);
|
|
4300
|
-
|
|
4301
|
-
serverMod.setCachedManifest(s.mergedRouteManifest);
|
|
4302
|
-
}
|
|
4303
|
-
if (s.mergedPrecomputedEntries && s.mergedPrecomputedEntries.length > 0 && serverMod?.setPrecomputedEntries) {
|
|
4304
|
-
serverMod.setPrecomputedEntries(s.mergedPrecomputedEntries);
|
|
4305
|
-
}
|
|
4306
|
-
if (s.mergedRouteTrie && serverMod?.setRouteTrie) {
|
|
4307
|
-
serverMod.setRouteTrie(s.mergedRouteTrie);
|
|
4308
|
-
}
|
|
4309
|
-
if (serverMod?.setRouterManifest) {
|
|
4310
|
-
for (const [routerId, manifest] of s.perRouterManifestDataMap) {
|
|
4311
|
-
serverMod.setRouterManifest(routerId, manifest);
|
|
4312
|
-
}
|
|
4313
|
-
}
|
|
4314
|
-
if (serverMod?.setRouterTrie) {
|
|
4315
|
-
for (const [routerId, trie] of s.perRouterTrieMap) {
|
|
4316
|
-
serverMod.setRouterTrie(routerId, trie);
|
|
4317
|
-
}
|
|
4318
|
-
}
|
|
4319
|
-
if (serverMod?.setRouterPrecomputedEntries) {
|
|
4320
|
-
for (const [routerId, entries] of s.perRouterPrecomputedMap) {
|
|
4321
|
-
serverMod.setRouterPrecomputedEntries(routerId, entries);
|
|
4322
|
-
}
|
|
4323
|
-
}
|
|
4388
|
+
await propagateDiscoveryState(rscEnv);
|
|
4324
4389
|
} catch (err) {
|
|
4325
4390
|
console.warn(
|
|
4326
4391
|
`[rsc-router] Router discovery failed: ${err.message}
|
|
@@ -4334,6 +4399,38 @@ ${err.stack}`
|
|
|
4334
4399
|
setTimeout(() => discover().then(resolve8, resolve8), 0);
|
|
4335
4400
|
});
|
|
4336
4401
|
let mainRegistry = null;
|
|
4402
|
+
const propagateDiscoveryState = async (rscEnv) => {
|
|
4403
|
+
const serverMod = await rscEnv.runner.import("@rangojs/router/server");
|
|
4404
|
+
if (!serverMod) return;
|
|
4405
|
+
if (serverMod.clearAllRouterData) {
|
|
4406
|
+
serverMod.clearAllRouterData();
|
|
4407
|
+
}
|
|
4408
|
+
mainRegistry = serverMod.RouterRegistry ?? null;
|
|
4409
|
+
if (s.mergedRouteManifest && serverMod.setCachedManifest) {
|
|
4410
|
+
serverMod.setCachedManifest(s.mergedRouteManifest);
|
|
4411
|
+
}
|
|
4412
|
+
if (s.mergedPrecomputedEntries && s.mergedPrecomputedEntries.length > 0 && serverMod.setPrecomputedEntries) {
|
|
4413
|
+
serverMod.setPrecomputedEntries(s.mergedPrecomputedEntries);
|
|
4414
|
+
}
|
|
4415
|
+
if (s.mergedRouteTrie && serverMod.setRouteTrie) {
|
|
4416
|
+
serverMod.setRouteTrie(s.mergedRouteTrie);
|
|
4417
|
+
}
|
|
4418
|
+
if (serverMod.setRouterManifest) {
|
|
4419
|
+
for (const [routerId, manifest] of s.perRouterManifestDataMap) {
|
|
4420
|
+
serverMod.setRouterManifest(routerId, manifest);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
if (serverMod.setRouterTrie) {
|
|
4424
|
+
for (const [routerId, trie] of s.perRouterTrieMap) {
|
|
4425
|
+
serverMod.setRouterTrie(routerId, trie);
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
if (serverMod.setRouterPrecomputedEntries) {
|
|
4429
|
+
for (const [routerId, entries] of s.perRouterPrecomputedMap) {
|
|
4430
|
+
serverMod.setRouterPrecomputedEntries(routerId, entries);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
};
|
|
4337
4434
|
server.middlewares.use("/__rsc_prerender", async (req, res) => {
|
|
4338
4435
|
if (s.discoveryDone) await s.discoveryDone;
|
|
4339
4436
|
const url = new URL(req.url || "/", "http://localhost");
|
|
@@ -4356,11 +4453,20 @@ ${err.stack}`
|
|
|
4356
4453
|
return;
|
|
4357
4454
|
}
|
|
4358
4455
|
const wantIntercept = url.searchParams.get("intercept") === "1";
|
|
4456
|
+
const wantRouteName = url.searchParams.get("routeName");
|
|
4457
|
+
const wantPassthrough = url.searchParams.get("passthrough") === "1";
|
|
4359
4458
|
for (const [, routerInstance] of registry) {
|
|
4360
4459
|
if (!routerInstance.matchForPrerender) continue;
|
|
4361
4460
|
try {
|
|
4362
|
-
const result = await routerInstance.matchForPrerender(
|
|
4461
|
+
const result = await routerInstance.matchForPrerender(
|
|
4462
|
+
pathname,
|
|
4463
|
+
{},
|
|
4464
|
+
void 0,
|
|
4465
|
+
wantPassthrough
|
|
4466
|
+
);
|
|
4363
4467
|
if (!result) continue;
|
|
4468
|
+
if (result.passthrough) continue;
|
|
4469
|
+
if (wantRouteName && result.routeName !== wantRouteName) continue;
|
|
4364
4470
|
res.setHeader("content-type", "application/json");
|
|
4365
4471
|
let payload;
|
|
4366
4472
|
if (wantIntercept && result.interceptSegments?.length) {
|
|
@@ -4397,10 +4503,29 @@ ${err.stack}`
|
|
|
4397
4503
|
const maybeHandleGeneratedRouteFileMutation = (filePath) => {
|
|
4398
4504
|
if (!isGeneratedRouteFile(filePath)) return false;
|
|
4399
4505
|
if (consumeSelfGenWrite(s, filePath)) return true;
|
|
4506
|
+
const hasRunner = !!server.environments?.rsc?.runner;
|
|
4507
|
+
if (!hasRunner) return true;
|
|
4400
4508
|
regenerateGeneratedRouteFiles();
|
|
4401
4509
|
return true;
|
|
4402
4510
|
};
|
|
4403
4511
|
let routeChangeTimer;
|
|
4512
|
+
let runtimeRediscoveryInProgress = false;
|
|
4513
|
+
const refreshRuntimeDiscovery = async () => {
|
|
4514
|
+
const rscEnv = server.environments?.rsc;
|
|
4515
|
+
if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
|
|
4516
|
+
runtimeRediscoveryInProgress = true;
|
|
4517
|
+
try {
|
|
4518
|
+
await discoverRouters(s, rscEnv);
|
|
4519
|
+
writeRouteTypesFiles(s);
|
|
4520
|
+
await propagateDiscoveryState(rscEnv);
|
|
4521
|
+
} catch (err) {
|
|
4522
|
+
console.warn(
|
|
4523
|
+
`[rsc-router] Runtime re-discovery failed: ${err.message}`
|
|
4524
|
+
);
|
|
4525
|
+
} finally {
|
|
4526
|
+
runtimeRediscoveryInProgress = false;
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4404
4529
|
const scheduleRouteRegeneration = () => {
|
|
4405
4530
|
clearTimeout(routeChangeTimer);
|
|
4406
4531
|
routeChangeTimer = setTimeout(() => {
|
|
@@ -4415,6 +4540,13 @@ ${err.stack}`
|
|
|
4415
4540
|
`[rsc-router] Route regeneration error: ${err.message}`
|
|
4416
4541
|
);
|
|
4417
4542
|
}
|
|
4543
|
+
if (s.perRouterManifests.length > 0) {
|
|
4544
|
+
refreshRuntimeDiscovery().catch((err) => {
|
|
4545
|
+
console.warn(
|
|
4546
|
+
`[rsc-router] Runtime re-discovery error: ${err.message}`
|
|
4547
|
+
);
|
|
4548
|
+
});
|
|
4549
|
+
}
|
|
4418
4550
|
}, 100);
|
|
4419
4551
|
};
|
|
4420
4552
|
const handleRouteFileChange = (filePath) => {
|
|
@@ -4441,6 +4573,8 @@ ${err.stack}`
|
|
|
4441
4573
|
server.watcher.on("change", handleRouteFileChange);
|
|
4442
4574
|
server.watcher.on("unlink", (filePath) => {
|
|
4443
4575
|
if (!isGeneratedRouteFile(filePath)) return;
|
|
4576
|
+
const hasRunner = !!server.environments?.rsc?.runner;
|
|
4577
|
+
if (!hasRunner) return;
|
|
4444
4578
|
regenerateGeneratedRouteFiles();
|
|
4445
4579
|
});
|
|
4446
4580
|
}
|
|
@@ -4699,7 +4833,7 @@ async function rango(options) {
|
|
|
4699
4833
|
const candidates = findRouterFiles(root, filter);
|
|
4700
4834
|
if (candidates.length === 1) {
|
|
4701
4835
|
const abs = candidates[0];
|
|
4702
|
-
routerRef.path = abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs;
|
|
4836
|
+
routerRef.path = (abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs).replaceAll("\\", "/");
|
|
4703
4837
|
} else if (candidates.length > 1) {
|
|
4704
4838
|
const list = candidates.map(
|
|
4705
4839
|
(f) => " - " + (f.startsWith(root) ? f.slice(root.length + 1) : f)
|