-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
const proggy = require('proggy') | ||
const { log, output, META } = require('proc-log') | ||
const { log, output, input, META } = require('proc-log') | ||
const { explain } = require('./explain-eresolve.js') | ||
const { formatWithOptions } = require('./format') | ||
|
||
|
@@ -137,6 +137,7 @@ class Display { | |
// Handlers are set immediately so they can buffer all events | ||
process.on('log', this.#logHandler) | ||
process.on('output', this.#outputHandler) | ||
process.on('input', this.#inputHandler) | ||
} | ||
|
||
off () { | ||
|
@@ -146,9 +147,8 @@ class Display { | |
process.off('output', this.#outputHandler) | ||
this.#outputState.buffer.length = 0 | ||
|
||
if (this.#progress) { | ||
this.#progress.stop() | ||
} | ||
process.off('input', this.#inputHandler) | ||
this.#progress.off() | ||
Check failure on line 151 in lib/utils/display.js
|
||
} | ||
|
||
get chalk () { | ||
|
@@ -207,12 +207,12 @@ class Display { | |
// STREAM WRITES | ||
|
||
// Write formatted and (non-)colorized output to streams | ||
#stdoutWrite (options, ...args) { | ||
this.#stdout.write(formatWithOptions({ colors: this.#stdoutColor, ...options }, ...args)) | ||
} | ||
|
||
#stderrWrite (options, ...args) { | ||
this.#stderr.write(formatWithOptions({ colors: this.#stderrColor, ...options }, ...args)) | ||
#write (stream, options, ...args) { | ||
this.#progress.clear() | ||
stream.write(formatWithOptions({ | ||
colors: stream === this.#stdout ? this.#stdoutColor : this.#stderrColor, | ||
...options, | ||
}, ...args)) | ||
} | ||
|
||
// HANDLERS | ||
|
@@ -259,6 +259,9 @@ class Display { | |
) | ||
} else { | ||
this.#outputState.buffer.forEach((item) => this.#writeOutput(...item)) | ||
if (args.length) { | ||
this.#outputState.buffer.push([output.KEYS.standard, meta, ...args]) | ||
} | ||
} | ||
|
||
this.#outputState.buffer.length = 0 | ||
|
@@ -277,10 +280,8 @@ class Display { | |
|
||
// HACK: if it looks like the banner and we are in a state where we hide the | ||
// banner then dont write any output. This hack can be replaced with proc-log.META | ||
const isBanner = args.length === 1 && | ||
typeof args[0] === 'string' && | ||
args[0].startsWith('\n> ') && | ||
args[0].endsWith('\n') | ||
const arg = args[0] | ||
const isBanner = typeof arg === 'string' && arg.startsWith('\n> ') && arg.endsWith('\n') | ||
const hideBanner = this.#silent || ['exec', 'explore'].includes(this.#command) | ||
if (isBanner && hideBanner) { | ||
return | ||
|
@@ -289,16 +290,31 @@ class Display { | |
this.#writeOutput(level, meta, ...args) | ||
}) | ||
|
||
#inputHandler = withMeta((level, meta, ...args) => { | ||
if (level === input.KEYS.start) { | ||
log.pause() | ||
this.#outputState.buffering = true | ||
this.#progress.pause() | ||
return | ||
} | ||
|
||
if (level === input.KEYS.end) { | ||
log.resume() | ||
output.flush() | ||
this.#progress.resume() | ||
} | ||
}) | ||
|
||
// OUTPUT | ||
|
||
#writeOutput (level, meta, ...args) { | ||
if (level === output.KEYS.standard) { | ||
this.#stdoutWrite({}, ...args) | ||
this.#write(this.#stdout, {}, ...args) | ||
return | ||
} | ||
|
||
if (level === output.KEYS.error) { | ||
this.#stderrWrite({}, ...args) | ||
this.#write(this.#stderr, {}, ...args) | ||
} | ||
} | ||
|
||
|
@@ -344,22 +360,123 @@ class Display { | |
this.#logColors[level](level), | ||
title ? this.#logColors.title(title) : null, | ||
] | ||
this.#stderrWrite({ prefix }, ...args) | ||
} else if (this.#progress) { | ||
// TODO: make this display a single log line of filtered messages | ||
this.#write(this.#stderr, { prefix }, ...args) | ||
} | ||
} | ||
|
||
// PROGRESS | ||
|
||
#startProgress ({ progress, unicode }) { | ||
if (!progress || this.#silent) { | ||
this.#progress = new Progress({ | ||
enabled: !!progress && !this.#silent, | ||
unicode, | ||
stream: this.#stderr, | ||
}) | ||
} | ||
} | ||
|
||
class Progress { | ||
// Taken from https://github.com/sindresorhus/cli-spinners | ||
// MIT License | ||
// Copyright (c) Sindre Sorhus <[email protected]> (https://sindresorhus.com) | ||
static dots = { duration: 80, frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] } | ||
static lines = { duration: 130, frames: ['-', '\\', '|', '/'] } | ||
|
||
#stream | ||
#spinner | ||
#client | ||
|
||
#frame = 0 | ||
#running = false | ||
#timeout | ||
#interval | ||
|
||
constructor ({ enabled, stream, unicode }) { | ||
if (!enabled) { | ||
return | ||
} | ||
|
||
this.#client = proggy.createClient({ normalize: true }) | ||
this.#stream = stream | ||
this.#spinner = unicode ? Progress.dots : Progress.lines | ||
this.#render({ delay: 500 }) | ||
} | ||
|
||
off () { | ||
if (!this.#stream) { | ||
return | ||
} | ||
this.#clear() | ||
} | ||
|
||
pause () { | ||
if (!this.#stream) { | ||
return | ||
} | ||
this.#progress = proggy.createClient({ normalize: true }) | ||
// TODO: implement proggy trackers in arborist/doctor | ||
// TODO: listen to progress events here and build progress UI | ||
// TODO: see deprecated gauge package for what unicode chars were used | ||
this.#clear({ clearLine: true }) | ||
} | ||
|
||
resume () { | ||
if (!this.#stream) { | ||
return | ||
} | ||
this.#render() | ||
} | ||
|
||
persist (stream, value) { | ||
if (this.#stream) { | ||
this.#stream.cursorTo(0) | ||
} | ||
stream.write(value) | ||
if (this.#stream) { | ||
this.#render({ delay: 10 }) | ||
} | ||
} | ||
|
||
clear () { | ||
if (!this.#stream) { | ||
return | ||
} | ||
this.#stream.cursorTo(0) | ||
} | ||
|
||
#clear ({ clearLine } = {}) { | ||
this.#running = false | ||
this.#stream.cursorTo(0) | ||
if (clearLine) { | ||
this.#stream.clearLine(1) | ||
} | ||
clearTimeout(this.#timeout) | ||
clearInterval(this.#interval) | ||
} | ||
|
||
#render ({ delay } = {}) { | ||
if (delay) { | ||
this.#stream.cursorTo(0) | ||
this.#stream.clearLine(1) | ||
clearTimeout(this.#timeout) | ||
this.#timeout = setTimeout(() => this.#render(), delay) | ||
return | ||
} | ||
this.#running = true | ||
this.#renderFrame() | ||
clearInterval(this.#interval) | ||
this.#interval = setInterval(() => this.#renderFrame(), this.#spinner.duration) | ||
// this.#interval.unref() | ||
} | ||
|
||
#renderFrame () { | ||
if (this.#running) { | ||
this.#stream.cursorTo(0) | ||
this.#stream.write(this.#spinner.frames[this.#nextFrame()]) | ||
} | ||
} | ||
|
||
#nextFrame () { | ||
if (this.#frame >= this.#spinner.frames.length) { | ||
this.#frame = 0 | ||
} | ||
return this.#frame++ | ||
} | ||
} | ||
|
||
|