run-script-cli
Version:
<p align="center"> <a href="https://www.npmjs.com/package/run-script-cli" target="_blank" rel="noopener noreferrer"> <img src="https://github.com/hunghg255/run-script-cli/blob/main/assets/icon.png?raw=true" alt="logo" width='100'/></a> </p>
1,865 lines (1,613 loc) • 123 kB
JavaScript
import path$3, { resolve, dirname, basename } from 'node:path';
import { existsSync, readFileSync, createWriteStream, createReadStream } from 'node:fs';
import { promisify, debuglog } from 'node:util';
import childProcess, { exec as exec$1, ChildProcess } from 'node:child_process';
import { Buffer as Buffer$1 } from 'node:buffer';
import process$2, { stdin, stdout } from 'node:process';
import require$$0$2 from 'child_process';
import require$$0$1 from 'path';
import require$$0 from 'fs';
import url from 'node:url';
import os, { constants } from 'node:os';
import { setTimeout as setTimeout$1 } from 'node:timers/promises';
import require$$0$3 from 'stream';
import ot from 'tty';
import S from 'node:readline';
import { WriteStream } from 'node:tty';
import 'events';
/**
* Promisified version of child_process {@link exec}.
*/
promisify(exec$1);
/**
* Executes a callback function with the current directory path as first argument. If the callback function returns a value other than `undefined` then the search is stopped and the value is returned. Otherwise it starts again with the parent directory.
*
* @example
* ```
* // Find the nearest package.json from current working directory.
* const packageJSON = walkDirectoryUp(directory => {
* const path = resolve(directory, 'package.json');
* return existsSync(path) ? path : undefined;
* });
* ```
*
* @param callback a callback function to be executed on each directory.
* @param directory the directory where the search begins, default to current working directory.
*/
async function walkDirectoryUp(callback, directory = process.cwd()) {
const currentDirectory = resolve(directory);
const result = await callback(currentDirectory);
if (result !== undefined) {
return result;
}
const parentDirectory = dirname(currentDirectory);
if (parentDirectory === currentDirectory) {
return;
}
return walkDirectoryUp(callback, parentDirectory);
}
/**
* Returns the absolute path to the first file found in the directory provided or `undefined`.
*
* @param fileName file name or array of file name to search.
* @param directory the directory where the search begins, default to current working directory.
*/
async function findFile(fileName, directory = process.cwd()) {
if (typeof fileName === 'string') {
const path = resolve(directory, fileName);
return existsSync(path) ? path : undefined;
}
for (const name of fileName) {
const file = await findFile(name, directory);
if (file) {
return file;
}
}
return;
}
/**
* Try to find the used agent from a directory with the following rules:
*
* - Try to find the agent in `packageManager` field from `package.json`.
* - If not found, try to find the agent from lock file name.
* - If not found, stats again in parent directory.
*
* @param directory If no directory is provided, the current working directory will be used.
*/
function detectAgent(directory) {
return walkDirectoryUp(async (directory) => {
let agent = await detectAgentFromPackageJSON(directory);
if (!agent) {
agent = await detectAgentFromLockFile(directory);
}
return agent;
}, directory);
}
/**
* Try to find the used agent defined in `packageManager` field from `package.json`.
*
* @param directory If no directory is provided, the current working directory will be used.
*/
async function detectAgentFromPackageJSON(directory = process.cwd()) {
const packageJSONPath = await findFile('package.json', directory);
if (!packageJSONPath) {
return;
}
const packageJSONContent = readFileSync(packageJSONPath, 'utf8');
const { packageManager } = JSON.parse(packageJSONContent);
if (!packageManager) {
return;
}
const [name, version] = packageManager.split('@');
return { name, version };
}
const lockFiles = [
'pnpm-lock.yaml',
'yarn.lock',
'package-lock.json',
'bun.lockb',
'shrinkwrap.yaml',
'npm-shrinkwrap.json',
];
function detectNPMVersionFromLockFile(path) {
// https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json#lockfileversion
const { lockfileVersion } = JSON.parse(readFileSync(path, 'utf8'));
switch (lockfileVersion) {
case 1:
return { name: 'npm', version: '5 - 6' };
case 2:
return { name: 'npm', version: '>=5' };
case 3:
return { name: 'npm', version: '>=7' };
default:
return { name: 'npm', version: '<5' };
}
}
const pnpmLockFileVersionToVersion = {
'5.4': '>=7.0.0',
'5.3': '>=6.0.0',
'5.2': '>=5.10.0',
'5.1': '>=3.5.0',
'5.0': '>=3.0.0',
'4.0': '>=2.17.0',
'3.9': '>=2.13.3',
'3.8': '>=2.8.0',
'3.7': '>=2.0.0',
'3.6': '>=1.43.0',
'3.5': '>=1.40.0',
'3.4': '>=1.23.0',
'3.3': '>=1.22.0',
'3.2': '>=1.18.1',
'3.1': '1.17 - 1.18.0',
'3': '1 - 1.16',
'2': '0.62 - 0',
};
function detectPNPMVersionFromLockFile(path) {
var _a;
// https://github.com/pnpm/spec/tree/master/lockfile
const content = readFileSync(path, 'utf8');
const [, lockfileVersion] = (_a = content.match(/^lockfileVersion: (\d(\.\d)?)$/m)) !== null && _a !== void 0 ? _a : [];
if (!lockfileVersion) {
return { name: 'pnpm', version: '<0.62' };
}
const version = pnpmLockFileVersionToVersion[lockfileVersion];
return { name: 'pnpm', version: version !== null && version !== void 0 ? version : Object.values(pnpmLockFileVersionToVersion)[0] };
}
const lockFileVersionDetector = {
'pnpm-lock.yaml': detectPNPMVersionFromLockFile,
'shrinkwrap.yaml': detectPNPMVersionFromLockFile,
'package-lock.json': detectNPMVersionFromLockFile,
'npm-shrinkwrap.json': detectNPMVersionFromLockFile,
'bun.lockb': () => ({ name: 'bun', version: '>=0' }),
'yarn.lock': (path) => {
// https://github.com/yarnpkg/berry/blob/635ed55d7582fe6ee1af4c4b2e033f0fdc33fd2f/packages/yarnpkg-core/sources/scriptUtils.ts#L87
const version = /^__metadata:$/m.test(readFileSync(path, 'utf8')) ? '2' : '1';
return { name: 'yarn', version };
},
};
/**
* Try to find the used agent from lock file name and/or content.
*
* @param directory If no directory is provided, the current working directory will be used.
*/
async function detectAgentFromLockFile(directory = process.cwd()) {
const lockFilePath = await findFile(lockFiles, directory);
if (!lockFilePath) {
return;
}
return lockFileVersionDetector[basename(lockFilePath)](lockFilePath);
}
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var crossSpawn$1 = {exports: {}};
var windows;
var hasRequiredWindows;
function requireWindows () {
if (hasRequiredWindows) return windows;
hasRequiredWindows = 1;
windows = isexe;
isexe.sync = sync;
var fs = require$$0;
function checkPathExt (path, options) {
var pathext = options.pathExt !== undefined ?
options.pathExt : process.env.PATHEXT;
if (!pathext) {
return true
}
pathext = pathext.split(';');
if (pathext.indexOf('') !== -1) {
return true
}
for (var i = 0; i < pathext.length; i++) {
var p = pathext[i].toLowerCase();
if (p && path.substr(-p.length).toLowerCase() === p) {
return true
}
}
return false
}
function checkStat (stat, path, options) {
if (!stat.isSymbolicLink() && !stat.isFile()) {
return false
}
return checkPathExt(path, options)
}
function isexe (path, options, cb) {
fs.stat(path, function (er, stat) {
cb(er, er ? false : checkStat(stat, path, options));
});
}
function sync (path, options) {
return checkStat(fs.statSync(path), path, options)
}
return windows;
}
var mode;
var hasRequiredMode;
function requireMode () {
if (hasRequiredMode) return mode;
hasRequiredMode = 1;
mode = isexe;
isexe.sync = sync;
var fs = require$$0;
function isexe (path, options, cb) {
fs.stat(path, function (er, stat) {
cb(er, er ? false : checkStat(stat, options));
});
}
function sync (path, options) {
return checkStat(fs.statSync(path), options)
}
function checkStat (stat, options) {
return stat.isFile() && checkMode(stat, options)
}
function checkMode (stat, options) {
var mod = stat.mode;
var uid = stat.uid;
var gid = stat.gid;
var myUid = options.uid !== undefined ?
options.uid : process.getuid && process.getuid();
var myGid = options.gid !== undefined ?
options.gid : process.getgid && process.getgid();
var u = parseInt('100', 8);
var g = parseInt('010', 8);
var o = parseInt('001', 8);
var ug = u | g;
var ret = (mod & o) ||
(mod & g) && gid === myGid ||
(mod & u) && uid === myUid ||
(mod & ug) && myUid === 0;
return ret
}
return mode;
}
var core;
if (process.platform === 'win32' || commonjsGlobal.TESTING_WINDOWS) {
core = requireWindows();
} else {
core = requireMode();
}
var isexe_1 = isexe$1;
isexe$1.sync = sync;
function isexe$1 (path, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
if (!cb) {
if (typeof Promise !== 'function') {
throw new TypeError('callback not provided')
}
return new Promise(function (resolve, reject) {
isexe$1(path, options || {}, function (er, is) {
if (er) {
reject(er);
} else {
resolve(is);
}
});
})
}
core(path, options || {}, function (er, is) {
// ignore EACCES because that just means we aren't allowed to run it
if (er) {
if (er.code === 'EACCES' || options && options.ignoreErrors) {
er = null;
is = false;
}
}
cb(er, is);
});
}
function sync (path, options) {
// my kingdom for a filtered catch
try {
return core.sync(path, options || {})
} catch (er) {
if (options && options.ignoreErrors || er.code === 'EACCES') {
return false
} else {
throw er
}
}
}
const isWindows = process.platform === 'win32' ||
process.env.OSTYPE === 'cygwin' ||
process.env.OSTYPE === 'msys';
const path$2 = require$$0$1;
const COLON = isWindows ? ';' : ':';
const isexe = isexe_1;
const getNotFoundError = (cmd) =>
Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' });
const getPathInfo = (cmd, opt) => {
const colon = opt.colon || COLON;
// If it has a slash, then we don't bother searching the pathenv.
// just check the file itself, and that's it.
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? ['']
: (
[
// windows always checks the cwd first
...(isWindows ? [process.cwd()] : []),
...(opt.path || process.env.PATH ||
/* istanbul ignore next: very unusual */ '').split(colon),
]
);
const pathExtExe = isWindows
? opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM'
: '';
const pathExt = isWindows ? pathExtExe.split(colon) : [''];
if (isWindows) {
if (cmd.indexOf('.') !== -1 && pathExt[0] !== '')
pathExt.unshift('');
}
return {
pathEnv,
pathExt,
pathExtExe,
}
};
const which$1 = (cmd, opt, cb) => {
if (typeof opt === 'function') {
cb = opt;
opt = {};
}
if (!opt)
opt = {};
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
const found = [];
const step = i => new Promise((resolve, reject) => {
if (i === pathEnv.length)
return opt.all && found.length ? resolve(found)
: reject(getNotFoundError(cmd))
const ppRaw = pathEnv[i];
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
const pCmd = path$2.join(pathPart, cmd);
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd
: pCmd;
resolve(subStep(p, i, 0));
});
const subStep = (p, i, ii) => new Promise((resolve, reject) => {
if (ii === pathExt.length)
return resolve(step(i + 1))
const ext = pathExt[ii];
isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
if (!er && is) {
if (opt.all)
found.push(p + ext);
else
return resolve(p + ext)
}
return resolve(subStep(p, i, ii + 1))
});
});
return cb ? step(0).then(res => cb(null, res), cb) : step(0)
};
const whichSync = (cmd, opt) => {
opt = opt || {};
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
const found = [];
for (let i = 0; i < pathEnv.length; i ++) {
const ppRaw = pathEnv[i];
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
const pCmd = path$2.join(pathPart, cmd);
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd
: pCmd;
for (let j = 0; j < pathExt.length; j ++) {
const cur = p + pathExt[j];
try {
const is = isexe.sync(cur, { pathExt: pathExtExe });
if (is) {
if (opt.all)
found.push(cur);
else
return cur
}
} catch (ex) {}
}
}
if (opt.all && found.length)
return found
if (opt.nothrow)
return null
throw getNotFoundError(cmd)
};
var which_1 = which$1;
which$1.sync = whichSync;
var pathKey$2 = {exports: {}};
const pathKey$1 = (options = {}) => {
const environment = options.env || process.env;
const platform = options.platform || process.platform;
if (platform !== 'win32') {
return 'PATH';
}
return Object.keys(environment).reverse().find(key => key.toUpperCase() === 'PATH') || 'Path';
};
pathKey$2.exports = pathKey$1;
// TODO: Remove this for the next major release
pathKey$2.exports.default = pathKey$1;
var pathKeyExports = pathKey$2.exports;
const path$1 = require$$0$1;
const which = which_1;
const getPathKey = pathKeyExports;
function resolveCommandAttempt(parsed, withoutPathExt) {
const env = parsed.options.env || process.env;
const cwd = process.cwd();
const hasCustomCwd = parsed.options.cwd != null;
// Worker threads do not have process.chdir()
const shouldSwitchCwd = hasCustomCwd && process.chdir !== undefined && !process.chdir.disabled;
// If a custom `cwd` was specified, we need to change the process cwd
// because `which` will do stat calls but does not support a custom cwd
if (shouldSwitchCwd) {
try {
process.chdir(parsed.options.cwd);
} catch (err) {
/* Empty */
}
}
let resolved;
try {
resolved = which.sync(parsed.command, {
path: env[getPathKey({ env })],
pathExt: withoutPathExt ? path$1.delimiter : undefined,
});
} catch (e) {
/* Empty */
} finally {
if (shouldSwitchCwd) {
process.chdir(cwd);
}
}
// If we successfully resolved, ensure that an absolute path is returned
// Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it
if (resolved) {
resolved = path$1.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved);
}
return resolved;
}
function resolveCommand$1(parsed) {
return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
}
var resolveCommand_1 = resolveCommand$1;
var _escape = {};
// See http://www.robvanderwoude.com/escapechars.php
const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
function escapeCommand(arg) {
// Escape meta chars
arg = arg.replace(metaCharsRegExp, '^$1');
return arg;
}
function escapeArgument(arg, doubleEscapeMetaChars) {
// Convert to string
arg = `${arg}`;
// Algorithm below is based on https://qntm.org/cmd
// Sequence of backslashes followed by a double quote:
// double up all the backslashes and escape the double quote
arg = arg.replace(/(\\*)"/g, '$1$1\\"');
// Sequence of backslashes followed by the end of the string
// (which will become a double quote later):
// double up all the backslashes
arg = arg.replace(/(\\*)$/, '$1$1');
// All other backslashes occur literally
// Quote the whole thing:
arg = `"${arg}"`;
// Escape meta chars
arg = arg.replace(metaCharsRegExp, '^$1');
// Double escape meta chars if necessary
if (doubleEscapeMetaChars) {
arg = arg.replace(metaCharsRegExp, '^$1');
}
return arg;
}
_escape.command = escapeCommand;
_escape.argument = escapeArgument;
var shebangRegex$1 = /^#!(.*)/;
const shebangRegex = shebangRegex$1;
var shebangCommand$1 = (string = '') => {
const match = string.match(shebangRegex);
if (!match) {
return null;
}
const [path, argument] = match[0].replace(/#! ?/, '').split(' ');
const binary = path.split('/').pop();
if (binary === 'env') {
return argument;
}
return argument ? `${binary} ${argument}` : binary;
};
const fs = require$$0;
const shebangCommand = shebangCommand$1;
function readShebang$1(command) {
// Read the first 150 bytes from the file
const size = 150;
const buffer = Buffer.alloc(size);
let fd;
try {
fd = fs.openSync(command, 'r');
fs.readSync(fd, buffer, 0, size, 0);
fs.closeSync(fd);
} catch (e) { /* Empty */ }
// Attempt to extract shebang (null is returned if not a shebang)
return shebangCommand(buffer.toString());
}
var readShebang_1 = readShebang$1;
const path = require$$0$1;
const resolveCommand = resolveCommand_1;
const escape = _escape;
const readShebang = readShebang_1;
const isWin$1 = process.platform === 'win32';
const isExecutableRegExp = /\.(?:com|exe)$/i;
const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
function detectShebang(parsed) {
parsed.file = resolveCommand(parsed);
const shebang = parsed.file && readShebang(parsed.file);
if (shebang) {
parsed.args.unshift(parsed.file);
parsed.command = shebang;
return resolveCommand(parsed);
}
return parsed.file;
}
function parseNonShell(parsed) {
if (!isWin$1) {
return parsed;
}
// Detect & add support for shebangs
const commandFile = detectShebang(parsed);
// We don't need a shell if the command filename is an executable
const needsShell = !isExecutableRegExp.test(commandFile);
// If a shell is required, use cmd.exe and take care of escaping everything correctly
// Note that `forceShell` is an hidden option used only in tests
if (parsed.options.forceShell || needsShell) {
// Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
// The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
// Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
// we need to double escape them
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
// Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
// This is necessary otherwise it will always fail with ENOENT in those cases
parsed.command = path.normalize(parsed.command);
// Escape command & arguments
parsed.command = escape.command(parsed.command);
parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
const shellCommand = [parsed.command].concat(parsed.args).join(' ');
parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
parsed.command = process.env.comspec || 'cmd.exe';
parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
}
return parsed;
}
function parse$1(command, args, options) {
// Normalize arguments, similar to nodejs
if (args && !Array.isArray(args)) {
options = args;
args = null;
}
args = args ? args.slice(0) : []; // Clone array to avoid changing the original
options = Object.assign({}, options); // Clone object to avoid changing the original
// Build our parsed object
const parsed = {
command,
args,
options,
file: undefined,
original: {
command,
args,
},
};
// Delegate further parsing to shell or non-shell
return options.shell ? parsed : parseNonShell(parsed);
}
var parse_1 = parse$1;
const isWin = process.platform === 'win32';
function notFoundError(original, syscall) {
return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
code: 'ENOENT',
errno: 'ENOENT',
syscall: `${syscall} ${original.command}`,
path: original.command,
spawnargs: original.args,
});
}
function hookChildProcess(cp, parsed) {
if (!isWin) {
return;
}
const originalEmit = cp.emit;
cp.emit = function (name, arg1) {
// If emitting "exit" event and exit code is 1, we need to check if
// the command exists and emit an "error" instead
// See https://github.com/IndigoUnited/node-cross-spawn/issues/16
if (name === 'exit') {
const err = verifyENOENT(arg1, parsed);
if (err) {
return originalEmit.call(cp, 'error', err);
}
}
return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params
};
}
function verifyENOENT(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawn');
}
return null;
}
function verifyENOENTSync(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawnSync');
}
return null;
}
var enoent$1 = {
hookChildProcess,
verifyENOENT,
verifyENOENTSync,
notFoundError,
};
const cp = require$$0$2;
const parse = parse_1;
const enoent = enoent$1;
function spawn(command, args, options) {
// Parse the arguments
const parsed = parse(command, args, options);
// Spawn the child process
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
// Hook into child process "exit" event to emit an error if the command
// does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
enoent.hookChildProcess(spawned, parsed);
return spawned;
}
function spawnSync(command, args, options) {
// Parse the arguments
const parsed = parse(command, args, options);
// Spawn the child process
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
// Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
return result;
}
crossSpawn$1.exports = spawn;
crossSpawn$1.exports.spawn = spawn;
crossSpawn$1.exports.sync = spawnSync;
crossSpawn$1.exports._parse = parse;
crossSpawn$1.exports._enoent = enoent;
var crossSpawnExports = crossSpawn$1.exports;
const crossSpawn = /*@__PURE__*/getDefaultExportFromCjs(crossSpawnExports);
function stripFinalNewline(input) {
const LF = typeof input === 'string' ? '\n' : '\n'.charCodeAt();
const CR = typeof input === 'string' ? '\r' : '\r'.charCodeAt();
if (input[input.length - 1] === LF) {
input = input.slice(0, -1);
}
if (input[input.length - 1] === CR) {
input = input.slice(0, -1);
}
return input;
}
function pathKey(options = {}) {
const {
env = process.env,
platform = process.platform
} = options;
if (platform !== 'win32') {
return 'PATH';
}
return Object.keys(env).reverse().find(key => key.toUpperCase() === 'PATH') || 'Path';
}
function npmRunPath(options = {}) {
const {
cwd = process$2.cwd(),
path: path_ = process$2.env[pathKey()],
execPath = process$2.execPath,
} = options;
let previous;
const execPathString = execPath instanceof URL ? url.fileURLToPath(execPath) : execPath;
const cwdString = cwd instanceof URL ? url.fileURLToPath(cwd) : cwd;
let cwdPath = path$3.resolve(cwdString);
const result = [];
while (previous !== cwdPath) {
result.push(path$3.join(cwdPath, 'node_modules/.bin'));
previous = cwdPath;
cwdPath = path$3.resolve(cwdPath, '..');
}
// Ensure the running `node` binary is used.
result.push(path$3.resolve(cwdString, execPathString, '..'));
return [...result, path_].join(path$3.delimiter);
}
function npmRunPathEnv({env = process$2.env, ...options} = {}) {
env = {...env};
const path = pathKey({env});
options.path = env[path];
env[path] = npmRunPath(options);
return env;
}
const copyProperty = (to, from, property, ignoreNonConfigurable) => {
// `Function#length` should reflect the parameters of `to` not `from` since we keep its body.
// `Function#prototype` is non-writable and non-configurable so can never be modified.
if (property === 'length' || property === 'prototype') {
return;
}
// `Function#arguments` and `Function#caller` should not be copied. They were reported to be present in `Reflect.ownKeys` for some devices in React Native (#41), so we explicitly ignore them here.
if (property === 'arguments' || property === 'caller') {
return;
}
const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);
if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
return;
}
Object.defineProperty(to, property, fromDescriptor);
};
// `Object.defineProperty()` throws if the property exists, is not configurable and either:
// - one its descriptors is changed
// - it is non-writable and its value is changed
const canCopyProperty = function (toDescriptor, fromDescriptor) {
return toDescriptor === undefined || toDescriptor.configurable || (
toDescriptor.writable === fromDescriptor.writable &&
toDescriptor.enumerable === fromDescriptor.enumerable &&
toDescriptor.configurable === fromDescriptor.configurable &&
(toDescriptor.writable || toDescriptor.value === fromDescriptor.value)
);
};
const changePrototype = (to, from) => {
const fromPrototype = Object.getPrototypeOf(from);
if (fromPrototype === Object.getPrototypeOf(to)) {
return;
}
Object.setPrototypeOf(to, fromPrototype);
};
const wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/\n${fromBody}`;
const toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, 'toString');
const toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name');
// We call `from.toString()` early (not lazily) to ensure `from` can be garbage collected.
// We use `bind()` instead of a closure for the same reason.
// Calling `from.toString()` early also allows caching it in case `to.toString()` is called several times.
const changeToString = (to, from, name) => {
const withName = name === '' ? '' : `with ${name.trim()}() `;
const newToString = wrappedToString.bind(null, withName, from.toString());
// Ensure `to.toString.toString` is non-enumerable and has the same `same`
Object.defineProperty(newToString, 'name', toStringName);
Object.defineProperty(to, 'toString', {...toStringDescriptor, value: newToString});
};
function mimicFunction(to, from, {ignoreNonConfigurable = false} = {}) {
const {name} = to;
for (const property of Reflect.ownKeys(from)) {
copyProperty(to, from, property, ignoreNonConfigurable);
}
changePrototype(to, from);
changeToString(to, from, name);
return to;
}
const calledFunctions = new WeakMap();
const onetime = (function_, options = {}) => {
if (typeof function_ !== 'function') {
throw new TypeError('Expected a function');
}
let returnValue;
let callCount = 0;
const functionName = function_.displayName || function_.name || '<anonymous>';
const onetime = function (...arguments_) {
calledFunctions.set(onetime, ++callCount);
if (callCount === 1) {
returnValue = function_.apply(this, arguments_);
function_ = null;
} else if (options.throw === true) {
throw new Error(`Function \`${functionName}\` can only be called once`);
}
return returnValue;
};
mimicFunction(onetime, function_);
calledFunctions.set(onetime, callCount);
return onetime;
};
onetime.callCount = function_ => {
if (!calledFunctions.has(function_)) {
throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`);
}
return calledFunctions.get(function_);
};
const getRealtimeSignals=()=>{
const length=SIGRTMAX-SIGRTMIN+1;
return Array.from({length},getRealtimeSignal)
};
const getRealtimeSignal=(value,index)=>({
name:`SIGRT${index+1}`,
number:SIGRTMIN+index,
action:"terminate",
description:"Application-specific signal (realtime)",
standard:"posix"
});
const SIGRTMIN=34;
const SIGRTMAX=64;
const SIGNALS=[
{
name:"SIGHUP",
number:1,
action:"terminate",
description:"Terminal closed",
standard:"posix"
},
{
name:"SIGINT",
number:2,
action:"terminate",
description:"User interruption with CTRL-C",
standard:"ansi"
},
{
name:"SIGQUIT",
number:3,
action:"core",
description:"User interruption with CTRL-\\",
standard:"posix"
},
{
name:"SIGILL",
number:4,
action:"core",
description:"Invalid machine instruction",
standard:"ansi"
},
{
name:"SIGTRAP",
number:5,
action:"core",
description:"Debugger breakpoint",
standard:"posix"
},
{
name:"SIGABRT",
number:6,
action:"core",
description:"Aborted",
standard:"ansi"
},
{
name:"SIGIOT",
number:6,
action:"core",
description:"Aborted",
standard:"bsd"
},
{
name:"SIGBUS",
number:7,
action:"core",
description:
"Bus error due to misaligned, non-existing address or paging error",
standard:"bsd"
},
{
name:"SIGEMT",
number:7,
action:"terminate",
description:"Command should be emulated but is not implemented",
standard:"other"
},
{
name:"SIGFPE",
number:8,
action:"core",
description:"Floating point arithmetic error",
standard:"ansi"
},
{
name:"SIGKILL",
number:9,
action:"terminate",
description:"Forced termination",
standard:"posix",
forced:true
},
{
name:"SIGUSR1",
number:10,
action:"terminate",
description:"Application-specific signal",
standard:"posix"
},
{
name:"SIGSEGV",
number:11,
action:"core",
description:"Segmentation fault",
standard:"ansi"
},
{
name:"SIGUSR2",
number:12,
action:"terminate",
description:"Application-specific signal",
standard:"posix"
},
{
name:"SIGPIPE",
number:13,
action:"terminate",
description:"Broken pipe or socket",
standard:"posix"
},
{
name:"SIGALRM",
number:14,
action:"terminate",
description:"Timeout or timer",
standard:"posix"
},
{
name:"SIGTERM",
number:15,
action:"terminate",
description:"Termination",
standard:"ansi"
},
{
name:"SIGSTKFLT",
number:16,
action:"terminate",
description:"Stack is empty or overflowed",
standard:"other"
},
{
name:"SIGCHLD",
number:17,
action:"ignore",
description:"Child process terminated, paused or unpaused",
standard:"posix"
},
{
name:"SIGCLD",
number:17,
action:"ignore",
description:"Child process terminated, paused or unpaused",
standard:"other"
},
{
name:"SIGCONT",
number:18,
action:"unpause",
description:"Unpaused",
standard:"posix",
forced:true
},
{
name:"SIGSTOP",
number:19,
action:"pause",
description:"Paused",
standard:"posix",
forced:true
},
{
name:"SIGTSTP",
number:20,
action:"pause",
description:"Paused using CTRL-Z or \"suspend\"",
standard:"posix"
},
{
name:"SIGTTIN",
number:21,
action:"pause",
description:"Background process cannot read terminal input",
standard:"posix"
},
{
name:"SIGBREAK",
number:21,
action:"terminate",
description:"User interruption with CTRL-BREAK",
standard:"other"
},
{
name:"SIGTTOU",
number:22,
action:"pause",
description:"Background process cannot write to terminal output",
standard:"posix"
},
{
name:"SIGURG",
number:23,
action:"ignore",
description:"Socket received out-of-band data",
standard:"bsd"
},
{
name:"SIGXCPU",
number:24,
action:"core",
description:"Process timed out",
standard:"bsd"
},
{
name:"SIGXFSZ",
number:25,
action:"core",
description:"File too big",
standard:"bsd"
},
{
name:"SIGVTALRM",
number:26,
action:"terminate",
description:"Timeout or timer",
standard:"bsd"
},
{
name:"SIGPROF",
number:27,
action:"terminate",
description:"Timeout or timer",
standard:"bsd"
},
{
name:"SIGWINCH",
number:28,
action:"ignore",
description:"Terminal window size changed",
standard:"bsd"
},
{
name:"SIGIO",
number:29,
action:"terminate",
description:"I/O is available",
standard:"other"
},
{
name:"SIGPOLL",
number:29,
action:"terminate",
description:"Watched event",
standard:"other"
},
{
name:"SIGINFO",
number:29,
action:"ignore",
description:"Request for process information",
standard:"other"
},
{
name:"SIGPWR",
number:30,
action:"terminate",
description:"Device running out of power",
standard:"systemv"
},
{
name:"SIGSYS",
number:31,
action:"core",
description:"Invalid system call",
standard:"other"
},
{
name:"SIGUNUSED",
number:31,
action:"terminate",
description:"Invalid system call",
standard:"other"
}];
const getSignals=()=>{
const realtimeSignals=getRealtimeSignals();
const signals=[...SIGNALS,...realtimeSignals].map(normalizeSignal);
return signals
};
const normalizeSignal=({
name,
number:defaultNumber,
description,
action,
forced=false,
standard
})=>{
const{
signals:{[name]:constantSignal}
}=constants;
const supported=constantSignal!==undefined;
const number=supported?constantSignal:defaultNumber;
return {name,number,description,supported,action,forced,standard}
};
const getSignalsByName=()=>{
const signals=getSignals();
return Object.fromEntries(signals.map(getSignalByName))
};
const getSignalByName=({
name,
number,
description,
supported,
action,
forced,
standard
})=>[name,{name,number,description,supported,action,forced,standard}];
const signalsByName=getSignalsByName();
const getSignalsByNumber=()=>{
const signals=getSignals();
const length=SIGRTMAX+1;
const signalsA=Array.from({length},(value,number)=>
getSignalByNumber(number,signals)
);
return Object.assign({},...signalsA)
};
const getSignalByNumber=(number,signals)=>{
const signal=findSignalByNumber(number,signals);
if(signal===undefined){
return {}
}
const{name,description,supported,action,forced,standard}=signal;
return {
[number]:{
name,
number,
description,
supported,
action,
forced,
standard
}
}
};
const findSignalByNumber=(number,signals)=>{
const signal=signals.find(({name})=>constants.signals[name]===number);
if(signal!==undefined){
return signal
}
return signals.find((signalA)=>signalA.number===number)
};
getSignalsByNumber();
const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => {
if (timedOut) {
return `timed out after ${timeout} milliseconds`;
}
if (isCanceled) {
return 'was canceled';
}
if (errorCode !== undefined) {
return `failed with ${errorCode}`;
}
if (signal !== undefined) {
return `was killed with ${signal} (${signalDescription})`;
}
if (exitCode !== undefined) {
return `failed with exit code ${exitCode}`;
}
return 'failed';
};
const makeError = ({
stdout,
stderr,
all,
error,
signal,
exitCode,
command,
escapedCommand,
timedOut,
isCanceled,
killed,
parsed: {options: {timeout, cwd = process$2.cwd()}},
}) => {
// `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`.
// We normalize them to `undefined`
exitCode = exitCode === null ? undefined : exitCode;
signal = signal === null ? undefined : signal;
const signalDescription = signal === undefined ? undefined : signalsByName[signal].description;
const errorCode = error && error.code;
const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled});
const execaMessage = `Command ${prefix}: ${command}`;
const isError = Object.prototype.toString.call(error) === '[object Error]';
const shortMessage = isError ? `${execaMessage}\n${error.message}` : execaMessage;
const message = [shortMessage, stderr, stdout].filter(Boolean).join('\n');
if (isError) {
error.originalMessage = error.message;
error.message = message;
} else {
error = new Error(message);
}
error.shortMessage = shortMessage;
error.command = command;
error.escapedCommand = escapedCommand;
error.exitCode = exitCode;
error.signal = signal;
error.signalDescription = signalDescription;
error.stdout = stdout;
error.stderr = stderr;
error.cwd = cwd;
if (all !== undefined) {
error.all = all;
}
if ('bufferedData' in error) {
delete error.bufferedData;
}
error.failed = true;
error.timedOut = Boolean(timedOut);
error.isCanceled = isCanceled;
error.killed = killed && !timedOut;
return error;
};
const aliases = ['stdin', 'stdout', 'stderr'];
const hasAlias = options => aliases.some(alias => options[alias] !== undefined);
const normalizeStdio = options => {
if (!options) {
return;
}
const {stdio} = options;
if (stdio === undefined) {
return aliases.map(alias => options[alias]);
}
if (hasAlias(options)) {
throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`);
}
if (typeof stdio === 'string') {
return stdio;
}
if (!Array.isArray(stdio)) {
throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``);
}
const length = Math.max(stdio.length, aliases.length);
return Array.from({length}, (value, index) => stdio[index]);
};
/**
* This is not the set of all possible signals.
*
* It IS, however, the set of all signals that trigger
* an exit on either Linux or BSD systems. Linux is a
* superset of the signal names supported on BSD, and
* the unknown signals just fail to register, so we can
* catch that easily enough.
*
* Windows signals are a different set, since there are
* signals that terminate Windows processes, but don't
* terminate (or don't even exist) on Posix systems.
*
* Don't bother with SIGKILL. It's uncatchable, which
* means that we can't fire any callbacks anyway.
*
* If a user does happen to register a handler on a non-
* fatal signal like SIGWINCH or something, and then
* exit, it'll end up firing `process.emit('exit')`, so
* the handler will be fired anyway.
*
* SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
* artificially, inherently leave the process in a
* state from which it is not safe to try and enter JS
* listeners.
*/
const signals = [];
signals.push('SIGHUP', 'SIGINT', 'SIGTERM');
if (process.platform !== 'win32') {
signals.push('SIGALRM', 'SIGABRT', 'SIGVTALRM', 'SIGXCPU', 'SIGXFSZ', 'SIGUSR2', 'SIGTRAP', 'SIGSYS', 'SIGQUIT', 'SIGIOT'
// should detect profiler and enable/disable accordingly.
// see #21
// 'SIGPROF'
);
}
if (process.platform === 'linux') {
signals.push('SIGIO', 'SIGPOLL', 'SIGPWR', 'SIGSTKFLT');
}
// Note: since nyc uses this module to output coverage, any lines
// that are in the direct sync flow of nyc's outputCoverage are
// ignored, since we can never get coverage for them.
// grab a reference to node's real process object right away
const processOk = (process) => !!process &&
typeof process === 'object' &&
typeof process.removeListener === 'function' &&
typeof process.emit === 'function' &&
typeof process.reallyExit === 'function' &&
typeof process.listeners === 'function' &&
typeof process.kill === 'function' &&
typeof process.pid === 'number' &&
typeof process.on === 'function';
const kExitEmitter = Symbol.for('signal-exit emitter');
const global$1 = globalThis;
const ObjectDefineProperty = Object.defineProperty.bind(Object);
// teeny special purpose ee
class Emitter {
emitted = {
afterExit: false,
exit: false,
};
listeners = {
afterExit: [],
exit: [],
};
count = 0;
id = Math.random();
constructor() {
if (global$1[kExitEmitter]) {
return global$1[kExitEmitter];
}
ObjectDefineProperty(global$1, kExitEmitter, {
value: this,
writable: false,
enumerable: false,
configurable: false,
});
}
on(ev, fn) {
this.listeners[ev].push(fn);
}
removeListener(ev, fn) {
const list = this.listeners[ev];
const i = list.indexOf(fn);
/* c8 ignore start */
if (i === -1) {
return;
}
/* c8 ignore stop */
if (i === 0 && list.length === 1) {
list.length = 0;
}
else {
list.splice(i, 1);
}
}
emit(ev, code, signal) {
if (this.emitted[ev]) {
return false;
}
this.emitted[ev] = true;
let ret = false;
for (const fn of this.listeners[ev]) {
ret = fn(code, signal) === true || ret;
}
if (ev === 'exit') {
ret = this.emit('afterExit', code, signal) || ret;
}
return ret;
}
}
class SignalExitBase {
}
const signalExitWrap = (handler) => {
return {
onExit(cb, opts) {
return handler.onExit(cb, opts);
},
load() {
return handler.load();
},
unload() {
return handler.unload();
},
};
};
class SignalExitFallback extends SignalExitBase {
onExit() {
return () => { };
}
load() { }
unload() { }
}
class SignalExit extends SignalExitBase {
// "SIGHUP" throws an `ENOSYS` error on Windows,
// so use a supported signal instead
/* c8 ignore start */
#hupSig = process$1.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
/* c8 ignore stop */
#emitter = new Emitter();
#process;
#originalProcessEmit;
#originalProcessReallyExit;
#sigListeners = {};
#loaded = false;
constructor(process) {
super();
this.#process = process;
// { <signal>: <listener fn>, ... }
this.#sigListeners = {};
for (const sig of signals) {
this.#sigListeners[sig] = () => {
// If there are no other listeners, an exit is coming!
// Simplest way: remove us and then re-send the signal.
// We know that this will kill the process, so we can
// safely emit now.
const listeners = this.#process.listeners(sig);
let { count } = this.#emitter;
// This is a workaround for the fact that signal-exit v3 and signal
// exit v4 are not aware of each other, and each will attempt to let
// the other handle it, so neither of them do. To correct this, we
// detect if we're the only handler *except* for previous versions
// of signal-exit, and increment by the count of listeners it has
// created.
/* c8 ignore start */
const p = process;
if (typeof p.__signal_exit_emitter__ === 'object' &&
typeof p.__signal_exit_emitter__.count === 'number') {
count += p.__signal_exit_emitter__.count;
}
/* c8 ignore stop */
if (listeners.length === count) {
this.unload();
const ret = this.#emitter.emit('exit', null, sig);
/* c8 ignore start */
const s = sig === 'SIGHUP' ? this.#hupSig : sig;
if (!ret)
process.kill(process.pid, s);
/* c8 ignore stop */
}
};
}
this.#originalProcessReallyExit = process.reallyExit;
this.#originalProcessEmit = process.emit;
}
onExit(cb, opts) {
/* c8 ignore start */
if (!processOk(this.#process)) {
return () => { };
}
/* c8 ignore stop */
if (this.#loaded === false) {
this.load();
}
const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
this.#emitter.on(ev, cb);
return () => {
this.#emitter.removeListener(ev, cb);
if (this.#emitter.listeners['exit'].length === 0 &&
this.#emitter.listeners['afterExit'].length === 0) {
this.unload();
}
};
}
load() {
if (this.#loaded) {
return;
}
this.#loaded = true;
// This is the number of onSignalExit's that are in play.
// It's important so that we can count the correct number of
// listeners on signals, and don't wait for the other one to
// handle it instead of us.
this.#emitter.count += 1;
for (const sig of signals) {
try {
const fn = this.#sigListeners[sig];
if (fn)
this.#process.on(sig, fn);
}
catch (_) { }
}
this.#process.emit = (ev, ...a) => {
return this.#processEmit(ev, ...a);
};
this.#process.reallyExit = (code) => {
return this.#processReallyExit(code);
};
}
unload() {
if (!this.#loaded) {
return;
}
this.#loaded = false;
signals.forEach(sig => {
const listener = this.#sigListeners[sig];
/* c8 ignore start */
if (!listener) {
throw new Error('Listener not defined for signal: ' + sig);
}
/* c8 ignore stop */
try {
this.#process.removeListener(sig, listener);
/* c8 ignore start */
}
catch (_) { }
/* c8 ignore stop */
});
this.#process.emit = this.#originalProcessEmit;
this.#process.reallyExit = this.#originalProcessReallyExit;
this.#emitter.count -= 1;
}
#processReallyExit(code) {
/* c8 ignore start */
if (!processOk(this.#process)) {
return 0;
}
this.#process.exitCode = code || 0;
/* c8 ignore stop */
this.#emitter.emit('exit', this.#process.exitCode, null);
return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
}
#processEmit(ev, ...args) {
const og = this.#originalProcessEmit;
if (ev === 'exit' && processOk(this.#process)) {
if (typeof args[0] === 'number') {
this.#process.exitCode = args[0];
/* c8 ignore start */
}
/* c8 ignore start */
const ret = og.call(this.#process, ev, ...args);
/* c8 ignore start */
this.#emitter.emit('exit', this.#process.exitCode, null);
/* c8 ignore stop */
return ret;
}
else {
return og.call(this.#process, ev, ...args);
}
}
}
const process$1 = globalThis.process;
// wrap so that we call the method on the actual handler, without
// exporting it directly.
const {
/**
* Called when the process is exiting, whether via signal, explicit
* exit, or running out of stuff to do.
*
* If the global process object is not suitable for instrumentation,
* then this will be a no-op.
*
* Returns a function that may be used to unload signal-exit.
*/
onExit,
/**
* Load the listeners. Likely you never need to call this, unless
* doing a rather deep integration with signal-exit functionality.
* Mostly exposed for the benefit of testing.
*
* @internal
*/
load,
/**
* Unload the listeners. Likely you never need to call this, unless
* doing a rather deep integration with signal-exit functionality.
* Mostly exposed for the benefit of testing.
*
* @internal
*/
unload, } = signalExitWrap(processOk(process$1) ? new SignalExit(process$1) : new SignalExitFallback());
const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;
// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior
const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => {
const killResult = kill(signal);
setKillTimeout(kill, signal, options, killResult);
return killResult;
};
const setKillTimeout = (kill, signal, options, killResult) => {
if (!shouldForceKill(signal, options, killResult)) {
return;
}
const timeout = getForceKillAfterTimeout(options);
const t = setTimeout(() => {
kill('SIGKILL');
}, timeout);
// Guarded because there's no `.unref()` when `execa` is used in the renderer
// process in Electron. This cannot be tested since we don't run tests in
// Electron.
// istanbul ignore else
if (t.unref) {
t.unref();
}
};
const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => isSigterm(signal) && forceKillAfterTimeout !== false && killResult;
const isSigterm = signal => signal === os.constants.signals.SIGTERM
|| (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM');
const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => {
if (forceKillAfterTimeout === true) {
return DEFAULT_FORCE_KILL_TIMEOUT;
}
if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) {
throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`);
}
return forceKillAfterTimeout;
};
// `childProcess.cancel()`
const spawnedCancel = (spawned, context) => {
const killResult = spawned.kill();
if (killResult) {
context.isCanceled = true;
}
};
const timeoutKill = (spawned, signal, reject) => {
spawned.kill(signal);
reject(Object.assign(new Error('Timed out'), {timedOut: true, signal}));
};
// `timeout` option handling
const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise)