UNPKG

@vendure/core

Version:

A modern, headless ecommerce framework

117 lines 5.07 kB
"use strict"; /** * Minimal vendored replacement for the `progress` npm package. Trimmed to the * exact surface used by the Importer (and by the dev-server load-testing * benchmark scripts). * * Supports the format tokens Vendure templates use: * * `:bar` width-aware progress bar (capped to terminal columns) * `:current` current tick count * `:total` total tick count * `:percent` completion percentage (e.g. "42%") * `:etas` estimated seconds remaining (e.g. "12s") * * Plus arbitrary custom tokens supplied via `tick({ tokenName: value })`. * * Renders are throttled (default 16ms between draws) to avoid hammering the * terminal. The bar redraws in-place using `\r` and clears the trailing line * with `\x1B[2K`. Output goes to `process.stderr` by default to match the * upstream `progress` package's behaviour. * * Deliberately omitted from the upstream surface (none of which the Importer * uses): the `head` character on the leading edge of the bar, the `clear` * option that wipes the bar on completion, the `:elapsed` and `:rate` tokens, * and the `update()` method. * * Non-TTY behaviour (CI logs, piped output): like upstream `progress`, output * is suppressed entirely so log files don't get peppered with `\r\x1B[2K` * escape sequences. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ProgressBar = void 0; const ANSI_CLEAR_LINE = '\x1B[2K'; class ProgressBar { constructor(fmt, options) { var _a, _b, _c, _d, _e; this.current = 0; this.startTime = 0; this.lastRender = 0; this.finished = false; this.fmt = fmt; this.total = options.total; this.width = (_a = options.width) !== null && _a !== void 0 ? _a : 40; this.complete = (_b = options.complete) !== null && _b !== void 0 ? _b : '='; this.incomplete = (_c = options.incomplete) !== null && _c !== void 0 ? _c : '-'; this.renderThrottle = (_d = options.renderThrottle) !== null && _d !== void 0 ? _d : 16; this.stream = (_e = options.stream) !== null && _e !== void 0 ? _e : process.stderr; } /** * Advance by 1 (or by the supplied delta if a number is passed). When an * object is passed, advance by 1 and substitute each entry as a custom * format token (e.g. `:prodName`). */ tick(deltaOrTokens) { if (this.finished) return; let delta = 1; let tokens; if (typeof deltaOrTokens === 'number') { delta = deltaOrTokens; } else if (deltaOrTokens && typeof deltaOrTokens === 'object') { tokens = deltaOrTokens; } this.current += delta; if (this.startTime === 0) { this.startTime = Date.now(); } const now = Date.now(); const isComplete = this.current >= this.total; if (!isComplete && now - this.lastRender < this.renderThrottle) { return; } this.lastRender = now; this.render(tokens); if (isComplete) { this.finished = true; this.stream.write('\n'); } } render(tokens) { var _a; // Match the original `progress` package: silent in non-TTY environments // (CI logs, file redirects) so we don't spam escape sequences into logs. const tty = this.stream; if (!tty.isTTY) return; const ratio = Math.min(Math.max(this.current / this.total, 0), 1); const percent = `${Math.floor(ratio * 100)}%`; const elapsedMs = Date.now() - this.startTime; const etaSeconds = this.current === 0 ? 0 : (elapsedMs / this.current) * (this.total - this.current) / 1000; const etas = this.current >= this.total ? '0s' : `${Math.round(etaSeconds)}s`; let output = this.fmt .replace(':current', String(this.current)) .replace(':total', String(this.total)) .replace(':percent', percent) .replace(':etas', etas); if (output.includes(':bar')) { // Cap bar width to whatever space the terminal can show after the // non-bar prefix/suffix. Without this, narrow terminals overflow. const nonBarLen = output.replace(':bar', '').length; const available = Math.max(0, ((_a = tty.columns) !== null && _a !== void 0 ? _a : this.width + nonBarLen) - nonBarLen); const barWidth = Math.min(this.width, available); const completeLength = Math.round(barWidth * ratio); const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(barWidth - completeLength); output = output.replace(':bar', bar); } if (tokens) { for (const [name, value] of Object.entries(tokens)) { output = output.replace(`:${name}`, String(value)); } } this.stream.write(`\r${ANSI_CLEAR_LINE}${output}`); } } exports.ProgressBar = ProgressBar; //# sourceMappingURL=progress-bar.js.map