@vercel/backends 0.0.61 → 0.0.63

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/index.mjs CHANGED
@@ -1,20 +1,17 @@
1
1
  import { builtinModules, createRequire } from "node:module";
2
- import { delimiter, dirname, extname, join } from "path";
2
+ import { delimiter, dirname, join } from "path";
3
3
  import { FileBlob, FileFsRef, NodejsLambda, Span, debug, defaultCachePathGlob, download, execCommand, getEnvForPackageManager, getLambdaOptionsFromFunction, getNodeBinPaths, getNodeVersion, glob, isBackendFramework, isBunVersion, isExperimentalBackendsWithoutIntrospectionEnabled, runNpmInstall, runPackageJsonScript, scanParentDirs } from "@vercel/build-utils";
4
4
  import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { lstat, readFile, rm, stat } from "node:fs/promises";
6
- import { basename, dirname as dirname$1, extname as extname$1, isAbsolute, join as join$1, relative } from "node:path";
6
+ import { basename, dirname as dirname$1, extname, isAbsolute, join as join$1, relative, resolve, sep } from "node:path";
7
7
  import { build as build$2 } from "rolldown";
8
8
  import { exports } from "resolve.exports";
9
9
  import { isNativeError } from "node:util/types";
10
- import { nodeFileTrace as nodeFileTrace$1, resolve } from "@vercel/nft";
10
+ import { nodeFileTrace as nodeFileTrace$1, resolve as resolve$1 } from "@vercel/nft";
11
11
  import { transform } from "oxc-transform";
12
- import { createRequire as createRequire$1 } from "module";
13
- import { spawn } from "child_process";
14
- import { existsSync as existsSync$1 } from "fs";
15
12
  import execa from "execa";
16
13
  import { readFile as readFile$1, writeFile } from "fs/promises";
17
- import { spawn as spawn$1 } from "node:child_process";
14
+ import { spawn } from "node:child_process";
18
15
  import { tmpdir } from "node:os";
19
16
  import { z } from "zod";
20
17
 
@@ -46,7 +43,7 @@ async function downloadInstallAndBundle(args) {
46
43
  spawnEnv
47
44
  };
48
45
  }
