UNPKG

@now/build-utils

Version:
306 lines (305 loc) 12.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.installDependencies = exports.runPackageJsonScript = exports.getScriptName = exports.runPipInstall = exports.runBundleInstall = exports.runNpmInstall = exports.walkParentDirs = exports.getNodeVersion = exports.getSpawnOptions = exports.runShellScript = exports.getNodeBinPath = exports.execCommand = exports.spawnCommand = exports.execAsync = exports.spawnAsync = void 0; const assert_1 = __importDefault(require("assert")); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const debug_1 = __importDefault(require("../debug")); const cross_spawn_1 = __importDefault(require("cross-spawn")); const util_1 = require("util"); const os_1 = require("os"); const errors_1 = require("../errors"); const node_version_1 = require("./node-version"); function spawnAsync(command, args, opts = {}) { return new Promise((resolve, reject) => { const stderrLogs = []; opts = { stdio: 'inherit', ...opts }; const child = cross_spawn_1.default(command, args, opts); if (opts.stdio === 'pipe' && child.stderr) { child.stderr.on('data', data => stderrLogs.push(data)); } child.on('error', reject); child.on('close', (code, signal) => { if (code === 0) { return resolve(); } const cmd = opts.prettyCommand ? `Command "${opts.prettyCommand}"` : 'Command'; reject(new errors_1.NowBuildError({ code: `BUILD_UTILS_SPAWN_${code || signal}`, message: opts.stdio === 'inherit' ? `${cmd} exited with ${code || signal}` : stderrLogs.map(line => line.toString()).join(''), })); }); }); } exports.spawnAsync = spawnAsync; function execAsync(command, args, opts = {}) { return new Promise((resolve, reject) => { opts.stdio = 'pipe'; const stdoutList = []; const stderrList = []; const child = cross_spawn_1.default(command, args, opts); child.stderr.on('data', data => { stderrList.push(data); }); child.stdout.on('data', data => { stdoutList.push(data); }); child.on('error', reject); child.on('close', (code, signal) => { if (code !== 0) { const cmd = opts.prettyCommand ? `Command "${opts.prettyCommand}"` : 'Command'; return reject(new errors_1.NowBuildError({ code: `BUILD_UTILS_EXEC_${code || signal}`, message: `${cmd} exited with ${code || signal}`, })); } return resolve({ code, stdout: Buffer.concat(stdoutList).toString(), stderr: Buffer.concat(stderrList).toString(), }); }); }); } exports.execAsync = execAsync; function spawnCommand(command, options = {}) { const opts = { ...options, prettyCommand: command }; if (process.platform === 'win32') { return cross_spawn_1.default('cmd.exe', ['/C', command], opts); } return cross_spawn_1.default('sh', ['-c', command], opts); } exports.spawnCommand = spawnCommand; async function execCommand(command, options = {}) { const opts = { ...options, prettyCommand: command }; if (process.platform === 'win32') { await spawnAsync('cmd.exe', ['/C', command], opts); } else { await spawnAsync('sh', ['-c', command], opts); } return true; } exports.execCommand = execCommand; async function getNodeBinPath({ cwd }) { const { stdout } = await execAsync('npm', ['bin'], { cwd }); return stdout.trim(); } exports.getNodeBinPath = getNodeBinPath; async function chmodPlusX(fsPath) { const s = await fs_extra_1.default.stat(fsPath); const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise if (s.mode === newMode) return; const base8 = newMode.toString(8).slice(-3); await fs_extra_1.default.chmod(fsPath, base8); } async function runShellScript(fsPath, args = [], spawnOpts) { assert_1.default(path_1.default.isAbsolute(fsPath)); const destPath = path_1.default.dirname(fsPath); await chmodPlusX(fsPath); const command = `./${path_1.default.basename(fsPath)}`; await spawnAsync(command, args, { ...spawnOpts, cwd: destPath, prettyCommand: command, }); return true; } exports.runShellScript = runShellScript; function getSpawnOptions(meta, nodeVersion) { const opts = { env: { ...process.env }, }; if (!meta.isDev) { opts.env.PATH = `/node${nodeVersion.major}/bin:${opts.env.PATH}`; } return opts; } exports.getSpawnOptions = getSpawnOptions; async function getNodeVersion(destPath, _nodeVersion, _config, meta) { if (meta && meta.isDev) { // Use the system-installed version of `node` in PATH for `vercel dev` const latest = node_version_1.getLatestNodeVersion(); return { ...latest, runtime: 'nodejs' }; } const { packageJson } = await scanParentDirs(destPath, true); let range; let isAuto = true; if (packageJson && packageJson.engines && packageJson.engines.node) { range = packageJson.engines.node; isAuto = false; } return node_version_1.getSupportedNodeVersion(range, isAuto); } exports.getNodeVersion = getNodeVersion; async function scanParentDirs(destPath, readPackageJson = false) { assert_1.default(path_1.default.isAbsolute(destPath)); let cliType = 'yarn'; let packageJson; let currentDestPath = destPath; // eslint-disable-next-line no-constant-condition while (true) { const packageJsonPath = path_1.default.join(currentDestPath, 'package.json'); // eslint-disable-next-line no-await-in-loop if (await fs_extra_1.default.pathExists(packageJsonPath)) { // eslint-disable-next-line no-await-in-loop if (readPackageJson) { packageJson = JSON.parse(await fs_extra_1.default.readFile(packageJsonPath, 'utf8')); } // eslint-disable-next-line no-await-in-loop const [hasPackageLockJson, hasYarnLock] = await Promise.all([ fs_extra_1.default.pathExists(path_1.default.join(currentDestPath, 'package-lock.json')), fs_extra_1.default.pathExists(path_1.default.join(currentDestPath, 'yarn.lock')), ]); if (hasPackageLockJson && !hasYarnLock) { cliType = 'npm'; } break; } const newDestPath = path_1.default.dirname(currentDestPath); if (currentDestPath === newDestPath) break; currentDestPath = newDestPath; } return { cliType, packageJson }; } async function walkParentDirs({ base, start, filename, }) { assert_1.default(path_1.default.isAbsolute(base), 'Expected "base" to be absolute path'); assert_1.default(path_1.default.isAbsolute(start), 'Expected "start" to be absolute path'); let parent = ''; for (let current = start; base.length <= current.length; current = parent) { const fullPath = path_1.default.join(current, filename); // eslint-disable-next-line no-await-in-loop if (await fs_extra_1.default.pathExists(fullPath)) { return fullPath; } parent = path_1.default.dirname(current); } return null; } exports.walkParentDirs = walkParentDirs; async function runNpmInstall(destPath, args = [], spawnOpts, meta) { if (meta && meta.isDev) { debug_1.default('Skipping dependency installation because dev mode is enabled'); return; } assert_1.default(path_1.default.isAbsolute(destPath)); debug_1.default(`Installing to ${destPath}`); const { cliType } = await scanParentDirs(destPath); const opts = { cwd: destPath, ...spawnOpts }; const env = opts.env ? { ...opts.env } : { ...process.env }; delete env.NODE_ENV; opts.env = env; let command; let commandArgs; if (cliType === 'npm') { opts.prettyCommand = 'npm install'; command = 'npm'; commandArgs = args .filter(a => a !== '--prefer-offline') .concat(['install', '--no-audit', '--unsafe-perm']); } else { opts.prettyCommand = 'yarn install'; command = 'yarn'; commandArgs = ['install', ...args]; // Yarn v2 PnP mode may be activated, so force "node-modules" linker style if (!env.YARN_NODE_LINKER) { env.YARN_NODE_LINKER = 'node-modules'; } } if (process.env.NPM_ONLY_PRODUCTION) { commandArgs.push('--production'); } await spawnAsync(command, commandArgs, opts); } exports.runNpmInstall = runNpmInstall; async function runBundleInstall(destPath, args = [], spawnOpts, meta) { if (meta && meta.isDev) { debug_1.default('Skipping dependency installation because dev mode is enabled'); return; } assert_1.default(path_1.default.isAbsolute(destPath)); const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'bundle install' }; await spawnAsync('bundle', args.concat([ 'install', '--no-prune', '--retry', '3', '--jobs', String(os_1.cpus().length || 1), ]), opts); } exports.runBundleInstall = runBundleInstall; async function runPipInstall(destPath, args = [], spawnOpts, meta) { if (meta && meta.isDev) { debug_1.default('Skipping dependency installation because dev mode is enabled'); return; } assert_1.default(path_1.default.isAbsolute(destPath)); const opts = { ...spawnOpts, cwd: destPath, prettyCommand: 'pip3 install' }; await spawnAsync('pip3', ['install', '--disable-pip-version-check', ...args], opts); } exports.runPipInstall = runPipInstall; function getScriptName(pkg, possibleNames) { if (pkg && pkg.scripts) { for (const name of possibleNames) { if (name in pkg.scripts) { return name; } } } return null; } exports.getScriptName = getScriptName; async function runPackageJsonScript(destPath, scriptNames, spawnOpts) { assert_1.default(path_1.default.isAbsolute(destPath)); const { packageJson, cliType } = await scanParentDirs(destPath, true); const scriptName = getScriptName(packageJson, typeof scriptNames === 'string' ? [scriptNames] : scriptNames); if (!scriptName) return false; debug_1.default('Running user script...'); const runScriptTime = Date.now(); if (cliType === 'npm') { const prettyCommand = `npm run ${scriptName}`; console.log(`Running "${prettyCommand}"`); await spawnAsync('npm', ['run', scriptName], { ...spawnOpts, cwd: destPath, prettyCommand, }); } else { // Yarn v2 PnP mode may be activated, so force "node-modules" linker style const env = { ...spawnOpts === null || spawnOpts === void 0 ? void 0 : spawnOpts.env }; if (!env.YARN_NODE_LINKER) { env.YARN_NODE_LINKER = 'node-modules'; } const prettyCommand = `yarn run ${scriptName}`; console.log(`Running "${prettyCommand}"`); await spawnAsync('yarn', ['run', scriptName], { ...spawnOpts, env, cwd: destPath, prettyCommand, }); } debug_1.default(`Script complete [${Date.now() - runScriptTime}ms]`); return true; } exports.runPackageJsonScript = runPackageJsonScript; /** * @deprecate installDependencies() is deprecated. * Please use runNpmInstall() instead. */ exports.installDependencies = util_1.deprecate(runNpmInstall, 'installDependencies() is deprecated. Please use runNpmInstall() instead.');