index.js 6.1 KB

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