49
- async function maybeExecBuildCommand(args, { spawnEnv, entrypointFsDirname }) {
46
+ async function maybeExecBuildCommand(args, { spawnEnv }) {
50
47
  const projectBuildCommand = args.config.projectSettings?.buildCommand;
51
48
  if (projectBuildCommand) {
52
49
  const nodeBinPath = getNodeBinPaths({
@@ -61,7 +58,7 @@ async function maybeExecBuildCommand(args, { spawnEnv, entrypointFsDirname }) {
61
58
  cwd: args.workPath
62
59
  });
63
60
  }
64
- return runPackageJsonScript(entrypointFsDirname, ["build"], { env: spawnEnv }, args.config.projectSettings?.createdAt);
61
+ return runPackageJsonScript(args.workPath, ["build"], { env: spawnEnv }, args.config.projectSettings?.createdAt);
65
62
  }
66
63
 
67
64
  //#endregion
@@ -93,7 +90,7 @@ const plugin = (args) => {
93
90
  * Determine if a resolved module is CommonJS based on package.json exports
94
91
  */
95
92
  const isCommonJS = async (bareImport, resolvedPath, resolvedInfo) => {
96
- const ext = extname$1(resolvedPath);
93
+ const ext = extname(resolvedPath);
97
94
  if (ext === ".cjs") return true;
98
95
  if (ext === ".mjs") return false;
99
96
  if (ext === ".js" || ext === ".ts") {
@@ -231,7 +228,7 @@ const nodeFileTrace = async (args) => {
231
228
  ts: true,
232
229
  mixedModules: true,
233
230
  async resolve(id, parent, job, cjsResolve) {
234
- return resolve(id, parent, job, cjsResolve);
231
+ return resolve$1(id, parent, job, cjsResolve);
235
232
  },
236
233
  async readFile(fsPath) {
237
234
  try {
@@ -276,7 +273,7 @@ var __dirname = typeof __dirname !== 'undefined' ? __dirname : __dirname_(__file
276
273
  const rolldown$1 = async (args) => {
277
274
  const entrypointPath = join$1(args.workPath, args.entrypoint);
278
275
  const outputDir = join$1(args.workPath, args.out);
279
- const extension = extname$1(args.entrypoint);
276
+ const extension = extname(args.entrypoint);
280
277
  const extensionMap = {
281
278
  ".ts": {
282
279
  format: "auto",
@@ -407,9 +404,24 @@ const Colors = {
407
404
  };
408
405
 
409
406
  //#endregion
410
- //#region src/cervel/typescript.ts
411
- const require_$1 = createRequire$1(import.meta.url);
412
- const typescript$1 = (args) => {
407
+ //#region src/typescript.ts
408
+ /**
409
+ * Typecheck via the TypeScript compiler API (`createProgram`, `getPreEmitDiagnostics`),
410
+ * not by spawning the `tsc` binary.
411
+ *
412
+ * We only want to validate the deployment entrypoint and its import graph, not every
413
+ * file matched by `tsconfig` `include`. The CLI cannot combine `--project` with explicit
414
+ * root files (TS5042), so expressing 'project options + entry-only roots' in one `tsc`
415
+ * call requires a generated tsconfig on disk. Writing beside the user's config is
416
+ * invasive; a temp config elsewhere often breaks `node_modules` / `@types` resolution
417
+ * relative to the real project. The API lets us reuse `parseJsonConfigFileContent` (same
418
+ * options as `-p`) with explicit `rootNames`, no files written, and a compiler host whose
419
+ * current directory stays `workPath`.
420
+ *
421
+ * The `typescript` package is resolved with `require` from the user's app (peer dependency), not bundled.
422
+ */
423
+ const require_ = createRequire(import.meta.url);
424
+ const typescript = (args) => {
413
425
  const { span } = args;
414
426
  return span.child("vc.builder.backends.tsCompile").trace(async () => {
415
427
  const extension = extname(args.entrypoint);
@@ -418,79 +430,76 @@ const typescript$1 = (args) => {
418
430
  ".mts",
419
431
  ".cts"
420
432
  ].includes(extension)) return;
421
- const tscPath = resolveTscPath$1(args);
422
- if (!tscPath) {
433
+ const ts = resolveTypeScriptModule(args.workPath);
434
+ if (!ts) {
423
435
  console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
424
436
  return null;
425
437
  }
426
- return doTypeCheck$1(args, tscPath);
438
+ return doTypeCheck(args, ts);
427
439
  });
428
440
  };
429
- async function doTypeCheck$1(args, tscPath) {
430
- let stdout = "";
431
- let stderr = "";
432
- /**
433
- * This might be subject to change.
434
- * - if no tscPath, skip typecheck
435
- * - if tsconfig, provide the tsconfig path
436
- * - else provide the entrypoint path
437
- */
438
- const tscArgs = [
439
- tscPath,
440
- "--noEmit",
441
- "--pretty",
442
- "--allowJs",
443
- "--esModuleInterop",
444
- "--skipLibCheck"
445
- ];
446
- const tsconfig = await findNearestTsconfig$1(args.workPath);
447
- if (tsconfig) tscArgs.push("--project", tsconfig);
448
- else tscArgs.push(args.entrypoint);
449
- const child = spawn(process.execPath, tscArgs, {
450
- cwd: args.workPath,
451
- stdio: [
452
- "ignore",
453
- "pipe",
454
- "pipe"
455
- ]
456
- });
457
- child.stdout?.on("data", (data) => {
458
- stdout += data.toString();
459
- });
460
- child.stderr?.on("data", (data) => {
461
- stderr += data.toString();
462
- });
463
- await new Promise((resolve$1, reject) => {
464
- child.on("close", (code) => {
465
- if (code === 0) {
466
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
467
- resolve$1();
468
- } else {
469
- const output = stdout || stderr;
470
- if (output) {
471
- console.error("\nTypeScript type check failed:\n");
472
- console.error(output);
473
- }
474
- reject(/* @__PURE__ */ new Error("TypeScript type check failed"));
475
- }
476
- });
477
- child.on("error", (err) => {
478
- reject(err);
479
- });
480
- });
441
+ async function doTypeCheck(args, ts) {
442
+ const entryAbsolute = resolve(args.workPath, args.entrypoint);
443
+ const tsconfig = await findNearestTsconfig(args.workPath);
444
+ const formatDiagnostics = process.stdout.isTTY ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics;
445
+ const diagnosticHost = {
446
+ getNewLine: () => ts.sys.newLine,
447
+ getCanonicalFileName: (fileName) => ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(),
448
+ getCurrentDirectory: () => args.workPath
449
+ };
450
+ let options;
451
+ let parseDiagnostics = [];
452
+ if (tsconfig) {
453
+ const configRead = ts.readConfigFile(tsconfig, ts.sys.readFile);
454
+ if (configRead.error) {
455
+ const message = formatDiagnostics([configRead.error], diagnosticHost);
456
+ console.error("\nTypeScript type check failed:\n");
457
+ console.error(message);
458
+ throw new Error("TypeScript type check failed");
459
+ }
460
+ const parsed = ts.parseJsonConfigFileContent(configRead.config, ts.sys, dirname$1(tsconfig), void 0, tsconfig);
461
+ parseDiagnostics = parsed.errors;
462
+ options = {
463
+ ...parsed.options,
464
+ noEmit: true,
465
+ skipLibCheck: true,
466
+ allowJs: true,
467
+ esModuleInterop: true
468
+ };
469
+ } else options = {
470
+ noEmit: true,
471
+ skipLibCheck: true,
472
+ allowJs: true,
473
+ esModuleInterop: true,
474
+ target: ts.ScriptTarget.ES2022,
475
+ module: ts.ModuleKind.NodeNext,
476
+ moduleResolution: ts.ModuleResolutionKind.NodeNext
477
+ };
478
+ const compilerHost = ts.createCompilerHost(options);
479
+ compilerHost.getCurrentDirectory = () => args.workPath;
480
+ const program = ts.createProgram([entryAbsolute], options, compilerHost);
481
+ const errors = [...parseDiagnostics, ...ts.getPreEmitDiagnostics(program)].filter((d) => d.category === ts.DiagnosticCategory.Error);
482
+ if (errors.length === 0) {
483
+ console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
484
+ return;
485
+ }
486
+ const output = formatDiagnostics(errors, diagnosticHost);
487
+ console.error("\nTypeScript type check failed:\n");
488
+ console.error(output);
489
+ throw new Error("TypeScript type check failed");
481
490
  }
482
- const resolveTscPath$1 = (args) => {
491
+ function resolveTypeScriptModule(workPath) {
483
492
  try {
484
- return require_$1.resolve("typescript/bin/tsc", { paths: [args.workPath] });
493
+ return require_(require_.resolve("typescript", { paths: [workPath] }));
485
494
  } catch (_e) {
486
495
  return null;
487
496
  }
488
- };
489
- const findNearestTsconfig$1 = async (workPath) => {
490
- const tsconfigPath = join(workPath, "tsconfig.json");
491
- if (existsSync$1(tsconfigPath)) return tsconfigPath;
497
+ }
498
+ const findNearestTsconfig = async (workPath) => {
499
+ const tsconfigPath = join$1(workPath, "tsconfig.json");
500
+ if (existsSync(tsconfigPath)) return tsconfigPath;
492
501
  if (workPath === "/") return;
493
- return findNearestTsconfig$1(join(workPath, ".."));
502
+ return findNearestTsconfig(join$1(workPath, ".."));
494
503
  };
495
504
 
496
505
  //#endregion
@@ -524,12 +533,24 @@ const entrypointExtensions = [
524
533
  const entrypoints = entrypointFilenames.flatMap((filename) => entrypointExtensions.map((extension) => `${filename}.${extension}`));
525
534
  const createFrameworkRegex = (framework) => new RegExp(`(?:from|require|import)\\s*(?:\\(\\s*)?["']${framework}["']\\s*(?:\\))?`, "g");
526
535
  const findEntrypoint = async (cwd) => {
527
- let framework;
536
+ let packageJsonObject = null;
528
537
  try {
529
538
  const packageJson = await readFile(join$1(cwd, "package.json"), "utf-8");
530
- const packageJsonObject = JSON.parse(packageJson);
531
- framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
539
+ packageJsonObject = JSON.parse(packageJson);
532
540
  } catch (_) {}
541
+ if (packageJsonObject) {
542
+ const main = typeof packageJsonObject.main === "string" ? packageJsonObject.main.trim() : "";
543
+ if (main) {
544
+ const abs = resolve(cwd, main);
545
+ const rel = relative(cwd, abs);
546
+ if (!rel.startsWith("..") && rel !== "") try {
547
+ await readFile(abs, "utf-8");
548
+ return rel.split(sep).join("/");
549
+ } catch {}
550
+ }
551
+ }
552
+ let framework;
553
+ if (packageJsonObject) framework = frameworks.find((framework$1) => packageJsonObject.dependencies?.[framework$1]);
533
554
  if (!framework) for (const entrypoint of entrypoints) {
534
555
  const entrypointPath = join$1(cwd, entrypoint);
535
556
  try {
@@ -550,7 +571,7 @@ const findEntrypoint = async (cwd) => {
550
571
  };
551
572
  const findEntrypointOrThrow = async (cwd) => {
552
573
  const entrypoint = await findEntrypoint(cwd);
553
- if (!entrypoint) throw new Error(`No entrypoint found in "${cwd}". Expected one of: ${entrypoints.join(", ")}`);
574
+ if (!entrypoint) throw new Error(`No entrypoint found in "${cwd}". Set package.json "main" to a server file, or add one of: ${entrypoints.join(", ")}`);
554
575
  return entrypoint;
555
576
  };
556
577
 
@@ -564,7 +585,7 @@ const getBuildSummary = async (outputDir) => {
564
585
  const build$1 = async (args) => {
565
586
  const entrypoint = args.entrypoint || await findEntrypointOrThrow(args.workPath);
566
587
  const span = args.span ?? new Span({ name: "cervel-build" });
567
- const [, rolldownResult] = await Promise.all([typescript$1({
588
+ const [, rolldownResult] = await Promise.all([typescript({
568
589
  entrypoint,
569
590
  workPath: args.workPath,
570
591
  span
@@ -625,7 +646,7 @@ const srvxOptions = {
625
646
  //#endregion
626
647
  //#region src/rolldown/resolve-format.ts
627
648
  const resolveEntrypointAndFormat = async (args) => {
628
- const extension = extname$1(args.entrypoint);
649
+ const extension = extname(args.entrypoint);
629
650
  const extensionMap = {
630
651
  ".ts": {
631
652
  format: "auto",
@@ -678,7 +699,7 @@ const resolveEntrypointAndFormat = async (args) => {
678
699
  async function applyServiceVcInit(args) {
679
700
  const { format, extension } = await resolveShimFormat(args);
680
701
  const handlerDir = dirname$1(args.handler);
681
- const vcInitName = `${basename(args.handler, extname$1(args.handler))}.__vc_service_vc_init${extension}`;
702
+ const vcInitName = `${basename(args.handler, extname(args.handler))}.__vc_service_vc_init${extension}`;
682
703
  const vcInitHandler = handlerDir === "." ? vcInitName : join$1(handlerDir, vcInitName);
683
704
  const handlerImportPath = `./${basename(args.handler)}`;
684
705
  const vcInitSource = format === "esm" ? createEsmServiceVcInit(handlerImportPath) : createCjsServiceVcInit(handlerImportPath);
@@ -700,7 +721,7 @@ async function resolveShimFormat(args) {
700
721
  });
701
722
  return {
702
723
  format,
703
- extension: extname$1(args.handler) || (format === "esm" ? ".mjs" : ".cjs")
724
+ extension: extname(args.handler) || (format === "esm" ? ".mjs" : ".cjs")
704
725
  };
705
726
  }
706
727
  const sharedShimPrelude = String.raw`
@@ -840,7 +861,7 @@ const nft = async (args) => {
840
861
  const nftSpan = args.span.child("vc.builder.backends.nft");
841
862
  const runNft = async () => {
842
863
  const ignorePatterns = [...args.ignoreNodeModules ? ["**/node_modules/**"] : [], ...args.ignore ? Array.isArray(args.ignore) ? args.ignore : [args.ignore] : []];
843
- const nftResult = await nodeFileTrace$1(Array.from(args.localBuildFiles), {
864
+ const nftResult = await nodeFileTrace$1(Array.from(args.localBuildFiles).filter((p) => existsSync(p)), {
844
865
  base: args.repoRootPath,
845
866
  processCwd: args.workPath,
846
867
  ts: true,
@@ -860,7 +881,13 @@ const nft = async (args) => {
860
881
  });
861
882
  for (const file of nftResult.fileList) {
862
883
  const absolutePath = join$1(args.repoRootPath, file);
863
- const stats = await lstat(absolutePath);
884
+ let stats;
885
+ try {
886
+ stats = await lstat(absolutePath);
887
+ } catch (error) {
888
+ if (isNativeError(error) && "code" in error && error.code === "ENOENT") continue;
889
+ throw error;
890
+ }
864
891
  const outputPath = file;
865
892
  if (args.localBuildFiles.has(join$1(args.repoRootPath, outputPath))) continue;
866
893
  if (stats.isSymbolicLink() || stats.isFile()) if (args.ignoreNodeModules) {
@@ -912,7 +939,7 @@ const rolldown = async (args) => {
912
939
  }
913
940
  };
914
941
  const isCommonJS = async (bareImport, resolvedPath, resolvedInfo) => {
915
- const ext = extname$1(resolvedPath);
942
+ const ext = extname(resolvedPath);
916
943
  if (ext === ".cjs") return true;
917
944
  if (ext === ".mjs") return false;
918
945
  if (ext === ".js" || ext === ".ts") {
@@ -974,8 +1001,12 @@ const rolldown = async (args) => {
974
1001
  id,
975
1002
  external: true
976
1003
  };
977
- if (resolved?.id && isLocalImport(resolved.id)) localBuildFiles.add(resolved.id);
978
- else if (!resolved && !(isBareImport(id) && importer)) localBuildFiles.add(join$1(args.workPath, id));
1004
+ if (resolved?.id && isLocalImport(resolved.id)) {
1005
+ if (existsSync(resolved.id)) localBuildFiles.add(resolved.id);
1006
+ } else if (!resolved && !(isBareImport(id) && importer)) {
1007
+ const candidate = join$1(args.workPath, id);
1008
+ if (existsSync(candidate)) localBuildFiles.add(candidate);
1009
+ }
979
1010
  if (importer?.startsWith(CJS_SHIM_PREFIX) && isBareImport(id)) return {
980
1011
  id,
981
1012
  external: true
@@ -1132,7 +1163,7 @@ const introspection = async (args) => {
1132
1163
  const tempFilePath = join$1(outputTempDir, "output.txt");
1133
1164
  const writeStream = createWriteStream(tempFilePath);
1134
1165
  let streamClosed = false;
1135
- const child = spawn$1("node", [
1166
+ const child = spawn("node", [
1136
1167
  "-r",
1137
1168
  rolldownCjsLoaderPath,
1138
1169
  "--import",
@@ -1272,6 +1303,20 @@ const introspection = async (args) => {
1272
1303
 
1273
1304
  //#endregion
1274
1305
  //#region src/build.ts
1306
+ /**
1307
+ * `outputDirectory` is usually the same project setting the static builder uses
1308
+ * (Vite/Webpack/etc. client output). We only reuse it for the Node lambda when we
1309
+ * find a known server entry file under that folder; otherwise we bundle from
1310
+ * source with rolldown. Errors here are swallowed so static-only output trees do
1311
+ * not fail the build.
1312
+ */
1313
+ async function findEntrypointInOutputDir(dir) {
1314
+ try {
1315
+ return await findEntrypoint(dir);
1316
+ } catch {
1317
+ return;
1318
+ }
1319
+ }
1275
1320
  const maybeDoBuildCommand = async (args, downloadResult) => {
1276
1321
  const buildCommandResult = await maybeExecBuildCommand(args, downloadResult);
1277
1322
  const outputSetting = args.config.outputDirectory;
@@ -1279,7 +1324,7 @@ const maybeDoBuildCommand = async (args, downloadResult) => {
1279
1324
  let entrypoint;
1280
1325
  if (buildCommandResult && outputSetting) if (outputSetting) {
1281
1326
  const _outputDir = join$1(args.workPath, outputSetting);
1282
- const _entrypoint = await findEntrypoint(_outputDir);
1327
+ const _entrypoint = await findEntrypointInOutputDir(_outputDir);
1283
1328
  if (_entrypoint) {
1284
1329
  outputDir = _outputDir;
1285
1330
  entrypoint = _entrypoint;
@@ -1291,7 +1336,7 @@ const maybeDoBuildCommand = async (args, downloadResult) => {
1291
1336
  ]) {
1292
1337
  const _outputDir = join$1(args.workPath, outputDirectory);
1293
1338
  if (existsSync(_outputDir)) {
1294
- const _entrypoint = await findEntrypoint(_outputDir);
1339
+ const _entrypoint = await findEntrypointInOutputDir(_outputDir);
1295
1340
  if (_entrypoint) {
1296
1341
  outputDir = _outputDir;
1297
1342
  entrypoint = _entrypoint;
@@ -1313,93 +1358,6 @@ const maybeDoBuildCommand = async (args, downloadResult) => {
1313
1358
  };
1314
1359
  };
1315
1360
 
1316
- //#endregion
1317
- //#region src/typescript.ts
1318
- const require_ = createRequire(import.meta.url);
1319
- const typescript = (args) => {
1320
- const { span } = args;
1321
- return span.child("vc.builder.backends.tsCompile").trace(async () => {
1322
- const extension = extname$1(args.entrypoint);
1323
- if (![
1324
- ".ts",
1325
- ".mts",
1326
- ".cts"
1327
- ].includes(extension)) return;
1328
- const tscPath = resolveTscPath(args);
1329
- if (!tscPath) {
1330
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(TypeScript not found)")}`));
1331
- return null;
1332
- }
1333
- return doTypeCheck(args, tscPath);
1334
- });
1335
- };
1336
- async function doTypeCheck(args, tscPath) {
1337
- let stdout = "";
1338
- let stderr = "";
1339
- /**
1340
- * This might be subject to change.
1341
- * - if no tscPath, skip typecheck
1342
- * - if tsconfig, provide the tsconfig path
1343
- * - else provide the entrypoint path
1344
- */
1345
- const tscArgs = [
1346
- tscPath,
1347
- "--noEmit",
1348
- "--pretty",
1349
- "--allowJs",
1350
- "--esModuleInterop",
1351
- "--skipLibCheck"
1352
- ];
1353
- const tsconfig = await findNearestTsconfig(args.workPath);
1354
- if (tsconfig) tscArgs.push("--project", tsconfig);
1355
- else tscArgs.push(args.entrypoint);
1356
- const child = spawn$1(process.execPath, tscArgs, {
1357
- cwd: args.workPath,
1358
- stdio: [
1359
- "ignore",
1360
- "pipe",
1361
- "pipe"
1362
- ]
1363
- });
1364
- child.stdout?.on("data", (data) => {
1365
- stdout += data.toString();
1366
- });
1367
- child.stderr?.on("data", (data) => {
1368
- stderr += data.toString();
1369
- });
1370
- await new Promise((resolve$1, reject) => {
1371
- child.on("close", (code) => {
1372
- if (code === 0) {
1373
- console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck complete`));
1374
- resolve$1();
1375
- } else {
1376
- const output = stdout || stderr;
1377
- if (output) {
1378
- console.error("\nTypeScript type check failed:\n");
1379
- console.error(output);
1380
- }
1381
- reject(/* @__PURE__ */ new Error("TypeScript type check failed"));
1382
- }
1383
- });
1384
- child.on("error", (err) => {
1385
- reject(err);
1386
- });
1387
- });
1388
- }
1389
- const resolveTscPath = (args) => {
1390
- try {
1391
- return require_.resolve("typescript/bin/tsc", { paths: [args.workPath] });
1392
- } catch (_e) {
1393
- return null;
1394
- }
1395
- };
1396
- const findNearestTsconfig = async (workPath) => {
1397
- const tsconfigPath = join$1(workPath, "tsconfig.json");
1398
- if (existsSync(tsconfigPath)) return tsconfigPath;
1399
- if (workPath === "/") return;
1400
- return findNearestTsconfig(join$1(workPath, ".."));
1401
- };
1402
-
1403
1361
  //#endregion
1404
1362
  //#region src/introspection/index.ts
1405
1363
  const require = createRequire(import.meta.url);
@@ -1429,7 +1387,7 @@ const introspectApp = async (args) => {
1429
1387
  await new Promise((resolvePromise) => {
1430
1388
  try {
1431
1389
  debug("Spawning introspection process");
1432
- const child = spawn$1("node", [
1390
+ const child = spawn("node", [
1433
1391
  "-r",
1434
1392
  cjsLoaderPath,
1435
1393
  "--import",
@@ -1589,6 +1547,11 @@ const getFramework = (args) => {
1589
1547
  //#endregion
1590
1548
  //#region src/index.ts
1591
1549
  const version = 2;
1550
+ /** Non-empty Build Command from project settings / vercel.json (not the default `build` script). */
1551
+ function hasExplicitBuildCommand(config) {
1552
+ const cmd = config.buildCommand ?? config.projectSettings?.buildCommand;
1553
+ return typeof cmd === "string" && cmd.trim().length > 0;
1554
+ }
1592
1555
  const build = async (args) => {
1593
1556
  const downloadResult = await downloadInstallAndBundle(args);
1594
1557
  const nodeVersion = await getNodeVersion(args.workPath, void 0, args.config, args.meta);
@@ -1621,7 +1584,11 @@ const build = async (args) => {
1621
1584
  additionalFolders: [],
1622
1585
  additionalDeps: []
1623
1586
  });
1624
- const typescriptPromise = typescript({
1587
+ let typescriptPromise;
1588
+ if (hasExplicitBuildCommand(args.config)) {
1589
+ console.log(Colors.gray(`${Colors.bold(Colors.cyan("✓"))} Typecheck skipped ${Colors.gray("(Build Command is configured)")}`));
1590
+ typescriptPromise = Promise.resolve();
1591
+ } else typescriptPromise = typescript({
1625
1592
  entrypoint,
1626
1593
  workPath: args.workPath,
1627
1594
  span: buildSpan
@@ -94,7 +94,7 @@ const nft = async (args) => {
94
94
  const nftSpan = args.span.child("vc.builder.backends.nft");
95
95
  const runNft = async () => {
96
96
  const ignorePatterns = [...args.ignoreNodeModules ? ["**/node_modules/**"] : [], ...args.ignore ? Array.isArray(args.ignore) ? args.ignore : [args.ignore] : []];
97
- const nftResult = await nodeFileTrace(Array.from(args.localBuildFiles), {
97
+ const nftResult = await nodeFileTrace(Array.from(args.localBuildFiles).filter((p) => existsSync(p)), {
98
98
  base: args.repoRootPath,
99
99
  processCwd: args.workPath,
100
100
  ts: true,
@@ -114,7 +114,13 @@ const nft = async (args) => {
114
114
  });
115
115
  for (const file of nftResult.fileList) {
116
116
  const absolutePath = join(args.repoRootPath, file);
117
- const stats = await lstat(absolutePath);
117
+ let stats;
118
+ try {
119
+ stats = await lstat(absolutePath);
120
+ } catch (error) {
121
+ if (isNativeError(error) && "code" in error && error.code === "ENOENT") continue;
122
+ throw error;
123
+ }
118
124
  const outputPath = file;
119
125
  if (args.localBuildFiles.has(join(args.repoRootPath, outputPath))) continue;
120
126
  if (stats.isSymbolicLink() || stats.isFile()) if (args.ignoreNodeModules) {
@@ -228,8 +234,12 @@ const rolldown = async (args) => {
228
234
  id,
229
235
  external: true
230
236
  };
231
- if (resolved?.id && isLocalImport(resolved.id)) localBuildFiles.add(resolved.id);
232
- else if (!resolved && !(isBareImport(id) && importer)) localBuildFiles.add(join(args.workPath, id));
237
+ if (resolved?.id && isLocalImport(resolved.id)) {
238
+ if (existsSync(resolved.id)) localBuildFiles.add(resolved.id);
239
+ } else if (!resolved && !(isBareImport(id) && importer)) {
240
+ const candidate = join(args.workPath, id);
241
+ if (existsSync(candidate)) localBuildFiles.add(candidate);
242
+ }
233
243
  if (importer?.startsWith(CJS_SHIM_PREFIX) && isBareImport(id)) return {
234
244
  id,
235
245
  external: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/backends",
3
- "version": "0.0.61",
3
+ "version": "0.0.63",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index.mjs",
6
6
  "homepage": "https://vercel.com/docs",
@@ -34,7 +34,7 @@
34
34
  "srvx": "0.8.9",
35
35
  "tsx": "4.21.0",
36
36
  "zod": "3.22.4",
37
- "@vercel/build-utils": "13.16.0"
37
+ "@vercel/build-utils": "13.17.1"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "typescript": "^4.0.0 || ^5.0.0"