diff --git a/CHANGELOG.md b/CHANGELOG.md index 57bc0fa..8b16ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change log +## 3.4.0-beta.1 (2024-12-12) + +- fix: url to GitHub in readme for npm package + +## 3.4.0-beta.0 (2024-12-12) + +- refactor: invisible code optimisations +- refactor: optimize readme for npm package to save ~400 bytes +- refactor: optimize index.d.ts for npm package to save ~500 bytes +- test: refactor benchmark tests +- test: add new representative benchmark tests for using 1, 2, 3 and 4 styles +- test: add picocolors complex benchmark test +- docs: update readme + ## 3.3.2 (2024-07-23) - fix: correct detect TTY on Windows platform diff --git a/README.md b/README.md index 5a39679..7281b99 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +

ansis
@@ -30,22 +31,20 @@ ansi256(214)`Orange` hex('#E0115F').bold.underline('TrueColor!') ``` -
- ## 👀 Why yet one lib? - Quality is first, test coverage 100%. -- Ansis has all [features](#features) that you need, compare with [other libraries](#compare). -- Ansis is one of the smallest, [3.5 KB](https://bundlephobia.com/package/ansis@3.2.0) only. -- Ansis is one of the [fastest](#benchmark), up to **x3 faster** than **Chalk**, [see benchmarks](#benchmark). -- Ansis is stable, continuously developing and improving. +- Ansis has all [features](#features) that you need, compare with [similar libraries](#compare). +- Ansis is one of the smallest, [3.5 kB](https://bundlephobia.com/package/ansis@3.2.0) minified and only [2 kB](https://bundlephobia.com/package/ansis@3.2.0) minzipped. +- Ansis is one of the [fastest](#benchmark), faster than **Chalk** and **Picocolors** (in some use cases), see [benchmarks](#benchmark). - Ansis is open for your [feature requests](https://github.com/webdiscus/ansis/issues). -- Quick response to issues. - Long term support. +- Used + by [NestJS](https://github.com/nestjs/nest), [Facebook/StyleX](https://github.com/facebook/stylex), [Sequelize](https://github.com/sequelize/sequelize), [Salesforce](https://github.com/salesforcecli/cli), [WebpackBar](https://github.com/unjs/webpackbar). ## ⚖️ Similar packages -Most popular ANSI libraries for Node.js: +The most popular similar libraries for Node.js: [chalk][chalk], [kleur][kleur], [ansi-colors][ansi-colors], [kolorist][kolorist], [colorette][colorette], [picocolors][picocolors], [cli-color][cli-color], [colors-cli][colors-cli], [colors.js][colors.js] @@ -53,6 +52,17 @@ Most popular ANSI libraries for Node.js: - 💾 [Compare package sizes](#compare-size) - 📊 [Benchmarks](#benchmark) +## 🔄 [Switch to Ansis](#switch-to-ansis) + +Ansis provides a powerful, small, and fast solution, enabling seamless replacement of heavy, legacy libraries. + +- [Replacing `chalk`](#replacing-chalk) +- [Replacing `colorette`](#replacing-colorette) +- [Replacing `picocolors`](#replacing-picocolors) +- [Replacing `ansi-colors`](#replacing-ansi-colors) +- [Replacing `kleur`](#replacing-kleur) +- [Replacing `cli-color`](#replacing-cli-color) + ## 💡 Highlights @@ -68,19 +78,14 @@ Most popular ANSI libraries for Node.js: - Supports both **ESM** and **CommonJS** - Supports **TypeScript** - Supports **Bun**, **Deno**, **Next.JS** runtimes -- [Standard API](#base-colors), drop-in replacement for **Chalk** - ```diff - - import chalk from 'chalk'; - + import chalk, { red } from 'ansis'; - ``` - ```js - chalk.red.bold('Error!'); // <- Chalk like syntax works fine with Ansis - red.bold('Error!'); // <- the same result with Ansis - red.bold`Error!`; // <- the same result with Ansis - ``` +- [Standard API](#base-colors) +- Drop-in replacement for [`chalk`](#replacing-chalk) +- Drop-in replacement for [`colorette`](#replacing-colorette) +- Drop-in replacement for [`picocolors`](#replacing-picocolors) +- Drop-in replacement for [`ansi-colors`](#replacing-ansi-colors) - Default and [named import](#named-import) `import ansis, { red, bold, ansi256, hex } from 'ansis'` - [Chained syntax](#chained-syntax) `red.bold.underline('text')` -- [Nested **template strings**](#nested-syntax) ``` red`RED ${green`GREEN`} RED` ``` +- [Nested **template strings**](#nested-syntax) ``` red`Error: ${blue`file.js`} not found!` ``` - [ANSI styles](#base-colors) `dim` **`bold`** _`italic`_ `underline` `strikethrough` - [ANSI 16 colors](#base-colors) ``` red`Error!` ``` ``` redBright`Error!` ``` ``` bgRed`Error!` ``` ``` bgRedBright`Error!` ``` - [ANSI 256 colors](#256-colors) ``` fg(56)`violet` ``` ``` bg(208)`orange` ``` @@ -95,6 +100,7 @@ Most popular ANSI libraries for Node.js: - Doesn't extend `String.prototype` - Zero dependencies + ## ❓Question / Feature Request / Bug @@ -309,7 +316,7 @@ Output:\ ## Base ANSI 16 colors and styles Colors and styles have standard names used by many popular libraries, such -as [chalk][chalk], [colorette][colorette], [kleur][kleur]. +as [chalk], [colorette], [picocolors], [kleur]. | Foreground colors | Background colors | Styles | |:----------------------------------------------------------|:----------------------------------------------------------------|-----------------------------------------| @@ -776,9 +783,9 @@ npm run compare | Npm package | Require size | Install size | Download size | |:-----------------------------|-------------:|-------------------------------:|----------------------------------------------------------------------:| -| [`picocolors`][picocolors] | 2.6 kB | [11.4 kB][npm-picocolors] | [2.6 kB](https://arve0.github.io/npm-download-size/#picocolors) | +| [`picocolors`][picocolors] | 2.6 kB | [6.4 kB][npm-picocolors] | [2.6 kB](https://arve0.github.io/npm-download-size/#picocolors) | | [`kleur`][kleur] | 2.7 kB | [20.3 kB][npm-kleur] | [6.0 kB](https://arve0.github.io/npm-download-size/#kleur) | -| [`ansis`][ansis] | 3.4 kB | [11.4 kB][npm-ansis] | [4.6 kB](https://arve0.github.io/npm-download-size/#ansis) | +| [`ansis`][ansis] | 3.4 kB | [10.3 kB][npm-ansis] | [4.6 kB](https://arve0.github.io/npm-download-size/#ansis) | | [`colorette`][colorette] | 3.4 kB | [17.0 kB][npm-colorette] | [4.9 kB](https://arve0.github.io/npm-download-size/#colorette) | | [`ansi-colors`][ansi-colors] | 5.8 kB | [26.1 kB][npm-ansi-colors] | [8.5 kB](https://arve0.github.io/npm-download-size/#ansi-colors) | | [`kolorist`][kolorist] | 6.8 kB | [51.0 kB][npm-kolorist] | [8.7 kB](https://arve0.github.io/npm-download-size/#kolorist) | @@ -834,177 +841,444 @@ To measure performance is used [benchmark.js](https://github.com/bestiejs/benchm git clone https://github.com/webdiscus/ansis.git cd ./ansis npm i +npm run build npm run bench ``` > ### Tested on > > MacBook Pro 16" M1 Max 64GB\ -> macOS Monterey 12.1\ -> Node.js v16.13.1\ +> macOS Sequoia 15.1\ +> Node.js v22.11.0\ > Terminal `iTerm2` +--- + +### Simple bench + +The simple test uses only single style. +Picocolors, Colorette and Kleur do not support [chained syntax](#chained-syntax) or [correct style break](#new-line) (wenn used ``` `\n` ``` in a string), +so they are the fastest in this simple use case. No function - no performance overhead. + +```js +ansis.red('foo') +chalk.red('foo') +picocolors.red('foo') +... +``` + +```diff ++ colorette@2.0.20 109.829.822 ops/sec +- picocolors@1.1.1 108.598.201 ops/sec + kleur@4.1.5 85.231.268 ops/sec +-> ansis@3.3.3 59.923.848 ops/sec -45.4% (~2x slower than 1st place) +- chalk@5.3.0 55.828.562 ops/sec -49.2% + kolorist@1.8.0 37.159.398 ops/sec + ansi-colors@4.1.3 14.202.622 ops/sec + colors@1.4.0 6.999.011 ops/sec + cli-color@2.0.4 2.742.775 ops/sec + colors-cli@1.0.33 913.542 ops/sec +``` + +### Using 2 styles + +Using only 2 styles, picocolors is already a bit slower, because using the [chained syntax](#chained-syntax) is faster than nested calls. + +```js +ansis.red.bold('foo') +chalk.red.bold('foo') +picocolors.red(picocolors.bold('foo')) // chained syntax is not supported +... +``` + +```diff ++ ansis@3.3.3 59.838.727 ops/sec +- picocolors@1.1.1 58.180.994 ops/sec -2.8% +- chalk@5.3.0 48.213.743 ops/sec -19.4% + colorette@2.0.20 33.308.706 ops/sec + kolorist@1.8.0 13.320.722 ops/sec + kleur@4.1.5 5.977.286 ops/sec + ansi-colors@4.1.3 4.123.687 ops/sec + colors@1.4.0 2.986.743 ops/sec + cli-color@2.0.4 1.783.370 ops/sec + colors-cli@1.0.33 690.808 ops/sec +``` + +### Using 3 styles + +Using 3 styles, picocolors is 2x slower than ansis. + +```js +ansis.red.bold.underline('foo') +chalk.red.bold.underline('foo') +picocolors.red(picocolors.bold(picocolors.underline('foo'))) // chained syntax is not supported +... +``` + +```diff ++ ansis@3.3.3 59.602.490 ops/sec +- chalk@5.3.0 42.015.098 ops/sec -29.5% +- picocolors@1.1.1 31.657.041 ops/sec -46.9% (~2x slower than Ansis) + colorette@2.0.20 14.499.195 ops/sec + kolorist@1.8.0 6.902.305 ops/sec + kleur@4.1.5 5.316.469 ops/sec + ansi-colors@4.1.3 2.231.060 ops/sec + colors@1.4.0 1.772.706 ops/sec + cli-color@2.0.4 1.461.792 ops/sec + colors-cli@1.0.33 555.396 ops/sec +``` + +### Using 4 styles + +In rare cases, when using 4 styles, picocolors becomes 3.4x slower than ansis. + +```js +ansis.red.bold.italic.underline('foo') +chalk.red.bold.italic.underline('foo') +picocolors.red(picocolors.bold(picocolors.italic(picocolors.underline('foo')))) // chained syntax is not supported +... +``` + +```diff ++ ansis@3.3.3 58.094.313 ops/sec +- chalk@5.3.0 36.304.684 ops/sec -37.5% +- picocolors@1.1.1 17.335.630 ops/sec -70.2% (~3.4x slower than Ansis) + colorette@2.0.20 8.705.424 ops/sec + kleur@4.1.5 4.801.696 ops/sec + kolorist@1.8.0 4.324.112 ops/sec + ansi-colors@4.1.3 1.524.480 ops/sec + colors@1.4.0 1.263.322 ops/sec + cli-color@2.0.4 1.210.104 ops/sec + colors-cli@1.0.33 484.108 ops/sec +``` + +### Deeply nested styles + +The complex test with deeply 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 ++ colorette@2.0.20 1.101.447 ops/sec +- picocolors@1.1.1 1.058.215 ops/sec +-> ansis@3.3.3 852.390 ops/sec -22.6% + kolorist@1.8.0 837.577 ops/sec +- chalk@5.3.0 571.903 ops/sec -48.0% + kleur@4.1.5 466.172 ops/sec + colors@1.4.0 452.397 ops/sec + ansi-colors@4.1.3 380.633 ops/sec + cli-color@2.0.4 215.335 ops/sec + colors-cli@1.0.33 41.589 ops/sec +``` + ### Colorette bench The benchmark used in [`colorette`](https://github.com/jorgebucaran/colorette/blob/main/bench/index.js). ```js -c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`); +c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`) ``` ```diff -+ colorette 4,572,582 ops/sec very fast - picocolors 3,841,124 ops/sec very fast --> ansis 2,725,758 ops/sec fast - chalk 2,287,146 ops/sec fast - kleur/colors 2,281,415 ops/sec fast - kleur 2,228,639 ops/sec fast - ansi-colors 1,265,615 ops/sec slow - colors.js 1,158,572 ops/sec slow - cli-color 470,320 ops/sec too slow - colors-cli 109,811 ops/sec too slow ++ picocolors@1.1.1 3.911.687 ops/sec + colorette@2.0.20 3.901.652 ops/sec +-> ansis@3.3.3 2.992.624 ops/sec -23.5% + kolorist@1.8.0 2.555.819 ops/sec +- chalk@5.3.0 2.544.110 ops/sec -35.0% + kleur@4.1.5 2.258.746 ops/sec + ansi-colors@4.1.3 1.457.300 ops/sec + colors@1.4.0 1.121.523 ops/sec + cli-color@2.0.4 481.131 ops/sec + colors-cli@1.0.33 115.755 ops/sec ``` -### Base colors +### Picocolors complex bench + +The [`picocolors`](https://github.com/alexeyraspopov/picocolors/blob/main/benchmarks/complex.mjs) benchmark, slightly modified. +Added a bit more complexity by applying 2 styles to the colorized word instead of one. ```js -const colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']; -colors.forEach((color) => c[color]('foo')); +let index = 1e8; +c.red('.') + +c.yellow('.') + +c.green('.') + +c.red.bold(' ERROR ') + +c.red('Add plugin ' + c.cyan.underline('name') + ' to use time limit with ' + c.cyan(++index)); ``` ```diff -+ picocolors 8,265,628 ops/sec very fast --> ansis 6,197,754 ops/sec fast - kleur 5,455,121 ops/sec fast - chalk 4,428,884 ops/sec fast - kleur/colors 2,074,111 ops/sec slow - colorette 1,874,506 ops/sec slow - ansi-colors 1,010,628 ops/sec slow - colors.js 640,101 ops/sec too slow - cli-color 305,690 ops/sec too slow - colors-cli 104,962 ops/sec too slow ++ picocolors@1.1.1 2,552,271 ops/sec +-> ansis@3.3.3 2,449,720 ops/sec -4.0% +- chalk@5.3.0 2,320,215 ops/sec -9.0% + colorette@2.0.20 2,279,859 ops/sec + kleur@4.1.5 1,729,928 ops/sec + kolorist@1.8.0 1,651,713 ops/sec + ansi-colors@4.1.3 807,154 ops/sec + colors@1.4.0 530,762 ops/sec + cli-color@2.0.4 289,246 ops/sec + colors-cli@1.0.33 96,758 ops/sec ``` -### Chained styles +> [!NOTE] +> In this near real test, each library uses the fastest possible styling method.\ +> So, `chalk`, `ansis`, `ansi-colors`, `cli-color`, `colors-cli` and `colors` uses chained method, e.g. `c.red.bold(' ERROR ')`. +> While `picocolors`, `colorette` and `kolorist` uses nested calls, e.g. `c.red(c.bold(' ERROR '))`, because doesn't support the chained syntax. + + +--- + +#### [↑ top](#top) + + + +## How to switch to `ansis` + +Ansis is a powerful, small, and fast replacement that requires **no code migration** for many similar libraries.\ +Just replace your `import ... from ...` or `require(...)` to `ansis`. + + + +### Drop-in replacement for [chalk], no migration required + +```diff +- import chalk from 'chalk'; ++ import chalk from 'ansis'; +``` + +Ansis supports the Chalk syntax and is compatible* with [styles and color names](https://github.com/chalk/chalk?tab=readme-ov-file#styles), so you don't need to modify the +original code: + +```js +chalk.red.bold('Error!'); + +// colorize "Error: file not found!" +chalk.red(`Error: ${chalk.cyan.bold('file')} not found!`); + +// ANSI 256 colors +chalk.ansi256(93)('Violet color'); +chalk.bgAnsi256(194)('Honeydew, more or less'); + +// truecolor +chalk.hex('#FFA500').bold('Bold orange color'); +chalk.rgb(123, 45, 67).underline('Underlined reddish color'); +chalk.bgHex('#E0115F')('Ruby'); +chalk.bgHex('#96C')('Amethyst'); +``` + +> [!WARNING] +> +> Ansis doesn't not support the `overline` style, because it's **not widely supported** and no one uses it.\ +> Check you code and remove the `overline` style: +> +> ```diff +> - chalk.red.overline('text'); +> + chalk.red('text'); +> ``` + +Optionally, you can rewrite the same code to make it even shorter and cleaner: ```js -const colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']; -colors.forEach((color) => c[color].bold.underline.italic('foo')); +import { red, cyan, ansi256, bgAnsi256, fg, bg, hex, rgb, bgHex, bgRgb } from 'ansis'; + +red.bold('Error!'); // using parentheses +red.bold`Error!`; // using template string + +// colorize "Error: file not found!" +red`Error: ${cyan.bold`file`} not found!`; + +// ANSI 256 colors +ansi256(93)`Violet color`; +bgAnsi256(194)`Honeydew, more or less`; +fg(93)`Violet color`; // alias for ansi256 +bg(194)`Honeydew, more or less`; // alias for bgAnsi256 + +// truecolor +hex('#FFA500').bold`Bold orange color`; +rgb(123, 45, 67).underline`Underlined reddish color`; +bgHex('#E0115F')`Ruby`; +bgHex('#96C')`Amethyst`; ``` + + +### Drop-in replacement for [colorette], no migration required + ```diff -+ ansis 5,515,868 ops/sec very fast - chalk 1,234,573 ops/sec fast - kleur 514,035 ops/sec slow - ansi-colors 158,921 ops/sec too slow - cli-color 144,837 ops/sec too slow - colors.js 138,219 ops/sec too slow - colors-cli 52,732 ops/sec too slow - kleur/colors (not supported) - colorette (not supported) - picocolors (not supported) +- import { red, bold, underline } from 'colorette'; ++ import { red, bold, underline } from 'ansis'; ``` -### Nested calls +Ansis is fully compatible with `colorette` [styles and color names](https://github.com/jorgebucaran/colorette#supported-colors), so you don't need to modify the +original code: ```js -const colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']; -colors.forEach((color) => c[color](c.bold(c.underline(c.italic('foo'))))); +red.bold('Error!'); +bold(`I'm ${red(`da ba ${underline("dee")} da ba`)} daa`); ``` +Optionally, you can rewrite the same code to make it even shorter and cleaner: + +```js +red.bold`Error!`; +bold`I'm ${red`da ba ${underline`dee`} da ba`} daa`; +``` + + + +### Drop-in replacement for [picocolors], no migration required + ```diff -+ picocolors 942,592 ops/sec very fast - colorette 695,350 ops/sec fast - kleur 648,195 ops/sec fast - kleur/colors 561,111 ops/sec fast --> ansis 558,575 ops/sec fast - chalk 497,292 ops/sec fast - ansi-colors 260,316 ops/sec slow - colors.js 166,425 ops/sec slow - cli-color 65,561 ops/sec too slow - colors-cli 13,800 ops/sec too slow +- import pico from 'picocolors'; ++ import pico from 'ansis'; ``` -### Nested styles +Ansis is fully compatible with `picocolors` [styles and color names](https://github.com/alexeyraspopov/picocolors#usage), so you don't need to modify the +original code: ```js -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.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.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.black('black')} red ${c.yellow('yellow')} red ${c.red('red')} 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.green('green')} 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.cyan('cyan')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red ${c.red('red')} red message` -); +pico.red(pico.bold('text')); +pico.red(pico.bold(variable)); + +// colorize "Error: file not found!" +pico.red('Error: ' + pico.cyan(pico.bold('file')) + ' not found!'); ``` +Optionally, you can rewrite the same code to make it even shorter and cleaner: + +```js +import { red, cyan } from 'ansis'; + +red.bold`text`; +red.bold(variable); + +// colorize "Error: file not found!" +red`Error: ${cyan.bold`file`} not found!` +``` + + + +### Drop-in replacement for [ansi-colors], no migration required + ```diff -+ picocolors 243,975 ops/sec very fast - colorette 243,139 ops/sec very fast - kleur/colors 234,132 ops/sec very fast - kleur 221,446 ops/sec very fast --> ansis 211,868 ops/sec very fast - chalk 189,960 ops/sec fast - ansi-colors 121,451 ops/sec slow - colors.js 89,633 ops/sec too slow - cli-color 41,657 ops/sec too slow - colors-cli 14,264 ops/sec too slow +- const c = require('ansi-colors'); ++ const c = require('ansis'); ``` -### Deep nested styles +Ansis is fully compatible with `ansi-color` [styles and color names](https://github.com/doowb/ansi-colors#available-styles), so you don't need to modify the +original code: ```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` -); +c.red.bold('Error!'); + +// colorize "Error: file not found!" +c.red(`Error: ${c.cyan.bold('file')} not found!`); +``` + +Optionally, you can rewrite the same code to make it even shorter and cleaner: + +```js +import { red, cyan } from 'ansis'; + +red.bold('Error!'); // using parentheses +red.bold`Error!`; // using template string + +// colorize "Error: file not found!" +red`Error: ${cyan.bold`file`} not found!`; ``` + + +### Migration from [kleur] + ```diff -+ colorette 1,131,757 ops/sec very fast - picocolors 1,002,649 ops/sec very fast --> ansis 882,220 ops/sec very fast - chalk 565,965 ops/sec fast - kleur/colors 478,547 ops/sec fast - kleur 464,004 ops/sec fast - colors.js 451,592 ops/sec fast - ansi-colors 362,733 ops/sec slow - cli-color 213,441 ops/sec slow - colors-cli 40,340 ops/sec too slow +- import { red, green, yellow, cyan } from 'kleur'; ++ import { red, green, yellow, cyan } from 'ansis'; +``` + +Ansis is fully compatible with `kleur` [styles and color names](https://github.com/lukeed/kleur#api), +but Kleur `v3.0` no longer uses Chalk-style syntax (magical getter): + +```js +green().bold().underline('this is a bold green underlined message'); +yellow(`foo ${red().bold('red')} bar ${cyan('cyan')} baz`); ``` -### HEX colors +If you uses chained methods then it requires a simple code modification. +Just replace `().` with `.`: + +```js +green.bold.underline('this is a bold green underlined message'); +yellow(`foo ${red.bold('red')} bar ${cyan('cyan')} baz`); +``` -Only two libraries support TrueColor: `ansis` and `chalk` +Optionally, you can rewrite the same code to make it even shorter and cleaner: ```js -c.hex('#FBA')('foo'); +yellow`foo ${red.bold`red`} bar ${cyan`cyan`} baz`; ``` + + +### Migration from [cli-color] + ```diff -+ ansis 4,944,572 ops/sec very fast - chalk 2,891,684 ops/sec fast - colors.js (not supported) - colorette (not supported) - picocolors (not supported) - cli-color (not supported) - colors-cli (not supported) - ansi-colors (not supported) - kleur/colors (not supported) - kleur (not supported) +- const clc = require('cli-color'); ++ const clc = require('ansis'); +``` + +Ansis is compatible* with `cli-color` [styles and color names](https://github.com/medikoo/cli-color#colors): + +```js +clc.red.bold('Error!'); + +// colorize "Error: file not found!" +clc.red(`Error: ${c.cyan.bold('file')} not found!`); ``` +> [!WARNING] +> +> Ansis doesn't not support the `blink` style, because it's **not widely supported** and no one uses it.\ +> Check you code and remove the `blink` style: +> +> ```diff +> - clc.red.blink('text'); +> + clc.red('text'); +> ``` + +If you use ANSI 256 color functions `xterm` or `bgXterm`, these must be replaced with `ansi256` `fn` or `bgAnsi256` `bg`: + +```diff +- clc.xterm(202).bgXterm(236)('Orange text on dark gray background'); ++ clc.ansi256(202).bgAnsi256(236)('Orange text on dark gray background'); +``` + +Optionally, you can rewrite the same code to make it even shorter and cleaner: + +```js +import { red, cyan, fg, bg } from 'ansis'; + +red.bold`Error!`; + +// colorize "Error: file not found!" +red`Error: ${cyan.bold`file`} not found!`; + +fg(202).bg(236)`Orange text on dark gray background`; +``` + +--- + #### [↑ top](#top) ## Testing @@ -1012,6 +1286,10 @@ c.hex('#FBA')('foo'); `npm run test` will run the unit and integration tests.\ `npm run test:coverage` will run the tests with coverage. +--- + +#### [↑ top](#top) + ## License [ISC](https://github.com/webdiscus/ansis/blob/master/LICENSE) @@ -1036,7 +1314,6 @@ c.hex('#FBA')('foo'); [ansis]: https://github.com/webdiscus/ansis - [npm-colors.js]: https://www.npmjs.com/package/colors [npm-colorette]: https://www.npmjs.com/package/colorette diff --git a/README.npm.md b/README.npm.md index 7df2811..de4c162 100644 --- a/README.npm.md +++ b/README.npm.md @@ -1,25 +1,13 @@ -

