@oclif/multi-stage-output
Version:
Terminal output for oclif commands with multiple stages
102 lines (101 loc) • 3.4 kB
JavaScript
import { Performance } from '@oclif/core/performance';
export class StageTracker {
stages;
current = [];
allowParallelTasks;
map = new Map();
markers = new Map();
constructor(stages, opts) {
this.stages = stages;
this.map = new Map(stages.map((stage) => [stage, 'pending']));
this.allowParallelTasks = opts?.allowParallelTasks ?? false;
}
get size() {
return this.map.size;
}
entries() {
return this.map.entries();
}
get(stage) {
return this.map.get(stage);
}
indexOf(stage) {
return this.stages.indexOf(stage);
}
refresh(nextStage, opts) {
const stages = [...this.map.keys()];
for (const stage of stages) {
if (this.map.get(stage) === 'skipped')
continue;
if (this.map.get(stage) === 'failed')
continue;
// .stop() was called with a finalStatus
if (nextStage === stage && opts?.finalStatus) {
this.stopStage(stage, opts.finalStatus);
continue;
}
// set the current stage
if (nextStage === stage) {
this.set(stage, 'current');
// create a marker for the current stage if it doesn't exist
if (!this.markers.has(stage)) {
this.markers.set(stage, Performance.mark('MultiStageComponent', stage.replaceAll(' ', '-').toLowerCase()));
}
continue;
}
// any pending stage before the current stage should be marked using opts.bypassStatus
if (stages.indexOf(stage) < stages.indexOf(nextStage) && this.map.get(stage) === 'pending') {
this.set(stage, opts?.bypassStatus ?? 'completed');
continue;
}
// any stage before the current stage should be marked as completed (if it hasn't been marked as skipped or failed yet)
if (stages.indexOf(nextStage) > stages.indexOf(stage)) {
this.stopStage(stage, 'completed');
continue;
}
// default to pending
this.set(stage, 'pending');
}
}
set(stage, status) {
if (status === 'current') {
if (!this.current.includes(stage)) {
this.current.push(stage);
}
}
else {
this.current = this.current.filter((s) => s !== stage);
}
this.map.set(stage, status);
}
stop(currentStage, finalStatus) {
if (this.allowParallelTasks) {
for (const [stage, status] of this.entries()) {
if (status === 'current') {
this.stopStage(stage, finalStatus);
}
}
}
else {
this.refresh(currentStage, { finalStatus });
}
}
update(stage, status) {
if (status === 'completed' || status === 'failed' || status === 'aborted') {
this.stopStage(stage, status);
}
else {
this.set(stage, status);
}
}
values() {
return this.map.values();
}
stopStage(stage, status) {
this.set(stage, status);
const marker = this.markers.get(stage);
if (marker && !marker.stopped) {
marker.stop();
}
}
}