@vercel/fs-detectors 7.6.2 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/detect-builders.d.ts +7 -0
- package/dist/detect-builders.js +141 -21
- package/dist/services/resolve-v2.js +1 -1
- package/package.json +5 -5
|
@@ -27,6 +27,13 @@ export interface ProxyConfig {
|
|
|
27
27
|
export interface Options {
|
|
28
28
|
tag?: string;
|
|
29
29
|
functions?: BuilderFunctions;
|
|
30
|
+
/**
|
|
31
|
+
* Source files built by a builder that detection does not create itself
|
|
32
|
+
* (e.g. schedule entrypoints resolved by the CLI). Detection neither builds
|
|
33
|
+
* them as `api/` functions nor serves them as static files, and `functions`
|
|
34
|
+
* patterns that match them count as used.
|
|
35
|
+
*/
|
|
36
|
+
reservedFunctionFiles?: string[];
|
|
30
37
|
ignoreBuildScript?: boolean;
|
|
31
38
|
projectSettings?: ProjectSettings;
|
|
32
39
|
cleanUrls?: boolean;
|
package/dist/detect-builders.js
CHANGED
|
@@ -119,6 +119,80 @@ var require_constants = __commonJS({
|
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
// ../../internals/runtimes/dist/entrypoint.js
|
|
123
|
+
var require_entrypoint = __commonJS({
|
|
124
|
+
"../../internals/runtimes/dist/entrypoint.js"(exports, module2) {
|
|
125
|
+
"use strict";
|
|
126
|
+
var __defProp2 = Object.defineProperty;
|
|
127
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
128
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
129
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
130
|
+
var __export2 = (target, all) => {
|
|
131
|
+
for (var name in all)
|
|
132
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
133
|
+
};
|
|
134
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
135
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
136
|
+
for (let key of __getOwnPropNames2(from))
|
|
137
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
138
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
139
|
+
}
|
|
140
|
+
return to;
|
|
141
|
+
};
|
|
142
|
+
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
143
|
+
var entrypoint_exports = {};
|
|
144
|
+
__export2(entrypoint_exports, {
|
|
145
|
+
SCHEDULE_ENTRYPOINT_FORMS_HINT: () => SCHEDULE_ENTRYPOINT_FORMS_HINT,
|
|
146
|
+
isDockerfileEntrypoint: () => isDockerfileEntrypoint,
|
|
147
|
+
parsePyModuleAttrEntrypoint: () => parsePyModuleAttrEntrypoint2,
|
|
148
|
+
parseScheduleEntrypoint: () => parseScheduleEntrypoint,
|
|
149
|
+
stripTrailingSlash: () => stripTrailingSlash
|
|
150
|
+
});
|
|
151
|
+
module2.exports = __toCommonJS2(entrypoint_exports);
|
|
152
|
+
var import_path2 = require("path");
|
|
153
|
+
var import_constants2 = require_constants();
|
|
154
|
+
var PYTHON_MODULE_ATTR_RE = /^([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*):([A-Za-z_][\w]*)$/;
|
|
155
|
+
var NODE_FILE_ENTRYPOINT_RE = /^(?!\/)(?!.*(?:^|\/)\.\.?(?:\/|$))(?:[\w@+.-]+\/)*[\w@+.-]+\.[cm]?[jt]s$/;
|
|
156
|
+
var NODE_DECLARATION_ENTRYPOINT_RE = /\.d\.[cm]?ts$/;
|
|
157
|
+
function parsePyModuleAttrEntrypoint2(entrypoint) {
|
|
158
|
+
const match = PYTHON_MODULE_ATTR_RE.exec(entrypoint);
|
|
159
|
+
if (!match)
|
|
160
|
+
return null;
|
|
161
|
+
const [, modulePart, attrName] = match;
|
|
162
|
+
if (!modulePart || !attrName)
|
|
163
|
+
return null;
|
|
164
|
+
return {
|
|
165
|
+
attrName,
|
|
166
|
+
filePath: modulePart.replace(/\./g, "/") + ".py",
|
|
167
|
+
moduleName: modulePart
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
var SCHEDULE_ENTRYPOINT_FORMS_HINT = 'Use a source file path like "jobs/nightly.ts" or a Python "module.path:function" reference.';
|
|
171
|
+
function parseScheduleEntrypoint(entrypoint) {
|
|
172
|
+
const python = parsePyModuleAttrEntrypoint2(entrypoint);
|
|
173
|
+
if (python) {
|
|
174
|
+
return { runtime: "python", ...python };
|
|
175
|
+
}
|
|
176
|
+
if (NODE_FILE_ENTRYPOINT_RE.test(entrypoint) && !NODE_DECLARATION_ENTRYPOINT_RE.test(entrypoint)) {
|
|
177
|
+
return { runtime: "node", filePath: entrypoint };
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
var CONTAINER_ENTRYPOINT_BASENAMES = new Set(
|
|
182
|
+
import_constants2.CONTAINER_ENTRYPOINT_CANDIDATES.map((name) => name.toLowerCase())
|
|
183
|
+
);
|
|
184
|
+
function isDockerfileEntrypoint(entrypoint) {
|
|
185
|
+
return CONTAINER_ENTRYPOINT_BASENAMES.has(
|
|
186
|
+
import_path2.posix.basename(entrypoint).toLowerCase()
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function stripTrailingSlash(p) {
|
|
190
|
+
const stripped = p.replace(/\/+$/, "");
|
|
191
|
+
return stripped === "" ? "." : stripped;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
122
196
|
// ../../internals/runtimes/dist/framework.js
|
|
123
197
|
var require_framework = __commonJS({
|
|
124
198
|
"../../internals/runtimes/dist/framework.js"(exports, module2) {
|
|
@@ -147,6 +221,7 @@ var require_framework = __commonJS({
|
|
|
147
221
|
frameworksBySlug: () => frameworksBySlug,
|
|
148
222
|
inferBuilderRuntimeFromFramework: () => inferBuilderRuntimeFromFramework2,
|
|
149
223
|
inferRuntimeFromFramework: () => inferRuntimeFromFramework,
|
|
224
|
+
inferServerRuntimeFromFramework: () => inferServerRuntimeFromFramework,
|
|
150
225
|
isBFFFramework: () => isBFFFramework,
|
|
151
226
|
isFrontendFramework: () => isFrontendFramework
|
|
152
227
|
});
|
|
@@ -177,6 +252,14 @@ var require_framework = __commonJS({
|
|
|
177
252
|
middleman: "ruby",
|
|
178
253
|
zola: "rust"
|
|
179
254
|
};
|
|
255
|
+
function inferServerRuntimeFromFramework(framework) {
|
|
256
|
+
if (!framework || framework === "services")
|
|
257
|
+
return void 0;
|
|
258
|
+
const builder = frameworksBySlug.get(framework)?.useRuntime?.use;
|
|
259
|
+
if (!builder)
|
|
260
|
+
return void 0;
|
|
261
|
+
return import_constants2.BUILDER_RUNTIMES[builder] ?? "node";
|
|
262
|
+
}
|
|
180
263
|
function inferBuilderRuntimeFromFramework2(framework) {
|
|
181
264
|
if (!framework || framework === "container" || framework === "services")
|
|
182
265
|
return void 0;
|
|
@@ -229,6 +312,7 @@ __export(detect_builders_exports, {
|
|
|
229
312
|
});
|
|
230
313
|
module.exports = __toCommonJS(detect_builders_exports);
|
|
231
314
|
var import_minimatch = __toESM(require("minimatch"));
|
|
315
|
+
var import_entrypoint = __toESM(require_entrypoint());
|
|
232
316
|
var import_semver = require("semver");
|
|
233
317
|
var import_path = require("path");
|
|
234
318
|
var import_frameworks = require("@vercel/frameworks");
|
|
@@ -381,6 +465,13 @@ async function detectBuilders(files, pkg, options = {}) {
|
|
|
381
465
|
usedFunctions.add(key);
|
|
382
466
|
};
|
|
383
467
|
const absolutePathCache = /* @__PURE__ */ new Map();
|
|
468
|
+
const proxyFile = options.proxy ? parseProxyEntrypoint(options.proxy.entrypoint).file : null;
|
|
469
|
+
const reservedFunctionFiles = options.reservedFunctionFiles ?? [];
|
|
470
|
+
const claimedFiles = /* @__PURE__ */ new Set([
|
|
471
|
+
...proxyFile !== null ? [proxyFile] : [],
|
|
472
|
+
...reservedFunctionFiles
|
|
473
|
+
]);
|
|
474
|
+
const hasReservedFunctions = reservedFunctionFiles.length > 0;
|
|
384
475
|
const { buildCommand, outputDirectory } = projectSettings;
|
|
385
476
|
const frameworkConfig = import_framework.frameworksBySlug.get(framework || "");
|
|
386
477
|
const rustStaticOutput = isRustStaticOutput(framework, projectSettings);
|
|
@@ -407,7 +498,7 @@ async function detectBuilders(files, pkg, options = {}) {
|
|
|
407
498
|
const apiRoutes = [];
|
|
408
499
|
const dynamicRoutes = [];
|
|
409
500
|
for (const fileName of sortedFiles) {
|
|
410
|
-
if (fileName
|
|
501
|
+
if (claimedFiles.has(fileName)) {
|
|
411
502
|
continue;
|
|
412
503
|
}
|
|
413
504
|
const apiBuilder = await maybeGetApiBuilder(fileName, apiMatches, options);
|
|
@@ -471,7 +562,7 @@ async function detectBuilders(files, pkg, options = {}) {
|
|
|
471
562
|
options
|
|
472
563
|
);
|
|
473
564
|
} else {
|
|
474
|
-
if (pkg && !makeFrontendStatic && !apiBuilders.length && !options.ignoreBuildScript) {
|
|
565
|
+
if (pkg && !makeFrontendStatic && !apiBuilders.length && !hasReservedFunctions && !options.ignoreBuildScript) {
|
|
475
566
|
errors.push(getMissingBuildScriptError());
|
|
476
567
|
return {
|
|
477
568
|
errors,
|
|
@@ -492,12 +583,10 @@ async function detectBuilders(files, pkg, options = {}) {
|
|
|
492
583
|
outputDirectory: usedOutputDirectory
|
|
493
584
|
}
|
|
494
585
|
};
|
|
495
|
-
} else if (apiBuilders.length && hasNoneApiFiles) {
|
|
586
|
+
} else if ((apiBuilders.length || hasReservedFunctions) && hasNoneApiFiles) {
|
|
496
587
|
frontendBuilder = {
|
|
497
588
|
use: "@vercel/static",
|
|
498
|
-
src: getStaticFilesPattern(
|
|
499
|
-
options.proxy ? [options.proxy.entrypoint] : []
|
|
500
|
-
),
|
|
589
|
+
src: getStaticFilesPattern([...claimedFiles]),
|
|
501
590
|
config: {
|
|
502
591
|
zeroConfig: true
|
|
503
592
|
}
|
|
@@ -523,6 +612,7 @@ async function detectBuilders(files, pkg, options = {}) {
|
|
|
523
612
|
const unusedFunctionError = checkUnusedFunctions(
|
|
524
613
|
frontendBuilder,
|
|
525
614
|
usedFunctions,
|
|
615
|
+
claimedFiles,
|
|
526
616
|
options
|
|
527
617
|
);
|
|
528
618
|
if (unusedFunctionError) {
|
|
@@ -659,14 +749,25 @@ function getFunction(fileName, { functions = {} }) {
|
|
|
659
749
|
const func = keys.find((key) => key === fileName || (0, import_minimatch.default)(fileName, key));
|
|
660
750
|
return func ? { fnPattern: func, func: functions[func] } : { fnPattern: null, func: null };
|
|
661
751
|
}
|
|
752
|
+
function parseProxyEntrypoint(entrypoint) {
|
|
753
|
+
if (process.env.VERCEL_PYTHON_PROXY) {
|
|
754
|
+
const parsed = (0, import_entrypoint.parsePyModuleAttrEntrypoint)(entrypoint);
|
|
755
|
+
if (parsed)
|
|
756
|
+
return { file: parsed.filePath, variable: parsed.attrName };
|
|
757
|
+
}
|
|
758
|
+
return { file: entrypoint };
|
|
759
|
+
}
|
|
662
760
|
function getProxyBuilder(proxy, tag, functions = {}) {
|
|
663
|
-
const {
|
|
761
|
+
const { file, variable } = parseProxyEntrypoint(proxy.entrypoint);
|
|
762
|
+
const isPython = (0, import_path.extname)(file) === ".py";
|
|
763
|
+
const { fnPattern, func } = getFunction(file, { functions });
|
|
664
764
|
const runtime = func?.runtime;
|
|
665
765
|
const config = {
|
|
666
766
|
zeroConfig: true,
|
|
667
767
|
middleware: true,
|
|
668
|
-
...!runtime ? { middlewareRuntime: "nodejs" } : {},
|
|
669
|
-
...proxy.matcher ? { middlewareMatcher: proxy.matcher } : {}
|
|
768
|
+
...!runtime && !isPython ? { middlewareRuntime: "nodejs" } : {},
|
|
769
|
+
...proxy.matcher ? { middlewareMatcher: proxy.matcher } : {},
|
|
770
|
+
...variable ? { handlerFunction: variable } : {}
|
|
670
771
|
};
|
|
671
772
|
if (fnPattern && func) {
|
|
672
773
|
config.functions = { [fnPattern]: func };
|
|
@@ -677,9 +778,10 @@ function getProxyBuilder(proxy, tag, functions = {}) {
|
|
|
677
778
|
config.excludeFiles = func.excludeFiles;
|
|
678
779
|
}
|
|
679
780
|
}
|
|
781
|
+
const defaultRuntime = isPython ? `@vercel/python${tag ? `@${tag}` : ""}` : `@vercel/node${tag ? `@${tag}` : ""}`;
|
|
680
782
|
return {
|
|
681
|
-
src:
|
|
682
|
-
use: runtime ||
|
|
783
|
+
src: file,
|
|
784
|
+
use: runtime || defaultRuntime,
|
|
683
785
|
config
|
|
684
786
|
};
|
|
685
787
|
}
|
|
@@ -702,21 +804,29 @@ function validateProxyConfig(proxy) {
|
|
|
702
804
|
if (typeof proxy !== "object" || typeof proxy.entrypoint !== "string" || !proxy.entrypoint) {
|
|
703
805
|
return {
|
|
704
806
|
code: "invalid_proxy",
|
|
705
|
-
message: "The `proxy` property must contain an `entrypoint` string
|
|
807
|
+
message: "The `proxy` property must contain an `entrypoint` string."
|
|
706
808
|
};
|
|
707
809
|
}
|
|
708
|
-
const
|
|
709
|
-
const
|
|
710
|
-
|
|
810
|
+
const { file, variable } = parseProxyEntrypoint(proxy.entrypoint);
|
|
811
|
+
const isPythonForm = (0, import_path.extname)(file) === ".py";
|
|
812
|
+
const isJsForm = /\.(?:js|ts)$/.test(file) && !file.endsWith(".d.ts");
|
|
813
|
+
const segments = file.split("/");
|
|
814
|
+
if (file.startsWith("/") || file.includes("\\") || segments.includes(".") || segments.includes("..") || /[?#\u0000-\u001f]/.test(file)) {
|
|
711
815
|
return {
|
|
712
816
|
code: "invalid_proxy_entrypoint",
|
|
713
817
|
message: "The `proxy.entrypoint` path must be relative to the project root and cannot contain traversal, query, fragment, or control characters."
|
|
714
818
|
};
|
|
715
819
|
}
|
|
716
|
-
if (
|
|
820
|
+
if (!isPythonForm && !isJsForm) {
|
|
821
|
+
return {
|
|
822
|
+
code: "invalid_proxy_entrypoint",
|
|
823
|
+
message: "The `proxy.entrypoint` must be a `.js` or `.ts` file path, or a Python `module:attr`."
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
if (isPythonForm && !variable) {
|
|
717
827
|
return {
|
|
718
828
|
code: "invalid_proxy_entrypoint",
|
|
719
|
-
message: "
|
|
829
|
+
message: "Python proxy entrypoints must use `module:attr` syntax, not a plain `.py` path."
|
|
720
830
|
};
|
|
721
831
|
}
|
|
722
832
|
if (proxy.matcher !== void 0) {
|
|
@@ -741,10 +851,11 @@ function validateProxy(options, files, framework) {
|
|
|
741
851
|
if (configError) {
|
|
742
852
|
return configError;
|
|
743
853
|
}
|
|
744
|
-
|
|
854
|
+
const { file } = parseProxyEntrypoint(proxy.entrypoint);
|
|
855
|
+
if (!files.includes(file)) {
|
|
745
856
|
return {
|
|
746
857
|
code: "proxy_entrypoint_not_found",
|
|
747
|
-
message: `The proxy entrypoint \`${proxy.entrypoint}\` does not exist
|
|
858
|
+
message: `The proxy entrypoint \`${proxy.entrypoint}\` does not exist.`
|
|
748
859
|
};
|
|
749
860
|
}
|
|
750
861
|
if (framework === "nextjs" || framework === "astro") {
|
|
@@ -754,7 +865,8 @@ function validateProxy(options, files, framework) {
|
|
|
754
865
|
};
|
|
755
866
|
}
|
|
756
867
|
const appRuntime = (0, import_framework.inferBuilderRuntimeFromFramework)(framework);
|
|
757
|
-
const
|
|
868
|
+
const { file: proxyFile } = parseProxyEntrypoint(proxy.entrypoint);
|
|
869
|
+
const proxyRuntime = import_constants.ENTRYPOINT_EXTENSIONS[(0, import_path.extname)(proxyFile)];
|
|
758
870
|
if (appRuntime !== void 0 && proxyRuntime !== void 0 && appRuntime !== proxyRuntime) {
|
|
759
871
|
return {
|
|
760
872
|
code: "proxy_runtime_mismatch",
|
|
@@ -922,10 +1034,18 @@ function validateFunctions({ functions = {} }) {
|
|
|
922
1034
|
}
|
|
923
1035
|
return null;
|
|
924
1036
|
}
|
|
925
|
-
function checkUnusedFunctions(frontendBuilder, usedFunctions, options) {
|
|
1037
|
+
function checkUnusedFunctions(frontendBuilder, usedFunctions, claimedFiles, options) {
|
|
926
1038
|
const unusedFunctions = new Set(
|
|
927
1039
|
Object.keys(options.functions || {}).filter((key) => !usedFunctions.has(key))
|
|
928
1040
|
);
|
|
1041
|
+
for (const fnKey of unusedFunctions) {
|
|
1042
|
+
for (const filePath of claimedFiles) {
|
|
1043
|
+
if (filePath === fnKey || (0, import_minimatch.default)(filePath, fnKey)) {
|
|
1044
|
+
unusedFunctions.delete(fnKey);
|
|
1045
|
+
break;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
929
1049
|
if (!unusedFunctions.size) {
|
|
930
1050
|
return null;
|
|
931
1051
|
}
|
|
@@ -248,7 +248,7 @@ async function resolveConfiguredServiceV2(name, config, fs) {
|
|
|
248
248
|
}
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
|
-
const buildpackRuntime = flaggedBuildpackRuntime && !entrypointFile && !(0, import_framework.isFrontendFramework)(framework) && await (0, import_buildpack_runtimes.hasBuildpackProjectMarker)(
|
|
251
|
+
const buildpackRuntime = flaggedBuildpackRuntime && (!entrypointFile || flaggedBuildpackRuntime === "ruby" && entrypointFile === "config.ru") && !(0, import_framework.isFrontendFramework)(framework) && await (0, import_buildpack_runtimes.hasBuildpackProjectMarker)(
|
|
252
252
|
flaggedBuildpackRuntime,
|
|
253
253
|
(marker) => (0, import_utils.hasFile)(serviceFs, marker)
|
|
254
254
|
) ? flaggedBuildpackRuntime : void 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/fs-detectors",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.0",
|
|
4
4
|
"description": "Vercel filesystem detectors",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"minimatch": "3.1.2",
|
|
21
21
|
"semver": "6.3.1",
|
|
22
22
|
"smol-toml": "1.5.2",
|
|
23
|
-
"@vercel/build-utils": "14.10.2",
|
|
24
23
|
"@vercel/error-utils": "2.2.1",
|
|
24
|
+
"@vercel/build-utils": "14.11.0",
|
|
25
25
|
"@vercel/frameworks": "3.34.0",
|
|
26
|
-
"@vercel/
|
|
27
|
-
"@vercel/
|
|
26
|
+
"@vercel/routing-utils": "6.6.0",
|
|
27
|
+
"@vercel/python-analysis": "0.14.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/glob": "7.2.0",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@types/node": "20.11.0",
|
|
34
34
|
"@types/semver": "7.3.10",
|
|
35
35
|
"vitest": "4.1.10",
|
|
36
|
-
"@vercel-internals/runtimes": "1.1.
|
|
36
|
+
"@vercel-internals/runtimes": "1.1.2"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "node build.mjs",
|