UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getGlobalTaskManager = exports.TaskManager = exports.TaskContext = exports.TaskState = void 0;
4const tslib_1 = require("tslib");
5const cli_loading_1 = require("./cli-loading");
6const chalk_1 = tslib_1.__importDefault(require("chalk"));
7const utils_1 = require("./utils");
8const fs_1 = require("./fs");
9const exec_1 = require("./exec");
10const logger_1 = require("./logger");
11const figures_1 = tslib_1.__importDefault(require("figures"));
12var TaskState;
13(function (TaskState) {
14 TaskState["waiting"] = "waiting";
15 TaskState["pending"] = "pending";
16 TaskState["skipped"] = "skipped";
17 TaskState["loading"] = "loading";
18 TaskState["succeeded"] = "succeeded";
19 TaskState["failed"] = "failed";
20})(TaskState = exports.TaskState || (exports.TaskState = {}));
21class TaskContext extends exec_1.ShellContext {
22 constructor(task, global) {
23 super();
24 this.task = task;
25 this.global = global;
26 this.fs = fs_1.fs;
27 this.logCommand = utils_1.defaults(task.logger && task.logCommand, global.logCommand, true);
28 this._logger = new logger_1.Logger(global.logger);
29 }
30 get debug() { return this._logger.debug; }
31 get info() { return this._logger.info; }
32 get log() { return this._logger.log; }
33 get warn() { return this._logger.warn; }
34 get error() { return this._logger.error; }
35 /**
36 * get task options
37 */
38 get options() {
39 return this.task.options || {};
40 }
41 /**
42 * run a task manually
43 * @param task
44 * @param options
45 */
46 run(task, options) {
47 return getGlobalTaskManager().run(task, Object.assign({ force: true, loading: false }, options));
48 }
49}
50exports.TaskContext = TaskContext;
51class TaskManager {
52 constructor() {
53 this._tasks = {};
54 this._didMap = new Map();
55 this.namespaces = [];
56 this.listeners = {
57 before: [],
58 after: [],
59 onerror: [],
60 };
61 this.globalOptions = {
62 loading: true,
63 options: {},
64 indent: 3,
65 logCommand: true,
66 logger: {}
67 };
68 }
69 getTasks() {
70 return Object.keys(this._tasks).map(k => this._tasks[k]);
71 }
72 addTask(task) {
73 if (this._tasks[task.name]) {
74 throw new TypeError(`Task name [${task.name}] already exists, please choose another task name!`);
75 }
76 this._tasks[task.name] = task;
77 return task;
78 }
79 resolveDependencyTree(t, depth = 0) {
80 let asyncDeps = [];
81 let syncDeps = [];
82 let asyncDepsMap = {};
83 if (t.async === true) {
84 t.async = 0;
85 }
86 if (t.dependencies) {
87 t.dependencies.forEach(taskDep => {
88 let fullTask = this._tasks[taskDep.name];
89 if (!fullTask) {
90 throw new TypeError(`Cannot find task with name [${taskDep.name}]`);
91 }
92 const t = Object.assign(Object.assign(Object.assign({}, fullTask), taskDep), { options: Object.assign(Object.assign({}, fullTask.options), taskDep.options) });
93 let depTask = this.resolveDependencyTree(t, depth + 1);
94 if (t.async === false || !utils_1.Is.defed(t.async)) { // sync tasks
95 syncDeps.push(depTask);
96 }
97 else {
98 let idx = t.async === true ? 0 : t.async;
99 let deps = asyncDepsMap[idx] = asyncDepsMap[idx] || [];
100 deps.push(depTask);
101 }
102 });
103 asyncDeps = [];
104 Object.keys(asyncDepsMap) // Sort async deps via priority, bigger is former
105 .map(Number)
106 .sort((a, b) => b - a)
107 .map(k => (asyncDeps.push(asyncDepsMap[k])));
108 }
109 return {
110 uid: Date.now().toString(36) +
111 Math.random()
112 .toString(36)
113 .slice(2),
114 task: t,
115 asyncDeps,
116 syncDeps,
117 state: TaskState.waiting,
118 depth,
119 priority: utils_1.Is.num(t.async) ? t.async : 0,
120 };
121 }
122 isLoading(t, props) {
123 return utils_1.defaults(props.loading, t.loading, this.globalOptions.loading, true);
124 }
125 runDepsTree(depsTree, props) {
126 return tslib_1.__awaiter(this, void 0, void 0, function* () {
127 let t = depsTree.task;
128 let taskHash = utils_1.hashAny(t);
129 let loading = this.isLoading(t, props);
130 let didResolved = null;
131 if (this._didMap.has(taskHash) && !t.force) {
132 depsTree.state = TaskState.skipped;
133 yield this._didMap.get(taskHash);
134 if (!loading) {
135 console.log(chalk_1.default.yellow(`Skip task: `) + t.name);
136 }
137 return;
138 }
139 this._didMap.set(taskHash, new Promise(res => (didResolved = res)));
140 t.rawArgs = props.rawArgs || [];
141 const ctx = new TaskContext(t, this.globalOptions);
142 const depProps = {
143 rawArgs: props.rawArgs,
144 indent: props.indent,
145 loading: props.loading,
146 parentCtx: ctx,
147 };
148 depsTree.state = TaskState.pending;
149 yield Promise.all([
150 (() => tslib_1.__awaiter(this, void 0, void 0, function* () {
151 for (const t of depsTree.syncDeps) {
152 yield this.runDepsTree(t, Object.assign(Object.assign({}, depProps), { parentCtx: ctx }));
153 }
154 }))(),
155 (() => tslib_1.__awaiter(this, void 0, void 0, function* () {
156 for (const deps of depsTree.asyncDeps) {
157 yield Promise.all(deps.map(t => this.runDepsTree(t, Object.assign(Object.assign({}, depProps), { parentCtx: ctx }))));
158 }
159 }))(),
160 ]);
161 depsTree.state = TaskState.loading;
162 if (t.resolveOptions && props.parentCtx) {
163 let lazyOptions = yield t.resolveOptions(props.parentCtx);
164 t.options = Object.assign(Object.assign({}, t.options), lazyOptions);
165 }
166 if (!loading) {
167 console.log(chalk_1.default.yellow('Task: ') + t.name);
168 let retPromise = t.fn && t.fn(ctx);
169 didResolved && didResolved();
170 return retPromise;
171 }
172 try {
173 let ret = t.fn && (yield t.fn(ctx));
174 depsTree.state = TaskState.succeeded;
175 didResolved && didResolved();
176 return ret;
177 }
178 catch (error) {
179 depsTree.state = TaskState.failed;
180 throw error;
181 }
182 });
183 }
184 runListner(name, ns, args = []) {
185 return tslib_1.__awaiter(this, void 0, void 0, function* () {
186 const listeners = this.listeners[name].slice();
187 if (name === 'before') {
188 listeners.sort((a, b) => a.namespaces.length - b.namespaces.length);
189 }
190 else {
191 listeners.sort((a, b) => b.namespaces.length - a.namespaces.length);
192 }
193 for (const fn of listeners) {
194 if (ns.join(':').startsWith(fn.namespaces.join(':'))) {
195 try {
196 yield fn.fn(...args);
197 }
198 catch (error) {
199 logger_1.logger.error(error);
200 }
201 }
202 }
203 });
204 }
205 run(name = 'default', props) {
206 return tslib_1.__awaiter(this, void 0, void 0, function* () {
207 logger_1.logger._props = Object.assign(Object.assign({}, logger_1.logger._props), this.globalOptions.logger);
208 props = Object.assign({ options: null, parentCtx: null, rawArgs: [], force: false }, props);
209 this._tasks.all =
210 this._tasks.all || (yield Promise.resolve().then(() => tslib_1.__importStar(require('./task'))).then(e => e.task('all', Object.keys(this._tasks))));
211 this._tasks.default = this._tasks.default || this._tasks.all;
212 if (!this._tasks[utils_1.Is.str(name) ? name : name.name]) {
213 throw new TypeError(`Cannot find task with name [${name}]`);
214 }
215 const t = Object.assign(Object.assign({}, (utils_1.Is.str(name) ? this._tasks[name] : name)), { force: props.force });
216 t.options = Object.assign(Object.assign({}, (t.options || null)), (props.options || null));
217 let depsTree = this.resolveDependencyTree(t);
218 let loading = this.isLoading(t, props);
219 let cliLoading = new cli_loading_1.CliLoading({
220 depsTree,
221 indent: utils_1.defaults(props.indent, this.globalOptions.indent),
222 });
223 if (loading) {
224 cliLoading.start();
225 }
226 else {
227 cliLoading.props.symbolMap = { [TaskState.waiting]: figures_1.default.line };
228 cliLoading.props.grayState = [];
229 console.log(chalk_1.default.yellow(`DependencyGraph for task [${t.name}]:`));
230 console.log(cliLoading.renderDepsTree(depsTree).join('\n') + '\n');
231 }
232 yield this.runListner('before', t.namespaces, [t]);
233 try {
234 let ret = yield this.runDepsTree(depsTree, props);
235 return ret;
236 }
237 catch (e) {
238 logger_1.logger.error(e);
239 yield this.runListner('onerror', t.namespaces, [e, t]);
240 }
241 finally {
242 yield this.runListner('after', t.namespaces, [t]);
243 if (loading) {
244 cliLoading.stop();
245 }
246 }
247 });
248 }
249}
250exports.TaskManager = TaskManager;
251const TMKey = `@foy${require('../package.json').version}/taskManager`;
252/** @internal */
253function getGlobalTaskManager() {
254 let taskManager = (global[TMKey] = global[TMKey] || new TaskManager());
255 return taskManager;
256}
257exports.getGlobalTaskManager = getGlobalTaskManager;
258//# sourceMappingURL=task-manager.js.map
\No newline at end of file