1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const cli_loading_1 = require("./cli-loading");
|
5 | const chalk_1 = require("chalk");
|
6 | const utils_1 = require("./utils");
|
7 | const stream_1 = require("stream");
|
8 | const fs_1 = require("./fs");
|
9 | const exec_1 = require("./exec");
|
10 | const logger_1 = require("./logger");
|
11 | const stripAnsi = require("strip-ansi");
|
12 | const figures = require("figures");
|
13 | var 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 = {}));
|
22 | class 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 | }
|
43 | exports.TaskContext = TaskContext;
|
44 | class 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 | }
|
205 | exports.TaskManager = TaskManager;
|
206 | const TMKey = '@foyjs/taskManager';
|
207 |
|
208 | function getGlobalTaskManager() {
|
209 | let taskManager = (global[TMKey] = global[TMKey] || new TaskManager());
|
210 | return taskManager;
|
211 | }
|
212 | exports.getGlobalTaskManager = getGlobalTaskManager;
|
213 |
|
\ | No newline at end of file |