@vercel/build-utils 13.22.1 → 13.23.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/CHANGELOG.md +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7394 -713
- package/dist/node-diagnostics.d.ts +12 -0
- package/dist/node-diagnostics.js +475 -0
- package/dist/types.d.ts +51 -6
- package/package.json +3 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NodeVersion } from './types';
|
|
2
|
+
type CliType = 'yarn' | 'npm' | 'pnpm' | 'bun' | 'vlt';
|
|
3
|
+
export declare function generateProjectManifest({ workPath, nodeVersion, cliType, lockfilePath, lockfileVersion, framework, serviceType, }: {
|
|
4
|
+
workPath: string;
|
|
5
|
+
nodeVersion: NodeVersion;
|
|
6
|
+
cliType: CliType;
|
|
7
|
+
lockfilePath: string | undefined;
|
|
8
|
+
lockfileVersion: number | undefined;
|
|
9
|
+
framework?: string;
|
|
10
|
+
serviceType?: string;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"use strict";
|
|
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 });
|
|
11
|
+
};
|
|
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 node_diagnostics_exports = {};
|
|
30
|
+
__export(node_diagnostics_exports, {
|
|
31
|
+
generateProjectManifest: () => generateProjectManifest
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(node_diagnostics_exports);
|
|
34
|
+
var import_fs = __toESM(require("fs"));
|
|
35
|
+
var import_path = __toESM(require("path"));
|
|
36
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
37
|
+
var import_parsers = require("@yarnpkg/parsers");
|
|
38
|
+
var import_package_manifest = require("./package-manifest");
|
|
39
|
+
var import_debug = __toESM(require("./debug"));
|
|
40
|
+
function classifySource(resolvedUrl) {
|
|
41
|
+
if (!resolvedUrl)
|
|
42
|
+
return {};
|
|
43
|
+
if (resolvedUrl.startsWith("file:")) {
|
|
44
|
+
return { source: "file", sourceUrl: resolvedUrl.slice("file:".length) };
|
|
45
|
+
}
|
|
46
|
+
if (resolvedUrl.startsWith("git+") || resolvedUrl.startsWith("git://")) {
|
|
47
|
+
return { source: "git", sourceUrl: resolvedUrl.replace(/^git\+/, "") };
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const url = new URL(resolvedUrl);
|
|
51
|
+
return { source: "registry", sourceUrl: url.origin };
|
|
52
|
+
} catch {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function npmEntryScopes(entry) {
|
|
57
|
+
const scopes = [];
|
|
58
|
+
if (entry.dev)
|
|
59
|
+
scopes.push("dev");
|
|
60
|
+
if (entry.peer)
|
|
61
|
+
scopes.push("peer");
|
|
62
|
+
if (entry.optional)
|
|
63
|
+
scopes.push("optional");
|
|
64
|
+
if (scopes.length === 0)
|
|
65
|
+
scopes.push("prod");
|
|
66
|
+
return scopes;
|
|
67
|
+
}
|
|
68
|
+
function parseNpmLock(content, lockfileVersion) {
|
|
69
|
+
const lockMap = /* @__PURE__ */ new Map();
|
|
70
|
+
const parsed = JSON.parse(content);
|
|
71
|
+
const lv = lockfileVersion ?? parsed.lockfileVersion ?? 1;
|
|
72
|
+
if (lv >= 2) {
|
|
73
|
+
const packages = parsed.packages;
|
|
74
|
+
if (!packages)
|
|
75
|
+
return lockMap;
|
|
76
|
+
for (const [key, entry] of Object.entries(packages)) {
|
|
77
|
+
if (key === "")
|
|
78
|
+
continue;
|
|
79
|
+
if (!key.startsWith("node_modules/"))
|
|
80
|
+
continue;
|
|
81
|
+
if (entry.link === true)
|
|
82
|
+
continue;
|
|
83
|
+
const rest = key.slice("node_modules/".length);
|
|
84
|
+
const isScoped = rest.startsWith("@");
|
|
85
|
+
const slashCount = (rest.match(/\//g) ?? []).length;
|
|
86
|
+
if (isScoped ? slashCount !== 1 : slashCount !== 0)
|
|
87
|
+
continue;
|
|
88
|
+
const resolved = entry.resolved;
|
|
89
|
+
if (resolved?.startsWith("file:"))
|
|
90
|
+
continue;
|
|
91
|
+
const version = entry.version ?? "";
|
|
92
|
+
const existing = lockMap.get(rest);
|
|
93
|
+
if (existing && !isHigherVersion(version, existing.resolved))
|
|
94
|
+
continue;
|
|
95
|
+
const { source, sourceUrl } = classifySource(resolved);
|
|
96
|
+
const lockEntry = {
|
|
97
|
+
resolved: version,
|
|
98
|
+
scopes: npmEntryScopes(entry)
|
|
99
|
+
};
|
|
100
|
+
if (source)
|
|
101
|
+
lockEntry.source = source;
|
|
102
|
+
if (sourceUrl)
|
|
103
|
+
lockEntry.sourceUrl = sourceUrl;
|
|
104
|
+
lockMap.set(rest, lockEntry);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
const dependencies = parsed.dependencies;
|
|
108
|
+
if (!dependencies)
|
|
109
|
+
return lockMap;
|
|
110
|
+
const walk = (deps) => {
|
|
111
|
+
for (const [name, entry] of Object.entries(deps)) {
|
|
112
|
+
const resolved = entry.resolved;
|
|
113
|
+
if (!resolved?.startsWith("file:")) {
|
|
114
|
+
const version = entry.version ?? "";
|
|
115
|
+
const existing = lockMap.get(name);
|
|
116
|
+
if (!existing || isHigherVersion(version, existing.resolved)) {
|
|
117
|
+
const { source, sourceUrl } = classifySource(resolved);
|
|
118
|
+
const lockEntry = {
|
|
119
|
+
resolved: version,
|
|
120
|
+
scopes: npmEntryScopes(entry)
|
|
121
|
+
};
|
|
122
|
+
if (source)
|
|
123
|
+
lockEntry.source = source;
|
|
124
|
+
if (sourceUrl)
|
|
125
|
+
lockEntry.sourceUrl = sourceUrl;
|
|
126
|
+
lockMap.set(name, lockEntry);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const nested = entry.dependencies;
|
|
130
|
+
if (nested)
|
|
131
|
+
walk(nested);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
walk(dependencies);
|
|
135
|
+
}
|
|
136
|
+
return lockMap;
|
|
137
|
+
}
|
|
138
|
+
function isHigherVersion(a, b) {
|
|
139
|
+
const seg = (v) => v.split(/[.\-+]/).map((s) => parseInt(s, 10) || 0);
|
|
140
|
+
const pa = seg(a);
|
|
141
|
+
const pb = seg(b);
|
|
142
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
143
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0))
|
|
144
|
+
return true;
|
|
145
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0))
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
function extractPackageName(spec) {
|
|
151
|
+
const s = spec.replace(/\(.*\)$/, "");
|
|
152
|
+
if (s.startsWith("@")) {
|
|
153
|
+
const i2 = s.indexOf("@", 1);
|
|
154
|
+
return i2 === -1 ? null : s.slice(0, i2);
|
|
155
|
+
}
|
|
156
|
+
const i = s.lastIndexOf("@");
|
|
157
|
+
return i <= 0 ? null : s.slice(0, i);
|
|
158
|
+
}
|
|
159
|
+
function parsePnpmV9Key(key) {
|
|
160
|
+
const name = extractPackageName(key);
|
|
161
|
+
if (!name)
|
|
162
|
+
return null;
|
|
163
|
+
const withoutPeers = key.replace(/\(.*\)$/, "");
|
|
164
|
+
return { name, version: withoutPeers.slice(name.length + 1) };
|
|
165
|
+
}
|
|
166
|
+
function parsePnpmV5Key(key) {
|
|
167
|
+
if (!key.startsWith("/"))
|
|
168
|
+
return null;
|
|
169
|
+
const rest = key.slice(1);
|
|
170
|
+
if (rest.startsWith("@")) {
|
|
171
|
+
const firstSlash = rest.indexOf("/");
|
|
172
|
+
if (firstSlash === -1)
|
|
173
|
+
return null;
|
|
174
|
+
const secondSlash = rest.indexOf("/", firstSlash + 1);
|
|
175
|
+
if (secondSlash === -1)
|
|
176
|
+
return null;
|
|
177
|
+
const name2 = rest.slice(0, secondSlash);
|
|
178
|
+
const version2 = rest.slice(secondSlash + 1).split("_")[0];
|
|
179
|
+
return { name: name2, version: version2 };
|
|
180
|
+
}
|
|
181
|
+
const slashIndex = rest.indexOf("/");
|
|
182
|
+
if (slashIndex === -1)
|
|
183
|
+
return null;
|
|
184
|
+
const name = rest.slice(0, slashIndex);
|
|
185
|
+
const version = rest.slice(slashIndex + 1).split("_")[0];
|
|
186
|
+
return { name, version };
|
|
187
|
+
}
|
|
188
|
+
function parsePnpmV6Key(key) {
|
|
189
|
+
if (!key.startsWith("/"))
|
|
190
|
+
return null;
|
|
191
|
+
const rest = key.slice(1);
|
|
192
|
+
let atIdx;
|
|
193
|
+
if (rest.startsWith("@")) {
|
|
194
|
+
atIdx = rest.indexOf("@", 1);
|
|
195
|
+
} else {
|
|
196
|
+
atIdx = rest.indexOf("@");
|
|
197
|
+
}
|
|
198
|
+
if (atIdx === -1)
|
|
199
|
+
return null;
|
|
200
|
+
const name = rest.slice(0, atIdx);
|
|
201
|
+
const version = rest.slice(atIdx + 1).split("_")[0].replace(/\(.*\)$/, "");
|
|
202
|
+
return { name, version };
|
|
203
|
+
}
|
|
204
|
+
function classifyPnpmResolution(resolution) {
|
|
205
|
+
if (!resolution)
|
|
206
|
+
return {};
|
|
207
|
+
if (resolution.type === "directory" || resolution.directory)
|
|
208
|
+
return { local: true };
|
|
209
|
+
if (typeof resolution.tarball === "string")
|
|
210
|
+
return classifySource(resolution.tarball);
|
|
211
|
+
if (resolution.type === "git" || typeof resolution.repo === "string") {
|
|
212
|
+
return {
|
|
213
|
+
source: "git",
|
|
214
|
+
sourceUrl: resolution.repo ?? void 0
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {};
|
|
218
|
+
}
|
|
219
|
+
function parsePnpmLock(content, lockfileVersion) {
|
|
220
|
+
const lockMap = /* @__PURE__ */ new Map();
|
|
221
|
+
const docs = [];
|
|
222
|
+
import_js_yaml.default.safeLoadAll(
|
|
223
|
+
content,
|
|
224
|
+
(doc) => docs.push(doc)
|
|
225
|
+
);
|
|
226
|
+
const parsedYaml = docs[0];
|
|
227
|
+
if (!parsedYaml)
|
|
228
|
+
return lockMap;
|
|
229
|
+
const lv = lockfileVersion ?? Number(parsedYaml.lockfileVersion ?? "0");
|
|
230
|
+
const packages = parsedYaml.packages;
|
|
231
|
+
if (!packages)
|
|
232
|
+
return lockMap;
|
|
233
|
+
const parseKey = lv >= 9 ? parsePnpmV9Key : lv >= 6 ? parsePnpmV6Key : parsePnpmV5Key;
|
|
234
|
+
for (const [key, entry] of Object.entries(packages)) {
|
|
235
|
+
const keyParsed = parseKey(key);
|
|
236
|
+
if (!keyParsed)
|
|
237
|
+
continue;
|
|
238
|
+
const { name, version } = keyParsed;
|
|
239
|
+
const resolution = entry.resolution;
|
|
240
|
+
const { local, source, sourceUrl } = classifyPnpmResolution(resolution);
|
|
241
|
+
if (local)
|
|
242
|
+
continue;
|
|
243
|
+
const existing = lockMap.get(name);
|
|
244
|
+
if (existing && !isHigherVersion(version, existing.resolved))
|
|
245
|
+
continue;
|
|
246
|
+
const lockEntry = { resolved: version, scopes: ["prod"] };
|
|
247
|
+
if (source)
|
|
248
|
+
lockEntry.source = source;
|
|
249
|
+
if (sourceUrl)
|
|
250
|
+
lockEntry.sourceUrl = sourceUrl;
|
|
251
|
+
lockMap.set(name, lockEntry);
|
|
252
|
+
}
|
|
253
|
+
return lockMap;
|
|
254
|
+
}
|
|
255
|
+
function parseYarnLock(content, lockfileVersion) {
|
|
256
|
+
const lockMap = /* @__PURE__ */ new Map();
|
|
257
|
+
const isBerry = (lockfileVersion ?? 1) >= 2;
|
|
258
|
+
const parsed = (0, import_parsers.parseSyml)(content);
|
|
259
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
260
|
+
if (key === "__metadata" || !entry)
|
|
261
|
+
continue;
|
|
262
|
+
if (isBerry && entry.linkType === "soft")
|
|
263
|
+
continue;
|
|
264
|
+
const version = entry.version;
|
|
265
|
+
if (!version)
|
|
266
|
+
continue;
|
|
267
|
+
let source;
|
|
268
|
+
let sourceUrl;
|
|
269
|
+
if (!isBerry && entry.resolved) {
|
|
270
|
+
if (entry.resolved.startsWith("file:"))
|
|
271
|
+
continue;
|
|
272
|
+
const classified = classifySource(entry.resolved);
|
|
273
|
+
source = classified.source;
|
|
274
|
+
sourceUrl = classified.sourceUrl;
|
|
275
|
+
}
|
|
276
|
+
const specifiers = key.split(",").map((s) => s.trim().replace(/^"|"$/g, ""));
|
|
277
|
+
let name = null;
|
|
278
|
+
for (const spec of specifiers) {
|
|
279
|
+
name = extractPackageName(spec);
|
|
280
|
+
if (name)
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
if (!name)
|
|
284
|
+
continue;
|
|
285
|
+
const existing = lockMap.get(name);
|
|
286
|
+
if (existing && !isHigherVersion(version, existing.resolved))
|
|
287
|
+
continue;
|
|
288
|
+
const lockEntry = { resolved: version, scopes: ["prod"] };
|
|
289
|
+
if (source)
|
|
290
|
+
lockEntry.source = source;
|
|
291
|
+
if (sourceUrl)
|
|
292
|
+
lockEntry.sourceUrl = sourceUrl;
|
|
293
|
+
lockMap.set(name, lockEntry);
|
|
294
|
+
}
|
|
295
|
+
return lockMap;
|
|
296
|
+
}
|
|
297
|
+
function parseBunLock(content) {
|
|
298
|
+
const lockMap = /* @__PURE__ */ new Map();
|
|
299
|
+
const json = content.replace(/,(\s*[}\]])/g, "$1");
|
|
300
|
+
const parsed = JSON.parse(json);
|
|
301
|
+
const packages = parsed.packages;
|
|
302
|
+
if (!packages)
|
|
303
|
+
return lockMap;
|
|
304
|
+
for (const [name, value] of Object.entries(packages)) {
|
|
305
|
+
if (!Array.isArray(value))
|
|
306
|
+
continue;
|
|
307
|
+
const ref = value[0];
|
|
308
|
+
if (!ref || typeof ref !== "string")
|
|
309
|
+
continue;
|
|
310
|
+
const pkgName = extractPackageName(ref);
|
|
311
|
+
if (!pkgName)
|
|
312
|
+
continue;
|
|
313
|
+
const version = ref.slice(pkgName.length + 1);
|
|
314
|
+
if (!version)
|
|
315
|
+
continue;
|
|
316
|
+
if (version.startsWith("file:") || version.startsWith("workspace:"))
|
|
317
|
+
continue;
|
|
318
|
+
const existing = lockMap.get(name);
|
|
319
|
+
if (existing && !isHigherVersion(version, existing.resolved))
|
|
320
|
+
continue;
|
|
321
|
+
lockMap.set(name, { resolved: version, scopes: ["prod"] });
|
|
322
|
+
}
|
|
323
|
+
return lockMap;
|
|
324
|
+
}
|
|
325
|
+
async function parseLockfile(cliType, lockfilePath, lockfileVersion) {
|
|
326
|
+
if (cliType === "bun" && lockfileVersion === 0)
|
|
327
|
+
return /* @__PURE__ */ new Map();
|
|
328
|
+
if (cliType === "vlt")
|
|
329
|
+
return /* @__PURE__ */ new Map();
|
|
330
|
+
const content = await import_fs.default.promises.readFile(lockfilePath, "utf-8");
|
|
331
|
+
switch (cliType) {
|
|
332
|
+
case "npm":
|
|
333
|
+
return parseNpmLock(content, lockfileVersion);
|
|
334
|
+
case "pnpm":
|
|
335
|
+
return parsePnpmLock(content, lockfileVersion);
|
|
336
|
+
case "yarn":
|
|
337
|
+
return parseYarnLock(content, lockfileVersion);
|
|
338
|
+
case "bun":
|
|
339
|
+
return parseBunLock(content);
|
|
340
|
+
default:
|
|
341
|
+
return /* @__PURE__ */ new Map();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function readPackageJson(startDir) {
|
|
345
|
+
let current = startDir;
|
|
346
|
+
for (; ; ) {
|
|
347
|
+
try {
|
|
348
|
+
const content = await import_fs.default.promises.readFile(
|
|
349
|
+
import_path.default.join(current, "package.json"),
|
|
350
|
+
"utf-8"
|
|
351
|
+
);
|
|
352
|
+
return JSON.parse(content);
|
|
353
|
+
} catch {
|
|
354
|
+
const parent = import_path.default.dirname(current);
|
|
355
|
+
if (parent === current)
|
|
356
|
+
return null;
|
|
357
|
+
current = parent;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function buildDirectMaps(pkgJson) {
|
|
362
|
+
const directScopes = /* @__PURE__ */ new Map();
|
|
363
|
+
const directRequested = /* @__PURE__ */ new Map();
|
|
364
|
+
const add = (deps, scope) => {
|
|
365
|
+
if (!deps || typeof deps !== "object")
|
|
366
|
+
return;
|
|
367
|
+
for (const [name, specifier] of Object.entries(
|
|
368
|
+
deps
|
|
369
|
+
)) {
|
|
370
|
+
if (!directScopes.has(name))
|
|
371
|
+
directScopes.set(name, /* @__PURE__ */ new Set());
|
|
372
|
+
directScopes.get(name).add(scope);
|
|
373
|
+
if (!directRequested.has(name))
|
|
374
|
+
directRequested.set(name, specifier);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
add(pkgJson.dependencies, "prod");
|
|
378
|
+
add(pkgJson.devDependencies, "dev");
|
|
379
|
+
add(pkgJson.peerDependencies, "peer");
|
|
380
|
+
add(pkgJson.optionalDependencies, "optional");
|
|
381
|
+
return { directScopes, directRequested };
|
|
382
|
+
}
|
|
383
|
+
async function generateProjectManifest({
|
|
384
|
+
workPath,
|
|
385
|
+
nodeVersion,
|
|
386
|
+
cliType,
|
|
387
|
+
lockfilePath,
|
|
388
|
+
lockfileVersion,
|
|
389
|
+
framework,
|
|
390
|
+
serviceType
|
|
391
|
+
}) {
|
|
392
|
+
try {
|
|
393
|
+
const pkgJson = await readPackageJson(workPath);
|
|
394
|
+
if (!pkgJson)
|
|
395
|
+
return;
|
|
396
|
+
const { directScopes, directRequested } = buildDirectMaps(pkgJson);
|
|
397
|
+
const lockMap = lockfilePath ? await parseLockfile(cliType, lockfilePath, lockfileVersion) : /* @__PURE__ */ new Map();
|
|
398
|
+
const directDeps = [];
|
|
399
|
+
const transitiveDeps = [];
|
|
400
|
+
for (const [name, scopes] of directScopes) {
|
|
401
|
+
const lock = lockMap.get(name);
|
|
402
|
+
const dep = {
|
|
403
|
+
name,
|
|
404
|
+
type: "direct",
|
|
405
|
+
scopes: [...scopes].sort(),
|
|
406
|
+
requested: directRequested.get(name),
|
|
407
|
+
resolved: lock?.resolved ?? ""
|
|
408
|
+
};
|
|
409
|
+
if (lock?.source)
|
|
410
|
+
dep.source = lock.source;
|
|
411
|
+
if (lock?.sourceUrl)
|
|
412
|
+
dep.sourceUrl = lock.sourceUrl;
|
|
413
|
+
directDeps.push(dep);
|
|
414
|
+
}
|
|
415
|
+
for (const [name, lock] of lockMap) {
|
|
416
|
+
if (directScopes.has(name))
|
|
417
|
+
continue;
|
|
418
|
+
const dep = {
|
|
419
|
+
name,
|
|
420
|
+
type: "transitive",
|
|
421
|
+
scopes: lock.scopes,
|
|
422
|
+
resolved: lock.resolved
|
|
423
|
+
};
|
|
424
|
+
if (lock.source)
|
|
425
|
+
dep.source = lock.source;
|
|
426
|
+
if (lock.sourceUrl)
|
|
427
|
+
dep.sourceUrl = lock.sourceUrl;
|
|
428
|
+
transitiveDeps.push(dep);
|
|
429
|
+
}
|
|
430
|
+
const runtimeVersion = {
|
|
431
|
+
resolved: String(nodeVersion.major)
|
|
432
|
+
};
|
|
433
|
+
const enginesNode = pkgJson.engines?.node;
|
|
434
|
+
if (enginesNode) {
|
|
435
|
+
runtimeVersion.requested = enginesNode;
|
|
436
|
+
runtimeVersion.requestedSource = "package.json";
|
|
437
|
+
} else {
|
|
438
|
+
for (const filename of [".node-version", ".nvmrc"]) {
|
|
439
|
+
try {
|
|
440
|
+
const val = await import_fs.default.promises.readFile(
|
|
441
|
+
import_path.default.join(workPath, filename),
|
|
442
|
+
"utf-8"
|
|
443
|
+
);
|
|
444
|
+
const trimmed = val.trim();
|
|
445
|
+
if (trimmed) {
|
|
446
|
+
runtimeVersion.requested = trimmed;
|
|
447
|
+
runtimeVersion.requestedSource = filename;
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
} catch {
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const manifest = {
|
|
455
|
+
version: import_package_manifest.MANIFEST_VERSION,
|
|
456
|
+
runtime: "node",
|
|
457
|
+
...framework ? { framework } : {},
|
|
458
|
+
...serviceType ? { serviceType } : {},
|
|
459
|
+
runtimeVersion,
|
|
460
|
+
dependencies: [
|
|
461
|
+
...directDeps.sort((a, b) => a.name.localeCompare(b.name)),
|
|
462
|
+
...transitiveDeps.sort((a, b) => a.name.localeCompare(b.name))
|
|
463
|
+
]
|
|
464
|
+
};
|
|
465
|
+
await (0, import_package_manifest.writeProjectManifest)(manifest, workPath, "node");
|
|
466
|
+
} catch (err) {
|
|
467
|
+
(0, import_debug.default)(
|
|
468
|
+
`generateProjectManifest: ${err instanceof Error ? err.message : String(err)}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
473
|
+
0 && (module.exports = {
|
|
474
|
+
generateProjectManifest
|
|
475
|
+
});
|
package/dist/types.d.ts
CHANGED
|
@@ -118,8 +118,8 @@ export interface BuildOptions {
|
|
|
118
118
|
subdomain?: string;
|
|
119
119
|
/** Workspace directory for this service, relative to the project root. */
|
|
120
120
|
workspace?: string;
|
|
121
|
-
/** Cron schedule expression (e.g., "0 0 * * *"). Only present for cron services. */
|
|
122
|
-
schedule?: string;
|
|
121
|
+
/** Cron schedule expression(s) (e.g., "0 0 * * *"). Only present for cron services. */
|
|
122
|
+
schedule?: string | string[];
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
export interface PrepareCacheOptions {
|
|
@@ -515,7 +515,7 @@ export interface Service {
|
|
|
515
515
|
routePrefix?: string;
|
|
516
516
|
routePrefixSource?: 'configured' | 'generated';
|
|
517
517
|
subdomain?: string;
|
|
518
|
-
schedule?: string;
|
|
518
|
+
schedule?: string | string[];
|
|
519
519
|
handlerFunction?: string;
|
|
520
520
|
topics?: ServiceTopics;
|
|
521
521
|
/** custom prefix to inject service URL env vars */
|
|
@@ -690,12 +690,16 @@ export interface TriggerEvent extends TriggerEventBase {
|
|
|
690
690
|
}
|
|
691
691
|
export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';
|
|
692
692
|
export type ServiceType = 'web' | 'cron' | 'worker' | 'job';
|
|
693
|
-
export interface
|
|
693
|
+
export interface ExperimentalServiceMount {
|
|
694
694
|
/** URL path prefix where the service is mounted. */
|
|
695
695
|
path?: string;
|
|
696
696
|
/** Optional subdomain this service is mounted on. */
|
|
697
697
|
subdomain?: string;
|
|
698
698
|
}
|
|
699
|
+
export interface ServiceMount {
|
|
700
|
+
/** URL path prefix where the service is mounted. */
|
|
701
|
+
path: string;
|
|
702
|
+
}
|
|
699
703
|
/**
|
|
700
704
|
* Configuration for a service in vercel.json.
|
|
701
705
|
* @experimental This feature is experimental and may change.
|
|
@@ -722,21 +726,23 @@ export interface ExperimentalServiceConfig {
|
|
|
722
726
|
builder?: string;
|
|
723
727
|
/** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
|
|
724
728
|
runtime?: string;
|
|
729
|
+
workspace?: string;
|
|
725
730
|
buildCommand?: string;
|
|
726
731
|
installCommand?: string;
|
|
732
|
+
preDeployCommand?: string;
|
|
727
733
|
/** Lambda config */
|
|
728
734
|
memory?: number;
|
|
729
735
|
maxDuration?: MaxDuration;
|
|
730
736
|
includeFiles?: string | string[];
|
|
731
737
|
excludeFiles?: string | string[];
|
|
732
738
|
/** Preferred routing config alias for routePrefix/subdomain. */
|
|
733
|
-
mount?: string |
|
|
739
|
+
mount?: string | ExperimentalServiceMount;
|
|
734
740
|
/** URL prefix for routing (deprecated, use mount instead) */
|
|
735
741
|
routePrefix?: string;
|
|
736
742
|
/** Subdomain this service should respond to (web services only). */
|
|
737
743
|
subdomain?: string;
|
|
738
744
|
/** Cron schedule expression(s) (e.g., "0 0 * * *") */
|
|
739
|
-
schedule?: string;
|
|
745
|
+
schedule?: string | string[];
|
|
740
746
|
topics?: ServiceTopics;
|
|
741
747
|
/** Custom prefix to use to inject service URL env vars */
|
|
742
748
|
envPrefix?: string;
|
|
@@ -746,6 +752,45 @@ export interface ExperimentalServiceConfig {
|
|
|
746
752
|
* @experimental This feature is experimental and may change.
|
|
747
753
|
*/
|
|
748
754
|
export type ExperimentalServices = Record<string, ExperimentalServiceConfig>;
|
|
755
|
+
/**
|
|
756
|
+
* Public configuration for a service in vercel.json.
|
|
757
|
+
*/
|
|
758
|
+
export interface ServiceConfig {
|
|
759
|
+
type?: ServiceType;
|
|
760
|
+
trigger?: JobTrigger;
|
|
761
|
+
/**
|
|
762
|
+
* Path to the service's root directory relative to the project root.
|
|
763
|
+
* Should contain a manifest file (package.json, pyproject.toml, etc.).
|
|
764
|
+
* Defaults to ".".
|
|
765
|
+
*/
|
|
766
|
+
root?: string;
|
|
767
|
+
/**
|
|
768
|
+
* Service entrypoint, relative to the service root directory.
|
|
769
|
+
* Can be either a file path (runtime entrypoint) or a directory path
|
|
770
|
+
* (service workspace for framework-based services).
|
|
771
|
+
*/
|
|
772
|
+
entrypoint?: string;
|
|
773
|
+
/** Framework to use */
|
|
774
|
+
framework?: string;
|
|
775
|
+
/** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
|
|
776
|
+
runtime?: string;
|
|
777
|
+
buildCommand?: string;
|
|
778
|
+
preDeployCommand?: string;
|
|
779
|
+
/** Lambda config */
|
|
780
|
+
memory?: number;
|
|
781
|
+
maxDuration?: MaxDuration;
|
|
782
|
+
includeFiles?: string | string[];
|
|
783
|
+
excludeFiles?: string | string[];
|
|
784
|
+
/** Preferred routing config for route paths. */
|
|
785
|
+
mount?: string | ServiceMount;
|
|
786
|
+
/** Cron schedule expression(s) (e.g., "0 0 * * *") */
|
|
787
|
+
schedule?: string | string[];
|
|
788
|
+
topics?: ServiceTopics;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Map of service name to public service configuration.
|
|
792
|
+
*/
|
|
793
|
+
export type Services = Record<string, ServiceConfig>;
|
|
749
794
|
/**
|
|
750
795
|
* Map of service group name to array of service names belonging to that group.
|
|
751
796
|
* @experimental This feature is experimental and may change.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/build-utils",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.23.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.js",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@types/node-fetch": "^2.1.6",
|
|
32
32
|
"@types/semver": "6.0.0",
|
|
33
33
|
"@types/yazl": "2.4.2",
|
|
34
|
+
"@yarnpkg/parsers": "^3.0.0",
|
|
34
35
|
"aggregate-error": "3.0.1",
|
|
35
36
|
"async-retry": "1.2.3",
|
|
36
37
|
"async-sema": "2.1.4",
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"semver": "6.3.1",
|
|
53
54
|
"smol-toml": "1.5.2",
|
|
54
55
|
"vitest": "2.0.1",
|
|
56
|
+
"typescript": "4.9.5",
|
|
55
57
|
"yazl": "2.5.1",
|
|
56
58
|
"@vercel/error-utils": "2.1.0",
|
|
57
59
|
"@vercel/routing-utils": "6.2.0"
|