@vercel/build-utils 13.22.1 → 13.24.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 +16 -0
- package/dist/get-service-url-env-vars.d.ts +33 -11
- package/dist/get-service-url-env-vars.js +41 -6
- package/dist/index.d.ts +3 -2
- package/dist/index.js +7435 -719
- package/dist/node-diagnostics.d.ts +12 -0
- package/dist/node-diagnostics.js +475 -0
- package/dist/types.d.ts +59 -10
- 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 {
|
|
@@ -500,6 +500,12 @@ export interface ServiceQueueTopic {
|
|
|
500
500
|
export type ServiceTopics = string[] | ServiceQueueTopic[];
|
|
501
501
|
export declare const JOB_TRIGGERS: readonly ["queue", "schedule", "workflow"];
|
|
502
502
|
export type JobTrigger = (typeof JOB_TRIGGERS)[number];
|
|
503
|
+
export interface ServiceRefEnvVar {
|
|
504
|
+
type: 'service-ref';
|
|
505
|
+
service: string;
|
|
506
|
+
}
|
|
507
|
+
export type EnvVar = ServiceRefEnvVar;
|
|
508
|
+
export type EnvVars = Record<string, EnvVar>;
|
|
503
509
|
export interface Service {
|
|
504
510
|
name: string;
|
|
505
511
|
type: ServiceType;
|
|
@@ -515,11 +521,10 @@ export interface Service {
|
|
|
515
521
|
routePrefix?: string;
|
|
516
522
|
routePrefixSource?: 'configured' | 'generated';
|
|
517
523
|
subdomain?: string;
|
|
518
|
-
schedule?: string;
|
|
524
|
+
schedule?: string | string[];
|
|
519
525
|
handlerFunction?: string;
|
|
520
526
|
topics?: ServiceTopics;
|
|
521
|
-
|
|
522
|
-
envPrefix?: string;
|
|
527
|
+
env?: EnvVars;
|
|
523
528
|
}
|
|
524
529
|
export declare function getServiceQueueTopicConfigs(config: {
|
|
525
530
|
type?: ServiceType;
|
|
@@ -690,12 +695,16 @@ export interface TriggerEvent extends TriggerEventBase {
|
|
|
690
695
|
}
|
|
691
696
|
export type ServiceRuntime = 'node' | 'python' | 'go' | 'rust' | 'ruby';
|
|
692
697
|
export type ServiceType = 'web' | 'cron' | 'worker' | 'job';
|
|
693
|
-
export interface
|
|
698
|
+
export interface ExperimentalServiceMount {
|
|
694
699
|
/** URL path prefix where the service is mounted. */
|
|
695
700
|
path?: string;
|
|
696
701
|
/** Optional subdomain this service is mounted on. */
|
|
697
702
|
subdomain?: string;
|
|
698
703
|
}
|
|
704
|
+
export interface ServiceMount {
|
|
705
|
+
/** URL path prefix where the service is mounted. */
|
|
706
|
+
path: string;
|
|
707
|
+
}
|
|
699
708
|
/**
|
|
700
709
|
* Configuration for a service in vercel.json.
|
|
701
710
|
* @experimental This feature is experimental and may change.
|
|
@@ -722,30 +731,70 @@ export interface ExperimentalServiceConfig {
|
|
|
722
731
|
builder?: string;
|
|
723
732
|
/** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
|
|
724
733
|
runtime?: string;
|
|
734
|
+
workspace?: string;
|
|
725
735
|
buildCommand?: string;
|
|
726
736
|
installCommand?: string;
|
|
737
|
+
preDeployCommand?: string;
|
|
727
738
|
/** Lambda config */
|
|
728
739
|
memory?: number;
|
|
729
740
|
maxDuration?: MaxDuration;
|
|
730
741
|
includeFiles?: string | string[];
|
|
731
742
|
excludeFiles?: string | string[];
|
|
732
743
|
/** Preferred routing config alias for routePrefix/subdomain. */
|
|
733
|
-
mount?: string |
|
|
744
|
+
mount?: string | ExperimentalServiceMount;
|
|
734
745
|
/** URL prefix for routing (deprecated, use mount instead) */
|
|
735
746
|
routePrefix?: string;
|
|
736
747
|
/** Subdomain this service should respond to (web services only). */
|
|
737
748
|
subdomain?: string;
|
|
738
749
|
/** Cron schedule expression(s) (e.g., "0 0 * * *") */
|
|
739
|
-
schedule?: string;
|
|
750
|
+
schedule?: string | string[];
|
|
740
751
|
topics?: ServiceTopics;
|
|
741
|
-
|
|
742
|
-
envPrefix?: string;
|
|
752
|
+
env?: EnvVars;
|
|
743
753
|
}
|
|
744
754
|
/**
|
|
745
755
|
* Map of service name to service configuration.
|
|
746
756
|
* @experimental This feature is experimental and may change.
|
|
747
757
|
*/
|
|
748
758
|
export type ExperimentalServices = Record<string, ExperimentalServiceConfig>;
|
|
759
|
+
/**
|
|
760
|
+
* Public configuration for a service in vercel.json.
|
|
761
|
+
*/
|
|
762
|
+
export interface ServiceConfig {
|
|
763
|
+
type?: ServiceType;
|
|
764
|
+
trigger?: JobTrigger;
|
|
765
|
+
/**
|
|
766
|
+
* Path to the service's root directory relative to the project root.
|
|
767
|
+
* Should contain a manifest file (package.json, pyproject.toml, etc.).
|
|
768
|
+
* Defaults to ".".
|
|
769
|
+
*/
|
|
770
|
+
root?: string;
|
|
771
|
+
/**
|
|
772
|
+
* Service entrypoint, relative to the service root directory.
|
|
773
|
+
* Can be either a file path (runtime entrypoint) or a directory path
|
|
774
|
+
* (service workspace for framework-based services).
|
|
775
|
+
*/
|
|
776
|
+
entrypoint?: string;
|
|
777
|
+
/** Framework to use */
|
|
778
|
+
framework?: string;
|
|
779
|
+
/** Specific lambda runtime to use, e.g. nodejs24.x, python3.14 */
|
|
780
|
+
runtime?: string;
|
|
781
|
+
buildCommand?: string;
|
|
782
|
+
preDeployCommand?: string;
|
|
783
|
+
/** Lambda config */
|
|
784
|
+
memory?: number;
|
|
785
|
+
maxDuration?: MaxDuration;
|
|
786
|
+
includeFiles?: string | string[];
|
|
787
|
+
excludeFiles?: string | string[];
|
|
788
|
+
/** Preferred routing config for route paths. */
|
|
789
|
+
mount?: string | ServiceMount;
|
|
790
|
+
/** Cron schedule expression(s) (e.g., "0 0 * * *") */
|
|
791
|
+
schedule?: string | string[];
|
|
792
|
+
topics?: ServiceTopics;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Map of service name to public service configuration.
|
|
796
|
+
*/
|
|
797
|
+
export type Services = Record<string, ServiceConfig>;
|
|
749
798
|
/**
|
|
750
799
|
* Map of service group name to array of service names belonging to that group.
|
|
751
800
|
* @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.24.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"
|