82 lines
2.1 KiB
JavaScript
82 lines
2.1 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
const throttle = require("lodash.throttle");
|
||
|
const readline = require("readline");
|
||
|
const tty = require("tty");
|
||
|
const util = require("util");
|
||
|
function clearStringBackwards(stream, str) {
|
||
|
readline.moveCursor(stream, -stream.columns, 0);
|
||
|
readline.clearLine(stream, 0);
|
||
|
let lineCount = (str.match(/\n/g) || []).length;
|
||
|
while (lineCount > 0) {
|
||
|
readline.moveCursor(stream, 0, -1);
|
||
|
readline.clearLine(stream, 0);
|
||
|
--lineCount;
|
||
|
}
|
||
|
}
|
||
|
function chunkString(str, size) {
|
||
|
const ANSI_COLOR = "\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?m";
|
||
|
const SKIP_ANSI = `(?:${ANSI_COLOR})*`;
|
||
|
return str.match(new RegExp(`(?:${SKIP_ANSI}.){1,${size}}`, "g")) || [];
|
||
|
}
|
||
|
function getTTYStream(stream) {
|
||
|
if (
|
||
|
stream instanceof tty.WriteStream &&
|
||
|
stream.isTTY &&
|
||
|
stream.columns >= 1
|
||
|
) {
|
||
|
return stream;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
class Terminal {
|
||
|
constructor(stream) {
|
||
|
this._logLines = [];
|
||
|
this._nextStatusStr = "";
|
||
|
this._scheduleUpdate = throttle(this._update, 33);
|
||
|
this._statusStr = "";
|
||
|
this._stream = stream;
|
||
|
}
|
||
|
_update() {
|
||
|
const { _statusStr, _stream } = this;
|
||
|
const ttyStream = getTTYStream(_stream);
|
||
|
if (_statusStr === this._nextStatusStr && this._logLines.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
if (ttyStream != null) {
|
||
|
clearStringBackwards(ttyStream, _statusStr);
|
||
|
}
|
||
|
this._logLines.forEach((line) => {
|
||
|
_stream.write(line);
|
||
|
_stream.write("\n");
|
||
|
});
|
||
|
this._logLines = [];
|
||
|
if (ttyStream != null) {
|
||
|
this._nextStatusStr = chunkString(
|
||
|
this._nextStatusStr,
|
||
|
ttyStream.columns
|
||
|
).join("\n");
|
||
|
_stream.write(this._nextStatusStr);
|
||
|
}
|
||
|
this._statusStr = this._nextStatusStr;
|
||
|
}
|
||
|
status(format, ...args) {
|
||
|
const { _nextStatusStr } = this;
|
||
|
this._nextStatusStr = util.format(format, ...args);
|
||
|
this._scheduleUpdate();
|
||
|
return _nextStatusStr;
|
||
|
}
|
||
|
log(format, ...args) {
|
||
|
this._logLines.push(util.format(format, ...args));
|
||
|
this._scheduleUpdate();
|
||
|
}
|
||
|
persistStatus() {
|
||
|
this.log(this._nextStatusStr);
|
||
|
this._nextStatusStr = "";
|
||
|
}
|
||
|
flush() {
|
||
|
this._scheduleUpdate.flush();
|
||
|
}
|
||
|
}
|
||
|
module.exports = Terminal;
|