Blame view

node_modules/bower/lib/util/cmd.js 3.6 KB
2dda2e10   Administrator   generator ignore
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
114
115
116
117
118
119
120
  var cp = require('child_process');
  var path = require('path');
  var Q = require('q');
  var mout = require('mout');
  var which = require('which');
  var PThrottler = require('p-throttler');
  var createError = require('./createError');
  
  // The concurrency limit here is kind of magic. You don't really gain a lot from
  // having a large number of commands spawned at once, so it isn't super
  // important for this number to be large. Reports have shown that much more than 5
  // or 10 cause issues for corporate networks, private repos or situations where
  // internet bandwidth is limited. We're running with a concurrency of 5 until
  // 1.4.X is released, at which time we'll move to what was discussed in #1262
  // https://github.com/bower/bower/pull/1262
  var throttler = new PThrottler(5);
  
  var winBatchExtensions;
  var winWhichCache;
  var isWin = process.platform === 'win32';
  
  if (isWin) {
      winBatchExtensions = ['.bat', '.cmd'];
      winWhichCache = {};
  }
  
  function getWindowsCommand(command) {
      var fullCommand;
      var extension;
  
      // Do we got the value converted in the cache?
      if (mout.object.hasOwn(winWhichCache, command)) {
          return winWhichCache[command];
      }
  
      // Use which to retrieve the full command, which puts the extension in the end
      try {
          fullCommand = which.sync(command);
      } catch (err) {
          return winWhichCache[command] = command;
      }
  
      extension = path.extname(fullCommand).toLowerCase();
  
      // Does it need to be converted?
      if (winBatchExtensions.indexOf(extension) === -1) {
          return winWhichCache[command] = command;
      }
  
      return winWhichCache[command] = fullCommand;
  }
  
  // Executes a shell command, buffering the stdout and stderr
  // If an error occurs, a meaningful error is generated
  // Returns a promise that gets fulfilled if the command succeeds
  // or rejected if it fails
  function executeCmd(command, args, options) {
      var process;
      var stderr = '';
      var stdout = '';
      var deferred = Q.defer();
  
      // Windows workaround for .bat and .cmd files, see #626
      if (isWin) {
          command = getWindowsCommand(command);
      }
  
      // Buffer output, reporting progress
      process = cp.spawn(command, args, options);
      process.stdout.on('data', function (data) {
          data = data.toString();
          deferred.notify(data);
          stdout += data;
      });
      process.stderr.on('data', function (data) {
          data = data.toString();
          deferred.notify(data);
          stderr += data;
      });
  
      // If there is an error spawning the command, reject the promise
      process.on('error', function (error) {
          return deferred.reject(error);
      });
  
      // Listen to the close event instead of exit
      // They are similar but close ensures that streams are flushed
      process.on('close', function (code) {
          var fullCommand;
          var error;
  
          if (code) {
              // Generate the full command to be presented in the error message
              if (!Array.isArray(args)) {
                  args = [];
              }
  
              fullCommand = command;
              fullCommand += args.length ? ' ' + args.join(' ') : '';
  
              // Build the error instance
              error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code + '\n' + stderr, 'ECMDERR', {
                  details: stderr,
                  exitCode: code
              });
  
              return deferred.reject(error);
          }
  
          return deferred.resolve([stdout, stderr]);
      });
  
      return deferred.promise;
  }
  
  function cmd(command, args, options) {
      return throttler.enqueue(executeCmd.bind(null, command, args, options));
  }
  
  module.exports = cmd;