11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst path = require('path'); 41cb0ef41Sopenharmony_ciconst resolveCommand = require('./util/resolveCommand'); 51cb0ef41Sopenharmony_ciconst escape = require('./util/escape'); 61cb0ef41Sopenharmony_ciconst readShebang = require('./util/readShebang'); 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ciconst isWin = process.platform === 'win32'; 91cb0ef41Sopenharmony_ciconst isExecutableRegExp = /\.(?:com|exe)$/i; 101cb0ef41Sopenharmony_ciconst isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_cifunction detectShebang(parsed) { 131cb0ef41Sopenharmony_ci parsed.file = resolveCommand(parsed); 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_ci const shebang = parsed.file && readShebang(parsed.file); 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_ci if (shebang) { 181cb0ef41Sopenharmony_ci parsed.args.unshift(parsed.file); 191cb0ef41Sopenharmony_ci parsed.command = shebang; 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci return resolveCommand(parsed); 221cb0ef41Sopenharmony_ci } 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci return parsed.file; 251cb0ef41Sopenharmony_ci} 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_cifunction parseNonShell(parsed) { 281cb0ef41Sopenharmony_ci if (!isWin) { 291cb0ef41Sopenharmony_ci return parsed; 301cb0ef41Sopenharmony_ci } 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ci // Detect & add support for shebangs 331cb0ef41Sopenharmony_ci const commandFile = detectShebang(parsed); 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci // We don't need a shell if the command filename is an executable 361cb0ef41Sopenharmony_ci const needsShell = !isExecutableRegExp.test(commandFile); 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci // If a shell is required, use cmd.exe and take care of escaping everything correctly 391cb0ef41Sopenharmony_ci // Note that `forceShell` is an hidden option used only in tests 401cb0ef41Sopenharmony_ci if (parsed.options.forceShell || needsShell) { 411cb0ef41Sopenharmony_ci // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/` 421cb0ef41Sopenharmony_ci // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument 431cb0ef41Sopenharmony_ci // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called, 441cb0ef41Sopenharmony_ci // we need to double escape them 451cb0ef41Sopenharmony_ci const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar) 481cb0ef41Sopenharmony_ci // This is necessary otherwise it will always fail with ENOENT in those cases 491cb0ef41Sopenharmony_ci parsed.command = path.normalize(parsed.command); 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci // Escape command & arguments 521cb0ef41Sopenharmony_ci parsed.command = escape.command(parsed.command); 531cb0ef41Sopenharmony_ci parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci const shellCommand = [parsed.command].concat(parsed.args).join(' '); 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; 581cb0ef41Sopenharmony_ci parsed.command = process.env.comspec || 'cmd.exe'; 591cb0ef41Sopenharmony_ci parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped 601cb0ef41Sopenharmony_ci } 611cb0ef41Sopenharmony_ci 621cb0ef41Sopenharmony_ci return parsed; 631cb0ef41Sopenharmony_ci} 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_cifunction parse(command, args, options) { 661cb0ef41Sopenharmony_ci // Normalize arguments, similar to nodejs 671cb0ef41Sopenharmony_ci if (args && !Array.isArray(args)) { 681cb0ef41Sopenharmony_ci options = args; 691cb0ef41Sopenharmony_ci args = null; 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci args = args ? args.slice(0) : []; // Clone array to avoid changing the original 731cb0ef41Sopenharmony_ci options = Object.assign({}, options); // Clone object to avoid changing the original 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_ci // Build our parsed object 761cb0ef41Sopenharmony_ci const parsed = { 771cb0ef41Sopenharmony_ci command, 781cb0ef41Sopenharmony_ci args, 791cb0ef41Sopenharmony_ci options, 801cb0ef41Sopenharmony_ci file: undefined, 811cb0ef41Sopenharmony_ci original: { 821cb0ef41Sopenharmony_ci command, 831cb0ef41Sopenharmony_ci args, 841cb0ef41Sopenharmony_ci }, 851cb0ef41Sopenharmony_ci }; 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci // Delegate further parsing to shell or non-shell 881cb0ef41Sopenharmony_ci return options.shell ? parsed : parseNonShell(parsed); 891cb0ef41Sopenharmony_ci} 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_cimodule.exports = parse; 92