execute.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /*
  2. * grunt-execute
  3. * https://github.com/Bartvds/grunt-execute
  4. *
  5. * Copyright (c) 2013 Bart van der Schoor
  6. * Licensed under the MIT license.
  7. */
  8. module.exports = function (grunt) {
  9. 'use strict';
  10. var path = require('path');
  11. function executeModuleFunction(func, context, callback) {
  12. // private
  13. var isFinalised = false;
  14. var isAsync = false;
  15. var funcDone = false;
  16. var calledDone = false;
  17. var finalise = function (err, msg) {
  18. if (isFinalised) {
  19. grunt.fail.warn('executeModuleFunction() detected async flow error');
  20. return null;
  21. }
  22. isFinalised = true;
  23. if (typeof msg !== 'undefined') {
  24. grunt.log.writeln(msg);
  25. }
  26. process.nextTick(function () {
  27. callback(err);
  28. });
  29. };
  30. var async = function () {
  31. if (funcDone) {
  32. grunt.fail.warn('executeModuleFunction() call async() in main body');
  33. return null;
  34. }
  35. if (isAsync) {
  36. grunt.fail.warn('executeModuleFunction() detected multiple calls to async()');
  37. return null;
  38. }
  39. isAsync = true;
  40. return function (err, msg) {
  41. if (calledDone) {
  42. grunt.fail.warn('executeModuleFunction() detected multiple calls to the async callback');
  43. return null;
  44. }
  45. calledDone = true;
  46. if (typeof msg !== 'undefined') {
  47. grunt.log.writeln(msg);
  48. }
  49. finalise(err, msg);
  50. };
  51. };
  52. var msg = func(grunt, context.options, async);
  53. funcDone = true;
  54. if (!isAsync && !calledDone) {
  55. finalise(null, msg);
  56. }
  57. else {
  58. if (typeof msg !== 'undefined') {
  59. grunt.log.writeln(msg);
  60. }
  61. }
  62. }
  63. function executeModule(func, context, callback) {
  64. grunt.log.writeln('-> '.cyan + 'module call ' + (func.name ? func.cyan : '<anonymous>'));
  65. var start = Date.now();
  66. executeModuleFunction(func, context, function (err) {
  67. if (err) {
  68. grunt.fail.warn('-> '.cyan + 'error '.red + '(' + (Date.now() - start) + 'ms)');
  69. } else {
  70. context.counter += 1;
  71. grunt.log.writeln('-> '.cyan + 'completed ' + '(' + (Date.now() - start) + 'ms)');
  72. }
  73. callback(err);
  74. });
  75. }
  76. function executeSrcAsModule(src, context, callback) {
  77. grunt.log.writeln('-> '.cyan + 'executing module ' + src.cyan);
  78. var mod;
  79. try {
  80. mod = require(src);
  81. }
  82. catch (err) {
  83. callback(new Error('not a function'));
  84. return;
  85. }
  86. if (!grunt.util._.isFunction(mod)) {
  87. grunt.fail.warn('-> '.cyan + 'error '.red + src.cyan + ' should export a Function');
  88. callback(new Error('not a function'));
  89. return;
  90. }
  91. var start = Date.now();
  92. executeModuleFunction(mod, context, function (err) {
  93. if (err) {
  94. grunt.fail.warn('-> '.cyan + 'error '.red + src.cyan + ' (' + (Date.now() - start) + 'ms)');
  95. } else {
  96. context.counter += 1;
  97. grunt.log.writeln('-> '.cyan + 'completed ' + src.cyan + ' (' + (Date.now() - start) + 'ms)');
  98. }
  99. callback(err);
  100. });
  101. }
  102. function executeSrcAsChild(src, context, callback) {
  103. grunt.log.writeln('-> '.cyan + 'executing ' + src.cyan);
  104. var start = Date.now();
  105. var args = context.options.args ? [src].concat(context.options.args) : [src];
  106. if (context.options.nodeargs) {
  107. args = context.options.nodeargs.concat(args);
  108. }
  109. //use spawn so we don't have to depend on process.exit();
  110. var child = grunt.util.spawn(
  111. {
  112. cmd: 'node',
  113. args: args,
  114. opts: {
  115. cwd: (context.options.cwd !== null) ? context.options.cwd : path.dirname(src)
  116. }
  117. },
  118. function (error, result, code) {
  119. if (error) {
  120. grunt.fail.warn('-> '.cyan + 'error '.red + ('' + code).red + ' ' + src.cyan + ' (' + (Date.now() - start) + 'ms)');
  121. callback(error);
  122. } else if (code !== 0) {
  123. grunt.fail.warn('-> '.cyan + 'exitcode '.red + ('' + code).red + ' ' + src.cyan + ' (' + (Date.now() - start) + 'ms)');
  124. callback(new Error('bad exit code ' + code), code);
  125. } else {
  126. context.counter += 1;
  127. grunt.log.writeln('-> '.cyan + 'completed ' + src.cyan + ' (' + (Date.now() - start) + 'ms)');
  128. callback();
  129. }
  130. }
  131. );
  132. child.stdout.on('data', function (data) {
  133. grunt.log.write(data);
  134. });
  135. child.stderr.on('data', function (data) {
  136. grunt.log.write(('' + data).red);
  137. });
  138. }
  139. function pluralise(count, str) {
  140. return count + ' ' + str + (count === 1 ? '' : 's');
  141. }
  142. grunt.registerMultiTask('execute', 'execute code in node', function () {
  143. var options = this.options({
  144. cwd: '.'
  145. });
  146. //var self = this;
  147. var done = this.async();
  148. var context = {
  149. timer: Date.now(),
  150. calls: 0,
  151. files: 0,
  152. counter: 0,
  153. options: options
  154. };
  155. var checkModuleFunc = function (func, callback) {
  156. if (func) {
  157. context.calls++;
  158. executeModule(func, context, callback);
  159. }
  160. else {
  161. callback();
  162. }
  163. };
  164. var self = this;
  165. grunt.util.async.series(
  166. [
  167. function (callback) {
  168. checkModuleFunc(options.before, callback);
  169. },
  170. function (callback) {
  171. checkModuleFunc(self.data.before, callback);
  172. },
  173. function (callback) {
  174. checkModuleFunc(self.data.call, callback);
  175. },
  176. function (callback) {
  177. grunt.util.async.forEachSeries(self.filesSrc,
  178. function (src, callback) {
  179. context.files++;
  180. src = path.resolve(src);
  181. if (!src) {
  182. grunt.fail.warn('undefined src parameter');
  183. return false;
  184. }
  185. if (!grunt.file.exists(src)) {
  186. grunt.fail.warn('file does not exist ' + src);
  187. return false;
  188. }
  189. if (options.module) {
  190. executeSrcAsModule(src, context, callback);
  191. }
  192. else {
  193. executeSrcAsChild(src, context, callback);
  194. }
  195. },
  196. callback, self);
  197. },
  198. function (callback) {
  199. checkModuleFunc(self.data.after, callback);
  200. },
  201. function (callback) {
  202. checkModuleFunc(options.after, callback);
  203. }
  204. ],
  205. function (err) {
  206. grunt.log.writeln('');
  207. if (err) {
  208. grunt.log.writeln(err);
  209. done(false);
  210. }
  211. else {
  212. grunt.log.ok('' + pluralise(context.files, 'file') + ' and ' + pluralise(context.calls, 'call') + ' executed (' + (Date.now() - context.timer) + 'ms)\n');
  213. done();
  214. }
  215. }
  216. );
  217. });
  218. };