Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

execa

Package Overview
Dependencies
Maintainers
2
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

execa - npm Package Compare versions

Comparing version 2.0.1 to 2.0.2

lib/command.js

257

index.js

@@ -7,14 +7,12 @@ 'use strict';

const npmRunPath = require('npm-run-path');
const isStream = require('is-stream');
const getStream = require('get-stream');
const mergeStream = require('merge-stream');
const pFinally = require('p-finally');
const onetime = require('onetime');
const makeError = require('./lib/error');
const normalizeStdio = require('./lib/stdio');
const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler, cleanup} = require('./lib/kill');
const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = require('./lib/kill');
const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = require('./lib/stream.js');
const {mergePromise, getSpawnedPromise} = require('./lib/promise.js');
const {joinCommand, parseCommand} = require('./lib/command.js');
const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100;
const SPACES_REGEXP = / +/g;
const handleArgs = (file, args, options = {}) => {

@@ -63,16 +61,2 @@ const parsed = crossSpawn._parse(file, args, options);

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
if (input === undefined || spawned.stdin === undefined) {
return;
}
if (isStream(input)) {
input.pipe(spawned.stdin);
} else {
spawned.stdin.end(input);
}
};
const handleOutput = (options, value, error) => {

@@ -91,127 +75,2 @@ if (typeof value !== 'string' && !Buffer.isBuffer(value)) {

const makeAllStream = spawned => {
if (!spawned.stdout && !spawned.stderr) {
return;
}
const mixed = mergeStream();
if (spawned.stdout) {
mixed.add(spawned.stdout);
}
if (spawned.stderr) {
mixed.add(spawned.stderr);
}
return mixed;
};
const getBufferedData = async (stream, streamPromise) => {
if (!stream) {
return;
}
stream.destroy();
try {
return await streamPromise;
} catch (error) {
return error.bufferedData;
}
};
const getStreamPromise = (stream, {encoding, buffer, maxBuffer}) => {
if (!stream) {
return;
}
if (!buffer) {
// TODO: Use `ret = util.promisify(stream.finished)(stream);` when targeting Node.js 10
return new Promise((resolve, reject) => {
stream
.once('end', resolve)
.once('error', reject);
});
}
if (encoding) {
return getStream(stream, {encoding, maxBuffer});
}
return getStream.buffer(stream, {maxBuffer});
};
const getPromiseResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => {
const stdoutPromise = getStreamPromise(stdout, {encoding, buffer, maxBuffer});
const stderrPromise = getStreamPromise(stderr, {encoding, buffer, maxBuffer});
const allPromise = getStreamPromise(all, {encoding, buffer, maxBuffer: maxBuffer * 2});
try {
return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]);
} catch (error) {
return Promise.all([
{error, code: error.code, signal: error.signal},
getBufferedData(stdout, stdoutPromise),
getBufferedData(stderr, stderrPromise),
getBufferedData(all, allPromise)
]);
}
};
const joinCommand = (file, args = []) => {
if (!Array.isArray(args)) {
return file;
}
return [file, ...args].join(' ');
};
const mergePromiseProperty = (spawned, getPromise, property) => {
Object.defineProperty(spawned, property, {
value(...args) {
return getPromise()[property](...args);
},
writable: true,
enumerable: false,
configurable: true
});
};
// The return value is a mixin of `childProcess` and `Promise`
const mergePromise = (spawned, getPromise) => {
mergePromiseProperty(spawned, getPromise, 'then');
mergePromiseProperty(spawned, getPromise, 'catch');
// TODO: Remove the `if`-guard when targeting Node.js 10
if (Promise.prototype.finally) {
mergePromiseProperty(spawned, getPromise, 'finally');
}
return spawned;
};
const handleSpawned = (spawned, context) => {
return new Promise((resolve, reject) => {
spawned.on('exit', (code, signal) => {
if (context.timedOut) {
reject(Object.assign(new Error('Timed out'), {code, signal}));
return;
}
resolve({code, signal});
});
spawned.on('error', error => {
reject(error);
});
if (spawned.stdin) {
spawned.stdin.on('error', error => {
reject(error);
});
}
});
};
const execa = (file, args, options) => {

@@ -225,42 +84,44 @@ const parsed = handleArgs(file, args, options);

} catch (error) {
return mergePromise(new childProcess.ChildProcess(), () =>
Promise.reject(makeError({
error,
stdout: '',
stderr: '',
all: '',
command,
parsed,
timedOut: false,
isCanceled: false,
killed: false
}))
);
// Ensure the returned error is always both a promise and a child process
const dummySpawned = new childProcess.ChildProcess();
const errorPromise = Promise.reject(makeError({
error,
stdout: '',
stderr: '',
all: '',
command,
parsed,
timedOut: false,
isCanceled: false,
killed: false
}));
return mergePromise(dummySpawned, errorPromise);
}
const context = {timedOut: false, isCanceled: false};
const spawnedPromise = getSpawnedPromise(spawned);
const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise);
const processDone = setExitHandler(spawned, parsed.options, timedPromise);
const context = {isCanceled: false};
spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned));
spawned.cancel = spawnedCancel.bind(null, spawned, context);
const timeoutId = setupTimeout(spawned, parsed.options, context);
const removeExitHandler = setExitHandler(spawned, parsed.options);
// TODO: Use native "finally" syntax when targeting Node.js 10
const processDone = pFinally(handleSpawned(spawned, context), () => {
cleanup(timeoutId, removeExitHandler);
});
const handlePromise = async () => {
const [result, stdout, stderr, all] = await getPromiseResult(spawned, parsed.options, processDone);
result.stdout = handleOutput(parsed.options, stdout);
result.stderr = handleOutput(parsed.options, stderr);
result.all = handleOutput(parsed.options, all);
const [{error, code, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone);
const stdout = handleOutput(parsed.options, stdoutResult);
const stderr = handleOutput(parsed.options, stderrResult);
const all = handleOutput(parsed.options, allResult);
if (result.error || result.code !== 0 || result.signal !== null) {
const error = makeError({
...result,
if (error || code !== 0 || signal !== null) {
const returnedError = makeError({
error,
code,
signal,
stdout,
stderr,
all,
command,
parsed,
timedOut: context.timedOut,
timedOut,
isCanceled: context.isCanceled,

@@ -271,6 +132,6 @@ killed: spawned.killed

if (!parsed.options.reject) {
return error;
return returnedError;
}
throw error;
throw returnedError;
}

@@ -282,5 +143,5 @@

exitCodeName: 'SUCCESS',
stdout: result.stdout,
stderr: result.stderr,
all: result.all,
stdout,
stderr,
all,
failed: false,

@@ -293,2 +154,4 @@ timedOut: false,

const handlePromiseOnce = onetime(handlePromise);
crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed);

@@ -300,3 +163,3 @@

return mergePromise(spawned, handlePromise);
return mergePromise(spawned, handlePromiseOnce);
};

@@ -310,5 +173,3 @@

if (isStream(parsed.options.input)) {
throw new TypeError('The `input` option cannot be a stream in sync mode');
}
validateInputSync(parsed.options);

@@ -366,24 +227,2 @@ let result;

// 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 previousToken = tokens[tokens.length - 1];
if (previousToken.endsWith('\\')) {
return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`];
}
return [...tokens, token];
};
const parseCommand = command => {
return command
.trim()
.split(SPACES_REGEXP)
.reduce(handleEscaping, []);
};
module.exports.command = (command, options) => {

@@ -405,3 +244,3 @@ const [file, ...args] = parseCommand(command);

const stdioOption = normalizeStdio.node(options);
const stdio = normalizeStdio.node(options);

@@ -422,3 +261,3 @@ const {nodePath = process.execPath, nodeOptions = process.execArgv} = options;

stderr: undefined,
stdio: stdioOption,
stdio,
shell: false

@@ -425,0 +264,0 @@ }

@@ -77,3 +77,3 @@ 'use strict';

error.failed = true;
error.timedOut = timedOut;
error.timedOut = Boolean(timedOut);
error.isCanceled = isCanceled;

@@ -80,0 +80,0 @@ error.killed = killed && !timedOut;

'use strict';
const os = require('os');
const onExit = require('signal-exit');
const pFinally = require('p-finally');

@@ -55,14 +56,33 @@ const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;

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}, context) => {
if (timeout > 0) {
return setTimeout(() => {
context.timedOut = true;
spawned.kill(killSignal);
const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => {
if (timeout === 0 || timeout === undefined) {
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(() => {
timeoutKill(spawned, killSignal, reject);
}, timeout);
}
});
const safeSpawnedPromise = pFinally(spawnedPromise, () => {
clearTimeout(timeoutId);
});
return Promise.race([timeoutPromise, safeSpawnedPromise]);
};
// `cleanup` option handling
const setExitHandler = (spawned, {cleanup, detached}) => {
const setExitHandler = (spawned, {cleanup, detached}, timedPromise) => {
if (!cleanup || detached) {

@@ -72,15 +92,8 @@ return;

return onExit(() => {
const removeExitHandler = onExit(() => {
spawned.kill();
});
};
const cleanup = (timeoutId, removeExitHandler) => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
if (removeExitHandler !== undefined) {
removeExitHandler();
}
// TODO: Use native "finally" syntax when targeting Node.js 10
return pFinally(timedPromise, removeExitHandler);
};

@@ -92,4 +105,3 @@

setupTimeout,
setExitHandler,
cleanup
setExitHandler
};
{
"name": "execa",
"version": "2.0.1",
"version": "2.0.2",
"description": "Process execution for humans",

@@ -46,2 +46,3 @@ "license": "MIT",

"npm-run-path": "^3.0.0",
"onetime": "^5.1.0",
"p-finally": "^2.0.0",

@@ -48,0 +49,0 @@ "signal-exit": "^3.0.2",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc