index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. * grunt
  3. * http://gruntjs.com/
  4. *
  5. * Copyright (c) 2016 "Cowboy" Ben Alman
  6. * Licensed under the MIT license.
  7. * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
  8. */
  9. 'use strict';
  10. // Nodejs libs.
  11. var spawn = require('child_process').spawn;
  12. var nodeUtil = require('util');
  13. var path = require('path');
  14. // The module to be exported.
  15. var util = module.exports = {};
  16. util.namespace = require('getobject');
  17. // External libs.
  18. util.hooker = require('hooker');
  19. util.async = require('async');
  20. // Dont pollute other lodash: https://github.com/gruntjs/grunt-legacy-util/issues/17
  21. var _ = util._ = require('lodash').runInContext();
  22. var which = require('which').sync;
  23. // Instead of process.exit. See https://github.com/cowboy/node-exit
  24. util.exit = require('exit');
  25. // Mixin Underscore.string methods.
  26. _.str = require('underscore.string');
  27. _.mixin(_.str.exports());
  28. // Return a function that normalizes the given function either returning a
  29. // value or accepting a "done" callback that accepts a single value.
  30. util.callbackify = function(fn) {
  31. return function callbackable() {
  32. // Invoke original function, getting its result.
  33. var result = fn.apply(this, arguments);
  34. // If the same number or less arguments were specified than fn accepts,
  35. // assume the "done" callback was already handled.
  36. var length = arguments.length;
  37. if (length === fn.length) { return; }
  38. // Otherwise, if the last argument is a function, assume it is a "done"
  39. // callback and call it.
  40. var done = arguments[length - 1];
  41. if (typeof done === 'function') { done(result); }
  42. };
  43. };
  44. // Create a new Error object, with an origError property that will be dumped
  45. // if grunt was run with the --debug=9 option.
  46. util.error = function(err, origError) {
  47. if (!nodeUtil.isError(err)) { err = new Error(err); }
  48. if (origError) { err.origError = origError; }
  49. return err;
  50. };
  51. // The line feed char for the current system.
  52. util.linefeed = process.platform === 'win32' ? '\r\n' : '\n';
  53. // Normalize linefeeds in a string.
  54. util.normalizelf = function(str) {
  55. return str.replace(/\r\n|\n/g, util.linefeed);
  56. };
  57. // What "kind" is a value?
  58. // I really need to rework https://github.com/cowboy/javascript-getclass
  59. var kindsOf = {};
  60. 'Number String Boolean Function RegExp Array Date Error'.split(' ').forEach(function(k) {
  61. kindsOf['[object ' + k + ']'] = k.toLowerCase();
  62. });
  63. util.kindOf = function(value) {
  64. // Null or undefined.
  65. if (value == null) { return String(value); }
  66. // Everything else.
  67. return kindsOf[kindsOf.toString.call(value)] || 'object';
  68. };
  69. // Coerce something to an Array.
  70. util.toArray = _.toArray;
  71. // Return the string `str` repeated `n` times.
  72. util.repeat = function(n, str) {
  73. return new Array(n + 1).join(str || ' ');
  74. };
  75. // Given str of "a/b", If n is 1, return "a" otherwise "b".
  76. util.pluralize = function(n, str, separator) {
  77. var parts = str.split(separator || '/');
  78. return n === 1 ? (parts[0] || '') : (parts[1] || '');
  79. };
  80. // Recurse through objects and arrays, executing fn for each non-object.
  81. util.recurse = function(value, fn, fnContinue) {
  82. function recurse(value, fn, fnContinue, state) {
  83. var error;
  84. if (state.objs.indexOf(value) !== -1) {
  85. error = new Error('Circular reference detected (' + state.path + ')');
  86. error.path = state.path;
  87. throw error;
  88. }
  89. var obj, key;
  90. if (fnContinue && fnContinue(value) === false) {
  91. // Skip value if necessary.
  92. return value;
  93. } else if (util.kindOf(value) === 'array') {
  94. // If value is an array, recurse.
  95. return value.map(function(item, index) {
  96. return recurse(item, fn, fnContinue, {
  97. objs: state.objs.concat([value]),
  98. path: state.path + '[' + index + ']',
  99. });
  100. });
  101. } else if (util.kindOf(value) === 'object' && !Buffer.isBuffer(value)) {
  102. // If value is an object, recurse.
  103. obj = {};
  104. for (key in value) {
  105. obj[key] = recurse(value[key], fn, fnContinue, {
  106. objs: state.objs.concat([value]),
  107. path: state.path + (/\W/.test(key) ? '["' + key + '"]' : '.' + key),
  108. });
  109. }
  110. return obj;
  111. } else {
  112. // Otherwise pass value into fn and return.
  113. return fn(value);
  114. }
  115. }
  116. return recurse(value, fn, fnContinue, {objs: [], path: ''});
  117. };
  118. // Spawn a child process, capturing its stdout and stderr.
  119. util.spawn = function(opts, done) {
  120. // Build a result object and pass it (among other things) into the
  121. // done function.
  122. var callDone = function(code, stdout, stderr) {
  123. // Remove trailing whitespace (newline)
  124. stdout = _.rtrim(stdout);
  125. stderr = _.rtrim(stderr);
  126. // Create the result object.
  127. var result = {
  128. stdout: stdout,
  129. stderr: stderr,
  130. code: code,
  131. toString: function() {
  132. if (code === 0) {
  133. return stdout;
  134. } else if ('fallback' in opts) {
  135. return opts.fallback;
  136. } else if (opts.grunt) {
  137. // grunt.log.error uses standard out, to be fixed in 0.5.
  138. return stderr || stdout;
  139. }
  140. return stderr;
  141. }
  142. };
  143. // On error (and no fallback) pass an error object, otherwise pass null.
  144. done(code === 0 || 'fallback' in opts ? null : new Error(stderr), result, code);
  145. };
  146. var cmd, args;
  147. var pathSeparatorRe = /[\\\/]/g;
  148. if (opts.grunt) {
  149. cmd = process.execPath;
  150. args = process.execArgv.concat(process.argv[1], opts.args);
  151. } else {
  152. // On Windows, child_process.spawn will only file .exe files in the PATH,
  153. // not other executable types (grunt issue #155).
  154. try {
  155. if (!pathSeparatorRe.test(opts.cmd)) {
  156. // Only use which if cmd has no path component.
  157. cmd = which(opts.cmd);
  158. } else {
  159. cmd = opts.cmd.replace(pathSeparatorRe, path.sep);
  160. }
  161. } catch (err) {
  162. callDone(127, '', String(err));
  163. return;
  164. }
  165. args = opts.args || [];
  166. }
  167. var child = spawn(cmd, args, opts.opts);
  168. var stdout = new Buffer('');
  169. var stderr = new Buffer('');
  170. if (child.stdout) {
  171. child.stdout.on('data', function(buf) {
  172. stdout = Buffer.concat([stdout, new Buffer(buf)]);
  173. });
  174. }
  175. if (child.stderr) {
  176. child.stderr.on('data', function(buf) {
  177. stderr = Buffer.concat([stderr, new Buffer(buf)]);
  178. });
  179. }
  180. child.on('close', function(code) {
  181. callDone(code, stdout.toString(), stderr.toString());
  182. });
  183. return child;
  184. };