chalk 2.0.1 → 2.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 (4) hide show
  1. package/index.js +16 -10
  2. package/package.json +5 -4
  3. package/readme.md +9 -5
  4. package/templates.js +94 -141
package/index.js CHANGED
@@ -19,13 +19,14 @@ function applyOptions(obj, options) {
19
19
  options = options || {};
20
20
 
21
21
  // Detect level if not set manually
22
- obj.level = options.level === undefined ? supportsColor.level : options.level;
22
+ const scLevel = supportsColor ? supportsColor.level : 0;
23
+ obj.level = options.level === undefined ? scLevel : options.level;
23
24
  obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0;
24
25
  }
25
26
 
26
27
  function Chalk(options) {
27
- // We check for this.template here since calling chalk.constructor()
28
- // by itself will have a `this` of a previously constructed chalk object.
28
+ // We check for this.template here since calling `chalk.constructor()`
29
+ // by itself will have a `this` of a previously constructed chalk object
29
30
  if (!this || !(this instanceof Chalk) || this.template) {
30
31
  const chalk = {};
31
32
  applyOptions(chalk, options);
@@ -142,7 +143,7 @@ function build(_styles, key) {
142
143
  builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey';
143
144
 
144
145
  // `__proto__` is used because we must return a function, but there is
145
- // no way to create a function with a different prototype.
146
+ // no way to create a function with a different prototype
146
147
  builder.__proto__ = proto; // eslint-disable-line no-proto
147
148
 
148
149
  return builder;
@@ -152,7 +153,11 @@ function applyStyle() {
152
153
  // Support varags, but simply cast to string in case there's only one arg
153
154
  const args = arguments;
154
155
  const argsLen = args.length;
155
- let str = argsLen !== 0 && String(arguments[0]);
156
+ let str = String(arguments[0]);
157
+
158
+ if (argsLen === 0) {
159
+ return '';
160
+ }
156
161
 
157
162
  if (argsLen > 1) {
158
163
  // Don't slice `arguments`, it prevents V8 optimizations
@@ -192,17 +197,18 @@ function applyStyle() {
192
197
  }
193
198
 
194
199
  function chalkTag(chalk, strings) {
195
- const args = [].slice.call(arguments, 2);
196
-
197
200
  if (!Array.isArray(strings)) {
198
- return strings.toString();
201
+ // If chalk() was called by itself or with a string,
202
+ // return the string itself as a string.
203
+ return [].slice.call(arguments, 1).join(' ');
199
204
  }
200
205
 
206
+ const args = [].slice.call(arguments, 2);
201
207
  const parts = [strings.raw[0]];
202
208
 
203
209
  for (let i = 1; i < strings.length; i++) {
204
- parts.push(args[i - 1].toString().replace(/[{}]/g, '\\$&'));
205
- parts.push(strings.raw[i]);
210
+ parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&'));
211
+ parts.push(String(strings.raw[i]));
206
212
  }
207
213
 
208
214
  return template(chalk, parts.join(''));
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "chalk",
3
- "version": "2.0.1",
4
- "description": "Terminal string styling done right. Much color",
3
+ "version": "2.1.0",
4
+ "description": "Terminal string styling done right",
5
5
  "license": "MIT",
6
6
  "repository": "chalk/chalk",
7
7
  "engines": {
8
8
  "node": ">=4"
9
9
  },
10
10
  "scripts": {
11
- "test": "xo && nyc mocha",
11
+ "test": "xo && nyc ava",
12
12
  "bench": "matcha benchmark.js",
13
13
  "coveralls": "nyc report --reporter=text-lcov | coveralls"
14
14
  },
@@ -45,10 +45,11 @@
45
45
  "supports-color": "^4.0.0"
46
46
  },
47
47
  "devDependencies": {
48
+ "ava": "*",
48
49
  "coveralls": "^2.11.2",
50
+ "execa": "^0.7.0",
49
51
  "import-fresh": "^2.0.0",
50
52
  "matcha": "^0.7.0",
51
- "mocha": "*",
52
53
  "nyc": "^11.0.2",
53
54
  "resolve-from": "^3.0.0",
54
55
  "xo": "*"
package/readme.md CHANGED
@@ -11,9 +11,7 @@
11
11
 
12
12
  [![Build Status](https://travis-ci.org/chalk/chalk.svg?branch=master)](https://travis-ci.org/chalk/chalk) [![Coverage Status](https://coveralls.io/repos/github/chalk/chalk/badge.svg?branch=master)](https://coveralls.io/github/chalk/chalk?branch=master) [![](https://img.shields.io/badge/unicorn-approved-ff69b4.svg)](https://www.youtube.com/watch?v=9auOCbH5Ns4) [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
13
13
 
14
- [colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68). Although there are other ones, they either do too much or not enough.
15
-
16
- **Chalk is a clean and focused alternative.**
14
+ ### [See what's new in Chalk 2](https://github.com/chalk/chalk/releases/tag/v2.0.0)
17
15
 
18
16
  ![](https://github.com/chalk/ansi-styles/raw/master/screenshot.png)
19
17
 
@@ -183,8 +181,7 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=
183
181
  - `magenta`
184
182
  - `cyan`
185
183
  - `white`
186
- - `gray`
187
- - `blackBright`
184
+ - `gray` ("bright black")
188
185
  - `redBright`
189
186
  - `greenBright`
190
187
  - `yellowBright`
@@ -278,6 +275,11 @@ The following color models can be used:
278
275
  If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) instead of `cmd.exe`.
279
276
 
280
277
 
278
+ ## Origin story
279
+
280
+ [colors.js](https://github.com/Marak/colors.js) used to be the most popular string styling module, but it has serious deficiencies like extending `String.prototype` which causes all kinds of [problems](https://github.com/yeoman/yo/issues/68) and the package is unmaintained. Although there are other packages, they either do too much or not enough. Chalk is a clean and focused alternative.
281
+
282
+
281
283
  ## Related
282
284
 
283
285
  - [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module
@@ -289,6 +291,8 @@ If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) i
289
291
  - [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
290
292
  - [slice-ansi](https://github.com/chalk/slice-ansi) - Slice a string with ANSI escape codes
291
293
  - [color-convert](https://github.com/qix-/color-convert) - Converts colors between different models
294
+ - [chalk-animation](https://github.com/bokub/chalk-animation) - Animate strings in the terminal
295
+ - [gradient-string](https://github.com/bokub/gradient-string) - Apply color gradients to strings
292
296
 
293
297
 
294
298
  ## Maintainers
package/templates.js CHANGED
@@ -1,175 +1,128 @@
1
1
  'use strict';
2
+ const TEMPLATE_REGEX = /(?:\\(u[a-f0-9]{4}|x[a-f0-9]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
3
+ const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
4
+ const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
5
+ const ESCAPE_REGEX = /\\(u[0-9a-f]{4}|x[0-9a-f]{2}|.)|([^\\])/gi;
6
+
7
+ const ESCAPES = {
8
+ n: '\n',
9
+ r: '\r',
10
+ t: '\t',
11
+ b: '\b',
12
+ f: '\f',
13
+ v: '\v',
14
+ 0: '\0',
15
+ '\\': '\\',
16
+ e: '\u001b',
17
+ a: '\u0007'
18
+ };
2
19
 
3
- function data(parent) {
4
- return {
5
- styles: [],
6
- parent,
7
- contents: []
8
- };
9
- }
10
-
11
- const zeroBound = n => n < 0 ? 0 : n;
12
- const lastIndex = a => zeroBound(a.length - 1);
20
+ function unescape(c) {
21
+ if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
22
+ return String.fromCharCode(parseInt(c.slice(1), 16));
23
+ }
13
24
 
14
- const last = a => a[lastIndex(a)];
25
+ return ESCAPES[c] || c;
26
+ }
15
27
 
16
- const takeWhileReverse = (array, predicate, start) => {
17
- const out = [];
28
+ function parseArguments(name, args) {
29
+ const results = [];
30
+ const chunks = args.trim().split(/\s*,\s*/g);
31
+ let matches;
18
32
 
19
- for (let i = start; i >= 0 && i <= start; i--) {
20
- if (predicate(array[i])) {
21
- out.unshift(array[i]);
33
+ for (const chunk of chunks) {
34
+ if (!isNaN(chunk)) {
35
+ results.push(Number(chunk));
36
+ } else if ((matches = chunk.match(STRING_REGEX))) {
37
+ results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr));
22
38
  } else {
23
- break;
39
+ throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
24
40
  }
25
41
  }
26
42
 
27
- return out;
28
- };
29
-
30
- /**
31
- * Checks if the character at position i in string is a normal character a.k.a a non control character.
32
- * */
33
- const isNormalCharacter = (string, i) => {
34
- const char = string[i];
35
- const backslash = '\\';
36
-
37
- if (!(char === backslash || char === '{' || char === '}')) {
38
- return true;
39
- }
40
-
41
- const n = i === 0 ? 0 : takeWhileReverse(string, x => x === '\\', zeroBound(i - 1)).length;
43
+ return results;
44
+ }
42
45
 
43
- return n % 2 === 1;
44
- };
46
+ function parseStyle(style) {
47
+ STYLE_REGEX.lastIndex = 0;
45
48
 
46
- const collectStyles = data => data ? collectStyles(data.parent).concat(data.styles) : ['reset'];
49
+ const results = [];
50
+ let matches;
47
51
 
48
- /**
49
- * Computes the style for a given data based on it's style and the style of it's parent. Also accounts for !style styles
50
- * which remove a style from the list if present.
51
- * */
52
- const sumStyles = data => {
53
- const negateRegex = /^~.+/;
54
- let out = [];
52
+ while ((matches = STYLE_REGEX.exec(style)) !== null) {
53
+ const name = matches[1];
55
54
 
56
- for (const style of collectStyles(data)) {
57
- if (negateRegex.test(style)) {
58
- const exclude = style.slice(1);
59
- out = out.filter(x => x !== exclude);
55
+ if (matches[2]) {
56
+ const args = parseArguments(name, matches[2]);
57
+ results.push([name].concat(args));
60
58
  } else {
61
- out.push(style);
59
+ results.push([name]);
62
60
  }
63
61
  }
64
62
 
65
- return out;
66
- };
67
-
68
- /**
69
- * Takes a string and parses it into a tree of data objects which inherit styles from their parent.
70
- * */
71
- function parse(string) {
72
- const root = data(null);
73
- let pushingStyle = false;
74
-
75
- let current = root;
63
+ return results;
64
+ }
76
65
 
77
- for (let i = 0; i < string.length; i++) {
78
- const char = string[i];
66
+ function buildStyle(chalk, styles) {
67
+ const enabled = {};
79
68
 
80
- const addNormalCharacter = () => {
81
- const lastChunk = last(current.contents);
69
+ for (const layer of styles) {
70
+ for (const style of layer.styles) {
71
+ enabled[style[0]] = layer.inverse ? null : style.slice(1);
72
+ }
73
+ }
82
74
 
83
- if (typeof lastChunk === 'string') {
84
- current.contents[lastIndex(current.contents)] = lastChunk + char;
85
- } else {
86
- current.contents.push(char);
75
+ let current = chalk;
76
+ for (const styleName of Object.keys(enabled)) {
77
+ if (Array.isArray(enabled[styleName])) {
78
+ if (!(styleName in current)) {
79
+ throw new Error(`Unknown Chalk style: ${styleName}`);
87
80
  }
88
- };
89
-
90
- if (pushingStyle) {
91
- if (' \t'.indexOf(char) > -1) {
92
- pushingStyle = false;
93
- } else if (char === '\n') {
94
- pushingStyle = false;
95
- addNormalCharacter();
96
- } else if (char === '.') {
97
- current.styles.push('');
81
+
82
+ if (enabled[styleName].length > 0) {
83
+ current = current[styleName].apply(current, enabled[styleName]);
98
84
  } else {
99
- current.styles[lastIndex(current.styles)] = (last(current.styles) || '') + char;
85
+ current = current[styleName];
100
86
  }
101
- } else if (isNormalCharacter(string, i)) {
102
- addNormalCharacter();
103
- } else if (char === '{') {
104
- pushingStyle = true;
105
- const nCurrent = data(current);
106
- current.contents.push(nCurrent);
107
- current = nCurrent;
108
- } else if (char === '}') {
109
- current = current.parent;
110
87
  }
111
88
  }
112
89
 
113
- if (current !== root) {
114
- throw new Error('literal template has an unclosed block');
115
- }
116
-
117
- return root;
90
+ return current;
118
91
  }
119
92
 
120
- /**
121
- * Takes a tree of data objects and flattens it to a list of data objects with the inherited and negations styles
122
- * accounted for.
123
- * */
124
- function flatten(data) {
125
- let flat = [];
126
-
127
- for (const content of data.contents) {
128
- if (typeof content === 'string') {
129
- flat.push({
130
- styles: sumStyles(data),
131
- content
132
- });
93
+ module.exports = (chalk, tmp) => {
94
+ const styles = [];
95
+ const chunks = [];
96
+ let chunk = [];
97
+
98
+ // eslint-disable-next-line max-params
99
+ tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => {
100
+ if (escapeChar) {
101
+ chunk.push(unescape(escapeChar));
102
+ } else if (style) {
103
+ const str = chunk.join('');
104
+ chunk = [];
105
+ chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str));
106
+ styles.push({inverse, styles: parseStyle(style)});
107
+ } else if (close) {
108
+ if (styles.length === 0) {
109
+ throw new Error('Found extraneous } in Chalk template literal');
110
+ }
111
+
112
+ chunks.push(buildStyle(chalk, styles)(chunk.join('')));
113
+ chunk = [];
114
+ styles.pop();
133
115
  } else {
134
- flat = flat.concat(flatten(content));
116
+ chunk.push(chr);
135
117
  }
136
- }
137
-
138
- return flat;
139
- }
118
+ });
140
119
 
141
- function assertStyle(chalk, style) {
142
- if (!chalk[style]) {
143
- throw new Error(`invalid Chalk style: ${style}`);
144
- }
145
- }
120
+ chunks.push(chunk.join(''));
146
121
 
147
- /**
148
- * Checks if a given style is valid and parses style functions.
149
- * */
150
- function parseStyle(chalk, style) {
151
- const fnMatch = style.match(/^\s*(\w+)\s*\(\s*([^)]*)\s*\)\s*/);
152
- if (!fnMatch) {
153
- assertStyle(chalk, style);
154
- return chalk[style];
122
+ if (styles.length > 0) {
123
+ const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`;
124
+ throw new Error(errMsg);
155
125
  }
156
126
 
157
- const name = fnMatch[1].trim();
158
- const args = fnMatch[2].split(/,/g).map(s => s.trim());
159
-
160
- assertStyle(chalk, name);
161
-
162
- return chalk[name].apply(chalk, args);
163
- }
164
-
165
- /**
166
- * Performs the actual styling of the string, essentially lifted from cli.js.
167
- * */
168
- function style(chalk, flat) {
169
- return flat.map(data => {
170
- const fn = data.styles.reduce(parseStyle, chalk);
171
- return fn(data.content.replace(/\n$/, ''));
172
- }).join('');
173
- }
174
-
175
- module.exports = (chalk, string) => style(chalk, flatten(parse(string)));
127
+ return chunks.join('');
128
+ };