program.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Jake JavaScript build tool
  3. * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. let fs = require('fs');
  19. let parseargs = require('./parseargs');
  20. let utils = require('./utils');
  21. let Program;
  22. let usage = require('fs').readFileSync(`${__dirname}/../usage.txt`).toString();
  23. let { Task } = require('./task/task');
  24. function die(msg) {
  25. console.log(msg);
  26. process.stdout.write('', function () {
  27. process.stderr.write('', function () {
  28. process.exit();
  29. });
  30. });
  31. }
  32. let preempts = {
  33. version: function () {
  34. die(jake.version);
  35. },
  36. help: function () {
  37. die(usage);
  38. }
  39. };
  40. let AVAILABLE_OPTS = [
  41. { full: 'jakefile',
  42. abbr: 'f',
  43. expectValue: true
  44. },
  45. { full: 'quiet',
  46. abbr: 'q',
  47. expectValue: false
  48. },
  49. { full: 'directory',
  50. abbr: 'C',
  51. expectValue: true
  52. },
  53. { full: 'always-make',
  54. abbr: 'B',
  55. expectValue: false
  56. },
  57. { full: 'tasks',
  58. abbr: 'T',
  59. expectValue: false,
  60. allowValue: true
  61. },
  62. // Alias t
  63. { full: 'tasks',
  64. abbr: 't',
  65. expectValue: false,
  66. allowValue: true
  67. },
  68. // Alias ls
  69. { full: 'tasks',
  70. abbr: 'ls',
  71. expectValue: false,
  72. allowValue: true
  73. },
  74. { full: 'help',
  75. abbr: 'h',
  76. },
  77. { full: 'version',
  78. abbr: 'V',
  79. },
  80. // Alias lowercase v
  81. { full: 'version',
  82. abbr: 'v',
  83. },
  84. { full: 'jakelibdir',
  85. abbr: 'J',
  86. expectValue: true
  87. },
  88. { full: 'allow-rejection',
  89. abbr: 'ar',
  90. expectValue: false
  91. }
  92. ];
  93. Program = function () {
  94. this.availableOpts = AVAILABLE_OPTS;
  95. this.opts = {};
  96. this.taskNames = null;
  97. this.taskArgs = null;
  98. this.envVars = null;
  99. this.die = die;
  100. };
  101. Program.prototype = new (function () {
  102. this.handleErr = function (err) {
  103. if (jake.listeners('error').length !== 0) {
  104. jake.emit('error', err);
  105. return;
  106. }
  107. if (jake.listeners('error').length) {
  108. jake.emit('error', err);
  109. return;
  110. }
  111. utils.logger.error('jake aborted.');
  112. if (err.stack) {
  113. utils.logger.error(err.stack);
  114. }
  115. else {
  116. utils.logger.error(err.message);
  117. }
  118. process.stdout.write('', function () {
  119. process.stderr.write('', function () {
  120. jake.errorCode = jake.errorCode || 1;
  121. process.exit(jake.errorCode);
  122. });
  123. });
  124. };
  125. this.parseArgs = function (args) {
  126. let result = (new parseargs.Parser(this.availableOpts)).parse(args);
  127. this.setOpts(result.opts);
  128. this.setTaskNames(result.taskNames);
  129. this.setEnvVars(result.envVars);
  130. };
  131. this.setOpts = function (options) {
  132. let opts = options || {};
  133. Object.assign(this.opts, opts);
  134. };
  135. this.internalOpts = function (options) {
  136. this.availableOpts = this.availableOpts.concat(options);
  137. };
  138. this.autocompletions = function (cur) {
  139. let p; let i; let task;
  140. let commonPrefix = '';
  141. let matches = [];
  142. for (p in jake.Task) {
  143. task = jake.Task[p];
  144. if (
  145. 'fullName' in task
  146. && (
  147. // if empty string, program converts to true
  148. cur === true ||
  149. task.fullName.indexOf(cur) === 0
  150. )
  151. ) {
  152. if (matches.length === 0) {
  153. commonPrefix = task.fullName;
  154. }
  155. else {
  156. for (i = commonPrefix.length; i > -1; --i) {
  157. commonPrefix = commonPrefix.substr(0, i);
  158. if (task.fullName.indexOf(commonPrefix) === 0) {
  159. break;
  160. }
  161. }
  162. }
  163. matches.push(task.fullName);
  164. }
  165. }
  166. if (matches.length > 1 && commonPrefix === cur) {
  167. matches.unshift('yes-space');
  168. }
  169. else {
  170. matches.unshift('no-space');
  171. }
  172. process.stdout.write(matches.join(' '));
  173. };
  174. this.setTaskNames = function (names) {
  175. if (names && !Array.isArray(names)) {
  176. throw new Error('Task names must be an array');
  177. }
  178. this.taskNames = (names && names.length) ? names : ['default'];
  179. };
  180. this.setEnvVars = function (vars) {
  181. this.envVars = vars || null;
  182. };
  183. this.firstPreemptiveOption = function () {
  184. let opts = this.opts;
  185. for (let p in opts) {
  186. if (preempts[p]) {
  187. return preempts[p];
  188. }
  189. }
  190. return false;
  191. };
  192. this.init = function (configuration) {
  193. let self = this;
  194. let config = configuration || {};
  195. if (config.options) {
  196. this.setOpts(config.options);
  197. }
  198. if (config.taskNames) {
  199. this.setTaskNames(config.taskNames);
  200. }
  201. if (config.envVars) {
  202. this.setEnvVars(config.envVars);
  203. }
  204. process.addListener('uncaughtException', function (err) {
  205. self.handleErr(err);
  206. });
  207. if (!this.opts['allow-rejection']) {
  208. process.addListener('unhandledRejection', (reason, promise) => {
  209. utils.logger.error('Unhandled rejection at:', promise, 'reason:', reason);
  210. self.handleErr(reason);
  211. });
  212. }
  213. if (this.envVars) {
  214. Object.assign(process.env, this.envVars);
  215. }
  216. };
  217. this.run = function () {
  218. let rootTask;
  219. let taskNames;
  220. let dirname;
  221. let opts = this.opts;
  222. if (opts.autocomplete) {
  223. return this.autocompletions(opts['autocomplete-cur'], opts['autocomplete-prev']);
  224. }
  225. // Run with `jake -T`, just show descriptions
  226. if (opts.tasks) {
  227. return jake.showAllTaskDescriptions(opts.tasks);
  228. }
  229. taskNames = this.taskNames;
  230. if (!(Array.isArray(taskNames) && taskNames.length)) {
  231. throw new Error('Please pass jake.runTasks an array of task-names');
  232. }
  233. // Set working dir
  234. dirname = opts.directory;
  235. if (dirname) {
  236. if (fs.existsSync(dirname) &&
  237. fs.statSync(dirname).isDirectory()) {
  238. process.chdir(dirname);
  239. }
  240. else {
  241. throw new Error(dirname + ' is not a valid directory path');
  242. }
  243. }
  244. rootTask = task(Task.ROOT_TASK_NAME, taskNames, function () {});
  245. rootTask._internal = true;
  246. rootTask.once('complete', function () {
  247. jake.emit('complete');
  248. });
  249. jake.emit('start');
  250. rootTask.invoke();
  251. };
  252. })();
  253. module.exports.Program = Program;