- -
- ANSI Styling -
-

+

---- -[![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) -[![size](https://img.shields.io/bundlephobia/minzip/ansis)](https://bundlephobia.com/package/ansis) +[![](https://codecov.io/gh/webdiscus/ansis/branch/master/graph/badge.svg?token=H7SFJONX1X)](https://codecov.io/gh/webdiscus/ansis) +[![](https://img.shields.io/npm/dm/ansis)](https://www.npmjs.com/package/ansis) +[![](https://img.shields.io/bundlephobia/minzip/ansis)](https://bundlephobia.com/package/ansis) Colorize terminal with ANSI colors & styles, smaller and faster alternative to Chalk. -🚀 [Install and Quick Start](https://github.com/webdiscus/ansis#install) - -✅ [Compare features](https://github.com/webdiscus/ansis#compare) with similar packages - -📊 [Benchmarks](https://github.com/webdiscus/ansis#benchmark) - -📖 [Read full docs on GitHub](https://github.com/webdiscus/ansis) +📖 [Docs on GitHub](https://github.com/webdiscus/ansis) --- -![ANSI demo](https://github.com/webdiscus/ansis/raw/master/docs/img/screenshot-readme-npm.png) +![](docs/img/screenshot-readme-npm.png) diff --git a/bench/index.js b/bench/index.js index d2ed33b..9e47b6b 100644 --- a/bench/index.js +++ b/bench/index.js @@ -26,7 +26,6 @@ 'use strict'; import Bench from './lib/bench.js'; -import { createFixture } from './lib/utils.js'; // vendor libraries import chalk from 'chalk'; @@ -36,7 +35,6 @@ import ansiColors from 'ansi-colors'; import cliColor from 'cli-color'; import colorCli from 'colors-cli/safe.js'; import kleur from 'kleur'; -import * as kleurColors from 'kleur/colors'; import * as kolorist from 'kolorist'; import picocolors from 'picocolors'; import { Ansis, green, red, yellow, hex, rgb } from 'ansis'; @@ -58,21 +56,6 @@ if (colorSpace < 3) { log('The result of some tests can be NOT correct! Choose a modern terminal, e.g. iTerm.\n'); } -// All vendor libraries to be tested -const vendors = [ - { name: packages['chalk'], lib: chalk }, - { name: packages['ansis'], lib: ansis }, - { name: packages['colorette'], lib: colorette }, - { name: packages['picocolors'], lib: picocolors }, - { name: packages['kleur'], lib: kleur }, - { name: 'kleur/colors', lib: kleurColors }, - { name: packages['ansi-colors'], lib: ansiColors }, - { name: packages['kolorist'], lib: kolorist }, - { name: packages['cli-color'], lib: cliColor }, - { name: packages['colors-cli'], lib: colorCli }, - { name: packages['colors'], lib: colorsJs }, -]; - const benchStyle = new Ansis(); const bench = new Bench({ minOpsWidth: 12, @@ -85,17 +68,14 @@ const bench = new Bench({ failColor: benchStyle.red.bold, }); -// colors present in all libraries -const baseColors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']; -let fixture; - log(hex('#F88').inverse.bold` -= Benchmark =- `); -bench('Using 1 style (red)'). +// Simple bench +bench('Simple, using 1 style'). add(packages['chalk'], () => chalk.red('foo')). add(packages['ansis'], () => ansis.red('foo')). - add(packages['colorette'], () => colorette.red('foo')). add(packages['picocolors'], () => picocolors.red('foo')). + add(packages['colorette'], () => colorette.red('foo')). add(packages['kleur'], () => kleur.red('foo')). add(packages['ansi-colors'], () => ansiColors.red('foo')). add(packages['kolorist'], () => kolorist.red('foo')). @@ -104,246 +84,192 @@ bench('Using 1 style (red)'). add(packages['colors'], () => colorsJs.red('foo')). run(); -bench(`Using 2 styles (bgWhite, red)`). - add(packages['chalk'], () => chalk.bgWhite.red('foo')). - add(packages['ansis'], () => ansis.bgWhite.red('foo')). - add(packages['colorette'], () => colorette.bgWhite(colorette.red('foo'))). - add(packages['picocolors'], () => picocolors.bgWhite(picocolors.red('foo'))). - add(packages['kleur'], () => kleur.bgWhite().red('foo')). - add(packages['ansi-colors'], () => ansiColors.bgWhite.red('foo')). - add(packages['kolorist'], () => kolorist.bgWhite(kolorist.red('foo'))). - add(packages['cli-color'], () => cliColor.bgWhite.red('foo')). - add(packages['colors-cli'], () => colorCli.white_b.red('foo')). - add(packages['colors'], () => colorsJs.bgWhite.red('foo')). - run(); - -bench(`Using 3 styles (bgWhite, red, bold)`). - add(packages['chalk'], () => chalk.bgWhite.red.bold('foo')). - add(packages['ansis'], () => ansis.bgWhite.red.bold('foo')). - add(packages['colorette'], () => colorette.bgWhite(colorette.red(colorette.bold('foo')))). - add(packages['picocolors'], () => picocolors.bgWhite(picocolors.red(picocolors.bold('foo')))). - add(packages['kleur'], () => kleur.bgWhite().red().bold('foo')). - add(packages['ansi-colors'], () => ansiColors.bgWhite.red.bold('foo')). - add(packages['kolorist'], () => kolorist.bgWhite(kolorist.red(kolorist.bold('foo')))). - add(packages['cli-color'], () => cliColor.bgWhite.red.bold('foo')). - add(packages['colors-cli'], () => colorCli.white_b.red.bold('foo')). - add(packages['colors'], () => colorsJs.bgWhite.red.bold('foo')). - run(); - -bench(`Using 4 styles (bgWhite red, bold, underline)`). - add(packages['chalk'], () => chalk.bgWhite.red.bold.underline('foo')). - add(packages['ansis'], () => ansis.bgWhite.red.bold.underline('foo')). - add(packages['colorette'], () => colorette.bgWhite(colorette.red(colorette.bold(colorette.underline('foo'))))). - add(packages['picocolors'], () => picocolors.bgWhite(picocolors.red(picocolors.bold(picocolors.underline('foo'))))). - add(packages['kleur'], () => kleur.bgWhite().red().bold().underline('foo')). - add(packages['ansi-colors'], () => ansiColors.bgWhite.red.bold.underline('foo')). - add(packages['kolorist'], () => kolorist.bgWhite(kolorist.red(kolorist.bold(kolorist.underline('foo'))))). - add(packages['cli-color'], () => cliColor.bgWhite.red.bold.underline('foo')). - add(packages['colors-cli'], () => colorCli.white_b.red.bold.underline('foo')). - add(packages['colors'], () => colorsJs.bgWhite.red.bold.underline('foo')). +// Fastest way for 2 styles +bench(`Use 2 styles`). + add(packages['chalk'], () => chalk.red.bold('foo')). + add(packages['ansis'], () => ansis.red.bold('foo')). + add(packages['picocolors'], () => picocolors.red(picocolors.bold('foo'))). + add(packages['colorette'], () => colorette.red(colorette.bold('foo'))). + add(packages['kleur'], () => kleur.red().bold('foo')). + add(packages['ansi-colors'], () => ansiColors.red.bold('foo')). + add(packages['kolorist'], () => kolorist.red(kolorist.bold('foo'))). + add(packages['cli-color'], () => cliColor.red.bold('foo')). + add(packages['colors-cli'], () => colorCli.red.bold('foo')). + add(packages['colors'], () => colorsJs.red.bold('foo')). run(); -// Colorette bench -// https://github.com/jorgebucaran/colorette/blob/main/bench/index.js -fixture = createFixture(vendors, coloretteBench); -bench('Colorette bench'). - add(vendors[0].name, () => fixture[0](vendors[0].lib)). - add(vendors[1].name, () => fixture[1](vendors[1].lib)). - add(vendors[2].name, () => fixture[2](vendors[2].lib)). - add(vendors[3].name, () => fixture[3](vendors[3].lib)). - add(vendors[4].name, () => fixture[4](vendors[4].lib)). - add(vendors[5].name, () => fixture[5](vendors[5].lib)). - add(vendors[6].name, () => fixture[6](vendors[6].lib)). - add(vendors[7].name, () => fixture[7](vendors[7].lib)). - add(vendors[8].name, () => fixture[8](vendors[8].lib)). - add(vendors[9].name, () => fixture[9](vendors[9].lib)). - add(vendors[10].name, () => fixture[10](vendors[10].lib)). - run(); - -// Base colors -bench('Base colors'). - add(packages['chalk'], () => baseColors.forEach((style) => chalk[style]('foo'))). - add(packages['ansis'], () => baseColors.forEach((style) => ansis[style]('foo'))). - add(packages['colorette'], () => baseColors.forEach((style) => colorette[style]('foo'))). - add(packages['picocolors'], () => baseColors.forEach((style) => picocolors[style]('foo'))). - add(packages['kleur'], () => baseColors.forEach((style) => kleur[style]('foo'))). - add('kleur/colors', () => baseColors.forEach((style) => kleurColors[style]('foo'))). - add(packages['ansi-colors'], () => baseColors.forEach((style) => ansiColors[style]('foo'))). - add(packages['kolorist'], () => baseColors.forEach((style) => kolorist[style]('foo'))). - add(packages['cli-color'], () => baseColors.forEach((style) => cliColor[style]('foo'))). - add(packages['colors-cli'], () => baseColors.forEach((style) => colorCli[style]('foo'))). - add(packages['colors'], () => baseColors.forEach((style) => colorsJs[style]('foo'))). +// Fastest way for 3 styles +bench('Use 3 styles'). + add(packages['chalk'], () => chalk.red.bold.underline('foo')). + add(packages['ansis'], () => ansis.red.bold.underline('foo')). + add(packages['picocolors'], () => picocolors.red(picocolors.bold(picocolors.underline('foo')))). + add(packages['colorette'], () => colorette.red(colorette.bold(colorette.underline('foo')))). + add(packages['kleur'], () => kleur.red().bold().underline('foo')). + add(packages['ansi-colors'], () => ansiColors.red.bold.underline('foo')). + add(packages['kolorist'], () => kolorist.red(kolorist.bold(kolorist.underline('foo')))). + add(packages['cli-color'], () => cliColor.red.bold.underline('foo')). + add(packages['colors-cli'], () => colorCli.red.bold.underline('foo')). + add(packages['colors'], () => colorsJs.red.bold.underline('foo')). run(); -// Chained styles -bench('Chained styles'). - add(packages['chalk'], () => baseColors.forEach((style) => chalk[style].bold.underline.italic('foo'))). - add(packages['ansis'], () => baseColors.forEach((style) => ansis[style].bold.underline.italic('foo'))). - add('colorette (not supported)', () => baseColors.forEach((style) => colorette[style].bold.underline.italic('foo'))). - add('picocolors (not supported)', () => - baseColors.forEach((style) => picocolors[style].bold.underline.italic('foo')), - ). - add(packages['kleur'], () => baseColors.forEach((style) => kleur[style]().bold().underline().italic('foo'))). // alternate syntax - add('kleur/colors (not supported)', () => - baseColors.forEach((style) => kleurColors[style].bold.underline.italic('foo')), - ). - add(packages['ansi-colors'], () => baseColors.forEach((style) => ansiColors[style].bold.underline.italic('foo'))). - add('kolorist (not supported)', () => baseColors.forEach((style) => kolorist[style].bold.underline.italic('foo'))). - add(packages['cli-color'], () => baseColors.forEach((style) => cliColor[style].bold.underline.italic('foo'))). - add(packages['colors-cli'], () => baseColors.forEach((style) => colorCli[style].bold.underline.italic('foo'))). - add(packages['colors'], () => baseColors.forEach((style) => colorsJs[style].bold.underline.italic('foo'))). +// Fastest way for 4 styles +bench('Use 4 styles'). + add(packages['chalk'], () => chalk.red.bold.italic.underline('foo')). + add(packages['ansis'], () => ansis.red.bold.italic.underline('foo')). + add(packages['picocolors'], () => picocolors.red(picocolors.bold(picocolors.italic(picocolors.underline('foo'))))). + add(packages['colorette'], () => colorette.red(colorette.bold(colorette.italic(colorette.underline('foo'))))). + add(packages['kleur'], () => kleur.red().bold().italic().underline('foo')). + add(packages['ansi-colors'], () => ansiColors.red.bold.italic.underline('foo')). + add(packages['kolorist'], () => kolorist.red(kolorist.bold(kolorist.italic(kolorist.underline('foo'))))). + add(packages['cli-color'], () => cliColor.red.bold.italic.underline('foo')). + add(packages['colors-cli'], () => colorCli.red.bold.italic.underline('foo')). + add(packages['colors'], () => colorsJs.red.bold.italic.underline('foo')). run(); -// Nested calls -bench('Nested calls'). - add(packages['chalk'], () => baseColors.forEach((style) => chalk[style](chalk.bold(chalk.underline(chalk.italic('foo')))))). - add(packages['ansis'], () => baseColors.forEach((style) => ansis[style](ansis.bold(ansis.underline(ansis.italic('foo')))))). - add(packages['colorette'], () => - baseColors.forEach((style) => colorette[style](colorette.bold(colorette.underline(colorette.italic('foo'))))), - ). - add(packages['picocolors'], () => - baseColors.forEach((style) => picocolors[style](picocolors.bold(picocolors.underline(picocolors.italic('foo'))))), - ). - add(packages['kleur'], () => baseColors.forEach((style) => kleur[style](kleur.bold(kleur.underline(kleur.italic('foo')))))). - add('kleur/colors', () => - baseColors.forEach((style) => - kleurColors[style](kleurColors.bold(kleurColors.underline(kleurColors.italic('foo')))), - ), - ). - add(packages['ansi-colors'], () => - baseColors.forEach((style) => ansiColors[style](ansiColors.bold(ansiColors.underline(ansiColors.italic('foo'))))), - ). - add(packages['kolorist'], () => - baseColors.forEach((style) => kolorist[style](kolorist.bold(kolorist.underline(kolorist.italic('foo'))))), - ). - add(packages['cli-color'], () => - baseColors.forEach((style) => cliColor[style](cliColor.bold(cliColor.underline(cliColor.italic('foo'))))), - ). - add(packages['colors-cli'], () => - baseColors.forEach((style) => colorCli[style](colorCli.bold(colorCli.underline(colorCli.italic('foo'))))), - ). - add(packages['colors'], () => - baseColors.forEach((style) => colorsJs[style](colorsJs.bold(colorsJs.underline(colorsJs.italic('foo'))))), - ). +// Chained syntax +bench('Chained syntax'). + add(packages['chalk'], () => chalk.red.bold.italic.underline('foo')). + add(packages['ansis'], () => ansis.red.bold.italic.underline('foo')). + add(packages['kleur'], () => kleur.red().bold().underline('foo')). + add(packages['ansi-colors'], () => ansiColors.red.bold.underline('foo')). + add(packages['cli-color'], () => cliColor.red.bold.underline('foo')). + add(packages['colors-cli'], () => colorCli.red.bold.underline('foo')). + add(packages['colors'], () => colorsJs.red.bold.underline('foo')). + // colorette - (not supported) + // picocolors - (not supported) + // kolorist - (not supported) run(); -// Nested styles -fixture = createFixture(vendors, nestedFixture); +// Nested styles, like picocolors recursion bench('Nested styles'). - add(packages['chalk'], () => fixture[0](chalk)). - add(packages['ansis'], () => fixture[1](ansis)). - add(packages['colorette'], () => fixture[2](colorette)). - add(packages['picocolors'], () => fixture[3](picocolors)). - add(packages['kleur'], () => fixture[4](kleur)). - add('kleur/colors', () => fixture[5](kleurColors)). - add(packages['ansi-colors'], () => fixture[6](ansiColors)). - add(packages['kolorist'], () => fixture[7](kolorist)). - add(packages['cli-color'], () => fixture[8](cliColor)). - add(packages['colors-cli'], () => fixture[9](colorCli)). - add(packages['colors'], () => fixture[10](colorsJs)). + add(packages['chalk'], () => chalk.red(chalk.bold(chalk.underline(chalk.italic('foo'))))). + add(packages['ansis'], () => ansis.red(ansis.bold(ansis.underline(ansis.italic('foo'))))). + add(packages['picocolors'], () => picocolors.red(picocolors.bold(picocolors.italic(picocolors.underline('foo'))))). + add(packages['colorette'], () => colorette.red(colorette.bold(colorette.underline(colorette.italic('foo'))))). + add(packages['kleur'], () => kleur.red(kleur.bold(kleur.underline(kleur.italic('foo'))))). + add(packages['ansi-colors'], () => ansiColors.red(ansiColors.bold(ansiColors.underline(ansiColors.italic('foo'))))). + add(packages['kolorist'], () => kolorist.red(kolorist.bold(kolorist.underline(kolorist.italic('foo'))))). + add(packages['cli-color'], () => cliColor.red(cliColor.bold(cliColor.underline(cliColor.italic('foo'))))). + add(packages['colors-cli'], () => colorCli.red(colorCli.bold(colorCli.underline(colorCli.italic('foo'))))). + add(packages['colors'], () => colorsJs.red(colorsJs.bold(colorsJs.underline(colorsJs.italic('foo'))))). run(); // Deep nested styles -fixture = createFixture(vendors, deepNestedFixture); +const deepNestedBench = (c) => 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`, +); + bench('Deep nested styles'). - add(packages['chalk'], () => fixture[0](chalk)). - add(packages['ansis'], () => fixture[1](ansis)). - add(packages['colorette'], () => fixture[2](colorette)). - add(packages['picocolors'], () => fixture[3](picocolors)). - add(packages['kleur'], () => fixture[4](kleur)). - add('kleur/colors', () => fixture[5](kleurColors)). - add(packages['ansi-colors'], () => fixture[6](ansiColors)). - add(packages['kolorist'], () => fixture[7](kolorist)). - add(packages['cli-color'], () => fixture[8](cliColor)). - add(packages['colors-cli'], () => fixture[9](colorCli)). - add(packages['colors'], () => fixture[10](colorsJs)). + add(packages['chalk'], () => deepNestedBench(chalk)). + add(packages['ansis'], () => deepNestedBench(ansis)). + add(packages['picocolors'], () => deepNestedBench(picocolors)). + add(packages['colorette'], () => deepNestedBench(colorette)). + add(packages['kleur'], () => deepNestedBench(kleur)). + add(packages['ansi-colors'], () => deepNestedBench(ansiColors)). + add(packages['kolorist'], () => deepNestedBench(kolorist)). + add(packages['cli-color'], () => deepNestedBench(cliColor)). + add(packages['colors-cli'], () => deepNestedBench(colorCli)). + add(packages['colors'], () => deepNestedBench(colorsJs)). run(); -// Check support of correct break style at new line - -// Break style at new line -//const breakStyleAtNewLineFixture = `\nAnsis\nNEW LINE\nNEXT NEW LINE\n`; -// bench('New Line') -// .add('colors.js', () => colorsJs.bgGreen(breakStyleAtNewLineFixture)) -// .add(packages['ansi-colors'], () => ansiColors.bgGreen(breakStyleAtNewLineFixture)) -// .add(packages['chalk'], () => chalk.bgGreen(breakStyleAtNewLineFixture)) -// .add(packages['ansis'], () => ansis.bgGreen(breakStyleAtNewLineFixture)) -// .run(); - -bench('RGB colors').add(packages['chalk'], () => { - for (let i = 0; i < 256; i++) chalk.rgb(i, 150, 200)('foo'); -}).add(packages['ansis'], () => { - for (let i = 0; i < 256; i++) rgb(i, 150, 200)('foo'); -}).run(); - -// HEX colors -// the hex(), rgb(), bgHex(), bgRgb() methods support only chalk and ansis -bench('HEX colors'). - add(packages['chalk'], () => chalk.hex('#FBA')('foo')). - add(packages['ansis'], () => hex('#FBA')('foo')). - run(); +// Colorette bench +// https://github.com/jorgebucaran/colorette/blob/main/bench/index.js +const coloretteBanch = (c) => c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`); -// Spectrum HEX colors -bench('Spectrum HEX colors'). - add(packages['chalk'], () => { - let str = ''; - spectrum.forEach(color => { - str += chalk.hex(color)('█'); - }); - return str; - }). - add(packages['ansis'], () => { - let str = ''; - spectrum.forEach(color => { - str += hex(color)('█'); - }); - return str; - }). +bench('Colorette bench'). + add(packages['chalk'], () => coloretteBanch(chalk)). + add(packages['ansis'], () => coloretteBanch(ansis)). + add(packages['picocolors'], () => coloretteBanch(picocolors)). + add(packages['colorette'], () => coloretteBanch(colorette)). + add(packages['kleur'], () => coloretteBanch(kleur)). + add(packages['ansi-colors'], () => coloretteBanch(ansiColors)). + add(packages['kolorist'], () => coloretteBanch(kolorist)). + add(packages['cli-color'], () => coloretteBanch(cliColor)). + add(packages['colors-cli'], () => coloretteBanch(colorCli)). + add(packages['colors'], () => coloretteBanch(colorsJs)). run(); -// Template literals -bench('Template literals'). - add(packages['ansis'], () => red`red ${yellow`yellow ${green`green`} yellow`} red`). +// Picocolors complex bench, slightly modified +// Added a bit more complexity by applying 2 styles to the colorized word instead of one. +let index = 1e8; +const picoComplex = (c) => c.red('.') + + c.yellow('.') + + c.green('.') + + c.red(c.bold(' ERROR ')) + // <= here is used nested calls, because picocolors doesn't support the chained syntax. + c.red('Add plugin ' + c.cyan(c.underline('name')) + ' to use time limit with ' + c.cyan(++index)); + +const ansisComplex = (c) => c.red('.') + + c.yellow('.') + + c.green('.') + + c.red.bold(' ERROR ') + // <= use chained syntax where is possible to improve readability and performance + c.red('Add plugin ' + c.cyan.underline('name') + ' to use time limit with ' + c.cyan(++index)); + +bench('Picocolors complex bench'). + add(packages['chalk'], () => ansisComplex(chalk)). + add(packages['ansis'], () => picoComplex(ansis)). + add(packages['ansis'], () => ansisComplex(ansis)). + add(packages['picocolors'], () => picoComplex(picocolors)). + add(packages['colorette'], () => picoComplex(colorette)). + add(packages['kleur'], () => picoComplex(kleur)). + add(packages['kolorist'], () => picoComplex(kolorist)). + add(packages['ansi-colors'], () => ansisComplex(ansiColors)). + add(packages['cli-color'], () => ansisComplex(cliColor)). + add(packages['colors-cli'], () => ansisComplex(colorCli)). + add(packages['colors'], () => ansisComplex(colorsJs)). run(); -function coloretteBench(c) { - return c.red(`${c.bold(`${c.cyan(`${c.yellow('yellow')}cyan`)}`)}red`); -} - -function nestedFixture(c) { - return 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`, - ); -} +// Check support of correct break style at new line -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`, - ); -} +// Break style at new line +// const breakStyleAtNewLineFixture = `\nAnsis\nNEW LINE\nNEXT NEW LINE\n`; +// bench('New Line'). +// add('colors.js', () => colorsJs.bgGreen(breakStyleAtNewLineFixture)). +// add(packages['ansi-colors'], () => ansiColors.bgGreen(breakStyleAtNewLineFixture)). +// add(packages['chalk'], () => chalk.bgGreen(breakStyleAtNewLineFixture)). +// // 2x slower as chalk because chalk use own implementation, but ansis save 400 bytes and uses regexp, this speed is not critical +// add(packages['ansis'], () => ansis.bgGreen(breakStyleAtNewLineFixture)). +// run(); + +// bench('RGB colors').add(packages['chalk'], () => { +// for (let i = 0; i < 256; i++) chalk.rgb(i, 150, 200)('foo'); +// }).add(packages['ansis'], () => { +// for (let i = 0; i < 256; i++) rgb(i, 150, 200)('foo'); +// }).run(); +// +// // HEX colors +// // the hex(), rgb(), bgHex(), bgRgb() methods support only chalk and ansis +// bench('HEX colors'). +// add(packages['chalk'], () => chalk.hex('#FBA')('foo')). +// add(packages['ansis'], () => hex('#FBA')('foo')). +// run(); +// +// // Spectrum HEX colors +// bench('Spectrum HEX colors'). +// add(packages['chalk'], () => { +// let str = ''; +// spectrum.forEach(color => { +// str += chalk.hex(color)('█'); +// }); +// return str; +// }). +// add(packages['ansis'], () => { +// let str = ''; +// spectrum.forEach(color => { +// str += hex(color)('█'); +// }); +// return str; +// }). +// run(); + +// // Template literals +// bench('Template literals'). +// add(packages['ansis'], () => red`red ${yellow`yellow ${green`green`} yellow`} red`). +// run(); diff --git a/bench/package.json b/bench/package.json index c6d4366..05ef2dd 100644 --- a/bench/package.json +++ b/bench/package.json @@ -19,7 +19,7 @@ "colors-cli": "1.0.33", "kleur": "4.1.5", "kolorist": "1.8.0", - "picocolors": "1.1.0", + "picocolors": "1.1.1", "vitest": "^2.1.1" } } diff --git a/bench/packages.js b/bench/packages.js index 9f01373..6b63bf3 100644 --- a/bench/packages.js +++ b/bench/packages.js @@ -1,5 +1,5 @@ -import pkgJson from './package.json' assert { type: 'json' }; -import mainPkgJson from '../package.json' assert { type: 'json' }; +import pkgJson from './package.json' with { type: 'json' }; +import mainPkgJson from '../package.json' with { type: 'json' }; const packages = Object.fromEntries( Object.entries(pkgJson.dependencies).map(([name, version]) => [name, `${name}@${version}`])); diff --git a/bench/yoctocolors/README.md b/bench/yoctocolors/README.md new file mode 100644 index 0000000..ac7ea63 --- /dev/null +++ b/bench/yoctocolors/README.md @@ -0,0 +1,9 @@ +# yoctocolors benchmark + +The benchmark from the [yoctocolors](https://github.com/sindresorhus/yoctocolors) GitHub repo. + +Run bench: + +``` +./benchmark.js +``` diff --git a/bench/yoctocolors/benchmark.js b/bench/yoctocolors/benchmark.js new file mode 100755 index 0000000..c654178 --- /dev/null +++ b/bench/yoctocolors/benchmark.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node +import { Suite } from '@jonahsnider/benchmark'; +import ansi from 'ansi-colors'; +import ansis from 'ansis'; +import chalk from 'chalk'; +import cliColor from 'cli-color'; +import * as colorette from 'colorette'; +import kleur from 'kleur'; +import * as kleurColors from 'kleur/colors'; +import * as nanocolors from 'nanocolors'; +import picocolors from 'picocolors'; +import * as yoctocolors from 'yoctocolors'; + +const suite = new Suite('simple', { + warmup: { trials: 10_000_000 }, + run: { trials: 1_000_000 }, +}); + +// eslint-disable-next-line no-unused-vars +let out; + +suite.addTest('yoctocolors', () => { + out = yoctocolors.red('Add plugin to use time limit'); + out = yoctocolors.green('Add plugin to use time limit'); + out = yoctocolors.blue(`Add plugin to ${yoctocolors.cyan('use')} time limit`); +}) + // .addTest('cli-color', () => { + // out = cliColor.red('Add plugin to use time limit'); + // out = cliColor.green('Add plugin to use time limit'); + // out = cliColor.blue(`Add plugin to ${cliColor.cyan('use')} time limit`); + // }).addTest('ansi-colors', () => { + // out = ansi.red('Add plugin to use time limit'); + // out = ansi.green('Add plugin to use time limit'); + // out = ansi.blue(`Add plugin to ${ansi.cyan('use')} time limit`); + // }).addTest('chalk', () => { + // out = chalk.red('Add plugin to use time limit'); + // out = chalk.green('Add plugin to use time limit'); + // out = chalk.blue(`Add plugin to ${chalk.cyan('use')} time limit`); + // }).addTest('kleur', () => { + // out = kleur.red('Add plugin to use time limit'); + // out = kleur.green('Add plugin to use time limit'); + // out = kleur.blue(`Add plugin to ${kleur.cyan('use')} time limit`); + // }).addTest('kleur/colors', () => { + // out = kleurColors.red('Add plugin to use time limit'); + // out = kleurColors.green('Add plugin to use time limit'); + // out = kleurColors.blue(`Add plugin to ${kleurColors.cyan('use')} time limit`); + // }).addTest('colorette', () => { + // out = colorette.red('Add plugin to use time limit'); + // out = colorette.green('Add plugin to use time limit'); + // out = colorette.blue(`Add plugin to ${colorette.cyan('use')} time limit`); + // }).addTest('nanocolors', () => { + // out = nanocolors.red('Add plugin to use time limit'); + // out = nanocolors.green('Add plugin to use time limit'); + // out = nanocolors.blue(`Add plugin to ${nanocolors.cyan('use')} time limit`); + // }).addTest('picocolors', () => { + // out = picocolors.red('Add plugin to use time limit'); + // out = picocolors.green('Add plugin to use time limit'); + // out = picocolors.blue(`Add plugin to ${picocolors.cyan('use')} time limit`); + // }) + .addTest('ansis', () => { + out = ansis.red('Add plugin to use time limit'); + out = ansis.green('Add plugin to use time limit'); + out = ansis.blue(`Add plugin to ${ansis.cyan('use')} time limit`); + }); + +out = ansis.red('Add plugin to use time limit'); +out = ansis.green('Add plugin to use time limit'); +out = ansis.blue(`Add plugin to ${ansis.cyan('use')} time limit`); +console.log(ansis.blue(`Add plugin to ${ansis.cyan('use')} time limit`)); + +const results = await suite.run(); + +const table = [...results] + // Convert median execution time to mean ops/sec + .map(([library, histogram]) => [ + library, + Math.round(1e9 / histogram.percentile(50)), + ]) + // Sort fastest to slowest + .sort(([, a], [, b]) => b - a) + // Convert to object for console.table + .map(([library, opsPerSec]) => ({ + library, + 'ops/sec': opsPerSec.toLocaleString(), + })); + +console.table(table); diff --git a/bench/yoctocolors/package.json b/bench/yoctocolors/package.json new file mode 100644 index 0000000..d2f8fe1 --- /dev/null +++ b/bench/yoctocolors/package.json @@ -0,0 +1,45 @@ +{ + "name": "bench-yoctocolors", + "version": "2.1.1", + "description": "Benchmark from yoctocolors package", + "license": "MIT", + "repository": "sindresorhus/yoctocolors", + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, + "sideEffects": false, + "engines": { + "node": ">=18" + }, + "files": [ + "index.js", + "index.d.ts", + "base.js", + "base.d.ts" + ], + "devDependencies": { + "@jonahsnider/benchmark": "^5.0.3", + "ansi-colors": "^4.1.3", + "ansis": "file:../../", + "chalk": "^5.3.0", + "cli-color": "^2.0.4", + "colorette": "^2.0.20", + "kleur": "^4.1.5", + "nanocolors": "^0.2.13", + "picocolors": "^1.0.1", + "yoctocolors": "^2.1.1" + }, + "ava": { + "environmentVariables": { + "FORCE_COLOR": "1" + } + } +} diff --git a/docs/README.md b/docs/README.md index c3f6ec9..d806c5c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,7 +5,7 @@ The files from the `package/` will be coped to `dist/` via `rollup`, see the `rollup.config.js`. > Note: -> - the `./package/package.json` work only from installed package via `npm install ansis`. +> - the `./package.npm.json` works only from installed package via `npm install ansis`. > - the `./package.json` configured for local usage, e.g. to build distributive, run test, bench. The files for NPM package will be published from the `dist/` directory. @@ -13,16 +13,19 @@ The files for NPM package will be published from the `dist/` directory. This allows you to reduce the package size and use a different `package.json` optimized only for the NPM package. ### Publish for public access + ``` npm run publish:public ``` ### Publish beta version + ``` npm run publish:beta ``` ### Unpublish beta + ``` npm deprecate ansis@ "remove beta version" ``` diff --git a/package.json b/package.json index 6cbfb88..2b36125 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ansis", - "version": "3.3.2", + "version": "3.4.0", "description": "Colorize terminal output with ANSI colors & styles", "keywords": [ "ansi", @@ -14,7 +14,6 @@ "cli", "log", "logging", - "strip-color", "truecolor", "rgb", "red", diff --git a/package.npm.json b/package.npm.json index daa513e..48ff19a 100644 --- a/package.npm.json +++ b/package.npm.json @@ -1,15 +1,13 @@ { "name": "ansis", - "version": "3.3.2", + "version": "3.4.0-beta.1", "description": "Colorize terminal output with ANSI colors & styles", "keywords": [ "ansi", "color", - "truecolor", "console", "terminal", - "cli", - "chalk" + "cli" ], "license": "ISC", "author": "webdiscus", @@ -25,7 +23,7 @@ }, "sideEffects": false, "engines": { - "node": ">=15" + "node": ">=16" }, "files": [ "index.*" diff --git a/rollup.config.js b/rollup.config.js index d04dba8..cbc5b4a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -61,7 +61,21 @@ export default [ { src: 'dist/index.d.ts', dest: 'dist/', - transform: (contents, name) => { return contents.toString().replaceAll(/\n/g, '');}, + transform: (contents, name) => { + return contents.toString(). + // remove insignificant spaces + replaceAll(/\n/g, ''). + replaceAll(/\s{2}/g, ' '). + replaceAll(' | ', '|'). + replaceAll(' = ', '='). + replaceAll(' => ', '=>'). + replaceAll(', ', ','). + replaceAll(': ', ':'). + replaceAll('{ ', '{'). + replaceAll(' {', '{'). + replaceAll(' }', '}'). + replaceAll('; ', ';'); + }, }, { src: 'package.npm.json', dest: 'dist/', rename: 'package.json' }, diff --git a/src/index.d.ts b/src/index.d.ts index a081c3a..22d71bd 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,3 +1,63 @@ +/** + * Base ANSI Styles + */ +type AnsiStyles = ( + | 'reset' + | 'inverse' + | 'hidden' + | 'visible' + | 'bold' + | 'dim' + | 'italic' + | 'underline' + | 'strikethrough' + | 'strike' + ); + +/** + * Base ANSI Colors + */ +type AnsiColors = ( + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'grey' + | 'gray' + | 'blackBright' + | 'redBright' + | 'greenBright' + | 'yellowBright' + | 'blueBright' + | 'magentaBright' + | 'cyanBright' + | 'whiteBright' + | 'bgBlack' + | 'bgRed' + | 'bgGreen' + | 'bgYellow' + | 'bgBlue' + | 'bgMagenta' + | 'bgCyan' + | 'bgWhite' + | 'bgGrey' + | 'bgGray' + | 'bgBlackBright' + | 'bgRedBright' + | 'bgGreenBright' + | 'bgYellowBright' + | 'bgBlueBright' + | 'bgMagentaBright' + | 'bgCyanBright' + | 'bgWhiteBright' + ); + +type StringUnion = T | (B & Record); +type AnsiColorsExtend = StringUnion; type ColorExtend = Record; interface Ansis { @@ -6,7 +66,7 @@ interface Ansis { * * @return {boolean} */ - isSupported: () => boolean; + isSupported(): boolean; /** * Return styled string. @@ -30,14 +90,14 @@ interface Ansis { * * @param {number} code in range [0, 255]. */ - ansi256: (code: number) => this; + ansi256(code: number): this; /** * Alias for ansi256. * * @param {number} code in range [0, 255]. */ - fg: (code: number) => this; + fg(code: number): this; /** * Set [256-color ANSI code](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) for background color. @@ -52,14 +112,14 @@ interface Ansis { * * @param {number} code in range [0, 255]. */ - bgAnsi256: (code: number) => this; + bgAnsi256(code: number): this; /** * Alias for bgAnsi256. * * @param {number} code in range [0, 255]. */ - bg: (code: number) => this; + bg(code: number): this; /** * Set RGB values for foreground color. @@ -68,14 +128,7 @@ interface Ansis { * @param {number} green The green value, in range [0, 255]. * @param {number} blue The blue value, in range [0, 255]. */ - rgb: (red: number, green: number, blue: number) => this; - - /** - * Set HEX value for foreground color. - * - * @param {string} hex - */ - hex: (color: string) => this; + rgb(red: number, green: number, blue: number): this; /** * Set RGB values for background color. @@ -84,14 +137,21 @@ interface Ansis { * @param {number} green The green value, in range [0, 255]. * @param {number} blue The blue value, in range [0, 255]. */ - bgRgb: (red: number, green: number, blue: number) => this; + bgRgb(red: number, green: number, blue: number): this; + + /** + * Set HEX value for foreground color. + * + * @param {string} hex + */ + hex(hex: string): this; /** * Set HEX value for background color. * * @param {string} hex */ - bgHex: (color: string) => this; + bgHex(hex: string): this; /** * Remove ANSI styling codes. @@ -99,7 +159,7 @@ interface Ansis { * @param {string} str * @return {string} */ - strip: (string: string) => string; + strip(str: string): string; /** * Extend base colors with custom ones. @@ -107,7 +167,7 @@ interface Ansis { * @param {Object.} colors The object with key as color name * and value as hex code of custom color or the object with 'open' and 'close' codes. */ - extend: (colors: ColorExtend) => void; + extend(colors: ColorExtend): void; /** The ANSI escape sequences for starting the current style. */ readonly open: string; @@ -142,9 +202,10 @@ interface Ansis { /** S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ style. (Not widely supported) */ readonly strikethrough: this; - /** S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ style. (Not widely supported) The alias for `strikethrough`. */ + /** S̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h̶ style. (Not widely supported) Alias for `strikethrough`. */ readonly strike: this; + // Colors readonly black: this; readonly red: this; readonly green: this; @@ -154,7 +215,7 @@ interface Ansis { readonly cyan: this; readonly white: this; readonly grey: this; - readonly gray: this; + readonly gray: this; // Alias for grey readonly blackBright: this; readonly redBright: this; readonly greenBright: this; @@ -163,6 +224,8 @@ interface Ansis { readonly magentaBright: this; readonly cyanBright: this; readonly whiteBright: this; + + // Background colors readonly bgBlack: this; readonly bgRed: this; readonly bgGreen: this; @@ -172,7 +235,7 @@ interface Ansis { readonly bgCyan: this; readonly bgWhite: this; readonly bgGrey: this; - readonly bgGray: this; + readonly bgGray: this; // Alias for bgGrey readonly bgBlackBright: this; readonly bgRedBright: this; readonly bgGreenBright: this; @@ -184,70 +247,15 @@ interface Ansis { } declare const ansis: Ansis; -declare const Ansis: new () => Ansis; - -/** - * Base ANSI Styles - */ -type AnsiStyles = ( - | 'reset' - | 'inverse' - | 'hidden' - | 'visible' - | 'bold' - | 'dim' - | 'italic' - | 'underline' - | 'strikethrough' - | 'strike' - ); - -/** - * Base ANSI Colors - */ -type AnsiColors = ( - | 'black' - | 'red' - | 'green' - | 'yellow' - | 'blue' - | 'magenta' - | 'cyan' - | 'white' - | 'grey' - | 'gray' - | 'blackBright' - | 'redBright' - | 'greenBright' - | 'yellowBright' - | 'blueBright' - | 'magentaBright' - | 'cyanBright' - | 'whiteBright' - | 'bgBlack' - | 'bgRed' - | 'bgGreen' - | 'bgYellow' - | 'bgBlue' - | 'bgMagenta' - | 'bgCyan' - | 'bgWhite' - | 'bgGrey' - | 'bgGray' - | 'bgBlackBright' - | 'bgRedBright' - | 'bgGreenBright' - | 'bgYellowBright' - | 'bgBlueBright' - | 'bgMagentaBright' - | 'bgCyanBright' - | 'bgWhiteBright' - ); -type StringUnion = T | (B & Record); -type AnsiColorsExtend = StringUnion; +export { + type AnsiColors, + type AnsiColorsExtend, + type AnsiStyles, + ansis as default, +}; -export { AnsiColors, AnsiColorsExtend, AnsiStyles, Ansis, ansis as default }; +export const Ansis: new () => Ansis; export declare function ansi256(code: number): Ansis; diff --git a/src/index.js b/src/index.js index 49578f5..dced330 100644 --- a/src/index.js +++ b/src/index.js @@ -5,16 +5,16 @@ import { hexToRgb } from './utils.js'; * @typedef {Object} AnsisProps * @property {string} open * @property {string} close - * @property {string?} openStack - * @property {string?} closeStack - * @property {null | AnsisProps} props + * @property {string?} _a The openStack. + * @property {string?} _b The closeStack. + * @property {null | AnsisProps} _p The props. */ const { defineProperty, defineProperties, setPrototypeOf } = Object; const stripANSIRegEx = /[›][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; const regexLFCR = /(\r?\n)/g; -const ESC = '\x1b'; -const LF = '\x0a'; +const ESC = ''; +const LF = '\n'; const styles = {}; /** @@ -38,29 +38,30 @@ const createStyle = ({ _p: props }, { open, close }) => { let props = style._p; let { _a: openStack, _b: closeStack } = props; - let str = strings.raw != null + let str = strings.raw // render template strings ? String.raw(strings, ...values) // convert the number to the string : '' + strings; + // --> detect nested styles // on node.js, the performance of `includes()` and `~indexOf()` is the same, no difference if (str.includes(ESC)) { - while (props != null) { - // this implementation is over 30% faster than String.replaceAll() + while (props) { + // this implementation is over 30% faster than native String.replaceAll() + //str = str.replaceAll(props.close, props.open); // -- begin replaceAll let search = props.close; + let replacement = props.open; let searchLength = search.length; + let result = ''; + let lastPos; + let pos; // the `visible` style has empty open/close props if (searchLength) { - let lastPos = 0; - let result = ''; - let pos; - - while (~(pos = str.indexOf(search, lastPos))) { - result += str.slice(lastPos, pos) + props.open; - lastPos = pos + searchLength; + for (lastPos = 0; ~(pos = str.indexOf(search, lastPos)); lastPos = pos + searchLength) { + result += str.slice(lastPos, pos) + replacement; } if (lastPos) str = result + str.slice(lastPos); @@ -71,6 +72,7 @@ const createStyle = ({ _p: props }, { open, close }) => { } } + // --> detect new line if (str.includes(LF)) { str = str.replace(regexLFCR, closeStack + '$1' + openStack); } @@ -81,7 +83,7 @@ const createStyle = ({ _p: props }, { open, close }) => { let openStack = open; let closeStack = close; - if (props != null) { + if (props) { openStack = props._a + open; closeStack = close + props._b; } @@ -124,17 +126,17 @@ const Ansis = function() { */ self.extend = (colors) => { for (let name in colors) { - let value = colors[name]; - let type = typeof value; + let color = colors[name]; + let type = typeof color; // detect whether the value is style property Object {open, close} // or a string with hex code of a color, e.g.: '#FF0000' - let styleProps = type === 'string' ? fnRgb(...hexToRgb(value)) : value; + let styleProps = type === 'string' ? fnRgb(...hexToRgb(color)) : color; if (type === 'function') { styles[name] = { get() { - return (...args) => createStyle(this, value(...args)); + return (...args) => createStyle(this, color(...args)); }, }; } else {