@vercel/fs-detectors 5.0.1 → 5.0.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/detect-builders.js +701 -748
- package/dist/detect-builders.js.map +7 -1
- package/dist/detect-file-system-api.js +177 -154
- package/dist/detect-file-system-api.js.map +7 -1
- package/dist/detect-framework.js +196 -143
- package/dist/detect-framework.js.map +7 -1
- package/dist/detectors/filesystem.js +106 -109
- package/dist/detectors/filesystem.js.map +7 -1
- package/dist/detectors/local-file-system-detector.js +90 -63
- package/dist/detectors/local-file-system-detector.js.map +7 -1
- package/dist/get-project-paths.js +74 -35
- package/dist/get-project-paths.js.map +7 -1
- package/dist/index.js +85 -48
- package/dist/index.js.map +7 -1
- package/dist/is-official-runtime.js +34 -19
- package/dist/is-official-runtime.js.map +7 -1
- package/dist/monorepos/get-monorepo-default-settings.js +154 -150
- package/dist/monorepos/get-monorepo-default-settings.js.map +7 -1
- package/dist/monorepos/monorepo-managers.js +112 -100
- package/dist/monorepos/monorepo-managers.js.map +7 -1
- package/dist/package-managers/package-managers.js +79 -55
- package/dist/package-managers/package-managers.js.map +7 -1
- package/dist/workspaces/get-glob-fs.js +90 -66
- package/dist/workspaces/get-glob-fs.js.map +7 -1
- package/dist/workspaces/get-workspace-package-paths.js +130 -79
- package/dist/workspaces/get-workspace-package-paths.js.map +7 -1
- package/dist/workspaces/get-workspaces.js +71 -31
- package/dist/workspaces/get-workspaces.js.map +7 -1
- package/dist/workspaces/workspace-managers.js +104 -92
- package/dist/workspaces/workspace-managers.js.map +7 -1
- package/package.json +5 -5
package/dist/detect-builders.js
CHANGED
|
@@ -1,821 +1,774 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
4
11
|
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var detect_builders_exports = {};
|
|
30
|
+
__export(detect_builders_exports, {
|
|
31
|
+
detectApiDirectory: () => detectApiDirectory,
|
|
32
|
+
detectApiExtensions: () => detectApiExtensions,
|
|
33
|
+
detectBuilders: () => detectBuilders,
|
|
34
|
+
detectOutputDirectory: () => detectOutputDirectory,
|
|
35
|
+
sortFiles: () => sortFiles
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(detect_builders_exports);
|
|
38
|
+
var import_minimatch = __toESM(require("minimatch"));
|
|
39
|
+
var import_semver = require("semver");
|
|
40
|
+
var import_path = require("path");
|
|
41
|
+
var import_frameworks = __toESM(require("@vercel/frameworks"));
|
|
42
|
+
var import_is_official_runtime = require("./is-official-runtime");
|
|
43
|
+
const slugToFramework = new Map(
|
|
44
|
+
import_frameworks.default.map((f) => [f.slug, f])
|
|
45
|
+
);
|
|
15
46
|
function sortFiles(fileA, fileB) {
|
|
16
|
-
|
|
47
|
+
return fileA.localeCompare(fileB);
|
|
17
48
|
}
|
|
18
|
-
exports.sortFiles = sortFiles;
|
|
19
49
|
function detectApiExtensions(builders) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
50
|
+
return new Set(
|
|
51
|
+
builders.filter(
|
|
52
|
+
(b) => Boolean(b.config && b.config.zeroConfig && b.src?.startsWith("api/"))
|
|
53
|
+
).map((b) => (0, import_path.extname)(b.src)).filter(Boolean)
|
|
54
|
+
);
|
|
24
55
|
}
|
|
25
|
-
exports.detectApiExtensions = detectApiExtensions;
|
|
26
56
|
function detectApiDirectory(builders) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
57
|
+
const found = builders.some(
|
|
58
|
+
(b) => b.config && b.config.zeroConfig && b.src?.startsWith("api/")
|
|
59
|
+
);
|
|
60
|
+
return found ? "api" : null;
|
|
31
61
|
}
|
|
32
|
-
exports.detectApiDirectory = detectApiDirectory;
|
|
33
|
-
// TODO: Replace this function with `config.outputDirectory`
|
|
34
62
|
function getPublicBuilder(builders) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
/^.*\/\*\*\/\*$/.test(builder.src) &&
|
|
39
|
-
builder.config?.zeroConfig === true) {
|
|
40
|
-
return builder;
|
|
41
|
-
}
|
|
63
|
+
for (const builder of builders) {
|
|
64
|
+
if (typeof builder.src === "string" && (0, import_is_official_runtime.isOfficialRuntime)("static", builder.use) && /^.*\/\*\*\/\*$/.test(builder.src) && builder.config?.zeroConfig === true) {
|
|
65
|
+
return builder;
|
|
42
66
|
}
|
|
43
|
-
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
44
69
|
}
|
|
45
70
|
function detectOutputDirectory(builders) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const publicBuilder = getPublicBuilder(builders);
|
|
49
|
-
return publicBuilder ? publicBuilder.src.replace('/**/*', '') : null;
|
|
71
|
+
const publicBuilder = getPublicBuilder(builders);
|
|
72
|
+
return publicBuilder ? publicBuilder.src.replace("/**/*", "") : null;
|
|
50
73
|
}
|
|
51
|
-
exports.detectOutputDirectory = detectOutputDirectory;
|
|
52
74
|
async function detectBuilders(files, pkg, options = {}) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
const sortedFiles = files.sort(sortFiles);
|
|
70
|
-
const apiSortedFiles = files.sort(sortFilesBySegmentCount);
|
|
71
|
-
// Keep track of functions that are used
|
|
72
|
-
const usedFunctions = new Set();
|
|
73
|
-
const addToUsedFunctions = (builder) => {
|
|
74
|
-
const key = Object.keys(builder.config.functions || {})[0];
|
|
75
|
-
if (key)
|
|
76
|
-
usedFunctions.add(key);
|
|
75
|
+
const errors = [];
|
|
76
|
+
const warnings = [];
|
|
77
|
+
let apiBuilders = [];
|
|
78
|
+
let frontendBuilder = null;
|
|
79
|
+
const functionError = validateFunctions(options);
|
|
80
|
+
if (functionError) {
|
|
81
|
+
return {
|
|
82
|
+
builders: null,
|
|
83
|
+
errors: [functionError],
|
|
84
|
+
warnings,
|
|
85
|
+
defaultRoutes: null,
|
|
86
|
+
redirectRoutes: null,
|
|
87
|
+
rewriteRoutes: null,
|
|
88
|
+
errorRoutes: null
|
|
77
89
|
};
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (apiRoute) {
|
|
122
|
-
apiRoutes.push(apiRoute);
|
|
123
|
-
if (isDynamic) {
|
|
124
|
-
dynamicRoutes.push(apiRoute);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
addToUsedFunctions(apiBuilder);
|
|
128
|
-
apiBuilders.push(apiBuilder);
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (!hasUsedOutputDirectory &&
|
|
132
|
-
fileName.startsWith(`${usedOutputDirectory}/`)) {
|
|
133
|
-
hasUsedOutputDirectory = true;
|
|
134
|
-
}
|
|
135
|
-
if (!hasNoneApiFiles &&
|
|
136
|
-
!fileName.startsWith('api/') &&
|
|
137
|
-
fileName !== 'package.json') {
|
|
138
|
-
hasNoneApiFiles = true;
|
|
139
|
-
}
|
|
140
|
-
if (!hasNextApiFiles &&
|
|
141
|
-
(fileName.startsWith('pages/api') || fileName.startsWith('src/pages/api'))) {
|
|
142
|
-
hasNextApiFiles = true;
|
|
143
|
-
}
|
|
144
|
-
if (!fallbackEntrypoint &&
|
|
145
|
-
buildCommand &&
|
|
146
|
-
!fileName.includes('/') &&
|
|
147
|
-
fileName !== 'now.json' &&
|
|
148
|
-
fileName !== 'vercel.json') {
|
|
149
|
-
fallbackEntrypoint = fileName;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (!makeFrontendStatic &&
|
|
153
|
-
(hasBuildScript(pkg) || buildCommand || framework)) {
|
|
154
|
-
// Framework or Build
|
|
155
|
-
frontendBuilder = detectFrontBuilder(pkg, files, usedFunctions, fallbackEntrypoint, options);
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
if (pkg &&
|
|
159
|
-
!makeFrontendStatic &&
|
|
160
|
-
!apiBuilders.length &&
|
|
161
|
-
!options.ignoreBuildScript) {
|
|
162
|
-
// We only show this error when there are no api builders
|
|
163
|
-
// since the dependencies of the pkg could be used for those
|
|
164
|
-
errors.push(getMissingBuildScriptError());
|
|
165
|
-
return {
|
|
166
|
-
errors,
|
|
167
|
-
warnings,
|
|
168
|
-
builders: null,
|
|
169
|
-
redirectRoutes: null,
|
|
170
|
-
defaultRoutes: null,
|
|
171
|
-
rewriteRoutes: null,
|
|
172
|
-
errorRoutes: null,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
// If `outputDirectory` is an empty string,
|
|
176
|
-
// we'll default to the root directory.
|
|
177
|
-
if (hasUsedOutputDirectory && outputDirectory !== '') {
|
|
178
|
-
frontendBuilder = {
|
|
179
|
-
use: '@vercel/static',
|
|
180
|
-
src: `${usedOutputDirectory}/**/*`,
|
|
181
|
-
config: {
|
|
182
|
-
zeroConfig: true,
|
|
183
|
-
outputDirectory: usedOutputDirectory,
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
else if (apiBuilders.length && hasNoneApiFiles) {
|
|
188
|
-
// Everything besides the api directory
|
|
189
|
-
// and package.json can be served as static files
|
|
190
|
-
frontendBuilder = {
|
|
191
|
-
use: '@vercel/static',
|
|
192
|
-
src: '!{api/**,package.json,middleware.[jt]s}',
|
|
193
|
-
config: {
|
|
194
|
-
zeroConfig: true,
|
|
195
|
-
},
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
const unusedFunctionError = checkUnusedFunctions(frontendBuilder, usedFunctions, options);
|
|
200
|
-
if (unusedFunctionError) {
|
|
90
|
+
}
|
|
91
|
+
const sortedFiles = files.sort(sortFiles);
|
|
92
|
+
const apiSortedFiles = files.sort(sortFilesBySegmentCount);
|
|
93
|
+
const usedFunctions = /* @__PURE__ */ new Set();
|
|
94
|
+
const addToUsedFunctions = (builder) => {
|
|
95
|
+
const key = Object.keys(builder.config.functions || {})[0];
|
|
96
|
+
if (key)
|
|
97
|
+
usedFunctions.add(key);
|
|
98
|
+
};
|
|
99
|
+
const absolutePathCache = /* @__PURE__ */ new Map();
|
|
100
|
+
const { projectSettings = {} } = options;
|
|
101
|
+
const { buildCommand, outputDirectory, framework } = projectSettings;
|
|
102
|
+
const frameworkConfig = slugToFramework.get(framework || "");
|
|
103
|
+
const ignoreRuntimes = new Set(frameworkConfig?.ignoreRuntimes);
|
|
104
|
+
const withTag = options.tag ? `@${options.tag}` : "";
|
|
105
|
+
const apiMatches = getApiMatches().filter(
|
|
106
|
+
(b) => (
|
|
107
|
+
// Root-level middleware is enabled, unless `disableRootMiddleware: true`
|
|
108
|
+
b.config?.middleware && !frameworkConfig?.disableRootMiddleware || // "api" dir runtimes are enabled, unless opted-out via `ignoreRuntimes`
|
|
109
|
+
!ignoreRuntimes.has(b.use)
|
|
110
|
+
)
|
|
111
|
+
).map((b) => {
|
|
112
|
+
b.use = `${b.use}${withTag}`;
|
|
113
|
+
return b;
|
|
114
|
+
});
|
|
115
|
+
const makeFrontendStatic = buildCommand === "" || outputDirectory === "";
|
|
116
|
+
const usedOutputDirectory = outputDirectory || "public";
|
|
117
|
+
let hasUsedOutputDirectory = false;
|
|
118
|
+
let hasNoneApiFiles = false;
|
|
119
|
+
let hasNextApiFiles = false;
|
|
120
|
+
let fallbackEntrypoint = null;
|
|
121
|
+
const apiRoutes = [];
|
|
122
|
+
const dynamicRoutes = [];
|
|
123
|
+
for (const fileName of sortedFiles) {
|
|
124
|
+
const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
|
|
125
|
+
if (apiBuilder) {
|
|
126
|
+
const { routeError, apiRoute, isDynamic } = getApiRoute(
|
|
127
|
+
fileName,
|
|
128
|
+
apiSortedFiles,
|
|
129
|
+
options,
|
|
130
|
+
absolutePathCache
|
|
131
|
+
);
|
|
132
|
+
if (routeError) {
|
|
201
133
|
return {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
134
|
+
builders: null,
|
|
135
|
+
errors: [routeError],
|
|
136
|
+
warnings,
|
|
137
|
+
defaultRoutes: null,
|
|
138
|
+
redirectRoutes: null,
|
|
139
|
+
rewriteRoutes: null,
|
|
140
|
+
errorRoutes: null
|
|
209
141
|
};
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
142
|
+
}
|
|
143
|
+
if (apiRoute) {
|
|
144
|
+
apiRoutes.push(apiRoute);
|
|
145
|
+
if (isDynamic) {
|
|
146
|
+
dynamicRoutes.push(apiRoute);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
addToUsedFunctions(apiBuilder);
|
|
150
|
+
apiBuilders.push(apiBuilder);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!hasUsedOutputDirectory && fileName.startsWith(`${usedOutputDirectory}/`)) {
|
|
154
|
+
hasUsedOutputDirectory = true;
|
|
155
|
+
}
|
|
156
|
+
if (!hasNoneApiFiles && !fileName.startsWith("api/") && fileName !== "package.json") {
|
|
157
|
+
hasNoneApiFiles = true;
|
|
158
|
+
}
|
|
159
|
+
if (!hasNextApiFiles && (fileName.startsWith("pages/api") || fileName.startsWith("src/pages/api"))) {
|
|
160
|
+
hasNextApiFiles = true;
|
|
161
|
+
}
|
|
162
|
+
if (!fallbackEntrypoint && buildCommand && !fileName.includes("/") && fileName !== "now.json" && fileName !== "vercel.json") {
|
|
163
|
+
fallbackEntrypoint = fileName;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!makeFrontendStatic && (hasBuildScript(pkg) || buildCommand || framework)) {
|
|
167
|
+
frontendBuilder = detectFrontBuilder(
|
|
168
|
+
pkg,
|
|
169
|
+
files,
|
|
170
|
+
usedFunctions,
|
|
171
|
+
fallbackEntrypoint,
|
|
172
|
+
options
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
if (pkg && !makeFrontendStatic && !apiBuilders.length && !options.ignoreBuildScript) {
|
|
176
|
+
errors.push(getMissingBuildScriptError());
|
|
177
|
+
return {
|
|
178
|
+
errors,
|
|
243
179
|
warnings,
|
|
244
|
-
builders:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
180
|
+
builders: null,
|
|
181
|
+
redirectRoutes: null,
|
|
182
|
+
defaultRoutes: null,
|
|
183
|
+
rewriteRoutes: null,
|
|
184
|
+
errorRoutes: null
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (hasUsedOutputDirectory && outputDirectory !== "") {
|
|
188
|
+
frontendBuilder = {
|
|
189
|
+
use: "@vercel/static",
|
|
190
|
+
src: `${usedOutputDirectory}/**/*`,
|
|
191
|
+
config: {
|
|
192
|
+
zeroConfig: true,
|
|
193
|
+
outputDirectory: usedOutputDirectory
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
} else if (apiBuilders.length && hasNoneApiFiles) {
|
|
197
|
+
frontendBuilder = {
|
|
198
|
+
use: "@vercel/static",
|
|
199
|
+
src: "!{api/**,package.json,middleware.[jt]s}",
|
|
200
|
+
config: {
|
|
201
|
+
zeroConfig: true
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const unusedFunctionError = checkUnusedFunctions(
|
|
207
|
+
frontendBuilder,
|
|
208
|
+
usedFunctions,
|
|
209
|
+
options
|
|
210
|
+
);
|
|
211
|
+
if (unusedFunctionError) {
|
|
212
|
+
return {
|
|
213
|
+
builders: null,
|
|
214
|
+
errors: [unusedFunctionError],
|
|
215
|
+
warnings,
|
|
216
|
+
redirectRoutes: null,
|
|
217
|
+
defaultRoutes: null,
|
|
218
|
+
rewriteRoutes: null,
|
|
219
|
+
errorRoutes: null
|
|
250
220
|
};
|
|
221
|
+
}
|
|
222
|
+
if (framework === null && frontendBuilder?.use === "@vercel/next" && apiBuilders.length > 0) {
|
|
223
|
+
apiBuilders = apiBuilders.filter((builder) => {
|
|
224
|
+
const isMiddlewareBuilder = builder.use === "@vercel/node" && builder.config?.middleware;
|
|
225
|
+
return !isMiddlewareBuilder;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const builders = [];
|
|
229
|
+
if (apiBuilders.length) {
|
|
230
|
+
builders.push(...apiBuilders);
|
|
231
|
+
}
|
|
232
|
+
if (frontendBuilder) {
|
|
233
|
+
builders.push(frontendBuilder);
|
|
234
|
+
if (hasNextApiFiles && apiBuilders.some((b) => (0, import_is_official_runtime.isOfficialRuntime)("node", b.use))) {
|
|
235
|
+
warnings.push({
|
|
236
|
+
code: "conflicting_files",
|
|
237
|
+
message: "When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.",
|
|
238
|
+
link: "https://nextjs.org/docs/api-routes/introduction",
|
|
239
|
+
action: "Learn More"
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const routesResult = getRouteResult(
|
|
244
|
+
apiRoutes,
|
|
245
|
+
dynamicRoutes,
|
|
246
|
+
usedOutputDirectory,
|
|
247
|
+
apiBuilders,
|
|
248
|
+
frontendBuilder,
|
|
249
|
+
options
|
|
250
|
+
);
|
|
251
|
+
return {
|
|
252
|
+
warnings,
|
|
253
|
+
builders: builders.length ? builders : null,
|
|
254
|
+
errors: errors.length ? errors : null,
|
|
255
|
+
redirectRoutes: routesResult.redirectRoutes,
|
|
256
|
+
defaultRoutes: routesResult.defaultRoutes,
|
|
257
|
+
rewriteRoutes: routesResult.rewriteRoutes,
|
|
258
|
+
errorRoutes: routesResult.errorRoutes
|
|
259
|
+
};
|
|
251
260
|
}
|
|
252
|
-
exports.detectBuilders = detectBuilders;
|
|
253
261
|
function maybeGetApiBuilder(fileName, apiMatches, options) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
};
|
|
301
|
-
return builder;
|
|
262
|
+
const middleware = fileName === "middleware.js" || fileName === "middleware.ts";
|
|
263
|
+
if (middleware && options.projectSettings?.framework === "nextjs") {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
if (!(fileName.startsWith("api/") || middleware)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
if (fileName.includes("/.")) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
if (fileName.includes("/_")) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
if (fileName.includes("/node_modules/")) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
if (fileName.endsWith(".d.ts")) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const match = apiMatches.find(({ src = "**" }) => {
|
|
282
|
+
return src === fileName || (0, import_minimatch.default)(fileName, src);
|
|
283
|
+
});
|
|
284
|
+
const { fnPattern, func } = getFunction(fileName, options);
|
|
285
|
+
const use = func?.runtime || match?.use;
|
|
286
|
+
if (!use) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const config = { zeroConfig: true };
|
|
290
|
+
if (middleware) {
|
|
291
|
+
config.middleware = true;
|
|
292
|
+
}
|
|
293
|
+
if (fnPattern && func) {
|
|
294
|
+
config.functions = { [fnPattern]: func };
|
|
295
|
+
if (func.includeFiles) {
|
|
296
|
+
config.includeFiles = func.includeFiles;
|
|
297
|
+
}
|
|
298
|
+
if (func.excludeFiles) {
|
|
299
|
+
config.excludeFiles = func.excludeFiles;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const builder = {
|
|
303
|
+
use,
|
|
304
|
+
src: fileName,
|
|
305
|
+
config
|
|
306
|
+
};
|
|
307
|
+
return builder;
|
|
302
308
|
}
|
|
303
309
|
function getFunction(fileName, { functions = {} }) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
? { fnPattern: func, func: functions[func] }
|
|
311
|
-
: { fnPattern: null, func: null };
|
|
310
|
+
const keys = Object.keys(functions);
|
|
311
|
+
if (!keys.length) {
|
|
312
|
+
return { fnPattern: null, func: null };
|
|
313
|
+
}
|
|
314
|
+
const func = keys.find((key) => key === fileName || (0, import_minimatch.default)(fileName, key));
|
|
315
|
+
return func ? { fnPattern: func, func: functions[func] } : { fnPattern: null, func: null };
|
|
312
316
|
}
|
|
313
317
|
function getApiMatches() {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
318
|
+
const config = { zeroConfig: true };
|
|
319
|
+
return [
|
|
320
|
+
{
|
|
321
|
+
src: "middleware.[jt]s",
|
|
322
|
+
use: `@vercel/node`,
|
|
323
|
+
config: { ...config, middleware: true }
|
|
324
|
+
},
|
|
325
|
+
{ src: "api/**/*.+(js|mjs|ts|tsx)", use: `@vercel/node`, config },
|
|
326
|
+
{ src: "api/**/!(*_test).go", use: `@vercel/go`, config },
|
|
327
|
+
{ src: "api/**/*.py", use: `@vercel/python`, config },
|
|
328
|
+
{ src: "api/**/*.rb", use: `@vercel/ruby`, config }
|
|
329
|
+
];
|
|
326
330
|
}
|
|
327
331
|
function hasBuildScript(pkg) {
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
const { scripts = {} } = pkg || {};
|
|
333
|
+
return Boolean(scripts && scripts["build"]);
|
|
330
334
|
}
|
|
331
335
|
function detectFrontBuilder(pkg, files, usedFunctions, fallbackEntrypoint, options) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
...pkg.dependencies,
|
|
359
|
-
...pkg.devDependencies,
|
|
360
|
-
};
|
|
361
|
-
if (deps['next']) {
|
|
362
|
-
framework = 'nextjs';
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (options.functions) {
|
|
366
|
-
// When the builder is not used yet we'll use it for the frontend
|
|
367
|
-
Object.entries(options.functions).forEach(([key, func]) => {
|
|
368
|
-
if (!usedFunctions.has(key)) {
|
|
369
|
-
if (!config.functions)
|
|
370
|
-
config.functions = {};
|
|
371
|
-
config.functions[key] = { ...func };
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
const f = slugToFramework.get(framework || '');
|
|
376
|
-
if (f && f.useRuntime) {
|
|
377
|
-
const { src, use } = f.useRuntime;
|
|
378
|
-
return { src, use: `${use}${withTag}`, config };
|
|
379
|
-
}
|
|
380
|
-
// Entrypoints for other frameworks
|
|
381
|
-
// TODO - What if just a build script is provided, but no entrypoint.
|
|
382
|
-
const entrypoints = new Set([
|
|
383
|
-
'package.json',
|
|
384
|
-
'config.yaml',
|
|
385
|
-
'config.toml',
|
|
386
|
-
'config.json',
|
|
387
|
-
'_config.yml',
|
|
388
|
-
'config.yml',
|
|
389
|
-
'config.rb',
|
|
390
|
-
]);
|
|
391
|
-
const source = pkg
|
|
392
|
-
? 'package.json'
|
|
393
|
-
: files.find(file => entrypoints.has(file)) ||
|
|
394
|
-
fallbackEntrypoint ||
|
|
395
|
-
'package.json';
|
|
396
|
-
return {
|
|
397
|
-
src: source || 'package.json',
|
|
398
|
-
use: `@vercel/static-build${withTag}`,
|
|
399
|
-
config,
|
|
336
|
+
const { tag, projectSettings = {} } = options;
|
|
337
|
+
const withTag = tag ? `@${tag}` : "";
|
|
338
|
+
const { createdAt = 0 } = projectSettings;
|
|
339
|
+
let { framework } = projectSettings;
|
|
340
|
+
const config = {
|
|
341
|
+
zeroConfig: true
|
|
342
|
+
};
|
|
343
|
+
if (framework) {
|
|
344
|
+
config.framework = framework;
|
|
345
|
+
}
|
|
346
|
+
if (projectSettings.devCommand) {
|
|
347
|
+
config.devCommand = projectSettings.devCommand;
|
|
348
|
+
}
|
|
349
|
+
if (typeof projectSettings.installCommand === "string") {
|
|
350
|
+
config.installCommand = projectSettings.installCommand;
|
|
351
|
+
}
|
|
352
|
+
if (projectSettings.buildCommand) {
|
|
353
|
+
config.buildCommand = projectSettings.buildCommand;
|
|
354
|
+
}
|
|
355
|
+
if (projectSettings.outputDirectory) {
|
|
356
|
+
config.outputDirectory = projectSettings.outputDirectory;
|
|
357
|
+
}
|
|
358
|
+
if (pkg && (framework === void 0 || framework !== "storybook" && createdAt < Date.parse("2020-03-01"))) {
|
|
359
|
+
const deps = {
|
|
360
|
+
...pkg.dependencies,
|
|
361
|
+
...pkg.devDependencies
|
|
400
362
|
};
|
|
363
|
+
if (deps["next"]) {
|
|
364
|
+
framework = "nextjs";
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (options.functions) {
|
|
368
|
+
Object.entries(options.functions).forEach(([key, func]) => {
|
|
369
|
+
if (!usedFunctions.has(key)) {
|
|
370
|
+
if (!config.functions)
|
|
371
|
+
config.functions = {};
|
|
372
|
+
config.functions[key] = { ...func };
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const f = slugToFramework.get(framework || "");
|
|
377
|
+
if (f && f.useRuntime) {
|
|
378
|
+
const { src, use } = f.useRuntime;
|
|
379
|
+
return { src, use: `${use}${withTag}`, config };
|
|
380
|
+
}
|
|
381
|
+
const entrypoints = /* @__PURE__ */ new Set([
|
|
382
|
+
"package.json",
|
|
383
|
+
"config.yaml",
|
|
384
|
+
"config.toml",
|
|
385
|
+
"config.json",
|
|
386
|
+
"_config.yml",
|
|
387
|
+
"config.yml",
|
|
388
|
+
"config.rb"
|
|
389
|
+
]);
|
|
390
|
+
const source = pkg ? "package.json" : files.find((file) => entrypoints.has(file)) || fallbackEntrypoint || "package.json";
|
|
391
|
+
return {
|
|
392
|
+
src: source || "package.json",
|
|
393
|
+
use: `@vercel/static-build${withTag}`,
|
|
394
|
+
config
|
|
395
|
+
};
|
|
401
396
|
}
|
|
402
397
|
function getMissingBuildScriptError() {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
};
|
|
398
|
+
return {
|
|
399
|
+
code: "missing_build_script",
|
|
400
|
+
message: "Your `package.json` file is missing a `build` property inside the `scripts` property.\nLearn More: https://vercel.link/missing-build-script"
|
|
401
|
+
};
|
|
408
402
|
}
|
|
409
403
|
function validateFunctions({ functions = {} }) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
code: 'invalid_function_runtime',
|
|
456
|
-
message: 'Function Runtimes must have a valid version, for example `now-php@1.0.0`.',
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
if (func.includeFiles !== undefined) {
|
|
461
|
-
if (typeof func.includeFiles !== 'string') {
|
|
462
|
-
return {
|
|
463
|
-
code: 'invalid_function_property',
|
|
464
|
-
message: `The property \`includeFiles\` must be a string.`,
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (func.excludeFiles !== undefined) {
|
|
469
|
-
if (typeof func.excludeFiles !== 'string') {
|
|
470
|
-
return {
|
|
471
|
-
code: 'invalid_function_property',
|
|
472
|
-
message: `The property \`excludeFiles\` must be a string.`,
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
|
-
function checkUnusedFunctions(frontendBuilder, usedFunctions, options) {
|
|
480
|
-
const unusedFunctions = new Set(Object.keys(options.functions || {}).filter(key => !usedFunctions.has(key)));
|
|
481
|
-
if (!unusedFunctions.size) {
|
|
482
|
-
return null;
|
|
483
|
-
}
|
|
484
|
-
// Next.js can use functions only for `src/pages` or `pages`
|
|
485
|
-
if (frontendBuilder && (0, is_official_runtime_1.isOfficialRuntime)('next', frontendBuilder.use)) {
|
|
486
|
-
for (const fnKey of unusedFunctions.values()) {
|
|
487
|
-
if (fnKey.startsWith('pages/') ||
|
|
488
|
-
fnKey.startsWith('src/pages') ||
|
|
489
|
-
fnKey.startsWith('app/') ||
|
|
490
|
-
fnKey.startsWith('src/app/')) {
|
|
491
|
-
unusedFunctions.delete(fnKey);
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
return {
|
|
495
|
-
code: 'unused_function',
|
|
496
|
-
message: `The pattern "${fnKey}" defined in \`functions\` doesn't match any Serverless Functions.`,
|
|
497
|
-
action: 'Learn More',
|
|
498
|
-
link: 'https://vercel.link/unmatched-function-pattern',
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
}
|
|
404
|
+
for (const [path, func] of Object.entries(functions)) {
|
|
405
|
+
if (path.length > 256) {
|
|
406
|
+
return {
|
|
407
|
+
code: "invalid_function_glob",
|
|
408
|
+
message: "Function globs must be less than 256 characters long."
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (!func || typeof func !== "object") {
|
|
412
|
+
return {
|
|
413
|
+
code: "invalid_function",
|
|
414
|
+
message: "Function must be an object."
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (Object.keys(func).length === 0) {
|
|
418
|
+
return {
|
|
419
|
+
code: "invalid_function",
|
|
420
|
+
message: "Function must contain at least one property."
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
if (func.maxDuration !== void 0 && (func.maxDuration < 1 || func.maxDuration > 900 || !Number.isInteger(func.maxDuration))) {
|
|
424
|
+
return {
|
|
425
|
+
code: "invalid_function_duration",
|
|
426
|
+
message: "Functions must have a duration between 1 and 900."
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
if (func.memory !== void 0 && (func.memory < 128 || func.memory > 3008)) {
|
|
430
|
+
return {
|
|
431
|
+
code: "invalid_function_memory",
|
|
432
|
+
message: "Functions must have a memory value between 128 and 3008"
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (path.startsWith("/")) {
|
|
436
|
+
return {
|
|
437
|
+
code: "invalid_function_source",
|
|
438
|
+
message: `The function path "${path}" is invalid. The path must be relative to your project root and therefore cannot start with a slash.`
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (func.runtime !== void 0) {
|
|
442
|
+
const tag = `${func.runtime}`.split("@").pop();
|
|
443
|
+
if (!tag || !(0, import_semver.valid)(tag)) {
|
|
444
|
+
return {
|
|
445
|
+
code: "invalid_function_runtime",
|
|
446
|
+
message: "Function Runtimes must have a valid version, for example `now-php@1.0.0`."
|
|
447
|
+
};
|
|
448
|
+
}
|
|
502
449
|
}
|
|
503
|
-
if (
|
|
504
|
-
|
|
450
|
+
if (func.includeFiles !== void 0) {
|
|
451
|
+
if (typeof func.includeFiles !== "string") {
|
|
505
452
|
return {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
action: 'Learn More',
|
|
509
|
-
link: 'https://vercel.link/unmatched-function-pattern',
|
|
453
|
+
code: "invalid_function_property",
|
|
454
|
+
message: `The property \`includeFiles\` must be a string.`
|
|
510
455
|
};
|
|
456
|
+
}
|
|
511
457
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
|
|
515
|
-
const conflictingSegment = getConflictingSegment(fileName);
|
|
516
|
-
if (conflictingSegment) {
|
|
458
|
+
if (func.excludeFiles !== void 0) {
|
|
459
|
+
if (typeof func.excludeFiles !== "string") {
|
|
517
460
|
return {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
routeError: {
|
|
521
|
-
code: 'conflicting_path_segment',
|
|
522
|
-
message: `The segment "${conflictingSegment}" occurs more than ` +
|
|
523
|
-
`one time in your path "${fileName}". Please make sure that ` +
|
|
524
|
-
`every segment in a path is unique.`,
|
|
525
|
-
},
|
|
461
|
+
code: "invalid_function_property",
|
|
462
|
+
message: `The property \`excludeFiles\` must be a string.`
|
|
526
463
|
};
|
|
464
|
+
}
|
|
527
465
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
function checkUnusedFunctions(frontendBuilder, usedFunctions, options) {
|
|
470
|
+
const unusedFunctions = new Set(
|
|
471
|
+
Object.keys(options.functions || {}).filter((key) => !usedFunctions.has(key))
|
|
472
|
+
);
|
|
473
|
+
if (!unusedFunctions.size) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
if (frontendBuilder && (0, import_is_official_runtime.isOfficialRuntime)("next", frontendBuilder.use)) {
|
|
477
|
+
for (const fnKey of unusedFunctions.values()) {
|
|
478
|
+
if (fnKey.startsWith("pages/") || fnKey.startsWith("src/pages") || fnKey.startsWith("app/") || fnKey.startsWith("src/app/")) {
|
|
479
|
+
unusedFunctions.delete(fnKey);
|
|
480
|
+
} else {
|
|
531
481
|
return {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
message: `Two or more files have conflicting paths or names. ` +
|
|
537
|
-
`Please make sure path segments and filenames, without their extension, are unique. ` +
|
|
538
|
-
`The path "${fileName}" has conflicts with ${messagePaths}.`,
|
|
539
|
-
},
|
|
482
|
+
code: "unused_function",
|
|
483
|
+
message: `The pattern "${fnKey}" defined in \`functions\` doesn't match any Serverless Functions.`,
|
|
484
|
+
action: "Learn More",
|
|
485
|
+
link: "https://vercel.link/unmatched-function-pattern"
|
|
540
486
|
};
|
|
487
|
+
}
|
|
541
488
|
}
|
|
542
|
-
|
|
489
|
+
}
|
|
490
|
+
if (unusedFunctions.size) {
|
|
491
|
+
const [fnKey] = Array.from(unusedFunctions);
|
|
543
492
|
return {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
493
|
+
code: "unused_function",
|
|
494
|
+
message: `The pattern "${fnKey}" defined in \`functions\` doesn't match any Serverless Functions inside the \`api\` directory.`,
|
|
495
|
+
action: "Learn More",
|
|
496
|
+
link: "https://vercel.link/unmatched-function-pattern"
|
|
547
497
|
};
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
function getApiRoute(fileName, sortedFiles, options, absolutePathCache) {
|
|
502
|
+
const conflictingSegment = getConflictingSegment(fileName);
|
|
503
|
+
if (conflictingSegment) {
|
|
504
|
+
return {
|
|
505
|
+
apiRoute: null,
|
|
506
|
+
isDynamic: false,
|
|
507
|
+
routeError: {
|
|
508
|
+
code: "conflicting_path_segment",
|
|
509
|
+
message: `The segment "${conflictingSegment}" occurs more than one time in your path "${fileName}". Please make sure that every segment in a path is unique.`
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const occurrences = pathOccurrences(fileName, sortedFiles, absolutePathCache);
|
|
514
|
+
if (occurrences.length > 0) {
|
|
515
|
+
const messagePaths = concatArrayOfText(
|
|
516
|
+
occurrences.map((name) => `"${name}"`)
|
|
517
|
+
);
|
|
518
|
+
return {
|
|
519
|
+
apiRoute: null,
|
|
520
|
+
isDynamic: false,
|
|
521
|
+
routeError: {
|
|
522
|
+
code: "conflicting_file_path",
|
|
523
|
+
message: `Two or more files have conflicting paths or names. Please make sure path segments and filenames, without their extension, are unique. The path "${fileName}" has conflicts with ${messagePaths}.`
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
const out = createRouteFromPath(
|
|
528
|
+
fileName,
|
|
529
|
+
Boolean(options.featHandleMiss),
|
|
530
|
+
Boolean(options.cleanUrls)
|
|
531
|
+
);
|
|
532
|
+
return {
|
|
533
|
+
apiRoute: out.route,
|
|
534
|
+
isDynamic: out.isDynamic,
|
|
535
|
+
routeError: null
|
|
536
|
+
};
|
|
548
537
|
}
|
|
549
|
-
// Checks if a placeholder with the same name is used
|
|
550
|
-
// multiple times inside the same path
|
|
551
538
|
function getConflictingSegment(filePath) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
if (name) {
|
|
559
|
-
segments.add(name);
|
|
560
|
-
}
|
|
539
|
+
const segments = /* @__PURE__ */ new Set();
|
|
540
|
+
for (const segment of filePath.split("/")) {
|
|
541
|
+
const name = getSegmentName(segment);
|
|
542
|
+
if (name !== null && segments.has(name)) {
|
|
543
|
+
return name;
|
|
561
544
|
}
|
|
562
|
-
|
|
545
|
+
if (name) {
|
|
546
|
+
segments.add(name);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
563
550
|
}
|
|
564
|
-
// Takes a filename or foldername, strips the extension
|
|
565
|
-
// gets the part between the "[]" brackets.
|
|
566
|
-
// It will return `null` if there are no brackets
|
|
567
|
-
// and therefore no segment.
|
|
568
551
|
function getSegmentName(segment) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
552
|
+
const { name } = (0, import_path.parse)(segment);
|
|
553
|
+
if (name.startsWith("[") && name.endsWith("]")) {
|
|
554
|
+
return name.slice(1, -1);
|
|
555
|
+
}
|
|
556
|
+
return null;
|
|
574
557
|
}
|
|
575
558
|
function getAbsolutePath(unresolvedPath) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
559
|
+
const { dir, name } = (0, import_path.parse)(unresolvedPath);
|
|
560
|
+
const parts = joinPath(dir, name).split("/");
|
|
561
|
+
return parts.map((part) => part.replace(/\[.*\]/, "1")).join("/");
|
|
579
562
|
}
|
|
580
|
-
// Counts how often a path occurs when all placeholders
|
|
581
|
-
// got resolved, so we can check if they have conflicts
|
|
582
563
|
function pathOccurrences(fileName, files, absolutePathCache) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return prev;
|
|
564
|
+
let currentAbsolutePath = absolutePathCache.get(fileName);
|
|
565
|
+
if (!currentAbsolutePath) {
|
|
566
|
+
currentAbsolutePath = getAbsolutePath(fileName);
|
|
567
|
+
absolutePathCache.set(fileName, currentAbsolutePath);
|
|
568
|
+
}
|
|
569
|
+
const prev = [];
|
|
570
|
+
for (const file of files) {
|
|
571
|
+
if (file === fileName) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
let absolutePath = absolutePathCache.get(file);
|
|
575
|
+
if (!absolutePath) {
|
|
576
|
+
absolutePath = getAbsolutePath(file);
|
|
577
|
+
absolutePathCache.set(file, absolutePath);
|
|
578
|
+
}
|
|
579
|
+
if (absolutePath === currentAbsolutePath) {
|
|
580
|
+
prev.push(file);
|
|
581
|
+
} else if (partiallyMatches(fileName, file)) {
|
|
582
|
+
prev.push(file);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return prev;
|
|
608
586
|
}
|
|
609
587
|
function joinPath(...segments) {
|
|
610
|
-
|
|
611
|
-
|
|
588
|
+
const joinedPath = segments.join("/");
|
|
589
|
+
return joinedPath.replace(/\/{2,}/g, "/");
|
|
612
590
|
}
|
|
613
591
|
function escapeName(name) {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
592
|
+
const special = "[]^$.|?*+()".split("");
|
|
593
|
+
for (const char of special) {
|
|
594
|
+
name = name.replace(new RegExp(`\\${char}`, "g"), `\\${char}`);
|
|
595
|
+
}
|
|
596
|
+
return name;
|
|
619
597
|
}
|
|
620
598
|
function concatArrayOfText(texts) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
599
|
+
if (texts.length <= 2) {
|
|
600
|
+
return texts.join(" and ");
|
|
601
|
+
}
|
|
602
|
+
const last = texts.pop();
|
|
603
|
+
return `${texts.join(", ")}, and ${last}`;
|
|
626
604
|
}
|
|
627
|
-
// Check if the path partially matches and has the same
|
|
628
|
-
// name for the path segment at the same position
|
|
629
605
|
function partiallyMatches(pathA, pathB) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
return false;
|
|
606
|
+
const partsA = pathA.split("/");
|
|
607
|
+
const partsB = pathB.split("/");
|
|
608
|
+
const long = partsA.length > partsB.length ? partsA : partsB;
|
|
609
|
+
const short = long === partsA ? partsB : partsA;
|
|
610
|
+
let index = 0;
|
|
611
|
+
for (const segmentShort of short) {
|
|
612
|
+
const segmentLong = long[index];
|
|
613
|
+
const nameLong = getSegmentName(segmentLong);
|
|
614
|
+
const nameShort = getSegmentName(segmentShort);
|
|
615
|
+
if (segmentShort !== segmentLong && (!nameLong || !nameShort)) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
if (nameLong !== nameShort) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
index += 1;
|
|
622
|
+
}
|
|
623
|
+
return false;
|
|
650
624
|
}
|
|
651
625
|
function createRouteFromPath(filePath, featHandleMiss, cleanUrls) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
check: true,
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
else {
|
|
699
|
-
route = {
|
|
700
|
-
src,
|
|
701
|
-
dest: `/${filePath}${queryString}`,
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
return { route, isDynamic };
|
|
626
|
+
const parts = filePath.split("/");
|
|
627
|
+
let counter = 1;
|
|
628
|
+
const query = [];
|
|
629
|
+
let isDynamic = false;
|
|
630
|
+
const srcParts = parts.map((segment, i) => {
|
|
631
|
+
const name = getSegmentName(segment);
|
|
632
|
+
const isLast = i === parts.length - 1;
|
|
633
|
+
if (name !== null) {
|
|
634
|
+
query.push(`${name}=$${counter++}`);
|
|
635
|
+
isDynamic = true;
|
|
636
|
+
return `([^/]+)`;
|
|
637
|
+
} else if (isLast) {
|
|
638
|
+
const { name: fileName2, ext: ext2 } = (0, import_path.parse)(segment);
|
|
639
|
+
const isIndex2 = fileName2 === "index";
|
|
640
|
+
const prefix = isIndex2 ? "/" : "";
|
|
641
|
+
const names = [
|
|
642
|
+
isIndex2 ? prefix : `${fileName2}/`,
|
|
643
|
+
prefix + escapeName(fileName2),
|
|
644
|
+
featHandleMiss && cleanUrls ? "" : prefix + escapeName(fileName2) + escapeName(ext2)
|
|
645
|
+
].filter(Boolean);
|
|
646
|
+
return `(${names.join("|")})${isIndex2 ? "?" : ""}`;
|
|
647
|
+
}
|
|
648
|
+
return segment;
|
|
649
|
+
});
|
|
650
|
+
const { name: fileName, ext } = (0, import_path.parse)(filePath);
|
|
651
|
+
const isIndex = fileName === "index";
|
|
652
|
+
const queryString = `${query.length ? "?" : ""}${query.join("&")}`;
|
|
653
|
+
const src = isIndex ? `^/${srcParts.slice(0, -1).join("/")}${srcParts.slice(-1)[0]}$` : `^/${srcParts.join("/")}$`;
|
|
654
|
+
let route;
|
|
655
|
+
if (featHandleMiss) {
|
|
656
|
+
const extensionless = ext ? filePath.slice(0, -ext.length) : filePath;
|
|
657
|
+
route = {
|
|
658
|
+
src,
|
|
659
|
+
dest: `/${extensionless}${queryString}`,
|
|
660
|
+
check: true
|
|
661
|
+
};
|
|
662
|
+
} else {
|
|
663
|
+
route = {
|
|
664
|
+
src,
|
|
665
|
+
dest: `/${filePath}${queryString}`
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return { route, isDynamic };
|
|
705
669
|
}
|
|
706
670
|
function getRouteResult(apiRoutes, dynamicRoutes, outputDirectory, apiBuilders, frontendBuilder, options) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const hasApiBuild = apiBuilders.find(builder => {
|
|
751
|
-
return builder.src?.startsWith('api/');
|
|
752
|
-
});
|
|
753
|
-
if (typeof ignoreRuntimes === 'undefined' && hasApiBuild) {
|
|
754
|
-
// This route is only necessary to hide the directory listing
|
|
755
|
-
// to avoid enumerating serverless function names.
|
|
756
|
-
// But it causes issues in `vc dev` for frameworks that handle
|
|
757
|
-
// their own functions such as redwood, so we ignore.
|
|
758
|
-
rewriteRoutes.push({
|
|
759
|
-
src: '^/api(/.*)?$',
|
|
760
|
-
status: 404,
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
else {
|
|
765
|
-
defaultRoutes.push(...apiRoutes);
|
|
766
|
-
if (apiRoutes.length) {
|
|
767
|
-
defaultRoutes.push({
|
|
768
|
-
status: 404,
|
|
769
|
-
src: '^/api(/.*)?$',
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (outputDirectory &&
|
|
775
|
-
frontendBuilder &&
|
|
776
|
-
!options.featHandleMiss &&
|
|
777
|
-
(0, is_official_runtime_1.isOfficialRuntime)('static', frontendBuilder.use)) {
|
|
778
|
-
defaultRoutes.push({
|
|
779
|
-
src: '/(.*)',
|
|
780
|
-
dest: `/${outputDirectory}/$1`,
|
|
671
|
+
const defaultRoutes = [];
|
|
672
|
+
const redirectRoutes = [];
|
|
673
|
+
const rewriteRoutes = [];
|
|
674
|
+
const errorRoutes = [];
|
|
675
|
+
const framework = frontendBuilder?.config?.framework || "";
|
|
676
|
+
const isGatsby = framework === "gatsby";
|
|
677
|
+
const isNextjs = framework === "nextjs" || (0, import_is_official_runtime.isOfficialRuntime)("next", frontendBuilder?.use);
|
|
678
|
+
const ignoreRuntimes = slugToFramework.get(framework)?.ignoreRuntimes;
|
|
679
|
+
if (apiRoutes && apiRoutes.length > 0) {
|
|
680
|
+
if (options.featHandleMiss) {
|
|
681
|
+
const extSet = detectApiExtensions(apiBuilders);
|
|
682
|
+
if (extSet.size > 0) {
|
|
683
|
+
const extGroup = `(?:\\.(?:${Array.from(extSet).map((ext) => ext.slice(1)).join("|")}))`;
|
|
684
|
+
if (options.cleanUrls) {
|
|
685
|
+
redirectRoutes.push({
|
|
686
|
+
src: `^/(api(?:.+)?)/index${extGroup}?/?$`,
|
|
687
|
+
headers: { Location: options.trailingSlash ? "/$1/" : "/$1" },
|
|
688
|
+
status: 308
|
|
689
|
+
});
|
|
690
|
+
redirectRoutes.push({
|
|
691
|
+
src: `^/api/(.+)${extGroup}/?$`,
|
|
692
|
+
headers: {
|
|
693
|
+
Location: options.trailingSlash ? "/api/$1/" : "/api/$1"
|
|
694
|
+
},
|
|
695
|
+
status: 308
|
|
696
|
+
});
|
|
697
|
+
} else {
|
|
698
|
+
defaultRoutes.push({ handle: "miss" });
|
|
699
|
+
defaultRoutes.push({
|
|
700
|
+
src: `^/api/(.+)${extGroup}$`,
|
|
701
|
+
dest: "/api/$1",
|
|
702
|
+
check: true
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
rewriteRoutes.push(...dynamicRoutes);
|
|
707
|
+
const hasApiBuild = apiBuilders.find((builder) => {
|
|
708
|
+
return builder.src?.startsWith("api/");
|
|
709
|
+
});
|
|
710
|
+
if (typeof ignoreRuntimes === "undefined" && hasApiBuild) {
|
|
711
|
+
rewriteRoutes.push({
|
|
712
|
+
src: "^/api(/.*)?$",
|
|
713
|
+
status: 404
|
|
781
714
|
});
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
dest: options.cleanUrls ? '/404' : '/404.html',
|
|
715
|
+
}
|
|
716
|
+
} else {
|
|
717
|
+
defaultRoutes.push(...apiRoutes);
|
|
718
|
+
if (apiRoutes.length) {
|
|
719
|
+
defaultRoutes.push({
|
|
720
|
+
status: 404,
|
|
721
|
+
src: "^/api(/.*)?$"
|
|
790
722
|
});
|
|
723
|
+
}
|
|
791
724
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
};
|
|
725
|
+
}
|
|
726
|
+
if (outputDirectory && frontendBuilder && !options.featHandleMiss && (0, import_is_official_runtime.isOfficialRuntime)("static", frontendBuilder.use)) {
|
|
727
|
+
defaultRoutes.push({
|
|
728
|
+
src: "/(.*)",
|
|
729
|
+
dest: `/${outputDirectory}/$1`
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
if (options.featHandleMiss && !isNextjs && !isGatsby) {
|
|
733
|
+
errorRoutes.push({
|
|
734
|
+
status: 404,
|
|
735
|
+
src: "^(?!/api).*$",
|
|
736
|
+
dest: options.cleanUrls ? "/404" : "/404.html"
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
defaultRoutes,
|
|
741
|
+
redirectRoutes,
|
|
742
|
+
rewriteRoutes,
|
|
743
|
+
errorRoutes
|
|
744
|
+
};
|
|
798
745
|
}
|
|
799
746
|
function sortFilesBySegmentCount(fileA, fileB) {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
return fileA.localeCompare(fileB);
|
|
747
|
+
const lengthA = fileA.split("/").length;
|
|
748
|
+
const lengthB = fileB.split("/").length;
|
|
749
|
+
if (lengthA > lengthB) {
|
|
750
|
+
return -1;
|
|
751
|
+
}
|
|
752
|
+
if (lengthA < lengthB) {
|
|
753
|
+
return 1;
|
|
754
|
+
}
|
|
755
|
+
const countSegments = (prev, segment) => getSegmentName(segment) ? prev + 1 : 0;
|
|
756
|
+
const segmentLengthA = fileA.split("/").reduce(countSegments, 0);
|
|
757
|
+
const segmentLengthB = fileB.split("/").reduce(countSegments, 0);
|
|
758
|
+
if (segmentLengthA > segmentLengthB) {
|
|
759
|
+
return 1;
|
|
760
|
+
}
|
|
761
|
+
if (segmentLengthA < segmentLengthB) {
|
|
762
|
+
return -1;
|
|
763
|
+
}
|
|
764
|
+
return fileA.localeCompare(fileB);
|
|
820
765
|
}
|
|
821
|
-
|
|
766
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
767
|
+
0 && (module.exports = {
|
|
768
|
+
detectApiDirectory,
|
|
769
|
+
detectApiExtensions,
|
|
770
|
+
detectBuilders,
|
|
771
|
+
detectOutputDirectory,
|
|
772
|
+
sortFiles
|
|
773
|
+
});
|
|
774
|
+
//# sourceMappingURL=detect-builders.js.map
|