|
'use strict'; |
|
const ansiEscapes = require('ansi-escapes'); |
|
const cliCursor = require('cli-cursor'); |
|
const wrapAnsi = require('wrap-ansi'); |
|
const sliceAnsi = require('slice-ansi'); |
|
|
|
const defaultTerminalHeight = 24; |
|
|
|
const getWidth = stream => { |
|
const {columns} = stream; |
|
|
|
if (!columns) { |
|
return 80; |
|
} |
|
|
|
return columns; |
|
}; |
|
|
|
const fitToTerminalHeight = (stream, text) => { |
|
const terminalHeight = stream.rows || defaultTerminalHeight; |
|
const lines = text.split('\n'); |
|
|
|
const toRemove = lines.length - terminalHeight; |
|
if (toRemove <= 0) { |
|
return text; |
|
} |
|
|
|
return sliceAnsi( |
|
text, |
|
lines.slice(0, toRemove).join('\n').length + 1, |
|
text.length); |
|
}; |
|
|
|
const main = (stream, {showCursor = false} = {}) => { |
|
let previousLineCount = 0; |
|
let previousWidth = getWidth(stream); |
|
let previousOutput = ''; |
|
|
|
const render = (...args) => { |
|
if (!showCursor) { |
|
cliCursor.hide(); |
|
} |
|
|
|
let output = args.join(' ') + '\n'; |
|
output = fitToTerminalHeight(stream, output); |
|
const width = getWidth(stream); |
|
if (output === previousOutput && previousWidth === width) { |
|
return; |
|
} |
|
|
|
previousOutput = output; |
|
previousWidth = width; |
|
output = wrapAnsi(output, width, { |
|
trim: false, |
|
hard: true, |
|
wordWrap: false |
|
}); |
|
stream.write(ansiEscapes.eraseLines(previousLineCount) + output); |
|
previousLineCount = output.split('\n').length; |
|
}; |
|
|
|
render.clear = () => { |
|
stream.write(ansiEscapes.eraseLines(previousLineCount)); |
|
previousOutput = ''; |
|
previousWidth = getWidth(stream); |
|
previousLineCount = 0; |
|
}; |
|
|
|
render.done = () => { |
|
previousOutput = ''; |
|
previousWidth = getWidth(stream); |
|
previousLineCount = 0; |
|
|
|
if (!showCursor) { |
|
cliCursor.show(); |
|
} |
|
}; |
|
|
|
return render; |
|
}; |
|
|
|
module.exports = main(process.stdout); |
|
module.exports.stderr = main(process.stderr); |
|
module.exports.create = main; |
|
|