vinext 0.1.2 → 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/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/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.js +73 -14
- package/dist/config/next-config.d.ts +46 -4
- package/dist/config/next-config.js +147 -48
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +180 -99
- 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 +64 -5
- 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 +53 -8
- package/dist/entries/pages-server-entry.js +41 -5
- package/dist/index.js +200 -62
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/optimize-imports.d.ts +2 -1
- package/dist/plugins/optimize-imports.js +11 -9
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- package/dist/routing/app-route-graph.d.ts +2 -1
- package/dist/routing/app-route-graph.js +44 -14
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +22 -1
- 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 +167 -90
- package/dist/server/app-browser-error.d.ts +10 -6
- package/dist/server/app-browser-error.js +43 -8
- 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 +4 -2
- package/dist/server/app-browser-navigation-controller.js +23 -2
- 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-stream.js +86 -43
- 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 +1 -0
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-optimistic-routing.js +2 -2
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +27 -14
- 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 +62 -1
- package/dist/server/app-page-dispatch.d.ts +26 -0
- package/dist/server/app-page-dispatch.js +149 -92
- package/dist/server/app-page-element-builder.d.ts +1 -0
- package/dist/server/app-page-element-builder.js +5 -2
- package/dist/server/app-page-execution.d.ts +6 -1
- package/dist/server/app-page-execution.js +21 -1
- 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 +12 -1
- package/dist/server/app-page-render.js +42 -4
- package/dist/server/app-page-request.d.ts +2 -0
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +14 -5
- package/dist/server/app-page-stream.d.ts +15 -3
- package/dist/server/app-page-stream.js +11 -5
- 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-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 +154 -54
- 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 +1 -0
- package/dist/server/app-server-action-execution.js +42 -13
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +83 -10
- package/dist/server/cache-control.js +4 -0
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +244 -51
- 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/navigation-planner.d.ts +133 -30
- package/dist/server/navigation-planner.js +114 -0
- package/dist/server/navigation-trace.d.ts +8 -1
- package/dist/server/navigation-trace.js +8 -1
- package/dist/server/pages-api-route.d.ts +6 -0
- package/dist/server/pages-api-route.js +13 -2
- package/dist/server/pages-asset-tags.d.ts +2 -1
- package/dist/server/pages-asset-tags.js +6 -2
- package/dist/server/pages-data-route.d.ts +8 -1
- package/dist/server/pages-data-route.js +11 -2
- 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.js +2 -2
- package/dist/server/pages-page-data.d.ts +11 -2
- package/dist/server/pages-page-data.js +204 -33
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +59 -22
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +7 -4
- package/dist/server/pages-request-pipeline.d.ts +1 -0
- package/dist/server/pages-request-pipeline.js +73 -36
- 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.js +9 -3
- 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 +3 -2
- 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.js +6 -4
- package/dist/shims/error-boundary.d.ts +2 -0
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +3 -2
- 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 +3 -1
- package/dist/shims/fetch-cache.js +45 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/headers.js +29 -4
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.js +8 -17
- 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/navigation.d.ts +8 -2
- package/dist/shims/navigation.js +61 -31
- 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 +419 -128
- package/dist/shims/server.d.ts +16 -1
- package/dist/shims/server.js +44 -12
- package/dist/shims/unified-request-context.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/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/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -1
- package/dist/utils/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +7 -1
- package/dist/utils/path.js +9 -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,152 @@
|
|
|
1
|
+
import { forEachAstChild, hasRange, isAstRecord, nodeArray } from "./ast-utils.js";
|
|
2
|
+
import { parseAst } from "vite";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
//#region src/plugins/extensionless-dynamic-import.ts
|
|
5
|
+
const MODULE_EXTENSIONS = [
|
|
6
|
+
".mjs",
|
|
7
|
+
".js",
|
|
8
|
+
".mts",
|
|
9
|
+
".ts",
|
|
10
|
+
".jsx",
|
|
11
|
+
".tsx",
|
|
12
|
+
".json"
|
|
13
|
+
];
|
|
14
|
+
const TRANSFORMABLE_EXTENSIONS = new Set([
|
|
15
|
+
".mjs",
|
|
16
|
+
".js",
|
|
17
|
+
".mts",
|
|
18
|
+
".ts",
|
|
19
|
+
".jsx",
|
|
20
|
+
".tsx",
|
|
21
|
+
".cjs",
|
|
22
|
+
".cts"
|
|
23
|
+
]);
|
|
24
|
+
function createExtensionlessDynamicImportPlugin() {
|
|
25
|
+
let moduleExtensions = MODULE_EXTENSIONS;
|
|
26
|
+
return {
|
|
27
|
+
name: "vinext:extensionless-dynamic-import",
|
|
28
|
+
enforce: "pre",
|
|
29
|
+
configResolved(config) {
|
|
30
|
+
moduleExtensions = getModuleExtensions(config);
|
|
31
|
+
},
|
|
32
|
+
transform(code, id) {
|
|
33
|
+
if (!/\bimport\s*\(/.test(code)) return null;
|
|
34
|
+
if (isDependencyId(id)) return null;
|
|
35
|
+
const lang = langForId(id);
|
|
36
|
+
if (!lang) return null;
|
|
37
|
+
let ast;
|
|
38
|
+
try {
|
|
39
|
+
ast = parseAst(code, { lang });
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const imports = collectExtensionlessImports(ast, code, moduleExtensions);
|
|
44
|
+
if (imports.length === 0) return null;
|
|
45
|
+
const output = new MagicString(code);
|
|
46
|
+
for (const dynamicImport of imports) {
|
|
47
|
+
const source = code.slice(dynamicImport.sourceStart, dynamicImport.sourceEnd);
|
|
48
|
+
output.overwrite(dynamicImport.start, dynamicImport.end, buildReplacement(source, dynamicImport.globPattern, dynamicImport.moduleExtensions));
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
code: output.toString(),
|
|
52
|
+
map: output.generateMap({ hires: "boundary" })
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function langForId(id) {
|
|
58
|
+
const clean = id.split("?", 1)[0];
|
|
59
|
+
const dot = clean.lastIndexOf(".");
|
|
60
|
+
if (dot < 0) return null;
|
|
61
|
+
const ext = clean.slice(dot).toLowerCase();
|
|
62
|
+
if (!TRANSFORMABLE_EXTENSIONS.has(ext)) return null;
|
|
63
|
+
if (ext === ".ts" || ext === ".mts" || ext === ".cts") return "ts";
|
|
64
|
+
if (ext === ".tsx") return "tsx";
|
|
65
|
+
return "jsx";
|
|
66
|
+
}
|
|
67
|
+
function isDependencyId(id) {
|
|
68
|
+
return id.split("?", 1)[0].replaceAll("\\", "/").includes("/node_modules/");
|
|
69
|
+
}
|
|
70
|
+
function collectExtensionlessImports(ast, code, moduleExtensions) {
|
|
71
|
+
const imports = [];
|
|
72
|
+
function visit(value) {
|
|
73
|
+
if (!isAstRecord(value)) return;
|
|
74
|
+
const parsed = parseExtensionlessImport(value, code, moduleExtensions);
|
|
75
|
+
if (parsed) {
|
|
76
|
+
imports.push(parsed);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
forEachAstChild(value, visit);
|
|
80
|
+
}
|
|
81
|
+
visit(ast);
|
|
82
|
+
return imports;
|
|
83
|
+
}
|
|
84
|
+
function parseExtensionlessImport(node, code, moduleExtensions) {
|
|
85
|
+
if (node.type !== "ImportExpression" || !hasRange(node)) return null;
|
|
86
|
+
if (node.options != null) return null;
|
|
87
|
+
const source = node.source;
|
|
88
|
+
if (!isAstRecord(source) || source.type !== "TemplateLiteral" || !hasRange(source)) return null;
|
|
89
|
+
if (nodeArray(source.expressions).length === 0) return null;
|
|
90
|
+
const quasiTexts = nodeArray(source.quasis).map(templateElementText);
|
|
91
|
+
if (quasiTexts.some((text) => text == null)) return null;
|
|
92
|
+
const texts = quasiTexts;
|
|
93
|
+
const first = texts[0];
|
|
94
|
+
if (!isImportPrefix(code.slice(node.start, source.start))) return null;
|
|
95
|
+
if (!(first.startsWith("./") || first.startsWith("../"))) return null;
|
|
96
|
+
if (texts.some((text) => /[*?[\]{}()!?#]/.test(text))) return null;
|
|
97
|
+
if (texts.slice(1).some((text) => text.includes("."))) return null;
|
|
98
|
+
const directoryEnd = first.lastIndexOf("/") + 1;
|
|
99
|
+
const directory = first.slice(0, directoryEnd);
|
|
100
|
+
const filenamePrefix = first.slice(directoryEnd);
|
|
101
|
+
if (filenamePrefix.includes(".")) return null;
|
|
102
|
+
return {
|
|
103
|
+
start: node.start,
|
|
104
|
+
end: node.end,
|
|
105
|
+
sourceStart: source.start,
|
|
106
|
+
sourceEnd: source.end,
|
|
107
|
+
globPattern: filenamePrefix.length > 0 ? [`${first}*`, `${first}*/**/*`] : `${directory}**/*`,
|
|
108
|
+
moduleExtensions
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function isImportPrefix(value) {
|
|
112
|
+
const prefix = value.match(/^import\s*\(/)?.[0];
|
|
113
|
+
if (!prefix) return false;
|
|
114
|
+
let index = prefix.length;
|
|
115
|
+
while (index < value.length) {
|
|
116
|
+
const char = value[index];
|
|
117
|
+
if (/\s/.test(char)) {
|
|
118
|
+
index += 1;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (value.startsWith("/*", index)) {
|
|
122
|
+
const commentEnd = value.indexOf("*/", index + 2);
|
|
123
|
+
if (commentEnd < 0) return false;
|
|
124
|
+
index = commentEnd + 2;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (value.startsWith("//", index)) {
|
|
128
|
+
const lineEnd = value.indexOf("\n", index + 2);
|
|
129
|
+
if (lineEnd < 0) return true;
|
|
130
|
+
index = lineEnd + 1;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
function templateElementText(value) {
|
|
138
|
+
if (!isAstRecord(value) || value.type !== "TemplateElement") return null;
|
|
139
|
+
const templateValue = value.value;
|
|
140
|
+
if (typeof templateValue !== "object" || templateValue === null) return null;
|
|
141
|
+
const cooked = Reflect.get(templateValue, "cooked");
|
|
142
|
+
return typeof cooked === "string" ? cooked : null;
|
|
143
|
+
}
|
|
144
|
+
function getModuleExtensions(config) {
|
|
145
|
+
return config.resolve.extensions.filter((extension) => extension !== ".node");
|
|
146
|
+
}
|
|
147
|
+
function buildReplacement(source, globPattern, moduleExtensions) {
|
|
148
|
+
const extensions = JSON.stringify(moduleExtensions);
|
|
149
|
+
return `((__vinextPath, __vinextModules = import.meta.glob(${JSON.stringify(globPattern)}), __vinextExtensions = ${extensions}) => { const __vinextLoader = __vinextModules[__vinextPath] ?? __vinextExtensions.map((__vinextExtension) => __vinextModules[__vinextPath + __vinextExtension]).find(Boolean) ?? __vinextExtensions.map((__vinextExtension) => __vinextModules[__vinextPath + "/index" + __vinextExtension]).find(Boolean); return __vinextLoader ? __vinextLoader() : Promise.reject(new Error("Cannot find module '" + __vinextPath + "'")); })(${source})`;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { createExtensionlessDynamicImportPlugin };
|
|
@@ -14,6 +14,7 @@ type BarrelExportMap = Map<string, BarrelExportEntry>;
|
|
|
14
14
|
* @see https://github.com/vercel/next.js/blob/9c31bbdaa/packages/next/src/server/config.ts#L1301
|
|
15
15
|
*/
|
|
16
16
|
declare const DEFAULT_OPTIMIZE_PACKAGES: string[];
|
|
17
|
+
declare function createOptimizedImportSourceMatcher(packages: Iterable<string>): (code: string) => boolean;
|
|
17
18
|
/**
|
|
18
19
|
* Build a map of exported names → source sub-module for a barrel package.
|
|
19
20
|
*
|
|
@@ -38,4 +39,4 @@ declare function buildBarrelExportMap(entryPath: string | null, readFile: (filep
|
|
|
38
39
|
*/
|
|
39
40
|
declare function createOptimizeImportsPlugin(getNextConfig: () => ResolvedNextConfig | undefined, getRoot: () => string): Plugin;
|
|
40
41
|
//#endregion
|
|
41
|
-
export { DEFAULT_OPTIMIZE_PACKAGES, buildBarrelExportMap, createOptimizeImportsPlugin };
|
|
42
|
+
export { DEFAULT_OPTIMIZE_PACKAGES, buildBarrelExportMap, createOptimizeImportsPlugin, createOptimizedImportSourceMatcher };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
+
import { escapeRegExp } from "../utils/regex.js";
|
|
2
3
|
import { getAstName } from "./ast-utils.js";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import path from "node:path";
|
|
@@ -100,6 +101,12 @@ const DEFAULT_OPTIMIZE_PACKAGES = [
|
|
|
100
101
|
"react-icons/wi",
|
|
101
102
|
"radix-ui"
|
|
102
103
|
];
|
|
104
|
+
function createOptimizedImportSourceMatcher(packages) {
|
|
105
|
+
const pattern = [...packages].map(escapeRegExp).join("|");
|
|
106
|
+
if (!pattern) return () => false;
|
|
107
|
+
const importFromPattern = new RegExp(String.raw`(?:^|[;}\n\r])\s*import(?!\s*\()(?:(?!\bfrom\b)[\s\S])*?\bfrom\s*["'](?:${pattern})["']`, "m");
|
|
108
|
+
return (code) => code.includes("import") && code.includes("from") && importFromPattern.test(code);
|
|
109
|
+
}
|
|
103
110
|
/**
|
|
104
111
|
* Resolve a package.json exports value to a string entry path.
|
|
105
112
|
* Prefers node → import → module → default conditions, recursing into nested objects.
|
|
@@ -417,14 +424,14 @@ function createOptimizeImportsPlugin(getNextConfig, getRoot) {
|
|
|
417
424
|
};
|
|
418
425
|
const entryPathCache = /* @__PURE__ */ new Map();
|
|
419
426
|
let optimizedPackages = /* @__PURE__ */ new Set();
|
|
420
|
-
let
|
|
427
|
+
let hasOptimizedImportSource = () => false;
|
|
421
428
|
const registeredBarrels = /* @__PURE__ */ new Set();
|
|
422
429
|
return {
|
|
423
430
|
name: "vinext:optimize-imports",
|
|
424
431
|
buildStart() {
|
|
425
432
|
const nextConfig = getNextConfig();
|
|
426
433
|
optimizedPackages = new Set([...DEFAULT_OPTIMIZE_PACKAGES, ...nextConfig?.optimizePackageImports ?? []]);
|
|
427
|
-
|
|
434
|
+
hasOptimizedImportSource = createOptimizedImportSourceMatcher(optimizedPackages);
|
|
428
435
|
entryPathCache.clear();
|
|
429
436
|
barrelCaches.exportMapCache.clear();
|
|
430
437
|
barrelCaches.subpkgOrigin.clear();
|
|
@@ -445,12 +452,7 @@ function createOptimizeImportsPlugin(getNextConfig, getRoot) {
|
|
|
445
452
|
const preferReactServer = env?.name === "rsc";
|
|
446
453
|
if (id.startsWith("\0")) return null;
|
|
447
454
|
const packages = optimizedPackages;
|
|
448
|
-
|
|
449
|
-
for (const quoted of quotedPackages) if (code.includes(quoted)) {
|
|
450
|
-
hasBarrelImport = true;
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
if (!hasBarrelImport) return null;
|
|
455
|
+
if (!hasOptimizedImportSource(code)) return null;
|
|
454
456
|
let ast;
|
|
455
457
|
try {
|
|
456
458
|
ast = parseAst(code);
|
|
@@ -576,4 +578,4 @@ function createOptimizeImportsPlugin(getNextConfig, getRoot) {
|
|
|
576
578
|
};
|
|
577
579
|
}
|
|
578
580
|
//#endregion
|
|
579
|
-
export { DEFAULT_OPTIMIZE_PACKAGES, buildBarrelExportMap, createOptimizeImportsPlugin };
|
|
581
|
+
export { DEFAULT_OPTIMIZE_PACKAGES, buildBarrelExportMap, createOptimizeImportsPlugin, createOptimizedImportSourceMatcher };
|
package/dist/plugins/postcss.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { importExportWithCommonJsFallback } from "../utils/commonjs-loader.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
5
|
//#region src/plugins/postcss.ts
|
|
6
|
+
async function loadPluginExport(resolvedPath) {
|
|
7
|
+
return importExportWithCommonJsFallback(resolvedPath);
|
|
8
|
+
}
|
|
6
9
|
/**
|
|
7
10
|
* PostCSS config file names to search for, in priority order.
|
|
8
11
|
* Matches the same search order as postcss-load-config / lilconfig.
|
|
@@ -65,8 +68,7 @@ async function resolvePostcssStringPluginsUncached(projectRoot) {
|
|
|
65
68
|
if (configPath.endsWith(".postcssrc")) {
|
|
66
69
|
if (fs.readFileSync(configPath, "utf-8").trim().startsWith("{")) return;
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
config = mod.default ?? mod;
|
|
71
|
+
config = await importExportWithCommonJsFallback(configPath);
|
|
70
72
|
} catch {
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
@@ -76,14 +78,12 @@ async function resolvePostcssStringPluginsUncached(projectRoot) {
|
|
|
76
78
|
try {
|
|
77
79
|
return { plugins: await Promise.all(config.plugins.filter(Boolean).map(async (plugin) => {
|
|
78
80
|
if (typeof plugin === "string") {
|
|
79
|
-
const
|
|
80
|
-
const fn = mod.default ?? mod;
|
|
81
|
+
const fn = await loadPluginExport(req.resolve(plugin));
|
|
81
82
|
return typeof fn === "function" ? fn() : fn;
|
|
82
83
|
}
|
|
83
84
|
if (Array.isArray(plugin) && typeof plugin[0] === "string") {
|
|
84
85
|
const [name, options] = plugin;
|
|
85
|
-
const
|
|
86
|
-
const fn = mod.default ?? mod;
|
|
86
|
+
const fn = await loadPluginExport(req.resolve(name));
|
|
87
87
|
return typeof fn === "function" ? fn(options) : fn;
|
|
88
88
|
}
|
|
89
89
|
return plugin;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/plugins/typeof-window.d.ts
|
|
2
|
+
type WindowType = "object" | "undefined";
|
|
3
|
+
type EnvironmentLike = {
|
|
4
|
+
config: {
|
|
5
|
+
consumer: "client" | "server";
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
declare function getTypeofWindowReplacement(environment: EnvironmentLike): WindowType;
|
|
9
|
+
declare function replaceTypeofWindow(code: string, replacement: WindowType): {
|
|
10
|
+
code: string;
|
|
11
|
+
map: import("magic-string").SourceMap;
|
|
12
|
+
} | null;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { getTypeofWindowReplacement, replaceTypeofWindow };
|
|
@@ -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 };
|
|
@@ -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
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
-
import {
|
|
2
|
+
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
3
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
4
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
5
5
|
import { compareStrings } from "../utils/compare.js";
|
|
@@ -346,23 +346,25 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
346
346
|
}
|
|
347
347
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
348
348
|
const routes = [];
|
|
349
|
+
const scanMatcher = { ...matcher };
|
|
350
|
+
findFileProbeCache.set(scanMatcher, /* @__PURE__ */ new Map());
|
|
349
351
|
const excludeDir = (name) => name.startsWith("@") && name !== "@children" || name.startsWith("_") || isInterceptionMarkerDir(name);
|
|
350
|
-
for await (const file of scanWithExtensions("**/page", appDir,
|
|
351
|
-
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);
|
|
352
354
|
if (route) routes.push(route);
|
|
353
355
|
}
|
|
354
|
-
for await (const file of scanWithExtensions("**/route", appDir,
|
|
355
|
-
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);
|
|
356
358
|
if (route) routes.push(route);
|
|
357
359
|
}
|
|
358
360
|
const routePatterns = new Set(routes.map((route) => route.pattern));
|
|
359
361
|
const ghostParentRoutes = [];
|
|
360
|
-
for await (const file of scanWithExtensions("**/layout", appDir,
|
|
362
|
+
for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
|
|
361
363
|
const dir = path.dirname(file);
|
|
362
364
|
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
363
365
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
364
|
-
if (discoverParallelSlots(routeDir, appDir,
|
|
365
|
-
const route = directoryToAppRoute(dir, appDir,
|
|
366
|
+
if (discoverParallelSlots(routeDir, appDir, scanMatcher).length === 0) continue;
|
|
367
|
+
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null);
|
|
366
368
|
if (!route) continue;
|
|
367
369
|
if (routePatterns.has(route.pattern)) {
|
|
368
370
|
ghostParentRoutes.push(route);
|
|
@@ -371,13 +373,13 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
371
373
|
routes.push(route);
|
|
372
374
|
routePatterns.add(route.pattern);
|
|
373
375
|
}
|
|
374
|
-
const slotSubRoutes = discoverSlotSubRoutes(routes,
|
|
376
|
+
const slotSubRoutes = discoverSlotSubRoutes(routes, scanMatcher, ghostParentRoutes);
|
|
375
377
|
routes.push(...slotSubRoutes);
|
|
376
|
-
discoverSiblingInterceptingRoutes(routes, appDir,
|
|
378
|
+
discoverSiblingInterceptingRoutes(routes, appDir, scanMatcher);
|
|
377
379
|
validatePageRouteConflicts(routes, appDir);
|
|
378
380
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
379
381
|
validateRoutePatterns([...new Set(routes.flatMap((route) => [...route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern)), ...route.siblingIntercepts.map((intercept) => intercept.targetPattern)]))]);
|
|
380
|
-
routes
|
|
382
|
+
sortRoutes(routes);
|
|
381
383
|
return {
|
|
382
384
|
routes,
|
|
383
385
|
routeManifest: createRouteManifest(routes)
|
|
@@ -1275,6 +1277,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1275
1277
|
targetPattern: targetPattern.pattern,
|
|
1276
1278
|
sourceMatchPattern,
|
|
1277
1279
|
pagePath: page,
|
|
1280
|
+
sourcePageSegments: normalizePathSeparators(path.relative(appDir, path.dirname(page))).split("/").filter(Boolean),
|
|
1278
1281
|
params: targetPattern.params
|
|
1279
1282
|
});
|
|
1280
1283
|
}
|
|
@@ -1384,10 +1387,37 @@ function markerForInterceptionConvention(convention) {
|
|
|
1384
1387
|
}
|
|
1385
1388
|
}
|
|
1386
1389
|
/**
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
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.
|
|
1402
|
+
*/
|
|
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.
|
|
1389
1410
|
*/
|
|
1390
|
-
|
|
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
|
+
}
|
|
1391
1421
|
/**
|
|
1392
1422
|
* Convert filesystem path segments to URL route parts, skipping invisible segments
|
|
1393
1423
|
* (route groups, @slots, ".") and converting dynamic segment syntax to Express-style
|
|
@@ -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 };
|
|
@@ -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
|