@serpent/common-cli
Version:
通用的 cli 相关的函数
405 lines (351 loc) • 16.8 kB
JavaScript
'use strict';
var _commonjsHelpers = require('./_commonjsHelpers-ed042b00.cjs');
var require$$1 = require('os');
var format = require('./format-4c37063e.cjs');
var supportColor = require('./supportColor-07ee281e.cjs');
var _helper = {};
/**
* Creates an array containing the numeric code points of each Unicode
* character in the string. While JavaScript uses UCS-2 internally,
* this function will convert a pair of surrogate halves (each of which
* UCS-2 exposes as separate characters) into a single code point,
* matching UTF-16.
* @see `punycode.ucs2.encode`
* @see <https://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode.ucs2
* @name decode
* @param {String} string The Unicode input string (UCS-2).
* @returns {Array} The new array of code points.
*/
function ucs2decode$1(string) {
var output = [];
var counter = 0;
var length = string.length;
/* istanbul ignore next */
while (counter < length) {
var value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// It's a high surrogate, and there is a next character.
var extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) === 0xDC00) { // Low surrogate.
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// It's an unmatched surrogate; only append this code unit, in case the
// next code unit is the high surrogate of a surrogate pair.
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output
}
/**
* Creates a string based on an array of numeric code points.
* @see `punycode.ucs2.decode`
* @memberOf punycode.ucs2
* @name encode
* @param {Array} codePoints The array of numeric code points.
* @returns {String} The new Unicode string (UCS-2).
*/
function ucs2encode(codePoints) {
return String.fromCodePoint.apply(String, codePoints)
}
_helper.ucs2decode = ucs2decode$1;
_helper.ucs2encode = ucs2encode;
/**
* @module libs/tty/cliTextSize
* @createdAt 2016-07-17
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*
* 可以参考: eastasianwidth string-width
*/
var stripAnsi = format.stripAnsiExports;
var isWin = supportColor.isWin;
var ucs2decode = _helper.ucs2decode;
// Window 对不支持的字符都使用了默认的占一个位置的字符替代
// 中国的 window 用户大多是 cp936 编码 (但是最近听说 window 也将支持 Linux 的 shell)
/* eslint-disable */
var sizeRangesMap = {"0":[[0,7],[11,31],[768,879],[1155,1161],[1425,1469],[1471],[1473,1474],[1476,1477],[1479],[1552,1562],[1611,1631],[1648],[1750,1756],[1759,1764],[1767,1768],[1770,1773],[1809],[1840,1866],[1958,1968],[2027,2035],[2070,2073],[2075,2083],[2085,2087],[2089,2093],[2137,2139],[2304,2307],[2362,2364],[2366,2383],[2385,2391],[2402,2403],[2433,2435],[2492],[2494,2500],[2503,2504],[2507,2509],[2519],[2530,2531],[2561,2563],[2620],[2622,2626],[2631,2632],[2635,2637],[2641],[2672,2673],[2677],[2689,2691],[2748],[2750,2757],[2759,2761],[2763,2765],[2786,2787],[2817,2819],[2876],[2878,2884],[2887,2888],[2891,2893],[2902,2903],[2914,2915],[2946],[3006,3010],[3014,3016],[3018,3021],[3031],[3073,3075],[3134,3140],[3142,3144],[3146,3149],[3157,3158],[3170,3171],[3202,3203],[3260],[3262,3268],[3270,3272],[3274,3277],[3285,3286],[3298,3299],[3330,3331],[3390,3396],[3398,3400],[3402,3405],[3415],[3426,3427],[3458,3459],[3530],[3535,3540],[3542],[3544,3551],[3570,3571],[3633],[3636,3642],[3655,3662],[3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893],[3895],[3897],[3902,3903],[3953,3972],[3974,3975],[3981,3991],[3993,4028],[4038],[4139,4158],[4182,4185],[4190,4192],[4194,4196],[4199,4205],[4209,4212],[4226,4237],[4239],[4250,4253],[4957,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6070,6099],[6109],[6155,6157],[6313],[6432,6443],[6448,6459],[6576,6592],[6600,6601],[6679,6683],[6741,6750],[6752,6780],[6783],[6912,6916],[6964,6980],[7019,7027],[7040,7042],[7073,7082],[7142,7155],[7204,7223],[7376,7378],[7380,7400],[7405],[7410],[7616,7654],[7676,7679],[8203,8205],[8400,8432],[11503,11505],[11647],[11744,11775],[12330,12335],[12441,12442],[42607,42610],[42620,42621],[42736,42737],[43010],[43014],[43019],[43043,43047],[43136,43137],[43188,43204],[43232,43249],[43302,43309],[43335,43347],[43392,43395],[43443,43456],[43561,43574],[43587],[43596,43597],[43643],[43696],[43698,43700],[43703,43704],[43710,43711],[43713],[44003,44010],[44012,44013],[64286],[65024,65039],[65056,65062],[65279]],"2":[[4352,4447],[4515,4519],[4602,4607],[9001,9002],[11904,11929],[11931,12019],[12032,12245],[12272,12283],[12288,12329],[12336,12350],[12353,12438],[12443,12543],[12549,12589],[12593,12686],[12688,12730],[12736,12771],[12784,12830],[12832,12871],[12880,13054],[13056,19903],[19968,42124],[42128,42182],[43360,43388],[44032,55203],[55216,55238],[55243,55291],[63744,64255],[65040,65049],[65072,65106],[65108,65126],[65128,65131],[65281,65376],[65504,65510],[110592,110593],[119134,119135],[119227,119228],[127488,127490],[127504,127546],[127552,127560],[127568,127569],[131072,196605],[196608,262141]],"3":[[119136,119140],[119229,119232]]};
var ambRanges = [[161],[164],[167,168],[170],[173,174],[176,180],[182,186],[188,191],[198],[208],[215,216],[222,225],[230],[232,234],[236,237],[240],[242,243],[247,250],[252],[254],[257],[273],[275],[283],[294,295],[299],[305,307],[312],[319,322],[324],[328,331],[333],[338,339],[358,359],[363],[462],[464],[466],[468],[470],[472],[474],[476],[593],[609],[708],[711],[713,715],[717],[720],[728,731],[733],[735],[903],[913,929],[931,937],[945,961],[963,969],[1025],[1040,1103],[1105],[8126],[8189],[8208],[8211,8214],[8216,8217],[8220,8221],[8224,8226],[8228,8231],[8240],[8242,8243],[8245],[8251],[8254],[8308],[8319],[8321,8324],[8364],[8451],[8453],[8457],[8467],[8470],[8481,8482],[8486],[8531,8532],[8539,8542],[8544,8555],[8560,8569],[8585],[8592,8601],[8632,8633],[8658],[8660],[8679],[8704],[8706,8707],[8711,8712],[8715],[8719],[8721],[8725],[8730],[8733,8736],[8739],[8741],[8743,8748],[8750],[8756,8759],[8764,8765],[8776],[8780],[8786],[8800,8801],[8804,8807],[8810,8811],[8814,8815],[8834,8835],[8838,8839],[8853],[8857],[8869],[8895],[8978],[9312,9449],[9451,9547],[9552,9587],[9600,9615],[9618,9621],[9632,9633],[9635,9641],[9650,9651],[9654,9655],[9660,9661],[9664,9665],[9670,9672],[9675],[9678,9681],[9698,9701],[9711],[9733,9734],[9737],[9742,9743],[9748,9749],[9756],[9758],[9792],[9794],[9824,9825],[9827,9829],[9831,9834],[9836,9837],[9839],[9886,9887],[9918,9919],[9924,9933],[9935,9953],[9955],[9960,9983],[10045],[10071],[10102,10111],[11093,11097],[12872,12879],[55296,61439],[61444,63743],[65533],[127232,127242],[127248,127277],[127280,127337],[127344,127386],[917760,917999],[983040,1048573],[1048576,1114109]];
var cp936Ranges = [[0,127],[164],[167,168],[176,177],[183],[215],[224,225],[232,234],[236,237],[242,243],[247],[249,250],[252],[257],[275],[283],[299],[324],[328],[333],[363],[462],[464],[466],[468],[470],[472],[474],[476],[593],[609],[711],[713,715],[729],[913,929],[931,937],[945,961],[963,969],[1025],[1040,1103],[1105],[8208],[8211,8214],[8216,8217],[8220,8221],[8229,8230],[8240],[8242,8243],[8245],[8251],[8364],[8451],[8453],[8457],[8470],[8481],[8544,8555],[8560,8569],[8592,8595],[8598,8601],[8712],[8719],[8721],[8725],[8730],[8733,8736],[8739],[8741],[8743,8747],[8750],[8756,8759],[8765],[8776],[8780],[8786],[8800,8801],[8804,8807],[8814,8815],[8853],[8857],[8869],[8895],[8978],[9312,9321],[9332,9371],[9472,9547],[9552,9587],[9601,9615],[9619,9621],[9632,9633],[9650,9651],[9660,9661],[9670,9671],[9675],[9678,9679],[9698,9701],[9733,9734],[9737],[9792],[9794],[12288,12291],[12293,12311],[12317,12318],[12321,12329],[12353,12435],[12443,12446],[12449,12534],[12540,12542],[12549,12585],[12832,12841],[12849],[12963],[13198,13199],[13212,13214],[13217],[13252],[13262],[13265,13266],[13269],[19968,40869],[63788],[63865],[63893],[63975],[63985],[64012,64015],[64017],[64019,64020],[64024],[64031,64033],[64035,64036],[64039,64041],[65072,65073],[65075,65092],[65097,65106],[65108,65111],[65113,65126],[65128,65131],[65281,65374],[65504,65509]];
/* eslint-enable */
var sizes = Object.keys(sizeRangesMap).map(function(s) { return parseInt(s, 10) });
/**
* 计算 Unicode 字符串在终端上的长度
*
* @param {String} str 将会显示在终端上的字符串
* @return {Number}
*
* @example
* cliTextSize('中国') // => 4
* cliTextSize('ab') // => 2
*
* @see [tty-text-size@1.0.0]{@link https://github.com/qiu8310/tty-text-size/tree/1.0.0}
* @throws {Error} 当包含 \t, \r, \v, \f, \n 这些无法计算长度的字符时
*
* @since 2.0.0
* @author Zhonglei Qiu
*/
var cliTextSize$1 = function(str) {
str = stripAnsi(str);
return ucs2decode(str).reduce(function(size, cp) {
return size + getCodePointCliSize(cp)
}, 0)
};
function getCodePointCliSize(cp) {
// 特殊字符无法知道它的 size
if ([9, 10, 11, 12, 13].indexOf(cp) >= 0) {
// throw new Error('Code point ' + cp + ' not allowed, its size is not predictable.')
return cp === 9 ? 6 : 0 // tab 在 Mac 的 iTerm 上占了 6 个字符
}
// window 对不支持的字符都使用了默认的占一个位置的字符替代
if (isWin && !inRanges(cp, cp936Ranges)) return 1
// Linux 很多终端此值都是 1,但用户可以修改,但程序不好获取用户修改的值
// 所以取默认值
if (inRanges(cp, ambRanges)) return isWin ? 2 : 1
for (var i = 0; i < sizes.length; i++) {
// console.log(cp, sizeRangesMap[sizes[i]], inRanges(cp, sizeRangesMap[sizes[i]]))
if (inRanges(cp, sizeRangesMap[sizes[i]])) {
// window 不会超过 2 个字符
return isWin ? Math.min(2, sizes[i]) : sizes[i]
}
}
return 1
}
function inRanges(cp, ranges) {
return ranges.some(function(range) {
if (range.length === 1) return cp === range[0]
return cp >= range[0] && cp <= range[1]
})
}
var _cliTextSize = /*@__PURE__*/_commonjsHelpers.getDefaultExportFromCjs(cliTextSize$1);
/**
* @module libs/tty/table
* @createdAt 2016-07-19
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
var punycode = _helper;
var os = require$$1;
var cliTextSize = cliTextSize$1;
var greAnsi = format.stripAnsiExports.gre;
var encode = punycode.ucs2encode;
var decode = punycode.ucs2decode;
// 获取当前屏幕的宽度
var stream = process.stdout;
var MIN_OVERFLOW_CELL_WIDTH = 10;
var SCREEN_WIDTH = 120;
/* istanbul ignore else */
if (stream.getWindowSize && !process.env.CI) SCREEN_WIDTH = stream.getWindowSize()[0]; // 在 CI 中以 120 为准,travis 中只有 32,导致很多输出断行了,很多测试失败
/**
* 将一个二维的字符串数组,转化成一个 table 格式的字符串,方便在终端上显示,主要特点有:
*
* 1. 精确计算字符的宽度,自动换行:很多老外写的类似的库都采用 str.length 来得到字符串在终端上的显示长度,
* 但是,中文在终端上一般占用两个字符,而且不同系统上字符长度也会不一样
* 2. 自动重置颜色:当一个 cell 中有颜色时,如果不重置色值,它是会影响后面 cell 的
* 3. 判断是否超出屏幕大小:如果超出,自动截断最宽的一行
*
* @param {Array<Array<String>>} rows table 中每个 cell 中的字符串,是个二维的字符串数组
* @return {String} table 化的字符串
*
* @example
*
* table([
* ['a', 'b', 'c'],
* ['d', 'ee', 'f']
* ])
*
* // ab c
* // deef
*
* @see [tty-wrap@0.1.0]{@link https://github.com/qiu8310/tty-wrap/tree/0.1.0}
* @since 2.0.0
* @author Zhonglei Qiu
*/
var table = function(rows) {
if (!Array.isArray(rows) || !Array.isArray(rows[0]) || !rows[0].length) return ''
var maxCellHeights = new Array(rows.length); // 同一行中, cell 占位最多的高度
// 初始化
each(rows, function(val, opt) {
rows[opt.r][opt.c] = val = val.replace(/[\r\v\f]/g, '').replace(/\t/g, ' ').split(/\n/);
maxCellHeights[opt.r] = Math.max(maxCellHeights[opt.r] || 0, val.length);
});
var maxColumnWidths = getMaxWidthsOfEachColumn(rows);
var sumwidth = sum(maxColumnWidths);
// 处理宽度最宽的一列的宽度(不能超过屏幕)
if (sumwidth > SCREEN_WIDTH) {
var maxindex = maxNumberIndex(maxColumnWidths);
var limit = Math.max(MIN_OVERFLOW_CELL_WIDTH, SCREEN_WIDTH - (sumwidth - maxColumnWidths[maxindex])); // 保证不要小于 0
maxColumnWidths[maxindex] = limit;
each(rows, function(val, opt) {
if (opt.c === maxindex) {
rows[opt.r][opt.c] = val = wrapCell(val, limit);
maxCellHeights[opt.r] = Math.max(maxCellHeights[opt.r], val.length);
}
});
}
// 保证每个 cell 的宽度和高度一致
each(rows, function(val, opt) {
var i;
var l = val.length;
// 统一高度
for (i = 0; i < maxCellHeights[opt.r] - l; i++) {
val.push('');
}
// 统一宽度
val.forEach(function(str, j) {
val[j] = str + space(maxColumnWidths[opt.c] - cliTextSize(str));
});
// 保证 cli 上的颜色不要互相污染
rows[opt.r][opt.c] = wrapColor(val);
});
// 组装
var r, c, j, row;
var result = [];
for (r = 0; r < rows.length; r++) {
for (j = 0; j < maxCellHeights[r]; j++) {
row = [];
for (c = 0; c < rows[r].length; c++) {
row.push(rows[r][c][j]);
}
result.push(row.join(''));
}
}
return result.join(os.EOL)
};
// 限制 cell 的宽度,过长就将它裁断
function wrapCell(cell, size) {
var res = [];
cell.forEach(function(str) {
if (cliTextSize(str) > size) {
res.push.apply(res, wrapStr(str, size));
} else {
res.push(str);
}
});
return res
}
// 限制 string 的宽度,过长就将它裁断
function wrapStr(str, size) {
var rows = [];
var ansis = [];
// str 中可能包含 ansi 控制字符
// 要先备份其位置
str = str.replace(greAnsi, function(ansi, i) {
// 需要减去之前 ansi 的长度
var index = i;
/* istanbul ignore next */
for (var j = 0; j < ansis.length; j++) index -= ansis[j][1].length;
// 如果和上一个对象的 index 相同,需要合并 (因为存在添加多个 ansi 前缀的可能)
var last = ansis[ansis.length - 1];
/* istanbul ignore next */
if (last && last[0] === index) {
last[1] += ansi;
} else {
ansis.push([index, ansi]);
}
return ''
});
var data = [];
var cps = decode(str);
cps.forEach(function(cp, i) {
var char = encode([cp]);
data.push({
char: char,
size: cliTextSize(char)
});
});
var i, ansi, ai;
var len = 0;
var fetchAnsi = function() {
var t = ansis.shift();
ai = t && t[0];
ansi = t && t[1];
};
str = '';
fetchAnsi();
for (i = 0; i < data.length; i++) {
if (len + data[i].size > size) {
rows.push(str);
str = '';
len = 0;
}
if (ansi && ai === i) {
str += ansi;
fetchAnsi();
}
str += data[i].char;
len += data[i].size;
}
/* istanbul ignore else */
if (str) rows.push(str);
if (ansi) rows[rows.length - 1] += ansi;
return rows
}
// 保证 cli 上的颜色不要互相污染
function wrapColor(cell) {
var hasAnsi;
var stack = '';
var res = [];
cell.forEach(function(str) {
var prevStack = stack;
str.replace(greAnsi, function(ansi) {
hasAnsi = true;
stack += ansi;
});
if (hasAnsi) str += '\x1b[0m';
res.push(prevStack + str);
});
return res
}
function each(data, fn) {
var r, c;
var rowSize = data.length;
var colSize = data[0].length;
for (r = 0; r < rowSize; r++) {
for (c = 0; c < colSize; c++) {
fn(data[r][c], {r: r, c: c});
}
}
}
// 获取每列中宽度最大的值
function getMaxWidthsOfEachColumn(rows) {
var result = new Array(rows[0].length);
each(rows, function(cell, opt) {
result[opt.c] = Math.max(result[opt.c] || 0, getCellWidth(cell));
});
return result
}
function getCellWidth(cell) {
return Math.max.apply(Math, cell.map(cliTextSize))
}
function space(n) {
if (n < 1) return ''
return new Array(n + 1).join(' ')
}
function sum(arr) {
return arr.reduce(function(s, i) {
return s + i
}, 0)
}
function maxNumberIndex(arr) {
var i;
var max = 0;
var res = -1;
for (i = 0; i < arr.length; i++) {
if (arr[i] >= max) { // 优先取最后的最大值
max = arr[i];
res = i;
}
}
return res
}
var table$1 = /*@__PURE__*/_commonjsHelpers.getDefaultExportFromCjs(table);
exports._cliTextSize = _cliTextSize;
exports.table = table;
exports.table$1 = table$1;