1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.getGlobalTaskManager = exports.TaskManager = exports.TaskContext = exports.TaskState = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const cli_loading_1 = require("./cli-loading");
|
6 | const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
7 | const utils_1 = require("./utils");
|
8 | const fs_1 = require("./fs");
|
9 | const exec_1 = require("./exec");
|
10 | const logger_1 = require("./logger");
|
11 | const figures_1 = tslib_1.__importDefault(require("figures"));
|
12 | var 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 = {}));
|
21 | class 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 |
|
37 |
|
38 | get options() {
|
39 | return this.task.options || {};
|
40 | }
|
41 | |
42 |
|
43 |
|
44 |
|
45 |
|
46 | run(task, options) {
|
47 | return getGlobalTaskManager().run(task, Object.assign({ force: true, loading: false }, options));
|
48 | }
|
49 | }
|
50 | exports.TaskContext = TaskContext;
|
51 | class 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)) {
|
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)
|
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 | }
|
250 | exports.TaskManager = TaskManager;
|
251 | const TMKey = `@foy${require('../package.json').version}/taskManager`;
|
252 |
|
253 | function getGlobalTaskManager() {
|
254 | let taskManager = (global[TMKey] = global[TMKey] || new TaskManager());
|
255 | return taskManager;
|
256 | }
|
257 | exports.getGlobalTaskManager = getGlobalTaskManager;
|
258 |
|
\ | No newline at end of file |