UNPKG

8.56 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const cli_loading_1 = require("./cli-loading");
5const chalk_1 = require("chalk");
6const utils_1 = require("./utils");
7const stream_1 = require("stream");
8const fs_1 = require("./fs");
9const exec_1 = require("./exec");
10const logger_1 = require("./logger");
11const stripAnsi = require("strip-ansi");
12const figures = require("figures");
13var TaskState;
14(function (TaskState) {
15 TaskState["waiting"] = "waiting";
16 TaskState["pending"] = "pending";
17 TaskState["skipped"] = "skipped";
18 TaskState["loading"] = "loading";
19 TaskState["succeeded"] = "succeeded";
20 TaskState["failed"] = "failed";
21})(TaskState = exports.TaskState || (exports.TaskState = {}));
22class TaskContext extends exec_1.ShellContext {
23 constructor(task, global) {
24 super();
25 this.task = task;
26 this.global = global;
27 this.fs = fs_1.fs;
28 this.debug = logger_1.logger.debug;
29 this.info = logger_1.logger.info;
30 this.log = logger_1.logger.log;
31 this.warn = logger_1.logger.warn;
32 this.error = logger_1.logger.error;
33 this.logCommand = utils_1.defaults(task.logCommand, global.logCommand, true);
34 this.redirectLog = utils_1.defaults(task.redirectLog, global.redirectLog, false);
35 }
36 get options() {
37 return this.task.options || {};
38 }
39 run(task, options) {
40 return getGlobalTaskManager().run(task, Object.assign({ force: true, loading: false }, options));
41 }
42}
43exports.TaskContext = TaskContext;
44class TaskManager {
45 constructor() {
46 this._tasks = {};
47 this._didSet = new Set();
48 this.globalOptions = {
49 logLevel: 'debug',
50 loading: true,
51 options: {},
52 logCommand: true,
53 indent: 3,
54 };
55 }
56 getTasks() {
57 return Object.keys(this._tasks).map(k => this._tasks[k]);
58 }
59 addTask(task) {
60 if (this._tasks[task.name]) {
61 throw new TypeError(`Task name [${task.name}] already exists, please choose another task name!`);
62 }
63 this._tasks[task.name] = task;
64 return task;
65 }
66 resolveDependencyTree(t, depth = 0) {
67 let asyncDeps = [];
68 let syncDeps = [];
69 if (t.dependencies) {
70 t.dependencies.forEach(taskDep => {
71 let fullTask = this._tasks[taskDep.name];
72 if (!fullTask) {
73 throw new TypeError(`Cannot find task with name [${taskDep.name}]`);
74 }
75 const t = Object.assign({}, fullTask, taskDep, { options: Object.assign({}, fullTask.options, taskDep.options) });
76 (t.async ? asyncDeps : syncDeps).push(this.resolveDependencyTree(t, depth + 1));
77 });
78 }
79 return {
80 uid: Date.now().toString(36) +
81 Math.random()
82 .toString(36)
83 .slice(2),
84 task: t,
85 asyncDeps,
86 syncDeps,
87 state: TaskState.waiting,
88 depth,
89 };
90 }
91 isLoading(t, props) {
92 return utils_1.defaults(props.loading, t.loading, this.globalOptions.loading, true);
93 }
94 runDepsTree(depsTree, props) {
95 return tslib_1.__awaiter(this, void 0, void 0, function* () {
96 let t = depsTree.task;
97 t.rawArgs = props.rawArgs || [];
98 const ctx = new TaskContext(t, this.globalOptions);
99 const depProps = {
100 rawArgs: props.rawArgs,
101 indent: props.indent,
102 loading: props.loading,
103 parentCtx: ctx,
104 };
105 depsTree.state = TaskState.pending;
106 yield Promise.all([
107 (() => tslib_1.__awaiter(this, void 0, void 0, function* () {
108 for (const t of depsTree.syncDeps) {
109 yield this.runDepsTree(t, Object.assign({}, depProps, { parentCtx: ctx }));
110 }
111 }))(),
112 Promise.all(depsTree.asyncDeps.map(t => this.runDepsTree(t, Object.assign({}, depProps, { parentCtx: ctx })))),
113 ]);
114 depsTree.state = TaskState.loading;
115 if (t.resolveOptions && props.parentCtx) {
116 let lazyOptions = yield t.resolveOptions(props.parentCtx);
117 t.options = Object.assign({}, t.options, lazyOptions);
118 }
119 let loading = this.isLoading(t, props);
120 let taskHash = utils_1.hashAny(t);
121 if (this._didSet.has(taskHash) && !t.force) {
122 depsTree.state = TaskState.skipped;
123 if (!loading) {
124 console.log(chalk_1.default.yellow(`Skip task: `) + t.name);
125 }
126 return;
127 }
128 if (!loading) {
129 console.log(chalk_1.default.yellow('Task: ') + t.name);
130 let ret = t.fn && (yield t.fn(ctx));
131 this._didSet.add(taskHash);
132 return ret;
133 }
134 try {
135 let ret = t.fn && (yield t.fn(ctx));
136 depsTree.state = TaskState.succeeded;
137 this._didSet.add(taskHash);
138 return ret;
139 }
140 catch (error) {
141 depsTree.state = TaskState.failed;
142 throw error;
143 }
144 });
145 }
146 run(name = 'default', props) {
147 return tslib_1.__awaiter(this, void 0, void 0, function* () {
148 let { redirectLog } = this.globalOptions;
149 let redirectStream = undefined;
150 if (redirectLog) {
151 if (redirectLog instanceof stream_1.Stream) {
152 redirectStream = redirectLog;
153 }
154 else {
155 redirectLog = utils_1.Is.str(redirectLog) ? redirectLog : utils_1.DefaultLogFile;
156 redirectStream = fs_1.fs.createWriteStream(redirectLog, { mode: fs_1.fs.constants.O_APPEND });
157 }
158 process.stdout.write = (buf, encoding, ...args) => {
159 if (buf instanceof Buffer) {
160 buf = buf.toString(utils_1.Is.str(encoding) ? encoding : 'utf-8');
161 }
162 buf = stripAnsi(buf);
163 return redirectStream.write(buf, encoding, ...args);
164 };
165 }
166 props = Object.assign({ options: null, parentCtx: null, rawArgs: [], force: false }, props);
167 this._tasks.all =
168 this._tasks.all || (yield Promise.resolve().then(() => require('./task')).then(e => e.task('all', Object.keys(this._tasks))));
169 this._tasks.default = this._tasks.default || this._tasks.all;
170 if (!this._tasks[utils_1.Is.str(name) ? name : name.name]) {
171 throw new TypeError(`Cannot find task with name [${name}]`);
172 }
173 const t = Object.assign({}, (utils_1.Is.str(name) ? this._tasks[name] : name), { force: props.force });
174 t.options = Object.assign({}, (t.options || null), (props.options || null));
175 let depsTree = this.resolveDependencyTree(t);
176 let loading = this.isLoading(t, props);
177 let cliLoading = new cli_loading_1.CliLoading({
178 depsTree,
179 indent: utils_1.defaults(props.indent, this.globalOptions.indent),
180 });
181 if (loading) {
182 cliLoading.start();
183 }
184 else {
185 cliLoading.props.symbolMap = { [TaskState.waiting]: figures.line };
186 cliLoading.props.grayState = [];
187 console.log(chalk_1.default.yellow(`DependencyGraph for task [${t.name}]:`));
188 console.log(cliLoading.renderDepsTree(depsTree).join('\n') + '\n');
189 }
190 try {
191 let ret = yield this.runDepsTree(depsTree, props);
192 return ret;
193 }
194 finally {
195 if (loading) {
196 cliLoading.stop();
197 }
198 if (redirectStream) {
199 redirectStream.end();
200 }
201 }
202 });
203 }
204}
205exports.TaskManager = TaskManager;
206const TMKey = '@foyjs/taskManager';
207/** @internal */
208function getGlobalTaskManager() {
209 let taskManager = (global[TMKey] = global[TMKey] || new TaskManager());
210 return taskManager;
211}
212exports.getGlobalTaskManager = getGlobalTaskManager;
213//# sourceMappingURL=task-manager.js.map
\No newline at end of file