vinext 0.1.1 → 0.1.3
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 +2 -5
- package/dist/build/client-build-config.d.ts +7 -1
- package/dist/build/client-build-config.js +9 -1
- package/dist/build/prerender.d.ts +9 -1
- package/dist/build/prerender.js +41 -12
- package/dist/build/run-prerender.d.ts +10 -2
- package/dist/build/run-prerender.js +15 -1
- package/dist/check.js +4 -3
- package/dist/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/navigation-runtime.d.ts +3 -2
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/window-next.d.ts +8 -5
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.d.ts +11 -4
- package/dist/config/config-matchers.js +88 -16
- package/dist/config/next-config.d.ts +59 -4
- package/dist/config/next-config.js +149 -48
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +189 -101
- package/dist/entries/app-browser-entry.d.ts +9 -3
- package/dist/entries/app-browser-entry.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +71 -6
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-ssr-entry.js +1 -1
- package/dist/entries/pages-client-entry.js +54 -9
- package/dist/entries/pages-server-entry.js +48 -11
- package/dist/index.d.ts +0 -2
- package/dist/index.js +285 -139
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +10 -5
- package/dist/plugins/optimize-imports.js +27 -21
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +25 -2
- package/dist/routing/app-route-graph.js +91 -22
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +23 -2
- package/dist/routing/pages-router.js +3 -3
- package/dist/routing/utils.d.ts +35 -6
- package/dist/routing/utils.js +59 -7
- package/dist/server/api-handler.d.ts +6 -1
- package/dist/server/api-handler.js +21 -15
- package/dist/server/app-browser-action-result.d.ts +19 -6
- package/dist/server/app-browser-action-result.js +19 -10
- package/dist/server/app-browser-entry.js +269 -297
- package/dist/server/app-browser-error.d.ts +10 -3
- package/dist/server/app-browser-error.js +47 -6
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-hydration.d.ts +2 -0
- package/dist/server/app-browser-hydration.js +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +7 -4
- package/dist/server/app-browser-navigation-controller.js +33 -9
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
- package/dist/server/app-browser-server-action-navigation.js +9 -0
- package/dist/server/app-browser-state.js +4 -7
- package/dist/server/app-browser-stream.js +86 -43
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +14 -4
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-fallback-renderer.d.ts +3 -1
- package/dist/server/app-fallback-renderer.js +6 -2
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +24 -3
- package/dist/server/app-page-boundary-render.d.ts +3 -1
- package/dist/server/app-page-boundary-render.js +31 -16
- package/dist/server/app-page-cache-render.d.ts +53 -0
- package/dist/server/app-page-cache-render.js +91 -0
- package/dist/server/app-page-cache.d.ts +16 -2
- package/dist/server/app-page-cache.js +71 -8
- package/dist/server/app-page-dispatch.d.ts +34 -0
- package/dist/server/app-page-dispatch.js +167 -97
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +42 -10
- package/dist/server/app-page-execution.d.ts +7 -2
- package/dist/server/app-page-execution.js +53 -18
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +4 -0
- package/dist/server/app-page-render-observation.d.ts +3 -1
- package/dist/server/app-page-render-observation.js +17 -1
- package/dist/server/app-page-render.d.ts +13 -2
- package/dist/server/app-page-render.js +48 -17
- package/dist/server/app-page-request.d.ts +3 -0
- package/dist/server/app-page-request.js +5 -3
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +5 -1
- package/dist/server/app-page-route-wiring.js +21 -11
- package/dist/server/app-page-stream.d.ts +16 -9
- package/dist/server/app-page-stream.js +12 -9
- package/dist/server/app-pages-bridge.d.ts +18 -0
- package/dist/server/app-pages-bridge.js +22 -5
- package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
- package/dist/server/app-ppr-fallback-shell-render.js +26 -0
- package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
- package/dist/server/app-ppr-fallback-shell.js +8 -1
- package/dist/server/app-route-handler-dispatch.js +9 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.js +12 -1
- package/dist/server/app-router-entry.js +5 -0
- package/dist/server/app-rsc-cache-busting.js +2 -0
- package/dist/server/app-rsc-handler.d.ts +25 -0
- package/dist/server/app-rsc-handler.js +153 -53
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +3 -0
- package/dist/server/app-rsc-route-matching.js +2 -0
- package/dist/server/app-segment-config.d.ts +9 -1
- package/dist/server/app-segment-config.js +12 -3
- package/dist/server/app-server-action-execution.d.ts +12 -0
- package/dist/server/app-server-action-execution.js +47 -15
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +81 -8
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/cache-control.js +4 -0
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +287 -63
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/hybrid-route-priority.d.ts +22 -0
- package/dist/server/hybrid-route-priority.js +33 -0
- package/dist/server/image-optimization.d.ts +18 -9
- package/dist/server/image-optimization.js +37 -23
- package/dist/server/implicit-tags.d.ts +2 -1
- package/dist/server/implicit-tags.js +4 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +186 -22
- package/dist/server/navigation-planner.js +302 -0
- package/dist/server/navigation-trace.d.ts +18 -1
- package/dist/server/navigation-trace.js +18 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +20 -3
- package/dist/server/pages-api-route.js +19 -3
- package/dist/server/pages-asset-tags.d.ts +16 -4
- package/dist/server/pages-asset-tags.js +22 -12
- package/dist/server/pages-data-route.d.ts +8 -1
- package/dist/server/pages-data-route.js +16 -3
- package/dist/server/pages-get-initial-props.d.ts +54 -4
- package/dist/server/pages-get-initial-props.js +43 -1
- package/dist/server/pages-node-compat.d.ts +3 -11
- package/dist/server/pages-node-compat.js +175 -122
- package/dist/server/pages-page-data.d.ts +39 -2
- package/dist/server/pages-page-data.js +261 -46
- package/dist/server/pages-page-handler.d.ts +5 -2
- package/dist/server/pages-page-handler.js +78 -25
- package/dist/server/pages-page-response.d.ts +47 -2
- package/dist/server/pages-page-response.js +73 -9
- package/dist/server/pages-readiness.d.ts +1 -1
- package/dist/server/pages-request-pipeline.d.ts +16 -1
- package/dist/server/pages-request-pipeline.js +96 -38
- package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
- package/dist/server/pregenerated-concrete-paths.js +2 -19
- package/dist/server/prerender-manifest.d.ts +33 -0
- package/dist/server/prerender-manifest.js +54 -0
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prod-server.d.ts +39 -1
- package/dist/server/prod-server.js +107 -37
- package/dist/server/request-pipeline.d.ts +3 -15
- package/dist/server/request-pipeline.js +58 -47
- package/dist/server/rsc-stream-hints.d.ts +5 -1
- package/dist/server/rsc-stream-hints.js +6 -1
- package/dist/server/seed-cache.js +10 -18
- package/dist/shims/app-router-scroll-state.d.ts +3 -1
- package/dist/shims/app-router-scroll-state.js +14 -2
- package/dist/shims/app-router-scroll.d.ts +3 -0
- package/dist/shims/app-router-scroll.js +28 -18
- package/dist/shims/cache-runtime.js +12 -4
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -1
- package/dist/shims/cdn-cache.d.ts +5 -5
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +79 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +6 -4
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +38 -11
- package/dist/shims/error.react-server.d.ts +9 -0
- package/dist/shims/error.react-server.js +6 -0
- package/dist/shims/fetch-cache.d.ts +11 -1
- package/dist/shims/fetch-cache.js +55 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +66 -5
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +18 -23
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
- package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
- package/dist/shims/internal/navigation-untracked.d.ts +35 -0
- package/dist/shims/internal/navigation-untracked.js +55 -0
- package/dist/shims/internal/pages-data-target.d.ts +7 -2
- package/dist/shims/internal/pages-data-target.js +17 -8
- package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
- package/dist/shims/internal/pages-router-accessor.js +13 -0
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js +3 -1
- package/dist/shims/link.js +12 -5
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +14 -17
- package/dist/shims/navigation.js +93 -46
- package/dist/shims/ppr-fallback-shell.d.ts +5 -1
- package/dist/shims/ppr-fallback-shell.js +28 -7
- package/dist/shims/router.d.ts +13 -2
- package/dist/shims/router.js +434 -116
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +33 -2
- package/dist/shims/server.js +75 -18
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +2 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/built-asset-url.d.ts +4 -0
- package/dist/utils/built-asset-url.js +11 -0
- package/dist/utils/client-build-manifest.js +15 -5
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/commonjs-loader.d.ts +16 -0
- package/dist/utils/commonjs-loader.js +100 -0
- package/dist/utils/deployment-id.d.ts +8 -0
- package/dist/utils/deployment-id.js +22 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +8 -1
- package/dist/utils/path.js +13 -1
- package/package.json +2 -2
- package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
- package/dist/shims/internal/parse-cookie-header.js +0 -30
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, nodeArray } from "./ast-utils.js";
|
|
2
|
+
import { parseAst } from "vite";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
//#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
|
+
function createChildScope(node, parent) {
|
|
45
|
+
if (node.type !== "Program" && node.type !== "BlockStatement" && node.type !== "StaticBlock" && node.type !== "SwitchStatement" && node.type !== "CatchClause" && node.type !== "ForStatement" && node.type !== "ForInStatement" && node.type !== "ForOfStatement" && node.type !== "ClassDeclaration" && node.type !== "ClassExpression") return null;
|
|
46
|
+
const scope = createScope(parent);
|
|
47
|
+
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") collectBindingNames(node.id, scope.bindings);
|
|
48
|
+
else if (node.type === "CatchClause") collectBindingNames(node.param, scope.bindings);
|
|
49
|
+
collectScopeBindings(node, scope);
|
|
50
|
+
if (node.type === "SwitchStatement") {
|
|
51
|
+
for (const switchCase of nodeArray(node.cases)) if (isAstRecord(switchCase)) collectScopeBindings(switchCase, scope);
|
|
52
|
+
}
|
|
53
|
+
return scope;
|
|
54
|
+
}
|
|
55
|
+
function getTypeofWindowReplacement(environment) {
|
|
56
|
+
return environment.config.consumer === "client" ? "object" : "undefined";
|
|
57
|
+
}
|
|
58
|
+
function stringLiteralValue(node) {
|
|
59
|
+
if (!isAstRecord(node)) return null;
|
|
60
|
+
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function evaluateTypeofWindowComparison(node, replacement, scope) {
|
|
64
|
+
if (!isAstRecord(node) || node.type !== "BinaryExpression") return null;
|
|
65
|
+
if (![
|
|
66
|
+
"==",
|
|
67
|
+
"===",
|
|
68
|
+
"!=",
|
|
69
|
+
"!=="
|
|
70
|
+
].includes(String(node.operator))) return null;
|
|
71
|
+
const left = isAstRecord(node.left) ? node.left : null;
|
|
72
|
+
const right = isAstRecord(node.right) ? node.right : null;
|
|
73
|
+
const leftIsTypeofWindow = left?.type === "UnaryExpression" && left.operator === "typeof" && isIdentifierNamed(left.argument, "window") && !hasBinding(scope, "window");
|
|
74
|
+
const rightIsTypeofWindow = right?.type === "UnaryExpression" && right.operator === "typeof" && isIdentifierNamed(right.argument, "window") && !hasBinding(scope, "window");
|
|
75
|
+
const comparedValue = leftIsTypeofWindow ? stringLiteralValue(right) : rightIsTypeofWindow ? stringLiteralValue(left) : null;
|
|
76
|
+
if (comparedValue === null) return null;
|
|
77
|
+
const equal = replacement === comparedValue;
|
|
78
|
+
return node.operator === "==" || node.operator === "===" ? equal : !equal;
|
|
79
|
+
}
|
|
80
|
+
function replaceTypeofWindow(code, replacement) {
|
|
81
|
+
if (!/typeof\s+window/.test(code)) return null;
|
|
82
|
+
let ast;
|
|
83
|
+
try {
|
|
84
|
+
ast = parseAst(code);
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const output = new MagicString(code);
|
|
89
|
+
let changed = false;
|
|
90
|
+
if (!isAstRecord(ast)) return null;
|
|
91
|
+
const rootScope = createScope(null);
|
|
92
|
+
collectScopeBindings(ast, rootScope);
|
|
93
|
+
collectVarBindings(ast, rootScope);
|
|
94
|
+
function visit(node, parentScope) {
|
|
95
|
+
if (isFunctionNode(node)) {
|
|
96
|
+
const parameterScope = createScope(parentScope);
|
|
97
|
+
collectBindingNames(node.id, parameterScope.bindings);
|
|
98
|
+
for (const parameter of nodeArray(node.params)) {
|
|
99
|
+
collectBindingNames(parameter, parameterScope.bindings);
|
|
100
|
+
if (isAstRecord(parameter)) visit(parameter, parameterScope);
|
|
101
|
+
}
|
|
102
|
+
if (isAstRecord(node.body)) if (node.body.type === "BlockStatement") {
|
|
103
|
+
const bodyScope = createScope(parameterScope);
|
|
104
|
+
collectVarBindings(node.body, bodyScope);
|
|
105
|
+
visit(node.body, bodyScope);
|
|
106
|
+
} else visit(node.body, parameterScope);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const scope = createChildScope(node, parentScope) ?? parentScope;
|
|
110
|
+
if (node.type === "IfStatement" && hasRange(node)) {
|
|
111
|
+
const result = evaluateTypeofWindowComparison(node.test, replacement, scope);
|
|
112
|
+
if (result !== null) {
|
|
113
|
+
const selected = result ? node.consequent : node.alternate;
|
|
114
|
+
if (isAstRecord(selected) && hasRange(selected)) {
|
|
115
|
+
output.remove(node.start, selected.start);
|
|
116
|
+
output.remove(selected.end, node.end);
|
|
117
|
+
visit(selected, scope);
|
|
118
|
+
} else output.overwrite(node.start, node.end, ";");
|
|
119
|
+
changed = true;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (node.type === "ConditionalExpression" && hasRange(node)) {
|
|
124
|
+
const result = evaluateTypeofWindowComparison(node.test, replacement, scope);
|
|
125
|
+
const selected = result ? node.consequent : node.alternate;
|
|
126
|
+
if (result !== null && isAstRecord(selected) && hasRange(selected)) {
|
|
127
|
+
output.overwrite(node.start, selected.start, "(");
|
|
128
|
+
if (selected.end < node.end) output.overwrite(selected.end, node.end, ")");
|
|
129
|
+
else output.appendLeft(selected.end, ")");
|
|
130
|
+
visit(selected, scope);
|
|
131
|
+
changed = true;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (node.type === "UnaryExpression" && node.operator === "typeof" && isIdentifierNamed(node.argument, "window") && !hasBinding(scope, "window") && hasRange(node)) {
|
|
136
|
+
output.overwrite(node.start, node.end, JSON.stringify(replacement));
|
|
137
|
+
changed = true;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
forEachAstChild(node, (child) => visit(child, scope));
|
|
141
|
+
}
|
|
142
|
+
for (const node of ast.body) if (isAstRecord(node)) visit(node, rootScope);
|
|
143
|
+
if (!changed) return null;
|
|
144
|
+
return {
|
|
145
|
+
code: output.toString(),
|
|
146
|
+
map: output.generateMap({ hires: "boundary" })
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { getTypeofWindowReplacement, replaceTypeofWindow };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/wasm-module-import.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* vinext:wasm-module-import — handle `import x from '*.wasm?module'`.
|
|
6
|
+
*
|
|
7
|
+
* Resolutions marked external by Vite or a target adapter are preserved. For
|
|
8
|
+
* non-external resolutions, this plugin reads the WASM file, inlines it as
|
|
9
|
+
* base64, and exports a compiled WebAssembly.Module.
|
|
10
|
+
*
|
|
11
|
+
* Fixes #1351.
|
|
12
|
+
*/
|
|
13
|
+
declare function createWasmModuleImportPlugin(): Plugin;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { createWasmModuleImportPlugin };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { stripViteModuleQuery } from "../utils/path.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
//#region src/plugins/wasm-module-import.ts
|
|
4
|
+
/**
|
|
5
|
+
* vinext:wasm-module-import — handle `import x from '*.wasm?module'`.
|
|
6
|
+
*
|
|
7
|
+
* Resolutions marked external by Vite or a target adapter are preserved. For
|
|
8
|
+
* non-external resolutions, this plugin reads the WASM file, inlines it as
|
|
9
|
+
* base64, and exports a compiled WebAssembly.Module.
|
|
10
|
+
*
|
|
11
|
+
* Fixes #1351.
|
|
12
|
+
*/
|
|
13
|
+
function createWasmModuleImportPlugin() {
|
|
14
|
+
return {
|
|
15
|
+
name: "vinext:wasm-module-import",
|
|
16
|
+
enforce: "pre",
|
|
17
|
+
resolveId: {
|
|
18
|
+
filter: { id: /\.wasm\?module$/ },
|
|
19
|
+
async handler(source, importer) {
|
|
20
|
+
if (this.environment?.name === "client") return null;
|
|
21
|
+
if ((importer ? (importer.startsWith("\0") ? importer.slice(1) : importer).split("?")[0] : "").includes("@vercel/og")) return null;
|
|
22
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
23
|
+
if (!resolved) return null;
|
|
24
|
+
if (resolved.external) return resolved;
|
|
25
|
+
return `\0vinext-wasm-module:${stripViteModuleQuery(resolved.id)}`;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
load: {
|
|
29
|
+
filter: { id: /^\u0000vinext-wasm-module:/ },
|
|
30
|
+
handler(id) {
|
|
31
|
+
const filePath = id.replace(/^\u0000vinext-wasm-module:/, "");
|
|
32
|
+
this.addWatchFile(filePath);
|
|
33
|
+
let bytes;
|
|
34
|
+
try {
|
|
35
|
+
bytes = fs.readFileSync(filePath);
|
|
36
|
+
} catch {
|
|
37
|
+
return this.error(`[vinext] Could not read WASM file: ${filePath}`);
|
|
38
|
+
}
|
|
39
|
+
const base64 = bytes.toString("base64");
|
|
40
|
+
return [
|
|
41
|
+
`const _b64 = ${JSON.stringify(base64)};`,
|
|
42
|
+
`const _buf = Uint8Array.from(atob(_b64), c => c.charCodeAt(0));`,
|
|
43
|
+
`export default await WebAssembly.compile(_buf.buffer);`
|
|
44
|
+
].join("\n");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { createWasmModuleImportPlugin };
|
|
@@ -21,7 +21,8 @@ type InterceptingRoute = {
|
|
|
21
21
|
* @see https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/interception-routes.ts
|
|
22
22
|
*/
|
|
23
23
|
sourceMatchPattern: string; /** Absolute path to the intercepting page component */
|
|
24
|
-
pagePath: string; /**
|
|
24
|
+
pagePath: string; /** Filesystem segments from app/ root to the intercepting page directory. */
|
|
25
|
+
sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
|
|
25
26
|
layoutPaths: string[]; /** Parameter names for dynamic segments */
|
|
26
27
|
params: string[];
|
|
27
28
|
/**
|
|
@@ -276,6 +277,28 @@ declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher):
|
|
|
276
277
|
routeManifest: RouteManifest;
|
|
277
278
|
}>;
|
|
278
279
|
declare function computeRootParamNames(routeSegments: readonly string[], layoutTreePositions: readonly number[]): string[];
|
|
280
|
+
/**
|
|
281
|
+
* Find the best route to attach a sibling intercept to, given the directory
|
|
282
|
+
* that contains the interception marker.
|
|
283
|
+
*
|
|
284
|
+
* 1. Exact hit: a route whose page/handler lives directly in `dir`.
|
|
285
|
+
* 2. Subtree hit: shallowest route whose page lives anywhere under `dir`
|
|
286
|
+
* (handles catch-all routes like `/templates/:catchAll+`).
|
|
287
|
+
* 3. Ancestor walk: walk up the directory tree toward `appDir` looking for
|
|
288
|
+
* any of the above. This handles the case where the marker directory has
|
|
289
|
+
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
290
|
+
* `deep/path/page.tsx`).
|
|
291
|
+
*
|
|
292
|
+
* All comparisons happen in forward-slash space: `appDir` is forward-slash
|
|
293
|
+
* (normalized once in the config hook), but `dir` and route file paths
|
|
294
|
+
* descend through native `path.join`/`path.dirname`, which reintroduce
|
|
295
|
+
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
296
|
+
* termination never fires there and the walk overshoots the app root.
|
|
297
|
+
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
298
|
+
*
|
|
299
|
+
* Exported for tests.
|
|
300
|
+
*/
|
|
301
|
+
declare function findOwnerRouteForDir(dir: string, appDir: string, routes: readonly AppRouteGraphRoute[], routesByDir: Map<string, AppRouteGraphRoute>): AppRouteGraphRoute | null;
|
|
279
302
|
/**
|
|
280
303
|
* Convert filesystem path segments to URL route parts, skipping invisible segments
|
|
281
304
|
* (route groups, @slots, ".") and converting dynamic segment syntax to Express-style
|
|
@@ -310,4 +333,4 @@ declare function computeAppRouteStaticSiblings(allRoutes: readonly {
|
|
|
310
333
|
patternParts?: readonly string[] | null;
|
|
311
334
|
}): string[];
|
|
312
335
|
//#endregion
|
|
313
|
-
export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, isInvisibleSegment };
|
|
336
|
+
export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
+
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
2
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
3
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
4
5
|
import { compareStrings } from "../utils/compare.js";
|
|
@@ -345,23 +346,25 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
345
346
|
}
|
|
346
347
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
347
348
|
const routes = [];
|
|
349
|
+
const scanMatcher = { ...matcher };
|
|
350
|
+
findFileProbeCache.set(scanMatcher, /* @__PURE__ */ new Map());
|
|
348
351
|
const excludeDir = (name) => name.startsWith("@") && name !== "@children" || name.startsWith("_") || isInterceptionMarkerDir(name);
|
|
349
|
-
for await (const file of scanWithExtensions("**/page", appDir,
|
|
350
|
-
const route = fileToAppRoute(file, appDir, "page",
|
|
352
|
+
for await (const file of scanWithExtensions("**/page", appDir, scanMatcher.extensions, excludeDir)) {
|
|
353
|
+
const route = fileToAppRoute(file, appDir, "page", scanMatcher);
|
|
351
354
|
if (route) routes.push(route);
|
|
352
355
|
}
|
|
353
|
-
for await (const file of scanWithExtensions("**/route", appDir,
|
|
354
|
-
const route = fileToAppRoute(file, appDir, "route",
|
|
356
|
+
for await (const file of scanWithExtensions("**/route", appDir, scanMatcher.extensions, excludeDir)) {
|
|
357
|
+
const route = fileToAppRoute(file, appDir, "route", scanMatcher);
|
|
355
358
|
if (route) routes.push(route);
|
|
356
359
|
}
|
|
357
360
|
const routePatterns = new Set(routes.map((route) => route.pattern));
|
|
358
361
|
const ghostParentRoutes = [];
|
|
359
|
-
for await (const file of scanWithExtensions("**/layout", appDir,
|
|
362
|
+
for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
|
|
360
363
|
const dir = path.dirname(file);
|
|
361
364
|
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
362
365
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
363
|
-
if (discoverParallelSlots(routeDir, appDir,
|
|
364
|
-
const route = directoryToAppRoute(dir, appDir,
|
|
366
|
+
if (discoverParallelSlots(routeDir, appDir, scanMatcher).length === 0) continue;
|
|
367
|
+
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null);
|
|
365
368
|
if (!route) continue;
|
|
366
369
|
if (routePatterns.has(route.pattern)) {
|
|
367
370
|
ghostParentRoutes.push(route);
|
|
@@ -370,13 +373,13 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
370
373
|
routes.push(route);
|
|
371
374
|
routePatterns.add(route.pattern);
|
|
372
375
|
}
|
|
373
|
-
const slotSubRoutes = discoverSlotSubRoutes(routes,
|
|
376
|
+
const slotSubRoutes = discoverSlotSubRoutes(routes, scanMatcher, ghostParentRoutes);
|
|
374
377
|
routes.push(...slotSubRoutes);
|
|
375
|
-
discoverSiblingInterceptingRoutes(routes, appDir,
|
|
378
|
+
discoverSiblingInterceptingRoutes(routes, appDir, scanMatcher);
|
|
376
379
|
validatePageRouteConflicts(routes, appDir);
|
|
377
380
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
378
381
|
validateRoutePatterns([...new Set(routes.flatMap((route) => [...route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern)), ...route.siblingIntercepts.map((intercept) => intercept.targetPattern)]))]);
|
|
379
|
-
routes
|
|
382
|
+
sortRoutes(routes);
|
|
380
383
|
return {
|
|
381
384
|
routes,
|
|
382
385
|
routeManifest: createRouteManifest(routes)
|
|
@@ -485,7 +488,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
485
488
|
const existingRoute = routesByPattern.get(pattern);
|
|
486
489
|
if (existingRoute) {
|
|
487
490
|
if (existingRoute.routePath && !existingRoute.pagePath) throw new Error(`You cannot have two routes that resolve to the same path ("${pattern}").`);
|
|
488
|
-
applySlotSubPages(existingRoute, slotPages, rawSegments);
|
|
491
|
+
if (urlParts.length > 0) applySlotSubPages(existingRoute, slotPages, rawSegments);
|
|
489
492
|
continue;
|
|
490
493
|
}
|
|
491
494
|
const syntheticParts = [...parentRoute.patternParts, ...urlParts];
|
|
@@ -1026,6 +1029,34 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
1026
1029
|
return true;
|
|
1027
1030
|
}
|
|
1028
1031
|
/**
|
|
1032
|
+
* Find a page file at the root URL level of a parallel slot directory, including
|
|
1033
|
+
* through transparent route-group subdirectories (e.g. `@slot/(group)/page.tsx`
|
|
1034
|
+
* is equivalent to `@slot/page.tsx` since `(group)` is invisible in the URL).
|
|
1035
|
+
*
|
|
1036
|
+
* Returns the absolute page path, or null if no root-level page is found.
|
|
1037
|
+
*
|
|
1038
|
+
* Only descends into route-group directories (those whose name starts with `(`
|
|
1039
|
+
* and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
|
|
1040
|
+
* are not transparent and are therefore not searched.
|
|
1041
|
+
*/
|
|
1042
|
+
function findSlotRootPage(slotDir, matcher) {
|
|
1043
|
+
const directPage = findFile(slotDir, "page", matcher);
|
|
1044
|
+
if (directPage) return directPage;
|
|
1045
|
+
let entries;
|
|
1046
|
+
try {
|
|
1047
|
+
entries = fs.readdirSync(slotDir, { withFileTypes: true });
|
|
1048
|
+
} catch {
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
for (const entry of entries) {
|
|
1052
|
+
if (!entry.isDirectory()) continue;
|
|
1053
|
+
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
1054
|
+
const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
|
|
1055
|
+
if (found) return found;
|
|
1056
|
+
}
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1029
1060
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
1030
1061
|
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page or default component.
|
|
1031
1062
|
*/
|
|
@@ -1038,7 +1069,7 @@ function discoverParallelSlots(dir, appDir, matcher) {
|
|
|
1038
1069
|
if (entry.name === "@children") continue;
|
|
1039
1070
|
const slotName = entry.name.slice(1);
|
|
1040
1071
|
const slotDir = path.join(dir, entry.name);
|
|
1041
|
-
const pagePath =
|
|
1072
|
+
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
1042
1073
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
1043
1074
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
1044
1075
|
if (!pagePath && !defaultPath && interceptingRoutes.length === 0) continue;
|
|
@@ -1127,7 +1158,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1127
1158
|
for (const route of routes) {
|
|
1128
1159
|
const filePath = route.pagePath ?? route.routePath;
|
|
1129
1160
|
if (!filePath) continue;
|
|
1130
|
-
const routeDir = path.dirname(filePath);
|
|
1161
|
+
const routeDir = normalizePathSeparators(path.dirname(filePath));
|
|
1131
1162
|
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1132
1163
|
}
|
|
1133
1164
|
function walk(dir) {
|
|
@@ -1171,22 +1202,32 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1171
1202
|
* any of the above. This handles the case where the marker directory has
|
|
1172
1203
|
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
1173
1204
|
* `deep/path/page.tsx`).
|
|
1205
|
+
*
|
|
1206
|
+
* All comparisons happen in forward-slash space: `appDir` is forward-slash
|
|
1207
|
+
* (normalized once in the config hook), but `dir` and route file paths
|
|
1208
|
+
* descend through native `path.join`/`path.dirname`, which reintroduce
|
|
1209
|
+
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
1210
|
+
* termination never fires there and the walk overshoots the app root.
|
|
1211
|
+
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
1212
|
+
*
|
|
1213
|
+
* Exported for tests.
|
|
1174
1214
|
*/
|
|
1175
1215
|
function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
1176
|
-
|
|
1216
|
+
const appRoot = normalizePathSeparators(appDir);
|
|
1217
|
+
let current = normalizePathSeparators(dir);
|
|
1177
1218
|
while (true) {
|
|
1178
1219
|
const exact = routesByDir.get(current);
|
|
1179
1220
|
if (exact) return exact;
|
|
1180
|
-
const currentWithSep = current +
|
|
1221
|
+
const currentWithSep = current + "/";
|
|
1181
1222
|
let best = null;
|
|
1182
1223
|
for (const route of routes) {
|
|
1183
1224
|
const filePath = route.pagePath ?? route.routePath;
|
|
1184
1225
|
if (!filePath) continue;
|
|
1185
|
-
if (!filePath.startsWith(currentWithSep)) continue;
|
|
1226
|
+
if (!normalizePathSeparators(filePath).startsWith(currentWithSep)) continue;
|
|
1186
1227
|
if (!best || route.patternParts.length < best.patternParts.length) best = route;
|
|
1187
1228
|
}
|
|
1188
1229
|
if (best) return best;
|
|
1189
|
-
if (current ===
|
|
1230
|
+
if (current === appRoot) break;
|
|
1190
1231
|
const parent = path.dirname(current);
|
|
1191
1232
|
if (parent === current) break;
|
|
1192
1233
|
current = parent;
|
|
@@ -1236,6 +1277,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1236
1277
|
targetPattern: targetPattern.pattern,
|
|
1237
1278
|
sourceMatchPattern,
|
|
1238
1279
|
pagePath: page,
|
|
1280
|
+
sourcePageSegments: normalizePathSeparators(path.relative(appDir, path.dirname(page))).split("/").filter(Boolean),
|
|
1239
1281
|
params: targetPattern.params
|
|
1240
1282
|
});
|
|
1241
1283
|
}
|
|
@@ -1345,10 +1387,37 @@ function markerForInterceptionConvention(convention) {
|
|
|
1345
1387
|
}
|
|
1346
1388
|
}
|
|
1347
1389
|
/**
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
1390
|
+
* Scan-scoped cache of convention-file probes, keyed by the per-scan matcher
|
|
1391
|
+
* created in `buildAppRouteGraph`. A single scan walks the appDir→leaf chain
|
|
1392
|
+
* separately for every route (layouts, templates, errors, boundaries, slots),
|
|
1393
|
+
* so shared ancestor directories — the `app/` root above all — get re-probed
|
|
1394
|
+
* once per descendant route. The probe result is deterministic within one scan
|
|
1395
|
+
* (the filesystem does not change mid-build), so memoizing it removes the
|
|
1396
|
+
* dominant cross-route redundancy.
|
|
1397
|
+
*
|
|
1398
|
+
* Keyed by matcher so the cache lifetime is exactly one `buildAppRouteGraph`
|
|
1399
|
+
* call: the scan registers a fresh matcher clone, and the entry is unreachable
|
|
1400
|
+
* (and GC-eligible) once the scan returns. A fresh key per scan is also what
|
|
1401
|
+
* makes this concurrency-safe — overlapping builds never share probe state.
|
|
1350
1402
|
*/
|
|
1351
|
-
const
|
|
1403
|
+
const findFileProbeCache = /* @__PURE__ */ new WeakMap();
|
|
1404
|
+
/**
|
|
1405
|
+
* Find a file by name (without extension) in a directory, checking configured
|
|
1406
|
+
* pageExtensions. Memoizes through `findFileProbeCache` when the matcher has a
|
|
1407
|
+
* registered per-scan cache; otherwise falls back to a direct probe (identical
|
|
1408
|
+
* result). The `null` "not found" outcome is cached too, so repeated misses on
|
|
1409
|
+
* shared ancestors cost a single set of `existsSync` calls per scan.
|
|
1410
|
+
*/
|
|
1411
|
+
function findFile(dir, name, matcher) {
|
|
1412
|
+
const cache = findFileProbeCache.get(matcher);
|
|
1413
|
+
if (!cache) return findFileWithExts(dir, name, matcher);
|
|
1414
|
+
const key = `${dir}\0${name}`;
|
|
1415
|
+
const cached = cache.get(key);
|
|
1416
|
+
if (cached !== void 0) return cached;
|
|
1417
|
+
const result = findFileWithExts(dir, name, matcher);
|
|
1418
|
+
cache.set(key, result);
|
|
1419
|
+
return result;
|
|
1420
|
+
}
|
|
1352
1421
|
/**
|
|
1353
1422
|
* Convert filesystem path segments to URL route parts, skipping invisible segments
|
|
1354
1423
|
* (route groups, @slots, ".") and converting dynamic segment syntax to Express-style
|
|
@@ -1446,4 +1515,4 @@ function computeAppRouteStaticSiblings(allRoutes, matchedRoute) {
|
|
|
1446
1515
|
return Array.from(siblings);
|
|
1447
1516
|
}
|
|
1448
1517
|
//#endregion
|
|
1449
|
-
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, isInvisibleSegment };
|
|
1518
|
+
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
@@ -43,9 +43,18 @@ declare function findFileWithExts(dir: string, name: string, matcher: ValidFileM
|
|
|
43
43
|
* See: cloudflare/vinext#1502
|
|
44
44
|
*/
|
|
45
45
|
declare function buildViteResolveExtensions(pageExtensions?: readonly string[] | null, viteDefaults?: readonly string[]): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Normalize an explicit Next.js resolver extension list for Vite.
|
|
48
|
+
*
|
|
49
|
+
* Unlike `pageExtensions`, both Turbopack's `resolveExtensions` and webpack's
|
|
50
|
+
* `resolve.extensions` replace their resolver defaults. The empty string is a
|
|
51
|
+
* webpack/Turbopack convention for trying the import exactly as written; Vite
|
|
52
|
+
* already does that before appending extensions, so it must be omitted here.
|
|
53
|
+
*/
|
|
54
|
+
declare function normalizeViteResolveExtensions(extensions: readonly string[]): string[];
|
|
46
55
|
/**
|
|
47
56
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
48
57
|
*/
|
|
49
58
|
declare function scanWithExtensions(stem: string, cwd: string, extensions: readonly string[], exclude?: (name: string) => boolean): AsyncGenerator<string>;
|
|
50
59
|
//#endregion
|
|
51
|
-
export { ValidFileMatcher, buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, scanWithExtensions };
|
|
60
|
+
export { ValidFileMatcher, buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
|
|
@@ -69,7 +69,7 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
69
69
|
*/
|
|
70
70
|
function findFileWithExts(dir, name, matcher) {
|
|
71
71
|
for (const ext of matcher.dottedExtensions) {
|
|
72
|
-
const filePath = path.join(dir, name + ext);
|
|
72
|
+
const filePath = path.posix.join(dir, name + ext);
|
|
73
73
|
if (existsSync(filePath)) return filePath;
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
@@ -113,6 +113,27 @@ function buildViteResolveExtensions(pageExtensions, viteDefaults = [
|
|
|
113
113
|
return result;
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
|
+
* Normalize an explicit Next.js resolver extension list for Vite.
|
|
117
|
+
*
|
|
118
|
+
* Unlike `pageExtensions`, both Turbopack's `resolveExtensions` and webpack's
|
|
119
|
+
* `resolve.extensions` replace their resolver defaults. The empty string is a
|
|
120
|
+
* webpack/Turbopack convention for trying the import exactly as written; Vite
|
|
121
|
+
* already does that before appending extensions, so it must be omitted here.
|
|
122
|
+
*/
|
|
123
|
+
function normalizeViteResolveExtensions(extensions) {
|
|
124
|
+
const seen = /* @__PURE__ */ new Set();
|
|
125
|
+
const result = [];
|
|
126
|
+
for (const extension of extensions) {
|
|
127
|
+
const trimmed = extension.trim();
|
|
128
|
+
if (!trimmed) continue;
|
|
129
|
+
const dotted = trimmed.startsWith(".") ? trimmed : `.${trimmed}`;
|
|
130
|
+
if (seen.has(dotted)) continue;
|
|
131
|
+
seen.add(dotted);
|
|
132
|
+
result.push(dotted);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
116
137
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
117
138
|
*/
|
|
118
139
|
async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
@@ -123,4 +144,4 @@ async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
|
123
144
|
})) yield file;
|
|
124
145
|
}
|
|
125
146
|
//#endregion
|
|
126
|
-
export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, scanWithExtensions };
|
|
147
|
+
export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeRouteSegment, sortRoutes } from "./utils.js";
|
|
2
2
|
import { createValidFileMatcher, scanWithExtensions } from "./file-matcher.js";
|
|
3
3
|
import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js";
|
|
4
4
|
import { createRouteTrieCache, matchRouteWithTrie } from "./route-matching.js";
|
|
@@ -54,7 +54,7 @@ async function scanPageRoutes(pagesDir, matcher) {
|
|
|
54
54
|
if (route) routes.push(route);
|
|
55
55
|
}
|
|
56
56
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
57
|
-
routes
|
|
57
|
+
sortRoutes(routes);
|
|
58
58
|
return routes;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -156,7 +156,7 @@ async function scanApiRoutes(pagesDir, matcher) {
|
|
|
156
156
|
if (route) routes.push(route);
|
|
157
157
|
}
|
|
158
158
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
159
|
-
routes
|
|
159
|
+
sortRoutes(routes);
|
|
160
160
|
return routes;
|
|
161
161
|
}
|
|
162
162
|
//#endregion
|
package/dist/routing/utils.d.ts
CHANGED
|
@@ -1,13 +1,42 @@
|
|
|
1
1
|
//#region src/routing/utils.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Sort
|
|
4
|
-
*
|
|
3
|
+
* Sort routes by precedence — lower score sorts first (higher priority), with a
|
|
4
|
+
* lexicographic tiebreaker on the pattern for determinism. Sorts in place and
|
|
5
|
+
* returns the same array (mirrors `Array.prototype.sort`).
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
+
* `routePrecedence` is a pure function of the pattern, so each pattern's score
|
|
8
|
+
* is computed exactly once up front (decorate-sort) instead of ~2·log n times
|
|
9
|
+
* by a comparator that re-parses on every comparison. The `localeCompare`
|
|
10
|
+
* tiebreaker already guarantees a total order, so the result is byte-identical
|
|
11
|
+
* to comparing precedence inline.
|
|
12
|
+
*
|
|
13
|
+
* Usage: sortRoutes(routes)
|
|
7
14
|
*/
|
|
8
|
-
declare function
|
|
15
|
+
declare function sortRoutes<T extends {
|
|
9
16
|
pattern: string;
|
|
10
|
-
}>(
|
|
17
|
+
}>(routes: T[]): T[];
|
|
18
|
+
/**
|
|
19
|
+
* Single source of truth for hybrid App/Pages route ownership.
|
|
20
|
+
*
|
|
21
|
+
* Mirrors Next.js's DefaultRouteMatcherManager ordering: Pages providers
|
|
22
|
+
* are registered before App providers, then merged dynamic matchers sort
|
|
23
|
+
* together. Returns the router that should own a request/navigation to
|
|
24
|
+
* a URL that matched BOTH routers.
|
|
25
|
+
*
|
|
26
|
+
* Centralised so the server's request handling and the client's link /
|
|
27
|
+
* prefetch / programmatic-navigation paths all reach the same owner for
|
|
28
|
+
* the same (pages pattern, app pattern) pair. This intentionally implements
|
|
29
|
+
* Next.js's segment-tree ordering directly instead of vinext's broader
|
|
30
|
+
* `sortRoutes()` score heuristic. It only arbitrates two routes that already
|
|
31
|
+
* matched the same URL; each router's own trie ordering remains unchanged.
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
* compareHybridRoutePatterns("/:slug", true, "/:slug", true) // → "pages"
|
|
35
|
+
* compareHybridRoutePatterns("/_sites/:slug*", true, "/:slug*", true) // → "pages"
|
|
36
|
+
* compareHybridRoutePatterns("/:path+", true, "/dashboard", false) // → "app"
|
|
37
|
+
* compareHybridRoutePatterns("/", false, "/", false) // → "app"
|
|
38
|
+
*/
|
|
39
|
+
declare function compareHybridRoutePatterns(pagesPattern: string, pagesIsDynamic: boolean, appPattern: string, appIsDynamic: boolean): "app" | "pages";
|
|
11
40
|
/**
|
|
12
41
|
* Decode a filesystem or URL path segment while preserving encoded path delimiters.
|
|
13
42
|
* Mirrors Next.js segment-wise decoding so "%5F" becomes "_" but "%2F" stays "%2F".
|
|
@@ -85,4 +114,4 @@ declare function isOptionalCatchAllSegment(segment: string): boolean;
|
|
|
85
114
|
*/
|
|
86
115
|
declare function countConsumedPathnameSegments(visibleTreePathSegments: readonly string[], pathnameSegmentCount: number): number;
|
|
87
116
|
//#endregion
|
|
88
|
-
export { buildParams,
|
|
117
|
+
export { buildParams, compareHybridRoutePatterns, countConsumedPathnameSegments, decodeMatchedParams, decodeRouteSegment, isCatchAllSegment, isInvisibleSegment, isOptionalCatchAllSegment, normalizePathnameForRouteMatch, normalizePathnameForRouteMatchStrict, sortRoutes, splitPathSegments, splitPathnameForRouteMatch };
|