diff options
Diffstat (limited to 'node_modules/cross-spawn/lib')
| -rw-r--r-- | node_modules/cross-spawn/lib/enoent.js | 59 | ||||
| -rw-r--r-- | node_modules/cross-spawn/lib/parse.js | 91 | ||||
| -rw-r--r-- | node_modules/cross-spawn/lib/util/escape.js | 45 | ||||
| -rw-r--r-- | node_modules/cross-spawn/lib/util/readShebang.js | 23 | ||||
| -rw-r--r-- | node_modules/cross-spawn/lib/util/resolveCommand.js | 52 | 
5 files changed, 270 insertions, 0 deletions
| diff --git a/node_modules/cross-spawn/lib/enoent.js b/node_modules/cross-spawn/lib/enoent.js new file mode 100644 index 0000000..14df9b6 --- /dev/null +++ b/node_modules/cross-spawn/lib/enoent.js @@ -0,0 +1,59 @@ +'use strict'; + +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, 'spawn'); + +            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; +} + +module.exports = { +    hookChildProcess, +    verifyENOENT, +    verifyENOENTSync, +    notFoundError, +}; diff --git a/node_modules/cross-spawn/lib/parse.js b/node_modules/cross-spawn/lib/parse.js new file mode 100644 index 0000000..0129d74 --- /dev/null +++ b/node_modules/cross-spawn/lib/parse.js @@ -0,0 +1,91 @@ +'use strict'; + +const path = require('path'); +const resolveCommand = require('./util/resolveCommand'); +const escape = require('./util/escape'); +const readShebang = require('./util/readShebang'); + +const isWin = 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) { +        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(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); +} + +module.exports = parse; diff --git a/node_modules/cross-spawn/lib/util/escape.js b/node_modules/cross-spawn/lib/util/escape.js new file mode 100644 index 0000000..b0bb84c --- /dev/null +++ b/node_modules/cross-spawn/lib/util/escape.js @@ -0,0 +1,45 @@ +'use strict'; + +// 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; +} + +module.exports.command = escapeCommand; +module.exports.argument = escapeArgument; diff --git a/node_modules/cross-spawn/lib/util/readShebang.js b/node_modules/cross-spawn/lib/util/readShebang.js new file mode 100644 index 0000000..5e83733 --- /dev/null +++ b/node_modules/cross-spawn/lib/util/readShebang.js @@ -0,0 +1,23 @@ +'use strict'; + +const fs = require('fs'); +const shebangCommand = require('shebang-command'); + +function readShebang(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()); +} + +module.exports = readShebang; diff --git a/node_modules/cross-spawn/lib/util/resolveCommand.js b/node_modules/cross-spawn/lib/util/resolveCommand.js new file mode 100644 index 0000000..7972455 --- /dev/null +++ b/node_modules/cross-spawn/lib/util/resolveCommand.js @@ -0,0 +1,52 @@ +'use strict'; + +const path = require('path'); +const which = require('which'); +const getPathKey = require('path-key'); + +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.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.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved); +    } + +    return resolved; +} + +function resolveCommand(parsed) { +    return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); +} + +module.exports = resolveCommand; | 
