Comparing version 5.0.0 to 6.0.0
@@ -5,5 +5,16 @@ # Change Log | ||
# [5.0.0](https://github.com/yargs/cliui/compare/v4.1.0...v5.0.0) (2019-04-10) | ||
## [6.0.0](https://www.github.com/yargs/cliui/compare/v5.0.0...v6.0.0) (2019-11-10) | ||
### ⚠ BREAKING CHANGES | ||
* update deps, drop Node 6 | ||
### Code Refactoring | ||
* update deps, drop Node 6 ([62056df](https://www.github.com/yargs/cliui/commit/62056df)) | ||
## [5.0.0](https://github.com/yargs/cliui/compare/v4.1.0...v5.0.0) (2019-04-10) | ||
### Bug Fixes | ||
@@ -21,3 +32,3 @@ | ||
<a name="4.1.0"></a> | ||
# [4.1.0](https://github.com/yargs/cliui/compare/v4.0.0...v4.1.0) (2018-04-23) | ||
## [4.1.0](https://github.com/yargs/cliui/compare/v4.0.0...v4.1.0) (2018-04-23) | ||
@@ -32,3 +43,3 @@ | ||
<a name="4.0.0"></a> | ||
# [4.0.0](https://github.com/yargs/cliui/compare/v3.2.0...v4.0.0) (2017-12-18) | ||
## [4.0.0](https://github.com/yargs/cliui/compare/v3.2.0...v4.0.0) (2017-12-18) | ||
@@ -59,3 +70,3 @@ | ||
<a name="3.2.0"></a> | ||
# [3.2.0](https://github.com/yargs/cliui/compare/v3.1.2...v3.2.0) (2016-04-11) | ||
## [3.2.0](https://github.com/yargs/cliui/compare/v3.1.2...v3.2.0) (2016-04-11) | ||
@@ -62,0 +73,0 @@ |
498
index.js
@@ -1,278 +0,305 @@ | ||
var stringWidth = require('string-width') | ||
var stripAnsi = require('strip-ansi') | ||
var wrap = require('wrap-ansi') | ||
var align = { | ||
'use strict' | ||
const stringWidth = require('string-width') | ||
const stripAnsi = require('strip-ansi') | ||
const wrap = require('wrap-ansi') | ||
const align = { | ||
right: alignRight, | ||
center: alignCenter | ||
} | ||
var top = 0 | ||
var right = 1 | ||
var bottom = 2 | ||
var left = 3 | ||
const top = 0 | ||
const right = 1 | ||
const bottom = 2 | ||
const left = 3 | ||
function UI (opts) { | ||
this.width = opts.width | ||
this.wrap = opts.wrap | ||
this.rows = [] | ||
} | ||
class UI { | ||
constructor (opts) { | ||
this.width = opts.width | ||
this.wrap = opts.wrap | ||
this.rows = [] | ||
} | ||
UI.prototype.span = function () { | ||
var cols = this.div.apply(this, arguments) | ||
cols.span = true | ||
} | ||
span (...args) { | ||
const cols = this.div(...args) | ||
cols.span = true | ||
} | ||
UI.prototype.resetOutput = function () { | ||
this.rows = [] | ||
} | ||
UI.prototype.div = function () { | ||
if (arguments.length === 0) this.div('') | ||
if (this.wrap && this._shouldApplyLayoutDSL.apply(this, arguments)) { | ||
return this._applyLayoutDSL(arguments[0]) | ||
resetOutput () { | ||
this.rows = [] | ||
} | ||
var cols = [] | ||
div (...args) { | ||
if (args.length === 0) { | ||
this.div('') | ||
} | ||
for (var i = 0, arg; (arg = arguments[i]) !== undefined; i++) { | ||
if (typeof arg === 'string') cols.push(this._colFromString(arg)) | ||
else cols.push(arg) | ||
} | ||
if (this.wrap && this._shouldApplyLayoutDSL(...args)) { | ||
return this._applyLayoutDSL(args[0]) | ||
} | ||
this.rows.push(cols) | ||
return cols | ||
} | ||
const cols = args.map(arg => { | ||
if (typeof arg === 'string') { | ||
return this._colFromString(arg) | ||
} | ||
UI.prototype._shouldApplyLayoutDSL = function () { | ||
return arguments.length === 1 && typeof arguments[0] === 'string' && | ||
/[\t\n]/.test(arguments[0]) | ||
} | ||
return arg | ||
}) | ||
UI.prototype._applyLayoutDSL = function (str) { | ||
var _this = this | ||
var rows = str.split('\n') | ||
var leftColumnWidth = 0 | ||
this.rows.push(cols) | ||
return cols | ||
} | ||
// simple heuristic for layout, make sure the | ||
// second column lines up along the left-hand. | ||
// don't allow the first column to take up more | ||
// than 50% of the screen. | ||
rows.forEach(function (row) { | ||
var columns = row.split('\t') | ||
if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) { | ||
leftColumnWidth = Math.min( | ||
Math.floor(_this.width * 0.5), | ||
stringWidth(columns[0]) | ||
) | ||
} | ||
}) | ||
_shouldApplyLayoutDSL (...args) { | ||
return args.length === 1 && typeof args[0] === 'string' && | ||
/[\t\n]/.test(args[0]) | ||
} | ||
// generate a table: | ||
// replacing ' ' with padding calculations. | ||
// using the algorithmically generated width. | ||
rows.forEach(function (row) { | ||
var columns = row.split('\t') | ||
_this.div.apply(_this, columns.map(function (r, i) { | ||
return { | ||
text: r.trim(), | ||
padding: _this._measurePadding(r), | ||
width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined | ||
_applyLayoutDSL (str) { | ||
const rows = str.split('\n').map(row => row.split('\t')) | ||
let leftColumnWidth = 0 | ||
// simple heuristic for layout, make sure the | ||
// second column lines up along the left-hand. | ||
// don't allow the first column to take up more | ||
// than 50% of the screen. | ||
rows.forEach(columns => { | ||
if (columns.length > 1 && stringWidth(columns[0]) > leftColumnWidth) { | ||
leftColumnWidth = Math.min( | ||
Math.floor(this.width * 0.5), | ||
stringWidth(columns[0]) | ||
) | ||
} | ||
})) | ||
}) | ||
}) | ||
return this.rows[this.rows.length - 1] | ||
} | ||
// generate a table: | ||
// replacing ' ' with padding calculations. | ||
// using the algorithmically generated width. | ||
rows.forEach(columns => { | ||
this.div(...columns.map((r, i) => { | ||
return { | ||
text: r.trim(), | ||
padding: this._measurePadding(r), | ||
width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined | ||
} | ||
})) | ||
}) | ||
UI.prototype._colFromString = function (str) { | ||
return { | ||
text: str, | ||
padding: this._measurePadding(str) | ||
return this.rows[this.rows.length - 1] | ||
} | ||
} | ||
UI.prototype._measurePadding = function (str) { | ||
// measure padding without ansi escape codes | ||
var noAnsi = stripAnsi(str) | ||
return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length] | ||
} | ||
_colFromString (text) { | ||
return { | ||
text, | ||
padding: this._measurePadding(text) | ||
} | ||
} | ||
UI.prototype.toString = function () { | ||
var _this = this | ||
var lines = [] | ||
_measurePadding (str) { | ||
// measure padding without ansi escape codes | ||
const noAnsi = stripAnsi(str) | ||
return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length] | ||
} | ||
_this.rows.forEach(function (row, i) { | ||
_this.rowToString(row, lines) | ||
}) | ||
toString () { | ||
const lines = [] | ||
// don't display any lines with the | ||
// hidden flag set. | ||
lines = lines.filter(function (line) { | ||
return !line.hidden | ||
}) | ||
this.rows.forEach(row => { | ||
this.rowToString(row, lines) | ||
}) | ||
return lines.map(function (line) { | ||
return line.text | ||
}).join('\n') | ||
} | ||
// don't display any lines with the | ||
// hidden flag set. | ||
return lines | ||
.filter(line => !line.hidden) | ||
.map(line => line.text) | ||
.join('\n') | ||
} | ||
UI.prototype.rowToString = function (row, lines) { | ||
var _this = this | ||
var padding | ||
var rrows = this._rasterize(row) | ||
var str = '' | ||
var ts | ||
var width | ||
var wrapWidth | ||
rowToString (row, lines) { | ||
this._rasterize(row).forEach((rrow, r) => { | ||
let str = '' | ||
rrow.forEach((col, c) => { | ||
const { width } = row[c] // the width with padding. | ||
const wrapWidth = this._negatePadding(row[c]) // the width without padding. | ||
rrows.forEach(function (rrow, r) { | ||
str = '' | ||
rrow.forEach(function (col, c) { | ||
ts = '' // temporary string used during alignment/padding. | ||
width = row[c].width // the width with padding. | ||
wrapWidth = _this._negatePadding(row[c]) // the width without padding. | ||
let ts = col // temporary string used during alignment/padding. | ||
ts += col | ||
if (wrapWidth > stringWidth(col)) { | ||
ts += ' '.repeat(wrapWidth - stringWidth(col)) | ||
} | ||
for (var i = 0; i < wrapWidth - stringWidth(col); i++) { | ||
ts += ' ' | ||
} | ||
// align the string within its column. | ||
if (row[c].align && row[c].align !== 'left' && this.wrap) { | ||
ts = align[row[c].align](ts, wrapWidth) | ||
if (stringWidth(ts) < wrapWidth) { | ||
ts += ' '.repeat(width - stringWidth(ts) - 1) | ||
} | ||
} | ||
// align the string within its column. | ||
if (row[c].align && row[c].align !== 'left' && _this.wrap) { | ||
ts = align[row[c].align](ts, wrapWidth) | ||
if (stringWidth(ts) < wrapWidth) ts += new Array(width - stringWidth(ts)).join(' ') | ||
} | ||
// apply border and padding to string. | ||
const padding = row[c].padding || [0, 0, 0, 0] | ||
if (padding[left]) { | ||
str += ' '.repeat(padding[left]) | ||
} | ||
// apply border and padding to string. | ||
padding = row[c].padding || [0, 0, 0, 0] | ||
if (padding[left]) str += new Array(padding[left] + 1).join(' ') | ||
str += addBorder(row[c], ts, '| ') | ||
str += ts | ||
str += addBorder(row[c], ts, ' |') | ||
if (padding[right]) str += new Array(padding[right] + 1).join(' ') | ||
str += addBorder(row[c], ts, '| ') | ||
str += ts | ||
str += addBorder(row[c], ts, ' |') | ||
if (padding[right]) { | ||
str += ' '.repeat(padding[right]) | ||
} | ||
// if prior row is span, try to render the | ||
// current row on the prior line. | ||
if (r === 0 && lines.length > 0) { | ||
str = _this._renderInline(str, lines[lines.length - 1]) | ||
} | ||
}) | ||
// if prior row is span, try to render the | ||
// current row on the prior line. | ||
if (r === 0 && lines.length > 0) { | ||
str = this._renderInline(str, lines[lines.length - 1]) | ||
} | ||
}) | ||
// remove trailing whitespace. | ||
lines.push({ | ||
text: str.replace(/ +$/, ''), | ||
span: row.span | ||
// remove trailing whitespace. | ||
lines.push({ | ||
text: str.replace(/ +$/, ''), | ||
span: row.span | ||
}) | ||
}) | ||
}) | ||
return lines | ||
} | ||
function addBorder (col, ts, style) { | ||
if (col.border) { | ||
if (/[.']-+[.']/.test(ts)) return '' | ||
else if (ts.trim().length) return style | ||
else return ' ' | ||
return lines | ||
} | ||
return '' | ||
} | ||
// if the full 'source' can render in | ||
// the target line, do so. | ||
UI.prototype._renderInline = function (source, previousLine) { | ||
var leadingWhitespace = source.match(/^ */)[0].length | ||
var target = previousLine.text | ||
var targetTextWidth = stringWidth(target.trimRight()) | ||
// if the full 'source' can render in | ||
// the target line, do so. | ||
_renderInline (source, previousLine) { | ||
const leadingWhitespace = source.match(/^ */)[0].length | ||
const target = previousLine.text | ||
const targetTextWidth = stringWidth(target.trimRight()) | ||
if (!previousLine.span) return source | ||
if (!previousLine.span) { | ||
return source | ||
} | ||
// if we're not applying wrapping logic, | ||
// just always append to the span. | ||
if (!this.wrap) { | ||
// if we're not applying wrapping logic, | ||
// just always append to the span. | ||
if (!this.wrap) { | ||
previousLine.hidden = true | ||
return target + source | ||
} | ||
if (leadingWhitespace < targetTextWidth) { | ||
return source | ||
} | ||
previousLine.hidden = true | ||
return target + source | ||
return target.trimRight() + ' '.repeat(leadingWhitespace - targetTextWidth) + source.trimLeft() | ||
} | ||
if (leadingWhitespace < targetTextWidth) return source | ||
_rasterize (row) { | ||
const rrows = [] | ||
const widths = this._columnWidths(row) | ||
let wrapped | ||
previousLine.hidden = true | ||
// word wrap all columns, and create | ||
// a data-structure that is easy to rasterize. | ||
row.forEach((col, c) => { | ||
// leave room for left and right padding. | ||
col.width = widths[c] | ||
if (this.wrap) { | ||
wrapped = wrap(col.text, this._negatePadding(col), { hard: true }).split('\n') | ||
} else { | ||
wrapped = col.text.split('\n') | ||
} | ||
return target.trimRight() + new Array(leadingWhitespace - targetTextWidth + 1).join(' ') + source.trimLeft() | ||
} | ||
if (col.border) { | ||
wrapped.unshift('.' + '-'.repeat(this._negatePadding(col) + 2) + '.') | ||
wrapped.push("'" + '-'.repeat(this._negatePadding(col) + 2) + "'") | ||
} | ||
UI.prototype._rasterize = function (row) { | ||
var _this = this | ||
var i | ||
var rrow | ||
var rrows = [] | ||
var widths = this._columnWidths(row) | ||
var wrapped | ||
// add top and bottom padding. | ||
if (col.padding) { | ||
wrapped.unshift(...new Array(col.padding[top] || 0).fill('')) | ||
wrapped.push(...new Array(col.padding[bottom] || 0).fill('')) | ||
} | ||
// word wrap all columns, and create | ||
// a data-structure that is easy to rasterize. | ||
row.forEach(function (col, c) { | ||
// leave room for left and right padding. | ||
col.width = widths[c] | ||
if (_this.wrap) wrapped = wrap(col.text, _this._negatePadding(col), { hard: true }).split('\n') | ||
else wrapped = col.text.split('\n') | ||
wrapped.forEach((str, r) => { | ||
if (!rrows[r]) { | ||
rrows.push([]) | ||
} | ||
const rrow = rrows[r] | ||
for (let i = 0; i < c; i++) { | ||
if (rrow[i] === undefined) { | ||
rrow.push('') | ||
} | ||
} | ||
rrow.push(str) | ||
}) | ||
}) | ||
return rrows | ||
} | ||
_negatePadding (col) { | ||
let wrapWidth = col.width | ||
if (col.padding) { | ||
wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0) | ||
} | ||
if (col.border) { | ||
wrapped.unshift('.' + new Array(_this._negatePadding(col) + 3).join('-') + '.') | ||
wrapped.push("'" + new Array(_this._negatePadding(col) + 3).join('-') + "'") | ||
wrapWidth -= 4 | ||
} | ||
// add top and bottom padding. | ||
if (col.padding) { | ||
for (i = 0; i < (col.padding[top] || 0); i++) wrapped.unshift('') | ||
for (i = 0; i < (col.padding[bottom] || 0); i++) wrapped.push('') | ||
return wrapWidth | ||
} | ||
_columnWidths (row) { | ||
if (!this.wrap) { | ||
return row.map(col => { | ||
return col.width || stringWidth(col.text) | ||
}) | ||
} | ||
wrapped.forEach(function (str, r) { | ||
if (!rrows[r]) rrows.push([]) | ||
let unset = row.length | ||
let remainingWidth = this.width | ||
rrow = rrows[r] | ||
// column widths can be set in config. | ||
const widths = row.map(col => { | ||
if (col.width) { | ||
unset-- | ||
remainingWidth -= col.width | ||
return col.width | ||
} | ||
for (var i = 0; i < c; i++) { | ||
if (rrow[i] === undefined) rrow.push('') | ||
} | ||
rrow.push(str) | ||
return undefined | ||
}) | ||
}) | ||
return rrows | ||
} | ||
// any unset widths should be calculated. | ||
const unsetWidth = unset ? Math.floor(remainingWidth / unset) : 0 | ||
UI.prototype._negatePadding = function (col) { | ||
var wrapWidth = col.width | ||
if (col.padding) wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0) | ||
if (col.border) wrapWidth -= 4 | ||
return wrapWidth | ||
return widths.map((w, i) => { | ||
if (w === undefined) { | ||
return Math.max(unsetWidth, _minWidth(row[i])) | ||
} | ||
return w | ||
}) | ||
} | ||
} | ||
UI.prototype._columnWidths = function (row) { | ||
var _this = this | ||
var widths = [] | ||
var unset = row.length | ||
var unsetWidth | ||
var remainingWidth = this.width | ||
function addBorder (col, ts, style) { | ||
if (col.border) { | ||
if (/[.']-+[.']/.test(ts)) { | ||
return '' | ||
} | ||
// column widths can be set in config. | ||
row.forEach(function (col, i) { | ||
if (col.width) { | ||
unset-- | ||
widths[i] = col.width | ||
remainingWidth -= col.width | ||
} else { | ||
widths[i] = undefined | ||
if (ts.trim().length !== 0) { | ||
return style | ||
} | ||
}) | ||
// any unset widths should be calculated. | ||
if (unset) unsetWidth = Math.floor(remainingWidth / unset) | ||
widths.forEach(function (w, i) { | ||
if (!_this.wrap) widths[i] = row[i].width || stringWidth(row[i].text) | ||
else if (w === undefined) widths[i] = Math.max(unsetWidth, _minWidth(row[i])) | ||
}) | ||
return ' ' | ||
} | ||
return widths | ||
return '' | ||
} | ||
@@ -283,5 +310,8 @@ | ||
function _minWidth (col) { | ||
var padding = col.padding || [] | ||
var minWidth = 1 + (padding[left] || 0) + (padding[right] || 0) | ||
if (col.border) minWidth += 4 | ||
const padding = col.padding || [] | ||
const minWidth = 1 + (padding[left] || 0) + (padding[right] || 0) | ||
if (col.border) { | ||
return minWidth + 4 | ||
} | ||
return minWidth | ||
@@ -291,3 +321,6 @@ } | ||
function getWindowWidth () { | ||
if (typeof process === 'object' && process.stdout && process.stdout.columns) return process.stdout.columns | ||
/* istanbul ignore next: depends on terminal */ | ||
if (typeof process === 'object' && process.stdout && process.stdout.columns) { | ||
return process.stdout.columns | ||
} | ||
} | ||
@@ -297,10 +330,9 @@ | ||
str = str.trim() | ||
var padding = '' | ||
var strWidth = stringWidth(str) | ||
const strWidth = stringWidth(str) | ||
if (strWidth < width) { | ||
padding = new Array(width - strWidth + 1).join(' ') | ||
return ' '.repeat(width - strWidth) + str | ||
} | ||
return padding + str | ||
return str | ||
} | ||
@@ -310,19 +342,17 @@ | ||
str = str.trim() | ||
var padding = '' | ||
var strWidth = stringWidth(str.trim()) | ||
const strWidth = stringWidth(str) | ||
if (strWidth < width) { | ||
padding = new Array(parseInt((width - strWidth) / 2, 10) + 1).join(' ') | ||
/* istanbul ignore next */ | ||
if (strWidth >= width) { | ||
return str | ||
} | ||
return padding + str | ||
return ' '.repeat((width - strWidth) >> 1) + str | ||
} | ||
module.exports = function (opts) { | ||
opts = opts || {} | ||
module.exports = function (opts = {}) { | ||
return new UI({ | ||
width: (opts || {}).width || getWindowWidth() || 80, | ||
wrap: typeof opts.wrap === 'boolean' ? opts.wrap : true | ||
width: opts.width || getWindowWidth() || /* istanbul ignore next */ 80, | ||
wrap: opts.wrap !== false | ||
}) | ||
} |
{ | ||
"name": "cliui", | ||
"version": "5.0.0", | ||
"version": "6.0.0", | ||
"description": "easily create complex multi-column command-line-interfaces", | ||
@@ -9,4 +9,3 @@ "main": "index.js", | ||
"test": "nyc mocha", | ||
"coverage": "nyc --reporter=text-lcov mocha | coveralls", | ||
"release": "standard-version" | ||
"coverage": "nyc --reporter=text-lcov mocha | coveralls" | ||
}, | ||
@@ -49,14 +48,13 @@ "repository": { | ||
"dependencies": { | ||
"string-width": "^3.1.0", | ||
"strip-ansi": "^5.2.0", | ||
"wrap-ansi": "^5.1.0" | ||
"string-width": "^4.2.0", | ||
"strip-ansi": "^6.0.0", | ||
"wrap-ansi": "^6.2.0" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.2.0", | ||
"chalk": "^2.4.2", | ||
"chalk": "^3.0.0", | ||
"coveralls": "^3.0.3", | ||
"mocha": "^6.0.2", | ||
"nyc": "^13.3.0", | ||
"standard": "^12.0.1", | ||
"standard-version": "^5.0.2" | ||
"mocha": "^6.2.2", | ||
"nyc": "^14.1.1", | ||
"standard": "^12.0.1" | ||
}, | ||
@@ -67,4 +65,4 @@ "files": [ | ||
"engine": { | ||
"node": ">=6" | ||
"node": ">=8" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14873
6
286
1
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedwrap-ansi@6.2.0(transitive)
- Removedansi-regex@4.1.1(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedemoji-regex@7.0.3(transitive)
- Removedis-fullwidth-code-point@2.0.0(transitive)
- Removedstring-width@3.1.0(transitive)
- Removedstrip-ansi@5.2.0(transitive)
- Removedwrap-ansi@5.1.0(transitive)
Updatedstring-width@^4.2.0
Updatedstrip-ansi@^6.0.0
Updatedwrap-ansi@^6.2.0