node-console-progress-bar-tqdm
Version:
Progress bar in console for Node.js in the style of TQDM Python library
276 lines • 9.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TqdmProgress = exports.Tqdm = exports.tqdm = exports.TqdmSyncResultIterator = exports.TqdmAsyncResultIterator = void 0;
const supply_1 = require("./supply");
const term_1 = require("./term");
const utils_1 = require("./utils");
var supply_2 = require("./supply");
Object.defineProperty(exports, "TqdmAsyncResultIterator", { enumerable: true, get: function () { return supply_2.TqdmAsyncResultIterator; } });
Object.defineProperty(exports, "TqdmSyncResultIterator", { enumerable: true, get: function () { return supply_2.TqdmSyncResultIterator; } });
const defaultOptions = {
description: '',
maxColWidth: -1,
progressBraces: ['|', '|'],
progressSymbol: '█',
progressColor: '',
unit: 'it',
unitScale: false,
initial: 0,
total: -1,
step: 1,
stream: process.stderr,
minInterval: 50,
forceTerminal: false,
};
function tqdm(input, opts = {}) {
return new Tqdm(input, opts);
}
exports.tqdm = tqdm;
class Tqdm {
input;
iterator;
progress;
constructor(input, options = {}) {
this.input = input;
if (typeof this.input == 'number') {
this.iterator = new supply_1.TqdmNumericIterator(this.input);
}
else if ((0, utils_1.isIterable)(this.input)) {
this.iterator = this.input[Symbol.iterator]();
}
else if ((0, utils_1.isAsyncIterable)(this.input)) {
this.iterator = this.input[Symbol.asyncIterator]();
}
else if ((0, utils_1.isIterator)(this.input)) {
this.iterator = this.input;
}
else {
throw new Error('Unknown TQDM input type');
}
if (options.total === undefined || options.total < 0) {
if ((0, utils_1.hasLength)(this.input)) {
options.total = this.input.length;
}
else if (typeof this.input == 'number') {
options.total = this.input;
}
}
this.progress = new TqdmProgress(options);
this.progress.render();
}
[Symbol.iterator]() {
return new supply_1.TqdmSyncResultIterator(this);
}
[Symbol.asyncIterator]() {
return new supply_1.TqdmAsyncResultIterator(this);
}
nextSync() {
const res = this.iterator.next();
if (res instanceof Promise) {
throw new Error('Async value in sync iterator');
}
if (res.done) {
this.progress.close();
}
else {
this.progress.update();
}
return res;
}
nextAsync() {
const pRes = this.iterator.next();
if (!(pRes instanceof Promise)) {
throw new Error('Sync value in async iterator');
}
return new Promise((resolve, reject) => {
pRes.then((res) => {
if (res.done) {
this.progress.close();
}
else {
this.progress.update();
}
resolve(res);
}).catch(reject);
});
}
}
exports.Tqdm = Tqdm;
class TqdmProgress {
description;
unit;
maxColWidth;
progressLeftBrace;
progressRightBrace;
progressSymbol;
progressColor = '';
progressColorReset = '';
stream;
minInterval;
total;
totalDigits = 0;
initial;
step;
unitScale;
unitDelimiter = '';
totalScaled = '';
haveCorrectTotal;
startTime = Date.now();
lastRenderTime = 0;
timeSpent = 0;
position;
itCounter = 0;
num;
static withProgress(fn, options = {}) {
const progressBar = new TqdmProgress(options);
try {
progressBar.render();
return fn(progressBar);
}
finally {
progressBar.close();
}
}
static async withAsyncProgress(fn, options = {}) {
const progressBar = new TqdmProgress(options);
try {
progressBar.render();
return await fn(progressBar);
}
finally {
progressBar.close();
}
}
constructor(options) {
const fullOptions = {
...defaultOptions,
...options,
};
this.description = fullOptions.description;
this.unit = (0, utils_1.handleUnit)(fullOptions.unit);
this.maxColWidth = fullOptions.maxColWidth;
[this.progressLeftBrace, this.progressRightBrace] = fullOptions.progressBraces;
this.progressSymbol = fullOptions.progressSymbol;
this.stream = new supply_1.TqdmWriteStream(fullOptions.stream, fullOptions.forceTerminal);
this.minInterval = fullOptions.minInterval;
if (this.stream.isTty) {
this.progressColor = (0, term_1.getTermColor)(fullOptions.progressColor);
if (this.progressColor) {
this.progressColorReset = (0, term_1.getTermColorReset)();
}
}
this.initial = this.position = fullOptions.initial;
this.total = fullOptions.total;
this.haveCorrectTotal = isFinite(this.total) && !isNaN(this.total) && this.total > 0;
if (this.haveCorrectTotal) {
this.totalDigits = Math.trunc(Math.log10(this.total)) + 1;
}
this.step = fullOptions.step;
this.unitScale = fullOptions.unitScale;
if (fullOptions.unitScale) {
this.num = this.numScale;
this.unitDelimiter = ' ';
if (this.haveCorrectTotal) {
this.totalScaled = (0, utils_1.scaleUnit)(this.total);
}
}
else {
this.num = this.numNoScale;
}
}
update(by = this.step) {
this.position += by;
++this.itCounter;
this.timeSpent = Date.now() - this.startTime;
this.render();
}
render(force = false) {
const now = Date.now();
if (!force && now - this.lastRenderTime < this.minInterval) {
return;
}
this.lastRenderTime = now;
const left = this.generateLeft();
const right = this.generateRight();
const progressBar = this.generateProgressBar(left.length + right.length);
this.stream.resetLine();
this.stream.write(`${left}${progressBar}${right}`);
}
close() {
this.render(true);
this.stream.finalize();
}
numScale(x) {
return (0, utils_1.scaleUnit)(x);
}
numNoScale(x) {
return x.toString();
}
doesPositionFitTotal() {
return this.haveCorrectTotal && this.position <= this.total;
}
generateLeft() {
let countStr;
if (this.doesPositionFitTotal()) {
const percent = Math.round(Math.min(this.position, this.total) * 100 / this.total);
countStr = percent == -1 ? '' : `${String(percent).padStart(3, ' ')}% `;
}
else {
const unitKey = utils_1.pluralService.select(this.position);
countStr = `${this.num(this.position)}${this.unitDelimiter}${this.unit[unitKey]}`;
}
const descStr = this.description ? `${this.description}: ` : '';
return ` ${descStr}${countStr}`;
}
generateRight() {
const res = [''];
if (this.doesPositionFitTotal()) {
if (this.unitScale) {
res.push(`${this.num(this.position)}/${this.totalScaled}`);
}
else {
const countStr = String(this.position).padStart(this.totalDigits, ' ');
res.push(`${countStr}/${this.total}`);
}
}
if (this.timeSpent) {
const timePerIt = this.timeSpent / this.itCounter;
const timePerItStr = (timePerIt / 1000).toFixed(3);
const timeSpentStr = (0, utils_1.formatTimeDelta)(this.timeSpent, true);
let elapsedTime = '';
if (this.doesPositionFitTotal()) {
const elapsedItems = this.step > 0 ?
Math.max(0, this.total - this.position) :
Math.max(0, this.total - (this.initial - this.position));
elapsedTime = '<' + (0, utils_1.formatTimeDelta)(timePerIt * elapsedItems, true);
}
res.push(`[${timeSpentStr}${elapsedTime}, ${timePerItStr}s/${this.unit['one']}]`);
}
if (res.length > 1) {
res.push('');
}
return res.join(' ');
}
generateProgressBar(reduceBy) {
if (!this.doesPositionFitTotal()) {
return '';
}
const ttyColumns = this.stream.columns;
const baseColumns = this.maxColWidth > 0 ? Math.min(this.maxColWidth, ttyColumns) : ttyColumns;
const columns = baseColumns - reduceBy - 2;
if (columns < 4) {
return '';
}
const cnt = Math.trunc(columns * Math.min(this.position, this.total) / this.total);
return [
this.progressLeftBrace,
this.progressColor,
this.progressSymbol.repeat(cnt),
' '.repeat(columns - cnt),
this.progressColorReset,
this.progressRightBrace,
].join('');
}
}
exports.TqdmProgress = TqdmProgress;
//# sourceMappingURL=index.js.map