Spaces:
Runtime error
Runtime error
; | |
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; | |