aboutsummaryrefslogtreecommitdiff
path: root/node_modules/cross-spawn/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/cross-spawn/lib')
-rw-r--r--node_modules/cross-spawn/lib/enoent.js56
-rw-r--r--node_modules/cross-spawn/lib/hasBrokenSpawn.js11
-rw-r--r--node_modules/cross-spawn/lib/parse.js197
-rw-r--r--node_modules/cross-spawn/lib/resolveCommand.js31
4 files changed, 112 insertions, 183 deletions
diff --git a/node_modules/cross-spawn/lib/enoent.js b/node_modules/cross-spawn/lib/enoent.js
index 74ff06e49..14df9b623 100644
--- a/node_modules/cross-spawn/lib/enoent.js
+++ b/node_modules/cross-spawn/lib/enoent.js
@@ -1,43 +1,37 @@
'use strict';
-var isWin = process.platform === 'win32';
-var resolveCommand = require('./resolveCommand');
-
-var isNode10 = process.version.indexOf('v0.10.') === 0;
-
-function notFoundError(command, syscall) {
- var err;
-
- err = new Error(syscall + ' ' + command + ' ENOENT');
- err.code = err.errno = 'ENOENT';
- err.syscall = syscall + ' ' + command;
-
- return err;
+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) {
- var originalEmit;
-
if (!isWin) {
return;
}
- originalEmit = cp.emit;
- cp.emit = function (name, arg1) {
- var err;
+ 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
+ // See https://github.com/IndigoUnited/node-cross-spawn/issues/16
if (name === 'exit') {
- err = verifyENOENT(arg1, parsed, 'spawn');
+ const err = verifyENOENT(arg1, parsed, 'spawn');
if (err) {
return originalEmit.call(cp, 'error', err);
}
}
- return originalEmit.apply(cp, arguments);
+ return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params
};
}
@@ -54,20 +48,12 @@ function verifyENOENTSync(status, parsed) {
return notFoundError(parsed.original, 'spawnSync');
}
- // If we are in node 10, then we are using spawn-sync; if it exited
- // with -1 it probably means that the command does not exist
- if (isNode10 && status === -1) {
- parsed.file = isWin ? parsed.file : resolveCommand(parsed.original);
-
- if (!parsed.file) {
- return notFoundError(parsed.original, 'spawnSync');
- }
- }
-
return null;
}
-module.exports.hookChildProcess = hookChildProcess;
-module.exports.verifyENOENT = verifyENOENT;
-module.exports.verifyENOENTSync = verifyENOENTSync;
-module.exports.notFoundError = notFoundError;
+module.exports = {
+ hookChildProcess,
+ verifyENOENT,
+ verifyENOENTSync,
+ notFoundError,
+};
diff --git a/node_modules/cross-spawn/lib/hasBrokenSpawn.js b/node_modules/cross-spawn/lib/hasBrokenSpawn.js
deleted file mode 100644
index e73f906b6..000000000
--- a/node_modules/cross-spawn/lib/hasBrokenSpawn.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = (function () {
- if (process.platform !== 'win32') {
- return false;
- }
- var nodeVer = process.version.substr(1).split('.').map(function (num) {
- return parseInt(num, 10);
- });
- return (nodeVer[0] === 0 && nodeVer[1] < 12);
-})();
diff --git a/node_modules/cross-spawn/lib/parse.js b/node_modules/cross-spawn/lib/parse.js
index 77cbb83d2..962827a94 100644
--- a/node_modules/cross-spawn/lib/parse.js
+++ b/node_modules/cross-spawn/lib/parse.js
@@ -1,140 +1,125 @@
'use strict';
-var fs = require('fs');
-var LRU = require('lru-cache');
-var resolveCommand = require('./resolveCommand');
-var hasBrokenSpawn = require('./hasBrokenSpawn');
-
-var isWin = process.platform === 'win32';
-var shebangCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec
-
-function readShebang(command) {
- var buffer;
- var fd;
- var match;
- var shebang;
-
- // Check if it is in the cache first
- if (shebangCache.has(command)) {
- return shebangCache.get(command);
- }
+const path = require('path');
+const niceTry = require('nice-try');
+const resolveCommand = require('./util/resolveCommand');
+const escape = require('./util/escape');
+const readShebang = require('./util/readShebang');
+const semver = require('semver');
- // Read the first 150 bytes from the file
- buffer = new Buffer(150);
+const isWin = process.platform === 'win32';
+const isExecutableRegExp = /\.(?:com|exe)$/i;
+const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
- try {
- fd = fs.openSync(command, 'r');
- fs.readSync(fd, buffer, 0, 150, 0);
- fs.closeSync(fd);
- } catch (e) { /* empty */ }
+// `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0
+const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false;
- // Check if it is a shebang
- match = buffer.toString().trim().match(/#!(.+)/i);
+function detectShebang(parsed) {
+ parsed.file = resolveCommand(parsed);
- if (match) {
- shebang = match[1].replace(/\/usr\/bin\/env\s+/i, ''); // Remove /usr/bin/env
- }
+ const shebang = parsed.file && readShebang(parsed.file);
- // Store the shebang in the cache
- shebangCache.set(command, shebang);
+ if (shebang) {
+ parsed.args.unshift(parsed.file);
+ parsed.command = shebang;
+
+ return resolveCommand(parsed);
+ }
- return shebang;
+ return parsed.file;
}
-function escapeArg(arg, quote) {
- // Convert to string
- arg = '' + arg;
+function parseNonShell(parsed) {
+ if (!isWin) {
+ return parsed;
+ }
- // If we are not going to quote the argument,
- // escape shell metacharacters, including double and single quotes:
- if (!quote) {
- arg = arg.replace(/([\(\)%!\^<>&|;,"'\s])/g, '^$1');
- } else {
- // Sequence of backslashes followed by a double quote:
- // double up all the backslashes and escape the double quote
- arg = arg.replace(/(\\*)"/g, '$1$1\\"');
+ // Detect & add support for shebangs
+ const commandFile = detectShebang(parsed);
- // 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');
+ // We don't need a shell if the command filename is an executable
+ const needsShell = !isExecutableRegExp.test(commandFile);
- // All other backslashes occur literally
+ // 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);
- // Quote the whole thing:
- arg = '"' + arg + '"';
- }
+ // 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);
- return arg;
-}
+ // Escape command & arguments
+ parsed.command = escape.command(parsed.command);
+ parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
-function escapeCommand(command) {
- // Do not escape if this command is not dangerous..
- // We do this so that commands like "echo" or "ifconfig" work
- // Quoting them, will make them unaccessible
- return /^[a-z0-9_-]+$/i.test(command) ? command : escapeArg(command, true);
-}
+ const shellCommand = [parsed.command].concat(parsed.args).join(' ');
-function requiresShell(command) {
- return !/\.(?:com|exe)$/i.test(command);
-}
+ 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
+ }
-function parse(command, args, options) {
- var shebang;
- var applyQuotes;
- var file;
- var original;
- var shell;
+ return parsed;
+}
- // Normalize arguments, similar to nodejs
- if (args && !Array.isArray(args)) {
- options = args;
- args = null;
+function parseShell(parsed) {
+ // If node supports the shell option, there's no need to mimic its behavior
+ if (supportsShellOption) {
+ return parsed;
}
- args = args ? args.slice(0) : []; // Clone array to avoid changing the original
- options = options || {};
- original = command;
+ // Mimic node shell option
+ // See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
+ const shellCommand = [parsed.command].concat(parsed.args).join(' ');
if (isWin) {
- // Detect & add support for shebangs
- file = resolveCommand(command);
- file = file || resolveCommand(command, true);
- shebang = file && readShebang(file);
- shell = options.shell || hasBrokenSpawn;
-
- if (shebang) {
- args.unshift(file);
- command = shebang;
- shell = shell || requiresShell(resolveCommand(shebang) || resolveCommand(shebang, true));
+ parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe';
+ parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
+ parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
+ } else {
+ if (typeof parsed.options.shell === 'string') {
+ parsed.command = parsed.options.shell;
+ } else if (process.platform === 'android') {
+ parsed.command = '/system/bin/sh';
} else {
- shell = shell || requiresShell(file);
+ parsed.command = '/bin/sh';
}
- if (shell) {
- // Escape command & arguments
- applyQuotes = (command !== 'echo'); // Do not quote arguments for the special "echo" command
- command = escapeCommand(command);
- args = args.map(function (arg) {
- return escapeArg(arg, applyQuotes);
- });
+ parsed.args = ['-c', shellCommand];
+ }
- // Use cmd.exe
- args = ['/s', '/c', '"' + command + (args.length ? ' ' + args.join(' ') : '') + '"'];
- command = process.env.comspec || 'cmd.exe';
+ return parsed;
+}
- // Tell node's spawn that the arguments are already escaped
- options.windowsVerbatimArguments = true;
- }
+function parse(command, args, options) {
+ // Normalize arguments, similar to nodejs
+ if (args && !Array.isArray(args)) {
+ options = args;
+ args = null;
}
- return {
- command: command,
- args: args,
- options: options,
- file: file,
- original: original,
+ 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 ? parseShell(parsed) : parseNonShell(parsed);
}
module.exports = parse;
diff --git a/node_modules/cross-spawn/lib/resolveCommand.js b/node_modules/cross-spawn/lib/resolveCommand.js
deleted file mode 100644
index b7a949097..000000000
--- a/node_modules/cross-spawn/lib/resolveCommand.js
+++ /dev/null
@@ -1,31 +0,0 @@
-'use strict';
-
-var path = require('path');
-var which = require('which');
-var LRU = require('lru-cache');
-
-var commandCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec
-
-function resolveCommand(command, noExtension) {
- var resolved;
-
- noExtension = !!noExtension;
- resolved = commandCache.get(command + '!' + noExtension);
-
- // Check if its resolved in the cache
- if (commandCache.has(command)) {
- return commandCache.get(command);
- }
-
- try {
- resolved = !noExtension ?
- which.sync(command) :
- which.sync(command, { pathExt: path.delimiter + (process.env.PATHEXT || '') });
- } catch (e) { /* empty */ }
-
- commandCache.set(command + '!' + noExtension, resolved);
-
- return resolved;
-}
-
-module.exports = resolveCommand;