@boost/core
Version:
Robust pipeline for creating dev tools that separate logic into routines and tasks.
97 lines (96 loc) • 3.61 kB
JavaScript
;
/* eslint-disable no-magic-numbers */
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const optimal_1 = __importStar(require("optimal"));
const common_1 = require("@boost/common");
const terminal_1 = require("@boost/terminal");
const Output_1 = __importDefault(require("../Output"));
const STYLES = {
bar: ['\u2588', '\u2591'],
classic: ['=', '-'],
hash: ['#', '-'],
pipe: ['|', '-'],
square: ['\u25A0', ' '],
};
class ProgressOutput extends Output_1.default {
constructor() {
super(...arguments);
this.startTime = 0;
this.stopTime = 0;
}
onStart() {
this.concurrent();
}
onFirst() {
this.startTime = Date.now();
}
onLast() {
this.stopTime = Date.now();
}
toString(state) {
const { color, current, style: styleName, template, total, transparent } = optimal_1.default(state, {
color: optimal_1.bool(),
current: optimal_1.number()
.required()
.gte(0),
style: optimal_1.string('bar').oneOf(Object.keys(STYLES)),
template: optimal_1.string('{percent} {bar} {progress}').notEmpty(),
total: optimal_1.number()
.required()
.gt(0),
transparent: optimal_1.bool(),
}, {
name: 'ProgressOutput',
});
// Mark as final for convenience
if (current >= total) {
this.markFinal();
}
// Compile our template
const progress = Math.min(Math.max(current / total, 0), 1);
const percent = Math.floor(progress * 100);
const elapsed = Date.now() - this.startTime;
const estimated = percent === 100 ? 0 : elapsed * (total / current - 1);
const rate = current / (elapsed / 1000);
const partialTemplate = template
.replace('{progress}', `${current}/${total}`)
.replace('{current}', String(current))
.replace('{elapsed}', common_1.formatMs(elapsed))
.replace('{estimated}', common_1.formatMs(estimated))
.replace('{percent}', `${percent.toFixed(0)}%`)
.replace('{rate}', String(rate.toFixed(2)))
.replace('{total}', String(total));
// Render the progress bar
const currentWidth = partialTemplate.replace('{bar}', '').length;
const remainingWidth = terminal_1.screen.size().columns - currentWidth;
const completed = Math.round(remainingWidth * progress);
const [complete, incomplete] = STYLES[styleName];
let bar = [
complete.repeat(Math.max(0, completed)),
(transparent ? ' ' : incomplete).repeat(Math.max(0, remainingWidth - completed)),
].join('');
if (color) {
if (percent >= 90) {
bar = terminal_1.style.green(bar);
}
else if (percent >= 45) {
bar = terminal_1.style.yellow(bar);
}
else {
bar = terminal_1.style.red(bar);
}
}
return partialTemplate.replace('{bar}', bar);
}
}
exports.default = ProgressOutput;