UNPKG

@controlplane/cli

Version:

Control Plane Corporation CLI

186 lines 6.07 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TtyProgress = void 0; exports.formatBarValue = formatBarValue; const cli_progress_1 = require("cli-progress"); const format_1 = require("../../../util/format"); const spinner_1 = require("../../../util/spinner"); const plain_1 = require("./plain"); // ANCHOR - Constants const PREPARING_LABEL = 'Preparing the build'; const BUILDING_LABEL = 'Building'; // ANCHOR - TtyProgress /** * Renders build progress for a terminal: the same milestone and log lines as * PlainProgress, plus an in-place byte bar for the upload and a spinner that stays lit * for the whole run. The spinner shows activity from the first moment, follows the * work through scanning and preparing, then stays pinned beneath the streaming build * log so a long quiet step never looks stuck. The upload byte bar is its own indicator, * so the spinner steps aside only while that bar is drawing. */ class TtyProgress extends plain_1.PlainProgress { constructor(env) { var _a, _b; super(env); this.bar = null; this.logStarted = false; this.stream = (_a = env.stream) !== null && _a !== void 0 ? _a : process.stderr; this.spinner = (_b = env.spinner) !== null && _b !== void 0 ? _b : new spinner_1.TerminalSpinner(this.stream, { paint: (text) => this.paint('gray', text) }); this.makeBar = env.bar; } // Public Methods // /** @inheritdoc */ onStart() { this.startOrRelabel(PREPARING_LABEL); } /** @inheritdoc */ onScanStarted(dir) { this.startOrRelabel(this.scanLabel(dir)); } /** @inheritdoc */ onScanned(index) { super.onScanned(index); this.spinner.setLabel(PREPARING_LABEL); } /** @inheritdoc */ onUploadProgress(snapshot) { if (!this.bar) { // The byte bar becomes the indicator for the upload, so the spinner steps aside. this.spinner.stop(); this.bar = this.makeBar ? this.makeBar() : this.createBar(); this.bar.start(Math.max(snapshot.totalBytes, 1), 0); } this.bar.update(snapshot.sentBytes); if (snapshot.completedFiles >= snapshot.totalFiles && snapshot.sentBytes >= snapshot.totalBytes) { this.stopBar(); super.onUploadProgress(snapshot); } } /** @inheritdoc */ onSubmitted(build) { this.spinner.stop(); this.stopBar(); super.onSubmitted(build); this.spinner.start(PREPARING_LABEL); } /** * While the build is preparing, a routine event refreshes the spinner label rather * than printing its own line; warnings and errors still persist above the spinner. * * @inheritdoc */ onBuildEvent(event) { if (this.spinner.active && !isDiagnostic(event)) { this.spinner.setLabel(event.message); return; } super.onBuildEvent(event); } /** * The first log line turns the spinner into the build's pinned footer: it keeps * spinning beneath the log while each line lands above it. * * @inheritdoc */ onBuildLog(chunk) { if (!this.logStarted) { this.logStarted = true; this.startOrRelabel(BUILDING_LABEL); } return super.onBuildLog(chunk); } /** @inheritdoc */ onDetached(build) { this.spinner.stop(); super.onDetached(build); } /** @inheritdoc */ onInterrupted(build) { this.spinner.stop(); this.stopBar(); super.onInterrupted(build); } /** @inheritdoc */ finish() { this.spinner.stop(); this.stopBar(); super.finish(); } // Private Methods // /** * Routes a finished line through the spinner when one is active, so it lands above * the animation instead of colliding with it. * * @param {string} line - The line to write. * @returns {void} */ err(line) { if (this.spinner.active) { this.spinner.persist(line); return; } super.err(line); } /** * Shows the spinner with a label: relabels the running one, or starts it fresh. * * @param {string} label - The label to show. * @returns {void} */ startOrRelabel(label) { if (this.spinner.active) { this.spinner.setLabel(label); return; } this.spinner.start(label); } /** * Builds the default byte progress bar drawing on the renderer's stream. * * @returns {ProgressBarLike} The progress bar. */ createBar() { return new cli_progress_1.SingleBar({ format: ' {bar} {percentage}% | {value}/{total}', stream: this.stream, clearOnComplete: true, formatValue: formatBarValue, }, cli_progress_1.Presets.shades_grey); } /** * Stops and releases the upload bar when one is active. * * @returns {void} */ stopBar() { if (this.bar) { this.bar.stop(); this.bar = null; } } } exports.TtyProgress = TtyProgress; // SECTION - Functions /** * Formats the bar's byte tokens as human sizes; every other token prints as-is. * * @param {number} value - The token's numeric value. * @param {Options} options - The bar's options. * @param {ValueType} type - The token being formatted. * @returns {string} The display value. */ function formatBarValue(value, options, type) { return type === 'value' || type === 'total' ? (0, format_1.humanSize)(value) : String(value); } /** * Whether an event is a warning or error, which must surface rather than be folded * into the spinner label. * * @param {BuildEvent} event - The service event. * @returns {boolean} True for a warning or error. */ function isDiagnostic(event) { return event.level === 'error' || event.level === 'warn' || event.level === 'warning'; } // !SECTION //# sourceMappingURL=tty.js.map