jake.js
9.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
if (!global.jake) {
let EventEmitter = require('events').EventEmitter;
// And so it begins
global.jake = new EventEmitter();
let fs = require('fs');
let chalk = require('chalk');
let taskNs = require('./task');
let Task = taskNs.Task;
let FileTask = taskNs.FileTask;
let DirectoryTask = taskNs.DirectoryTask;
let Rule = require('./rule').Rule;
let Namespace = require('./namespace').Namespace;
let RootNamespace = require('./namespace').RootNamespace;
let api = require('./api');
let utils = require('./utils');
let Program = require('./program').Program;
let loader = require('./loader')();
let pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString());
const MAX_RULE_RECURSION_LEVEL = 16;
// Globalize jake and top-level API methods (e.g., `task`, `desc`)
Object.assign(global, api);
// Copy utils onto base jake
jake.logger = utils.logger;
jake.exec = utils.exec;
// File utils should be aliased directly on base jake as well
Object.assign(jake, utils.file);
// Also add top-level API methods to exported object for those who don't want to
// use the globals (`file` here will overwrite the 'file' utils namespace)
Object.assign(jake, api);
Object.assign(jake, new (function () {
this._invocationChain = [];
this._taskTimeout = 30000;
// Public properties
// =================
this.version = pkg.version;
// Used when Jake exits with a specific error-code
this.errorCode = null;
// Loads Jakefiles/jakelibdirs
this.loader = loader;
// The root of all ... namespaces
this.rootNamespace = new RootNamespace();
// Non-namespaced tasks are placed into the default
this.defaultNamespace = this.rootNamespace;
// Start in the default
this.currentNamespace = this.defaultNamespace;
// Saves the description created by a 'desc' call that prefaces a
// 'task' call that defines a task.
this.currentTaskDescription = null;
this.program = new Program();
this.FileList = require('filelist').FileList;
this.PackageTask = require('./package_task').PackageTask;
this.PublishTask = require('./publish_task').PublishTask;
this.TestTask = require('./test_task').TestTask;
this.Task = Task;
this.FileTask = FileTask;
this.DirectoryTask = DirectoryTask;
this.Namespace = Namespace;
this.Rule = Rule;
this.parseAllTasks = function () {
let _parseNs = function (ns) {
let nsTasks = ns.tasks;
let nsNamespaces = ns.childNamespaces;
for (let q in nsTasks) {
let nsTask = nsTasks[q];
jake.Task[nsTask.fullName] = nsTask;
}
for (let p in nsNamespaces) {
let nsNamespace = nsNamespaces[p];
_parseNs(nsNamespace);
}
};
_parseNs(jake.defaultNamespace);
};
/**
* Displays the list of descriptions avaliable for tasks defined in
* a Jakefile
*/
this.showAllTaskDescriptions = function (f) {
let p;
let maxTaskNameLength = 0;
let task;
let padding;
let name;
let descr;
let filter = typeof f == 'string' ? f : null;
for (p in jake.Task) {
if (!Object.prototype.hasOwnProperty.call(jake.Task, p)) {
continue;
}
if (filter && p.indexOf(filter) == -1) {
continue;
}
task = jake.Task[p];
// Record the length of the longest task name -- used for
// pretty alignment of the task descriptions
if (task.description) {
maxTaskNameLength = p.length > maxTaskNameLength ?
p.length : maxTaskNameLength;
}
}
// Print out each entry with descriptions neatly aligned
for (p in jake.Task) {
if (!Object.prototype.hasOwnProperty.call(jake.Task, p)) {
continue;
}
if (filter && p.indexOf(filter) == -1) {
continue;
}
task = jake.Task[p];
//name = '\033[32m' + p + '\033[39m ';
name = chalk.green(p);
descr = task.description;
if (descr) {
descr = chalk.gray('# ' + descr);
// Create padding-string with calculated length
padding = (new Array(maxTaskNameLength - p.length + 2)).join(' ');
console.log('jake ' + name + padding + descr);
}
}
};
this.createTask = function () {
let args = Array.prototype.slice.call(arguments);
let arg;
let obj;
let task;
let type;
let name;
let action;
let opts = {};
let prereqs = [];
type = args.shift();
// name, [deps], [action]
// Name (string) + deps (array) format
if (typeof args[0] == 'string') {
name = args.shift();
if (Array.isArray(args[0])) {
prereqs = args.shift();
}
}
// name:deps, [action]
// Legacy object-literal syntax, e.g.: {'name': ['depA', 'depB']}
else {
obj = args.shift();
for (let p in obj) {
prereqs = prereqs.concat(obj[p]);
name = p;
}
}
// Optional opts/callback or callback/opts
while ((arg = args.shift())) {
if (typeof arg == 'function') {
action = arg;
}
else {
opts = Object.assign(Object.create(null), arg);
}
}
task = jake.currentNamespace.resolveTask(name);
if (task && !action) {
// Task already exists and no action, just update prereqs, and return it.
task.prereqs = task.prereqs.concat(prereqs);
return task;
}
switch (type) {
case 'directory':
action = function () {
jake.mkdirP(name);
};
task = new DirectoryTask(name, prereqs, action, opts);
break;
case 'file':
task = new FileTask(name, prereqs, action, opts);
break;
default:
task = new Task(name, prereqs, action, opts);
}
jake.currentNamespace.addTask(task);
if (jake.currentTaskDescription) {
task.description = jake.currentTaskDescription;
jake.currentTaskDescription = null;
}
// FIXME: Should only need to add a new entry for the current
// task-definition, not reparse the entire structure
jake.parseAllTasks();
return task;
};
this.attemptRule = function (name, ns, level) {
let prereqRule;
let prereq;
if (level > MAX_RULE_RECURSION_LEVEL) {
return null;
}
// Check Rule
prereqRule = ns.matchRule(name);
if (prereqRule) {
prereq = prereqRule.createTask(name, level);
}
return prereq || null;
};
this.createPlaceholderFileTask = function (name, namespace) {
let parsed = name.split(':');
let filePath = parsed.pop(); // Strip any namespace
let task;
task = namespace.resolveTask(name);
// If there's not already an existing dummy FileTask for it,
// create one
if (!task) {
// Create a dummy FileTask only if file actually exists
if (fs.existsSync(filePath)) {
task = new jake.FileTask(filePath);
task.dummy = true;
let ns;
if (parsed.length) {
ns = namespace.resolveNamespace(parsed.join(':'));
}
else {
ns = namespace;
}
if (!namespace) {
throw new Error('Invalid namespace, cannot add FileTask');
}
ns.addTask(task);
// Put this dummy Task in the global Tasks list so
// modTime will be eval'd correctly
jake.Task[`${ns.path}:${filePath}`] = task;
}
}
return task || null;
};
this.run = function () {
let args = Array.prototype.slice.call(arguments);
let program = this.program;
let loader = this.loader;
let preempt;
let opts;
program.parseArgs(args);
program.init();
preempt = program.firstPreemptiveOption();
if (preempt) {
preempt();
}
else {
opts = program.opts;
// jakefile flag set but no jakefile yet
if (opts.autocomplete && opts.jakefile === true) {
process.stdout.write('no-complete');
return;
}
// Load Jakefile and jakelibdir files
let jakefileLoaded = loader.loadFile(opts.jakefile);
let jakelibdirLoaded = loader.loadDirectory(opts.jakelibdir);
if(!jakefileLoaded && !jakelibdirLoaded && !opts.autocomplete) {
fail('No Jakefile. Specify a valid path with -f/--jakefile, ' +
'or place one in the current directory.');
}
program.run();
}
};
})());
}
module.exports = jake;