Blame view

node_modules/cross-spawn/lib/parse.js 3.8 KB
ce4c83ff   wxy   初始提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  'use strict';
  
  var resolveCommand = require('./util/resolveCommand');
  var hasEmptyArgumentBug = require('./util/hasEmptyArgumentBug');
  var escapeArgument = require('./util/escapeArgument');
  var escapeCommand = require('./util/escapeCommand');
  var readShebang = require('./util/readShebang');
  
  var isWin = process.platform === 'win32';
  var skipShellRegExp = /\.(?:com|exe)$/i;
  
  // Supported in Node >= 6 and >= 4.8
  var supportsShellOption = parseInt(process.version.substr(1).split('.')[0], 10) >= 6 ||
   parseInt(process.version.substr(1).split('.')[0], 10) === 4 && parseInt(process.version.substr(1).split('.')[1], 10) >= 8;
  
  function parseNonShell(parsed) {
      var shebang;
      var needsShell;
      var applyQuotes;
  
      if (!isWin) {
          return parsed;
      }
  
      // Detect & add support for shebangs
      parsed.file = resolveCommand(parsed.command);
      parsed.file = parsed.file || resolveCommand(parsed.command, true);
      shebang = parsed.file && readShebang(parsed.file);
  
      if (shebang) {
          parsed.args.unshift(parsed.file);
          parsed.command = shebang;
          needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(resolveCommand(shebang) || resolveCommand(shebang, true));
      } else {
          needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(parsed.file);
      }
  
      // If a shell is required, use cmd.exe and take care of escaping everything correctly
      if (needsShell) {
          // Escape command & arguments
          applyQuotes = (parsed.command !== 'echo');  // Do not quote arguments for the special "echo" command
          parsed.command = escapeCommand(parsed.command);
          parsed.args = parsed.args.map(function (arg) {
              return escapeArgument(arg, applyQuotes);
          });
  
          // Make use of cmd.exe
          parsed.args = ['/d', '/s', '/c', '"' + parsed.command + (parsed.args.length ? ' ' + parsed.args.join(' ') : '') + '"'];
          parsed.command = process.env.comspec || 'cmd.exe';
          parsed.options.windowsVerbatimArguments = true;  // Tell node's spawn that the arguments are already escaped
      }
  
      return parsed;
  }
  
  function parseShell(parsed) {
      var shellCommand;
  
      // If node supports the shell option, there's no need to mimic its behavior
      if (supportsShellOption) {
          return parsed;
      }
  
      // Mimic node shell option, see: https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
      shellCommand = [parsed.command].concat(parsed.args).join(' ');
  
      if (isWin) {
          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 {
              parsed.command = '/bin/sh';
          }
  
          parsed.args = ['-c', shellCommand];
      }
  
      return parsed;
  }
  
  // ------------------------------------------------
  
  function parse(command, args, options) {
      var parsed;
  
      // 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 = options || {};
  
      // Build our parsed object
      parsed = {
          command: command,
          args: args,
          options: options,
          file: undefined,
          original: command,
      };
  
      // Delegate further parsing to shell or non-shell
      return options.shell ? parseShell(parsed) : parseNonShell(parsed);
  }
  
  module.exports = parse;