vinext 0.1.8 → 0.2.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/README.md +51 -30
- package/dist/build/inject-pregenerated-paths.d.ts +4 -0
- package/dist/build/inject-pregenerated-paths.js +18 -0
- package/dist/build/layout-classification.js +1 -1
- package/dist/build/nitro-route-rules.js +1 -1
- package/dist/build/pages-client-assets-module.d.ts +11 -0
- package/dist/build/pages-client-assets-module.js +27 -0
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +13 -6
- package/dist/build/report.d.ts +13 -2
- package/dist/build/report.js +99 -2
- package/dist/build/run-prerender.d.ts +7 -0
- package/dist/build/run-prerender.js +10 -1
- package/dist/build/standalone.js +2 -0
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.js +40 -2
- package/dist/cli-args.d.ts +1 -2
- package/dist/cli-args.js +1 -1
- package/dist/cli-dev-config.d.ts +12 -0
- package/dist/cli-dev-config.js +23 -0
- package/dist/cli.js +111 -95
- package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay-store.js +1 -1
- package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay.js +2 -2
- package/dist/client/navigation-runtime.d.ts +2 -0
- package/dist/client/navigation-runtime.js +3 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/config/config-matchers.js +14 -10
- package/dist/config/next-config.d.ts +5 -3
- package/dist/config/next-config.js +44 -1
- package/dist/config/prerender.d.ts +34 -0
- package/dist/config/prerender.js +55 -0
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +40 -10
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/pages-client-entry.d.ts +3 -0
- package/dist/entries/pages-client-entry.js +15 -1
- package/dist/entries/pages-server-entry.js +5 -2
- package/dist/image/image-adapters-virtual.d.ts +59 -0
- package/dist/image/image-adapters-virtual.js +59 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +167 -100
- package/dist/init-cloudflare.d.ts +43 -0
- package/dist/init-cloudflare.js +839 -0
- package/dist/init-platform.d.ts +40 -0
- package/dist/init-platform.js +195 -0
- package/dist/init.d.ts +16 -38
- package/dist/init.js +209 -99
- package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
- package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
- package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +1 -1
- package/dist/packages/cloudflare/src/deploy-config.js +150 -0
- package/dist/packages/cloudflare/src/deploy-help.js +55 -0
- package/dist/packages/cloudflare/src/deploy.js +276 -0
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +18 -15
- package/dist/plugins/ast-scope.d.ts +16 -0
- package/dist/plugins/ast-scope.js +62 -0
- package/dist/plugins/ast-utils.d.ts +27 -1
- package/dist/plugins/ast-utils.js +32 -1
- package/dist/plugins/css-module-imports.d.ts +14 -0
- package/dist/plugins/css-module-imports.js +59 -0
- package/dist/plugins/extensionless-dynamic-import.js +2 -2
- package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
- package/dist/plugins/ignore-dynamic-requests.js +530 -0
- package/dist/plugins/typeof-window.d.ts +1 -1
- package/dist/plugins/typeof-window.js +28 -56
- package/dist/routing/app-route-graph.d.ts +23 -2
- package/dist/routing/app-route-graph.js +126 -32
- package/dist/routing/file-matcher.d.ts +3 -0
- package/dist/routing/file-matcher.js +3 -0
- package/dist/routing/pages-router.js +2 -2
- package/dist/server/api-handler.js +9 -14
- package/dist/server/app-browser-action-result.d.ts +4 -1
- package/dist/server/app-browser-action-result.js +8 -1
- package/dist/server/app-browser-entry.js +161 -52
- package/dist/server/app-browser-interception-context.d.ts +1 -0
- package/dist/server/app-browser-interception-context.js +4 -2
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +60 -31
- package/dist/server/app-browser-server-action-client.d.ts +1 -1
- package/dist/server/app-browser-server-action-client.js +3 -4
- package/dist/server/app-browser-state.d.ts +11 -1
- package/dist/server/app-browser-state.js +25 -8
- package/dist/server/app-browser-visible-commit.d.ts +6 -1
- package/dist/server/app-browser-visible-commit.js +43 -25
- package/dist/server/app-elements-wire.d.ts +9 -2
- package/dist/server/app-elements-wire.js +8 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +4 -3
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +10 -6
- package/dist/server/app-hydration-cache-publication.d.ts +11 -0
- package/dist/server/app-hydration-cache-publication.js +43 -0
- package/dist/server/app-middleware.d.ts +4 -3
- package/dist/server/app-middleware.js +8 -3
- package/dist/server/app-optimistic-routing.js +11 -4
- package/dist/server/app-page-cache-finalizer.js +2 -1
- package/dist/server/app-page-cache-render.d.ts +1 -0
- package/dist/server/app-page-cache-render.js +8 -4
- package/dist/server/app-page-cache.d.ts +1 -0
- package/dist/server/app-page-cache.js +5 -2
- package/dist/server/app-page-dispatch.js +1 -0
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +58 -4
- package/dist/server/app-page-probe.js +5 -0
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render.js +18 -0
- package/dist/server/app-page-request.d.ts +14 -0
- package/dist/server/app-page-request.js +69 -4
- package/dist/server/app-page-route-wiring.d.ts +8 -1
- package/dist/server/app-page-route-wiring.js +32 -18
- package/dist/server/app-page-segment-state.d.ts +2 -1
- package/dist/server/app-page-segment-state.js +3 -1
- package/dist/server/app-page-stream.d.ts +5 -0
- package/dist/server/app-page-stream.js +3 -1
- package/dist/server/app-route-handler-execution.js +5 -1
- package/dist/server/app-route-handler-response.js +1 -0
- package/dist/server/app-route-handler-runtime.js +1 -1
- package/dist/server/app-router-entry.js +9 -2
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +62 -31
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +6 -3
- package/dist/server/app-segment-config.d.ts +11 -0
- package/dist/server/app-segment-config.js +62 -6
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +58 -28
- package/dist/server/app-ssr-entry.d.ts +3 -0
- package/dist/server/app-ssr-entry.js +15 -7
- package/dist/server/app-ssr-stream.d.ts +7 -3
- package/dist/server/app-ssr-stream.js +10 -6
- package/dist/server/app-visited-response-cache.d.ts +6 -0
- package/dist/server/app-visited-response-cache.js +3 -1
- package/dist/server/client-trace-metadata.js +26 -0
- package/dist/server/default-global-not-found-module.d.ts +14 -0
- package/dist/server/default-global-not-found-module.js +14 -0
- package/dist/server/dev-route-files.js +4 -0
- package/dist/server/dev-server.js +19 -18
- package/dist/server/dev-stack-sourcemap.d.ts +1 -1
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/fetch-handler.d.ts +2 -0
- package/dist/server/fetch-handler.js +18 -0
- package/dist/server/headers.d.ts +3 -15
- package/dist/server/headers.js +2 -15
- package/dist/server/image-optimization.d.ts +51 -1
- package/dist/server/image-optimization.js +52 -2
- package/dist/server/implicit-tags.js +1 -1
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +2 -2
- package/dist/server/metadata-routes.d.ts +3 -1
- package/dist/server/metadata-routes.js +12 -2
- package/dist/server/middleware-runtime.js +6 -1
- package/dist/server/navigation-planner.d.ts +1 -0
- package/dist/server/navigation-planner.js +14 -3
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-asset-tags.d.ts +4 -6
- package/dist/server/pages-asset-tags.js +12 -12
- package/dist/server/pages-client-assets.d.ts +12 -0
- package/dist/server/pages-client-assets.js +10 -0
- package/dist/server/pages-node-compat.d.ts +8 -1
- package/dist/server/pages-node-compat.js +32 -4
- package/dist/server/pages-page-data.d.ts +24 -1
- package/dist/server/pages-page-data.js +45 -24
- package/dist/server/pages-page-handler.d.ts +2 -1
- package/dist/server/pages-page-handler.js +19 -12
- package/dist/server/pages-page-response.d.ts +7 -0
- package/dist/server/pages-page-response.js +4 -1
- package/dist/server/pages-request-pipeline.d.ts +2 -0
- package/dist/server/pages-request-pipeline.js +25 -1
- package/dist/server/pages-router-entry.d.ts +30 -0
- package/dist/server/pages-router-entry.js +108 -0
- package/dist/server/prerender-manifest.d.ts +3 -1
- package/dist/server/prerender-route-params.js +1 -1
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +33 -20
- package/dist/server/request-pipeline.js +1 -0
- package/dist/server/seed-cache.js +4 -4
- package/dist/shims/app-router-scroll-state.d.ts +1 -0
- package/dist/shims/app-router-scroll-state.js +1 -0
- package/dist/shims/app-router-scroll.js +2 -1
- package/dist/shims/cache.js +19 -15
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/dynamic-preload-chunks.js +2 -1
- package/dist/shims/form.d.ts +3 -1
- package/dist/shims/form.js +37 -43
- package/dist/shims/headers.d.ts +7 -1
- package/dist/shims/headers.js +17 -3
- package/dist/shims/image.js +75 -14
- package/dist/shims/internal/app-route-detection.d.ts +2 -17
- package/dist/shims/internal/app-route-detection.js +4 -17
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -15
- package/dist/shims/internal/hybrid-client-route-owner.js +9 -60
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +9 -5
- package/dist/shims/internal/pages-data-fetch-dedup.js +73 -14
- package/dist/shims/internal/pages-data-target.d.ts +10 -9
- package/dist/shims/internal/pages-data-target.js +91 -12
- package/dist/shims/internal/pages-router-components.d.ts +7 -0
- package/dist/shims/internal/pages-router-components.js +13 -0
- package/dist/shims/layout-segment-context.d.ts +4 -1
- package/dist/shims/layout-segment-context.js +18 -4
- package/dist/shims/link.js +55 -26
- package/dist/shims/navigation.d.ts +5 -1
- package/dist/shims/navigation.js +45 -10
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/root-params.d.ts +15 -1
- package/dist/shims/root-params.js +21 -1
- package/dist/shims/router.d.ts +5 -7
- package/dist/shims/router.js +362 -93
- package/dist/shims/server.js +3 -2
- package/dist/shims/slot.js +3 -6
- package/dist/utils/client-runtime-metadata.d.ts +2 -18
- package/dist/utils/client-runtime-metadata.js +31 -22
- package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
- package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
- package/dist/utils/domain-locale.d.ts +6 -3
- package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
- package/dist/{server → utils}/middleware-request-headers.js +2 -2
- package/dist/utils/project.d.ts +48 -2
- package/dist/utils/project.js +225 -4
- package/dist/utils/protocol-headers.d.ts +17 -0
- package/dist/utils/protocol-headers.js +17 -0
- package/dist/utils/react-version.d.ts +4 -0
- package/dist/utils/react-version.js +44 -0
- package/package.json +47 -2
- package/dist/deploy.d.ts +0 -197
- package/dist/deploy.js +0 -1040
- package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
- /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
|
@@ -1,55 +1,17 @@
|
|
|
1
1
|
import { collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, nodeArray } from "./ast-utils.js";
|
|
2
|
+
import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode } from "./ast-scope.js";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import { parseAst } from "vite";
|
|
3
5
|
import MagicString from "magic-string";
|
|
4
6
|
//#region src/plugins/typeof-window.ts
|
|
5
|
-
function createScope(parent) {
|
|
6
|
-
return {
|
|
7
|
-
parent,
|
|
8
|
-
bindings: /* @__PURE__ */ new Set()
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
function hasBinding(scope, name) {
|
|
12
|
-
for (let current = scope; current; current = current.parent) if (current.bindings.has(name)) return true;
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
function collectScopeBindings(node, scope) {
|
|
16
|
-
forEachAstChild(node, (child) => {
|
|
17
|
-
if (child.type === "ExportNamedDeclaration" || child.type === "ExportDefaultDeclaration") {
|
|
18
|
-
if (isAstRecord(child.declaration)) collectScopeBindings(child, scope);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (child.type === "FunctionDeclaration" || child.type === "ClassDeclaration") {
|
|
22
|
-
collectBindingNames(child.id, scope.bindings);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (child.type === "VariableDeclaration") {
|
|
26
|
-
for (const declaration of nodeArray(child.declarations)) if (isAstRecord(declaration)) collectBindingNames(declaration.id, scope.bindings);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (child.type === "ImportDeclaration") {
|
|
30
|
-
for (const specifier of nodeArray(child.specifiers)) if (isAstRecord(specifier)) collectBindingNames(specifier.local, scope.bindings);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function collectVarBindings(node, scope, isRoot = true) {
|
|
35
|
-
if (!isRoot && (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression")) return;
|
|
36
|
-
if (node.type === "VariableDeclaration" && node.kind === "var") {
|
|
37
|
-
for (const declaration of nodeArray(node.declarations)) if (isAstRecord(declaration)) collectBindingNames(declaration.id, scope.bindings);
|
|
38
|
-
}
|
|
39
|
-
forEachAstChild(node, (child) => collectVarBindings(child, scope, false));
|
|
40
|
-
}
|
|
41
|
-
function isFunctionNode(node) {
|
|
42
|
-
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
43
|
-
}
|
|
44
7
|
function createChildScope(node, parent) {
|
|
45
|
-
if (node.type !== "Program" && node.type !== "BlockStatement" && node.type !== "StaticBlock" && node.type !== "
|
|
46
|
-
const scope =
|
|
8
|
+
if (node.type !== "Program" && node.type !== "BlockStatement" && node.type !== "StaticBlock" && node.type !== "TSModuleBlock" && node.type !== "CatchClause" && node.type !== "ForStatement" && node.type !== "ForInStatement" && node.type !== "ForOfStatement" && node.type !== "ClassDeclaration" && node.type !== "ClassExpression") return null;
|
|
9
|
+
const scope = createAstScope(parent);
|
|
47
10
|
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") collectBindingNames(node.id, scope.bindings);
|
|
48
11
|
else if (node.type === "CatchClause") collectBindingNames(node.param, scope.bindings);
|
|
49
|
-
|
|
50
|
-
if (node.type === "
|
|
51
|
-
|
|
52
|
-
}
|
|
12
|
+
collectDirectScopeBindings(node, scope);
|
|
13
|
+
if (node.type === "StaticBlock" || node.type === "TSModuleBlock") collectVarScopeBindings(node, scope);
|
|
14
|
+
if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") collectLoopScopeBindings(node, scope);
|
|
53
15
|
return scope;
|
|
54
16
|
}
|
|
55
17
|
function getTypeofWindowReplacement(environment) {
|
|
@@ -70,42 +32,52 @@ function evaluateTypeofWindowComparison(node, replacement, scope) {
|
|
|
70
32
|
].includes(String(node.operator))) return null;
|
|
71
33
|
const left = isAstRecord(node.left) ? node.left : null;
|
|
72
34
|
const right = isAstRecord(node.right) ? node.right : null;
|
|
73
|
-
const leftIsTypeofWindow = left?.type === "UnaryExpression" && left.operator === "typeof" && isIdentifierNamed(left.argument, "window") && !
|
|
74
|
-
const rightIsTypeofWindow = right?.type === "UnaryExpression" && right.operator === "typeof" && isIdentifierNamed(right.argument, "window") && !
|
|
35
|
+
const leftIsTypeofWindow = left?.type === "UnaryExpression" && left.operator === "typeof" && isIdentifierNamed(left.argument, "window") && !hasAstBinding(scope, "window");
|
|
36
|
+
const rightIsTypeofWindow = right?.type === "UnaryExpression" && right.operator === "typeof" && isIdentifierNamed(right.argument, "window") && !hasAstBinding(scope, "window");
|
|
75
37
|
const comparedValue = leftIsTypeofWindow ? stringLiteralValue(right) : rightIsTypeofWindow ? stringLiteralValue(left) : null;
|
|
76
38
|
if (comparedValue === null) return null;
|
|
77
39
|
const equal = replacement === comparedValue;
|
|
78
40
|
return node.operator === "==" || node.operator === "===" ? equal : !equal;
|
|
79
41
|
}
|
|
80
|
-
function replaceTypeofWindow(code, replacement) {
|
|
42
|
+
function replaceTypeofWindow(code, replacement, id = "file.js") {
|
|
81
43
|
if (!/typeof\s+window/.test(code)) return null;
|
|
44
|
+
const extension = path.extname(id.split("?", 1)[0]);
|
|
45
|
+
const lang = extension === ".ts" || extension === ".mts" || extension === ".cts" ? "ts" : extension === ".tsx" ? "tsx" : extension === ".jsx" ? "jsx" : "js";
|
|
82
46
|
let ast;
|
|
83
47
|
try {
|
|
84
|
-
ast = parseAst(code);
|
|
48
|
+
ast = parseAst(code, { lang });
|
|
85
49
|
} catch {
|
|
86
50
|
return null;
|
|
87
51
|
}
|
|
88
52
|
const output = new MagicString(code);
|
|
89
53
|
let changed = false;
|
|
90
54
|
if (!isAstRecord(ast)) return null;
|
|
91
|
-
const rootScope =
|
|
92
|
-
|
|
93
|
-
|
|
55
|
+
const rootScope = createAstScope(null);
|
|
56
|
+
collectDirectScopeBindings(ast, rootScope);
|
|
57
|
+
collectVarScopeBindings(ast, rootScope);
|
|
94
58
|
function visit(node, parentScope) {
|
|
95
59
|
if (isFunctionNode(node)) {
|
|
96
|
-
const parameterScope =
|
|
60
|
+
const parameterScope = createAstScope(parentScope);
|
|
97
61
|
collectBindingNames(node.id, parameterScope.bindings);
|
|
98
62
|
for (const parameter of nodeArray(node.params)) {
|
|
99
63
|
collectBindingNames(parameter, parameterScope.bindings);
|
|
100
64
|
if (isAstRecord(parameter)) visit(parameter, parameterScope);
|
|
101
65
|
}
|
|
102
66
|
if (isAstRecord(node.body)) if (node.body.type === "BlockStatement") {
|
|
103
|
-
const bodyScope =
|
|
104
|
-
|
|
67
|
+
const bodyScope = createAstScope(parameterScope);
|
|
68
|
+
collectDirectScopeBindings(node.body, bodyScope);
|
|
69
|
+
collectVarScopeBindings(node.body, bodyScope);
|
|
105
70
|
visit(node.body, bodyScope);
|
|
106
71
|
} else visit(node.body, parameterScope);
|
|
107
72
|
return;
|
|
108
73
|
}
|
|
74
|
+
if (node.type === "SwitchStatement") {
|
|
75
|
+
if (isAstRecord(node.discriminant)) visit(node.discriminant, parentScope);
|
|
76
|
+
const switchScope = createAstScope(parentScope);
|
|
77
|
+
collectSwitchScopeBindings(node, switchScope);
|
|
78
|
+
for (const switchCase of nodeArray(node.cases)) if (isAstRecord(switchCase)) visit(switchCase, switchScope);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
109
81
|
const scope = createChildScope(node, parentScope) ?? parentScope;
|
|
110
82
|
if (node.type === "IfStatement" && hasRange(node)) {
|
|
111
83
|
const result = evaluateTypeofWindowComparison(node.test, replacement, scope);
|
|
@@ -132,7 +104,7 @@ function replaceTypeofWindow(code, replacement) {
|
|
|
132
104
|
return;
|
|
133
105
|
}
|
|
134
106
|
}
|
|
135
|
-
if (node.type === "UnaryExpression" && node.operator === "typeof" && isIdentifierNamed(node.argument, "window") && !
|
|
107
|
+
if (node.type === "UnaryExpression" && node.operator === "typeof" && isIdentifierNamed(node.argument, "window") && !hasAstBinding(scope, "window") && hasRange(node)) {
|
|
136
108
|
output.overwrite(node.start, node.end, JSON.stringify(replacement));
|
|
137
109
|
changed = true;
|
|
138
110
|
return;
|
|
@@ -83,7 +83,12 @@ type AppRoute = {
|
|
|
83
83
|
routePath: string | null; /** Ordered list of layout files from root to leaf */
|
|
84
84
|
layouts: string[]; /** Ordered list of all discovered template files from root to leaf (not necessarily aligned 1:1 with layouts) */
|
|
85
85
|
templates: string[]; /** Parallel route slots (from @slot directories at the route's directory level) */
|
|
86
|
-
parallelSlots: ParallelSlot[];
|
|
86
|
+
parallelSlots: ParallelSlot[]; /** Stable implicit children-slot identity for parallel-slot sub-route families. */
|
|
87
|
+
childrenSlot?: {
|
|
88
|
+
id: string;
|
|
89
|
+
ownerTreePath: string;
|
|
90
|
+
state: "active" | "default" | "unmatched";
|
|
91
|
+
};
|
|
87
92
|
/**
|
|
88
93
|
* Interception markers not wrapped in an `@slot` directory.
|
|
89
94
|
* On soft-nav, the intercepting page replaces the entire page response.
|
|
@@ -122,7 +127,16 @@ type AppRoute = {
|
|
|
122
127
|
* Includes route groups and dynamic segments (as template strings like "[id]").
|
|
123
128
|
* Used at render time to compute the child segments for useSelectedLayoutSegments().
|
|
124
129
|
*/
|
|
125
|
-
routeSegments: string[];
|
|
130
|
+
routeSegments: string[];
|
|
131
|
+
/**
|
|
132
|
+
* Active filesystem segments for the default `children` slot.
|
|
133
|
+
*
|
|
134
|
+
* Synthetic routes materialized only from named parallel-slot pages still
|
|
135
|
+
* use the full `routeSegments` for URL matching, params, and route identity,
|
|
136
|
+
* but their children slot renders the parent's default.tsx. In that case the
|
|
137
|
+
* active children segments remain at the parent route.
|
|
138
|
+
*/
|
|
139
|
+
childrenRouteSegments?: string[]; /** Tree position (directory depth from app/ root) for each template. */
|
|
126
140
|
templateTreePositions?: number[];
|
|
127
141
|
/**
|
|
128
142
|
* Tree position (directory depth from app/ root) for each layout.
|
|
@@ -276,6 +290,13 @@ type RouteManifest = {
|
|
|
276
290
|
graphVersion: GraphVersion;
|
|
277
291
|
segmentGraph: StaticSegmentGraph;
|
|
278
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* Build the App Router route graph by scanning `appDir`.
|
|
295
|
+
*
|
|
296
|
+
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
297
|
+
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
298
|
+
* separators on Windows. Production callers normalize it at their entry.
|
|
299
|
+
*/
|
|
279
300
|
declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher): Promise<{
|
|
280
301
|
routes: AppRouteGraphRoute[];
|
|
281
302
|
routeManifest: RouteManifest;
|
|
@@ -83,6 +83,19 @@ function createStaticSegmentGraph(routes) {
|
|
|
83
83
|
templateIds: [...route.ids.templates],
|
|
84
84
|
slotIds: route.parallelSlots.map((slot) => slot.id).sort(compareStableStrings)
|
|
85
85
|
});
|
|
86
|
+
if (route.childrenSlot) {
|
|
87
|
+
const ownerLayoutId = findRouteManifestOwnerLayoutIdByTreePath(route, route.childrenSlot.ownerTreePath);
|
|
88
|
+
routeEntries.get(route.ids.route).slotIds = [...routeEntries.get(route.ids.route).slotIds, route.childrenSlot.id].sort(compareStableStrings);
|
|
89
|
+
slotBindings.set(`${route.ids.route}::${route.childrenSlot.id}`, {
|
|
90
|
+
id: `${route.ids.route}::${route.childrenSlot.id}`,
|
|
91
|
+
routeId: route.ids.route,
|
|
92
|
+
slotId: route.childrenSlot.id,
|
|
93
|
+
ownerLayoutId,
|
|
94
|
+
state: route.childrenSlot.state,
|
|
95
|
+
defaultId: null,
|
|
96
|
+
routeSegments: null
|
|
97
|
+
});
|
|
98
|
+
}
|
|
86
99
|
if (route.ids.page) pages.set(route.ids.page, {
|
|
87
100
|
id: route.ids.page,
|
|
88
101
|
routeId: route.ids.route,
|
|
@@ -225,6 +238,10 @@ function findRouteManifestOwnerLayoutId(route, treePosition) {
|
|
|
225
238
|
const layoutIndex = route.layoutTreePositions.indexOf(treePosition);
|
|
226
239
|
return route.ids.layouts[layoutIndex] ?? null;
|
|
227
240
|
}
|
|
241
|
+
function findRouteManifestOwnerLayoutIdByTreePath(route, treePath) {
|
|
242
|
+
const layoutIndex = route.layoutTreePositions.findIndex((treePosition) => createAppRouteGraphTreePath(route.routeSegments, treePosition) === treePath);
|
|
243
|
+
return route.ids.layouts[layoutIndex] ?? null;
|
|
244
|
+
}
|
|
228
245
|
function findSlotOwnerLayoutId(route, slot) {
|
|
229
246
|
if (slot.layoutIndex < 0) return null;
|
|
230
247
|
return route.ids.layouts[slot.layoutIndex] ?? null;
|
|
@@ -344,6 +361,13 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
344
361
|
};
|
|
345
362
|
return `graph:${createHash("sha256").update(JSON.stringify(stableShape)).digest("hex")}`;
|
|
346
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Build the App Router route graph by scanning `appDir`.
|
|
366
|
+
*
|
|
367
|
+
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
368
|
+
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
369
|
+
* separators on Windows. Production callers normalize it at their entry.
|
|
370
|
+
*/
|
|
347
371
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
348
372
|
const routes = [];
|
|
349
373
|
const scanMatcher = { ...matcher };
|
|
@@ -363,9 +387,13 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
363
387
|
const dir = path.posix.dirname(file);
|
|
364
388
|
const routeDir = dir === "." ? appDir : path.posix.join(appDir, dir);
|
|
365
389
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
366
|
-
if (discoverParallelSlots(routeDir, appDir, scanMatcher).length === 0) continue;
|
|
367
|
-
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null);
|
|
390
|
+
if (discoverParallelSlots(routeDir, appDir, scanMatcher, true).length === 0) continue;
|
|
391
|
+
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null, true);
|
|
368
392
|
if (!route) continue;
|
|
393
|
+
if (routes.some((candidate) => candidate.patternParts.length === route.patternParts.length + 1 && candidate.patternParts.at(-1)?.endsWith("*") && patternsStructurallyEquivalent(candidate.patternParts.slice(0, -1), route.patternParts))) {
|
|
394
|
+
ghostParentRoutes.push(route);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
369
397
|
if (routePatterns.has(route.pattern)) {
|
|
370
398
|
ghostParentRoutes.push(route);
|
|
371
399
|
continue;
|
|
@@ -482,8 +510,29 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
482
510
|
}
|
|
483
511
|
}
|
|
484
512
|
if (subPathMap.size === 0) continue;
|
|
513
|
+
const childrenOwnerTreePath = parentRoute.parallelSlots.find((slot) => path.dirname(slot.ownerDir) === parentPageDir)?.ownerTreePath;
|
|
514
|
+
if (!childrenOwnerTreePath) throw new Error(`[vinext] App route graph invariant violated: missing children slot owner for ${parentRoute.pattern}`);
|
|
485
515
|
const childrenDefault = findFile(parentPageDir, "default", matcher);
|
|
486
516
|
if (parentRoute.pagePath && !childrenDefault) continue;
|
|
517
|
+
const childrenSlotId = createAppRouteGraphSlotId("children", childrenOwnerTreePath);
|
|
518
|
+
if (parentRoute.pagePath) parentRoute.childrenSlot = {
|
|
519
|
+
id: childrenSlotId,
|
|
520
|
+
ownerTreePath: childrenOwnerTreePath,
|
|
521
|
+
state: "active"
|
|
522
|
+
};
|
|
523
|
+
for (const route of routes) {
|
|
524
|
+
if (!route.pagePath || route === parentRoute) continue;
|
|
525
|
+
const relativePageDir = path.relative(parentPageDir, path.dirname(route.pagePath));
|
|
526
|
+
if (relativePageDir === "" || relativePageDir === ".." || relativePageDir.startsWith(`..${path.sep}`) || path.isAbsolute(relativePageDir)) continue;
|
|
527
|
+
const existingOwnerDepth = route.childrenSlot?.ownerTreePath.split("/").filter(Boolean).length;
|
|
528
|
+
const candidateOwnerDepth = childrenOwnerTreePath.split("/").filter(Boolean).length;
|
|
529
|
+
if (existingOwnerDepth !== void 0 && existingOwnerDepth >= candidateOwnerDepth) continue;
|
|
530
|
+
route.childrenSlot = {
|
|
531
|
+
id: childrenSlotId,
|
|
532
|
+
ownerTreePath: childrenOwnerTreePath,
|
|
533
|
+
state: "active"
|
|
534
|
+
};
|
|
535
|
+
}
|
|
487
536
|
const childrenCatchAll = childrenDefault ? null : findCatchAllPage(parentPageDir, matcher);
|
|
488
537
|
const childrenFallback = childrenDefault ?? childrenCatchAll;
|
|
489
538
|
for (const { rawSegments, converted: convertedSubRoute, slotPages } of subPathMap.values()) {
|
|
@@ -525,6 +574,11 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
525
574
|
layouts: parentRoute.layouts,
|
|
526
575
|
templates: parentRoute.templates,
|
|
527
576
|
parallelSlots: subSlots,
|
|
577
|
+
childrenSlot: {
|
|
578
|
+
id: childrenSlotId,
|
|
579
|
+
ownerTreePath: childrenOwnerTreePath,
|
|
580
|
+
state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
|
|
581
|
+
},
|
|
528
582
|
loadingPath: parentRoute.loadingPath,
|
|
529
583
|
errorPath: parentRoute.errorPath,
|
|
530
584
|
layoutErrorPaths: parentRoute.layoutErrorPaths,
|
|
@@ -535,6 +589,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
535
589
|
unauthorizedPath: parentRoute.unauthorizedPath,
|
|
536
590
|
unauthorizedPaths: parentRoute.unauthorizedPaths,
|
|
537
591
|
routeSegments: [...parentRoute.routeSegments, ...rawSegments],
|
|
592
|
+
childrenRouteSegments: childrenDefault ? parentRoute.routeSegments : void 0,
|
|
538
593
|
templateTreePositions: parentRoute.templateTreePositions,
|
|
539
594
|
layoutTreePositions: parentRoute.layoutTreePositions,
|
|
540
595
|
isDynamic: parentRoute.isDynamic || subIsDynamic,
|
|
@@ -549,6 +604,26 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
549
604
|
}
|
|
550
605
|
return syntheticRoutes;
|
|
551
606
|
}
|
|
607
|
+
const dirEntriesCache = /* @__PURE__ */ new WeakMap();
|
|
608
|
+
function readDirEntriesCached(dir, matcher) {
|
|
609
|
+
let perMatcher = dirEntriesCache.get(matcher);
|
|
610
|
+
if (!perMatcher) {
|
|
611
|
+
perMatcher = /* @__PURE__ */ new Map();
|
|
612
|
+
dirEntriesCache.set(matcher, perMatcher);
|
|
613
|
+
}
|
|
614
|
+
let entries = perMatcher.get(dir);
|
|
615
|
+
if (entries === void 0) {
|
|
616
|
+
try {
|
|
617
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
618
|
+
} catch (error) {
|
|
619
|
+
const code = error.code;
|
|
620
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
621
|
+
entries = [];
|
|
622
|
+
}
|
|
623
|
+
perMatcher.set(dir, entries);
|
|
624
|
+
}
|
|
625
|
+
return entries;
|
|
626
|
+
}
|
|
552
627
|
const findSlotSubPagesCache = /* @__PURE__ */ new WeakMap();
|
|
553
628
|
function findSlotSubPages(slotDir, matcher) {
|
|
554
629
|
let perMatcher = findSlotSubPagesCache.get(matcher);
|
|
@@ -629,23 +704,29 @@ function findCatchAllPage(dir, matcher) {
|
|
|
629
704
|
}
|
|
630
705
|
/**
|
|
631
706
|
* Convert a file path relative to app/ into an AppRoute.
|
|
707
|
+
*
|
|
708
|
+
* `file` and `appDir` must be forward-slash. `file` comes from
|
|
709
|
+
* `scanWithExtensions` (already forward-slash) and is joined onto `appDir` with
|
|
710
|
+
* `path.posix.join` to form the page/route path, so a native input would
|
|
711
|
+
* produce a mixed separator on Windows.
|
|
632
712
|
*/
|
|
633
713
|
function fileToAppRoute(file, appDir, type, matcher) {
|
|
634
|
-
let dir = path.dirname(file);
|
|
635
|
-
if (type === "page" && dir !== "." && path.basename(dir) === "@children") {
|
|
636
|
-
const parent = path.dirname(dir);
|
|
714
|
+
let dir = path.posix.dirname(file);
|
|
715
|
+
if (type === "page" && dir !== "." && path.posix.basename(dir) === "@children") {
|
|
716
|
+
const parent = path.posix.dirname(dir);
|
|
637
717
|
dir = parent === "" || parent === "." ? "." : parent;
|
|
638
718
|
}
|
|
639
|
-
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
|
|
719
|
+
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.posix.join(appDir, file) : null, type === "route" ? path.posix.join(appDir, file) : null);
|
|
640
720
|
}
|
|
641
721
|
/**
|
|
642
|
-
* `dir` and `
|
|
643
|
-
* `path.posix.sep` and joined onto `appDir` with `path.posix.join
|
|
644
|
-
* is threaded to the layout/slot/boundary discovery below, which
|
|
645
|
-
* same way.
|
|
722
|
+
* `dir`, `appDir`, `pagePath`, and `routePath` must all be forward-slash. `dir`
|
|
723
|
+
* is split on `path.posix.sep` and joined onto `appDir` with `path.posix.join`.
|
|
724
|
+
* `appDir` is threaded to the layout/slot/boundary discovery below, which builds
|
|
725
|
+
* paths the same way. `pagePath` and `routePath` are stored on the route node as
|
|
726
|
+
* canonical ids that get compared and re-joined downstream.
|
|
646
727
|
*/
|
|
647
|
-
function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
|
|
648
|
-
const segments = dir === "." ? [] : dir.split(
|
|
728
|
+
function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeNestedOnlySlots = false) {
|
|
729
|
+
const segments = dir === "." ? [] : dir.split("/");
|
|
649
730
|
const params = [];
|
|
650
731
|
let isDynamic = false;
|
|
651
732
|
const convertedRoute = convertSegmentsToRouteParts(segments);
|
|
@@ -663,6 +744,7 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
|
|
|
663
744
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
664
745
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
665
746
|
const routeDir = dir === "." ? appDir : path.posix.join(appDir, dir);
|
|
747
|
+
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
666
748
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
667
749
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
668
750
|
const notFoundPath = discoverBoundaryFile(segments, appDir, "not-found", matcher);
|
|
@@ -671,11 +753,11 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
|
|
|
671
753
|
const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
|
|
672
754
|
const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
|
|
673
755
|
const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
|
|
674
|
-
const parallelSlots = discoverInheritedParallelSlots(segments, appDir, routeDir, matcher);
|
|
756
|
+
const parallelSlots = discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots);
|
|
675
757
|
return {
|
|
676
758
|
ids: createAppRouteSemanticIds({
|
|
677
759
|
pattern: pattern === "/" ? "/" : pattern,
|
|
678
|
-
pagePath,
|
|
760
|
+
pagePath: effectivePagePath,
|
|
679
761
|
routePath,
|
|
680
762
|
routeSegments: segments,
|
|
681
763
|
layoutTreePositions,
|
|
@@ -683,7 +765,7 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
|
|
|
683
765
|
slots: parallelSlots
|
|
684
766
|
}),
|
|
685
767
|
pattern: pattern === "/" ? "/" : pattern,
|
|
686
|
-
pagePath,
|
|
768
|
+
pagePath: effectivePagePath,
|
|
687
769
|
routePath,
|
|
688
770
|
layouts,
|
|
689
771
|
templates,
|
|
@@ -899,7 +981,7 @@ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
|
|
|
899
981
|
* `appDir` via `path.posix.join`, and the `dir === routeDir` active-level test
|
|
900
982
|
* below only matches when both share the canonical separator.
|
|
901
983
|
*/
|
|
902
|
-
function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher) {
|
|
984
|
+
function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots = false) {
|
|
903
985
|
const slotMap = /* @__PURE__ */ new Map();
|
|
904
986
|
let currentDir = appDir;
|
|
905
987
|
const dirsToCheck = [];
|
|
@@ -922,9 +1004,9 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher) {
|
|
|
922
1004
|
for (const { dir, layoutIdx: lvlLayoutIdx, segmentIndex } of dirsToCheck) {
|
|
923
1005
|
if (lvlLayoutIdx < 0 && routeHasLayout) continue;
|
|
924
1006
|
const slotLayoutIdx = Math.max(lvlLayoutIdx, 0);
|
|
925
|
-
const slotsAtLevel = discoverParallelSlots(dir, appDir, matcher);
|
|
926
1007
|
const segmentsBelow = segments.slice(segmentIndex);
|
|
927
1008
|
const isActiveUrlLevel = dir === routeDir || segmentsBelow.every(isInvisibleSegment);
|
|
1009
|
+
const slotsAtLevel = discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots && isActiveUrlLevel);
|
|
928
1010
|
for (const slot of slotsAtLevel) if (isActiveUrlLevel) {
|
|
929
1011
|
slot.layoutIndex = slotLayoutIdx;
|
|
930
1012
|
slotMap.set(slot.key, slot);
|
|
@@ -1083,12 +1165,7 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
1083
1165
|
function findSlotRootPage(slotDir, matcher) {
|
|
1084
1166
|
const directPage = findFile(slotDir, "page", matcher);
|
|
1085
1167
|
if (directPage) return directPage;
|
|
1086
|
-
|
|
1087
|
-
try {
|
|
1088
|
-
entries = fs.readdirSync(slotDir, { withFileTypes: true });
|
|
1089
|
-
} catch {
|
|
1090
|
-
return null;
|
|
1091
|
-
}
|
|
1168
|
+
const entries = readDirEntriesCached(slotDir, matcher);
|
|
1092
1169
|
for (const entry of entries) {
|
|
1093
1170
|
if (!entry.isDirectory()) continue;
|
|
1094
1171
|
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
@@ -1099,11 +1176,16 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1099
1176
|
}
|
|
1100
1177
|
/**
|
|
1101
1178
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
1102
|
-
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page
|
|
1179
|
+
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page,
|
|
1180
|
+
* default component, intercepting route, or nested page-backed sub-route.
|
|
1181
|
+
*
|
|
1182
|
+
* `dir` and `appDir` must be forward-slash. The slot directory is built from
|
|
1183
|
+
* `dir` with `path.posix.join`, and the owner segments and slot key come from
|
|
1184
|
+
* `path.posix.relative(appDir, …)`, which only yields a forward-slash relative
|
|
1185
|
+
* path when both operands already are.
|
|
1103
1186
|
*/
|
|
1104
|
-
function discoverParallelSlots(dir, appDir, matcher) {
|
|
1105
|
-
|
|
1106
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1187
|
+
function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
|
|
1188
|
+
const entries = readDirEntriesCached(dir, matcher);
|
|
1107
1189
|
const slots = [];
|
|
1108
1190
|
for (const entry of entries) {
|
|
1109
1191
|
if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
|
|
@@ -1113,13 +1195,14 @@ function discoverParallelSlots(dir, appDir, matcher) {
|
|
|
1113
1195
|
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
1114
1196
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
1115
1197
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
1116
|
-
|
|
1117
|
-
|
|
1198
|
+
const hasNestedPages = includeNestedOnlySlots && findSlotSubPages(slotDir, matcher).length > 0;
|
|
1199
|
+
if (!pagePath && !defaultPath && interceptingRoutes.length === 0 && !hasNestedPages) continue;
|
|
1200
|
+
const ownerSegments = path.posix.relative(appDir, dir).split("/").filter((segment) => segment.length > 0);
|
|
1118
1201
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1119
1202
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1120
1203
|
slots.push({
|
|
1121
1204
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1122
|
-
key: `${slotName}@${path.relative(appDir, slotDir)
|
|
1205
|
+
key: `${slotName}@${path.posix.relative(appDir, slotDir)}`,
|
|
1123
1206
|
name: slotName,
|
|
1124
1207
|
ownerDir: slotDir,
|
|
1125
1208
|
ownerTreePath,
|
|
@@ -1178,6 +1261,10 @@ function isInterceptionMarkerDir(name) {
|
|
|
1178
1261
|
* Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
|
|
1179
1262
|
* They intercept navigation to another route and render within the slot instead.
|
|
1180
1263
|
*
|
|
1264
|
+
* `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
|
|
1265
|
+
* down to `path.posix.join` and `path.posix.relative` when building the
|
|
1266
|
+
* intercept page paths and target patterns.
|
|
1267
|
+
*
|
|
1181
1268
|
* @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
|
|
1182
1269
|
* @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
|
|
1183
1270
|
* @param appDir - The root app directory
|
|
@@ -1281,6 +1368,10 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1281
1368
|
/**
|
|
1282
1369
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1283
1370
|
* intercepting route directories.
|
|
1371
|
+
*
|
|
1372
|
+
* `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
|
|
1373
|
+
* descends with `path.posix.join` and all three reach the `path.posix.join` /
|
|
1374
|
+
* `path.posix.relative` calls that build the intercept page paths and patterns.
|
|
1284
1375
|
*/
|
|
1285
1376
|
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1286
1377
|
if (!fs.existsSync(currentDir)) return;
|
|
@@ -1289,11 +1380,11 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher
|
|
|
1289
1380
|
if (!entry.isDirectory()) continue;
|
|
1290
1381
|
if (entry.name.startsWith("_")) continue;
|
|
1291
1382
|
const interceptMatch = matchInterceptConvention(entry.name);
|
|
1383
|
+
const interceptDir = path.posix.join(currentDir, entry.name);
|
|
1292
1384
|
if (interceptMatch) {
|
|
1293
1385
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1294
|
-
const interceptDir = path.join(currentDir, entry.name);
|
|
1295
1386
|
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
1296
|
-
} else scanForInterceptingPages(
|
|
1387
|
+
} else scanForInterceptingPages(interceptDir, routeDir, appDir, results, matcher);
|
|
1297
1388
|
}
|
|
1298
1389
|
}
|
|
1299
1390
|
/**
|
|
@@ -1455,6 +1546,9 @@ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
|
|
|
1455
1546
|
* registered per-scan cache; otherwise falls back to a direct probe (identical
|
|
1456
1547
|
* result). The `null` "not found" outcome is cached too, so repeated misses on
|
|
1457
1548
|
* shared ancestors cost a single set of `existsSync` calls per scan.
|
|
1549
|
+
*
|
|
1550
|
+
* `dir` must be forward-slash. The returned path comes from `findFileWithExts`,
|
|
1551
|
+
* so it is forward-slash too.
|
|
1458
1552
|
*/
|
|
1459
1553
|
function findFile(dir, name, matcher) {
|
|
1460
1554
|
const cache = findFileProbeCache.get(matcher);
|
|
@@ -21,6 +21,9 @@ declare function findFileWithExtensions(basePath: string, matcher: ValidFileMatc
|
|
|
21
21
|
/**
|
|
22
22
|
* Find a file by basename and configured page extension in a directory.
|
|
23
23
|
* Returns the first matching absolute path, or null if not found.
|
|
24
|
+
*
|
|
25
|
+
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
26
|
+
* so it is forward-slash too.
|
|
24
27
|
*/
|
|
25
28
|
declare function findFileWithExts(dir: string, name: string, matcher: ValidFileMatcher): string | null;
|
|
26
29
|
/**
|
|
@@ -67,6 +67,9 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
67
67
|
/**
|
|
68
68
|
* Find a file by basename and configured page extension in a directory.
|
|
69
69
|
* Returns the first matching absolute path, or null if not found.
|
|
70
|
+
*
|
|
71
|
+
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
72
|
+
* so it is forward-slash too.
|
|
70
73
|
*/
|
|
71
74
|
function findFileWithExts(dir, name, matcher) {
|
|
72
75
|
for (const ext of matcher.dottedExtensions) {
|
|
@@ -63,7 +63,7 @@ async function scanPageRoutes(pagesDir, matcher) {
|
|
|
63
63
|
function fileToRoute(file, pagesDir, matcher) {
|
|
64
64
|
const withoutExt = matcher.stripExtension(file);
|
|
65
65
|
if (withoutExt === file) return null;
|
|
66
|
-
const segments = withoutExt.split(
|
|
66
|
+
const segments = withoutExt.split("/");
|
|
67
67
|
if (segments[segments.length - 1] === "index") segments.pop();
|
|
68
68
|
const params = [];
|
|
69
69
|
let isDynamic = false;
|
|
@@ -152,7 +152,7 @@ async function scanApiRoutes(pagesDir, matcher) {
|
|
|
152
152
|
}
|
|
153
153
|
const routes = [];
|
|
154
154
|
for (const file of files) {
|
|
155
|
-
const route = fileToRoute(path.join("api", file), pagesDir, matcher);
|
|
155
|
+
const route = fileToRoute(path.posix.join("api", file), pagesDir, matcher);
|
|
156
156
|
if (route) routes.push(route);
|
|
157
157
|
}
|
|
158
158
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { hasBasePath } from "../utils/base-path.js";
|
|
2
2
|
import { matchRoute } from "../routing/pages-router.js";
|
|
3
3
|
import "./server-globals.js";
|
|
4
|
-
import { parseCookieHeader } from "../utils/parse-cookie.js";
|
|
5
4
|
import { NextRequest } from "../shims/server.js";
|
|
6
5
|
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
7
6
|
import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
|
|
8
|
-
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
9
|
-
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
10
7
|
import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
8
|
+
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
11
9
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
12
10
|
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
11
|
+
import { attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
12
|
+
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
13
13
|
import { decode } from "node:querystring";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
15
15
|
//#region src/server/api-handler.ts
|
|
@@ -69,12 +69,6 @@ async function parseBody(req, sizeLimit = MAX_BODY_SIZE) {
|
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Parse cookies from the Cookie header.
|
|
74
|
-
*/
|
|
75
|
-
function parseCookies(req) {
|
|
76
|
-
return parseCookieHeader(req.headers.cookie);
|
|
77
|
-
}
|
|
78
72
|
function isEdgeApiRouteModule(module) {
|
|
79
73
|
if (typeof module.default !== "function") return false;
|
|
80
74
|
const bare = module.runtime;
|
|
@@ -167,12 +161,13 @@ async function writeEdgeApiResponseBody(res, body) {
|
|
|
167
161
|
* Enhance a Node.js req/res pair with Next.js API route helpers.
|
|
168
162
|
*/
|
|
169
163
|
function enhanceApiObjects(req, res, query, body) {
|
|
164
|
+
const apiReq = Object.assign(req, {
|
|
165
|
+
body,
|
|
166
|
+
query
|
|
167
|
+
});
|
|
168
|
+
attachPagesRequestCookies(apiReq);
|
|
170
169
|
return {
|
|
171
|
-
apiReq
|
|
172
|
-
body,
|
|
173
|
-
cookies: parseCookies(req),
|
|
174
|
-
query
|
|
175
|
-
}),
|
|
170
|
+
apiReq,
|
|
176
171
|
apiRes: Object.assign(res, {
|
|
177
172
|
status(code) {
|
|
178
173
|
this.statusCode = code;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OperationLane } from "./operation-token.js";
|
|
1
2
|
import { ServerActionResultFacts } from "./navigation-planner.js";
|
|
2
3
|
|
|
3
4
|
//#region src/server/app-browser-action-result.d.ts
|
|
@@ -24,7 +25,9 @@ type ServerActionInitiationSnapshot<TRouterState> = {
|
|
|
24
25
|
declare function isServerActionResult<TRoot>(value: unknown): value is AppBrowserServerActionResult<TRoot>;
|
|
25
26
|
declare function shouldClearClientNavigationCachesForServerActionResult<TRoot>(result: AppBrowserServerActionResult<TRoot> | TRoot, revalidation?: ServerActionRevalidationKind): boolean;
|
|
26
27
|
declare function parseServerActionRevalidationHeader(headers: Pick<Headers, "get">): ServerActionRevalidationKind;
|
|
28
|
+
declare function resolveServerActionOperationLane(revalidation: ServerActionRevalidationKind): Extract<OperationLane, "refresh" | "server-action">;
|
|
27
29
|
declare function normalizeServerActionThrownValue(data: unknown, responseStatus: number): unknown;
|
|
30
|
+
declare function shouldSyncServerActionHttpFallbackHead<TRoot>(result: AppBrowserServerActionResult<TRoot> | TRoot): boolean;
|
|
28
31
|
declare function readInvalidServerActionResponseError(response: Pick<Response, "headers" | "status" | "text">, hasRedirectLocation: boolean): Promise<Error | null>;
|
|
29
32
|
type ServerActionResultResponseFactsInput = {
|
|
30
33
|
actionRedirectHref: string | null;
|
|
@@ -61,4 +64,4 @@ type DiscardedServerActionRefreshSchedulerOptions = {
|
|
|
61
64
|
};
|
|
62
65
|
declare function createDiscardedServerActionRefreshScheduler(options: DiscardedServerActionRefreshSchedulerOptions): DiscardedServerActionRefreshScheduler;
|
|
63
66
|
//#endregion
|
|
64
|
-
export { AppBrowserServerActionResult, ServerActionResultResponseFactsInput, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };
|
|
67
|
+
export { AppBrowserServerActionResult, ServerActionResultResponseFactsInput, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, resolveServerActionOperationLane, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction, shouldSyncServerActionHttpFallbackHead };
|
|
@@ -32,6 +32,9 @@ function parseServerActionRevalidationHeader(headers) {
|
|
|
32
32
|
default: return "none";
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
function resolveServerActionOperationLane(revalidation) {
|
|
36
|
+
return revalidation === "none" ? "server-action" : "refresh";
|
|
37
|
+
}
|
|
35
38
|
function createServerActionHttpFallbackError(status) {
|
|
36
39
|
if (status !== 401 && status !== 403 && status !== 404) return null;
|
|
37
40
|
const digest = status === 404 ? "NEXT_HTTP_ERROR_FALLBACK;404" : `NEXT_HTTP_ERROR_FALLBACK;${status}`;
|
|
@@ -41,6 +44,10 @@ function createServerActionHttpFallbackError(status) {
|
|
|
41
44
|
function normalizeServerActionThrownValue(data, responseStatus) {
|
|
42
45
|
return createServerActionHttpFallbackError(responseStatus) ?? data;
|
|
43
46
|
}
|
|
47
|
+
function shouldSyncServerActionHttpFallbackHead(result) {
|
|
48
|
+
if (!isServerActionResult(result) || result.root !== void 0) return false;
|
|
49
|
+
return result.returnValue?.ok !== false;
|
|
50
|
+
}
|
|
44
51
|
async function readInvalidServerActionResponseError(response, hasRedirectLocation) {
|
|
45
52
|
const contentType = response.headers.get("content-type") ?? "";
|
|
46
53
|
if (contentType.startsWith("text/x-component") || hasRedirectLocation) return null;
|
|
@@ -108,4 +115,4 @@ function createDiscardedServerActionRefreshScheduler(options) {
|
|
|
108
115
|
};
|
|
109
116
|
}
|
|
110
117
|
//#endregion
|
|
111
|
-
export { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };
|
|
118
|
+
export { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, createServerActionResultFacts, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, resolveServerActionOperationLane, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction, shouldSyncServerActionHttpFallbackHead };
|