@vercel/backends 0.4.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/index.mjs +2 -346
  2. package/package.json +2 -4
package/dist/index.mjs CHANGED
@@ -1,11 +1,8 @@
1
1
  import { builtinModules, createRequire } from "node:module";
2
2
  import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
4
- import path, { delimiter, dirname as dirname$1, join as join$1 } from "path";
5
- import { FileBlob, FileFsRef, MANIFEST_VERSION, NodejsLambda, Span, createDiagnostics, debug, defaultCachePathGlob, download, execCommand, getEnvForPackageManager, getInternalServiceCronPath, getLambdaOptionsFromFunction, getNodeBinPaths, getNodeVersion, getReportedServiceType, glob, isBackendFramework, isBunVersion, isExperimentalBackendsWithoutIntrospectionEnabled, isScheduleTriggeredService, runNpmInstall, runPackageJsonScript, scanParentDirs, writeProjectManifest } from "@vercel/build-utils";
6
- import fs from "fs";
7
- import yaml from "js-yaml";
8
- import { parseSyml } from "@yarnpkg/parsers";
4
+ import { delimiter, dirname as dirname$1, join as join$1 } from "path";
5
+ import { FileBlob, FileFsRef, NodejsLambda, Span, createDiagnostics, debug, defaultCachePathGlob, download, execCommand, generateProjectManifest, getEnvForPackageManager, getInternalServiceCronPath, getLambdaOptionsFromFunction, getNodeBinPaths, getNodeVersion, getReportedServiceType, glob, isBackendFramework, isBunVersion, isExperimentalBackendsWithoutIntrospectionEnabled, isScheduleTriggeredService, runNpmInstall, runPackageJsonScript, scanParentDirs } from "@vercel/build-utils";
9
6
  import { lstat, readFile, rm, stat } from "node:fs/promises";
10
7
  import { build as build$2 } from "rolldown";
11
8
  import { exports } from "resolve.exports";
