wrap-ansi 3.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.
Files changed (3) hide show
  1. package/index.js +77 -78
  2. package/package.json +11 -12
  3. package/readme.md +19 -0
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  const stringWidth = require('string-width');
3
3
  const stripAnsi = require('strip-ansi');
4
+ const ansiStyles = require('ansi-styles');
4
5
 
5
6
  const ESCAPES = new Set([
6
7
  '\u001B',
@@ -9,63 +10,33 @@ const ESCAPES = new Set([
9
10
 
10
11
  const END_CODE = 39;
11
12
 
12
- const ESCAPE_CODES = new Map([
13
- [0, 0],
14
- [1, 22],
15
- [2, 22],
16
- [3, 23],
17
- [4, 24],
18
- [7, 27],
19
- [8, 28],
20
- [9, 29],
21
- [30, 39],
22
- [31, 39],
23
- [32, 39],
24
- [33, 39],
25
- [34, 39],
26
- [35, 39],
27
- [36, 39],
28
- [37, 39],
29
- [90, 39],
30
- [40, 49],
31
- [41, 49],
32
- [42, 49],
33
- [43, 49],
34
- [44, 49],
35
- [45, 49],
36
- [46, 49],
37
- [47, 49]
38
- ]);
39
-
40
13
  const wrapAnsi = code => `${ESCAPES.values().next().value}[${code}m`;
41
14
 
42
15
  // Calculate the length of words split on ' ', ignoring
43
16
  // the extra characters added by ansi escape codes
44
- const wordLengths = str => str.split(' ').map(s => stringWidth(s));
17
+ const wordLengths = string => string.split(' ').map(character => stringWidth(character));
45
18
 
46
19
  // Wrap a long word across multiple rows
47
20
  // Ansi escape codes do not count towards length
48
- const wrapWord = (rows, word, cols) => {
49
- const arr = Array.from(word);
21
+ const wrapWord = (rows, word, columns) => {
22
+ const characters = [...word];
50
23
 
51
24
  let insideEscape = false;
52
25
  let visible = stringWidth(stripAnsi(rows[rows.length - 1]));
53
26
 
54
- for (const item of arr.entries()) {
55
- const i = item[0];
56
- const char = item[1];
57
- const charLength = stringWidth(char);
27
+ for (const [index, character] of characters.entries()) {
28
+ const characterLength = stringWidth(character);
58
29
 
59
- if (visible + charLength <= cols) {
60
- rows[rows.length - 1] += char;
30
+ if (visible + characterLength <= columns) {
31
+ rows[rows.length - 1] += character;
61
32
  } else {
62
- rows.push(char);
33
+ rows.push(character);
63
34
  visible = 0;
64
35
  }
65
36
 
66
- if (ESCAPES.has(char)) {
37
+ if (ESCAPES.has(character)) {
67
38
  insideEscape = true;
68
- } else if (insideEscape && char === 'm') {
39
+ } else if (insideEscape && character === 'm') {
69
40
  insideEscape = false;
70
41
  continue;
71
42
  }
@@ -74,9 +45,9 @@ const wrapWord = (rows, word, cols) => {
74
45
  continue;
75
46
  }
76
47
 
77
- visible += charLength;
48
+ visible += characterLength;
78
49
 
79
- if (visible === cols && i < arr.length - 1) {
50
+ if (visible === columns && index < characters.length - 1) {
80
51
  rows.push('');
81
52
  visible = 0;
82
53
  }
@@ -89,88 +60,116 @@ const wrapWord = (rows, word, cols) => {
89
60
  }
90
61
  };
91
62
 
63
+ // Trims spaces from a string ignoring invisible sequences
64
+ const stringVisibleTrimSpacesRight = str => {
65
+ const words = str.split(' ');
66
+ let last = words.length;
67
+
68
+ while (last > 0) {
69
+ if (stringWidth(words[last - 1]) > 0) {
70
+ break;
71
+ }
72
+
73
+ last--;
74
+ }
75
+
76
+ if (last === words.length) {
77
+ return str;
78
+ }
79
+
80
+ return words.slice(0, last).join(' ') + words.slice(last).join('');
81
+ };
82
+
92
83
  // The wrap-ansi module can be invoked
93
84
  // in either 'hard' or 'soft' wrap mode
94
85
  //
95
86
  // 'hard' will never allow a string to take up more
96
- // than cols characters
87
+ // than columns characters
97
88
  //
98
89
  // 'soft' allows long words to expand past the column length
99
- const exec = (str, cols, opts) => {
100
- const options = opts || {};
90
+ const exec = (string, columns, options = {}) => {
91
+ if (options.trim !== false && string.trim() === '') {
92
+ return '';
93
+ }
101
94
 
102
95
  let pre = '';
103
96
  let ret = '';
104
97
  let escapeCode;
105
98
 
106
- const lengths = wordLengths(str);
107
- const words = str.split(' ');
108
- const rows = [''];
99
+ const lengths = wordLengths(string);
100
+ let rows = [''];
109
101
 
110
- for (const item of Array.from(words).entries()) {
111
- const i = item[0];
112
- const word = item[1];
102
+ for (const [index, word] of string.split(' ').entries()) {
103
+ if (options.trim !== false) {
104
+ rows[rows.length - 1] = rows[rows.length - 1].trimLeft();
105
+ }
113
106
 
114
- rows[rows.length - 1] = options.trim === false ? rows[rows.length - 1] : rows[rows.length - 1].trim();
115
107
  let rowLength = stringWidth(rows[rows.length - 1]);
116
108
 
117
- if (rowLength || word === '') {
118
- if (rowLength === cols && options.wordWrap === false) {
109
+ if (index !== 0) {
110
+ if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
119
111
  // If we start with a new word but the current row length equals the length of the columns, add a new row
120
112
  rows.push('');
121
113
  rowLength = 0;
122
114
  }
123
115
 
124
- rows[rows.length - 1] += ' ';
125
- rowLength++;
116
+ if (rowLength > 0 || options.trim === false) {
117
+ rows[rows.length - 1] += ' ';
118
+ rowLength++;
119
+ }
126
120
  }
127
121
 
128
122
  // In 'hard' wrap mode, the length of a line is
129
- // never allowed to extend past 'cols'
130
- if (lengths[i] > cols && options.hard) {
131
- if (rowLength) {
123
+ // never allowed to extend past 'columns'
124
+ if (options.hard && lengths[index] > columns) {
125
+ const remainingColumns = (columns - rowLength);
126
+ const breaksStartingThisLine = 1 + Math.floor((lengths[index] - remainingColumns - 1) / columns);
127
+ const breaksStartingNextLine = Math.floor((lengths[index] - 1) / columns);
128
+ if (breaksStartingNextLine < breaksStartingThisLine) {
132
129
  rows.push('');
133
130
  }
134
- wrapWord(rows, word, cols);
131
+
132
+ wrapWord(rows, word, columns);
135
133
  continue;
136
134
  }
137
135
 
138
- if (rowLength + lengths[i] > cols && rowLength > 0) {
139
- if (options.wordWrap === false && rowLength < cols) {
140
- wrapWord(rows, word, cols);
136
+ if (rowLength + lengths[index] > columns && rowLength > 0 && lengths[index] > 0) {
137
+ if (options.wordWrap === false && rowLength < columns) {
138
+ wrapWord(rows, word, columns);
141
139
  continue;
142
140
  }
143
141
 
144
142
  rows.push('');
145
143
  }
146
144
 
147
- if (rowLength + lengths[i] > cols && options.wordWrap === false) {
148
- wrapWord(rows, word, cols);
145
+ if (rowLength + lengths[index] > columns && options.wordWrap === false) {
146
+ wrapWord(rows, word, columns);
149
147
  continue;
150
148
  }
151
149
 
152
150
  rows[rows.length - 1] += word;
153
151
  }
154
152
 
155
- pre = rows.map(r => options.trim === false ? r : r.trim()).join('\n');
153
+ if (options.trim !== false) {
154
+ rows = rows.map(stringVisibleTrimSpacesRight);
155
+ }
156
156
 
157
- for (const item of Array.from(pre).entries()) {
158
- const i = item[0];
159
- const char = item[1];
157
+ pre = rows.join('\n');
160
158
 
161
- ret += char;
159
+ for (const [index, character] of [...pre].entries()) {
160
+ ret += character;
162
161
 
163
- if (ESCAPES.has(char)) {
164
- const code = parseFloat(/\d[^m]*/.exec(pre.slice(i, i + 4)));
162
+ if (ESCAPES.has(character)) {
163
+ const code = parseFloat(/\d[^m]*/.exec(pre.slice(index, index + 4)));
165
164
  escapeCode = code === END_CODE ? null : code;
166
165
  }
167
166
 
168
- const code = ESCAPE_CODES.get(Number(escapeCode));
167
+ const code = ansiStyles.codes.get(Number(escapeCode));
169
168
 
170
169
  if (escapeCode && code) {
171
- if (pre[i + 1] === '\n') {
170
+ if (pre[index + 1] === '\n') {
172
171
  ret += wrapAnsi(code);
173
- } else if (char === '\n') {
172
+ } else if (character === '\n') {
174
173
  ret += wrapAnsi(escapeCode);
175
174
  }
176
175
  }
@@ -180,10 +179,10 @@ const exec = (str, cols, opts) => {
180
179
  };
181
180
 
182
181
  // For each newline, invoke the method separately
183
- module.exports = (str, cols, opts) => {
184
- return String(str)
182
+ module.exports = (string, columns, options) => {
183
+ return String(string)
185
184
  .normalize()
186
185
  .split('\n')
187
- .map(line => exec(line, cols, opts))
186
+ .map(line => exec(line, columns, options))
188
187
  .join('\n');
189
188
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wrap-ansi",
3
- "version": "3.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "Wordwrap a string with ANSI escape codes",
5
5
  "license": "MIT",
6
6
  "repository": "chalk/wrap-ansi",
@@ -10,11 +10,10 @@
10
10
  "url": "sindresorhus.com"
11
11
  },
12
12
  "engines": {
13
- "node": ">=4"
13
+ "node": ">=6"
14
14
  },
15
15
  "scripts": {
16
- "test": "xo && nyc ava",
17
- "coveralls": "nyc report --reporter=text-lcov | coveralls"
16
+ "test": "xo && nyc ava"
18
17
  },
19
18
  "files": [
20
19
  "index.js"
@@ -47,16 +46,16 @@
47
46
  "text"
48
47
  ],
49
48
  "dependencies": {
50
- "string-width": "^2.1.1",
51
- "strip-ansi": "^4.0.0"
49
+ "ansi-styles": "^3.2.0",
50
+ "string-width": "^3.0.0",
51
+ "strip-ansi": "^5.0.0"
52
52
  },
53
53
  "devDependencies": {
54
- "ava": "^0.21.0",
55
- "chalk": "^2.0.1",
56
- "coveralls": "^2.11.4",
54
+ "ava": "^1.2.1",
55
+ "chalk": "^2.4.2",
56
+ "coveralls": "^3.0.3",
57
57
  "has-ansi": "^3.0.0",
58
- "nyc": "^11.0.3",
59
- "strip-ansi": "^4.0.0",
60
- "xo": "^0.18.2"
58
+ "nyc": "^13.3.0",
59
+ "xo": "^0.24.0"
61
60
  }
62
61
  }
package/readme.md CHANGED
@@ -24,6 +24,20 @@ console.log(wrapAnsi(input, 20));
24
24
 
25
25
  <img width="331" src="screenshot.png">
26
26
 
27
+ ---
28
+
29
+ <div align="center">
30
+ <b>
31
+ <a href="https://tidelift.com/subscription/pkg/npm-wrap_ansi?utm_source=npm-wrap-ansi&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
32
+ </b>
33
+ <br>
34
+ <sub>
35
+ Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
36
+ </sub>
37
+ </div>
38
+
39
+ ---
40
+
27
41
 
28
42
  ## API
29
43
 
@@ -84,6 +98,11 @@ Whitespace on all lines is removed by default. Set this option to `false` if you
84
98
  - [Benjamin Coe](https://github.com/bcoe)
85
99
 
86
100
 
101
+ ## Security
102
+
103
+ To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
104
+
105
+
87
106
  ## License
88
107
 
89
108
  MIT