Skip to content

Commit 09ddbad

Browse files
Support template literals for nested calls (#392)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent 55816cd commit 09ddbad

File tree

7 files changed

+61
-2
lines changed

7 files changed

+61
-2
lines changed

benchmark.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,16 @@ suite('chalk', () => {
4747
bench('cached: 1 style nested non-intersecting', () => {
4848
chalkBgRed(blueStyledString);
4949
});
50+
51+
set('iterations', 10000);
52+
53+
bench('cached: 1 style template literal', () => {
54+
// eslint-disable-next-line no-unused-expressions
55+
chalkRed`the fox jumps over the lazy dog`;
56+
});
57+
58+
bench('cached: nested styles template literal', () => {
59+
// eslint-disable-next-line no-unused-expressions
60+
chalkRed`the fox {bold jumps} over the {underline lazy} dog`;
61+
});
5062
});

index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ declare namespace chalk {
137137
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
138138
`);
139139
```
140+
141+
@example
142+
```
143+
import chalk = require('chalk');
144+
145+
log(chalk.red.bgBlack`2 + 3 = {bold ${2 + 3}}`)
146+
```
140147
*/
141148
(text: TemplateStringsArray, ...placeholders: unknown[]): string;
142149

index.test-d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ expectType<string>(chalk.bgWhiteBright`foo`);
152152
expectType<string>(chalk.red.bgGreen.underline('foo'));
153153
expectType<string>(chalk.underline.red.bgGreen('foo'));
154154

155+
// -- Complex template literal --
156+
expectType<string>(chalk.underline``);
157+
expectType<string>(chalk.red.bgGreen.bold`Hello {italic.blue ${name}}`);
158+
expectType<string>(chalk.strikethrough.cyanBright.bgBlack`Works with {reset {bold numbers}} {bold.red ${1}}`);
159+
155160
// -- Color types ==
156161
expectType<typeof chalk.Color>('red');
157162
expectError<typeof chalk.Color>('hotpink');

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,11 @@ console.log(chalk`
215215

216216
Blocks are delimited by an opening curly brace (`{`), a style, some content, and a closing curly brace (`}`).
217217

218-
Template styles are chained exactly like normal Chalk styles. The following two statements are equivalent:
218+
Template styles are chained exactly like normal Chalk styles. The following three statements are equivalent:
219219

220220
```js
221221
console.log(chalk.bold.rgb(10, 100, 200)('Hello!'));
222+
console.log(chalk.bold.rgb(10, 100, 200)`Hello!`);
222223
console.log(chalk`{bold.rgb(10,100,200) Hello!}`);
223224
```
224225

source/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const {
66
stringEncaseCRLFWithFirstIndex
77
} = require('./util');
88

9+
const {isArray} = Array;
10+
911
// `supportsColor.level` → `ansiStyles.color[name]` mapping
1012
const levelMapping = [
1113
'ansi',
@@ -135,6 +137,11 @@ const createStyler = (open, close, parent) => {
135137

136138
const createBuilder = (self, _styler, _isEmpty) => {
137139
const builder = (...arguments_) => {
140+
if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) {
141+
// Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}`
142+
return applyStyle(builder, chalkTag(builder, ...arguments_));
143+
}
144+
138145
// Single argument is hot path, implicit coercion is faster than anything
139146
// eslint-disable-next-line no-implicit-coercion
140147
return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
@@ -189,7 +196,7 @@ let template;
189196
const chalkTag = (chalk, ...strings) => {
190197
const [firstString] = strings;
191198

192-
if (!Array.isArray(firstString)) {
199+
if (!isArray(firstString) || !isArray(firstString.raw)) {
193200
// If chalk() was called by itself or with a string,
194201
// return the string itself as a string.
195202
return strings.join(' ');

test/chalk.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ test('support multiple arguments in base function', t => {
1616
t.is(chalk('hello', 'there'), 'hello there');
1717
});
1818

19+
test('support automatic casting to string', t => {
20+
t.is(chalk(['hello', 'there']), 'hello,there');
21+
t.is(chalk(123), '123');
22+
23+
t.is(chalk.bold(['foo', 'bar']), '\u001B[1mfoo,bar\u001B[22m');
24+
t.is(chalk.green(98765), '\u001B[32m98765\u001B[39m');
25+
});
26+
1927
test('style string', t => {
2028
t.is(chalk.underline('foo'), '\u001B[4mfoo\u001B[24m');
2129
t.is(chalk.red('foo'), '\u001B[31mfoo\u001B[39m');

test/template-literal.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ test('correctly perform template substitutions', t => {
3030
instance.bold('Hello,', instance.cyan.inverse(name + '!'), 'This is a') + ' test. ' + instance.green(exclamation + '!'));
3131
});
3232

33+
test('correctly perform nested template substitutions', t => {
34+
const instance = new chalk.Instance({level: 0});
35+
const name = 'Sindre';
36+
const exclamation = 'Neat';
37+
t.is(instance.bold`Hello, {cyan.inverse ${name}!} This is a` + ' test. ' + instance.green`${exclamation}!`,
38+
instance.bold('Hello,', instance.cyan.inverse(name + '!'), 'This is a') + ' test. ' + instance.green(exclamation + '!'));
39+
40+
t.is(instance.red.bgGreen.bold`Hello {italic.blue ${name}}`,
41+
instance.red.bgGreen.bold('Hello ' + instance.italic.blue(name)));
42+
43+
t.is(instance.strikethrough.cyanBright.bgBlack`Works with {reset {bold numbers}} {bold.red ${1}}`,
44+
instance.strikethrough.cyanBright.bgBlack('Works with ' + instance.reset.bold('numbers') + ' ' + instance.bold.red(1)));
45+
46+
t.is(chalk.bold`Also works on the shared {bgBlue chalk} object`,
47+
'\u001B[1mAlso works on the shared \u001B[1m' +
48+
'\u001B[44mchalk\u001B[49m\u001B[22m' +
49+
'\u001B[1m object\u001B[22m');
50+
});
51+
3352
test('correctly parse and evaluate color-convert functions', t => {
3453
const instance = new chalk.Instance({level: 3});
3554
t.is(instance`{bold.rgb(144,10,178).inverse Hello, {~inverse there!}}`,

0 commit comments

Comments
 (0)