@now/build-utils
Version:
306 lines (305 loc) • 12.1 kB
JavaScript
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.');
;