291 lines
7.0 KiB
JavaScript
291 lines
7.0 KiB
JavaScript
/**
|
|
* Takes any input and guarantees an array back.
|
|
*
|
|
* - converts array-like objects (e.g. `arguments`) to a real array
|
|
* - converts `undefined` to an empty array
|
|
* - converts any another other, singular value (including `null`) into an array containing that value
|
|
* - ignores input which is already an array
|
|
*
|
|
* @module array-back
|
|
* @example
|
|
* > const arrayify = require('array-back')
|
|
*
|
|
* > arrayify(undefined)
|
|
* []
|
|
*
|
|
* > arrayify(null)
|
|
* [ null ]
|
|
*
|
|
* > arrayify(0)
|
|
* [ 0 ]
|
|
*
|
|
* > arrayify([ 1, 2 ])
|
|
* [ 1, 2 ]
|
|
*
|
|
* > function f(){ return arrayify(arguments); }
|
|
* > f(1,2,3)
|
|
* [ 1, 2, 3 ]
|
|
*/
|
|
|
|
function isObject (input) {
|
|
return typeof input === 'object' && input !== null
|
|
}
|
|
|
|
function isArrayLike (input) {
|
|
return isObject(input) && typeof input.length === 'number'
|
|
}
|
|
|
|
/**
|
|
* @param {*} - the input value to convert to an array
|
|
* @returns {Array}
|
|
* @alias module:array-back
|
|
*/
|
|
function arrayify (input) {
|
|
if (Array.isArray(input)) {
|
|
return input
|
|
} else {
|
|
if (input === undefined) {
|
|
return []
|
|
} else if (isArrayLike(input)) {
|
|
return Array.prototype.slice.call(input)
|
|
} else {
|
|
return [ input ]
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Control Sequence Initiator */
|
|
const csi = '\x1b[';
|
|
|
|
/**
|
|
* @exports ansi-escape-sequences
|
|
* @typicalname ansi
|
|
* @example
|
|
* const ansi = require('ansi-escape-sequences')
|
|
*/
|
|
const ansi = {};
|
|
|
|
/**
|
|
* Various formatting styles (aka Select Graphic Rendition codes).
|
|
* @enum {string}
|
|
* @example
|
|
* console.log(ansi.style.red + 'this is red' + ansi.style.reset)
|
|
*/
|
|
ansi.style = {
|
|
reset: '\x1b[0m',
|
|
bold: '\x1b[1m',
|
|
italic: '\x1b[3m',
|
|
underline: '\x1b[4m',
|
|
fontDefault: '\x1b[10m',
|
|
font2: '\x1b[11m',
|
|
font3: '\x1b[12m',
|
|
font4: '\x1b[13m',
|
|
font5: '\x1b[14m',
|
|
font6: '\x1b[15m',
|
|
imageNegative: '\x1b[7m',
|
|
imagePositive: '\x1b[27m',
|
|
black: '\x1b[30m',
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m',
|
|
white: '\x1b[37m',
|
|
grey: '\x1b[90m',
|
|
gray: '\x1b[90m',
|
|
'bg-black': '\x1b[40m',
|
|
'bg-red': '\x1b[41m',
|
|
'bg-green': '\x1b[42m',
|
|
'bg-yellow': '\x1b[43m',
|
|
'bg-blue': '\x1b[44m',
|
|
'bg-magenta': '\x1b[45m',
|
|
'bg-cyan': '\x1b[46m',
|
|
'bg-white': '\x1b[47m',
|
|
'bg-grey': '\x1b[100m',
|
|
'bg-gray': '\x1b[100m'
|
|
};
|
|
|
|
/**
|
|
* style enum - used by `ansi.styles()`.
|
|
* @enum {number}
|
|
* @private
|
|
*/
|
|
const eStyles = {
|
|
reset: 0,
|
|
bold: 1,
|
|
italic: 3,
|
|
underline: 4,
|
|
imageNegative: 7,
|
|
fontDefault: 10,
|
|
font2: 11,
|
|
font3: 12,
|
|
font4: 13,
|
|
font5: 14,
|
|
font6: 15,
|
|
imagePositive: 27,
|
|
black: 30,
|
|
red: 31,
|
|
green: 32,
|
|
yellow: 33,
|
|
blue: 34,
|
|
magenta: 35,
|
|
cyan: 36,
|
|
white: 37,
|
|
grey: 90,
|
|
gray: 90,
|
|
'bg-black': 40,
|
|
'bg-red': 41,
|
|
'bg-green': 42,
|
|
'bg-yellow': 43,
|
|
'bg-blue': 44,
|
|
'bg-magenta': 45,
|
|
'bg-cyan': 46,
|
|
'bg-white': 47,
|
|
'bg-grey': 100,
|
|
'bg-gray': 100
|
|
};
|
|
|
|
/**
|
|
* Returns an ansi sequence setting one or more effects
|
|
* @param {string | string[]} - a style, or list or styles
|
|
* @returns {string}
|
|
* @example
|
|
* > ansi.styles('green')
|
|
* '\u001b[32m'
|
|
*
|
|
* > ansi.styles([ 'green', 'underline' ])
|
|
* '\u001b[32;4m'
|
|
*/
|
|
ansi.styles = function (effectArray) {
|
|
effectArray = arrayify(effectArray);
|
|
return csi + effectArray.map(function (effect) { return eStyles[effect] }).join(';') + 'm'
|
|
};
|
|
|
|
/**
|
|
* A convenience function, applying the provided styles to the input string and then resetting.
|
|
*
|
|
* Inline styling can be applied using the syntax `[style-list]{text to format}`, where `style-list` is a space-separated list of styles from {@link module:ansi-escape-sequences.style ansi.style}. For example `[bold white bg-red]{bold white text on a red background}`.
|
|
*
|
|
* @param {string} - the string to format
|
|
* @param [styleArray] {string[]} - a list of styles to add to the input string
|
|
* @returns {string}
|
|
* @example
|
|
* > ansi.format('what?', 'green')
|
|
* '\u001b[32mwhat?\u001b[0m'
|
|
*
|
|
* > ansi.format('what?', ['green', 'bold'])
|
|
* '\u001b[32;1mwhat?\u001b[0m'
|
|
*
|
|
* > ansi.format('[green bold]{what?}')
|
|
* '\u001b[32;1mwhat?\u001b[0m'
|
|
*/
|
|
ansi.format = function (str, styleArray) {
|
|
const re = /\[([\w\s-]+)\]{([^]*?)}/;
|
|
let matches;
|
|
if (!str) return ''
|
|
|
|
while (matches = str.match(re)) {
|
|
const inlineStyles = matches[1].split(/\s+/);
|
|
const inlineString = matches[2];
|
|
str = str.replace(matches[0], ansi.format(inlineString, inlineStyles));
|
|
}
|
|
|
|
return (styleArray && styleArray.length)
|
|
? ansi.styles(styleArray) + str + ansi.style.reset
|
|
: str
|
|
};
|
|
|
|
/**
|
|
* cursor-related sequences
|
|
*/
|
|
ansi.cursor = {
|
|
/**
|
|
* Moves the cursor `lines` cells up. If the cursor is already at the edge of the screen, this has no effect
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
up: function (lines) { return csi + (lines || 1) + 'A' },
|
|
|
|
/**
|
|
* Moves the cursor `lines` cells down. If the cursor is already at the edge of the screen, this has no effect
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
down: function (lines) { return csi + (lines || 1) + 'B' },
|
|
|
|
/**
|
|
* Moves the cursor `lines` cells forward. If the cursor is already at the edge of the screen, this has no effect
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
forward: function (lines) { return csi + (lines || 1) + 'C' },
|
|
|
|
/**
|
|
* Moves the cursor `lines` cells back. If the cursor is already at the edge of the screen, this has no effect
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
back: function (lines) { return csi + (lines || 1) + 'D' },
|
|
|
|
/**
|
|
* Moves cursor to beginning of the line n lines down.
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
nextLine: function (lines) { return csi + (lines || 1) + 'E' },
|
|
|
|
/**
|
|
* Moves cursor to beginning of the line n lines up.
|
|
* @param [lines=1] {number}
|
|
* @return {string}
|
|
*/
|
|
previousLine: function (lines) { return csi + (lines || 1) + 'F' },
|
|
|
|
/**
|
|
* Moves the cursor to column n.
|
|
* @param n {number} - column number
|
|
* @return {string}
|
|
*/
|
|
horizontalAbsolute: function (n) { return csi + n + 'G' },
|
|
|
|
/**
|
|
* Moves the cursor to row n, column m. The values are 1-based, and default to 1 (top left corner) if omitted.
|
|
* @param n {number} - row number
|
|
* @param m {number} - column number
|
|
* @return {string}
|
|
*/
|
|
position: function (n, m) { return csi + (n || 1) + ';' + (m || 1) + 'H' },
|
|
|
|
/**
|
|
* Hides the cursor
|
|
*/
|
|
hide: csi + '?25l',
|
|
|
|
/**
|
|
* Shows the cursor
|
|
*/
|
|
show: csi + '?25h'
|
|
};
|
|
|
|
/**
|
|
* erase sequences
|
|
*/
|
|
ansi.erase = {
|
|
/**
|
|
* Clears part of the screen. If n is 0 (or missing), clear from cursor to end of screen. If n is 1, clear from cursor to beginning of the screen. If n is 2, clear entire screen.
|
|
* @param n {number}
|
|
* @return {string}
|
|
*/
|
|
display: function (n) { return csi + (n || 0) + 'J' },
|
|
|
|
/**
|
|
* Erases part of the line. If n is zero (or missing), clear from cursor to the end of the line. If n is one, clear from cursor to beginning of the line. If n is two, clear entire line. Cursor position does not change.
|
|
* @param n {number}
|
|
* @return {string}
|
|
*/
|
|
inLine: function (n) { return csi + (n || 0) + 'K' }
|
|
};
|
|
|
|
export default ansi;
|