@alterior/terminal 3.6.7 → 3.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/task-list.d.ts +104 -0
- package/dist/task-list.js +452 -0
- package/dist/task-list.js.map +1 -0
- package/package.json +5 -4
- package/src/index.ts +2 -1
- package/src/task-list.ts +561 -0
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./style"), exports);
|
|
18
18
|
__exportStar(require("./read"), exports);
|
|
19
19
|
__exportStar(require("./terminal-ui"), exports);
|
|
20
|
+
__exportStar(require("./task-list"), exports);
|
|
20
21
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,yCAAuB;AACvB,gDAA8B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,yCAAuB;AACvB,gDAA8B;AAC9B,8CAA4B"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export interface CLITaskInit {
|
|
3
|
+
omitInNonInteractive?: boolean;
|
|
4
|
+
parentTask?: CLITask;
|
|
5
|
+
}
|
|
6
|
+
export declare class CLITaskList {
|
|
7
|
+
readonly interactive: boolean;
|
|
8
|
+
readonly silent: boolean;
|
|
9
|
+
constructor(interactive?: boolean, silent?: boolean);
|
|
10
|
+
private timer;
|
|
11
|
+
private renderedLineCount;
|
|
12
|
+
readonly spinner: CLITaskStatusWidget;
|
|
13
|
+
private globalTask;
|
|
14
|
+
private started;
|
|
15
|
+
startCursor: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
private start;
|
|
20
|
+
stop(): void;
|
|
21
|
+
get remainingLines(): number;
|
|
22
|
+
startTask(title: string, init?: CLITaskInit): CLITask;
|
|
23
|
+
addTask(task: CLITask): void;
|
|
24
|
+
get allTasksFinished(): boolean;
|
|
25
|
+
idle: boolean;
|
|
26
|
+
private idleTimeout;
|
|
27
|
+
beforeCpu: NodeJS.CpuUsage;
|
|
28
|
+
cpu: NodeJS.CpuUsage;
|
|
29
|
+
ram: NodeJS.MemoryUsage;
|
|
30
|
+
lastTimestamp: bigint;
|
|
31
|
+
secondTicks: number;
|
|
32
|
+
startedAt: number;
|
|
33
|
+
private render;
|
|
34
|
+
drawHeader(): void;
|
|
35
|
+
private startFrame;
|
|
36
|
+
private endFrame;
|
|
37
|
+
drawLine(input?: string): void;
|
|
38
|
+
private truncateLineIfNeeded;
|
|
39
|
+
}
|
|
40
|
+
declare class LineTracker {
|
|
41
|
+
private _maxLines;
|
|
42
|
+
constructor(_maxLines: number);
|
|
43
|
+
static unbounded: LineTracker;
|
|
44
|
+
private _consumed;
|
|
45
|
+
get maxLines(): number;
|
|
46
|
+
get consumed(): number;
|
|
47
|
+
consume(count?: number): void;
|
|
48
|
+
}
|
|
49
|
+
export type CLITaskCharm = () => string;
|
|
50
|
+
export type CLITaskStatus = 'running' | 'error' | 'finished';
|
|
51
|
+
export declare class CLITask {
|
|
52
|
+
constructor(title: string, init?: CLITaskInit);
|
|
53
|
+
parent?: CLITask;
|
|
54
|
+
children: CLITask[];
|
|
55
|
+
title: string;
|
|
56
|
+
status: CLITaskStatus;
|
|
57
|
+
finishedAt: number;
|
|
58
|
+
startedAt: number;
|
|
59
|
+
charms: CLITaskCharm[];
|
|
60
|
+
staleAfter: number;
|
|
61
|
+
staleWithErrorsAndLogAfter: number;
|
|
62
|
+
omitInNonInteractive: boolean;
|
|
63
|
+
private logMessages;
|
|
64
|
+
get depth(): any;
|
|
65
|
+
get indent(): string;
|
|
66
|
+
get isFinishedAndStale(): boolean;
|
|
67
|
+
get containsLogsOrError(): boolean;
|
|
68
|
+
get isDeleted(): boolean;
|
|
69
|
+
get parents(): CLITask[];
|
|
70
|
+
get chain(): CLITask[];
|
|
71
|
+
onUpdated: (task: CLITask) => void;
|
|
72
|
+
onDeleted: (task: CLITask) => void;
|
|
73
|
+
onLog: (task: CLITask, message: string) => void;
|
|
74
|
+
log(message: string): void;
|
|
75
|
+
error(message?: string): void;
|
|
76
|
+
finish(notify?: boolean): void;
|
|
77
|
+
delete(): void;
|
|
78
|
+
subtask(title: string, init?: CLITaskInit): CLITask;
|
|
79
|
+
addTask(child: CLITask): CLITask;
|
|
80
|
+
render(renderer: CLITaskList, lineTracker: LineTracker, parentFinished?: boolean): number;
|
|
81
|
+
renderNonInteractive(renderer: CLITaskList): void;
|
|
82
|
+
renderSelfAndParents(renderer: CLITaskList): void;
|
|
83
|
+
renderSelf(renderer: CLITaskList, lineTracker: LineTracker): void;
|
|
84
|
+
renderLogs(renderer: CLITaskList, lineTracker: LineTracker): void;
|
|
85
|
+
renderLogLine(renderer: CLITaskList, line: string): void;
|
|
86
|
+
renderChildren(renderer: CLITaskList, lineTracker: LineTracker, parentFinished?: boolean): number;
|
|
87
|
+
private summarizeCounts;
|
|
88
|
+
private summarizeCount;
|
|
89
|
+
private statusStyle;
|
|
90
|
+
private iconForStatus;
|
|
91
|
+
}
|
|
92
|
+
export declare class CLITaskStatusWidget {
|
|
93
|
+
private task?;
|
|
94
|
+
constructor(task?: CLITask);
|
|
95
|
+
protected readonly spinner: string[];
|
|
96
|
+
private id?;
|
|
97
|
+
private spinnerPosition;
|
|
98
|
+
spin(): void;
|
|
99
|
+
render(): string;
|
|
100
|
+
isRunning(): boolean;
|
|
101
|
+
start(cb?: () => void, interval?: number): void;
|
|
102
|
+
stop(): void;
|
|
103
|
+
}
|
|
104
|
+
export {};
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CLITaskStatusWidget = exports.CLITask = exports.CLITaskList = void 0;
|
|
4
|
+
const functions_1 = require("@alterior/functions");
|
|
5
|
+
const style_1 = require("./style");
|
|
6
|
+
const CLI_TASK_ERROR_ICON = '𐄂';
|
|
7
|
+
const CLI_TASK_FINISHED_ICON = '✓';
|
|
8
|
+
const INDENT = ' ';
|
|
9
|
+
class CLITaskList {
|
|
10
|
+
constructor(interactive = true, silent = false) {
|
|
11
|
+
this.interactive = interactive;
|
|
12
|
+
this.silent = silent;
|
|
13
|
+
this.renderedLineCount = 0;
|
|
14
|
+
this.spinner = new CLITaskStatusWidget();
|
|
15
|
+
this.globalTask = new CLITask('Global Task');
|
|
16
|
+
this.started = false;
|
|
17
|
+
this.startCursor = { x: 0, y: 0 };
|
|
18
|
+
this.idle = false;
|
|
19
|
+
this.lastTimestamp = BigInt(0);
|
|
20
|
+
this.secondTicks = 1000000;
|
|
21
|
+
this.startedAt = Date.now();
|
|
22
|
+
this.globalTask.onUpdated = task => {
|
|
23
|
+
if (!this.interactive) {
|
|
24
|
+
if (task.children.length === 0)
|
|
25
|
+
task.renderNonInteractive(this);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// When the last task completes, we need to re-render inline to be sure we render the right state.
|
|
29
|
+
// But we only want to do this once, and only when needed, otherwise its a waste.
|
|
30
|
+
if (this.globalTask.children.every(t => t.status !== 'running'))
|
|
31
|
+
this.render();
|
|
32
|
+
};
|
|
33
|
+
this.globalTask.onLog = (task, message) => {
|
|
34
|
+
if (this.interactive)
|
|
35
|
+
return;
|
|
36
|
+
task.renderNonInteractive(this);
|
|
37
|
+
for (let line of message.split(/\r?\n/))
|
|
38
|
+
task.renderLogLine(this, line);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
start() {
|
|
42
|
+
if (!this.interactive)
|
|
43
|
+
return;
|
|
44
|
+
if (this.started)
|
|
45
|
+
return;
|
|
46
|
+
this.started = true;
|
|
47
|
+
this.timer = setInterval(() => this.render(), 1000 / 30);
|
|
48
|
+
this.spinner.start();
|
|
49
|
+
}
|
|
50
|
+
stop() {
|
|
51
|
+
if (!this.interactive)
|
|
52
|
+
return;
|
|
53
|
+
if (!this.started)
|
|
54
|
+
return;
|
|
55
|
+
// Render once more to be sure we capture the final state.
|
|
56
|
+
this.render();
|
|
57
|
+
this.started = false;
|
|
58
|
+
clearInterval(this.timer);
|
|
59
|
+
this.timer = undefined;
|
|
60
|
+
this.spinner.stop();
|
|
61
|
+
this.renderedLineCount = 0;
|
|
62
|
+
}
|
|
63
|
+
get remainingLines() {
|
|
64
|
+
// The -2 here is:
|
|
65
|
+
// - (x) Its desirable to keep the prompt line visible at the top of the screen if possible (-1)
|
|
66
|
+
// - We will have one empty line visible at the end of the output to avoid flickering the
|
|
67
|
+
// cursor (-1)
|
|
68
|
+
return Math.max(0, process.stdout.rows - this.renderedLineCount - 1);
|
|
69
|
+
}
|
|
70
|
+
startTask(title, init) {
|
|
71
|
+
let task = new CLITask(title, init);
|
|
72
|
+
this.addTask(task);
|
|
73
|
+
return task;
|
|
74
|
+
}
|
|
75
|
+
addTask(task) {
|
|
76
|
+
if (!this.started) {
|
|
77
|
+
this.start();
|
|
78
|
+
}
|
|
79
|
+
this.globalTask.addTask(task);
|
|
80
|
+
}
|
|
81
|
+
get allTasksFinished() {
|
|
82
|
+
return this.globalTask.children.every(x => x.status !== 'running');
|
|
83
|
+
}
|
|
84
|
+
render() {
|
|
85
|
+
let newSecond = process.hrtime.bigint();
|
|
86
|
+
if (this.lastTimestamp + BigInt(1000000000) < newSecond) {
|
|
87
|
+
this.secondTicks = Number((newSecond - this.lastTimestamp) / BigInt(1000));
|
|
88
|
+
this.lastTimestamp = newSecond;
|
|
89
|
+
this.cpu = process.cpuUsage(this.beforeCpu);
|
|
90
|
+
this.beforeCpu = process.cpuUsage();
|
|
91
|
+
this.ram = process.memoryUsage();
|
|
92
|
+
}
|
|
93
|
+
this.startFrame();
|
|
94
|
+
// other cool u characters include: 𝖚
|
|
95
|
+
// see more: https://unicodeplus.com/U+0075
|
|
96
|
+
let elapsedTime = formatDuration(Date.now() - this.startedAt);
|
|
97
|
+
this.drawHeader();
|
|
98
|
+
this.globalTask.renderChildren(this, new LineTracker(this.remainingLines));
|
|
99
|
+
this.endFrame();
|
|
100
|
+
}
|
|
101
|
+
drawHeader() {
|
|
102
|
+
}
|
|
103
|
+
startFrame() {
|
|
104
|
+
process.stdout.cork();
|
|
105
|
+
process.stdout.moveCursor(0, -this.renderedLineCount);
|
|
106
|
+
this.renderedLineCount = 0;
|
|
107
|
+
}
|
|
108
|
+
endFrame() {
|
|
109
|
+
process.stdout.clearScreenDown();
|
|
110
|
+
process.stdout.uncork();
|
|
111
|
+
// Update idle
|
|
112
|
+
if (this.idle !== this.allTasksFinished) {
|
|
113
|
+
this.idle = this.allTasksFinished;
|
|
114
|
+
if (this.idle) {
|
|
115
|
+
this.idleTimeout = setTimeout(() => {
|
|
116
|
+
if (this.idle) {
|
|
117
|
+
this.stop();
|
|
118
|
+
}
|
|
119
|
+
}, 1000);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.idle = false;
|
|
123
|
+
clearTimeout(this.idleTimeout);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
drawLine(input = '') {
|
|
128
|
+
if (this.silent)
|
|
129
|
+
return;
|
|
130
|
+
if (!this.interactive) {
|
|
131
|
+
console.log(input);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let lines = input.split(/\r?\n/);
|
|
135
|
+
let count = 0;
|
|
136
|
+
for (let line of lines) {
|
|
137
|
+
process.stdout.clearLine(0);
|
|
138
|
+
process.stdout.write(`${this.truncateLineIfNeeded(line)}\n`);
|
|
139
|
+
process.stdout.clearLine(1);
|
|
140
|
+
this.renderedLineCount += 1;
|
|
141
|
+
count += 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
truncateLineIfNeeded(line) {
|
|
145
|
+
let indicatorPattern = /\u001b\[[^m]*m/g;
|
|
146
|
+
let visibleLength = line.replace(indicatorPattern, '').length;
|
|
147
|
+
if (visibleLength < process.stdout.columns)
|
|
148
|
+
return line;
|
|
149
|
+
let newLine = '';
|
|
150
|
+
let newVisible = 0;
|
|
151
|
+
for (let i = 0, max = line.length; i < max && newVisible < process.stdout.columns - 3; ++i) {
|
|
152
|
+
let match = /^\u001b\[[^m]*m/.exec(line.slice(i));
|
|
153
|
+
if (match) {
|
|
154
|
+
newLine += `${match[0]}`;
|
|
155
|
+
i += match[0].length - 1;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
newLine += line.at(i);
|
|
159
|
+
newVisible += 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
newLine += (0, style_1.styled)(style_1.style.$dim('...'));
|
|
163
|
+
// We may have cut off style codes by eliding this line. To prevent weirdness, we must
|
|
164
|
+
// reset the terminal back to default
|
|
165
|
+
newLine += (0, style_1.startStyle)('reset');
|
|
166
|
+
return newLine;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.CLITaskList = CLITaskList;
|
|
170
|
+
class LineTracker {
|
|
171
|
+
constructor(_maxLines) {
|
|
172
|
+
this._maxLines = _maxLines;
|
|
173
|
+
this._consumed = 0;
|
|
174
|
+
}
|
|
175
|
+
get maxLines() {
|
|
176
|
+
if (this._maxLines === undefined)
|
|
177
|
+
return Number.MAX_SAFE_INTEGER;
|
|
178
|
+
return this._maxLines;
|
|
179
|
+
}
|
|
180
|
+
get consumed() {
|
|
181
|
+
if (this._maxLines === undefined)
|
|
182
|
+
return 0;
|
|
183
|
+
return this._consumed;
|
|
184
|
+
}
|
|
185
|
+
consume(count = 1) {
|
|
186
|
+
if (this._maxLines !== undefined)
|
|
187
|
+
this._maxLines -= count;
|
|
188
|
+
this._consumed += count;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
LineTracker.unbounded = new LineTracker(undefined);
|
|
192
|
+
class CLITask {
|
|
193
|
+
constructor(title, init = {}) {
|
|
194
|
+
this.children = [];
|
|
195
|
+
this.status = 'running';
|
|
196
|
+
this.startedAt = Date.now();
|
|
197
|
+
this.charms = [];
|
|
198
|
+
this.staleAfter = 0;
|
|
199
|
+
this.staleWithErrorsAndLogAfter = 10000;
|
|
200
|
+
this.omitInNonInteractive = false;
|
|
201
|
+
this.logMessages = [];
|
|
202
|
+
this.title = title;
|
|
203
|
+
if (init.omitInNonInteractive)
|
|
204
|
+
this.omitInNonInteractive = true;
|
|
205
|
+
}
|
|
206
|
+
// Computed Properties
|
|
207
|
+
get depth() {
|
|
208
|
+
var _a, _b;
|
|
209
|
+
return ((_b = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.depth) !== null && _b !== void 0 ? _b : -1) + 1;
|
|
210
|
+
}
|
|
211
|
+
get indent() {
|
|
212
|
+
if (this.depth === 0)
|
|
213
|
+
return ``;
|
|
214
|
+
return Array.from(Array(this.depth - 1)).map(() => INDENT).join('');
|
|
215
|
+
}
|
|
216
|
+
get isFinishedAndStale() {
|
|
217
|
+
let staleTime = this.containsLogsOrError ? this.staleWithErrorsAndLogAfter : this.staleAfter;
|
|
218
|
+
let freshness = Math.min(staleTime);
|
|
219
|
+
return this.status === 'finished' && this.finishedAt + freshness < Date.now();
|
|
220
|
+
}
|
|
221
|
+
get containsLogsOrError() {
|
|
222
|
+
return this.logMessages.length > 0 || this.status === 'error' || this.children.some(x => x.containsLogsOrError);
|
|
223
|
+
}
|
|
224
|
+
get isDeleted() {
|
|
225
|
+
var _a;
|
|
226
|
+
return this.parent ? !((_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.includes(this)) : false;
|
|
227
|
+
}
|
|
228
|
+
get parents() {
|
|
229
|
+
let parents = [];
|
|
230
|
+
let parent = this.parent;
|
|
231
|
+
while (parent === null || parent === void 0 ? void 0 : parent.parent) {
|
|
232
|
+
parents.unshift(parent);
|
|
233
|
+
parent = parent.parent;
|
|
234
|
+
}
|
|
235
|
+
return parents;
|
|
236
|
+
}
|
|
237
|
+
get chain() {
|
|
238
|
+
return [...this.parents, this];
|
|
239
|
+
}
|
|
240
|
+
// Lifecycle API
|
|
241
|
+
log(message) {
|
|
242
|
+
var _a;
|
|
243
|
+
this.logMessages.push(...message.split(/\r?\n/));
|
|
244
|
+
(_a = this.onLog) === null || _a === void 0 ? void 0 : _a.call(this, this, message);
|
|
245
|
+
}
|
|
246
|
+
error(message) {
|
|
247
|
+
this.status = 'error';
|
|
248
|
+
this.log(`[Error] ${message || 'The task failed.'}`);
|
|
249
|
+
}
|
|
250
|
+
finish(notify = true) {
|
|
251
|
+
var _a;
|
|
252
|
+
this.status = 'finished';
|
|
253
|
+
this.finishedAt = Date.now();
|
|
254
|
+
for (let child of this.children)
|
|
255
|
+
child.finish(false);
|
|
256
|
+
if (notify)
|
|
257
|
+
(_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, this);
|
|
258
|
+
}
|
|
259
|
+
delete() {
|
|
260
|
+
var _a;
|
|
261
|
+
(_a = this.onDeleted) === null || _a === void 0 ? void 0 : _a.call(this, this);
|
|
262
|
+
}
|
|
263
|
+
// Subtasks
|
|
264
|
+
subtask(title, init = {}) {
|
|
265
|
+
let child = new CLITask(title, init);
|
|
266
|
+
this.addTask(child);
|
|
267
|
+
return child;
|
|
268
|
+
}
|
|
269
|
+
addTask(child) {
|
|
270
|
+
this.children.push(child);
|
|
271
|
+
child.parent = this;
|
|
272
|
+
child.onUpdated = child => { var _a; return (_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, child); };
|
|
273
|
+
child.onLog = (task, message) => { var _a; return (_a = this.onLog) === null || _a === void 0 ? void 0 : _a.call(this, task, message); };
|
|
274
|
+
child.onDeleted = child => {
|
|
275
|
+
var _a;
|
|
276
|
+
let index = this.children.indexOf(child);
|
|
277
|
+
if (index >= 0)
|
|
278
|
+
this.children.splice(index, 1);
|
|
279
|
+
(_a = this.onUpdated) === null || _a === void 0 ? void 0 : _a.call(this, child);
|
|
280
|
+
};
|
|
281
|
+
return child;
|
|
282
|
+
}
|
|
283
|
+
// Rendering
|
|
284
|
+
render(renderer, lineTracker, parentFinished = false) {
|
|
285
|
+
if (parentFinished && !this.containsLogsOrError)
|
|
286
|
+
return 0;
|
|
287
|
+
this.renderSelf(renderer, lineTracker);
|
|
288
|
+
this.renderChildren(renderer, lineTracker, parentFinished || this.isFinishedAndStale);
|
|
289
|
+
this.renderLogs(renderer, lineTracker);
|
|
290
|
+
}
|
|
291
|
+
renderNonInteractive(renderer) {
|
|
292
|
+
if (this.omitInNonInteractive)
|
|
293
|
+
return;
|
|
294
|
+
renderer.drawLine(this.chain.filter(x => !x.omitInNonInteractive).map(p => `${p.statusStyle(p.title)}`).join(' » '));
|
|
295
|
+
}
|
|
296
|
+
renderSelfAndParents(renderer) {
|
|
297
|
+
for (let parent of this.parents)
|
|
298
|
+
parent.renderSelf(renderer, LineTracker.unbounded);
|
|
299
|
+
this.renderSelf(renderer, LineTracker.unbounded);
|
|
300
|
+
}
|
|
301
|
+
renderSelf(renderer, lineTracker) {
|
|
302
|
+
var _a;
|
|
303
|
+
renderer.drawLine(`${(_a = this.indent) !== null && _a !== void 0 ? _a : ''}`
|
|
304
|
+
+ `${this.statusStyle(`${this.iconForStatus(renderer)} ${this.title}`, this.status)}`
|
|
305
|
+
+ `${this.summarizeCounts(renderer)}`
|
|
306
|
+
//+ ` DEBUG: ${maxLines}`
|
|
307
|
+
);
|
|
308
|
+
lineTracker.consume();
|
|
309
|
+
}
|
|
310
|
+
renderLogs(renderer, lineTracker) {
|
|
311
|
+
let displayedLogs = this.logMessages.slice();
|
|
312
|
+
while (displayedLogs.length > lineTracker.maxLines)
|
|
313
|
+
displayedLogs.shift();
|
|
314
|
+
for (let line of displayedLogs) {
|
|
315
|
+
this.renderLogLine(renderer, line);
|
|
316
|
+
lineTracker.consume();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
renderLogLine(renderer, line) {
|
|
320
|
+
var _a;
|
|
321
|
+
renderer.drawLine(`${(_a = this.indent) !== null && _a !== void 0 ? _a : ''} ${(0, style_1.styled)(style_1.style.$dim(line))}`);
|
|
322
|
+
}
|
|
323
|
+
renderChildren(renderer, lineTracker, parentFinished = false) {
|
|
324
|
+
if (lineTracker.maxLines <= 0)
|
|
325
|
+
return 0;
|
|
326
|
+
let statusOrder = {
|
|
327
|
+
'error': 0,
|
|
328
|
+
'finished': 1,
|
|
329
|
+
'running': 2
|
|
330
|
+
};
|
|
331
|
+
this.children.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
|
|
332
|
+
let maxDisplayedTasks = lineTracker.maxLines;
|
|
333
|
+
let displayedTasks = this.children.slice();
|
|
334
|
+
if (parentFinished) {
|
|
335
|
+
displayedTasks = displayedTasks.filter(x => x.containsLogsOrError);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
displayedTasks = displayedTasks.filter(x => !x.isFinishedAndStale);
|
|
339
|
+
}
|
|
340
|
+
// If we're tight on space, elide freshly finished tasks, too.
|
|
341
|
+
while (displayedTasks.length > maxDisplayedTasks) {
|
|
342
|
+
let taskIndex = displayedTasks.findIndex(x => x.status === 'finished');
|
|
343
|
+
if (taskIndex < 0)
|
|
344
|
+
break;
|
|
345
|
+
displayedTasks.splice(taskIndex, 1);
|
|
346
|
+
}
|
|
347
|
+
// Elide errors (better to see ongoing progress) ////////////////
|
|
348
|
+
while (displayedTasks.length > maxDisplayedTasks) {
|
|
349
|
+
let taskIndex = displayedTasks.findIndex(x => x.status === 'error');
|
|
350
|
+
if (taskIndex < 0)
|
|
351
|
+
break;
|
|
352
|
+
displayedTasks.splice(taskIndex, 1);
|
|
353
|
+
}
|
|
354
|
+
// Elide all remaining from end of sorted list until we fit within the maxDisplayedTasks
|
|
355
|
+
if (displayedTasks.length > maxDisplayedTasks)
|
|
356
|
+
displayedTasks.splice(maxDisplayedTasks, displayedTasks.length);
|
|
357
|
+
let extraLinesForChildren = lineTracker.maxLines - displayedTasks.length;
|
|
358
|
+
for (let task of displayedTasks) {
|
|
359
|
+
let childLineTracker = new LineTracker(1 + extraLinesForChildren);
|
|
360
|
+
task.render(renderer, childLineTracker, parentFinished);
|
|
361
|
+
lineTracker.consume(childLineTracker.consumed);
|
|
362
|
+
extraLinesForChildren -= (childLineTracker.consumed - 1);
|
|
363
|
+
extraLinesForChildren = Math.max(0, extraLinesForChildren);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Helpers
|
|
367
|
+
summarizeCounts(r) {
|
|
368
|
+
let counts = [
|
|
369
|
+
this.logMessages.length > 0 ? `${(0, style_1.styled)(style_1.style.$yellow(`• ${this.logMessages.length}`))}` : undefined,
|
|
370
|
+
this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'finished'), 'finished'),
|
|
371
|
+
this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'error'), 'error'),
|
|
372
|
+
this.summarizeCount(r, (0, functions_1.count)(this.children, x => x.status === 'running'), 'running'),
|
|
373
|
+
].filter(x => x);
|
|
374
|
+
if (counts.length === 0)
|
|
375
|
+
return '';
|
|
376
|
+
return ` ${(0, style_1.styled)(style_1.style.$dim(counts.join(' ')))}`;
|
|
377
|
+
}
|
|
378
|
+
summarizeCount(r, count, status) {
|
|
379
|
+
if (count <= 0)
|
|
380
|
+
return undefined;
|
|
381
|
+
return this.statusStyle(`${this.iconForStatus(r, status)} ${count}`, status);
|
|
382
|
+
}
|
|
383
|
+
statusStyle(line, status = this.status) {
|
|
384
|
+
if (status === 'running') {
|
|
385
|
+
line = (0, style_1.styled)(style_1.style.$bold(style_1.style.$blue(line)));
|
|
386
|
+
}
|
|
387
|
+
else if (status === 'finished') {
|
|
388
|
+
line = (0, style_1.styled)(style_1.style.$green(line));
|
|
389
|
+
}
|
|
390
|
+
else if (status === 'error') {
|
|
391
|
+
line = (0, style_1.styled)(style_1.style.$red(line));
|
|
392
|
+
}
|
|
393
|
+
return line;
|
|
394
|
+
}
|
|
395
|
+
iconForStatus(renderer, status = this.status) {
|
|
396
|
+
if (this.status === 'error')
|
|
397
|
+
return CLI_TASK_ERROR_ICON;
|
|
398
|
+
else if (this.status === 'finished')
|
|
399
|
+
return CLI_TASK_FINISHED_ICON;
|
|
400
|
+
return renderer.spinner.render();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
exports.CLITask = CLITask;
|
|
404
|
+
class CLITaskStatusWidget {
|
|
405
|
+
constructor(task) {
|
|
406
|
+
this.task = task;
|
|
407
|
+
this.spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
408
|
+
this.spinnerPosition = 0;
|
|
409
|
+
this.start();
|
|
410
|
+
}
|
|
411
|
+
spin() {
|
|
412
|
+
this.spinnerPosition = ++this.spinnerPosition % this.spinner.length;
|
|
413
|
+
if (this.task && this.task.status !== 'running') {
|
|
414
|
+
clearInterval(this.id);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
render() {
|
|
418
|
+
var _a, _b;
|
|
419
|
+
if (((_a = this.task) === null || _a === void 0 ? void 0 : _a.status) === 'error')
|
|
420
|
+
return CLI_TASK_ERROR_ICON;
|
|
421
|
+
else if (((_b = this.task) === null || _b === void 0 ? void 0 : _b.status) === 'finished')
|
|
422
|
+
return CLI_TASK_FINISHED_ICON;
|
|
423
|
+
return this.spinner[this.spinnerPosition];
|
|
424
|
+
}
|
|
425
|
+
isRunning() {
|
|
426
|
+
return !!this.id;
|
|
427
|
+
}
|
|
428
|
+
start(cb, interval = 100) {
|
|
429
|
+
clearInterval(this.id);
|
|
430
|
+
this.id = setInterval(() => {
|
|
431
|
+
this.spin();
|
|
432
|
+
if (cb) {
|
|
433
|
+
cb();
|
|
434
|
+
}
|
|
435
|
+
}, interval);
|
|
436
|
+
}
|
|
437
|
+
stop() {
|
|
438
|
+
clearInterval(this.id);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
exports.CLITaskStatusWidget = CLITaskStatusWidget;
|
|
442
|
+
function formatDuration(duration) {
|
|
443
|
+
const msPerSec = 1000;
|
|
444
|
+
const msPerMinute = (1000 * 60);
|
|
445
|
+
const msPerHour = (1000 * 60 * 60);
|
|
446
|
+
let hours = duration / msPerHour | 0;
|
|
447
|
+
let minutes = (duration - (hours * msPerHour)) / msPerMinute | 0;
|
|
448
|
+
let seconds = (duration - (hours * msPerHour + minutes * msPerMinute)) / msPerSec | 0;
|
|
449
|
+
let ms = duration % 1000;
|
|
450
|
+
return `${(0, functions_1.zeroPad)(hours)}:${(0, functions_1.zeroPad)(minutes)}:${(0, functions_1.zeroPad)(seconds)}:${(0, functions_1.zeroPad)(ms, 3)}`;
|
|
451
|
+
}
|
|
452
|
+
//# sourceMappingURL=task-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-list.js","sourceRoot":"","sources":["../src/task-list.ts"],"names":[],"mappings":";;;AAAA,mDAAqD;AACrD,mCAAoD;AAEpD,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACjC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,MAAM,GAAG,MAAM,CAAC;AAOtB,MAAa,WAAW;IACpB,YAAqB,cAAc,IAAI,EAAW,SAAS,KAAK;QAA3C,gBAAW,GAAX,WAAW,CAAO;QAAW,WAAM,GAAN,MAAM,CAAQ;QAuBxD,sBAAiB,GAAG,CAAC,CAAC;QACrB,YAAO,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACrC,eAAU,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;QAExC,YAAO,GAAG,KAAK,CAAC;QACxB,gBAAW,GAA6B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QA0DvD,SAAI,GAAG,KAAK,CAAC;QAMb,kBAAa,GAAW,MAAM,CAAC,CAAC,CAAC,CAAC;QAClC,gBAAW,GAAG,OAAS,CAAC;QACxB,cAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QA7FnB,IAAI,CAAC,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;YAC/B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;gBACnB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;oBAC1B,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;gBACpC,OAAO;aACV;YACD,kGAAkG;YAClG,iFAAiF;YACjF,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;gBAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,IAAa,EAAE,OAAe,EAAE,EAAE;YACvD,IAAI,IAAI,CAAC,WAAW;gBAChB,OAAO;YAEX,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAChC,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;gBACnC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC,CAAA;IACL,CAAC;IAUO,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,WAAW;YACjB,OAAO;QAEX,IAAI,IAAI,CAAC,OAAO;YACZ,OAAO;QAEX,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,IAAI;QACA,IAAI,CAAC,IAAI,CAAC,WAAW;YACjB,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,OAAO;YACb,OAAO;QAEX,0DAA0D;QAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;QAEd,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAGpB,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,cAAc;QACd,kBAAkB;QAClB,iGAAiG;QACjG,yFAAyF;QACzF,gBAAgB;QAChB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,SAAS,CAAC,KAAa,EAAE,IAAkB;QACvC,IAAI,IAAI,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,CAAC,IAAa;QACjB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACf,IAAI,CAAC,KAAK,EAAE,CAAC;SAChB;QAED,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,gBAAgB;QAChB,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACvE,CAAC;IAYO,MAAM;QACV,IAAI,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,UAAa,CAAC,GAAG,SAAS,EAAE;YACxD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC,IAAK,CAAC,CAAC,CAAC;YAC5E,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;SACpC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,sCAAsC;QACtC,2CAA2C;QAE3C,IAAI,WAAW,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAA;QAE7D,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,UAAU;IAEV,CAAC;IAEO,UAAU;QACd,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACtD,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;IAC/B,CAAC;IAEO,QAAQ;QACZ,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAExB,cAAc;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,gBAAgB,EAAE;YACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC;YAClC,IAAI,IAAI,CAAC,IAAI,EAAE;gBACX,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC/B,IAAI,IAAI,CAAC,IAAI,EAAE;wBACX,IAAI,CAAC,IAAI,EAAE,CAAC;qBACf;gBACL,CAAC,EAAE,IAAK,CAAC,CAAC;aACb;iBAAM;gBACH,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;aAClC;SACJ;IACL,CAAC;IAED,QAAQ,CAAC,QAAgB,EAAE;QACvB,IAAI,IAAI,CAAC,MAAM;YACX,OAAO;QAEX,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;SACV;QAED,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;YACpB,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7D,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAE5B,IAAI,CAAC,iBAAiB,IAAI,CAAC,CAAC;YAC5B,KAAK,IAAI,CAAC,CAAC;SACd;IACL,CAAC;IAEO,oBAAoB,CAAC,IAAY;QACrC,IAAI,gBAAgB,GAAG,iBAAiB,CAAC;QACzC,IAAI,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;QAC9D,IAAI,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO;YACtC,OAAO,IAAI,CAAC;QAEhB,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,GAAG,IAAI,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE;YACxF,IAAI,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,IAAI,KAAK,EAAE;gBACP,OAAO,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzB,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC5B;iBAAM;gBACH,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACtB,UAAU,IAAI,CAAC,CAAC;aACnB;SACJ;QAED,OAAO,IAAI,IAAA,cAAM,EAAC,aAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAErC,sFAAsF;QACtF,qCAAqC;QACrC,OAAO,IAAI,IAAA,kBAAU,EAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ;AAvMD,kCAuMC;AAED,MAAM,WAAW;IACb,YAAoB,SAAiB;QAAjB,cAAS,GAAT,SAAS,CAAQ;QAI7B,cAAS,GAAG,CAAC,CAAC;IAHtB,CAAC;IAKD,IAAI,QAAQ;QACR,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,OAAO,MAAM,CAAC,gBAAgB,CAAC;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,IAAI,QAAQ;QACR,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,OAAO,CAAC,CAAC;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,OAAO,CAAC,QAAgB,CAAC;QACrB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5B,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC5B,CAAC;;AAnBM,qBAAS,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;AAwBlD,MAAa,OAAO;IAChB,YAAY,KAAa,EAAE,OAAoB,EAAE;QAMjD,aAAQ,GAAc,EAAE,CAAC;QAEzB,WAAM,GAAkB,SAAS,CAAC;QAElC,cAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,WAAM,GAAmB,EAAE,CAAC;QAC5B,eAAU,GAAG,CAAC,CAAC;QACf,+BAA0B,GAAG,KAAM,CAAC;QACpC,yBAAoB,GAAG,KAAK,CAAC;QAErB,gBAAW,GAAa,EAAE,CAAC;QAhB/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,IAAI,CAAC,oBAAoB;YACzB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;IACzC,CAAC;IAeD,sBAAsB;IAEtB,IAAI,KAAK;;QACL,OAAO,CAAC,MAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,KAAK,mCAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,MAAM;QACN,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;YAChB,OAAO,EAAE,CAAC;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,kBAAkB;QAClB,IAAI,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;QAC7F,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpC,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,UAAU,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAClF,CAAC;IAED,IAAI,mBAAmB;QACnB,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACpH,CAAC;IAED,IAAI,SAAS;;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA,MAAA,IAAI,CAAC,MAAM,0CAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA,CAAC,CAAC,CAAC,KAAK,CAAC;IACvE,CAAC;IAED,IAAI,OAAO;QACP,IAAI,OAAO,GAAc,EAAE,CAAC;QAC5B,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACzB,OAAO,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,EAAE;YACnB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACxB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;SAC1B;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,IAAI,KAAK;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAQD,gBAAgB;IAEhB,GAAG,CAAC,OAAe;;QACf,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACjD,MAAA,IAAI,CAAC,KAAK,qDAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAgB;QAClB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,WAAW,OAAO,IAAI,kBAAkB,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,MAAM,GAAG,IAAI;;QAChB,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ;YAC3B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAExB,IAAI,MAAM;YACN,MAAA,IAAI,CAAC,SAAS,qDAAG,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM;;QACF,MAAA,IAAI,CAAC,SAAS,qDAAG,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,WAAW;IAEX,OAAO,CAAC,KAAa,EAAE,OAAoB,EAAE;QACzC,IAAI,KAAK,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACpB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,KAAc;QAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,SAAS,qDAAG,KAAK,CAAC,CAAA,EAAA,CAAC;QACnD,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,WAAC,OAAA,MAAA,IAAI,CAAC,KAAK,qDAAG,IAAI,EAAE,OAAO,CAAC,CAAA,EAAA,CAAC;QAC7D,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE;;YACtB,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,KAAK,IAAI,CAAC;gBACV,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACnC,MAAA,IAAI,CAAC,SAAS,qDAAG,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC;QAEF,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,YAAY;IAEZ,MAAM,CAAC,QAAqB,EAAE,WAAwB,EAAE,cAAc,GAAG,KAAK;QAC1E,IAAI,cAAc,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAC3C,OAAO,CAAC,CAAC;QAEb,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,IAAI,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACtF,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC3C,CAAC;IAED,oBAAoB,CAAC,QAAqB;QACtC,IAAI,IAAI,CAAC,oBAAoB;YACzB,OAAO;QACX,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACzH,CAAC;IAED,oBAAoB,CAAC,QAAqB;QACtC,KAAK,IAAI,MAAM,IAAI,IAAI,CAAC,OAAO;YAC3B,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IAED,UAAU,CAAC,QAAqB,EAAE,WAAwB;;QACtD,QAAQ,CAAC,QAAQ,CACb,GAAG,MAAA,IAAI,CAAC,MAAM,mCAAI,EAAE,EAAE;cACpB,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE;cACnF,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE;QACrC,yBAAyB;SAC5B,CAAC;QACF,WAAW,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,UAAU,CAAC,QAAqB,EAAE,WAAwB;QACtD,IAAI,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAC7C,OAAO,aAAa,CAAC,MAAM,GAAG,WAAW,CAAC,QAAQ;YAC9C,aAAa,CAAC,KAAK,EAAE,CAAC;QAC1B,KAAK,IAAI,IAAI,IAAI,aAAa,EAAE;YAC5B,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACnC,WAAW,CAAC,OAAO,EAAE,CAAC;SACzB;IACL,CAAC;IAED,aAAa,CAAC,QAAqB,EAAE,IAAY;;QAC7C,QAAQ,CAAC,QAAQ,CAAC,GAAG,MAAA,IAAI,CAAC,MAAM,mCAAI,EAAE,KAAK,IAAA,cAAM,EAAC,aAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,cAAc,CAAC,QAAqB,EAAE,WAAwB,EAAE,cAAc,GAAG,KAAK;QAClF,IAAI,WAAW,CAAC,QAAQ,IAAI,CAAC;YACzB,OAAO,CAAC,CAAC;QAEb,IAAI,WAAW,GAAG;YACd,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,CAAC;SACf,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAE5E,IAAI,iBAAiB,GAAG,WAAW,CAAC,QAAQ,CAAC;QAC7C,IAAI,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAE3C,IAAI,cAAc,EAAE;YAChB,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;SACtE;aAAM;YACH,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;SACtE;QAED,8DAA8D;QAC9D,OAAO,cAAc,CAAC,MAAM,GAAG,iBAAiB,EAAE;YAC9C,IAAI,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;YACvE,IAAI,SAAS,GAAG,CAAC;gBACb,MAAM;YAEV,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;SACvC;QAED,iEAAiE;QAEjE,OAAO,cAAc,CAAC,MAAM,GAAG,iBAAiB,EAAE;YAC9C,IAAI,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC;YACpE,IAAI,SAAS,GAAG,CAAC;gBACb,MAAM;YAEV,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;SACvC;QAED,wFAAwF;QAExF,IAAI,cAAc,CAAC,MAAM,GAAG,iBAAiB;YACzC,cAAc,CAAC,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;QAEpE,IAAI,qBAAqB,GAAG,WAAW,CAAC,QAAQ,GAAG,cAAc,CAAC,MAAM,CAAC;QACzE,KAAK,IAAI,IAAI,IAAI,cAAc,EAAE;YAC7B,IAAI,gBAAgB,GAAG,IAAI,WAAW,CAAC,CAAC,GAAG,qBAAqB,CAAC,CAAC;YAClE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;YACxD,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YAC/C,qBAAqB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YACzD,qBAAqB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;SAC9D;IACL,CAAC;IAED,UAAU;IAEF,eAAe,CAAC,CAAc;QAClC,IAAI,MAAM,GAAG;YACT,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAA,cAAM,EAAC,aAAK,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;YACpG,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAA,iBAAK,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,EAAE,UAAU,CAAC;YACtF,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAA,iBAAK,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,EAAE,OAAO,CAAC;YAChF,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,IAAA,iBAAK,EAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,SAAS,CAAC;SACvF,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAEjB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YACnB,OAAO,EAAE,CAAC;QAEd,OAAO,KAAK,IAAA,cAAM,EAAC,aAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,CAAC;IAEO,cAAc,CAAC,CAAc,EAAE,KAAa,EAAE,MAAqB;QACvE,IAAI,KAAK,IAAI,CAAC;YACV,OAAO,SAAS,CAAC;QACrB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IACjF,CAAC;IAEO,WAAW,CAAC,IAAY,EAAE,SAAwB,IAAI,CAAC,MAAM;QACjE,IAAI,MAAM,KAAK,SAAS,EAAE;YACtB,IAAI,GAAG,IAAA,cAAM,EAAC,aAAK,CAAC,KAAK,CAAC,aAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjD;aAAM,IAAI,MAAM,KAAK,UAAU,EAAE;YAC9B,IAAI,GAAG,IAAA,cAAM,EAAC,aAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;SACrC;aAAM,IAAI,MAAM,KAAK,OAAO,EAAE;YAC3B,IAAI,GAAG,IAAA,cAAM,EAAC,aAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SACnC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,aAAa,CAAC,QAAqB,EAAE,SAAwB,IAAI,CAAC,MAAM;QAC5E,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO;YACvB,OAAO,mBAAmB,CAAC;aAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU;YAC/B,OAAO,sBAAsB,CAAC;QAElC,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC;CACJ;AAnQD,0BAmQC;AAED,MAAa,mBAAmB;IAC5B,YAAoB,IAAc;QAAd,SAAI,GAAJ,IAAI,CAAU;QAIf,YAAO,GAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;QAGjF,oBAAe,GAAG,CAAC,CAAA;QANvB,IAAI,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;IAOM,IAAI;QACP,IAAI,CAAC,eAAe,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;QACnE,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE;YAC7C,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC1B;IACL,CAAC;IAEM,MAAM;;QACT,IAAI,CAAA,MAAA,IAAI,CAAC,IAAI,0CAAE,MAAM,MAAK,OAAO;YAC7B,OAAO,mBAAmB,CAAC;aAC1B,IAAI,CAAA,MAAA,IAAI,CAAC,IAAI,0CAAE,MAAM,MAAK,UAAU;YACrC,OAAO,sBAAsB,CAAC;QAElC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC7C,CAAC;IAEM,SAAS;QACZ,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAA;IACpB,CAAC;IAEM,KAAK,CAAC,EAAe,EAAE,QAAQ,GAAG,GAAG;QACxC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE;YACvB,IAAI,CAAC,IAAI,EAAE,CAAA;YAEX,IAAI,EAAE,EAAE;gBACJ,EAAE,EAAE,CAAA;aACP;QACL,CAAC,EAAE,QAAQ,CAAC,CAAA;IAChB,CAAC;IAEM,IAAI;QACP,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC1B,CAAC;CACJ;AA5CD,kDA4CC;AAED,SAAS,cAAc,CAAC,QAAgB;IAEpC,MAAM,QAAQ,GAAG,IAAI,CAAC;IACtB,MAAM,WAAW,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAEnC,IAAI,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC,QAAQ,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC;IACjE,IAAI,OAAO,GAAG,CAAC,QAAQ,GAAG,CAAC,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC;IACtF,IAAI,EAAE,GAAG,QAAQ,GAAG,IAAI,CAAC;IAEzB,OAAO,GAAG,IAAA,mBAAO,EAAC,KAAK,CAAC,IAAI,IAAA,mBAAO,EAAC,OAAO,CAAC,IAAI,IAAA,mBAAO,EAAC,OAAO,CAAC,IAAI,IAAA,mBAAO,EAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;AACzF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alterior/terminal",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"mute-stream": "~1.0.0"
|
|
10
|
+
"mute-stream": "~1.0.0",
|
|
11
|
+
"@alterior/functions": "^3.11.1"
|
|
11
12
|
},
|
|
12
13
|
"devDependencies": {
|
|
13
14
|
"@types/mute-stream": "^0.0.1"
|
|
@@ -27,5 +28,5 @@
|
|
|
27
28
|
"test": "npm run build && node dist/test",
|
|
28
29
|
"prepublishOnly": "npm test"
|
|
29
30
|
},
|
|
30
|
-
"gitHead": "
|
|
31
|
-
}
|
|
31
|
+
"gitHead": "87f31ea4078faaca53b45573c6a0013cdb40dd07"
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
package/src/task-list.ts
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import { count, zeroPad } from '@alterior/functions';
|
|
2
|
+
import { startStyle, style, styled } from './style';
|
|
3
|
+
|
|
4
|
+
const CLI_TASK_ERROR_ICON = '𐄂';
|
|
5
|
+
const CLI_TASK_FINISHED_ICON = '✓';
|
|
6
|
+
const INDENT = ' ';
|
|
7
|
+
|
|
8
|
+
export interface CLITaskInit {
|
|
9
|
+
omitInNonInteractive?: boolean;
|
|
10
|
+
parentTask?: CLITask;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class CLITaskList {
|
|
14
|
+
constructor(readonly interactive = true, readonly silent = false) {
|
|
15
|
+
this.globalTask.onUpdated = task => {
|
|
16
|
+
if (!this.interactive) {
|
|
17
|
+
if (task.children.length === 0)
|
|
18
|
+
task.renderNonInteractive(this);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// When the last task completes, we need to re-render inline to be sure we render the right state.
|
|
22
|
+
// But we only want to do this once, and only when needed, otherwise its a waste.
|
|
23
|
+
if (this.globalTask.children.every(t => t.status !== 'running'))
|
|
24
|
+
this.render();
|
|
25
|
+
};
|
|
26
|
+
this.globalTask.onLog = (task: CLITask, message: string) => {
|
|
27
|
+
if (this.interactive)
|
|
28
|
+
return;
|
|
29
|
+
|
|
30
|
+
task.renderNonInteractive(this);
|
|
31
|
+
for (let line of message.split(/\r?\n/))
|
|
32
|
+
task.renderLogLine(this, line);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private timer;
|
|
37
|
+
private renderedLineCount = 0;
|
|
38
|
+
readonly spinner = new CLITaskStatusWidget();
|
|
39
|
+
private globalTask = new CLITask('Global Task');
|
|
40
|
+
|
|
41
|
+
private started = false;
|
|
42
|
+
startCursor: { x: number, y: number } = { x: 0, y: 0 };
|
|
43
|
+
|
|
44
|
+
private start() {
|
|
45
|
+
if (!this.interactive)
|
|
46
|
+
return;
|
|
47
|
+
|
|
48
|
+
if (this.started)
|
|
49
|
+
return;
|
|
50
|
+
|
|
51
|
+
this.started = true;
|
|
52
|
+
this.timer = setInterval(() => this.render(), 1000 / 30);
|
|
53
|
+
this.spinner.start();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
stop() {
|
|
57
|
+
if (!this.interactive)
|
|
58
|
+
return;
|
|
59
|
+
if (!this.started)
|
|
60
|
+
return;
|
|
61
|
+
|
|
62
|
+
// Render once more to be sure we capture the final state.
|
|
63
|
+
this.render();
|
|
64
|
+
|
|
65
|
+
this.started = false;
|
|
66
|
+
clearInterval(this.timer);
|
|
67
|
+
this.timer = undefined;
|
|
68
|
+
this.spinner.stop();
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
this.renderedLineCount = 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get remainingLines() {
|
|
75
|
+
// The -2 here is:
|
|
76
|
+
// - (x) Its desirable to keep the prompt line visible at the top of the screen if possible (-1)
|
|
77
|
+
// - We will have one empty line visible at the end of the output to avoid flickering the
|
|
78
|
+
// cursor (-1)
|
|
79
|
+
return Math.max(0, process.stdout.rows - this.renderedLineCount - 1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
startTask(title: string, init?: CLITaskInit) {
|
|
83
|
+
let task = new CLITask(title, init);
|
|
84
|
+
this.addTask(task);
|
|
85
|
+
return task;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addTask(task: CLITask) {
|
|
89
|
+
if (!this.started) {
|
|
90
|
+
this.start();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.globalTask.addTask(task);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
get allTasksFinished() {
|
|
97
|
+
return this.globalTask.children.every(x => x.status !== 'running');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
idle = false;
|
|
101
|
+
private idleTimeout: any;
|
|
102
|
+
beforeCpu: NodeJS.CpuUsage;
|
|
103
|
+
cpu: NodeJS.CpuUsage;
|
|
104
|
+
ram: NodeJS.MemoryUsage;
|
|
105
|
+
|
|
106
|
+
lastTimestamp: bigint = BigInt(0);
|
|
107
|
+
secondTicks = 1_000_000;
|
|
108
|
+
startedAt = Date.now();
|
|
109
|
+
|
|
110
|
+
private render() {
|
|
111
|
+
let newSecond = process.hrtime.bigint();
|
|
112
|
+
if (this.lastTimestamp + BigInt(1_000_000_000) < newSecond) {
|
|
113
|
+
this.secondTicks = Number((newSecond - this.lastTimestamp) / BigInt(1_000));
|
|
114
|
+
this.lastTimestamp = newSecond;
|
|
115
|
+
this.cpu = process.cpuUsage(this.beforeCpu);
|
|
116
|
+
this.beforeCpu = process.cpuUsage();
|
|
117
|
+
this.ram = process.memoryUsage();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.startFrame();
|
|
121
|
+
|
|
122
|
+
// other cool u characters include: 𝖚
|
|
123
|
+
// see more: https://unicodeplus.com/U+0075
|
|
124
|
+
|
|
125
|
+
let elapsedTime = formatDuration(Date.now() - this.startedAt)
|
|
126
|
+
|
|
127
|
+
this.drawHeader();
|
|
128
|
+
|
|
129
|
+
this.globalTask.renderChildren(this, new LineTracker(this.remainingLines));
|
|
130
|
+
this.endFrame();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
drawHeader() {
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private startFrame() {
|
|
138
|
+
process.stdout.cork();
|
|
139
|
+
process.stdout.moveCursor(0, -this.renderedLineCount);
|
|
140
|
+
this.renderedLineCount = 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private endFrame() {
|
|
144
|
+
process.stdout.clearScreenDown();
|
|
145
|
+
process.stdout.uncork();
|
|
146
|
+
|
|
147
|
+
// Update idle
|
|
148
|
+
if (this.idle !== this.allTasksFinished) {
|
|
149
|
+
this.idle = this.allTasksFinished;
|
|
150
|
+
if (this.idle) {
|
|
151
|
+
this.idleTimeout = setTimeout(() => {
|
|
152
|
+
if (this.idle) {
|
|
153
|
+
this.stop();
|
|
154
|
+
}
|
|
155
|
+
}, 1_000);
|
|
156
|
+
} else {
|
|
157
|
+
this.idle = false;
|
|
158
|
+
clearTimeout(this.idleTimeout);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
drawLine(input: string = ''): void {
|
|
164
|
+
if (this.silent)
|
|
165
|
+
return;
|
|
166
|
+
|
|
167
|
+
if (!this.interactive) {
|
|
168
|
+
console.log(input);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let lines = input.split(/\r?\n/);
|
|
173
|
+
let count = 0;
|
|
174
|
+
|
|
175
|
+
for (let line of lines) {
|
|
176
|
+
process.stdout.clearLine(0);
|
|
177
|
+
process.stdout.write(`${this.truncateLineIfNeeded(line)}\n`);
|
|
178
|
+
process.stdout.clearLine(1);
|
|
179
|
+
|
|
180
|
+
this.renderedLineCount += 1;
|
|
181
|
+
count += 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private truncateLineIfNeeded(line: string) {
|
|
186
|
+
let indicatorPattern = /\u001b\[[^m]*m/g;
|
|
187
|
+
let visibleLength = line.replace(indicatorPattern, '').length;
|
|
188
|
+
if (visibleLength < process.stdout.columns)
|
|
189
|
+
return line;
|
|
190
|
+
|
|
191
|
+
let newLine = '';
|
|
192
|
+
let newVisible = 0;
|
|
193
|
+
|
|
194
|
+
for (let i = 0, max = line.length; i < max && newVisible < process.stdout.columns - 3; ++i) {
|
|
195
|
+
let match = /^\u001b\[[^m]*m/.exec(line.slice(i));
|
|
196
|
+
if (match) {
|
|
197
|
+
newLine += `${match[0]}`;
|
|
198
|
+
i += match[0].length - 1;
|
|
199
|
+
} else {
|
|
200
|
+
newLine += line.at(i);
|
|
201
|
+
newVisible += 1;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
newLine += styled(style.$dim('...'));
|
|
206
|
+
|
|
207
|
+
// We may have cut off style codes by eliding this line. To prevent weirdness, we must
|
|
208
|
+
// reset the terminal back to default
|
|
209
|
+
newLine += startStyle('reset');
|
|
210
|
+
return newLine;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
class LineTracker {
|
|
215
|
+
constructor(private _maxLines: number) {
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static unbounded = new LineTracker(undefined);
|
|
219
|
+
private _consumed = 0;
|
|
220
|
+
|
|
221
|
+
get maxLines() {
|
|
222
|
+
if (this._maxLines === undefined)
|
|
223
|
+
return Number.MAX_SAFE_INTEGER;
|
|
224
|
+
return this._maxLines;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
get consumed() {
|
|
228
|
+
if (this._maxLines === undefined)
|
|
229
|
+
return 0;
|
|
230
|
+
return this._consumed;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
consume(count: number = 1) {
|
|
234
|
+
if (this._maxLines !== undefined)
|
|
235
|
+
this._maxLines -= count;
|
|
236
|
+
this._consumed += count;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export type CLITaskCharm = () => string;
|
|
241
|
+
export type CLITaskStatus = 'running' | 'error' | 'finished';
|
|
242
|
+
export class CLITask {
|
|
243
|
+
constructor(title: string, init: CLITaskInit = {}) {
|
|
244
|
+
this.title = title;
|
|
245
|
+
if (init.omitInNonInteractive)
|
|
246
|
+
this.omitInNonInteractive = true;
|
|
247
|
+
}
|
|
248
|
+
parent?: CLITask;
|
|
249
|
+
children: CLITask[] = [];
|
|
250
|
+
title: string;
|
|
251
|
+
status: CLITaskStatus = 'running';
|
|
252
|
+
finishedAt: number;
|
|
253
|
+
startedAt = Date.now();
|
|
254
|
+
|
|
255
|
+
charms: CLITaskCharm[] = [];
|
|
256
|
+
staleAfter = 0;
|
|
257
|
+
staleWithErrorsAndLogAfter = 10_000;
|
|
258
|
+
omitInNonInteractive = false;
|
|
259
|
+
|
|
260
|
+
private logMessages: string[] = [];
|
|
261
|
+
|
|
262
|
+
// Computed Properties
|
|
263
|
+
|
|
264
|
+
get depth() {
|
|
265
|
+
return (this.parent?.depth ?? -1) + 1;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
get indent() {
|
|
269
|
+
if (this.depth === 0)
|
|
270
|
+
return ``;
|
|
271
|
+
return Array.from(Array(this.depth - 1)).map(() => INDENT).join('');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
get isFinishedAndStale(): boolean {
|
|
275
|
+
let staleTime = this.containsLogsOrError ? this.staleWithErrorsAndLogAfter : this.staleAfter;
|
|
276
|
+
let freshness = Math.min(staleTime);
|
|
277
|
+
return this.status === 'finished' && this.finishedAt + freshness < Date.now();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
get containsLogsOrError(): boolean {
|
|
281
|
+
return this.logMessages.length > 0 || this.status === 'error' || this.children.some(x => x.containsLogsOrError);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
get isDeleted() {
|
|
285
|
+
return this.parent ? !this.parent?.children.includes(this) : false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
get parents() {
|
|
289
|
+
let parents: CLITask[] = [];
|
|
290
|
+
let parent = this.parent;
|
|
291
|
+
while (parent?.parent) {
|
|
292
|
+
parents.unshift(parent);
|
|
293
|
+
parent = parent.parent;
|
|
294
|
+
}
|
|
295
|
+
return parents;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
get chain() {
|
|
299
|
+
return [...this.parents, this];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Events
|
|
303
|
+
|
|
304
|
+
onUpdated: (task: CLITask) => void;
|
|
305
|
+
onDeleted: (task: CLITask) => void;
|
|
306
|
+
onLog: (task: CLITask, message: string) => void;
|
|
307
|
+
|
|
308
|
+
// Lifecycle API
|
|
309
|
+
|
|
310
|
+
log(message: string) {
|
|
311
|
+
this.logMessages.push(...message.split(/\r?\n/));
|
|
312
|
+
this.onLog?.(this, message);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
error(message?: string) {
|
|
316
|
+
this.status = 'error';
|
|
317
|
+
this.log(`[Error] ${message || 'The task failed.'}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
finish(notify = true) {
|
|
321
|
+
this.status = 'finished';
|
|
322
|
+
this.finishedAt = Date.now();
|
|
323
|
+
|
|
324
|
+
for (let child of this.children)
|
|
325
|
+
child.finish(false);
|
|
326
|
+
|
|
327
|
+
if (notify)
|
|
328
|
+
this.onUpdated?.(this);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
delete() {
|
|
332
|
+
this.onDeleted?.(this);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Subtasks
|
|
336
|
+
|
|
337
|
+
subtask(title: string, init: CLITaskInit = {}): CLITask {
|
|
338
|
+
let child = new CLITask(title, init);
|
|
339
|
+
this.addTask(child);
|
|
340
|
+
return child;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
addTask(child: CLITask) {
|
|
344
|
+
this.children.push(child);
|
|
345
|
+
child.parent = this;
|
|
346
|
+
child.onUpdated = child => this.onUpdated?.(child);
|
|
347
|
+
child.onLog = (task, message) => this.onLog?.(task, message);
|
|
348
|
+
child.onDeleted = child => {
|
|
349
|
+
let index = this.children.indexOf(child);
|
|
350
|
+
if (index >= 0)
|
|
351
|
+
this.children.splice(index, 1);
|
|
352
|
+
this.onUpdated?.(child);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
return child;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Rendering
|
|
359
|
+
|
|
360
|
+
render(renderer: CLITaskList, lineTracker: LineTracker, parentFinished = false) {
|
|
361
|
+
if (parentFinished && !this.containsLogsOrError)
|
|
362
|
+
return 0;
|
|
363
|
+
|
|
364
|
+
this.renderSelf(renderer, lineTracker);
|
|
365
|
+
this.renderChildren(renderer, lineTracker, parentFinished || this.isFinishedAndStale);
|
|
366
|
+
this.renderLogs(renderer, lineTracker);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
renderNonInteractive(renderer: CLITaskList) {
|
|
370
|
+
if (this.omitInNonInteractive)
|
|
371
|
+
return;
|
|
372
|
+
renderer.drawLine(this.chain.filter(x => !x.omitInNonInteractive).map(p => `${p.statusStyle(p.title)}`).join(' » '));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
renderSelfAndParents(renderer: CLITaskList) {
|
|
376
|
+
for (let parent of this.parents)
|
|
377
|
+
parent.renderSelf(renderer, LineTracker.unbounded);
|
|
378
|
+
this.renderSelf(renderer, LineTracker.unbounded);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
renderSelf(renderer: CLITaskList, lineTracker: LineTracker) {
|
|
382
|
+
renderer.drawLine(
|
|
383
|
+
`${this.indent ?? ''}`
|
|
384
|
+
+ `${this.statusStyle(`${this.iconForStatus(renderer)} ${this.title}`, this.status)}`
|
|
385
|
+
+ `${this.summarizeCounts(renderer)}`
|
|
386
|
+
//+ ` DEBUG: ${maxLines}`
|
|
387
|
+
);
|
|
388
|
+
lineTracker.consume();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
renderLogs(renderer: CLITaskList, lineTracker: LineTracker) {
|
|
392
|
+
let displayedLogs = this.logMessages.slice();
|
|
393
|
+
while (displayedLogs.length > lineTracker.maxLines)
|
|
394
|
+
displayedLogs.shift();
|
|
395
|
+
for (let line of displayedLogs) {
|
|
396
|
+
this.renderLogLine(renderer, line);
|
|
397
|
+
lineTracker.consume();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
renderLogLine(renderer: CLITaskList, line: string) {
|
|
402
|
+
renderer.drawLine(`${this.indent ?? ''} ${styled(style.$dim(line))}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
renderChildren(renderer: CLITaskList, lineTracker: LineTracker, parentFinished = false) {
|
|
406
|
+
if (lineTracker.maxLines <= 0)
|
|
407
|
+
return 0;
|
|
408
|
+
|
|
409
|
+
let statusOrder = {
|
|
410
|
+
'error': 0,
|
|
411
|
+
'finished': 1,
|
|
412
|
+
'running': 2
|
|
413
|
+
};
|
|
414
|
+
this.children.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
|
|
415
|
+
|
|
416
|
+
let maxDisplayedTasks = lineTracker.maxLines;
|
|
417
|
+
let displayedTasks = this.children.slice();
|
|
418
|
+
|
|
419
|
+
if (parentFinished) {
|
|
420
|
+
displayedTasks = displayedTasks.filter(x => x.containsLogsOrError);
|
|
421
|
+
} else {
|
|
422
|
+
displayedTasks = displayedTasks.filter(x => !x.isFinishedAndStale);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// If we're tight on space, elide freshly finished tasks, too.
|
|
426
|
+
while (displayedTasks.length > maxDisplayedTasks) {
|
|
427
|
+
let taskIndex = displayedTasks.findIndex(x => x.status === 'finished');
|
|
428
|
+
if (taskIndex < 0)
|
|
429
|
+
break;
|
|
430
|
+
|
|
431
|
+
displayedTasks.splice(taskIndex, 1);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Elide errors (better to see ongoing progress) ////////////////
|
|
435
|
+
|
|
436
|
+
while (displayedTasks.length > maxDisplayedTasks) {
|
|
437
|
+
let taskIndex = displayedTasks.findIndex(x => x.status === 'error');
|
|
438
|
+
if (taskIndex < 0)
|
|
439
|
+
break;
|
|
440
|
+
|
|
441
|
+
displayedTasks.splice(taskIndex, 1);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Elide all remaining from end of sorted list until we fit within the maxDisplayedTasks
|
|
445
|
+
|
|
446
|
+
if (displayedTasks.length > maxDisplayedTasks)
|
|
447
|
+
displayedTasks.splice(maxDisplayedTasks, displayedTasks.length);
|
|
448
|
+
|
|
449
|
+
let extraLinesForChildren = lineTracker.maxLines - displayedTasks.length;
|
|
450
|
+
for (let task of displayedTasks) {
|
|
451
|
+
let childLineTracker = new LineTracker(1 + extraLinesForChildren);
|
|
452
|
+
task.render(renderer, childLineTracker, parentFinished);
|
|
453
|
+
lineTracker.consume(childLineTracker.consumed);
|
|
454
|
+
extraLinesForChildren -= (childLineTracker.consumed - 1);
|
|
455
|
+
extraLinesForChildren = Math.max(0, extraLinesForChildren);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Helpers
|
|
460
|
+
|
|
461
|
+
private summarizeCounts(r: CLITaskList) {
|
|
462
|
+
let counts = [
|
|
463
|
+
this.logMessages.length > 0 ? `${styled(style.$yellow(`• ${this.logMessages.length}`))}` : undefined,
|
|
464
|
+
this.summarizeCount(r, count(this.children, x => x.status === 'finished'), 'finished'),
|
|
465
|
+
this.summarizeCount(r, count(this.children, x => x.status === 'error'), 'error'),
|
|
466
|
+
this.summarizeCount(r, count(this.children, x => x.status === 'running'), 'running'),
|
|
467
|
+
].filter(x => x);
|
|
468
|
+
|
|
469
|
+
if (counts.length === 0)
|
|
470
|
+
return '';
|
|
471
|
+
|
|
472
|
+
return ` ${styled(style.$dim(counts.join(' ')))}`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private summarizeCount(r: CLITaskList, count: number, status: CLITaskStatus) {
|
|
476
|
+
if (count <= 0)
|
|
477
|
+
return undefined;
|
|
478
|
+
return this.statusStyle(`${this.iconForStatus(r, status)} ${count}`, status);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private statusStyle(line: string, status: CLITaskStatus = this.status) {
|
|
482
|
+
if (status === 'running') {
|
|
483
|
+
line = styled(style.$bold(style.$blue(line)));
|
|
484
|
+
} else if (status === 'finished') {
|
|
485
|
+
line = styled(style.$green(line));
|
|
486
|
+
} else if (status === 'error') {
|
|
487
|
+
line = styled(style.$red(line));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return line;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private iconForStatus(renderer: CLITaskList, status: CLITaskStatus = this.status) {
|
|
494
|
+
if (this.status === 'error')
|
|
495
|
+
return CLI_TASK_ERROR_ICON;
|
|
496
|
+
else if (this.status === 'finished')
|
|
497
|
+
return CLI_TASK_FINISHED_ICON;
|
|
498
|
+
|
|
499
|
+
return renderer.spinner.render();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export class CLITaskStatusWidget {
|
|
504
|
+
constructor(private task?: CLITask) {
|
|
505
|
+
this.start();
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
protected readonly spinner: string[] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
|
509
|
+
private id?: NodeJS.Timeout
|
|
510
|
+
|
|
511
|
+
private spinnerPosition = 0
|
|
512
|
+
|
|
513
|
+
public spin(): void {
|
|
514
|
+
this.spinnerPosition = ++this.spinnerPosition % this.spinner.length
|
|
515
|
+
if (this.task && this.task.status !== 'running') {
|
|
516
|
+
clearInterval(this.id);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
public render(): string {
|
|
521
|
+
if (this.task?.status === 'error')
|
|
522
|
+
return CLI_TASK_ERROR_ICON;
|
|
523
|
+
else if (this.task?.status === 'finished')
|
|
524
|
+
return CLI_TASK_FINISHED_ICON;
|
|
525
|
+
|
|
526
|
+
return this.spinner[this.spinnerPosition]
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
public isRunning(): boolean {
|
|
530
|
+
return !!this.id
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
public start(cb?: () => void, interval = 100): void {
|
|
534
|
+
clearInterval(this.id);
|
|
535
|
+
this.id = setInterval(() => {
|
|
536
|
+
this.spin()
|
|
537
|
+
|
|
538
|
+
if (cb) {
|
|
539
|
+
cb()
|
|
540
|
+
}
|
|
541
|
+
}, interval)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
public stop(): void {
|
|
545
|
+
clearInterval(this.id)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function formatDuration(duration: number) {
|
|
550
|
+
|
|
551
|
+
const msPerSec = 1000;
|
|
552
|
+
const msPerMinute = (1000 * 60);
|
|
553
|
+
const msPerHour = (1000 * 60 * 60);
|
|
554
|
+
|
|
555
|
+
let hours = duration / msPerHour | 0;
|
|
556
|
+
let minutes = (duration - (hours * msPerHour)) / msPerMinute | 0;
|
|
557
|
+
let seconds = (duration - (hours * msPerHour + minutes * msPerMinute)) / msPerSec | 0;
|
|
558
|
+
let ms = duration % 1000;
|
|
559
|
+
|
|
560
|
+
return `${zeroPad(hours)}:${zeroPad(minutes)}:${zeroPad(seconds)}:${zeroPad(ms, 3)}`;
|
|
561
|
+
}
|