UNPKG

vercel

Version:

The command-line interface for Vercel

1,422 lines (1,412 loc) • 67.7 kB
import { createRequire as __createRequire } from 'node:module'; import { fileURLToPath as __fileURLToPath } from 'node:url'; import { dirname as __dirname_ } from 'node:path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __dirname_(__filename); import { OUTPUT_DIR, getStaticServiceSchedules, importBuilders, isLambda, staticFiles, writeBuildResult } from "../../chunks/chunk-PNA7EZB2.js"; import { require_semver } from "../../chunks/chunk-IB5L4LKZ.js"; import { pullCommandLogic } from "../../chunks/chunk-FXOKKAK2.js"; import { AGENT_REASON, AGENT_STATUS } from "../../chunks/chunk-E3NE4SKN.js"; import { pickOverrides, readProjectSettings } from "../../chunks/chunk-LVUE7LLE.js"; import { ua_default } from "../../chunks/chunk-76ZNZKIN.js"; import "../../chunks/chunk-N733ZD4W.js"; import "../../chunks/chunk-N4WFAZKO.js"; import "../../chunks/chunk-UTXSTM52.js"; import "../../chunks/chunk-4VPRHRPA.js"; import { buildCommand } from "../../chunks/chunk-ZKKIBUCU.js"; import { help } from "../../chunks/chunk-MMF4BVAP.js"; import { DEFAULT_VERCEL_CONFIG_FILENAME, VERCEL_DIR, compileVercelConfig, findSourceVercelConfigFile, getProjectLink, parseTarget, pullEnvRecords, readJSONFile, require_ajv, require_dist, require_dist2, require_dist3, require_frameworks, require_lib, require_main, require_minimatch, resolveProjectCwd, validateConfig } from "../../chunks/chunk-X775BOSL.js"; import { TelemetryClient } from "../../chunks/chunk-4OEA5ILS.js"; import { outputAgentError } from "../../chunks/chunk-ULXHXZCZ.js"; import { stamp_default } from "../../chunks/chunk-CO5D46AG.js"; import "../../chunks/chunk-N2T234LO.js"; import "../../chunks/chunk-DKD6GTQT.js"; import { getFlagsSpecification, getGlobalFlagsOnlyFromArgs, parseArguments, printError, toEnumerableError } from "../../chunks/chunk-4GQQJY5Y.js"; import { CantParseJSONFile, cmd, getCommandName, getCommandNamePlain, packageName, require_lib as require_lib2 } from "../../chunks/chunk-UGXBNJMO.js"; import { pkg_default } from "../../chunks/chunk-P4QNYOFB.js"; import { emoji, output_manager_default, prependEmoji } from "../../chunks/chunk-ZQKJVHXY.js"; import { require_source } from "../../chunks/chunk-S7KYDPEM.js"; import { __toESM } from "../../chunks/chunk-TZ2YI2VH.js"; // src/commands/build/index.ts var import_chalk = __toESM(require_source(), 1); var import_dotenv = __toESM(require_main(), 1); var import_fs_extra2 = __toESM(require_lib(), 1); var import_minimatch = __toESM(require_minimatch(), 1); var import_semver = __toESM(require_semver(), 1); var import_client = __toESM(require_dist(), 1); var import_frameworks2 = __toESM(require_frameworks(), 1); var import_fs_detectors2 = __toESM(require_dist3(), 1); var import_routing_utils2 = __toESM(require_dist2(), 1); import { dirname, join as join2, normalize, relative as relative2, resolve, sep } from "path"; import { readdirSync, statSync } from "fs"; import { download, FileBlob, FileFsRef, getDiscontinuedNodeVersions, getInstalledPackageVersion, getServiceUrlEnvVars, getExperimentalServiceUrlEnvVars, normalizePath, NowBuildError as NowBuildError2, runNpmInstall, runCustomInstallCommand, resetCustomInstallCommandSet, Span, validateNpmrc, glob, getInternalServiceCronPath, getInternalServiceFunctionPath, getServiceQueueTopicConfigs, isBackendBuilder, isQueueTriggeredService, isScheduleTriggeredService, sanitizeConsumerName, downloadFile } from "@vercel/build-utils"; // src/util/build/corepack.ts var import_fs_extra = __toESM(require_lib(), 1); import { delimiter, join } from "path"; import { spawnAsync } from "@vercel/build-utils"; async function initCorepack({ repoRootPath }) { if (process.env.ENABLE_EXPERIMENTAL_COREPACK !== "1") { return null; } const pkg = await readJSONFile( join(repoRootPath, "package.json") ); if (pkg instanceof CantParseJSONFile) { output_manager_default.warn( "Warning: Could not enable corepack because package.json is invalid JSON", pkg.meta.parseErrorLocation ); } else if (!pkg?.packageManager) { output_manager_default.warn( 'Warning: Could not enable corepack because package.json is missing "packageManager" property' ); } else { output_manager_default.log( `Detected ENABLE_EXPERIMENTAL_COREPACK=1 and "${pkg.packageManager}" in package.json` ); const corepackRootDir = join(repoRootPath, VERCEL_DIR, "cache", "corepack"); const corepackHomeDir = join(corepackRootDir, "home"); const corepackShimDir = join(corepackRootDir, "shim"); await import_fs_extra.default.mkdirp(corepackHomeDir); await import_fs_extra.default.mkdirp(corepackShimDir); process.env.COREPACK_HOME = corepackHomeDir; process.env.PATH = `${corepackShimDir}${delimiter}${process.env.PATH}`; const pkgManagerName = pkg.packageManager.split("@")[0]; await spawnAsync( "corepack", ["enable", pkgManagerName, "--install-directory", corepackShimDir], { prettyCommand: `corepack enable ${pkgManagerName}` } ); return corepackShimDir; } return null; } function cleanupCorepack(corepackShimDir) { if (process.env.COREPACK_HOME) { delete process.env.COREPACK_HOME; } if (process.env.PATH) { process.env.PATH = process.env.PATH.replace( `${corepackShimDir}${delimiter}`, "" ); } } // src/util/build/monorepo.ts var import_fs_detectors = __toESM(require_dist3(), 1); var import_title = __toESM(require_lib2(), 1); import { relative, basename } from "path"; import { debug } from "@vercel/build-utils"; async function setMonorepoDefaultSettings(cwd, workPath, projectSettings) { const localFileSystem = new import_fs_detectors.LocalFileSystemDetector(cwd); const projectName = basename(workPath); const relativeToRoot = relative(workPath, cwd); const setCommand = (command, value) => { if (projectSettings[command]) { debug( `Skipping auto-assignment of ${command} as it is already set via project settings or configuration overrides.` ); } else { projectSettings[command] = value; } }; try { const result = await (0, import_fs_detectors.getMonorepoDefaultSettings)( projectName, relative(cwd, workPath), relativeToRoot, localFileSystem ); if (result === null) { return; } projectSettings.monorepoManager = result.monorepoManager; const { monorepoManager, ...commands } = result; output_manager_default.log( `Detected ${(0, import_title.default)(monorepoManager)}. Adjusting default settings...` ); if (commands.buildCommand) { setCommand("buildCommand", commands.buildCommand); } if (commands.installCommand) { setCommand("installCommand", commands.installCommand); } if (commands.commandForIgnoringBuildStep) { setCommand( "commandForIgnoringBuildStep", commands.commandForIgnoringBuildStep ); } } catch (error) { if (error instanceof import_fs_detectors.MissingBuildPipeline || error instanceof import_fs_detectors.MissingBuildTarget) { output_manager_default.warn(`${error.message} Skipping automatic setting assignment.`); return; } throw error; } } // src/util/build/scrub-argv.ts function scrubArgv(argv) { const clonedArgv = [...argv]; const tokenRE = /^(-[A-Za-z]*[bet]|--(?:build-env|env|token))(=.*)?$/; for (let i = 0, len = clonedArgv.length; i < len; i++) { const m = clonedArgv[i].match(tokenRE); if (m?.[2]) { clonedArgv[i] = `${m[1]}=REDACTED`; } else if (m && i + 1 < len) { clonedArgv[++i] = "REDACTED"; } } return clonedArgv; } // src/util/build/service-route-ownership.ts var import_routing_utils = __toESM(require_dist2(), 1); function isWebServiceWithPrefix(service) { return service.type === "web" && typeof service.routePrefix === "string"; } function getWebRoutePrefixes(services) { const unique = /* @__PURE__ */ new Set(); for (const service of services) { if (!isWebServiceWithPrefix(service)) continue; unique.add((0, import_routing_utils.normalizeRoutePrefix)(service.routePrefix)); } return Array.from(unique); } function scopeRoutesToServiceOwnership({ routes, owner, allServices }) { if (!isWebServiceWithPrefix(owner)) { return routes; } const allWebPrefixes = getWebRoutePrefixes(allServices); const ownershipGuard = (0, import_routing_utils.getOwnershipGuard)(owner.routePrefix, allWebPrefixes); if (!ownershipGuard) { return routes; } return routes.map((route) => { if ("handle" in route || typeof route.src !== "string") { return route; } return { ...route, src: (0, import_routing_utils.scopeRouteSourceToOwnership)(route.src, ownershipGuard) }; }); } // src/util/build/sort-builders.ts var import_frameworks = __toESM(require_frameworks(), 1); function sortBuilders(builds) { const frontendRuntimeSet = new Set( import_frameworks.frameworkList.map((f) => f.useRuntime?.use || "@vercel/static-build") ); frontendRuntimeSet.delete("@vercel/python"); frontendRuntimeSet.delete("@vercel/ruby"); frontendRuntimeSet.delete("@vercel/rust"); const toNumber = (build) => build.use === "@vercel/python" || build.use === "@vercel/ruby" || build.use === "@vercel/rust" ? 1 : frontendRuntimeSet.has(build.use) ? 0 : 2; return builds.sort((build1, build2) => { return toNumber(build1) - toNumber(build2); }); } // src/util/telemetry/commands/build/index.ts var BuildTelemetryClient = class extends TelemetryClient { trackCliOptionOutput(path) { if (path) { this.trackCliOption({ option: "output", value: this.redactedValue }); } } trackCliOptionTarget(option) { if (option) { this.trackCliOption({ option: "target", value: this.redactedTargetName(option) }); } } trackCliFlagProd(flag) { if (flag) { this.trackCliFlag("prod"); } } trackCliFlagYes(flag) { if (flag) { this.trackCliFlag("yes"); } } trackCliFlagStandalone(flag) { if (flag) { this.trackCliFlag("standalone"); } } trackCliOptionId(id) { if (id) { this.trackCliOption({ option: "id", value: this.redactedValue }); } } }; // src/util/validate-cron-secret.ts import { NowBuildError } from "@vercel/build-utils"; function validateCronSecret(cronSecret) { if (!cronSecret) { return null; } if (cronSecret !== cronSecret.trim()) { return new NowBuildError({ code: "INVALID_CRON_SECRET", message: "The `CRON_SECRET` environment variable contains leading or trailing whitespace, which is not allowed in HTTP header values.", link: "https://vercel.link/securing-cron-jobs", action: "Learn More" }); } const invalidChars = []; for (let i = 0; i < cronSecret.length; i++) { const code = cronSecret.charCodeAt(i); const isValidChar = code === 9 || // HTAB code >= 32 && code <= 126; if (!isValidChar) { invalidChars.push({ char: cronSecret[i], index: i, code }); } } if (invalidChars.length > 0) { const descriptions = invalidChars.slice(0, 3).map(({ code, index }) => { if (code < 32) { return `control character (0x${code.toString(16).padStart(2, "0")}) at position ${index}`; } else if (code === 127) { return `DEL character at position ${index}`; } else { return `non-ASCII character (0x${code.toString(16).padStart(2, "0")}) at position ${index}`; } }); const moreCount = invalidChars.length - 3; const moreText = moreCount > 0 ? `, and ${moreCount} more` : ""; return new NowBuildError({ code: "INVALID_CRON_SECRET", message: `The \`CRON_SECRET\` environment variable contains characters that are not valid in HTTP headers: ${descriptions.join(", ")}${moreText}. Only visible ASCII characters (letters, digits, symbols), spaces, and tabs are allowed.`, link: "https://vercel.link/securing-cron-jobs", action: "Learn More" }); } return null; } // src/util/validate-package-manifest.ts var import_ajv = __toESM(require_ajv(), 1); import { packageManifestSchema } from "@vercel/build-utils"; var ajv = new import_ajv.default(); var validate = ajv.compile(packageManifestSchema); function validatePackageManifest(data) { if (validate(data)) { return null; } const errors = validate.errors ?? []; return errors.map((e) => `${e.dataPath || "(root)"} ${e.message}`).join("; "); } // src/commands/build/index.ts function buildCommandWithGlobalFlags(baseSubcommand, argv) { const globalFlags = getGlobalFlagsOnlyFromArgs(argv.slice(2)); const full = globalFlags.length ? `${baseSubcommand} ${globalFlags.join(" ")}` : baseSubcommand; return getCommandNamePlain(full); } async function main(client) { const telemetryClient = new BuildTelemetryClient({ opts: { store: client.telemetryEventStore } }); const rootSpan = client.rootSpan?.child("vc") ?? new Span({ name: "vc" }); let { cwd } = client; cwd = await resolveProjectCwd(cwd); if (process.env.__VERCEL_BUILD_RUNNING) { output_manager_default.error( `${cmd( `${packageName} build` )} must not recursively invoke itself. Check the Build Command in the Project Settings or the ${cmd( "build" )} script in ${cmd("package.json")}` ); output_manager_default.error( `Learn More: https://vercel.link/recursive-invocation-of-commands` ); return 1; } else { process.env.__VERCEL_BUILD_RUNNING = "1"; } let parsedArgs = null; const flagsSpecification = getFlagsSpecification(buildCommand.options); try { parsedArgs = parseArguments(client.argv.slice(2), flagsSpecification); telemetryClient.trackCliOptionOutput(parsedArgs.flags["--output"]); telemetryClient.trackCliOptionTarget(parsedArgs.flags["--target"]); telemetryClient.trackCliFlagProd(parsedArgs.flags["--prod"]); telemetryClient.trackCliFlagYes(parsedArgs.flags["--yes"]); telemetryClient.trackCliFlagStandalone(parsedArgs.flags["--standalone"]); telemetryClient.trackCliOptionId(parsedArgs.flags["--id"]); } catch (error) { printError(error); return 1; } if (parsedArgs.flags["--help"]) { telemetryClient.trackCliFlagHelp("build"); output_manager_default.print(help(buildCommand, { columns: client.stderr.columns })); return 2; } const target = parseTarget({ flagName: "target", flags: parsedArgs.flags }) || "preview"; const yes = Boolean(parsedArgs.flags["--yes"]); const hasDeprecatedEnvVar = process.env.VERCEL_EXPERIMENTAL_STANDALONE_BUILD === "1"; if (hasDeprecatedEnvVar) { output_manager_default.warn( "The VERCEL_EXPERIMENTAL_STANDALONE_BUILD environment variable is deprecated. Please use the --standalone flag instead." ); } const standalone = Boolean( parsedArgs.flags["--standalone"] || hasDeprecatedEnvVar ); try { await validateNpmrc(cwd); } catch (err) { output_manager_default.prettyError(err); return 1; } const link = await rootSpan.child("vc.getProjectLink").trace(() => getProjectLink(client, cwd)); const projectRootDirectory = link?.projectRootDirectory ?? ""; if (link?.repoRoot) { cwd = client.cwd = link.repoRoot; } const vercelDir = join2(cwd, projectRootDirectory, VERCEL_DIR); let project = await rootSpan.child("vc.readProjectSettings").trace(() => readProjectSettings(vercelDir)); const isTTY = process.stdin.isTTY; while (!project?.settings) { let confirmed = yes; if (!confirmed) { if (client.nonInteractive) { outputAgentError( client, { status: AGENT_STATUS.ERROR, reason: AGENT_REASON.PROJECT_SETTINGS_REQUIRED, message: "No project settings found locally. Run pull to retrieve them, or re-run with --yes to pull automatically.", next: [ { command: buildCommandWithGlobalFlags( `pull --yes --environment ${target}`, client.argv ), when: "retrieve project settings" }, { command: buildCommandWithGlobalFlags( "build --yes", client.argv ), when: "re-run build after pull" } ] }, 1 ); return 1; } if (!isTTY) { output_manager_default.print( `No Project Settings found locally. Run ${getCommandName( "pull --yes" )} to retrieve them. In non-interactive mode, set VERCEL_TOKEN for authentication.` ); return 1; } confirmed = await client.input.confirm( `No Project Settings found locally. Run ${getCommandName( "pull" )} for retrieving them?`, true ); } if (!confirmed) { if (!client.nonInteractive) output_manager_default.print(`Canceled. No Project Settings retrieved. `); return 0; } const { argv: originalArgv } = client; client.cwd = join2(cwd, projectRootDirectory); client.setArgv([ ...originalArgv.slice(0, 2), "pull", `--environment`, target ]); const result = await pullCommandLogic( client, client.cwd, Boolean(parsedArgs.flags["--yes"]), target, parsedArgs.flags ); if (result !== 0) { return result; } client.cwd = cwd; client.setArgv(originalArgv); project = await readProjectSettings(vercelDir); } const defaultOutputDir = join2(cwd, projectRootDirectory, OUTPUT_DIR); const outputDir = parsedArgs.flags["--output"] ? resolve(parsedArgs.flags["--output"]) : defaultOutputDir; client.traceDiagnosticsPath = join2( outputDir, "diagnostics", "cli_traces.json" ); await Promise.all([ import_fs_extra2.default.remove(outputDir), // Also delete `.vercel/output`, in case the script is targeting Build Output API directly outputDir !== defaultOutputDir ? import_fs_extra2.default.remove(defaultOutputDir) : void 0 ]); const buildsJson = { "//": "This file was generated by the `vercel build` command. It is not part of the Build Output API.", target, argv: scrubArgv(process.argv), cliVersion: pkg_default.version }; const deploymentId = parsedArgs.flags["--id"]; if (!process.env.VERCEL_BUILD_IMAGE && !deploymentId && !client.nonInteractive) { output_manager_default.warn( "Build not running on Vercel. System environment variables will not be available." ); } const envToUnset = /* @__PURE__ */ new Set(["VERCEL", "NOW_BUILDER"]); try { const loadEnvSpan = rootSpan.child("vc.loadEnv"); try { if (deploymentId) { if (link?.orgId?.startsWith("team_")) { client.config.currentTeam = link.orgId; } output_manager_default.debug( `Fetching environment variables for deployment ${deploymentId}` ); const { buildEnv } = await fetchDeploymentBuildEnv( client, deploymentId ); for (const [key, value] of Object.entries(buildEnv)) { envToUnset.add(key); process.env[key] = value; } output_manager_default.debug( `Loaded ${Object.keys(buildEnv).length} environment variables from deployment ${deploymentId}` ); } else { const envPath = join2( cwd, projectRootDirectory, VERCEL_DIR, `.env.${target}.local` ); const dotenvResult = import_dotenv.default.config({ path: envPath, debug: output_manager_default.isDebugEnabled() }); if (dotenvResult.error) { output_manager_default.debug( `Failed loading environment variables: ${dotenvResult.error}` ); } else if (dotenvResult.parsed) { for (const key of Object.keys(dotenvResult.parsed)) { envToUnset.add(key); } output_manager_default.debug(`Loaded environment variables from "${envPath}"`); } } } finally { loadEnvSpan.stop(); } if (project.settings.analyticsId) { envToUnset.add("VERCEL_ANALYTICS_ID"); process.env.VERCEL_ANALYTICS_ID = project.settings.analyticsId; } process.env.VERCEL = "1"; process.env.NOW_BUILDER = "1"; try { await rootSpan.child("vc.doBuild").trace( (span) => doBuild(client, project, buildsJson, cwd, outputDir, span, standalone) ); } finally { await rootSpan.stop(); } if (client.nonInteractive) { const relOutputDir = relative2(cwd, outputDir); client.stdout.write( `${JSON.stringify( { status: AGENT_STATUS.OK, outputDir, outputDirRelative: relOutputDir.startsWith("..") ? outputDir : relOutputDir, target, message: "Build completed successfully.", next: [ { command: buildCommandWithGlobalFlags("deploy", client.argv), when: "Deploy the build output" } ] }, null, 2 )} ` ); } return 0; } catch (err) { if (client.nonInteractive) { client.stdout.write( `${JSON.stringify( { status: AGENT_STATUS.ERROR, reason: "build_failed", message: err?.message ?? String(err), next: [ { command: buildCommandWithGlobalFlags("pull --yes", client.argv), when: "Ensure project settings are present" }, { command: buildCommandWithGlobalFlags( "build --yes", client.argv ), when: "re-run build" } ] }, null, 2 )} ` ); } output_manager_default.prettyError(err); buildsJson.error = toEnumerableError(err); const buildsJsonPath = join2(outputDir, "builds.json"); const configJsonPath = join2(outputDir, "config.json"); await import_fs_extra2.default.outputJSON(buildsJsonPath, buildsJson, { spaces: 2 }); await import_fs_extra2.default.writeJSON(configJsonPath, { version: 3 }, { spaces: 2 }); return 1; } finally { for (const key of envToUnset) { delete process.env[key]; } delete process.env.VERCEL_INSTALL_COMPLETED; resetCustomInstallCommandSet(); } } async function doBuild(client, project, buildsJson, cwd, outputDir, span, standalone = false) { const { localConfigPath } = client; const VALID_DEPLOYMENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/; const workPath = join2(cwd, project.settings.rootDirectory || "."); const sourceConfigFile = await findSourceVercelConfigFile(workPath); let corepackShimDir; if (sourceConfigFile) { corepackShimDir = await initCorepack({ repoRootPath: cwd }); const installDepsSpan = span.child("vc.installDeps"); try { const installCommand = project.settings.installCommand; if (typeof installCommand === "string") { if (installCommand.trim()) { output_manager_default.log(`Running install command before config compilation...`); await runCustomInstallCommand({ destPath: workPath, installCommand, spawnOpts: { env: process.env }, projectCreatedAt: project.settings.createdAt }); } else { output_manager_default.debug("Skipping empty install command"); } } else { output_manager_default.log(`Installing dependencies before config compilation...`); await runNpmInstall( workPath, [], { env: process.env }, void 0, project.settings.createdAt ); } } finally { installDepsSpan.stop(); } process.env.VERCEL_INSTALL_COMPLETED = "1"; } const compileResult = await span.child("vc.compileVercelConfig").trace(() => compileVercelConfig(workPath)); const vercelConfigPath = localConfigPath || compileResult.configPath || join2(workPath, "vercel.json"); const [pkg, vercelConfig, hasInstrumentation] = await Promise.all([ readJSONFile(join2(workPath, "package.json")), readJSONFile(vercelConfigPath), (0, import_fs_detectors2.detectInstrumentation)(new import_fs_detectors2.LocalFileSystemDetector(workPath)) ]); if (pkg instanceof CantParseJSONFile) throw pkg; if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig; if (hasInstrumentation) { output_manager_default.debug( "OpenTelemetry instrumentation detected. Automatic fetch instrumentation will be disabled." ); process.env.VERCEL_TRACING_DISABLE_AUTOMATIC_FETCH_INSTRUMENTATION = "1"; } if (vercelConfig) { vercelConfig[import_client.fileNameSymbol] = compileResult.wasCompiled ? compileResult.sourceFile || DEFAULT_VERCEL_CONFIG_FILENAME : "vercel.json"; } const localConfig = vercelConfig || {}; const validateError = validateConfig(localConfig); if (validateError) { throw validateError; } if (localConfig.crons && localConfig.crons.length > 0) { const cronSecretError = validateCronSecret(process.env.CRON_SECRET); if (cronSecretError) { throw cronSecretError; } } const projectSettings = { ...project.settings, ...pickOverrides(localConfig) }; if (process.env.VERCEL_BUILD_MONOREPO_SUPPORT === "1" && pkg?.scripts?.["vercel-build"] === void 0 && projectSettings.rootDirectory !== null && projectSettings.rootDirectory !== ".") { await setMonorepoDefaultSettings(cwd, workPath, projectSettings); } if (process.env.VERCEL_FLAGS_DISABLE_DEFINITION_EMBEDDING !== "1") { const { prepareFlagsDefinitions } = await import("@vercel/prepare-flags-definitions"); await prepareFlagsDefinitions({ cwd, env: process.env, userAgentSuffix: ua_default, output: output_manager_default }); } const files = (await staticFiles(workPath, {})).map( (f) => normalizePath(relative2(workPath, f)) ); const routesResult = (0, import_routing_utils2.getTransformedRoutes)(localConfig); if (routesResult.error) { throw routesResult.error; } if (localConfig.builds && localConfig.functions) { throw new NowBuildError2({ code: "bad_request", message: "The `functions` property cannot be used in conjunction with the `builds` property. Please remove one of them.", link: "https://vercel.link/functions-and-builds" }); } let builds = localConfig.builds || []; let zeroConfigRoutes = []; let zeroConfigFallbackRoutes = []; let detectedServices; let isZeroConfig = false; if (builds.length > 0) { output_manager_default.warn( "Due to `builds` existing in your configuration file, the Build and Development Settings defined in your Project Settings will not apply. Learn More: https://vercel.link/unused-build-settings" ); builds = builds.flatMap((b) => expandBuild(files, b)); } else { isZeroConfig = true; const detectedBuilders = await span.child("vc.detectBuilders").trace( () => (0, import_fs_detectors2.detectBuilders)(files, pkg, { ...localConfig, projectSettings, ignoreBuildScript: true, featHandleMiss: true, workPath }) ); if (detectedBuilders.errors && detectedBuilders.errors.length > 0) { throw detectedBuilders.errors[0]; } for (const w of detectedBuilders.warnings) { output_manager_default.warn(w.message, null, w.link, w.action || "Learn More"); } if (detectedBuilders.builders) { builds = detectedBuilders.builders; } else { builds = [{ src: "**", use: "@vercel/static" }]; } detectedServices = detectedBuilders.services; if (detectedBuilders.useImplicitEnvInjection && detectedServices && detectedServices.length > 0) { const serviceUrlEnvVars = getExperimentalServiceUrlEnvVars({ services: detectedServices, frameworkList: import_frameworks2.frameworkList, currentEnv: process.env, deploymentUrl: process.env.VERCEL_URL }); for (const [key, value] of Object.entries(serviceUrlEnvVars)) { process.env[key] = value; output_manager_default.debug(`Injected service URL env var: ${key}=${value}`); } } zeroConfigRoutes.push(...detectedBuilders.redirectRoutes || []); const detectedHostRewriteRoutes = detectedBuilders.hostRewriteRoutes; zeroConfigRoutes = (0, import_routing_utils2.appendRoutesToPhase)({ routes: zeroConfigRoutes, newRoutes: detectedHostRewriteRoutes ?? null, phase: null }); zeroConfigRoutes.push( ...(0, import_routing_utils2.appendRoutesToPhase)({ routes: [], newRoutes: detectedBuilders.rewriteRoutes, phase: "filesystem" }) ); zeroConfigRoutes = (0, import_routing_utils2.appendRoutesToPhase)({ routes: zeroConfigRoutes, newRoutes: detectedBuilders.errorRoutes, phase: "error" }); zeroConfigRoutes.push(...detectedBuilders.defaultRoutes || []); zeroConfigFallbackRoutes = detectedBuilders.fallbackRoutes || []; } const builderSpecs = new Set(builds.map((b) => b.use)); const buildersWithPkgs = await span.child("vc.importBuilders").trace(() => importBuilders(builderSpecs, cwd, span)); const filesMap = {}; for (const path of files) { const fsPath = join2(workPath, path); const { mode } = await import_fs_extra2.default.stat(fsPath); filesMap[path] = new FileFsRef({ mode, fsPath }); } const buildStamp = stamp_default(); await import_fs_extra2.default.mkdirp(outputDir); const ops = []; const buildsJsonBuilds = new Map( builds.map((build) => { const builderWithPkg = buildersWithPkgs.get(build.use); if (!builderWithPkg) { throw new Error(`Failed to load Builder "${build.use}"`); } const { builder, pkg: builderPkg } = builderWithPkg; return [ build, { require: builderPkg.name, requirePath: builderWithPkg.path, apiVersion: builder.version, ...build } ]; }) ); buildsJson.builds = Array.from(buildsJsonBuilds.values()); await writeBuildJson(buildsJson, outputDir); const meta = { skipDownload: true, cliVersion: pkg_default.version }; const sortedBuilders = sortBuilders(builds); const buildResults = /* @__PURE__ */ new Map(); const overrides = []; const repoRootPath = cwd; if (!corepackShimDir) { corepackShimDir = await initCorepack({ repoRootPath }); } const diagnostics = {}; const packageManifests = []; const hasDetectedServices = detectedServices !== void 0 && detectedServices.length > 0; const hasQueueServices = hasDetectedServices && detectedServices.some(isQueueTriggeredService); const synthesizedServiceCrons = []; const serviceByBuilder = /* @__PURE__ */ new Map(); if (hasDetectedServices) { for (const service of detectedServices) { serviceByBuilder.set(service.builder, service); } } const preDeployEntries = []; for (const build of sortedBuilders) { if (typeof build.src !== "string") continue; const builderWithPkg = buildersWithPkgs.get(build.use); if (!builderWithPkg) { throw new Error(`Failed to load Builder "${build.use}"`); } try { const { builder, pkg: builderPkg } = builderWithPkg; const service = hasDetectedServices ? serviceByBuilder.get(build) : void 0; const stripServiceRoutePrefix = !!service?.routePrefix && service.routePrefix !== "/"; let buildWorkPath = workPath; let buildEntrypoint = build.src; let buildFiles = filesMap; if (service && service.workspace !== ".") { const wsPrefix = service.workspace + "/"; buildWorkPath = join2(workPath, service.workspace); buildEntrypoint = build.src.startsWith(wsPrefix) ? build.src.slice(wsPrefix.length) : build.src; buildFiles = {}; for (const [filePath, file] of Object.entries(filesMap)) { if (filePath.startsWith(wsPrefix)) { buildFiles[filePath.slice(wsPrefix.length)] = file; } } output_manager_default.debug( `Service "${service.name}": workspace-rooted build at "${buildWorkPath}", entrypoint "${buildEntrypoint}" (original: "${build.src}")` ); } const settingsForEnv = service ? { buildCommand: service.buildCommand ?? void 0, installCommand: service.installCommand ?? void 0, outputDirectory: projectSettings.outputDirectory ?? void 0, nodeVersion: projectSettings.nodeVersion ?? void 0 } : projectSettings; for (const key of [ "buildCommand", "installCommand", "outputDirectory", "nodeVersion" ]) { const value = settingsForEnv[key]; const envKey = `VERCEL_PROJECT_SETTINGS_` + key.replace(/[A-Z]/g, (letter) => `_${letter}`).toUpperCase(); if (typeof value === "string") { process.env[envKey] = value; output_manager_default.debug(`Setting env ${envKey} to "${value}"`); } else { delete process.env[envKey]; } } const isFrontendBuilder = build.config && "framework" in build.config; const builderFramework = build.config?.framework ?? projectSettings.framework; let buildConfig; if (isZeroConfig) { if (service) { buildConfig = { ...build.config, ...hasQueueServices ? { hasWorkerServices: true } : void 0, // Override project-level settings with service-specific ones. // The project-level framework is "services" which must NOT be // propagated to individual builders. projectSettings: { ...projectSettings, framework: service.framework ?? null, buildCommand: service.buildCommand ?? null, installCommand: service.installCommand ?? null }, installCommand: service.installCommand ?? void 0, buildCommand: service.buildCommand ?? void 0, preDeployCommand: service.preDeployCommand ?? void 0, framework: builderFramework, nodeVersion: projectSettings.nodeVersion, bunVersion: localConfig.bunVersion ?? void 0 }; } else { buildConfig = { outputDirectory: projectSettings.outputDirectory ?? void 0, ...build.config, projectSettings, installCommand: projectSettings.installCommand ?? void 0, devCommand: projectSettings.devCommand ?? void 0, buildCommand: projectSettings.buildCommand ?? void 0, framework: projectSettings.framework, nodeVersion: projectSettings.nodeVersion, bunVersion: localConfig.bunVersion ?? void 0 }; } } else { buildConfig = { ...build.config || {}, bunVersion: localConfig.bunVersion ?? void 0 }; } const builderSpan = span.child("vc.builder", { "builder.name": builderPkg.name, "builder.version": builderPkg.version, "builder.dynamicallyInstalled": String( builderWithPkg.dynamicallyInstalled ) }); const serviceRoutePrefix = build.config?.routePrefix; const serviceWorkspace = build.config?.workspace; const preDeployCmd = service?.preDeployCommand?.trim(); const preDeployEntry = preDeployCmd && service ? { service: service.name } : void 0; if (preDeployEntry) { preDeployEntries.push(preDeployEntry); } const buildOptions = { files: buildFiles, entrypoint: buildEntrypoint, workPath: buildWorkPath, repoRootPath, config: buildConfig, meta, span: builderSpan, ...preDeployCmd ? { registerPreDeploy: (callback) => { preDeployEntry.callback = callback; } } : void 0, ...service ? { service: { name: service.name, type: service.type, trigger: service.trigger, routePrefix: typeof serviceRoutePrefix === "string" ? serviceRoutePrefix : void 0, workspace: typeof serviceWorkspace === "string" ? serviceWorkspace : void 0, schedule: service.schedule } } : void 0 }; output_manager_default.debug( `Building entrypoint "${build.src}" with "${builderPkg.name}"` ); const restoreEnv = /* @__PURE__ */ new Map(); if (detectedServices && service?.env) { const perServiceEnv = getServiceUrlEnvVars({ requestedEnv: service.env, consumerService: service, services: detectedServices, frameworkList: import_frameworks2.frameworkList, currentEnv: process.env, deploymentUrl: process.env.VERCEL_URL }); for (const [key, value] of Object.entries(perServiceEnv)) { if (key in process.env) continue; restoreEnv.set(key, process.env[key]); process.env[key] = value; output_manager_default.debug(`Injected service URL env var: ${key}=${value}`); } } let buildResult; let rawBuildResult; try { rawBuildResult = await builderSpan.trace(async () => builder.build(buildOptions)); if (builder.version === -1) { const vx = rawBuildResult; buildResult = vx.result; } else { buildResult = rawBuildResult; } if (!hasDetectedServices && buildConfig.zeroConfig && isFrontendBuilder && "output" in buildResult && !buildResult.routes) { const framework2 = import_frameworks2.frameworkList.find( (f) => f.slug === buildConfig.framework ); if (framework2) { const defaultRoutes = await getFrameworkRoutes( framework2, buildWorkPath ); buildResult.routes = defaultRoutes; } } } finally { for (const [key, prior] of restoreEnv) { if (prior === void 0) { delete process.env[key]; } else { process.env[key] = prior; } } try { const builderDiagnostics = await builderSpan.child("vc.builder.diagnostics").trace(async () => { return await builder.diagnostics?.(buildOptions); }); if (builderDiagnostics) { const prefix = service && service.workspace !== "." ? service.workspace + "/" + builderPkg.name + "/" : ""; for (const [key, value] of Object.entries(builderDiagnostics)) { const fullKey = prefix + key; if (key.endsWith("package-manifest.json")) { try { let data; if (value.type === "FileBlob") { data = value.data.toString(); } else { data = await streamToString(value.toStream()); } const packageManifest = JSON.parse(data); const validationError = validatePackageManifest(packageManifest); if (validationError) { output_manager_default.warn( `Invalid package-manifest.json from ${fullKey}: ${validationError}` ); } else { const workspace = service && service.workspace !== "." ? service.workspace : "."; packageManifests.push({ workspace, key: fullKey, manifest: packageManifest, service, builderUse: builderPkg.name }); } } catch (e) { output_manager_default.debug( `Failed to parse ${fullKey}: ${e instanceof Error ? e.message : String(e)}` ); } } else { diagnostics[fullKey] = value; } } } } catch (error) { output_manager_default.error("Collecting diagnostics failed"); output_manager_default.debug(error); } } if (buildResult && "output" in buildResult && "runtime" in buildResult.output && "type" in buildResult.output && buildResult.output.type === "Lambda") { const lambdaRuntime = buildResult.output.runtime; if (getDiscontinuedNodeVersions().some((o) => o.runtime === lambdaRuntime)) { throw new NowBuildError2({ code: "NODEJS_DISCONTINUED_VERSION", message: `The Runtime "${build.use}" is using "${lambdaRuntime}", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.`, link: "https://vercel.link/function-runtimes" }); } } if ("output" in buildResult && buildResult.output && (isBackendBuilder(build) || build.use === "@vercel/python")) { const routesJsonPath = join2(buildWorkPath, ".vercel", "routes.json"); if ((0, import_fs_extra2.existsSync)(routesJsonPath)) { try { const routesJson = await readJSONFile(routesJsonPath); if (routesJson && typeof routesJson === "object" && "routes" in routesJson && Array.isArray(routesJson.routes)) { const indexLambda = "index" in buildResult.output ? buildResult.output["index"] : void 0; const convertedRoutes = []; const convertedOutputs = indexLambda ? { index: indexLambda } : {}; for (const route of routesJson.routes) { if (typeof route.source !== "string") { continue; } const { src } = (0, import_routing_utils2.sourceToRegex)(route.source); const newRoute = { src, dest: route.source }; if (route.methods) { newRoute.methods = route.methods; } if (route.source === "/") { continue; } if (indexLambda) { convertedOutputs[route.source] = indexLambda; } convertedRoutes.push(newRoute); } buildResult.routes = [ { handle: "filesystem" }, ...convertedRoutes, { src: "/(.*)", dest: "/" } ]; if (indexLambda) { buildResult.output = convertedOutputs; } } } catch (error) { output_manager_default.error(`Failed to read routes.json: ${error}`); } } } if (hasDetectedServices && service && "routes" in buildResult && Array.isArray(buildResult.routes) && detectedServices) { buildResult.routes = scopeRoutesToServiceOwnership({ routes: buildResult.routes, owner: service, allServices: detectedServices }); } if (service && isQueueTriggeredService(service) && "output" in buildResult) { attachQueueServiceTrigger(buildResult.output, service); } if (service && isScheduleTriggeredService(service) && !("crons" in buildResult && buildResult.crons?.length)) { const staticSchedules = getStaticServiceSchedules(service.schedule); if (typeof service.runtime === "string" && staticSchedules.length > 0) { const cronEntrypoint = service.entrypoint || service.builder.src || "index"; for (const schedule of staticSchedules) { synthesizedServiceCrons.push({ path: getInternalServiceCronPath( service.name, cronEntrypoint, service.handlerFunction || "cron" ), schedule }); } } else { throw new NowBuildError2({ code: "CRON_SERVICE_NO_CRONS", message: `Scheduled service "${service.name}" did not produce any cron entries. The builder "${builderPkg.name}" may not support scheduled services.` }); } } let mergedBuildResult = buildResult; if ("buildOutputPath" in buildResult) { const buildOutputConfigPath = join2( buildResult.buildOutputPath, "config.json" ); const buildOutputConfig = await readJSONFile( buildOutputConfigPath ); if (buildOutputConfig instanceof CantParseJSONFile) { throw buildOutputConfig; } if (buildOutputConfig) { if (buildOutputConfig.overrides) { overrides.push(buildOutputConfig.overrides); } if (hasDetectedServices && service && Array.isArray(buildOutputConfig.routes) && detectedServices) { buildOutputConfig.routes = scopeRoutesToServiceOwnership({ routes: buildOutputConfig.routes, owner: service, allServices: detectedServices }); } mergedBuildResult = buildOutputConfig; } } buildResults.set(build, mergedBuildResult); let buildOutputLength = 0; if ("output" in buildResult) { buildOutputLength = Array.isArray(buildResult.output) ? buildResult.output.length : 1; } ops.push( builderSpan.child("vc.builder.writeBuildResult", { buildOutputLength: String(buildOutputLength) }).trace( () => writeBuildResult({ repoRootPath, outputDir, buildResult: rawBuildResult, build, builder, builderPkg, vercelConfig: localConfig, standalone, workPath: buildWorkPath, service, stripServiceRoutePrefix }) ).then( (override) => { if (override) overrides.push(override); }, (err) => err ) ); } catch (err) { const buildJsonBuild = buildsJsonBuilds.get(build); if (buildJsonBuild) { buildJsonBuild.error = toEnumerableError(err); } throw err; } finally { ops.push( download(diagnostics, join2(outputDir, "diagnostics")).then( () => void 0, (err) => err ) ); } } for (const entry of preDeployEntries) { if (entry.callback) { await entry.callback(); } else { output_manager_default.warn( `Service "${entry.service}" has a preDeployCommand but its builder does not support it. The command was not executed.` ); } } if (packageManifests.length > 0) { const projectManifest = {}; for (const { workspace, manifest, service, builderUse } of packageManifests) { projectManifest[`${builderUse}:${workspace}`] = { ...manifest, workspace, builder: builderUse, framework: service?.framework, serviceName: service?.name, serviceType: service?.type, routePrefix: service?.routePrefix }; } if (Object.keys(projectManifest).length > 0) { const projectManifestBlob = new FileBlob({ data: JSON.stringify(projectManifest) }); diagnostics["project-manifest.json"] = projectManifestBlob; ops.push( downloadFile( projectManifestBlob, join2(outputDir, "diagnostics", "project-manifest.json") ).then( () => void 0, (err) => err ) ); } } if (corepackShimDir) { cleanupCorepack(corepackShimDir); } const collectSpan = span.child("vc.finalizeBuildOutput"); const errors = await Promise.all(ops); for (const error of errors) { if (error) { throw error; } } let needBuildsJsonOverride = false; const speedInsightsVersion = await getInstalledPackageVersion( "@vercel/speed-insights" ); if (speedInsightsVersion) { buildsJson.features = { ...buildsJson.features ?? {}, speedInsightsVersion }; needBuildsJsonOverride = true; } const webAnalyticsVersion = await getInstalledPackageVersion("@vercel/analytics"); if (webAnalyticsVersion) { buildsJson.features = { ...buildsJson.features ?? {}, webAnalyticsVersion }; needBuildsJsonOverride = true; } if (needBuildsJsonOverride) { await writeBuildJson(buildsJson, outputDir); } const configPath = join2(outputDir, "config.json"); const existingConfig = await readJSONFile(configPath); if (existingConfig instanceof CantParseJSONFile) { throw existingConfig; } if (existingConfig) { if ("deploymentId" in existingConfig && typeof existingConfig.deploymentId === "string") { const deploymentId = existingConfig.deploymentId; if (deploymentId.length > 32) { throw new NowBuildError2({ code: "INVALID_DEPLOYMENT_ID", message: `The deploymentId "${deploymentId}" must be 32 characters or less. Please choose a shorter deploymentId in your config.`, link: "https://vercel.com/docs/skew-protection#custom-skew-protection-deployment-id" }); } if (!VALID_