update
This commit is contained in:
+34
-20
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user