@controlplane/cli
Version:
Control Plane Corporation CLI
186 lines • 6.07 kB
JavaScript
"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