Merge branch 'master' of github.com:chalk/chalk into fix/nested-styling

This commit is contained in:
Ahad Birang 2019-07-12 22:26:08 +04:30
commit 93d8c7f980
25 changed files with 553 additions and 588 deletions

View file

@ -1,6 +0,0 @@
[ignore]
.*/node_modules/.*
[options]
suppress_comment= \\(.\\|\n\\)*\\$ExpectError
include_warnings=true

5
.github/funding.yml vendored Normal file
View file

@ -0,0 +1,5 @@
github: sindresorhus
open_collective: sindresorhus
patreon: sindresorhus
tidelift: npm/chalk
custom: https://sindresorhus.com/donate

3
.github/security.md vendored Normal file
View file

@ -0,0 +1,3 @@
# Security Policy
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.

View file

@ -1,5 +1,6 @@
language: node_js
node_js:
- '12'
- '10'
- '8'
after_success:

View file

@ -3,22 +3,48 @@
const chalk = require('.');
suite('chalk', () => {
set('iterations', 100000);
set('iterations', 1000000);
bench('single style', () => {
const chalkRed = chalk.red;
const chalkBgRed = chalk.bgRed;
const chalkBlueBgRed = chalk.blue.bgRed;
const chalkBlueBgRedBold = chalk.blue.bgRed.bold;
const blueStyledString = 'the fox jumps' + chalk.blue('over the lazy dog') + '!';
bench('1 style', () => {
chalk.red('the fox jumps over the lazy dog');
});
bench('several styles', () => {
bench('2 styles', () => {
chalk.blue.bgRed('the fox jumps over the lazy dog');
});
bench('3 styles', () => {
chalk.blue.bgRed.bold('the fox jumps over the lazy dog');
});
const cached = chalk.blue.bgRed.bold;
bench('cached styles', () => {
cached('the fox jumps over the lazy dog');
bench('cached: 1 style', () => {
chalkRed('the fox jumps over the lazy dog');
});
bench('nested styles', () => {
chalk.red('the fox jumps', chalk.underline.bgBlue('over the lazy dog') + '!');
bench('cached: 2 styles', () => {
chalkBlueBgRed('the fox jumps over the lazy dog');
});
bench('cached: 3 styles', () => {
chalkBlueBgRedBold('the fox jumps over the lazy dog');
});
bench('cached: 1 style with newline', () => {
chalkRed('the fox jumps\nover the lazy dog');
});
bench('cached: 1 style nested intersecting', () => {
chalkRed(blueStyledString);
});
bench('cached: 1 style nested non-intersecting', () => {
chalkBgRed(blueStyledString);
});
});

View file

@ -1,6 +1,6 @@
'use strict';
const chalk = require('..');
const styles = require('ansi-styles');
const chalk = require('..');
// Generates screenshot
for (const key of Object.keys(styles)) {

561
index.d.ts vendored
View file

@ -1,276 +1,325 @@
export const enum Level {
declare const enum LevelEnum {
/**
* All colors disabled.
*/
All colors disabled.
*/
None = 0,
/**
* Basic 16 colors support.
*/
Basic 16 colors support.
*/
Basic = 1,
/**
* ANSI 256 colors support.
*/
ANSI 256 colors support.
*/
Ansi256 = 2,
/**
* Truecolor 16 million colors support.
*/
Truecolor 16 million colors support.
*/
TrueColor = 3
}
export interface Options {
/**
* Enable or disable Chalk.
*
* @default true
*/
enabled?: boolean;
declare namespace chalk {
type Level = LevelEnum;
interface Options {
/**
Enable or disable Chalk.
@default true
*/
enabled?: boolean;
/**
Specify the color support for Chalk.
By default, color support is automatically detected based on the environment.
*/
level?: Level;
}
interface Instance {
/**
Return a new Chalk instance.
*/
new (options?: Options): Chalk;
}
/**
* Specify the color support for Chalk.
* By default, color support is automatically detected based on the environment.
*/
level?: Level;
}
Detect whether the terminal supports color.
*/
interface ColorSupport {
/**
The color level used by Chalk.
*/
level: Level;
export interface Instance {
/**
* Return a new Chalk instance.
*/
new (options?: Options): Chalk;
/**
Return whether Chalk supports basic 16 colors.
*/
hasBasic: boolean;
/**
Return whether Chalk supports ANSI 256 colors.
*/
has256: boolean;
/**
Return whether Chalk supports Truecolor 16 million colors.
*/
has16m: boolean;
}
interface ChalkFunction {
/**
Use a template string.
@remarks Template literals are unsupported for nested calls (see [issue #341](https://github.com/chalk/chalk/issues/341))
@example
```
import chalk = require('chalk');
log(chalk`
CPU: {red ${cpu.totalPercent}%}
RAM: {green ${ram.used / ram.total * 100}%}
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
`);
```
*/
(text: TemplateStringsArray, ...placeholders: unknown[]): string;
(...text: unknown[]): string;
}
interface Chalk extends ChalkFunction {
/**
Return a new Chalk instance.
*/
Instance: Instance;
/**
Enable or disable Chalk.
@default true
*/
enabled: boolean;
/**
The color support for Chalk.
By default, color support is automatically detected based on the environment.
*/
level: Level;
/**
Use HEX value to set text color.
@param color - Hexadecimal value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.hex('#DEADED');
```
*/
hex(color: string): Chalk;
/**
Use keyword color value to set text color.
@param color - Keyword value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.keyword('orange');
```
*/
keyword(color: string): Chalk;
/**
Use RGB values to set text color.
*/
rgb(red: number, green: number, blue: number): Chalk;
/**
Use HSL values to set text color.
*/
hsl(hue: number, saturation: number, lightness: number): Chalk;
/**
Use HSV values to set text color.
*/
hsv(hue: number, saturation: number, value: number): Chalk;
/**
Use HWB values to set text color.
*/
hwb(hue: number, whiteness: number, blackness: number): Chalk;
/**
Use HEX value to set background color.
@param color - Hexadecimal value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.bgHex('#DEADED');
```
*/
bgHex(color: string): Chalk;
/**
Use keyword color value to set background color.
@param color - Keyword value representing the desired color.
@example
```
import chalk = require('chalk');
chalk.bgKeyword('orange');
```
*/
bgKeyword(color: string): Chalk;
/**
Use RGB values to set background color.
*/
bgRgb(red: number, green: number, blue: number): Chalk;
/**
Use HSL values to set background color.
*/
bgHsl(hue: number, saturation: number, lightness: number): Chalk;
/**
Use HSV values to set background color.
*/
bgHsv(hue: number, saturation: number, value: number): Chalk;
/**
Use HWB values to set background color.
*/
bgHwb(hue: number, whiteness: number, blackness: number): Chalk;
/**
Modifier: Resets the current color chain.
*/
readonly reset: Chalk;
/**
Modifier: Make text bold.
*/
readonly bold: Chalk;
/**
Modifier: Emitting only a small amount of light.
*/
readonly dim: Chalk;
/**
Modifier: Make text italic. (Not widely supported)
*/
readonly italic: Chalk;
/**
Modifier: Make text underline. (Not widely supported)
*/
readonly underline: Chalk;
/**
Modifier: Inverse background and foreground colors.
*/
readonly inverse: Chalk;
/**
Modifier: Prints the text, but makes it invisible.
*/
readonly hidden: Chalk;
/**
Modifier: Puts a horizontal line through the center of the text. (Not widely supported)
*/
readonly strikethrough: Chalk;
/**
Modifier: Prints the text only when Chalk is enabled.
Can be useful for things that are purely cosmetic.
*/
readonly visible: Chalk;
readonly black: Chalk;
readonly red: Chalk;
readonly green: Chalk;
readonly yellow: Chalk;
readonly blue: Chalk;
readonly magenta: Chalk;
readonly cyan: Chalk;
readonly white: Chalk;
/*
Alias for `blackBright`.
*/
readonly gray: Chalk;
/*
Alias for `blackBright`.
*/
readonly grey: Chalk;
readonly blackBright: Chalk;
readonly redBright: Chalk;
readonly greenBright: Chalk;
readonly yellowBright: Chalk;
readonly blueBright: Chalk;
readonly magentaBright: Chalk;
readonly cyanBright: Chalk;
readonly whiteBright: Chalk;
readonly bgBlack: Chalk;
readonly bgRed: Chalk;
readonly bgGreen: Chalk;
readonly bgYellow: Chalk;
readonly bgBlue: Chalk;
readonly bgMagenta: Chalk;
readonly bgCyan: Chalk;
readonly bgWhite: Chalk;
/*
Alias for `bgBlackBright`.
*/
readonly bgGray: Chalk;
/*
Alias for `bgBlackBright`.
*/
readonly bgGrey: Chalk;
readonly bgBlackBright: Chalk;
readonly bgRedBright: Chalk;
readonly bgGreenBright: Chalk;
readonly bgYellowBright: Chalk;
readonly bgBlueBright: Chalk;
readonly bgMagentaBright: Chalk;
readonly bgCyanBright: Chalk;
readonly bgWhiteBright: Chalk;
}
}
/**
* Detect whether the terminal supports color.
*/
export interface ColorSupport {
/**
* The color level used by Chalk.
*/
level: Level;
Main Chalk object that allows to chain styles together.
Call the last one as a method with a string argument.
Order doesn't matter, and later styles take precedent in case of a conflict.
This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`.
*/
declare const chalk: chalk.Chalk & chalk.ChalkFunction & {
supportsColor: chalk.ColorSupport | false;
Level: typeof LevelEnum;
};
/**
* Return whether Chalk supports basic 16 colors.
*/
hasBasic: boolean;
/**
* Return whether Chalk supports ANSI 256 colors.
*/
has256: boolean;
/**
* Return whether Chalk supports Truecolor 16 million colors.
*/
has16m: boolean;
}
export interface Chalk {
(...text: unknown[]): string;
(text: TemplateStringsArray, ...placeholders: unknown[]): string;
/**
* Return a new Chalk instance.
*/
Instance: Instance;
/**
* Enable or disable Chalk.
*
* @default true
*/
enabled: boolean;
/**
* The color support for Chalk.
* By default, color support is automatically detected based on the environment.
*/
level: Level;
/**
* Use HEX value to set text color.
*
* @param color - Hexadecimal value representing the desired color.
*
* @example
*
* import chalk from 'chalk';
*
* chalk.hex('#DEADED');
*/
hex(color: string): this;
/**
* Use keyword color value to set text color.
*
* @param color - Keyword value representing the desired color.
*
* @example
*
* import chalk from 'chalk';
*
* chalk.keyword('orange');
*/
keyword(color: string): this;
/**
* Use RGB values to set text color.
*/
rgb(red: number, green: number, blue: number): this;
/**
* Use HSL values to set text color.
*/
hsl(hue: number, saturation: number, lightness: number): this;
/**
* Use HSV values to set text color.
*/
hsv(hue: number, saturation: number, value: number): this;
/**
* Use HWB values to set text color.
*/
hwb(hue: number, whiteness: number, blackness: number): this;
/**
* Use HEX value to set background color.
*
* @param color - Hexadecimal value representing the desired color.
*
* @example
*
* import chalk from 'chalk';
*
* chalk.bgHex('#DEADED');
*/
bgHex(color: string): this;
/**
* Use keyword color value to set background color.
*
* @param color - Keyword value representing the desired color.
*
* @example
*
* import chalk from 'chalk';
*
* chalk.bgKeyword('orange');
*/
bgKeyword(color: string): this;
/**
* Use RGB values to set background color.
*/
bgRgb(red: number, green: number, blue: number): this;
/**
* Use HSL values to set background color.
*/
bgHsl(hue: number, saturation: number, lightness: number): this;
/**
* Use HSV values to set background color.
*/
bgHsv(hue: number, saturation: number, value: number): this;
/**
* Use HWB values to set background color.
*/
bgHwb(hue: number, whiteness: number, blackness: number): this;
/**
* Modifier: Resets the current color chain.
*/
readonly reset: this;
/**
* Modifier: Make text bold.
*/
readonly bold: this;
/**
* Modifier: Emitting only a small amount of light.
*/
readonly dim: this;
/**
* Modifier: Make text italic. (Not widely supported)
*/
readonly italic: this;
/**
* Modifier: Make text underline. (Not widely supported)
*/
readonly underline: this;
/**
* Modifier: Inverse background and foreground colors.
*/
readonly inverse: this;
/**
* Modifier: Prints the text, but makes it invisible.
*/
readonly hidden: this;
/**
* Modifier: Puts a horizontal line through the center of the text. (Not widely supported)
*/
readonly strikethrough: this;
/**
* Modifier: Prints the text only when Chalk is enabled.
* Can be useful for things that are purely cosmetic.
*/
readonly visible: this;
readonly black: this;
readonly red: this;
readonly green: this;
readonly yellow: this;
readonly blue: this;
readonly magenta: this;
readonly cyan: this;
readonly white: this;
readonly gray: this;
readonly grey: this;
readonly blackBright: this;
readonly redBright: this;
readonly greenBright: this;
readonly yellowBright: this;
readonly blueBright: this;
readonly magentaBright: this;
readonly cyanBright: this;
readonly whiteBright: this;
readonly bgBlack: this;
readonly bgRed: this;
readonly bgGreen: this;
readonly bgYellow: this;
readonly bgBlue: this;
readonly bgMagenta: this;
readonly bgCyan: this;
readonly bgWhite: this;
readonly bgBlackBright: this;
readonly bgRedBright: this;
readonly bgGreenBright: this;
readonly bgYellowBright: this;
readonly bgBlueBright: this;
readonly bgMagentaBright: this;
readonly bgCyanBright: this;
readonly bgWhiteBright: this;
}
/**
* Main Chalk object that allows to chain styles together.
* Call the last one as a method with a string argument.
* Order doesn't matter, and later styles take precedent in case of a conflict.
* This simply means that `chalk.red.yellow.green` is equivalent to `chalk.green`.
*/
declare const chalk: Chalk & {supportsColor: ColorSupport};
export default chalk;
export = chalk;

View file

@ -1,93 +0,0 @@
// @flow strict
type TemplateStringsArray = $ReadOnlyArray<string>;
export type Level = $Values<{
None: 0,
Basic: 1,
Ansi256: 2,
TrueColor: 3
}>;
export type Options = {|
enabled?: boolean,
level?: Level
|};
export type ColorSupport = {|
level: Level,
hasBasic: boolean,
has256: boolean,
has16m: boolean
|};
export interface Chalk {
(...text: string[]): string,
(text: TemplateStringsArray, ...placeholders: mixed[]): string,
Instance(options?: Options): Chalk,
enabled: boolean,
level: Level,
rgb(red: number, green: number, blue: number): Chalk,
hsl(hue: number, saturation: number, lightness: number): Chalk,
hsv(hue: number, saturation: number, value: number): Chalk,
hwb(hue: number, whiteness: number, blackness: number): Chalk,
bgHex(color: string): Chalk,
bgKeyword(color: string): Chalk,
bgRgb(red: number, green: number, blue: number): Chalk,
bgHsl(hue: number, saturation: number, lightness: number): Chalk,
bgHsv(hue: number, saturation: number, value: number): Chalk,
bgHwb(hue: number, whiteness: number, blackness: number): Chalk,
hex(color: string): Chalk,
keyword(color: string): Chalk,
+reset: Chalk,
+bold: Chalk,
+dim: Chalk,
+italic: Chalk,
+underline: Chalk,
+inverse: Chalk,
+hidden: Chalk,
+strikethrough: Chalk,
+visible: Chalk,
+black: Chalk,
+red: Chalk,
+green: Chalk,
+yellow: Chalk,
+blue: Chalk,
+magenta: Chalk,
+cyan: Chalk,
+white: Chalk,
+gray: Chalk,
+grey: Chalk,
+blackBright: Chalk,
+redBright: Chalk,
+greenBright: Chalk,
+yellowBright: Chalk,
+blueBright: Chalk,
+magentaBright: Chalk,
+cyanBright: Chalk,
+whiteBright: Chalk,
+bgBlack: Chalk,
+bgRed: Chalk,
+bgGreen: Chalk,
+bgYellow: Chalk,
+bgBlue: Chalk,
+bgMagenta: Chalk,
+bgCyan: Chalk,
+bgWhite: Chalk,
+bgBlackBright: Chalk,
+bgRedBright: Chalk,
+bgGreenBright: Chalk,
+bgYellowBright: Chalk,
+bgBlueBright: Chalk,
+bgMagentaBright: Chalk,
+bgCyanBright: Chalk,
+bgWhiteBright: Chalk,
supportsColor: ColorSupport
};
declare module.exports: Chalk;

View file

@ -1,27 +1,31 @@
import {expectType} from 'tsd-check';
import chalk, {Level, Chalk, ColorSupport} from '.';
import {expectType, expectError} from 'tsd';
import chalk = require('.');
// - Helpers -
type colorReturn = Chalk & {supportsColor: ColorSupport};
type colorReturn = chalk.Chalk & {supportsColor?: never};
// - Level -
expectType<number>(Level.None);
expectType<number>(Level.Basic);
expectType<number>(Level.Ansi256);
expectType<number>(Level.TrueColor);
expectType<number>(chalk.Level.None);
expectType<number>(chalk.Level.Basic);
expectType<number>(chalk.Level.Ansi256);
expectType<number>(chalk.Level.TrueColor);
// - supportsColor -
expectType<boolean>(chalk.supportsColor.hasBasic);
expectType<boolean>(chalk.supportsColor.has256);
expectType<boolean>(chalk.supportsColor.has16m);
expectType<chalk.ColorSupport | false>(chalk.supportsColor);
expectType<boolean>((chalk.supportsColor as chalk.ColorSupport).hasBasic);
expectType<boolean>((chalk.supportsColor as chalk.ColorSupport).has256);
expectType<boolean>((chalk.supportsColor as chalk.ColorSupport).has16m);
// -- `supportsColor` is not a member of the Chalk interface --
expectError(chalk.reset.supportsColor);
// - Chalk -
// -- Instance --
expectType<Chalk>(new chalk.Instance({level: 1}));
expectType<chalk.Chalk>(new chalk.Instance({level: 1}));
// -- Properties --
expectType<boolean>(chalk.enabled);
expectType<Level>(chalk.level);
expectType<chalk.Level>(chalk.level);
// -- Template literal --
expectType<string>(chalk``);

View file

@ -4,18 +4,17 @@
"description": "Terminal string styling done right",
"license": "MIT",
"repository": "chalk/chalk",
"main": "source",
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && nyc ava && tsd-check && flow",
"test": "xo && nyc ava && tsd",
"bench": "matcha benchmark.js"
},
"files": [
"index.js",
"templates.js",
"index.d.ts",
"index.js.flow"
"source",
"index.d.ts"
],
"keywords": [
"color",
@ -41,27 +40,18 @@
"text"
],
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^6.1.0"
"ansi-styles": "^4.0.0",
"supports-color": "^7.0.0"
},
"devDependencies": {
"@sindresorhus/tsconfig": "^0.2.1",
"ava": "^1.3.1",
"coveralls": "^3.0.3",
"execa": "^1.0.0",
"flow-bin": "^0.94.0",
"import-fresh": "^3.0.0",
"ava": "^2.2.0",
"coveralls": "^3.0.5",
"execa": "^2.0.3",
"import-fresh": "^3.1.0",
"matcha": "^0.7.0",
"nyc": "^13.3.0",
"resolve-from": "^4.0.0",
"tsd-check": "^0.3.0",
"nyc": "^14.1.1",
"resolve-from": "^5.0.0",
"tsd": "^0.7.4",
"xo": "^0.24.0"
},
"types": "index.d.ts",
"xo": {
"ignores": [
"test/_flow.js"
]
}
}

View file

@ -9,23 +9,11 @@
> Terminal string styling done right
[![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) [![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents) [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk) [![](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/xojs/xo) ![TypeScript- and Flow-ready](https://img.shields.io/npm/types/chalk.svg)
[![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) [![npm dependents](https://badgen.net/npm/dependents/chalk)](https://www.npmjs.com/package/chalk?activeTab=dependents) [![Downloads](https://badgen.net/npm/dt/chalk)](https://www.npmjs.com/package/chalk) [![](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/xojs/xo) ![TypeScript-ready](https://img.shields.io/npm/types/chalk.svg)
<img src="https://cdn.jsdelivr.net/gh/chalk/ansi-styles@8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900">
---
<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-chalk?utm_source=npm-chalk&utm_medium=referral&utm_campaign=readme">Get professional support for Chalk with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>
---
**This readme reflects the next major version that is currently in development. You probably want [the v2 readme](https://www.npmjs.com/package/chalk).**
## Highlights
@ -47,10 +35,6 @@
$ npm install chalk
```
<a href="https://www.patreon.com/sindresorhus">
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
</a>
## Usage
@ -169,7 +153,7 @@ Levels are as follows:
Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience.
Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add the environment variable `FORCE_COLOR=1` to forcefully enable color or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks.
Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks.
Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively.
@ -194,11 +178,11 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=
- `red`
- `green`
- `yellow`
- `blue` *(On Windows the bright version is used since normal blue is illegible)*
- `blue`
- `magenta`
- `cyan`
- `white`
- `gray` ("bright black")
- `blackBright` (alias: `gray`, `grey`)
- `redBright`
- `greenBright`
- `yellowBright`
@ -217,7 +201,7 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=
- `bgMagenta`
- `bgCyan`
- `bgWhite`
- `bgBlackBright`
- `bgBlackBright` (alias: `bgGray`, `bgGrey`)
- `bgRedBright`
- `bgGreenBright`
- `bgYellowBright`
@ -238,8 +222,8 @@ const miles = 18;
const calculateFeet = miles => miles * 5280;
console.log(chalk`
There are {bold 5280 feet} in a mile.
In {bold ${miles} miles}, there are {green.bold ${calculateFeet(miles)} feet}.
There are {bold 5280 feet} in a mile.
In {bold ${miles} miles}, there are {green.bold ${calculateFeet(miles)} feet}.
`);
```
@ -297,11 +281,6 @@ If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) i
[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.
## Security
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
## Related
- [chalk-cli](https://github.com/chalk/chalk-cli) - CLI for this module
@ -326,6 +305,14 @@ To report a security vulnerability, please use the [Tidelift security contact](h
- [Josh Junon](https://github.com/qix-)
## License
---
MIT
<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-chalk?utm_source=npm-chalk&utm_medium=referral&utm_campaign=readme">Get professional support for Chalk with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>

View file

@ -1,8 +1,11 @@
'use strict';
const escapeStringRegexp = require('escape-string-regexp');
const ansiStyles = require('ansi-styles');
const {stdout: stdoutColor} = require('supports-color');
const template = require('./templates.js');
const template = require('./templates');
const {
stringReplaceAll,
stringEncaseCRLFWithFirstIndex
} = require('./util');
// `supportsColor.level` → `ansiStyles.color[name]` mapping
const levelMapping = [
@ -57,22 +60,23 @@ function Chalk(options) {
}
for (const [styleName, style] of Object.entries(ansiStyles)) {
style.closeRe = new RegExp(escapeStringRegexp(style.close), 'g');
styles[styleName] = {
get() {
return createBuilder(this, [...(this._styles || []), style], this._isEmpty);
const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty);
Object.defineProperty(this, styleName, {value: builder});
return builder;
}
};
}
styles.visible = {
get() {
return createBuilder(this, this._styles || [], true);
const builder = createBuilder(this, this._styler, true);
Object.defineProperty(this, 'visible', {value: builder});
return builder;
}
};
ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g');
for (const model of Object.keys(ansiStyles.color.ansi)) {
if (skipModels.has(model)) {
continue;
@ -82,19 +86,13 @@ for (const model of Object.keys(ansiStyles.color.ansi)) {
get() {
const {level} = this;
return function (...arguments_) {
const open = ansiStyles.color[levelMapping[level]][model](...arguments_);
const codes = {
open,
close: ansiStyles.color.close,
closeRe: ansiStyles.color.closeRe
};
return createBuilder(this, [...(this._styles || []), codes], this._isEmpty);
const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g');
for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
if (skipModels.has(model)) {
continue;
@ -105,72 +103,105 @@ for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
get() {
const {level} = this;
return function (...arguments_) {
const open = ansiStyles.bgColor[levelMapping[level]][model](...arguments_);
const codes = {
open,
close: ansiStyles.bgColor.close,
closeRe: ansiStyles.bgColor.closeRe
};
return createBuilder(this, [...(this._styles || []), codes], this._isEmpty);
const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
};
}
const proto = Object.defineProperties(() => {}, styles);
const createBuilder = (self, _styles, _isEmpty) => {
const builder = (...arguments_) => applyStyle(builder, ...arguments_);
builder._styles = _styles;
builder._isEmpty = _isEmpty;
Object.defineProperty(builder, 'level', {
const proto = Object.defineProperties(() => {}, {
...styles,
level: {
enumerable: true,
get() {
return self.level;
return this._generator.level;
},
set(level) {
self.level = level;
this._generator.level = level;
}
});
Object.defineProperty(builder, 'enabled', {
},
enabled: {
enumerable: true,
get() {
return self.enabled;
return this._generator.enabled;
},
set(enabled) {
self.enabled = enabled;
this._generator.enabled = enabled;
}
});
}
});
const createStyler = (open, close, parent) => {
let openAll;
let closeAll;
if (parent === undefined) {
openAll = open;
closeAll = close;
} else {
openAll = parent.openAll + open;
closeAll = close + parent.closeAll;
}
return {
open,
close,
openAll,
closeAll,
parent
};
};
const createBuilder = (self, _styler, _isEmpty) => {
const builder = (...arguments_) => {
// Single argument is hot path, implicit coercion is faster than anything
// eslint-disable-next-line no-implicit-coercion
return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' '));
};
// `__proto__` is used because we must return a function, but there is
// no way to create a function with a different prototype
builder.__proto__ = proto; // eslint-disable-line no-proto
builder._generator = self;
builder._styler = _styler;
builder._isEmpty = _isEmpty;
return builder;
};
const applyStyle = (self, ...arguments_) => {
let string = arguments_.join(' ');
const applyStyle = (self, string) => {
if (!self.enabled || self.level <= 0 || !string) {
return self._isEmpty ? '' : string;
}
for (const code of self._styles.slice().reverse()) {
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
string = code.open + string.replace(code.closeRe, code.close + code.open) + code.close;
let styler = self._styler;
// Close the styling before a linebreak and reopen
// after next line to fix a bleed issue on macOS
// https://github.com/chalk/chalk/pull/92
string = string.replace(/\r?\n/g, `${code.close}$&${code.open}`);
if (styler === undefined) {
return string;
}
return string;
const {openAll, closeAll} = styler;
if (string.indexOf('\u001B') !== -1) {
while (styler !== undefined) {
// Replace any instances already present with a re-opening code
// otherwise only the part of the string until said closing code
// will be colored, and the rest will simply be 'plain'.
string = stringReplaceAll(string, styler.close, styler.open);
styler = styler.parent;
}
}
// We can move both next actions out of loop, because remaining actions in loop won't have
// any/visible effect on parts we add here. Close the styling before a linebreak and reopen
// after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92
const lfIndex = string.indexOf('\n');
if (lfIndex !== -1) {
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
}
return openAll + string + closeAll;
};
const chalkTag = (chalk, ...strings) => {
@ -199,4 +230,3 @@ Object.defineProperties(Chalk.prototype, styles);
module.exports = Chalk(); // eslint-disable-line new-cap
module.exports.supportsColor = stdoutColor;
module.exports.default = module.exports; // For TypeScript

View file

@ -1,8 +1,8 @@
'use strict';
const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi;
const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.)|([^\\])/gi;
const ESCAPES = new Map([
['n', '\n'],
@ -18,10 +18,17 @@ const ESCAPES = new Map([
]);
function unescape(c) {
if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
const u = c[0] === 'u';
const bracket = c[1] === '{';
if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) {
return String.fromCharCode(parseInt(c.slice(1), 16));
}
if (u && bracket) {
return String.fromCodePoint(parseInt(c.slice(2, -1), 16));
}
return ESCAPES.get(c) || c;
}

39
source/util.js Normal file
View file

@ -0,0 +1,39 @@
'use strict';
const stringReplaceAll = (string, substring, replacer) => {
let index = string.indexOf(substring);
if (index === -1) {
return string;
}
const substringLength = substring.length;
let endIndex = 0;
let returnValue = '';
do {
returnValue += string.substr(endIndex, index - endIndex) + substring + replacer;
endIndex = index + substringLength;
index = string.indexOf(substring, endIndex);
} while (index !== -1);
returnValue += string.substr(endIndex);
return returnValue;
};
const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => {
let endIndex = 0;
let returnValue = '';
do {
const gotCR = string[index - 1] === '\r';
returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix;
endIndex = index + 1;
index = string.indexOf('\n', endIndex);
} while (index !== -1);
returnValue += string.substr(endIndex);
return returnValue;
};
module.exports = {
stringReplaceAll,
stringEncaseCRLFWithFirstIndex
};

View file

@ -1,4 +1,4 @@
'use strict';
const chalk = require('..');
const chalk = require('../source');
console.log(chalk.hex('#ff6159')('test'));

View file

@ -1,86 +0,0 @@
// @flow
import chalk from '..';
// $ExpectError (Can't have typo in option name)
new chalk.Instance({levl: 1});
new chalk.Instance({level: 1});
// $ExpectError (Option must have proper type)
new chalk.Instance({enabled: 'true'});
new chalk.Instance({enabled: true});
// $ExpectError (Can't have typo in chalk method)
chalk.rd('foo');
chalk.red('foo');
// $ExpectError (Can't have typo in chalk method)
chalk.gren`foo`;
chalk.green`foo`;
// $ExpectError (Can't have typo in chalk method)
chalk.red.bgBlu.underline('foo');
chalk.red.bgBlue.underline('foo');
// $ExpectError (Level must be 0, 1, 2, or 3)
const badCtx = chalk.Instance({level: 4});
const ctx = chalk.Instance({level: 3});
// $ExpectError (Can't have typo in method name)
ctx.gry('foo');
ctx.grey('foo');
// $ExpectError (Can't have typo in method name)
ctx`foo`.value();
ctx`foo`.valueOf();
// $ExpectError (Can't have typo in property name)
chalk.abled = true;
chalk.enabled = true;
// $ExpectError (Can't use invalid Level for property setter)
chalk.level = 10;
chalk.level = 1;
const chalkInstance = new chalk.Instance();
// $ExpectError (Can't have typo in method name)
chalkInstance.blu('foo');
chalkInstance.blue('foo');
chalkInstance`foo`;
// $ExpectError (Can't have typo in method name)
chalk.keywrd('orange').bgBlue('foo');
chalk.keyword('orange').bgBlue('foo');
// $ExpectError (rgb should take in 3 numbers)
chalk.rgb(1, 14).bgBlue('foo');
chalk.rgb(1, 14, 9).bgBlue('foo');
// $ExpectError (hsl should take in 3 numbers)
chalk.hsl(1, 14, '9').bgBlue('foo');
chalk.hsl(1, 14, 9).bgBlue('foo');
// $ExpectError (hsv should take in 3 numbers)
chalk.hsv(1, 14).bgBlue('foo');
chalk.hsv(1, 14, 9).bgBlue('foo');
// $ExpectError (hwb should take in 3 numbers)
chalk.hwb(1, 14).bgBlue('foo');
chalk.hwb(1, 14, 9).bgBlue('foo');
// $ExpectError (Can't have typo in method name)
chalk.visibl('foo');
chalk.visible('foo');
// $ExpectError (Can't have typo in method name)
chalk.red.visibl('foo');
chalk.red.visible('foo');
chalk.visible.red('foo');
// $ExpectError (Can't write to readonly property)
chalk.black = 'foo';
chalk.black;
// $ExpectError (Can't write to readonly property)
chalk.reset = 'foo';
console.log(chalk.reset);

View file

@ -3,7 +3,7 @@ import test from 'ava';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
console.log('TERM:', process.env.TERM || '[none]');
console.log('platform:', process.platform || '[unknown]');
@ -82,6 +82,10 @@ test('line breaks should open and close colors', t => {
t.is(chalk.grey('hello\nworld'), '\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m');
});
test('line breaks should open and close colors with CRLF', t => {
t.is(chalk.grey('hello\r\nworld'), '\u001B[90mhello\u001B[39m\r\n\u001B[90mworld\u001B[39m');
});
test('properly convert RGB to 16 colors on basic color terminals', t => {
t.is(new chalk.Instance({level: 1}).hex('#FF0000')('hello'), '\u001B[91mhello\u001B[39m');
t.is(new chalk.Instance({level: 1}).bgHex('#FF0000')('hello'), '\u001B[101mhello\u001B[49m');
@ -97,3 +101,7 @@ test('don\'t emit RGB codes if level is 0', t => {
t.is(new chalk.Instance({level: 0}).hex('#FF0000')('hello'), 'hello');
t.is(new chalk.Instance({level: 0}).bgHex('#FF0000')('hello'), 'hello');
});
test('supports blackBright color', t => {
t.is(chalk.blackBright('foo'), '\u001B[90mfoo\u001B[39m');
});

View file

@ -1,6 +1,6 @@
import test from 'ava';
const chalk = require('..');
const chalk = require('../source');
test('Chalk.constructor should throw an expected error', t => {
const expectedError = t.throws(() => {

View file

@ -3,7 +3,7 @@ import test from 'ava';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
test('don\'t output colors when manually disabled', t => {
chalk.enabled = false;

View file

@ -3,7 +3,7 @@ import test from 'ava';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
test('create an isolated context where colors can be disabled (by level)', t => {
const instance = new chalk.Instance({level: 0, enabled: true});

View file

@ -5,7 +5,7 @@ import execa from 'execa';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
test('don\'t output colors when manually disabled', t => {
const oldLevel = chalk.level;
@ -40,5 +40,6 @@ test('propagate enable/disable changes from child colors', t => {
});
test('disable colors if they are not supported', async t => {
t.is(await execa.stdout('node', [path.join(__dirname, '_fixture')]), 'test');
const {stdout} = await execa.node(path.join(__dirname, '_fixture'));
t.is(stdout, 'test');
});

View file

@ -8,7 +8,7 @@ require('./_supports-color')(__dirname, {
has16m: false
});
const chalk = require('..');
const chalk = require('../source');
test.failing('colors can be forced by using chalk.enabled', t => {
chalk.enabled = true;

View file

@ -4,7 +4,7 @@ import test from 'ava';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
test('return an empty string for an empty literal', t => {
const instance = new chalk.Instance();
@ -168,3 +168,10 @@ test('should properly handle undefined template interpolated values', t => {
t.is(instance`hello ${undefined}`, 'hello undefined');
t.is(instance`hello ${null}`, 'hello null');
});
test('should allow bracketed Unicode escapes', t => {
const instance = new chalk.Instance({level: 3});
t.is(instance`\u{AB}`, '\u{AB}');
t.is(instance`This is a {bold \u{AB681}} test`, 'This is a \u001B[1m\u{AB681}\u001B[22m test');
t.is(instance`This is a {bold \u{10FFFF}} test`, 'This is a \u001B[1m\u{10FFFF}\u001B[22m test');
});

View file

@ -3,7 +3,7 @@ import test from 'ava';
// Spoof supports-color
require('./_supports-color')(__dirname);
const chalk = require('..');
const chalk = require('../source');
test('visible: normal output when enabled', t => {
const instance = new chalk.Instance({level: 3, enabled: true});

View file

@ -1,7 +0,0 @@
{
"extends": "@sindresorhus/tsconfig",
"compilerOptions": {
"noEmit": true,
"allowJs": true
}
}