UNPKG

2.55 kBJavaScriptView Raw
1'use strict';
2const sliceAnsi = require('slice-ansi');
3const stringWidth = require('string-width');
4
5function getIndexOfNearestSpace(string, index, shouldSearchRight) {
6 if (string.charAt(index) === ' ') {
7 return index;
8 }
9
10 for (let i = 1; i <= 3; i++) {
11 if (shouldSearchRight) {
12 if (string.charAt(index + i) === ' ') {
13 return index + i;
14 }
15 } else if (string.charAt(index - i) === ' ') {
16 return index - i;
17 }
18 }
19
20 return index;
21}
22
23module.exports = (text, columns, options) => {
24 options = {
25 position: 'end',
26 preferTruncationOnSpace: false,
27 ...options
28 };
29
30 const {position, space, preferTruncationOnSpace} = options;
31 let ellipsis = '…';
32 let ellipsisWidth = 1;
33
34 if (typeof text !== 'string') {
35 throw new TypeError(`Expected \`input\` to be a string, got ${typeof text}`);
36 }
37
38 if (typeof columns !== 'number') {
39 throw new TypeError(`Expected \`columns\` to be a number, got ${typeof columns}`);
40 }
41
42 if (columns < 1) {
43 return '';
44 }
45
46 if (columns === 1) {
47 return ellipsis;
48 }
49
50 const length = stringWidth(text);
51
52 if (length <= columns) {
53 return text;
54 }
55
56 if (position === 'start') {
57 if (preferTruncationOnSpace) {
58 const nearestSpace = getIndexOfNearestSpace(text, length - columns + 1, true);
59 return ellipsis + sliceAnsi(text, nearestSpace, length).trim();
60 }
61
62 if (space === true) {
63 ellipsis += ' ';
64 ellipsisWidth = 2;
65 }
66
67 return ellipsis + sliceAnsi(text, length - columns + ellipsisWidth, length);
68 }
69
70 if (position === 'middle') {
71 if (space === true) {
72 ellipsis = ' ' + ellipsis + ' ';
73 ellipsisWidth = 3;
74 }
75
76 const half = Math.floor(columns / 2);
77
78 if (preferTruncationOnSpace) {
79 const spaceNearFirstBreakPoint = getIndexOfNearestSpace(text, half);
80 const spaceNearSecondBreakPoint = getIndexOfNearestSpace(text, length - (columns - half) + 1, true);
81 return sliceAnsi(text, 0, spaceNearFirstBreakPoint) + ellipsis + sliceAnsi(text, spaceNearSecondBreakPoint, length).trim();
82 }
83
84 return (
85 sliceAnsi(text, 0, half) +
86 ellipsis +
87 sliceAnsi(text, length - (columns - half) + ellipsisWidth, length)
88 );
89 }
90
91 if (position === 'end') {
92 if (preferTruncationOnSpace) {
93 const nearestSpace = getIndexOfNearestSpace(text, columns - 1);
94 return sliceAnsi(text, 0, nearestSpace) + ellipsis;
95 }
96
97 if (space === true) {
98 ellipsis = ' ' + ellipsis;
99 ellipsisWidth = 2;
100 }
101
102 return sliceAnsi(text, 0, columns - ellipsisWidth) + ellipsis;
103 }
104
105 throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${position}`);
106};