1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict';
|
6 |
|
7 | const gulp = require('gulp'),
|
8 | gulpClean = require('gulp-clean'),
|
9 | prettyTime = require('pretty-hrtime'),
|
10 | extend = require('extend'),
|
11 | EventEmitter = require('events').EventEmitter,
|
12 | util = require('../utils'),
|
13 | T = require('../tools'),
|
14 | serconf = require('../serconf'),
|
15 | DeployManager = require('../deploy/deployManager'),
|
16 | TaskNode = require('./taskNode'),
|
17 | TaskNodeFun = require('./taskNodeFun');
|
18 |
|
19 | process.setMaxListeners(0);
|
20 |
|
21 |
|
22 | const
|
23 | isStartServer = T.hasArg(['s', 'server']),
|
24 | isOpenBrowser = T.hasArg(['o', 'open-browser']),
|
25 | isSkipBackup = T.hasArg('skip-backup');
|
26 |
|
27 | class Gupack extends EventEmitter {
|
28 | constructor(config, gulpInstance) {
|
29 | super();
|
30 | this.env = process.env.NODE_ENV || T.getArg(['e', 'env']) || 'local';
|
31 | this.project = T.getArg(['p', 'project']);
|
32 | this.basePath = T.getArg('cwdir') || process.cwd();
|
33 | this.sourceDir = 'src';
|
34 | this.buildDir = 'dist';
|
35 | this.indexFile = '';
|
36 | this.host = serconf.servers.host;
|
37 | this.port = serconf.servers.port;
|
38 | this.sport = serconf.servers.sport;
|
39 | this.liveDelay = serconf.servers.liveDelay;
|
40 | this.devServer = isStartServer;
|
41 | this.openBrowser = isOpenBrowser;
|
42 | this.hostname = null;
|
43 |
|
44 | this.startClean = true;
|
45 | this.isRunAloneTask = false;
|
46 | this.watch = true;
|
47 | this.cleans = [];
|
48 | this.cleansFilter = ['!.svn', '!.git'];
|
49 | this.buildTasks = [];
|
50 | this.tasks = [];
|
51 | this.relies = {};
|
52 | this.watchers = {};
|
53 | this.basePath = process.cwd();
|
54 | this.gulp = gulpInstance || gulp;
|
55 | this.server = null;
|
56 | this.runIndex = 0;
|
57 | this.taskStartCallback = null;
|
58 | this.taskStopCallback = null;
|
59 | this.allTaskDoneCallback = null;
|
60 | this.isAllTaskDone = true;
|
61 | this.isDeploy = T.getArg('skip-deploy');
|
62 | this.deploy = null;
|
63 | this.deploies = [];
|
64 |
|
65 | for (let prop in config) {
|
66 | if (config.hasOwnProperty(prop)) {
|
67 | this[prop] = config[prop];
|
68 | }
|
69 | }
|
70 |
|
71 | this.init();
|
72 | }
|
73 |
|
74 | init() {
|
75 |
|
76 | this.initPaths();
|
77 |
|
78 | this.initDeploy();
|
79 |
|
80 | this.initTasks();
|
81 |
|
82 |
|
83 |
|
84 | this.watch && this.initWatch();
|
85 |
|
86 | this.initClean();
|
87 | }
|
88 |
|
89 | initPaths() {
|
90 | if (!T.isAbsolutePath(this.sourceDir)) {
|
91 | this.sourceDir = T.Path.resolve(this.basePath, this.sourceDir || 'src');
|
92 | }
|
93 | if (!T.isAbsolutePath(this.buildDir)) {
|
94 | this.buildDir = T.Path.resolve(this.basePath, this.buildDir || 'dist');
|
95 | }
|
96 | }
|
97 |
|
98 |
|
99 | initDeploy() {
|
100 | if (util.isObject(this.deploy)) {
|
101 | if (!this.deploy['localPath']) {
|
102 | this.deploy.localPath = T.Path.join(this.buildDir, '/**/*');
|
103 | }
|
104 | this.deploies.push(this.deploy);
|
105 | } else if (util.isArray(this.deploy)) {
|
106 | this.deploies = this.deploy.map(d => {
|
107 | if (!d['localPath']) {
|
108 | d.localPath = T.Path.join(this.buildDir, '/**/*');
|
109 | }
|
110 | return d;
|
111 | });
|
112 | }
|
113 | const deploies = extend(true, [], this.deploies);
|
114 | this.deployManager = new DeployManager(deploies, this.watch);
|
115 | }
|
116 |
|
117 | initClean() {
|
118 | if (!util.isArray(this.cleans)) return false;
|
119 | this.cleansFilter = this.cleansFilter.concat(this.buildDir).map(cf => {
|
120 | return '!' + cf.replace(/^!/i, '');
|
121 | });
|
122 | if (this.cleans.length > 0) {
|
123 | this.cleans = this.cleans.concat(this.cleansFilter);
|
124 | this.gulp.task('build._cleans', () => {
|
125 | return this.gulp.src(this.cleans).pipe(gulpClean({
|
126 | read: true
|
127 | }));
|
128 | });
|
129 |
|
130 | this.tasks.unshift('build._cleans');
|
131 | }
|
132 | }
|
133 |
|
134 | initTasks() {
|
135 | if (!util.isObject(this.buildTasks)) {
|
136 | T.log.error(
|
137 | `× ${new TypeError('参数 buildTask 未找到或类型错误(buildTask必须是一个对象Object)' + '\n× 请您检查项目的gupack-config.js文件配置')}`
|
138 | );
|
139 | return false;
|
140 | }
|
141 |
|
142 | let tasks = this.buildTasks;
|
143 | for (let key in tasks) {
|
144 | if (tasks.hasOwnProperty(key)) {
|
145 | const task = tasks[key];
|
146 | const taskNode = !util.isFunction(task) ? new TaskNode(task, this) : new TaskNodeFun(task);
|
147 | taskNode.name = key;
|
148 | this.buildTasks[key] = taskNode;
|
149 | }
|
150 | }
|
151 | this.setTask();
|
152 |
|
153 | this.tasks = Object.keys(this.buildTasks).map(taskName => {
|
154 | let tasker = this.buildTasks[taskName];
|
155 |
|
156 | if (!tasker.fun) {
|
157 | this.gulp.task(taskName, tasker['rely'], done => {
|
158 | return !!tasker.run === true ? tasker.getTaskFunction(done) : done();
|
159 | });
|
160 | } else {
|
161 | this.gulp.task(taskName, tasker['rely'], tasker.fun);
|
162 | }
|
163 |
|
164 | return taskName;
|
165 | });
|
166 | }
|
167 |
|
168 | initWatch() {
|
169 |
|
170 | if (util.isArray(this.watch) || util.isString(this.watch) || util.isObject(this.watch)) {
|
171 | let watchSource = [],
|
172 | ts = null;
|
173 | if (util.isArray(this.watch)) {
|
174 | watchSource = this.watch.map(w => T.Path.resolve(this.sourceDir, w));
|
175 | }
|
176 | if (util.isString(this.watch)) {
|
177 | watchSource = [T.Path.resolve(this.sourceDir, this.watch)];
|
178 | }
|
179 | if (util.isObject(this.watch)) {
|
180 | ts = [];
|
181 | Object.keys(this.watch).map(w => {
|
182 | watchSource.push(T.Path.resolve(this.sourceDir, this.watch[w]));
|
183 | ts.push(w);
|
184 | });
|
185 | }
|
186 |
|
187 | this.gulp.task('build._watch', () => {
|
188 | this.gulp.watch(watchSource, ts || this.tasks);
|
189 | });
|
190 |
|
191 | return this.tasks.push('build._watch');
|
192 | }
|
193 |
|
194 |
|
195 | let watchers = this.watchers,
|
196 | relies = this.relies;
|
197 |
|
198 | Object.keys(watchers).length > 0 &&
|
199 | (() => {
|
200 | this.gulp.task('build._watch', () => {
|
201 | let source, watcher;
|
202 |
|
203 | Object.keys(watchers).forEach(k => {
|
204 | source = watchers[k];
|
205 | let ts = [];
|
206 |
|
207 |
|
208 | Object.keys(relies).forEach(rely => {
|
209 | if (relies[rely] && relies[rely].indexOf(k) !== -1) {
|
210 | ts.unshift(rely);
|
211 | }
|
212 | });
|
213 | ts.push(k);
|
214 | watcher = this.gulp.watch(source, ts);
|
215 | });
|
216 | });
|
217 |
|
218 | this.tasks.push('build._watch');
|
219 | })();
|
220 | }
|
221 |
|
222 | recombineTasks() {
|
223 |
|
224 | let paiallels = [],
|
225 | sequences = [];
|
226 |
|
227 | Object.keys(this.relies).forEach(rely => {
|
228 | if (util.isArray(this.relies[rely]) && this.relies[rely].length !== 0) {
|
229 | this.relies[rely].forEach(task => {
|
230 | if (paiallels.indexOf(task) === -1) {
|
231 | paiallels.unshift(task);
|
232 | }
|
233 | });
|
234 | }
|
235 | });
|
236 |
|
237 | Object.keys(this.relies).forEach(rely => {
|
238 | if (this.relies[rely] && paiallels.indexOf(rely) !== -1) {
|
239 | let index = paiallels.indexOf(rely);
|
240 | let removeTemp = paiallels.splice(index, 1);
|
241 | sequences.unshift(removeTemp[0]);
|
242 | } else if (paiallels.indexOf(rely) === -1) {
|
243 | sequences.push(rely);
|
244 | }
|
245 | });
|
246 | paiallels.length !== 0 && sequences.unshift(paiallels);
|
247 | this.tasks = sequences;
|
248 | }
|
249 |
|
250 | setTask() {
|
251 | let tasks = this.buildTasks;
|
252 | let tns = Object.keys(tasks);
|
253 |
|
254 | tns.forEach(tn => {
|
255 | let task = tasks[tn];
|
256 | task.name = tn;
|
257 | if (task.watch && !this.nowatch) {
|
258 | this.watchers[tn] = task.watch;
|
259 | }
|
260 | this.relies[tn] = task.rely;
|
261 | task.dest && task.beforeClean !== false && this.cleans.push(task.dest);
|
262 | });
|
263 | }
|
264 |
|
265 | addEvent() {
|
266 | let self = this,
|
267 | logCache = [],
|
268 |
|
269 | isStart = false,
|
270 | tasks = Object.keys(this.buildTasks),
|
271 | finishMsg = `√ Builded finish OK =^_^= (^_^) =^_^= !!! \n\r`;
|
272 |
|
273 | this.gulp.on('task_start', e => {
|
274 | this.isAllTaskDone = false;
|
275 | let name = e.task,
|
276 | task = this.buildTasks[name],
|
277 | isRun = (task instanceof TaskNode || task instanceof TaskNodeFun) && task.run === true;
|
278 | if (util.isFunction(this.taskStartCallback)) {
|
279 | return this.taskStartCallback.call(this, e);
|
280 | }
|
281 | if (tasks.indexOf(name) > -1 && logCache.indexOf(name) === -1 && isRun) {
|
282 | T.log.green(`→ [${T.getTime()}] Starting '${name}' ...`);
|
283 | }
|
284 | if (!isStart) {
|
285 | isStart = true;
|
286 | this.server && this.server.emitBuilding();
|
287 | }
|
288 | });
|
289 | this.gulp.on('task_stop', e => {
|
290 | this.isAllTaskDone = false;
|
291 | let name = e.task,
|
292 | task = this.buildTasks[name],
|
293 | isRun = (task instanceof TaskNode || task instanceof TaskNodeFun) && task.run === true,
|
294 | duration = prettyTime(e.hrDuration);
|
295 | if (util.isFunction(this.taskStopCallback)) {
|
296 | return this.taskStopCallback.call(this, e);
|
297 | }
|
298 | if (tasks.indexOf(name) > -1 && logCache.indexOf(name) === -1 && isRun) {
|
299 | T.log.green(`√ [${T.getTime()}] Finished '${e.task}', after ${T.msg.yellow(duration)}`);
|
300 | logCache.push(name);
|
301 | this.emit('finish_task', {
|
302 | task: e.task,
|
303 | name,
|
304 | duration
|
305 | });
|
306 | }
|
307 | });
|
308 |
|
309 | this.gulp.on('stop', e => {
|
310 | isStart = false;
|
311 | logCache = [];
|
312 |
|
313 |
|
314 | if (this.isRunAloneTask) {
|
315 | return process.exit(0);
|
316 | }
|
317 |
|
318 | if (util.isFunction(this.allTaskDoneCallback)) {
|
319 | this.allTaskDoneCallback.call(this, e);
|
320 | _doneNext.apply(this);
|
321 | return false;
|
322 | } else {
|
323 | T.log.yellow(finishMsg);
|
324 | }
|
325 | this.emit('finish_all_task', {
|
326 | event: e,
|
327 | message: finishMsg
|
328 | });
|
329 |
|
330 |
|
331 | _doneNext.apply(this);
|
332 | });
|
333 |
|
334 | this.gulp.on('task_err', e => {
|
335 | self.server && self.server.stop();
|
336 | T.log.error(`'${e.task}' errored after ${prettyTime(e.hrDuration)} \n\r ${Gupack.formatError(e)}`);
|
337 | });
|
338 |
|
339 | this.gulp.on('task_not_found', err => {
|
340 | self.server && self.server.stop();
|
341 | T.log.error(`Task \'' + err.task + '\' is not found \n\r Please check the documentation for proper gupack-config file formatting`);
|
342 | });
|
343 |
|
344 | function _doneNext() {
|
345 | this.emit('finish_all_task_next_before', {
|
346 | gupack: this
|
347 | });
|
348 |
|
349 |
|
350 | this.devServer && this.startServer();
|
351 |
|
352 | const deploies = extend(true, [], this.deploies);
|
353 | if (this.isDeploy) {
|
354 | if (deploies.length > 0) {
|
355 | T.log.yellow(`→ has deploys. but now skip deploy(arg: --skip-deploy).`);
|
356 | }
|
357 | return T.log.end();
|
358 | }
|
359 | if (!this.deployManager) {
|
360 | this.deployManager = new DeployManager(deploies);
|
361 | } else {
|
362 | this.deployManager.setDeploy(deploies);
|
363 | }
|
364 | this.deployManager.startDeploy(isSkipBackup, this.watch);
|
365 | this.emit('finish_all_task_next_after', {
|
366 | gupack: this
|
367 | });
|
368 | this.isAllTaskDone = true;
|
369 | }
|
370 | }
|
371 |
|
372 | startServer() {
|
373 | if (!this.devServer || this.runIndex) return;
|
374 | process.nextTick(() => {
|
375 |
|
376 | if (util.isFunction(this.devServer)) {
|
377 | this.devServer.call(this);
|
378 | }
|
379 | if (this.devServer === true) {
|
380 | this.server = require('../gupackServer');
|
381 | this.server.createLiveServer(this);
|
382 | }
|
383 | this.runIndex++;
|
384 | });
|
385 | }
|
386 |
|
387 | setDefaultTask() {
|
388 | this.gulp.task('default', this.tasks);
|
389 |
|
390 | }
|
391 |
|
392 | run() {
|
393 | let _run = () => {
|
394 | if (!this.isAllTaskDone) {
|
395 | return T.log.yellow(`» The last task has not been completed )====( `);
|
396 | }
|
397 | this.addEvent();
|
398 |
|
399 | this.setDefaultTask();
|
400 | this.gulp.start.apply(this.gulp, this.tasks);
|
401 | };
|
402 |
|
403 | if (this.startClean) {
|
404 | Gupack.cleanBuildDir(e => {
|
405 | _run();
|
406 | }, this.buildDir);
|
407 | } else {
|
408 | _run();
|
409 | }
|
410 | }
|
411 |
|
412 | runTask(name) {
|
413 | let ts = [];
|
414 | if (util.isArray(name)) {
|
415 | name.forEach(nm => {
|
416 | ts.push(nm);
|
417 | });
|
418 | } else {
|
419 | ts = [name];
|
420 | }
|
421 |
|
422 | ts = ts.filter(t => {
|
423 | let isIndex = util.isObject(this.buildTasks[t]);
|
424 | if (!isIndex) T.log.red(`× not fount task: '${t}'`);
|
425 | return isIndex;
|
426 | });
|
427 |
|
428 | if (this.startClean) {
|
429 | ts.forEach(t => {
|
430 | let taskNode = this.buildTasks[t],
|
431 | dest = taskNode.dest;
|
432 | if (!T.fsa.removeSync(dest)) T.log.green(`√ '${dest}' clean successfully !!!`);
|
433 | else T.log.green(`× '${dest}' clean failed (╯︵╰,)`);
|
434 | });
|
435 | }
|
436 |
|
437 | if (ts.length === 0) {
|
438 | T.log.error(`× ${new TypeError('not fount task')}`);
|
439 | } else {
|
440 | this.isRunAloneTask = true;
|
441 | this.addEvent();
|
442 | this.gulp.task('default', ts);
|
443 | this.gulp.start.apply(this.gulp, ts);
|
444 | }
|
445 | }
|
446 |
|
447 | runDeploy() {
|
448 | this.deployManager.startDeploy(isSkipBackup, false);
|
449 | }
|
450 |
|
451 | runBackup() {
|
452 | this.deployManager.startBackup(null);
|
453 | }
|
454 |
|
455 | runRollback() {
|
456 | this.deployManager.startRollback();
|
457 | }
|
458 |
|
459 | static cleanBuildDir(cb, dist) {
|
460 | let startDate = Date.now();
|
461 | T.log.green('→ clean starting ......');
|
462 | T.fsa.remove(T.Path.join(dist, '**/*'), err => {
|
463 | if (err) {
|
464 | T.log.error('× clean failed: ' + err.toString());
|
465 | } else {
|
466 | let time = (Date.now() - startDate) * 0.001;
|
467 | time = time.toFixed(2) + ' s';
|
468 | T.log.green(`√ clean successfully !!!, after ${T.msg.yellow(time)}`);
|
469 | cb && cb();
|
470 | }
|
471 | });
|
472 | }
|
473 | static formatError(e) {
|
474 | if (!e.err) {
|
475 | return e.message;
|
476 | }
|
477 | if (typeof e.err.showStack === 'boolean') {
|
478 | return e.err.toString();
|
479 | }
|
480 | if (e.err.stack) {
|
481 | return e.err.stack;
|
482 | }
|
483 | return new Error(String(e.err)).stack;
|
484 | }
|
485 | }
|
486 |
|
487 | module.exports = Gupack; |
\ | No newline at end of file |