UNPKG

@boost/core

Version:

Robust pipeline for creating dev tools that separate logic into routines and tasks.

148 lines (147 loc) 4.01 kB
"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;