@boost/core
Version:
Robust pipeline for creating dev tools that separate logic into routines and tasks.
148 lines (147 loc) • 4.01 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const term_size_1 = __importDefault(require("term-size"));
const ansi_escapes_1 = __importDefault(require("ansi-escapes"));
class Output {
constructor(cli, renderer) {
this.previousHeight = 0;
this.state = {
completed: false,
concurrent: false,
final: false,
first: true,
};
if (typeof renderer !== 'function') {
throw new TypeError('Output renderer must be a function.');
}
this.console = cli;
this.renderer = renderer;
}
/**
* Mark the output as concurrent to be rendered at the same time as other output.
*/
concurrent() {
this.state.concurrent = true;
return this;
}
/**
* Enqueue a render. Optionally mark the update as the final render.
*/
enqueue(final = false) {
if (this.isComplete()) {
return this;
}
if (final) {
this.markFinal();
}
else {
this.onStart();
}
this.console.render(this);
return this;
}
/**
* Erase the previous content if it exists.
*/
erasePrevious() {
if (this.previousHeight > 0) {
this.console.out(ansi_escapes_1.default.eraseLines(this.previousHeight));
}
return this;
}
/**
* Return true if the output is complete and fully rendered.
*/
isComplete() {
return this.state.completed;
}
/**
* Return true if the output should be rendered concurrently.
*/
isConcurrent() {
return this.state.concurrent;
}
/**
* Return true if the next render is the final render.
*/
isFinal() {
return this.state.final;
}
/**
* Render the content to the console and calculate a new height.
* Since an output represents an exact line, or a collection of lines,
* we must always end with a new line to calculate height correctly.
*/
render() {
if (this.isComplete()) {
return this;
}
// Mark first render
if (this.state.first) {
this.state.first = false;
this.onFirst();
}
let content = this.toString(this.renderer());
// Always end with a new line
if (!content.endsWith('\n')) {
content += '\n';
}
// Content cannot be higher than the terminal
const lines = content.split('\n');
const maxHeight = term_size_1.default().rows - 1; // Buffer for input line
if (lines.length >= maxHeight) {
content = lines.slice(-maxHeight).join('\n');
}
// Write output
this.console.out(content);
// Mark output as complete if the final render
if (this.isFinal()) {
this.markComplete();
// Otherwise calculate the height of the output
}
else {
this.previousHeight = lines.length;
}
return this;
}
/**
* Mark the output as complete.
*/
markComplete() {
this.state.completed = true;
this.onComplete();
}
/**
* Mark as the final render.
*/
markFinal() {
this.state.final = true;
this.onLast();
}
/**
* Callback fired when output is completed.
*/
onComplete() { }
/**
* Callback fired before the first render.
*/
onFirst() { }
/**
* Callback fired before the last render.
*/
onLast() { }
/**
* Callback fired when the output has been enqueued.
*/
onStart() { }
/**
* Convert the renderer output to a string for the console.
*/
toString(output) {
return String(output);
}
}
exports.default = Output;