@rangojs/router 0.5.2 → 0.6.0
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/bin/rango.js +343 -125
- package/dist/types/browser/react/use-router.d.ts +10 -3
- package/dist/types/browser/react/use-search-params.d.ts +57 -10
- package/dist/types/browser/types.d.ts +22 -0
- package/dist/types/build/merge-full-manifests.d.ts +3 -0
- package/dist/types/build/route-trie.d.ts +4 -73
- package/dist/types/build/route-types/per-module-writer.d.ts +6 -4
- package/dist/types/build/route-types/router-processing.d.ts +2 -3
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/taint.d.ts +12 -6
- package/dist/types/client-urls/client-root.d.ts +38 -0
- package/dist/types/client-urls/client-urls.d.ts +5 -0
- package/dist/types/client-urls/navigation.d.ts +38 -0
- package/dist/types/client-urls/revalidation-protocol.d.ts +25 -0
- package/dist/types/client-urls/server-projection.d.ts +62 -0
- package/dist/types/client-urls/types.d.ts +144 -0
- package/dist/types/client.d.ts +12 -4
- package/dist/types/client.rsc.d.ts +4 -1
- package/dist/types/decode-loader-results.d.ts +37 -0
- package/dist/types/errors.d.ts +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +27 -0
- package/dist/types/outlet-context.d.ts +12 -0
- package/dist/types/outlet-provider.d.ts +3 -1
- package/dist/types/redirect-origin.d.ts +4 -0
- package/dist/types/route-content-wrapper.d.ts +42 -1
- package/dist/types/route-definition/helpers-types.d.ts +13 -2
- package/dist/types/router/error-handling.d.ts +35 -1
- package/dist/types/router/intercept-resolution.d.ts +12 -0
- package/dist/types/router/loader-resolution.d.ts +24 -2
- package/dist/types/router/revalidation.d.ts +7 -0
- package/dist/types/router/route-trie-builder.d.ts +77 -0
- package/dist/types/router/router-interfaces.d.ts +20 -0
- package/dist/types/router/segment-resolution/helpers.d.ts +1 -1
- package/dist/types/router/trie-matching.d.ts +1 -1
- package/dist/types/rsc/manifest-init.d.ts +5 -5
- package/dist/types/rsc/shell-capture.d.ts +9 -0
- package/dist/types/rsc/shell-serve.d.ts +11 -0
- package/dist/types/rsc/types.d.ts +30 -0
- package/dist/types/segment-system.d.ts +2 -0
- package/dist/types/server/context.d.ts +10 -0
- package/dist/types/server/handle-store.d.ts +34 -3
- package/dist/types/server/request-context.d.ts +11 -1
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +22 -0
- package/dist/types/ssr/ssr-root.d.ts +10 -0
- package/dist/types/testing/dom.entry.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +16 -6
- package/dist/types/testing/run-loader.d.ts +9 -0
- package/dist/types/types/boundaries.d.ts +22 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/loader-types.d.ts +57 -5
- package/dist/types/types/segments.d.ts +7 -0
- package/dist/types/urls/path-helper-types.d.ts +10 -4
- package/dist/types/vite/discovery/client-urls-projection.d.ts +53 -0
- package/dist/types/vite/discovery/discover-routers.d.ts +1 -1
- package/dist/types/vite/discovery/state.d.ts +8 -1
- package/dist/vite/index.js +5313 -2365
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +39 -9
- package/skills/catalog.json +7 -1
- package/skills/client-urls/SKILL.md +338 -0
- package/skills/comparison/references/framework-comparison.md +23 -9
- package/skills/hooks/SKILL.md +2 -2
- package/skills/hooks/data.md +11 -2
- package/skills/hooks/handle-and-actions.md +7 -0
- package/skills/hooks/outlets.md +26 -5
- package/skills/hooks/urls.md +40 -3
- package/skills/loader/SKILL.md +132 -20
- package/skills/migrate-nextjs/SKILL.md +70 -10
- package/skills/migrate-react-router/SKILL.md +49 -13
- package/skills/migrate-react-router/component-migration.md +18 -13
- package/skills/migrate-react-router/data-and-actions.md +14 -3
- package/skills/migrate-react-router/route-mapping.md +15 -2
- package/skills/parallel/SKILL.md +32 -1
- package/skills/ppr/SKILL.md +16 -6
- package/skills/prerender/SKILL.md +8 -4
- package/skills/rango/SKILL.md +21 -17
- package/skills/react-compiler/SKILL.md +3 -3
- package/skills/route/SKILL.md +5 -2
- package/skills/router-setup/SKILL.md +16 -2
- package/skills/scripts/SKILL.md +16 -6
- package/skills/shell-manifest/SKILL.md +16 -7
- package/skills/testing/SKILL.md +2 -2
- package/skills/testing/client-components.md +6 -0
- package/skills/testing/handles.md +30 -8
- package/skills/testing/loader.md +51 -49
- package/skills/testing/middleware.md +1 -1
- package/skills/theme/SKILL.md +8 -5
- package/src/bin/rango.ts +7 -3
- package/src/browser/navigation-bridge.ts +6 -0
- package/src/browser/navigation-client.ts +5 -0
- package/src/browser/partial-update.ts +65 -13
- package/src/browser/react/use-router.ts +40 -11
- package/src/browser/react/use-search-params.ts +140 -17
- package/src/browser/rsc-router.tsx +59 -0
- package/src/browser/server-action-bridge.ts +26 -0
- package/src/browser/types.ts +22 -0
- package/src/build/merge-full-manifests.ts +161 -0
- package/src/build/route-trie.ts +9 -332
- package/src/build/route-types/include-resolution.ts +66 -11
- package/src/build/route-types/per-module-writer.ts +11 -6
- package/src/build/route-types/router-processing.ts +184 -153
- package/src/build/runtime-discovery.ts +23 -12
- package/src/cache/cache-exec-scope.ts +47 -0
- package/src/cache/cache-runtime.ts +24 -25
- package/src/cache/taint.ts +28 -9
- package/src/client-urls/client-root.tsx +168 -0
- package/src/client-urls/client-urls.ts +698 -0
- package/src/client-urls/navigation.ts +237 -0
- package/src/client-urls/revalidation-protocol.ts +56 -0
- package/src/client-urls/server-projection.ts +579 -0
- package/src/client-urls/types.ts +195 -0
- package/src/client.rsc.tsx +12 -0
- package/src/client.tsx +49 -6
- package/src/decode-loader-results.ts +113 -0
- package/src/errors.ts +14 -0
- package/src/handles/deferred-resolution.ts +14 -7
- package/src/index.ts +1 -0
- package/src/loader-redirect.tsx +64 -0
- package/src/outlet-context.ts +12 -0
- package/src/outlet-provider.tsx +15 -1
- package/src/redirect-origin.ts +29 -0
- package/src/route-content-wrapper.tsx +96 -3
- package/src/route-definition/dsl-helpers.ts +28 -3
- package/src/route-definition/helpers-types.ts +13 -0
- package/src/route-definition/redirect.ts +17 -18
- package/src/router/error-handling.ts +65 -11
- package/src/router/intercept-resolution.ts +29 -0
- package/src/router/loader-resolution.ts +261 -28
- package/src/router/match-result.ts +7 -0
- package/src/router/revalidation.ts +24 -11
- package/src/router/route-trie-builder.ts +334 -0
- package/src/router/router-interfaces.ts +38 -0
- package/src/router/segment-resolution/fresh.ts +47 -0
- package/src/router/segment-resolution/helpers.ts +9 -11
- package/src/router/segment-resolution/loader-cache.ts +14 -24
- package/src/router/segment-resolution/revalidation.ts +20 -1
- package/src/router/trie-matching.ts +3 -3
- package/src/router.ts +46 -1
- package/src/rsc/full-payload.ts +6 -0
- package/src/rsc/handler.ts +10 -7
- package/src/rsc/loader-fetch.ts +2 -2
- package/src/rsc/manifest-init.ts +28 -9
- package/src/rsc/rsc-rendering.ts +15 -1
- package/src/rsc/shell-capture.ts +12 -0
- package/src/rsc/shell-serve.ts +15 -2
- package/src/rsc/ssr-setup.ts +10 -1
- package/src/rsc/types.ts +31 -2
- package/src/segment-system.tsx +83 -26
- package/src/server/context.ts +10 -0
- package/src/server/cookie-store.ts +19 -19
- package/src/server/handle-store.ts +185 -48
- package/src/server/request-context.ts +30 -6
- package/src/server.ts +7 -0
- package/src/ssr/index.tsx +37 -2
- package/src/ssr/ssr-root.tsx +29 -2
- package/src/testing/dom.entry.ts +1 -1
- package/src/testing/render-route.tsx +22 -8
- package/src/testing/run-loader.ts +51 -13
- package/src/types/boundaries.ts +19 -0
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +60 -5
- package/src/types/segments.ts +7 -0
- package/src/urls/include-helper.ts +22 -4
- package/src/urls/path-helper-types.ts +14 -1
- package/src/use-loader.tsx +67 -6
- package/src/vite/discovery/client-urls-projection.ts +322 -0
- package/src/vite/discovery/discover-routers.ts +43 -17
- package/src/vite/discovery/state.ts +11 -1
- package/src/vite/discovery/virtual-module-codegen.ts +20 -0
- package/src/vite/plugins/virtual-entries.ts +12 -3
- package/src/vite/router-discovery.ts +163 -12
package/dist/bin/rango.js
CHANGED
|
@@ -274,6 +274,10 @@ function readSourceMemoized(memo, realPath) {
|
|
|
274
274
|
if (memo) memo.files.set(realPath, source);
|
|
275
275
|
return source;
|
|
276
276
|
}
|
|
277
|
+
function isUrlsDefinitionCall(node) {
|
|
278
|
+
const callee = node.expression;
|
|
279
|
+
return ts3.isIdentifier(callee) && (callee.text === "urls" || callee.text === "clientUrls");
|
|
280
|
+
}
|
|
277
281
|
function extractNamePrefixFromInclude(node) {
|
|
278
282
|
if (node.arguments.length >= 3) {
|
|
279
283
|
const thirdArg = node.arguments[2];
|
|
@@ -366,11 +370,8 @@ function resolveDefaultExportTarget(code, sourceFile) {
|
|
|
366
370
|
const expr = node.expression;
|
|
367
371
|
if (ts3.isIdentifier(expr)) {
|
|
368
372
|
result = { variableName: expr.text };
|
|
369
|
-
} else if (ts3.isCallExpression(expr)) {
|
|
370
|
-
|
|
371
|
-
if (ts3.isIdentifier(callee) && callee.text === "urls") {
|
|
372
|
-
result = { inlineBlock: expr.getText(sourceFile) };
|
|
373
|
-
}
|
|
373
|
+
} else if (ts3.isCallExpression(expr) && isUrlsDefinitionCall(expr)) {
|
|
374
|
+
result = { inlineBlock: expr.getText(sourceFile) };
|
|
374
375
|
}
|
|
375
376
|
return;
|
|
376
377
|
}
|
|
@@ -441,6 +442,13 @@ function extractIncludesWithDiagnostics(code, sourceFileArg) {
|
|
|
441
442
|
return { resolved, unresolvable };
|
|
442
443
|
}
|
|
443
444
|
function resolveImportedVariable(code, localName) {
|
|
445
|
+
const defaultImportRegex = /import\s+([\w$]+)\s*(?:,\s*\{[^}]*\})?\s+from\s*["']([^"']+)["']/g;
|
|
446
|
+
let defaultMatch;
|
|
447
|
+
while ((defaultMatch = defaultImportRegex.exec(code)) !== null) {
|
|
448
|
+
if (defaultMatch[1] === localName) {
|
|
449
|
+
return { specifier: defaultMatch[2], exportedName: "default" };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
444
452
|
const importRegex = /import\s*(?:[\w$]+\s*,\s*)?\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
|
|
445
453
|
let match;
|
|
446
454
|
while ((match = importRegex.exec(code)) !== null) {
|
|
@@ -490,8 +498,7 @@ function extractUrlsBlockForVariable(code, varName, sourceFileArg) {
|
|
|
490
498
|
function visit(node) {
|
|
491
499
|
if (result) return;
|
|
492
500
|
if (ts3.isVariableDeclaration(node) && ts3.isIdentifier(node.name) && node.name.text === varName && node.initializer && ts3.isCallExpression(node.initializer)) {
|
|
493
|
-
|
|
494
|
-
if (ts3.isIdentifier(callee) && callee.text === "urls") {
|
|
501
|
+
if (isUrlsDefinitionCall(node.initializer)) {
|
|
495
502
|
result = node.initializer.getText(sourceFile);
|
|
496
503
|
return;
|
|
497
504
|
}
|
|
@@ -707,6 +714,48 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
|
|
|
707
714
|
let block;
|
|
708
715
|
if (inlineBlock) {
|
|
709
716
|
block = inlineBlock;
|
|
717
|
+
} else if (variableName === "default") {
|
|
718
|
+
const defaultTarget = resolveDefaultExportTarget(
|
|
719
|
+
source,
|
|
720
|
+
parseBlock(memo, source)
|
|
721
|
+
);
|
|
722
|
+
if (!defaultTarget) {
|
|
723
|
+
visited.delete(key);
|
|
724
|
+
return { routes: {}, searchSchemas: {} };
|
|
725
|
+
}
|
|
726
|
+
if (defaultTarget.inlineBlock) {
|
|
727
|
+
block = defaultTarget.inlineBlock;
|
|
728
|
+
} else {
|
|
729
|
+
const targetName = defaultTarget.variableName;
|
|
730
|
+
const imported = resolveImportedVariable(source, targetName);
|
|
731
|
+
if (imported) {
|
|
732
|
+
const targetFile = resolveImportPath(imported.specifier, realPath);
|
|
733
|
+
if (!targetFile) {
|
|
734
|
+
visited.delete(key);
|
|
735
|
+
return { routes: {}, searchSchemas: {} };
|
|
736
|
+
}
|
|
737
|
+
const result = buildCombinedRouteMapWithSearch(
|
|
738
|
+
targetFile,
|
|
739
|
+
imported.exportedName,
|
|
740
|
+
visited,
|
|
741
|
+
diagnosticsOut,
|
|
742
|
+
void 0,
|
|
743
|
+
memo
|
|
744
|
+
);
|
|
745
|
+
visited.delete(key);
|
|
746
|
+
return result;
|
|
747
|
+
}
|
|
748
|
+
const extracted = extractUrlsBlockForVariable(
|
|
749
|
+
source,
|
|
750
|
+
targetName,
|
|
751
|
+
parseBlock(memo, source)
|
|
752
|
+
);
|
|
753
|
+
if (!extracted) {
|
|
754
|
+
visited.delete(key);
|
|
755
|
+
return { routes: {}, searchSchemas: {} };
|
|
756
|
+
}
|
|
757
|
+
block = extracted;
|
|
758
|
+
}
|
|
710
759
|
} else if (variableName) {
|
|
711
760
|
const extracted = extractUrlsBlockForVariable(
|
|
712
761
|
source,
|
|
@@ -754,7 +803,7 @@ function findUrlsVariableNames(code) {
|
|
|
754
803
|
function visit(node) {
|
|
755
804
|
if (ts4.isVariableDeclaration(node) && ts4.isIdentifier(node.name) && node.initializer && ts4.isCallExpression(node.initializer)) {
|
|
756
805
|
const callee = node.initializer.expression;
|
|
757
|
-
if (ts4.isIdentifier(callee) && callee.text === "urls") {
|
|
806
|
+
if (ts4.isIdentifier(callee) && (callee.text === "urls" || callee.text === "clientUrls")) {
|
|
758
807
|
names.push(node.name.text);
|
|
759
808
|
}
|
|
760
809
|
}
|
|
@@ -766,7 +815,7 @@ function findUrlsVariableNames(code) {
|
|
|
766
815
|
function writePerModuleRouteTypesForFile(filePath) {
|
|
767
816
|
try {
|
|
768
817
|
const source = readFileSync2(filePath, "utf-8");
|
|
769
|
-
if (!source.includes("urls(")) return;
|
|
818
|
+
if (!source.includes("urls(") && !source.includes("clientUrls(")) return;
|
|
770
819
|
const varNames = findUrlsVariableNames(source);
|
|
771
820
|
let routes;
|
|
772
821
|
if (varNames.length > 0) {
|
|
@@ -1057,7 +1106,33 @@ Router root: ${conflict.ancestor}
|
|
|
1057
1106
|
Nested router: ${conflict.nested}
|
|
1058
1107
|
Move the nested router into a sibling directory or configure it as a separate app root.`;
|
|
1059
1108
|
}
|
|
1060
|
-
function
|
|
1109
|
+
function isCreateRouterCall(node) {
|
|
1110
|
+
if (!ts5.isCallExpression(node)) return false;
|
|
1111
|
+
const callee = node.expression;
|
|
1112
|
+
return ts5.isIdentifier(callee) && callee.text === "createRouter";
|
|
1113
|
+
}
|
|
1114
|
+
function extractUrlsArgument(node, sourceFile) {
|
|
1115
|
+
if (ts5.isIdentifier(node)) {
|
|
1116
|
+
return { kind: "variable", name: node.text };
|
|
1117
|
+
}
|
|
1118
|
+
if (ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)) {
|
|
1119
|
+
return { kind: "inline", block: node.getText(sourceFile) };
|
|
1120
|
+
}
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
function collectCreateRouterCallChain(endpoint) {
|
|
1124
|
+
const calls = [];
|
|
1125
|
+
let current = endpoint;
|
|
1126
|
+
while (true) {
|
|
1127
|
+
calls.push(current);
|
|
1128
|
+
if (isCreateRouterCall(current)) return calls.reverse();
|
|
1129
|
+
if (!ts5.isPropertyAccessExpression(current.expression)) return null;
|
|
1130
|
+
const receiver = current.expression.expression;
|
|
1131
|
+
if (!ts5.isCallExpression(receiver)) return null;
|
|
1132
|
+
current = receiver;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
function extractAllUrlsFromRouter(code) {
|
|
1061
1136
|
const sourceFile = ts5.createSourceFile(
|
|
1062
1137
|
"router.tsx",
|
|
1063
1138
|
code,
|
|
@@ -1066,58 +1141,45 @@ function extractUrlsFromRouter(code) {
|
|
|
1066
1141
|
ts5.ScriptKind.TSX
|
|
1067
1142
|
);
|
|
1068
1143
|
let result = null;
|
|
1069
|
-
function isCreateRouterCall(node) {
|
|
1070
|
-
if (!ts5.isCallExpression(node)) return false;
|
|
1071
|
-
const callee = node.expression;
|
|
1072
|
-
return ts5.isIdentifier(callee) && callee.text === "createRouter";
|
|
1073
|
-
}
|
|
1074
|
-
function isInlineBuilder(node) {
|
|
1075
|
-
return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
|
|
1076
|
-
}
|
|
1077
|
-
function isRoutesOnCreateRouter(node) {
|
|
1078
|
-
if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
|
|
1079
|
-
return false;
|
|
1080
|
-
let inner = node.expression.expression;
|
|
1081
|
-
while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
|
|
1082
|
-
inner = inner.expression.expression;
|
|
1083
|
-
}
|
|
1084
|
-
return isCreateRouterCall(inner);
|
|
1085
|
-
}
|
|
1086
1144
|
function visit(node) {
|
|
1087
1145
|
if (result) return;
|
|
1088
|
-
if (ts5.isCallExpression(node)
|
|
1089
|
-
const
|
|
1090
|
-
if (
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
if (isCreateRouterCall(node)) {
|
|
1098
|
-
const callExpr = node;
|
|
1099
|
-
for (const callArg of callExpr.arguments) {
|
|
1100
|
-
if (ts5.isObjectLiteralExpression(callArg)) {
|
|
1146
|
+
if (ts5.isCallExpression(node)) {
|
|
1147
|
+
const chain = collectCreateRouterCallChain(node);
|
|
1148
|
+
if (chain) {
|
|
1149
|
+
const extracted = [];
|
|
1150
|
+
const createRouterCall = chain[0];
|
|
1151
|
+
for (const callArg of createRouterCall.arguments) {
|
|
1152
|
+
if (!ts5.isObjectLiteralExpression(callArg)) continue;
|
|
1101
1153
|
for (const prop of callArg.properties) {
|
|
1102
|
-
if (ts5.isPropertyAssignment(prop)
|
|
1103
|
-
|
|
1104
|
-
result = { kind: "variable", name: prop.initializer.text };
|
|
1105
|
-
} else if (isInlineBuilder(prop.initializer)) {
|
|
1106
|
-
result = {
|
|
1107
|
-
kind: "inline",
|
|
1108
|
-
block: prop.initializer.getText(sourceFile)
|
|
1109
|
-
};
|
|
1110
|
-
}
|
|
1111
|
-
return;
|
|
1154
|
+
if (!ts5.isPropertyAssignment(prop) || !ts5.isIdentifier(prop.name) || prop.name.text !== "urls") {
|
|
1155
|
+
continue;
|
|
1112
1156
|
}
|
|
1157
|
+
const urls = extractUrlsArgument(prop.initializer, sourceFile);
|
|
1158
|
+
if (urls) extracted.push(urls);
|
|
1159
|
+
break;
|
|
1113
1160
|
}
|
|
1114
1161
|
}
|
|
1162
|
+
for (const call of chain.slice(1)) {
|
|
1163
|
+
if (!ts5.isPropertyAccessExpression(call.expression) || call.expression.name.text !== "routes" || call.arguments.length === 0) {
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
const urls = extractUrlsArgument(call.arguments[0], sourceFile);
|
|
1167
|
+
if (urls) extracted.push(urls);
|
|
1168
|
+
}
|
|
1169
|
+
if (extracted.length > 0) {
|
|
1170
|
+
result = extracted;
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1115
1173
|
}
|
|
1116
1174
|
}
|
|
1117
1175
|
ts5.forEachChild(node, visit);
|
|
1118
1176
|
}
|
|
1119
1177
|
visit(sourceFile);
|
|
1120
|
-
return result;
|
|
1178
|
+
return result ?? [];
|
|
1179
|
+
}
|
|
1180
|
+
function extractUrlsFromRouter(code) {
|
|
1181
|
+
const extractions = extractAllUrlsFromRouter(code);
|
|
1182
|
+
return extractions[extractions.length - 1] ?? null;
|
|
1121
1183
|
}
|
|
1122
1184
|
function extractBasenameFromRouter(code) {
|
|
1123
1185
|
const sourceFile = ts5.createSourceFile(
|
|
@@ -1160,6 +1222,59 @@ function applyBasenameToRoutes(result, basename) {
|
|
|
1160
1222
|
}
|
|
1161
1223
|
return { routes: prefixed, searchSchemas: result.searchSchemas };
|
|
1162
1224
|
}
|
|
1225
|
+
function resolveRouterUrlsExtraction(routerFilePath, routerSource, extraction, diagnosticsOut, memo) {
|
|
1226
|
+
if (extraction.kind === "inline") {
|
|
1227
|
+
return buildCombinedRouteMapWithSearch(
|
|
1228
|
+
routerFilePath,
|
|
1229
|
+
void 0,
|
|
1230
|
+
/* @__PURE__ */ new Set(),
|
|
1231
|
+
diagnosticsOut,
|
|
1232
|
+
extraction.block,
|
|
1233
|
+
memo
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
1237
|
+
if (imported) {
|
|
1238
|
+
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
1239
|
+
if (!targetFile) {
|
|
1240
|
+
diagnosticsOut?.push({
|
|
1241
|
+
pathPrefix: "/",
|
|
1242
|
+
namePrefix: null,
|
|
1243
|
+
reason: "file-not-found",
|
|
1244
|
+
sourceFile: routerFilePath,
|
|
1245
|
+
detail: `import "${imported.specifier}" resolved to no file`
|
|
1246
|
+
});
|
|
1247
|
+
return { routes: {}, searchSchemas: {} };
|
|
1248
|
+
}
|
|
1249
|
+
return buildCombinedRouteMapWithSearch(
|
|
1250
|
+
targetFile,
|
|
1251
|
+
imported.exportedName,
|
|
1252
|
+
/* @__PURE__ */ new Set(),
|
|
1253
|
+
diagnosticsOut,
|
|
1254
|
+
void 0,
|
|
1255
|
+
memo
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
return buildCombinedRouteMapWithSearch(
|
|
1259
|
+
routerFilePath,
|
|
1260
|
+
extraction.name,
|
|
1261
|
+
/* @__PURE__ */ new Set(),
|
|
1262
|
+
diagnosticsOut,
|
|
1263
|
+
void 0,
|
|
1264
|
+
memo
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
function mergeRouteMaps(target, source) {
|
|
1268
|
+
for (const [name, pattern] of Object.entries(source.routes)) {
|
|
1269
|
+
target.routes[name] = pattern;
|
|
1270
|
+
const search = source.searchSchemas[name];
|
|
1271
|
+
if (search) {
|
|
1272
|
+
target.searchSchemas[name] = search;
|
|
1273
|
+
} else {
|
|
1274
|
+
delete target.searchSchemas[name];
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1163
1278
|
function genFileTsPath(sourceFile) {
|
|
1164
1279
|
const base = pathBasename(sourceFile).replace(/\.(tsx?|jsx?)$/, "");
|
|
1165
1280
|
return join2(dirname2(sourceFile), `${base}.named-routes.gen.ts`);
|
|
@@ -1186,38 +1301,28 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
|
|
|
1186
1301
|
} catch {
|
|
1187
1302
|
return { routes: {}, searchSchemas: {} };
|
|
1188
1303
|
}
|
|
1189
|
-
const
|
|
1190
|
-
if (
|
|
1304
|
+
const extractions = extractAllUrlsFromRouter(routerSource);
|
|
1305
|
+
if (extractions.length === 0) {
|
|
1191
1306
|
return { routes: {}, searchSchemas: {} };
|
|
1192
1307
|
}
|
|
1193
1308
|
const rawBasename = extractBasenameFromRouter(routerSource);
|
|
1194
1309
|
const basename = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1310
|
+
const result = { routes: {}, searchSchemas: {} };
|
|
1311
|
+
const memo = createScanMemo();
|
|
1312
|
+
for (const extraction of extractions) {
|
|
1313
|
+
mergeRouteMaps(
|
|
1314
|
+
result,
|
|
1315
|
+
resolveRouterUrlsExtraction(
|
|
1316
|
+
routerFilePath,
|
|
1317
|
+
routerSource,
|
|
1318
|
+
extraction,
|
|
1319
|
+
void 0,
|
|
1320
|
+
memo
|
|
1321
|
+
)
|
|
1203
1322
|
);
|
|
1204
|
-
} else {
|
|
1205
|
-
const imported = resolveImportedVariable(routerSource, extraction.name);
|
|
1206
|
-
if (imported) {
|
|
1207
|
-
const targetFile = resolveImportPath(imported.specifier, routerFilePath);
|
|
1208
|
-
if (!targetFile) {
|
|
1209
|
-
return { routes: {}, searchSchemas: {} };
|
|
1210
|
-
}
|
|
1211
|
-
result = buildCombinedRouteMapWithSearch(
|
|
1212
|
-
targetFile,
|
|
1213
|
-
imported.exportedName
|
|
1214
|
-
);
|
|
1215
|
-
} else {
|
|
1216
|
-
result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
|
|
1217
|
-
}
|
|
1218
1323
|
}
|
|
1219
1324
|
if (basename) {
|
|
1220
|
-
|
|
1325
|
+
return applyBasenameToRoutes(result, basename);
|
|
1221
1326
|
}
|
|
1222
1327
|
return result;
|
|
1223
1328
|
}
|
|
@@ -1229,47 +1334,19 @@ function detectUnresolvableIncludes(routerFilePath) {
|
|
|
1229
1334
|
} catch {
|
|
1230
1335
|
return [];
|
|
1231
1336
|
}
|
|
1232
|
-
const
|
|
1233
|
-
if (
|
|
1337
|
+
const extractions = extractAllUrlsFromRouter(source);
|
|
1338
|
+
if (extractions.length === 0) return [];
|
|
1234
1339
|
const diagnostics = [];
|
|
1235
|
-
|
|
1236
|
-
|
|
1340
|
+
const memo = createScanMemo();
|
|
1341
|
+
for (const extraction of extractions) {
|
|
1342
|
+
resolveRouterUrlsExtraction(
|
|
1237
1343
|
realPath,
|
|
1238
|
-
|
|
1239
|
-
|
|
1344
|
+
source,
|
|
1345
|
+
extraction,
|
|
1240
1346
|
diagnostics,
|
|
1241
|
-
|
|
1347
|
+
memo
|
|
1242
1348
|
);
|
|
1243
|
-
return diagnostics;
|
|
1244
|
-
}
|
|
1245
|
-
const imported = resolveImportedVariable(source, extraction.name);
|
|
1246
|
-
let targetFile;
|
|
1247
|
-
let exportedName;
|
|
1248
|
-
if (imported) {
|
|
1249
|
-
const resolved = resolveImportPath(imported.specifier, realPath);
|
|
1250
|
-
if (!resolved) {
|
|
1251
|
-
return [
|
|
1252
|
-
{
|
|
1253
|
-
pathPrefix: "/",
|
|
1254
|
-
namePrefix: null,
|
|
1255
|
-
reason: "file-not-found",
|
|
1256
|
-
sourceFile: realPath,
|
|
1257
|
-
detail: `import "${imported.specifier}" resolved to no file`
|
|
1258
|
-
}
|
|
1259
|
-
];
|
|
1260
|
-
}
|
|
1261
|
-
targetFile = resolved;
|
|
1262
|
-
exportedName = imported.exportedName;
|
|
1263
|
-
} else {
|
|
1264
|
-
targetFile = realPath;
|
|
1265
|
-
exportedName = extraction.name;
|
|
1266
1349
|
}
|
|
1267
|
-
buildCombinedRouteMapWithSearch(
|
|
1268
|
-
targetFile,
|
|
1269
|
-
exportedName,
|
|
1270
|
-
/* @__PURE__ */ new Set(),
|
|
1271
|
-
diagnostics
|
|
1272
|
-
);
|
|
1273
1350
|
return diagnostics;
|
|
1274
1351
|
}
|
|
1275
1352
|
function detectUnresolvableIncludesForUrlsFile(filePath) {
|
|
@@ -1390,6 +1467,138 @@ var init_generate_route_types = __esm({
|
|
|
1390
1467
|
}
|
|
1391
1468
|
});
|
|
1392
1469
|
|
|
1470
|
+
// src/build/merge-full-manifests.ts
|
|
1471
|
+
function clonePrefixNode(node) {
|
|
1472
|
+
return {
|
|
1473
|
+
staticPrefix: node.staticPrefix,
|
|
1474
|
+
fullPrefix: node.fullPrefix,
|
|
1475
|
+
...node.namePrefix === void 0 ? {} : { namePrefix: node.namePrefix },
|
|
1476
|
+
children: clonePrefixTree(node.children),
|
|
1477
|
+
routes: [...node.routes]
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
function clonePrefixTree(tree) {
|
|
1481
|
+
return Object.fromEntries(
|
|
1482
|
+
Object.entries(tree).map(([prefix, node]) => [
|
|
1483
|
+
prefix,
|
|
1484
|
+
clonePrefixNode(node)
|
|
1485
|
+
])
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
function removeRoutesFromPrefixTree(tree, routeNames) {
|
|
1489
|
+
return Object.fromEntries(
|
|
1490
|
+
Object.entries(tree).map(([prefix, node]) => [
|
|
1491
|
+
prefix,
|
|
1492
|
+
{
|
|
1493
|
+
staticPrefix: node.staticPrefix,
|
|
1494
|
+
fullPrefix: node.fullPrefix,
|
|
1495
|
+
...node.namePrefix === void 0 ? {} : { namePrefix: node.namePrefix },
|
|
1496
|
+
children: removeRoutesFromPrefixTree(node.children, routeNames),
|
|
1497
|
+
routes: node.routes.filter((name) => !routeNames.has(name))
|
|
1498
|
+
}
|
|
1499
|
+
])
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
function mergePrefixTrees(earlier, later) {
|
|
1503
|
+
const merged = clonePrefixTree(earlier);
|
|
1504
|
+
for (const [prefix, laterNode] of Object.entries(later)) {
|
|
1505
|
+
const earlierNode = merged[prefix];
|
|
1506
|
+
if (!earlierNode) {
|
|
1507
|
+
merged[prefix] = clonePrefixNode(laterNode);
|
|
1508
|
+
continue;
|
|
1509
|
+
}
|
|
1510
|
+
merged[prefix] = {
|
|
1511
|
+
staticPrefix: laterNode.staticPrefix,
|
|
1512
|
+
fullPrefix: laterNode.fullPrefix,
|
|
1513
|
+
...laterNode.namePrefix === void 0 ? {} : { namePrefix: laterNode.namePrefix },
|
|
1514
|
+
children: mergePrefixTrees(earlierNode.children, laterNode.children),
|
|
1515
|
+
routes: [.../* @__PURE__ */ new Set([...earlierNode.routes, ...laterNode.routes])]
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
return merged;
|
|
1519
|
+
}
|
|
1520
|
+
function mergeRouteRecord(earlier, later, overwrittenRoutes, cloneValue) {
|
|
1521
|
+
const merged = {};
|
|
1522
|
+
for (const [name, value] of Object.entries(earlier ?? {})) {
|
|
1523
|
+
if (!overwrittenRoutes.has(name)) merged[name] = cloneValue(value);
|
|
1524
|
+
}
|
|
1525
|
+
for (const [name, value] of Object.entries(later ?? {})) {
|
|
1526
|
+
merged[name] = cloneValue(value);
|
|
1527
|
+
}
|
|
1528
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
1529
|
+
}
|
|
1530
|
+
function mergeRouteList(earlier, later, overwrittenRoutes) {
|
|
1531
|
+
const merged = [
|
|
1532
|
+
...(earlier ?? []).filter((name) => !overwrittenRoutes.has(name)),
|
|
1533
|
+
...later ?? []
|
|
1534
|
+
];
|
|
1535
|
+
const unique = [...new Set(merged)];
|
|
1536
|
+
return unique.length > 0 ? unique : void 0;
|
|
1537
|
+
}
|
|
1538
|
+
function mergeFullManifests(manifests) {
|
|
1539
|
+
let merged = {
|
|
1540
|
+
prefixTree: {},
|
|
1541
|
+
routeManifest: {}
|
|
1542
|
+
};
|
|
1543
|
+
for (const manifest of manifests) {
|
|
1544
|
+
const overwrittenRoutes = new Set(Object.keys(manifest.routeManifest));
|
|
1545
|
+
const prefixTreeWithoutOverwrites = removeRoutesFromPrefixTree(
|
|
1546
|
+
merged.prefixTree,
|
|
1547
|
+
overwrittenRoutes
|
|
1548
|
+
);
|
|
1549
|
+
merged = {
|
|
1550
|
+
prefixTree: mergePrefixTrees(
|
|
1551
|
+
prefixTreeWithoutOverwrites,
|
|
1552
|
+
manifest.prefixTree
|
|
1553
|
+
),
|
|
1554
|
+
routeManifest: {
|
|
1555
|
+
...merged.routeManifest,
|
|
1556
|
+
...manifest.routeManifest
|
|
1557
|
+
},
|
|
1558
|
+
routeTrailingSlash: mergeRouteRecord(
|
|
1559
|
+
merged.routeTrailingSlash,
|
|
1560
|
+
manifest.routeTrailingSlash,
|
|
1561
|
+
overwrittenRoutes,
|
|
1562
|
+
(value) => value
|
|
1563
|
+
),
|
|
1564
|
+
prerenderRoutes: mergeRouteList(
|
|
1565
|
+
merged.prerenderRoutes,
|
|
1566
|
+
manifest.prerenderRoutes,
|
|
1567
|
+
overwrittenRoutes
|
|
1568
|
+
),
|
|
1569
|
+
passthroughRoutes: mergeRouteList(
|
|
1570
|
+
merged.passthroughRoutes,
|
|
1571
|
+
manifest.passthroughRoutes,
|
|
1572
|
+
overwrittenRoutes
|
|
1573
|
+
),
|
|
1574
|
+
responseTypeRoutes: mergeRouteRecord(
|
|
1575
|
+
merged.responseTypeRoutes,
|
|
1576
|
+
manifest.responseTypeRoutes,
|
|
1577
|
+
overwrittenRoutes,
|
|
1578
|
+
(value) => value
|
|
1579
|
+
),
|
|
1580
|
+
routeSearchSchemas: mergeRouteRecord(
|
|
1581
|
+
merged.routeSearchSchemas,
|
|
1582
|
+
manifest.routeSearchSchemas,
|
|
1583
|
+
overwrittenRoutes,
|
|
1584
|
+
(value) => ({ ...value })
|
|
1585
|
+
),
|
|
1586
|
+
_prerenderDefs: mergeRouteRecord(
|
|
1587
|
+
merged._prerenderDefs,
|
|
1588
|
+
manifest._prerenderDefs,
|
|
1589
|
+
overwrittenRoutes,
|
|
1590
|
+
(value) => value
|
|
1591
|
+
)
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
return merged;
|
|
1595
|
+
}
|
|
1596
|
+
var init_merge_full_manifests = __esm({
|
|
1597
|
+
"src/build/merge-full-manifests.ts"() {
|
|
1598
|
+
"use strict";
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1393
1602
|
// src/vite/plugins/virtual-entries.ts
|
|
1394
1603
|
function getVirtualVersionContent(version) {
|
|
1395
1604
|
return `export const VERSION = ${JSON.stringify(version)};`;
|
|
@@ -1798,20 +2007,28 @@ async function discoverAndWriteRouteTypes(opts) {
|
|
|
1798
2007
|
);
|
|
1799
2008
|
}
|
|
1800
2009
|
const buildMod = await rscEnv.runner.import("@rangojs/router/build");
|
|
1801
|
-
const
|
|
1802
|
-
if (!
|
|
1803
|
-
throw new Error(
|
|
2010
|
+
const generateManifestFull = buildMod.generateManifestFull;
|
|
2011
|
+
if (!generateManifestFull) {
|
|
2012
|
+
throw new Error(
|
|
2013
|
+
"generateManifestFull not found in @rangojs/router/build"
|
|
2014
|
+
);
|
|
1804
2015
|
}
|
|
1805
2016
|
const outputFiles = [];
|
|
1806
2017
|
let totalRouteCount = 0;
|
|
1807
|
-
let
|
|
2018
|
+
let globalMountIndex = 0;
|
|
2019
|
+
let routerCount = 0;
|
|
1808
2020
|
for (const [id, router] of registry) {
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2021
|
+
const mounts = router.__urlpatternMounts ?? (router.urlpatterns ? [{ patterns: router.urlpatterns }] : []);
|
|
2022
|
+
if (mounts.length === 0) continue;
|
|
2023
|
+
const mountManifests = [];
|
|
2024
|
+
for (const mount of mounts) {
|
|
2025
|
+
mountManifests.push(
|
|
2026
|
+
await generateManifestFull(mount.patterns, globalMountIndex)
|
|
2027
|
+
);
|
|
2028
|
+
globalMountIndex++;
|
|
2029
|
+
}
|
|
2030
|
+
const manifest = mergeFullManifests(mountManifests);
|
|
2031
|
+
routerCount++;
|
|
1815
2032
|
const rawManifest = manifest.routeManifest;
|
|
1816
2033
|
const routeManifest = {};
|
|
1817
2034
|
for (const [name, pattern] of Object.entries(rawManifest)) {
|
|
@@ -1856,7 +2073,7 @@ Set an explicit \`id\` on createRouter() or check the call site.`
|
|
|
1856
2073
|
);
|
|
1857
2074
|
}
|
|
1858
2075
|
return {
|
|
1859
|
-
routerCount
|
|
2076
|
+
routerCount,
|
|
1860
2077
|
routeCount: totalRouteCount,
|
|
1861
2078
|
outputFiles
|
|
1862
2079
|
};
|
|
@@ -1870,6 +2087,7 @@ var init_runtime_discovery = __esm({
|
|
|
1870
2087
|
"src/build/runtime-discovery.ts"() {
|
|
1871
2088
|
"use strict";
|
|
1872
2089
|
init_generate_route_types();
|
|
2090
|
+
init_merge_full_manifests();
|
|
1873
2091
|
init_route_name();
|
|
1874
2092
|
}
|
|
1875
2093
|
});
|
|
@@ -1982,7 +2200,7 @@ function runStaticGeneration(args, mode) {
|
|
|
1982
2200
|
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
1983
2201
|
routerFiles.push(filePath);
|
|
1984
2202
|
}
|
|
1985
|
-
if (source.includes("urls(")) {
|
|
2203
|
+
if (source.includes("urls(") || source.includes("clientUrls(")) {
|
|
1986
2204
|
urlsFiles.push(filePath);
|
|
1987
2205
|
}
|
|
1988
2206
|
} catch (err) {
|
|
@@ -2070,7 +2288,7 @@ async function runRuntimeDiscovery(args, configFile) {
|
|
|
2070
2288
|
if (/\bcreateRouter\s*[<(]/.test(source)) {
|
|
2071
2289
|
routerEntries.push(filePath);
|
|
2072
2290
|
}
|
|
2073
|
-
if (source.includes("urls(")) {
|
|
2291
|
+
if (source.includes("urls(") || source.includes("clientUrls(")) {
|
|
2074
2292
|
writePerModuleRouteTypesForFile(filePath);
|
|
2075
2293
|
}
|
|
2076
2294
|
} catch {
|
|
@@ -2,8 +2,8 @@ import type { RouterInstance } from "../types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Hook to access router actions (push, replace, refresh, prefetch, back, forward).
|
|
4
4
|
*
|
|
5
|
-
* Returns a STABLE reference
|
|
6
|
-
*
|
|
5
|
+
* Returns a STABLE reference per component, so components using useRouter()
|
|
6
|
+
* do not re-render on navigation state changes.
|
|
7
7
|
* For reactive navigation state, use useNavigation() instead.
|
|
8
8
|
*
|
|
9
9
|
* Methods read `basename` from the context on each call. It is set once from
|
|
@@ -11,10 +11,17 @@ import type { RouterInstance } from "../types.js";
|
|
|
11
11
|
* is a full document load (X-RSC-Reload), so the target app mounts fresh with
|
|
12
12
|
* its own basename.
|
|
13
13
|
*
|
|
14
|
+
* RELATIVE paths (no leading `/`, no scheme/query/hash) resolve against the
|
|
15
|
+
* current include() mount: `router.push("cart")` inside `include("/shop")`
|
|
16
|
+
* navigates to `/shop/cart`. Absolute paths stay APP-absolute — unlike
|
|
17
|
+
* basename, the mount is scoped, and absolute pushes legitimately target
|
|
18
|
+
* outside it, so they are never auto-prefixed.
|
|
19
|
+
*
|
|
14
20
|
* @example
|
|
15
21
|
* ```tsx
|
|
16
22
|
* const router = useRouter();
|
|
17
|
-
* router.push("/products");
|
|
23
|
+
* router.push("/products"); // app-absolute
|
|
24
|
+
* router.push("cart"); // mount-relative
|
|
18
25
|
* router.replace("/login", { scroll: false });
|
|
19
26
|
* router.prefetch("/dashboard");
|
|
20
27
|
* router.back();
|