This commit is contained in:
2022-07-18 02:50:52 +00:00
parent befd344ab0
commit 06181b34d6
8569 changed files with 818704 additions and 352705 deletions
+34 -20
View File
@@ -1,38 +1,52 @@
'use strict';
const SPACES_REGEXP = / +/g;
const joinCommand = (file, args = []) => {
const normalizeArgs = (file, args = []) => {
if (!Array.isArray(args)) {
return file;
return [file];
}
return [file, ...args].join(' ');
return [file, ...args];
};
// Allow spaces to be escaped by a backslash if not meant as a delimiter
const handleEscaping = (tokens, token, index) => {
if (index === 0) {
return [token];
const NO_ESCAPE_REGEXP = /^[\w.-]+$/;
const DOUBLE_QUOTES_REGEXP = /"/g;
const escapeArg = arg => {
if (typeof arg !== 'string' || NO_ESCAPE_REGEXP.test(arg)) {
return arg;
}
const previousToken = tokens[tokens.length - 1];
if (previousToken.endsWith('\\')) {
return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`];
}
return [...tokens, token];
return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`;
};
const joinCommand = (file, args) => {
return normalizeArgs(file, args).join(' ');
};
const getEscapedCommand = (file, args) => {
return normalizeArgs(file, args).map(arg => escapeArg(arg)).join(' ');
};
const SPACES_REGEXP = / +/g;
// Handle `execa.command()`
const parseCommand = command => {
return command
.trim()
.split(SPACES_REGEXP)
.reduce(handleEscaping, []);
const tokens = [];
for (const token of command.trim().split(SPACES_REGEXP)) {
// Allow spaces to be escaped by a backslash if not meant as a delimiter
const previousToken = tokens[tokens.length - 1];
if (previousToken && previousToken.endsWith('\\')) {
// Merge previous token with current one
tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`;
} else {
tokens.push(token);
}
}
return tokens;
};
module.exports = {
joinCommand,
getEscapedCommand,
parseCommand
};
+9 -3
View File
@@ -33,6 +33,7 @@ const makeError = ({
signal,
exitCode,
command,
escapedCommand,
timedOut,
isCanceled,
killed,
@@ -47,16 +48,21 @@ const makeError = ({
const errorCode = error && error.code;
const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled});
const message = `Command ${prefix}: ${command}`;
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 (error instanceof Error) {
if (isError) {
error.originalMessage = error.message;
error.message = `${message}\n${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;
+23 -12
View File
@@ -1,7 +1,6 @@
'use strict';
const os = require('os');
const onExit = require('signal-exit');
const pFinally = require('p-finally');
const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;
@@ -18,9 +17,17 @@ const setKillTimeout = (kill, signal, options, killResult) => {
}
const timeout = getForceKillAfterTimeout(options);
setTimeout(() => {
const t = setTimeout(() => {
kill('SIGKILL');
}, timeout).unref();
}, 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) => {
@@ -37,7 +44,7 @@ const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => {
return DEFAULT_FORCE_KILL_TIMEOUT;
}
if (!Number.isInteger(forceKillAfterTimeout) || forceKillAfterTimeout < 0) {
if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) {
throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`);
}
@@ -64,10 +71,6 @@ const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise
return spawnedPromise;
}
if (!Number.isInteger(timeout) || timeout < 0) {
throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`);
}
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
@@ -75,15 +78,21 @@ const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise
}, timeout);
});
const safeSpawnedPromise = pFinally(spawnedPromise, () => {
const safeSpawnedPromise = spawnedPromise.finally(() => {
clearTimeout(timeoutId);
});
return Promise.race([timeoutPromise, safeSpawnedPromise]);
};
const validateTimeout = ({timeout}) => {
if (timeout !== undefined && (!Number.isFinite(timeout) || timeout < 0)) {
throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`);
}
};
// `cleanup` option handling
const setExitHandler = (spawned, {cleanup, detached}, timedPromise) => {
const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => {
if (!cleanup || detached) {
return timedPromise;
}
@@ -92,13 +101,15 @@ const setExitHandler = (spawned, {cleanup, detached}, timedPromise) => {
spawned.kill();
});
// TODO: Use native "finally" syntax when targeting Node.js 10
return pFinally(timedPromise, removeExitHandler);
return timedPromise.finally(() => {
removeExitHandler();
});
};
module.exports = {
spawnedKill,
spawnedCancel,
setupTimeout,
validateTimeout,
setExitHandler
};
+11 -17
View File
@@ -1,26 +1,20 @@
'use strict';
const mergePromiseProperty = (spawned, promise, property) => {
// Starting the main `promise` is deferred to avoid consuming streams
const value = typeof promise === 'function' ?
(...args) => promise()[property](...args) :
promise[property].bind(promise);
Object.defineProperty(spawned, property, {
value,
writable: true,
enumerable: false,
configurable: true
});
};
const nativePromisePrototype = (async () => {})().constructor.prototype;
const descriptors = ['then', 'catch', 'finally'].map(property => [
property,
Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property)
]);
// The return value is a mixin of `childProcess` and `Promise`
const mergePromise = (spawned, promise) => {
mergePromiseProperty(spawned, promise, 'then');
mergePromiseProperty(spawned, promise, 'catch');
for (const [property, descriptor] of descriptors) {
// Starting the main `promise` is deferred to avoid consuming streams
const value = typeof promise === 'function' ?
(...args) => Reflect.apply(descriptor.value, promise(), args) :
descriptor.value.bind(promise);
// TODO: Remove the `if`-guard when targeting Node.js 10
if (Promise.prototype.finally) {
mergePromiseProperty(spawned, promise, 'finally');
Reflect.defineProperty(spawned, property, {...descriptor, value});
}
return spawned;
+8 -8
View File
@@ -1,20 +1,20 @@
'use strict';
const aliases = ['stdin', 'stdout', 'stderr'];
const hasAlias = opts => aliases.some(alias => opts[alias] !== undefined);
const hasAlias = options => aliases.some(alias => options[alias] !== undefined);
const normalizeStdio = opts => {
if (!opts) {
const normalizeStdio = options => {
if (!options) {
return;
}
const {stdio} = opts;
const {stdio} = options;
if (stdio === undefined) {
return aliases.map(alias => opts[alias]);
return aliases.map(alias => options[alias]);
}
if (hasAlias(opts)) {
if (hasAlias(options)) {
throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`);
}
@@ -33,8 +33,8 @@ const normalizeStdio = opts => {
module.exports = normalizeStdio;
// `ipc` is pushed unless it is already present
module.exports.node = opts => {
const stdio = normalizeStdio(opts);
module.exports.node = options => {
const stdio = normalizeStdio(options);
if (stdio === 'ipc') {
return 'ipc';
+1 -1
View File
@@ -6,7 +6,7 @@ const mergeStream = require('merge-stream');
// `input` option
const handleInput = (spawned, input) => {
// Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852
// TODO: Remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0
// @todo remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0
if (input === undefined || spawned.stdin === undefined) {
return;
}