| 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 |
|
|---|
| 19 | let fs = require('fs');
|
|---|
| 20 | let parseargs = require('./parseargs');
|
|---|
| 21 | let utils = require('./utils');
|
|---|
| 22 | let Program;
|
|---|
| 23 | let usage = require('fs').readFileSync(`${__dirname}/../usage.txt`).toString();
|
|---|
| 24 | let { Task } = require('./task/task');
|
|---|
| 25 |
|
|---|
| 26 | function die(msg) {
|
|---|
| 27 | console.log(msg);
|
|---|
| 28 | process.stdout.write('', function () {
|
|---|
| 29 | process.stderr.write('', function () {
|
|---|
| 30 | process.exit();
|
|---|
| 31 | });
|
|---|
| 32 | });
|
|---|
| 33 | }
|
|---|
| 34 |
|
|---|
| 35 | let preempts = {
|
|---|
| 36 | version: function () {
|
|---|
| 37 | die(jake.version);
|
|---|
| 38 | },
|
|---|
| 39 | help: function () {
|
|---|
| 40 | die(usage);
|
|---|
| 41 | }
|
|---|
| 42 | };
|
|---|
| 43 |
|
|---|
| 44 | let AVAILABLE_OPTS = [
|
|---|
| 45 | { full: 'jakefile',
|
|---|
| 46 | abbr: 'f',
|
|---|
| 47 | expectValue: true
|
|---|
| 48 | },
|
|---|
| 49 | { full: 'quiet',
|
|---|
| 50 | abbr: 'q',
|
|---|
| 51 | expectValue: false
|
|---|
| 52 | },
|
|---|
| 53 | { full: 'directory',
|
|---|
| 54 | abbr: 'C',
|
|---|
| 55 | expectValue: true
|
|---|
| 56 | },
|
|---|
| 57 | { full: 'always-make',
|
|---|
| 58 | abbr: 'B',
|
|---|
| 59 | expectValue: false
|
|---|
| 60 | },
|
|---|
| 61 | { full: 'tasks',
|
|---|
| 62 | abbr: 'T',
|
|---|
| 63 | expectValue: false,
|
|---|
| 64 | allowValue: true
|
|---|
| 65 | },
|
|---|
| 66 | // Alias t
|
|---|
| 67 | { full: 'tasks',
|
|---|
| 68 | abbr: 't',
|
|---|
| 69 | expectValue: false,
|
|---|
| 70 | allowValue: true
|
|---|
| 71 | },
|
|---|
| 72 | // Alias ls
|
|---|
| 73 | { full: 'tasks',
|
|---|
| 74 | abbr: 'ls',
|
|---|
| 75 | expectValue: false,
|
|---|
| 76 | allowValue: true
|
|---|
| 77 | },
|
|---|
| 78 | { full: 'help',
|
|---|
| 79 | abbr: 'h',
|
|---|
| 80 | },
|
|---|
| 81 | { full: 'version',
|
|---|
| 82 | abbr: 'V',
|
|---|
| 83 | },
|
|---|
| 84 | // Alias lowercase v
|
|---|
| 85 | { full: 'version',
|
|---|
| 86 | abbr: 'v',
|
|---|
| 87 | },
|
|---|
| 88 | { full: 'jakelibdir',
|
|---|
| 89 | abbr: 'J',
|
|---|
| 90 | expectValue: true
|
|---|
| 91 | },
|
|---|
| 92 | { full: 'allow-rejection',
|
|---|
| 93 | abbr: 'ar',
|
|---|
| 94 | expectValue: false
|
|---|
| 95 | }
|
|---|
| 96 | ];
|
|---|
| 97 |
|
|---|
| 98 | Program = function () {
|
|---|
| 99 | this.availableOpts = AVAILABLE_OPTS;
|
|---|
| 100 | this.opts = {};
|
|---|
| 101 | this.taskNames = null;
|
|---|
| 102 | this.taskArgs = null;
|
|---|
| 103 | this.envVars = null;
|
|---|
| 104 | this.die = die;
|
|---|
| 105 | };
|
|---|
| 106 |
|
|---|
| 107 | Program.prototype = new (function () {
|
|---|
| 108 |
|
|---|
| 109 | this.handleErr = function (err) {
|
|---|
| 110 | if (jake.listeners('error').length !== 0) {
|
|---|
| 111 | jake.emit('error', err);
|
|---|
| 112 | return;
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | if (jake.listeners('error').length) {
|
|---|
| 116 | jake.emit('error', err);
|
|---|
| 117 | return;
|
|---|
| 118 | }
|
|---|
| 119 |
|
|---|
| 120 | utils.logger.error('jake aborted.');
|
|---|
| 121 | if (err.stack) {
|
|---|
| 122 | utils.logger.error(err.stack);
|
|---|
| 123 | }
|
|---|
| 124 | else {
|
|---|
| 125 | utils.logger.error(err.message);
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | process.stdout.write('', function () {
|
|---|
| 129 | process.stderr.write('', function () {
|
|---|
| 130 | jake.errorCode = jake.errorCode || 1;
|
|---|
| 131 | process.exit(jake.errorCode);
|
|---|
| 132 | });
|
|---|
| 133 | });
|
|---|
| 134 | };
|
|---|
| 135 |
|
|---|
| 136 | this.parseArgs = function (args) {
|
|---|
| 137 | let result = (new parseargs.Parser(this.availableOpts)).parse(args);
|
|---|
| 138 | this.setOpts(result.opts);
|
|---|
| 139 | this.setTaskNames(result.taskNames);
|
|---|
| 140 | this.setEnvVars(result.envVars);
|
|---|
| 141 | };
|
|---|
| 142 |
|
|---|
| 143 | this.setOpts = function (options) {
|
|---|
| 144 | let opts = options || {};
|
|---|
| 145 | Object.assign(this.opts, opts);
|
|---|
| 146 | };
|
|---|
| 147 |
|
|---|
| 148 | this.internalOpts = function (options) {
|
|---|
| 149 | this.availableOpts = this.availableOpts.concat(options);
|
|---|
| 150 | };
|
|---|
| 151 |
|
|---|
| 152 | this.autocompletions = function (cur) {
|
|---|
| 153 | let p; let i; let task;
|
|---|
| 154 | let commonPrefix = '';
|
|---|
| 155 | let matches = [];
|
|---|
| 156 |
|
|---|
| 157 | for (p in jake.Task) {
|
|---|
| 158 | task = jake.Task[p];
|
|---|
| 159 | if (
|
|---|
| 160 | 'fullName' in task
|
|---|
| 161 | && (
|
|---|
| 162 | // if empty string, program converts to true
|
|---|
| 163 | cur === true ||
|
|---|
| 164 | task.fullName.indexOf(cur) === 0
|
|---|
| 165 | )
|
|---|
| 166 | ) {
|
|---|
| 167 | if (matches.length === 0) {
|
|---|
| 168 | commonPrefix = task.fullName;
|
|---|
| 169 | }
|
|---|
| 170 | else {
|
|---|
| 171 | for (i = commonPrefix.length; i > -1; --i) {
|
|---|
| 172 | commonPrefix = commonPrefix.substr(0, i);
|
|---|
| 173 | if (task.fullName.indexOf(commonPrefix) === 0) {
|
|---|
| 174 | break;
|
|---|
| 175 | }
|
|---|
| 176 | }
|
|---|
| 177 | }
|
|---|
| 178 | matches.push(task.fullName);
|
|---|
| 179 | }
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | if (matches.length > 1 && commonPrefix === cur) {
|
|---|
| 183 | matches.unshift('yes-space');
|
|---|
| 184 | }
|
|---|
| 185 | else {
|
|---|
| 186 | matches.unshift('no-space');
|
|---|
| 187 | }
|
|---|
| 188 |
|
|---|
| 189 | process.stdout.write(matches.join(' '));
|
|---|
| 190 | };
|
|---|
| 191 |
|
|---|
| 192 | this.setTaskNames = function (names) {
|
|---|
| 193 | if (names && !Array.isArray(names)) {
|
|---|
| 194 | throw new Error('Task names must be an array');
|
|---|
| 195 | }
|
|---|
| 196 | this.taskNames = (names && names.length) ? names : ['default'];
|
|---|
| 197 | };
|
|---|
| 198 |
|
|---|
| 199 | this.setEnvVars = function (vars) {
|
|---|
| 200 | this.envVars = vars || null;
|
|---|
| 201 | };
|
|---|
| 202 |
|
|---|
| 203 | this.firstPreemptiveOption = function () {
|
|---|
| 204 | let opts = this.opts;
|
|---|
| 205 | for (let p in opts) {
|
|---|
| 206 | if (preempts[p]) {
|
|---|
| 207 | return preempts[p];
|
|---|
| 208 | }
|
|---|
| 209 | }
|
|---|
| 210 | return false;
|
|---|
| 211 | };
|
|---|
| 212 |
|
|---|
| 213 | this.init = function (configuration) {
|
|---|
| 214 | let self = this;
|
|---|
| 215 | let config = configuration || {};
|
|---|
| 216 | if (config.options) {
|
|---|
| 217 | this.setOpts(config.options);
|
|---|
| 218 | }
|
|---|
| 219 | if (config.taskNames) {
|
|---|
| 220 | this.setTaskNames(config.taskNames);
|
|---|
| 221 | }
|
|---|
| 222 | if (config.envVars) {
|
|---|
| 223 | this.setEnvVars(config.envVars);
|
|---|
| 224 | }
|
|---|
| 225 | process.addListener('uncaughtException', function (err) {
|
|---|
| 226 | self.handleErr(err);
|
|---|
| 227 | });
|
|---|
| 228 | if (!this.opts['allow-rejection']) {
|
|---|
| 229 | process.addListener('unhandledRejection', (reason, promise) => {
|
|---|
| 230 | utils.logger.error('Unhandled rejection at:', promise, 'reason:', reason);
|
|---|
| 231 | self.handleErr(reason);
|
|---|
| 232 | });
|
|---|
| 233 | }
|
|---|
| 234 | if (this.envVars) {
|
|---|
| 235 | Object.assign(process.env, this.envVars);
|
|---|
| 236 | }
|
|---|
| 237 | };
|
|---|
| 238 |
|
|---|
| 239 | this.run = function () {
|
|---|
| 240 | let rootTask;
|
|---|
| 241 | let taskNames;
|
|---|
| 242 | let dirname;
|
|---|
| 243 | let opts = this.opts;
|
|---|
| 244 |
|
|---|
| 245 | if (opts.autocomplete) {
|
|---|
| 246 | return this.autocompletions(opts['autocomplete-cur'], opts['autocomplete-prev']);
|
|---|
| 247 | }
|
|---|
| 248 | // Run with `jake -T`, just show descriptions
|
|---|
| 249 | if (opts.tasks) {
|
|---|
| 250 | return jake.showAllTaskDescriptions(opts.tasks);
|
|---|
| 251 | }
|
|---|
| 252 |
|
|---|
| 253 | taskNames = this.taskNames;
|
|---|
| 254 | if (!(Array.isArray(taskNames) && taskNames.length)) {
|
|---|
| 255 | throw new Error('Please pass jake.runTasks an array of task-names');
|
|---|
| 256 | }
|
|---|
| 257 |
|
|---|
| 258 | // Set working dir
|
|---|
| 259 | dirname = opts.directory;
|
|---|
| 260 | if (dirname) {
|
|---|
| 261 | if (fs.existsSync(dirname) &&
|
|---|
| 262 | fs.statSync(dirname).isDirectory()) {
|
|---|
| 263 | process.chdir(dirname);
|
|---|
| 264 | }
|
|---|
| 265 | else {
|
|---|
| 266 | throw new Error(dirname + ' is not a valid directory path');
|
|---|
| 267 | }
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | rootTask = task(Task.ROOT_TASK_NAME, taskNames, function () {});
|
|---|
| 271 | rootTask._internal = true;
|
|---|
| 272 |
|
|---|
| 273 | rootTask.once('complete', function () {
|
|---|
| 274 | jake.emit('complete');
|
|---|
| 275 | });
|
|---|
| 276 | jake.emit('start');
|
|---|
| 277 | rootTask.invoke();
|
|---|
| 278 | };
|
|---|
| 279 |
|
|---|
| 280 | })();
|
|---|
| 281 |
|
|---|
| 282 | module.exports.Program = Program;
|
|---|