cli-truncate 4.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -93,7 +93,9 @@ cliTruncate('unicorn', 4, {position: 'middle'});
93
93
  //=> 'un…n'
94
94
 
95
95
  cliTruncate('\u001B[31municorn\u001B[39m', 4);
96
- //=> '\u001B[31muni\u001B[39m'
96
+ //=> '\u001B[31muni…\u001B[39m'
97
+
98
+ // Note: When truncating styled text (ANSI escapes), the truncation character inherits the style at the breaking point for `position: 'start'` and `position: 'end'`. This does not apply to `position: 'middle'`.
97
99
 
98
100
  // Truncate Unicode surrogate pairs
99
101
  cliTruncate('uni\uD83C\uDE00corn', 5);
package/index.js CHANGED
@@ -49,21 +49,88 @@ export default function cliTruncate(text, columns, options = {}) {
49
49
  return text;
50
50
  }
51
51
 
52
+ // ANSI escape sequence constants
53
+ const ANSI = {
54
+ ESC: 27,
55
+ LEFT_BRACKET: 91,
56
+ LETTER_M: 109,
57
+ };
58
+
59
+ const isSgrParameter = code => (code >= 48 && code <= 57) || code === 59; // 0-9 or ;
60
+
61
+ function leadingSgrSpanEndIndex(string) {
62
+ let index = 0;
63
+ while (index + 2 < string.length && string.codePointAt(index) === ANSI.ESC && string.codePointAt(index + 1) === ANSI.LEFT_BRACKET) {
64
+ let j = index + 2;
65
+ while (j < string.length && isSgrParameter(string.codePointAt(j))) {
66
+ j++;
67
+ }
68
+
69
+ if (j < string.length && string.codePointAt(j) === ANSI.LETTER_M) {
70
+ index = j + 1;
71
+ continue;
72
+ }
73
+
74
+ break;
75
+ }
76
+
77
+ return index;
78
+ }
79
+
80
+ function trailingSgrSpanStartIndex(string) {
81
+ let start = string.length;
82
+ while (start > 1 && string.codePointAt(start - 1) === ANSI.LETTER_M) {
83
+ let j = start - 2;
84
+ while (j >= 0 && isSgrParameter(string.codePointAt(j))) {
85
+ j--;
86
+ }
87
+
88
+ if (j >= 1 && string.codePointAt(j - 1) === ANSI.ESC && string.codePointAt(j) === ANSI.LEFT_BRACKET) {
89
+ start = j - 1;
90
+ continue;
91
+ }
92
+
93
+ break;
94
+ }
95
+
96
+ return start;
97
+ }
98
+
99
+ function appendWithInheritedStyleFromEnd(visible, suffix) {
100
+ const start = trailingSgrSpanStartIndex(visible);
101
+ if (start === visible.length) {
102
+ return visible + suffix;
103
+ }
104
+
105
+ return visible.slice(0, start) + suffix + visible.slice(start);
106
+ }
107
+
108
+ function prependWithInheritedStyleFromStart(prefix, visible) {
109
+ const end = leadingSgrSpanEndIndex(visible);
110
+ if (end === 0) {
111
+ return prefix + visible;
112
+ }
113
+
114
+ return visible.slice(0, end) + prefix + visible.slice(end);
115
+ }
116
+
52
117
  if (position === 'start') {
53
118
  if (preferTruncationOnSpace) {
54
119
  const nearestSpace = getIndexOfNearestSpace(text, length - columns + 1, true);
55
- return truncationCharacter + sliceAnsi(text, nearestSpace, length).trim();
120
+ const right = sliceAnsi(text, nearestSpace, length).trim();
121
+ return prependWithInheritedStyleFromStart(truncationCharacter, right);
56
122
  }
57
123
 
58
- if (space === true) {
124
+ if (space) {
59
125
  truncationCharacter += ' ';
60
126
  }
61
127
 
62
- return truncationCharacter + sliceAnsi(text, length - columns + stringWidth(truncationCharacter), length);
128
+ const right = sliceAnsi(text, length - columns + stringWidth(truncationCharacter), length);
129
+ return prependWithInheritedStyleFromStart(truncationCharacter, right);
63
130
  }
64
131
 
65
132
  if (position === 'middle') {
66
- if (space === true) {
133
+ if (space) {
67
134
  truncationCharacter = ` ${truncationCharacter} `;
68
135
  }
69
136
 
@@ -77,22 +144,24 @@ export default function cliTruncate(text, columns, options = {}) {
77
144
 
78
145
  return (
79
146
  sliceAnsi(text, 0, half)
80
- + truncationCharacter
81
- + sliceAnsi(text, length - (columns - half) + stringWidth(truncationCharacter), length)
147
+ + truncationCharacter
148
+ + sliceAnsi(text, length - (columns - half) + stringWidth(truncationCharacter), length)
82
149
  );
83
150
  }
84
151
 
85
152
  if (position === 'end') {
86
153
  if (preferTruncationOnSpace) {
87
154
  const nearestSpace = getIndexOfNearestSpace(text, columns - 1);
88
- return sliceAnsi(text, 0, nearestSpace) + truncationCharacter;
155
+ const left = sliceAnsi(text, 0, nearestSpace);
156
+ return appendWithInheritedStyleFromEnd(left, truncationCharacter);
89
157
  }
90
158
 
91
- if (space === true) {
159
+ if (space) {
92
160
  truncationCharacter = ` ${truncationCharacter}`;
93
161
  }
94
162
 
95
- return sliceAnsi(text, 0, columns - stringWidth(truncationCharacter)) + truncationCharacter;
163
+ const left = sliceAnsi(text, 0, columns - stringWidth(truncationCharacter));
164
+ return appendWithInheritedStyleFromEnd(left, truncationCharacter);
96
165
  }
97
166
 
98
167
  throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${position}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-truncate",
3
- "version": "4.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Truncate a string to a specific width in the terminal",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/cli-truncate",
@@ -15,8 +15,9 @@
15
15
  "types": "./index.d.ts",
16
16
  "default": "./index.js"
17
17
  },
18
+ "sideEffects": false,
18
19
  "engines": {
19
- "node": ">=18"
20
+ "node": ">=20"
20
21
  },
21
22
  "scripts": {
22
23
  "test": "xo && ava && tsd"
@@ -40,12 +41,12 @@
40
41
  "string"
41
42
  ],
42
43
  "dependencies": {
43
- "slice-ansi": "^5.0.0",
44
- "string-width": "^7.0.0"
44
+ "slice-ansi": "^7.1.0",
45
+ "string-width": "^8.0.0"
45
46
  },
46
47
  "devDependencies": {
47
- "ava": "^5.3.1",
48
- "tsd": "^0.29.0",
49
- "xo": "^0.56.0"
48
+ "ava": "^6.4.1",
49
+ "tsd": "^0.33.0",
50
+ "xo": "^1.2.2"
50
51
  }
51
52
  }
package/readme.md CHANGED
@@ -25,11 +25,14 @@ cliTruncate('unicorn', 4, {position: 'start'});
25
25
  cliTruncate('unicorn', 4, {position: 'middle'});
26
26
  //=> 'un…n'
27
27
 
28
- cliTruncate('unicorns rainbow dragons', 6, {position: 'end'})
28
+ cliTruncate('unicorns rainbow dragons', 6, {position: 'end'});
29
29
  //=> 'unico…'
30
30
 
31
31
  cliTruncate('\u001B[31municorn\u001B[39m', 4);
32
- //=> '\u001B[31muni\u001B[39m'
32
+ //=> '\u001B[31muni…\u001B[39m'
33
+
34
+ > [!NOTE]
35
+ > When truncating styled text (ANSI escapes), the truncation character inherits the style at the breaking point for `position: 'start'` and `position: 'end'`. This does not apply to `position: 'middle'`.
33
36
 
34
37
  // Truncate Unicode surrogate pairs
35
38
  cliTruncate('uni\uD83C\uDE00corn', 5);
@@ -106,21 +109,21 @@ Truncate the string from a whitespace if it is within 3 characters from the actu
106
109
  ```js
107
110
  import cliTruncate from 'cli-truncate';
108
111
 
109
- cliTruncate('unicorns rainbow dragons', 20, {position: 'start', preferTruncationOnSpace: true})
112
+ cliTruncate('unicorns rainbow dragons', 20, {position: 'start', preferTruncationOnSpace: true});
110
113
  //=> '…rainbow dragons'
111
114
 
112
- // without preferTruncationOnSpace
113
- cliTruncate('unicorns rainbow dragons', 20, {position: 'start'})
115
+ // Without preferTruncationOnSpace
116
+ cliTruncate('unicorns rainbow dragons', 20, {position: 'start'});
114
117
  //=> '…rns rainbow dragons'
115
118
 
116
- cliTruncate('unicorns rainbow dragons', 20, {position: 'middle', preferTruncationOnSpace: true})
119
+ cliTruncate('unicorns rainbow dragons', 20, {position: 'middle', preferTruncationOnSpace: true});
117
120
  //=> 'unicorns…dragons'
118
121
 
119
- cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true})
122
+ cliTruncate('unicorns rainbow dragons', 6, {position: 'end', preferTruncationOnSpace: true});
120
123
  //=> 'unico…'
121
124
 
122
- // preferTruncationOnSpace would have no effect if space isn't found within next 3 indexes
123
- cliTruncate('unicorns rainbow dragons', 6, {position: 'middle', preferTruncationOnSpace: true})
125
+ // preferTruncationOnSpace has no effect if space isn't found within 3 characters
126
+ cliTruncate('unicorns rainbow dragons', 6, {position: 'middle', preferTruncationOnSpace: true});
124
127
  //=> 'uni…ns'
125
128
  ```
126
129
 
@@ -142,6 +145,7 @@ cliTruncate('unicorns', 5, {position: 'end', truncationCharacter: '.'});
142
145
 
143
146
  cliTruncate('unicorns', 5, {position: 'end', truncationCharacter: ''});
144
147
  //=> 'unico'
148
+
145
149
  ```
146
150
 
147
151
  ## Related