diff --git a/CHANGELOG.md b/CHANGELOG.md
index f477aa6..c519f0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,20 @@
# Change log
+## 1.2.0 (2021-12-27)
+- added supports the environment variables `NO_COLOR` `FORCE_COLOR` and flags `--no-color` `--color`
+- added aliases `ansi` for `ansi256` and `bgAnsi` for `bgAnsi256`
+- added to readme the compare of most popular ANSI libraries
+
## 1.1.1 (2021-12-27)
-- add the class Ansis to create more independent instances to increase the performance by benchmark
+- added the class Ansis to create more independent instances to increase the performance by benchmark
- improve performance
- code refactoring
- update readme
## 1.1.0 (2021-12-25)
-- add supports the use of `open` and `close` properties for each style
-- fix codes for methods ansi256() and bgAnsi256()
-- add demo to npm package
+- added supports the use of `open` and `close` properties for each style
+- added demo to npm package
+- fixed codes for methods ansi256() and bgAnsi256()
- update package.json
- update readme
diff --git a/README.md b/README.md
index b666044..9f652d4 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,10 @@
---
-[![npm version](https://badge.fury.io/js/ansis.svg)](https://www.npmjs.com/package/ansis)
+[![npm](https://img.shields.io/npm/v/ansis?logo=npm&color=brightgreen "npm package")](https://www.npmjs.com/package/ansis "download npm package")
[![codecov](https://codecov.io/gh/webdiscus/ansis/branch/master/graph/badge.svg?token=H7SFJONX1X)](https://codecov.io/gh/webdiscus/ansis)
[![node](https://img.shields.io/npm/dm/ansis)](https://www.npmjs.com/package/ansis)
-
Color styling of text for ANSI terminals using the SGR (Select Graphic Rendition) codes defined in the [ECMA-48](https://www.ecma-international.org/publications-and-standards/standards/ecma-48/) standard.\
This is improved and faster implementation for `Node.js`.
@@ -44,6 +43,7 @@ Output:
- powerful and lightweight library is faster than many others such as `chalk` `kleur` `ansi-colors` etc.
- supports the standard de facto API of the `chalk`
- supports 256 color and Truecolor
+ - supports the environment variables [`NO_COLOR`](https://no-color.org) `FORCE_COLOR` and flags `--no-color` `--color`
- supports styles like: `bold` `red` `yellowBright` `bgGreen` `bgCyanBright` ect.
- supports chained styles, e.g.:
```js
@@ -151,6 +151,11 @@ ansis.ansi256(96).bold('bold Bright Cyan');
// background color
ansis.bgAnsi256(105)('Bright Magenta');
```
+> Aliases
+>
+> `ansis.ansi()` is alias for `ansis.ansi256()`\
+> `ansis.bgAnsi()` is alias for `ansis.bgAnsi256()`
+
## Truecolor
@@ -166,6 +171,31 @@ ansis.bgHex('#96C')('Amethyst');
ansis.bgRgb(224, 17, 95)('Ruby');
```
+## Compare most popular ANSI libraries
+
+| Library | Standard
style / color
naming | Chain
styles | Nested
styles | New
Line | ANSI 256
colors | Truecolor
RGB / HEX | NO_COLOR |
+|------------------------------|-------------------|-----------------|------------------|-------------|---------------------------------|------------------------|:---------------------------------------------------------|
+| [`colors.js`][colors.js] | no, e.g.
`brightRed` | yes | yes | yes | - | - | only
`FORCE_COLOR`
`--no-color`
`--color` |
+| [`colorette`][colorette] | yes
(16 colors) | - | yes | - | - | - | yes |
+| [`picocolors`][picocolors] | yes
(8 colors) | - | yes | - | - | - | yes |
+| [`cli-color`][cli-color] | yes
(16 colors) | yes | yes | - | `.xterm(num)` | - | yes |
+| [`color-cli`][color-cli] | no, e.g.
`red_bbt` | yes | buggy | yes | `.x` | - | only
`--no-color`
`--color` |
+| [`ansi-colors`][ansi-colors] | yes
(16 colors) | yes | yes | yes | - | - | only
`FORCE_COLOR` |
+| [`kleur`][kleur] | yes
(8 colors) | yes* | yes | - | - | - | yes |
+| [`chalk`][chalk] | yes
(16 colors) | yes | yes | yes | `.ansi256(num)` | `.hex()` `.rgb()` | yes |
+| **+ ansis** | yes
(16 colors) | yes | yes | yes | `.ansi256(num)`
`.ansi(num)` | `.hex()` `.rgb()` | yes |
+
+### Column description
+- **Standard style and color naming**: `red` `redBright` `bgRed` `bgRedBright` etc., see above the **Foreground / Background colors**.
+- **Chain styles**: `ansis.red.bold.underline('text')`.\
+ `kleur` use the chain of functions: `kleur.red().bold().underline('text')`.
+- **Nested styles**:
+ ```js
+ c.red(`red ${c.green(`green ${c.underline(`underline`)} green`)} red`)
+- **New Line**: correct break of escape sequences at `end of line`\
+
+- **NO_COLOR**: supports the environment variables [`NO_COLOR`](https://no-color.org) `FORCE_COLOR` and flags `--no-color` `--color`
+
## Benchmark
### Initialize
@@ -179,7 +209,6 @@ npm i
npm run bench
```
-
> ### Tested on
>
> MacBook Pro 16" M1 Max 64GB\
@@ -194,16 +223,16 @@ c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`);
```
```diff
- colors-js 1,152,114 ops/sec
- colorette 4,548,418 ops/sec
- picocolors 3,832,593 ops/sec
- cli-color 471,929 ops/sec
- color-cli 110,282 ops/sec
- ansi-colors 1,272,164 ops/sec
- kleur/colors 2,278,569 ops/sec
- kleur 2,223,929 ops/sec
- chalk 2,255,589 ops/sec
-+ ansis 2,674,316 ops/sec
+ colors-js 1,158,572 ops/sec
+ colorette 4,572,582 ops/sec
+ picocolors 3,841,124 ops/sec
+ cli-color 470,320 ops/sec
+ color-cli 109,811 ops/sec
+ ansi-colors 1,265,615 ops/sec
+ kleur/colors 2,281,415 ops/sec
+ kleur 2,228,639 ops/sec
+ chalk 2,287,146 ops/sec
++ ansis 2,669,734 ops/sec
```
### Base styles
@@ -211,16 +240,16 @@ c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`);
styles.forEach((style) => c[style]('foo'));
```
```diff
- colors-js 475,774 ops/sec
- colorette 1,174,392 ops/sec
- picocolors 5,724,714 ops/sec
- cli-color 220,577 ops/sec
- color-cli 73,535 ops/sec
- ansi-colors 727,414 ops/sec
- kleur/colors 1,275,337 ops/sec
- kleur 3,843,212 ops/sec
- chalk 3,144,045 ops/sec
-+ ansis 4,360,629 ops/sec
+ colors-js 471,395 ops/sec
+ colorette 1,103,314 ops/sec
+ picocolors 5,725,578 ops/sec
+ cli-color 221,282 ops/sec
+ color-cli 73,725 ops/sec
+ ansi-colors 716,280 ops/sec
+ kleur/colors 1,259,858 ops/sec
+ kleur 3,829,838 ops/sec
+ chalk 3,165,933 ops/sec
++ ansis 4,483,217 ops/sec
```
### Chained styles
@@ -246,16 +275,16 @@ colors.forEach((color) => c[color].bold.underline.italic('foo'));
colors.forEach((color) => c[color](c.bold(c.underline(c.italic('foo')))));
```
```diff
- colors-js 165,202 ops/sec
- colorette 712,604 ops/sec
- picocolors 939,536 ops/sec
- cli-color 64,758 ops/sec
- color-cli 13,833 ops/sec
- ansi-colors 258,930 ops/sec
- kleur/colors 563,266 ops/sec
- kleur 646,985 ops/sec
- chalk 385,590 ops/sec
-+ ansis 554,813 ops/sec
+ colors-js 166,425 ops/sec
+ colorette 695,350 ops/sec
+ picocolors 942,592 ops/sec
+ cli-color 65,561 ops/sec
+ color-cli 13,800 ops/sec
+ ansi-colors 260,316 ops/sec
+ kleur/colors 561,111 ops/sec
+ kleur 648,195 ops/sec
+ chalk 497,292 ops/sec
++ ansis 558,575 ops/sec
```
@@ -264,16 +293,48 @@ colors.forEach((color) => c[color](c.bold(c.underline(c.italic('foo')))));
c.red(`a red ${c.white('white')} red ${c.red('red')} red ${c.cyan('cyan')} red ${c.black('black')} red ${c.red('red')} red ${c.green('green')} red ${c.red('red')} red ${c.yellow('yellow')} red ${c.blue('blue')} red ${c.red('red')} red ${c.magenta('magenta')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.green('green')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.magenta('magenta')} red ${c.red('red')} red ${c.red('red')} red ${c.cyan('cyan')} red ${c.red('red')} red ${c.red('red')} red ${c.yellow('yellow')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} message`);
```
```diff
- colors-js 89,529 ops/sec
- colorette 243,237 ops/sec
- picocolors 242,528 ops/sec
- cli-color 41,897 ops/sec
- color-cli 14,245 ops/sec
- ansi-colors 120,991 ops/sec
- kleur/colors 233,875 ops/sec
- kleur 220,233 ops/sec
- chalk 157,450 ops/sec
-+ ansis 205,393 ops/sec
+ colors-js 89,633 ops/sec
+ colorette 243,139 ops/sec
+ picocolors 243,975 ops/sec
+ cli-color 41,657 ops/sec
+ color-cli 14,264 ops/sec
+ ansi-colors 121,451 ops/sec
+ kleur/colors 234,132 ops/sec
+ kleur 221,446 ops/sec
+ chalk 189,960 ops/sec
++ ansis 211,868 ops/sec
+
+```
+
+### Deep nested styles
+```js
+c.green(
+ `green ${c.cyan(
+ `cyan ${c.red(
+ `red ${c.yellow(
+ `yellow ${c.blue(
+ `blue ${c.magenta(
+ `magenta ${c.underline(
+ `underline ${c.italic(`italic`)} underline`
+ )} magenta`
+ )} blue`
+ )} yellow`
+ )} red`
+ )} cyan`
+ )} green`
+);
+```
+```diff
+ colors-js 451,592 ops/sec
+ colorette 1,131,757 ops/sec
+ picocolors 1,002,649 ops/sec
+ cli-color 213,441 ops/sec
+ color-cli 40,340 ops/sec
+ ansi-colors 362,733 ops/sec
+ kleur/colors 478,547 ops/sec
+ kleur 464,004 ops/sec
+ chalk 565,965 ops/sec
++ ansis 882,220 ops/sec
```
@@ -291,8 +352,8 @@ c.hex('#FBA')('foo');
ansi-colors (not supported)
kleur/colors (not supported)
kleur (not supported)
- chalk 2,746,362 ops/sec
-+ ansis 4,584,357 ops/sec
+ chalk 2,891,684 ops/sec
++ ansis 4,944,572 ops/sec
```
## Testing
diff --git a/bench/index.js b/bench/index.js
index bac2a1b..ee28eaa 100644
--- a/bench/index.js
+++ b/bench/index.js
@@ -40,6 +40,8 @@ import kleur from 'kleur';
import * as kleurColors from 'kleur/colors';
import picocolors from 'picocolors';
+const log = console.log;
+
// create new instance of Ansis for correct measure in benchmark
const ansis = new Ansis();
@@ -86,22 +88,9 @@ const colorStyles = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan
let fixture = [];
-function outputNested(name, lib) {
- const rgb = lib.hex('#80109f');
-
- const str = lib.red(
- `begin ${rgb.bold('RGB')} ${lib.yellow('yellow')} red ${lib.italic.cyan('italic cyan')} red ${lib.red(
- 'red'
- )} red ${lib.underline.green.italic(
- `underline italic green ${lib.hex('#e5850a')('underline italic blue')} underline italic green`
- )} red ${lib.cyan('cyan')} red ${lib.bold.yellow('bold yellow')} red ${lib.green('green')} end`
- );
-
- console.log(` ${name}`, str);
-}
-
-outputNested('chalk\t', chalk);
-outputNested('ansis\t', ansis);
+//showSupportOfDeepNestedStyling();
+//showSupportOfDeepNestedMultiStyling();
+//showSupportOfBreakStyleAtNewLine();
// Colorette bench
// https://github.com/jorgebucaran/colorette/blob/main/bench/index.js
@@ -198,18 +187,38 @@ bench('Nested styles')
.add('ansis', () => fixture[8](ansis))
.run();
-// reserved
-bench('Break style')
- .add('colors-js', () => colorsJs.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('colorette', () => colorette.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('picocolors', () => picocolors.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('cli-color', () => cliColor.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('color-cli', () => colorCli.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('ansi-colors', () => ansiColors.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('kleur/colors', () => kleurColors.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('kleur', () => ansis.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('chalk', () => kleur.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
- .add('ansis', () => ansis.green(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
+// Deep nested styles
+fixture = createFixture(vendors, deepNestedFixture);
+bench('Deep nested styles')
+ .add('colors-js', () => fixture[9](colorsJs))
+ .add('colorette', () => fixture[0](colorette))
+ .add('picocolors', () => fixture[1](picocolors))
+ .add('cli-color', () => fixture[2](cliColor))
+ .add('color-cli', () => fixture[3](colorCli))
+ .add('ansi-colors', () => fixture[4](ansiColors))
+ .add('kleur/colors', () => fixture[5](kleurColors))
+ .add('kleur', () => fixture[6](kleur))
+ .add('chalk', () => fixture[7](chalk))
+ .add('ansis', () => fixture[8](ansis))
+ .run();
+
+// Check support of correct break style at new line
+
+// Break style at new line
+// bench('New Line')
+// .add('colors-js', () => colorsJs.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
+// .add('ansi-colors', () => ansiColors.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
+// .add('chalk', () => chalk.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
+// .add('ansis', () => ansis.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`))
+// .run();
+
+bench('RGB colors')
+ .add('chalk', () => {
+ for (let i = 0; i < 256; i++) chalk.rgb(i, 150, 200)('foo');
+ })
+ .add('ansis', () => {
+ for (let i = 0; i < 256; i++) ansis.rgb(i, 150, 200)('foo');
+ })
.run();
// HEX colors
@@ -246,3 +255,56 @@ function nestedFixture(c) {
)} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} message`
);
}
+
+function deepNestedFixture(c) {
+ return c.green(
+ `green ${c.cyan(
+ `cyan ${c.red(
+ `red ${c.yellow(
+ `yellow ${c.blue(
+ `blue ${c.magenta(`magenta ${c.underline(`underline ${c.italic(`italic`)} underline`)} magenta`)} blue`
+ )} yellow`
+ )} red`
+ )} cyan`
+ )} green`
+ );
+}
+
+function complexNestedFixture(c) {
+ return c.red(
+ `red ${c.yellow('yellow')} red ${c.italic.cyan('italic cyan')} red ${c.underline.green(
+ `underline green ${c.yellow('underline yellow')} underline green`
+ )} red ${c.cyan('cyan')} red ${c.bold.yellow('bold yellow')} red ${c.green('green')} red`
+ );
+}
+
+function showSupportOfDeepNestedStyling() {
+ log('colors-js: ', deepNestedFixture(colorsJs));
+ log('colorette: ', deepNestedFixture(colorette));
+ log('picocolors: ', deepNestedFixture(picocolors));
+ log('cli-color: ', deepNestedFixture(cliColor));
+ log('color-cli: ', deepNestedFixture(colorCli)); // buggy
+ log('ansi-colors: ', deepNestedFixture(ansiColors));
+ log('kleur/colors: ', deepNestedFixture(kleurColors));
+ log('kleur: ', deepNestedFixture(kleur));
+ log('chalk: ', deepNestedFixture(chalk));
+ log('ansis: ', deepNestedFixture(ansis));
+}
+
+function showSupportOfDeepNestedMultiStyling() {
+ log('chalk: ', complexNestedFixture(chalk));
+ log('ansis: ', complexNestedFixture(ansis));
+}
+
+function showSupportOfBreakStyleAtNewLine() {
+ log('colors-js: ', colorsJs.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // OK
+ log('colorette: ', colorette.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('picocolors: ', picocolors.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('cli-color: ', cliColor.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('color-cli: ', colorCli.green_b(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('ansi-colors: ', ansiColors.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // OK
+ log('kleur/colors: ', kleurColors.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('kleur: ', kleur.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // (not supported)
+ log('chalk: ', chalk.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // OK
+ log('ansis: ', ansis.bgGreen(`\nAnsis\nNEW LINE\nNEXT NEW LINE\n`)); // OK
+}
diff --git a/doc/img/break-style-nl.png b/doc/img/break-style-nl.png
new file mode 100644
index 0000000..a9c7383
Binary files /dev/null and b/doc/img/break-style-nl.png differ
diff --git a/examples/index.js b/examples/index.js
index 13095e8..3c7897a 100644
--- a/examples/index.js
+++ b/examples/index.js
@@ -25,7 +25,8 @@ log();
// background color
log(ansis.bold.italic.hex('#800909').bgHex('#ffe49e')('Hello ansis!'));
log(ansis.bold.italic.bgHex('#800909').hex('#ffe49e')('Hello ansis!'));
-log(ansis.black.bgRgb(200, 80, 300)(`\nAnsis\n${ansis.black.bgBlueBright('NEW LINE')}\nNEXT NEW LINE\n`));
+log(ansis.black.bgGreen(`\n Ansis \n NEW LINE \n NEXT NEW LINE \n`));
+//log(ansis.black.bgRgb(200, 80, 300)(`\nAnsis\n${ansis.black.bgBlueBright('NEW LINE')}\nNEXT NEW LINE\n`));
// example from readme
log(ansis.green(`Hello ${ansis.inverse('ANSI')} World!`));
@@ -69,7 +70,7 @@ function outputNested(name, lib) {
// created via https://patorjk.com/software/taag/#p=testall&h=1&f=Graceful&t=ANSIS
function ansisLogo() {
- const paddingLeft = 6;
+ const paddingLeft = 5;
const logo = `
█████╗ ███╗ ██╗███████╗██╗███████╗
@@ -103,7 +104,7 @@ function ansisLogo() {
for (let charWidthIdx = 0; charWidthIdx < width; charWidthIdx++) {
char = logo[i++];
code = Array.isArray(codes) ? codes[row] : codes + row;
- out += ansis.ansi256(code)(char);
+ out += ansis.ansi(code)(char);
}
}
row++;
diff --git a/package.json b/package.json
index 5465743..c7a84fd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ansis",
- "version": "1.1.1",
+ "version": "1.2.0",
"description": "Color styling of text for ANSI terminals using the SGR codes defined in the ECMA-48 standard.",
"keywords": [
"ansi",
@@ -16,7 +16,8 @@
"rgb",
"hex",
"log",
- "logging"
+ "logging",
+ "NO_COLOR"
],
"license": "ISC",
"author": "webdiscus (https://github.com/webdiscus)",
diff --git a/src/ansi-codes.js b/src/ansi-codes.js
index 5029ce6..f080d32 100644
--- a/src/ansi-codes.js
+++ b/src/ansi-codes.js
@@ -1,64 +1,31 @@
-const esc = ([open, close]) => ({ open: `\x1b[${open}m`, close: `\x1b[${close}m` });
+/**
+ * @param {Object?} processTest Used by unit test only.
+ * @returns {boolean}
+ */
+export const isSupported = (processTest) => {
+ const proc = processTest ? processTest : process || {};
+ const env = proc.env || {};
+ const argv = proc.argv || [];
+ const stdout = proc.stdout && proc.stdout.isTTY;
+ //const stderr = proc.stderr && proc.stderr.isTTY;
-export const codes = {
- // commands
- reset: [0, 0],
- inverse: [7, 27],
- hidden: [8, 28],
+ const isDisabled = 'NO_COLOR' in env || argv.includes('--no-color') || argv.includes('--color=false');
+ const isForced = 'FORCE_COLOR' in env || argv.includes('--color');
- // styles
- bold: [1, 22],
- dim: [2, 22], // alias for faint
- faint: [2, 22],
- italic: [3, 23],
- underline: [4, 24],
- doubleUnderline: [21, 24],
- strikethrough: [9, 29],
- strike: [9, 29], // alias for strikethrough
- frame: [51, 54],
- encircle: [52, 54],
- overline: [53, 55],
-
- // foreground colors
- black: [30, 39],
- red: [31, 39],
- green: [32, 39],
- yellow: [33, 39],
- blue: [34, 39],
- magenta: [35, 39],
- cyan: [36, 39],
- white: [37, 39],
- gray: [90, 39], // alias for blackBright
- blackBright: [90, 39],
- redBright: [91, 39],
- greenBright: [92, 39],
- yellowBright: [93, 39],
- blueBright: [94, 39],
- magentaBright: [95, 39],
- cyanBright: [96, 39],
- whiteBright: [97, 39],
+ const isTerm = env.TERM !== 'dumb' && /^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM);
+ const isCompatibleTerminal = (stdout && isTerm) || proc.platform === 'win32';
+ const isCI = 'CI' in env;
- // background colors
- bgBlack: [40, 49],
- bgRed: [41, 49],
- bgGreen: [42, 49],
- bgYellow: [43, 49],
- bgBlue: [44, 49],
- bgMagenta: [45, 49],
- bgCyan: [46, 49],
- bgWhite: [47, 49],
- bgBlackBright: [100, 49],
- bgRedBright: [101, 49],
- bgGreenBright: [102, 49],
- bgYellowBright: [103, 49],
- bgBlueBright: [104, 49],
- bgMagentaBright: [105, 49],
- bgCyanBright: [106, 49],
- bgWhiteBright: [107, 49],
+ return !isDisabled && (isForced || isCompatibleTerminal || isCI);
};
-export const ansiCodes = {
- // commands
+export const supported = isSupported();
+
+const noColorProps = { open: '', close: '' };
+const esc = supported ? ([open, close]) => ({ open: `\x1b[${open}m`, close: `\x1b[${close}m` }) : () => noColorProps;
+
+export const baseCodes = {
+ // misc
reset: esc([0, 0]),
inverse: esc([7, 27]),
hidden: esc([8, 28]),
@@ -113,3 +80,10 @@ export const ansiCodes = {
bgCyanBright: esc([106, 49]),
bgWhiteBright: esc([107, 49]),
};
+
+export const extendedCodes = {
+ ansi256: supported ? (code) => ({ open: `\x1B[38;5;${code}m`, close: '\x1B[39m' }) : () => noColorProps,
+ bgAnsi256: supported ? (code) => ({ open: `\x1B[48;5;${code}m`, close: '\x1B[49m' }) : () => noColorProps,
+ rgb: supported ? (r, g, b) => ({ open: `\x1B[38;2;${r};${g};${b}m`, close: '\x1B[39m' }) : () => noColorProps,
+ bgRgb: supported ? (r, g, b) => ({ open: `\x1B[48;2;${r};${g};${b}m`, close: '\x1B[49m' }) : () => noColorProps,
+};
diff --git a/src/index.d.ts b/src/index.d.ts
index 5fada2f..391329a 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -36,10 +36,15 @@ export interface AnsisInstance extends StyleFunction, StyleProperties {
* ```
*
* @param {number} code in range [0, 255].
- * @return {AnsisInstance}
*/
ansi256: (code: number) => AnsisInstance;
+ /**
+ * Alias to ansi256.
+ * @param {number} code in range [0, 255].
+ */
+ ansi: (code: number) => AnsisInstance;
+
/**
* Set RGB values for foreground color.
*
@@ -71,6 +76,12 @@ export interface AnsisInstance extends StyleFunction, StyleProperties {
*/
bgAnsi256: (code: number) => AnsisInstance;
+ /**
+ * Alias to bgAnsi256.
+ * @param {number} code in range [0, 255].
+ */
+ bgAnsi: (code: number) => AnsisInstance;
+
/**
* Set RGB values for background color.
*
diff --git a/src/index.js b/src/index.js
index 1f7df2a..360be56 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,8 +1,8 @@
import { clamp, hexToRgb, strReplaceAll } from './utils.js';
-import { ansiCodes } from './ansi-codes.js';
+import { baseCodes, extendedCodes } from './ansi-codes.js';
/**
- * All methods are implemented in prototype of the `styleProxy` object.
+ * Note: all methods are implemented in prototype of the `styleProxy` object.
* @implements {AnsisInstance}
*/
export class Ansis {
@@ -73,8 +73,8 @@ const createStyle = (open, close, parent) => {
/**
* Create base styles.
*/
-for (let name in ansiCodes) {
- const { open, close } = ansiCodes[name];
+for (let name in baseCodes) {
+ const { open, close } = baseCodes[name];
styles[name] = {
get() {
const style = createStyle(open, close, this.props);
@@ -98,25 +98,39 @@ styles.visible = {
*/
styles.ansi256 = {
get() {
- return (num) => {
- num = clamp(num, 0, 255);
- return createStyle(`\x1B[38;5;${num}m`, '\x1B[39m', this.props);
+ return (code) => {
+ code = clamp(code, 0, 255);
+ const { open, close } = extendedCodes.ansi256(code);
+ return createStyle(open, close, this.props);
};
},
};
+/**
+ * Alias to ansi256.
+ * @type {AnsisInstance.ansi256}
+ */
+styles.ansi = styles.ansi256;
+
/**
* @type {AnsisInstance.bgAnsi256}
*/
styles.bgAnsi256 = {
get() {
- return (num) => {
- num = clamp(num, 0, 255);
- return createStyle(`\x1B[48;5;${num}m`, '\x1B[49m', this.props);
+ return (code) => {
+ code = clamp(code, 0, 255);
+ const { open, close } = extendedCodes.bgAnsi256(code);
+ return createStyle(open, close, this.props);
};
},
};
+/**
+ * Alias to bgAnsi256.
+ * @type {AnsisInstance.bgAnsi256}
+ */
+styles.bgAnsi = styles.bgAnsi256;
+
/**
* @type {AnsisInstance.rgb}
*/
@@ -126,7 +140,8 @@ styles.rgb = {
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
- return createStyle(`\x1B[38;2;${r};${g};${b}m`, '\x1B[39m', this.props);
+ const { open, close } = extendedCodes.rgb(r, g, b);
+ return createStyle(open, close, this.props);
};
},
};
@@ -137,8 +152,8 @@ styles.rgb = {
styles.hex = {
get() {
return (hex) => {
- const [r, g, b] = hexToRgb(hex);
- return createStyle(`\x1B[38;2;${r};${g};${b}m`, '\x1B[39m', this.props);
+ const { open, close } = extendedCodes.rgb(...hexToRgb(hex));
+ return createStyle(open, close, this.props);
};
},
};
@@ -152,7 +167,8 @@ styles.bgRgb = {
r = clamp(r, 0, 255);
g = clamp(g, 0, 255);
b = clamp(b, 0, 255);
- return createStyle(`\x1B[48;2;${r};${g};${b}m`, '\x1B[49m', this.props);
+ const { open, close } = extendedCodes.bgRgb(r, g, b);
+ return createStyle(open, close, this.props);
};
},
};
@@ -163,8 +179,8 @@ styles.bgRgb = {
styles.bgHex = {
get() {
return (hex) => {
- const [r, g, b] = hexToRgb(hex);
- return createStyle(`\x1B[48;2;${r};${g};${b}m`, '\x1B[49m', this.props);
+ const { open, close } = extendedCodes.bgRgb(...hexToRgb(hex));
+ return createStyle(open, close, this.props);
};
},
};
diff --git a/src/utils.js b/src/utils.js
index 16480d7..417b5b5 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,20 +1,3 @@
-import tty from 'tty';
-
-// todo Add supports NO_COLOR
-// export const isSupported = () => {
-// if (!process) return false;
-//
-// const env = process.env || {};
-// const argv = process.argv || [];
-// const isDisabled = 'NO_COLOR' in env || argv.includes('--no-color');
-// const isForced = 'FORCE_COLOR' in env || argv.includes('--color');
-//
-// const isCompatibleTerminal = (tty.isatty(1) && env.TERM && env.TERM !== 'dumb') || process.platform === 'win32';
-// const isCI = 'CI' in env;
-//
-// return !isDisabled && (isForced || isCompatibleTerminal || isCI);
-// };
-
/**
* Convert hex color string to RGB values.
*
diff --git a/test/cli/output.js b/test/cli/output.js
new file mode 100644
index 0000000..2a9e25c
--- /dev/null
+++ b/test/cli/output.js
@@ -0,0 +1,14 @@
+import ansis from '../../src/index.js';
+const c = ansis;
+
+console.log(
+ c.red('red') +
+ '|' +
+ c.rgb(80, 80, 80)('rgb') +
+ '|' +
+ c.bgRgb(80, 80, 80)('bgRgb') +
+ '|' +
+ c.hex('#fff')('hex') +
+ '|' +
+ c.bgHex('#fff')('bgHex')
+);
\ No newline at end of file
diff --git a/test/index.test.js b/test/index.test.js
index d0a0b8a..74159db 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -1,9 +1,27 @@
+import { execSync } from 'child_process';
+import path from 'path';
+
import ansis from '../src/index.js';
+import { hexToRgb, clamp } from '../src/utils.js';
+import { isSupported } from '../src/ansi-codes.js';
-import { hexToRgb } from '../src/utils.js';
+const TEST_PATH = path.resolve('./test/');
/**
- * Escape ESC-symbol.
+ * Return output of javascript file.
+ *
+ * @param {string} file
+ * @param {array} flags
+ * @return {string}
+ */
+const execScriptSync = (file, flags = []) => {
+ const result = execSync('node ' + file + ' ' + flags.join(' '));
+ // replace last newline in result
+ return result.toString().replace(/\n$/, '');
+};
+
+/**
+ * Escape the slash `\` in ESC-symbol.
* Use it to show by an error the received ESC sequence string in console output.
*
* @param {string} str
@@ -35,6 +53,40 @@ describe('default tests', () => {
});
});
+describe('isSupported', () => {
+ test(`true`, (done) => {
+ let received = isSupported({});
+ const expected = false;
+ expect(received).toEqual(expected);
+ done();
+ });
+
+ test(`true`, (done) => {
+ let received = isSupported({
+ platform: 'win32',
+ env: { FORCE_COLOR: true, CI: 'GITLAB_CI', TERM: 'ansi' },
+ argv: ['--color'],
+ stdout: { isTTY: true },
+ stderr: { isTTY: true },
+ });
+ const expected = true;
+ expect(received).toEqual(expected);
+ done();
+ });
+
+ test(`false`, (done) => {
+ let received = isSupported({
+ env: { NO_COLOR: true, TERM: 'dumb' },
+ argv: ['--color=false', '--no-color'],
+ stdout: {},
+ stderr: {},
+ });
+ const expected = false;
+ expect(received).toEqual(expected);
+ done();
+ });
+});
+
describe('utils tests', () => {
test(`hexToRgb('FFAA99')`, (done) => {
let received = hexToRgb('FFAA99');
@@ -70,6 +122,47 @@ describe('utils tests', () => {
expect(received).toEqual(expected);
done();
});
+
+ test(`clamp(3, 0, 2)`, (done) => {
+ let received = clamp(3, 0, 2);
+ const expected = 2;
+ expect(received).toEqual(expected);
+ done();
+ });
+
+ test(`clamp(0, 1, 2)`, (done) => {
+ let received = clamp(0, 1, 2);
+ const expected = 1;
+ expect(received).toEqual(expected);
+ done();
+ });
+});
+
+describe('node script flags', () => {
+ test(`flag --color`, (done) => {
+ const filename = path.join(TEST_PATH, './cli/output.js');
+ let received = execScriptSync(filename, ['--color']);
+ const expected =
+ '\x1b[31mred\x1b[39m|\x1b[38;2;80;80;80mrgb\x1b[39m|\x1b[48;2;80;80;80mbgRgb\x1b[49m|\x1b[38;2;255;255;255mhex\x1b[39m|\x1b[48;2;255;255;255mbgHex\x1b[49m';
+ expect(esc(received)).toEqual(esc(expected));
+ done();
+ });
+
+ test(`flag --color=false`, (done) => {
+ const filename = path.join(TEST_PATH, './cli/output.js');
+ let received = execScriptSync(filename, ['--color=false']);
+ const expected = 'red|rgb|bgRgb|hex|bgHex';
+ expect(esc(received)).toEqual(esc(expected));
+ done();
+ });
+
+ test(`flag --no-color`, (done) => {
+ const filename = path.join(TEST_PATH, './cli/output.js');
+ let received = execScriptSync(filename, ['--no-color']);
+ const expected = 'red|rgb|bgRgb|hex|bgHex';
+ expect(esc(received)).toEqual(esc(expected));
+ done();
+ });
});
describe('style tests', () => {
@@ -150,6 +243,13 @@ describe('style tests', () => {
done();
});
+ test(`ansis.ansi(97)`, (done) => {
+ let received = ansis.ansi(97)('foo');
+ const expected = '\x1b[38;5;97mfoo\x1b[39m';
+ expect(esc(received)).toEqual(esc(expected));
+ done();
+ });
+
test(`ansis.bgAnsi256(97)`, (done) => {
let received = ansis.bgAnsi256(97)('foo');
const expected = '\x1b[48;5;97mfoo\x1b[49m';
@@ -157,6 +257,13 @@ describe('style tests', () => {
done();
});
+ test(`ansis.bgAnsi(97)`, (done) => {
+ let received = ansis.bgAnsi(97)('foo');
+ const expected = '\x1b[48;5;97mfoo\x1b[49m';
+ expect(esc(received)).toEqual(esc(expected));
+ done();
+ });
+
test(`ansis.green('\nHello\nNew line\nNext new line.\n')`, (done) => {
let received = ansis.green('\nHello\nNew line\nNext new line.\n');
const expected = `\x1b[32m\x1b[39m