@@ -70,347 +67,6 @@ async function maybeExecBuildCommand(args, { spawnEnv }) {
70
67
 
71
68
  //#endregion
72
69
  //#region src/diagnostics.ts
73
- function classifySource(resolvedUrl) {
74
- if (!resolvedUrl) return {};
75
- if (resolvedUrl.startsWith("file:")) return {
76
- source: "file",
77
- sourceUrl: resolvedUrl.slice(5)
78
- };
79
- if (resolvedUrl.startsWith("git+") || resolvedUrl.startsWith("git://")) return {
80
- source: "git",
81
- sourceUrl: resolvedUrl.replace(/^git\+/, "")
82
- };
83
- try {
84
- return {
85
- source: "registry",
86
- sourceUrl: new URL(resolvedUrl).origin
87
- };
88
- } catch {
89
- return {};
90
- }
91
- }
92
- function npmEntryScopes(entry) {
93
- const scopes = [];
94
- if (entry.dev) scopes.push("dev");
95
- if (entry.peer) scopes.push("peer");
96
- if (entry.optional) scopes.push("optional");
97
- if (scopes.length === 0) scopes.push("prod");
98
- return scopes;
99
- }
100
- function parseNpmLock(content, lockfileVersion) {
101
- const lockMap = /* @__PURE__ */ new Map();
102
- const parsed = JSON.parse(content);
103
- if ((lockfileVersion ?? parsed.lockfileVersion ?? 1) >= 2) {
104
- const packages = parsed.packages;
105
- if (!packages) return lockMap;
106
- for (const [key, entry] of Object.entries(packages)) {
107
- if (key === "") continue;
108
- if (!key.startsWith("node_modules/")) continue;
109
- if (entry.link === true) continue;
110
- const rest = key.slice(13);
111
- const isScoped = rest.startsWith("@");
112
- const slashCount = (rest.match(/\//g) ?? []).length;
113
- if (isScoped ? slashCount !== 1 : slashCount !== 0) continue;
114
- const resolved = entry.resolved;
115
- if (resolved?.startsWith("file:")) continue;
116
- const version$1 = entry.version ?? "";
117
- const existing = lockMap.get(rest);
118
- if (existing && !isHigherVersion(version$1, existing.resolved)) continue;
119
- const { source, sourceUrl } = classifySource(resolved);
120
- const lockEntry = {
121
- resolved: version$1,
122
- scopes: npmEntryScopes(entry)
123
- };
124
- if (source) lockEntry.source = source;
125
- if (sourceUrl) lockEntry.sourceUrl = sourceUrl;
126
- lockMap.set(rest, lockEntry);
127
- }
128
- } else {
129
- const dependencies = parsed.dependencies;
130
- if (!dependencies) return lockMap;
131
- const walk = (deps) => {
132
- for (const [name, entry] of Object.entries(deps)) {
133
- const resolved = entry.resolved;
134
- if (!resolved?.startsWith("file:")) {
135
- const version$1 = entry.version ?? "";
136
- const existing = lockMap.get(name);
137
- if (!existing || isHigherVersion(version$1, existing.resolved)) {
138
- const { source, sourceUrl } = classifySource(resolved);
139
- const lockEntry = {
140
- resolved: version$1,
141
- scopes: npmEntryScopes(entry)
142
- };
143
- if (source) lockEntry.source = source;
144
- if (sourceUrl) lockEntry.sourceUrl = sourceUrl;
145
- lockMap.set(name, lockEntry);
146
- }
147
- }
148
- const nested = entry.dependencies;
149
- if (nested) walk(nested);
150
- }
151
- };
152
- walk(dependencies);
153
- }
154
- return lockMap;
155
- }
156
- function isHigherVersion(a, b) {
157
- const seg = (v) => v.split(/[.\-+]/).map((s) => parseInt(s, 10) || 0);
158
- const pa = seg(a);
159
- const pb = seg(b);
160
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
161
- if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
162
- if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
163
- }
164
- return false;
165
- }
166
- function extractPackageName(spec) {
167
- const s = spec.replace(/\(.*\)$/, "");
168
- if (s.startsWith("@")) {
169
- const i$1 = s.indexOf("@", 1);
170
- return i$1 === -1 ? null : s.slice(0, i$1);
171
- }
172
- const i = s.lastIndexOf("@");
173
- return i <= 0 ? null : s.slice(0, i);
174
- }
175
- function parsePnpmV9Key(key) {
176
- const name = extractPackageName(key);
177
- if (!name) return null;
178
- return {
179
- name,
180
- version: key.replace(/\(.*\)$/, "").slice(name.length + 1)
181
- };
182
- }
183
- function parsePnpmV5Key(key) {
184
- if (!key.startsWith("/")) return null;
185
- const rest = key.slice(1);
186
- if (rest.startsWith("@")) {
187
- const firstSlash = rest.indexOf("/");
188
- if (firstSlash === -1) return null;
189
- const secondSlash = rest.indexOf("/", firstSlash + 1);
190
- if (secondSlash === -1) return null;
191
- return {
192
- name: rest.slice(0, secondSlash),
193
- version: rest.slice(secondSlash + 1).split("_")[0]
194
- };
195
- }
196
- const slashIndex = rest.indexOf("/");
197
- if (slashIndex === -1) return null;
198
- return {
199
- name: rest.slice(0, slashIndex),
200
- version: rest.slice(slashIndex + 1).split("_")[0]
201
- };
202
- }
203
- function parsePnpmV6Key(key) {
204
- if (!key.startsWith("/")) return null;
205
- const rest = key.slice(1);
206
- let atIdx;
207
- if (rest.startsWith("@")) atIdx = rest.indexOf("@", 1);
208
- else atIdx = rest.indexOf("@");
209
- if (atIdx === -1) return null;
210
- return {
211
- name: rest.slice(0, atIdx),
212
- version: rest.slice(atIdx + 1).split("_")[0].replace(/\(.*\)$/, "")
213
- };
214
- }
215
- function classifyPnpmResolution(resolution) {
216
- if (!resolution) return {};
217
- if (resolution.type === "directory" || resolution.directory) return { local: true };
218
- if (typeof resolution.tarball === "string") return classifySource(resolution.tarball);
219
- if (resolution.type === "git" || typeof resolution.repo === "string") return {
220
- source: "git",
221
- sourceUrl: resolution.repo ?? void 0
222
- };
223
- return {};
224
- }
225
- function parsePnpmLock(content, lockfileVersion) {
226
- const lockMap = /* @__PURE__ */ new Map();
227
- const docs = [];
228
- yaml.safeLoadAll(content, (doc) => docs.push(doc));
229
- const parsedYaml = docs[0];
230
- if (!parsedYaml) return lockMap;
231
- const lv = lockfileVersion ?? Number(parsedYaml.lockfileVersion ?? "0");
232
- const packages = parsedYaml.packages;
233
- if (!packages) return lockMap;
234
- const parseKey = lv >= 9 ? parsePnpmV9Key : lv >= 6 ? parsePnpmV6Key : parsePnpmV5Key;
235
- for (const [key, entry] of Object.entries(packages)) {
236
- const keyParsed = parseKey(key);
237
- if (!keyParsed) continue;
238
- const { name, version: version$1 } = keyParsed;
239
- const resolution = entry.resolution;
240
- const { local, source, sourceUrl } = classifyPnpmResolution(resolution);
241
- if (local) continue;
242
- const existing = lockMap.get(name);
243
- if (existing && !isHigherVersion(version$1, existing.resolved)) continue;
244
- const lockEntry = {
245
- resolved: version$1,
246
- scopes: ["prod"]
247
- };
248
- if (source) lockEntry.source = source;
249
- if (sourceUrl) lockEntry.sourceUrl = sourceUrl;
250
- lockMap.set(name, lockEntry);
251
- }
252
- return lockMap;
253
- }
254
- function parseYarnLock(content, lockfileVersion) {
255
- const lockMap = /* @__PURE__ */ new Map();
256
- const isBerry = (lockfileVersion ?? 1) >= 2;
257
- const parsed = parseSyml(content);
258
- for (const [key, entry] of Object.entries(parsed)) {
259
- if (key === "__metadata" || !entry) continue;
260
- if (isBerry && entry.linkType === "soft") continue;
261
- const version$1 = entry.version;
262
- if (!version$1) continue;
263
- let source;
264
- let sourceUrl;
265
- if (!isBerry && entry.resolved) {
266
- if (entry.resolved.startsWith("file:")) continue;
267
- const classified = classifySource(entry.resolved);
268
- source = classified.source;
269
- sourceUrl = classified.sourceUrl;
270
- }
271
- const specifiers = key.split(",").map((s) => s.trim().replace(/^"|"$/g, ""));
272
- let name = null;
273
- for (const spec of specifiers) {
274
- name = extractPackageName(spec);
275
- if (name) break;
276
- }
277
- if (!name) continue;
278
- const existing = lockMap.get(name);
279
- if (existing && !isHigherVersion(version$1, existing.resolved)) continue;
280
- const lockEntry = {
281
- resolved: version$1,
282
- scopes: ["prod"]
283
- };
284
- if (source) lockEntry.source = source;
285
- if (sourceUrl) lockEntry.sourceUrl = sourceUrl;
286
- lockMap.set(name, lockEntry);
287
- }
288
- return lockMap;
289
- }
290
- function parseBunLock(content) {
291
- const lockMap = /* @__PURE__ */ new Map();
292
- const json = content.replace(/,(\s*[}\]])/g, "$1");
293
- const packages = JSON.parse(json).packages;
294
- if (!packages) return lockMap;
295
- for (const [name, value] of Object.entries(packages)) {
296
- if (!Array.isArray(value)) continue;
297
- const ref = value[0];
298
- if (!ref || typeof ref !== "string") continue;
299
- const pkgName = extractPackageName(ref);
300
- if (!pkgName) continue;
301
- const version$1 = ref.slice(pkgName.length + 1);
302
- if (!version$1) continue;
303
- if (version$1.startsWith("file:") || version$1.startsWith("workspace:")) continue;
304
- const existingBun = lockMap.get(name);
305
- if (existingBun && !isHigherVersion(version$1, existingBun.resolved)) continue;
306
- lockMap.set(name, {
307
- resolved: version$1,
308
- scopes: ["prod"]
309
- });
310
- }
311
- return lockMap;
312
- }
313
- async function parseLockfile(cliType, lockfilePath, lockfileVersion) {
314
- if (cliType === "bun" && lockfileVersion === 0) return /* @__PURE__ */ new Map();
315
- if (cliType === "vlt") return /* @__PURE__ */ new Map();
316
- const content = await fs.promises.readFile(lockfilePath, "utf-8");
317
- switch (cliType) {
318
- case "npm": return parseNpmLock(content, lockfileVersion);
319
- case "pnpm": return parsePnpmLock(content, lockfileVersion);
320
- case "yarn": return parseYarnLock(content, lockfileVersion);
321
- case "bun": return parseBunLock(content);
322
- default: return /* @__PURE__ */ new Map();
323
- }
324
- }
325
- async function readPackageJson(startDir) {
326
- let current = startDir;
327
- for (;;) try {
328
- const content = await fs.promises.readFile(path.join(current, "package.json"), "utf-8");
329
- return JSON.parse(content);
330
- } catch {
331
- const parent = path.dirname(current);
332
- if (parent === current) return null;
333
- current = parent;
334
- }
335
- }
336
- function buildDirectMaps(pkgJson) {
337
- const directScopes = /* @__PURE__ */ new Map();
338
- const directRequested = /* @__PURE__ */ new Map();
339
- const add = (deps, scope) => {
340
- if (!deps || typeof deps !== "object") return;
341
- for (const [name, specifier] of Object.entries(deps)) {
342
- if (!directScopes.has(name)) directScopes.set(name, /* @__PURE__ */ new Set());
343
- directScopes.get(name).add(scope);
344
- if (!directRequested.has(name)) directRequested.set(name, specifier);
345
- }
346
- };
347
- add(pkgJson.dependencies, "prod");
348
- add(pkgJson.devDependencies, "dev");
349
- add(pkgJson.peerDependencies, "peer");
350
- add(pkgJson.optionalDependencies, "optional");
351
- return {
352
- directScopes,
353
- directRequested
354
- };
355
- }
356
- async function generateProjectManifest({ workPath, nodeVersion, cliType, lockfilePath, lockfileVersion, framework, serviceType }) {
357
- try {
358
- const pkgJson = await readPackageJson(workPath);
359
- if (!pkgJson) return;
360
- const { directScopes, directRequested } = buildDirectMaps(pkgJson);
361
- const lockMap = lockfilePath ? await parseLockfile(cliType, lockfilePath, lockfileVersion) : /* @__PURE__ */ new Map();
362
- const directDeps = [];
363
- const transitiveDeps = [];
364
- for (const [name, scopes] of directScopes) {
365
- const lock = lockMap.get(name);
366
- const dep = {
367
- name,
368
- type: "direct",
369
- scopes: [...scopes].sort(),
370
- requested: directRequested.get(name),
371
- resolved: lock?.resolved ?? ""
372
- };
373
- if (lock?.source) dep.source = lock.source;
374
- if (lock?.sourceUrl) dep.sourceUrl = lock.sourceUrl;
375
- directDeps.push(dep);
376
- }
377
- for (const [name, lock] of lockMap) {
378
- if (directScopes.has(name)) continue;
379
- const dep = {
380
- name,
381
- type: "transitive",
382
- scopes: lock.scopes,
383
- resolved: lock.resolved
384
- };
385
- if (lock.source) dep.source = lock.source;
386
- if (lock.sourceUrl) dep.sourceUrl = lock.sourceUrl;
387
- transitiveDeps.push(dep);
388
- }
389
- const runtimeVersion = { resolved: String(nodeVersion.major) };
390
- const enginesNode = pkgJson.engines?.node;
391
- if (enginesNode) {
392
- runtimeVersion.requested = enginesNode;
393
- runtimeVersion.requestedSource = "package.json";
394
- } else for (const filename of [".node-version", ".nvmrc"]) try {
395
- const trimmed = (await fs.promises.readFile(path.join(workPath, filename), "utf-8")).trim();
396
- if (trimmed) {
397
- runtimeVersion.requested = trimmed;
398
- runtimeVersion.requestedSource = filename;
399
- break;
400
- }
401
- } catch {}
402
- await writeProjectManifest({
403
- version: MANIFEST_VERSION,
404
- runtime: "node",
405
- ...framework ? { framework } : {},
406
- ...serviceType ? { serviceType } : {},
407
- runtimeVersion,
408
- dependencies: [...directDeps.sort((a, b) => a.name.localeCompare(b.name)), ...transitiveDeps.sort((a, b) => a.name.localeCompare(b.name))]
409
- }, workPath, "node");
410
- } catch (err) {
411
- debug(`generateProjectManifest: ${err instanceof Error ? err.message : String(err)}`);
412
- }
413
- }
414
70
  const diagnostics = createDiagnostics("node");
415
71
 
416
72
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/backends",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.mjs",
6
6
  "homepage": "https://vercel.com/docs",
@@ -25,8 +25,6 @@
25
25
  "templates"
26
26
  ],
27
27
  "dependencies": {
28
- "@yarnpkg/parsers": "^3.0.0",
29
- "js-yaml": "^3.13.1",
30
28
  "@vercel/nft": "1.5.0",
31
29
  "execa": "3.2.0",
32
30
  "fs-extra": "11.1.0",
@@ -37,7 +35,7 @@
37
35
  "srvx": "0.8.9",
38
36
  "tsx": "4.21.0",
39
37
  "zod": "3.22.4",
40
- "@vercel/build-utils": "13.22.0"
38
+ "@vercel/build-utils": "13.23.0"
41
39
  },
42
40
  "devDependencies": {
43
41
  "@types/express": "5.0.3",