├── .editorconfig ├── .github └── workflows │ ├── benchmarks.yaml │ └── testing.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── benchmarks ├── complex.mjs ├── loading.mjs ├── recursion.mjs ├── simple.mjs └── size.mjs ├── package.json ├── picocolors.browser.js ├── picocolors.d.ts ├── picocolors.js ├── tests ├── environments.js └── test.js └── types.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | [*.{js,d.ts}] 10 | charset = utf-8 11 | indent_style = tab 12 | indent_size = 2 13 | trim_trailing_whitespace = true 14 | 15 | [package.json,*.yaml] 16 | indent_style = space 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.github/workflows/benchmarks.yaml: -------------------------------------------------------------------------------- 1 | name: Benchmarks 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | FORCE_COLOR: 3 11 | 12 | jobs: 13 | benchmarks: 14 | name: Benchmarks 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: 22 21 | - run: npm install 22 | - name: Ensure color support detection 23 | run: node tests/environments.js 24 | - name: Install esbuild 25 | run: npm install esbuild 26 | - name: Install missing libs 27 | run: npm install chalk5@"npm:chalk@v5.x" 28 | - name: Simple API calls 29 | run: node benchmarks/simple.mjs --expose-gc 30 | - name: Complex formatting expression 31 | run: node benchmarks/complex.mjs --expose-gc 32 | - name: Library module's init time 33 | run: node benchmarks/loading.mjs --expose-gc 34 | - name: Total loaded code size 35 | run: node benchmarks/size.mjs 36 | -------------------------------------------------------------------------------- /.github/workflows/testing.yaml: -------------------------------------------------------------------------------- 1 | name: Testing 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | modern: 11 | name: Node v${{ matrix.node-version }} 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: 16 | - 20 17 | - 18 18 | - 16 19 | - 14 20 | - 12 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm install 27 | - run: node tests/test.js 28 | legacy: 29 | name: Node v${{ matrix.node-version }} (Legacy) 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | node-version: 34 | - 10 35 | - 8 36 | - 6 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{ matrix.node-version }} 42 | - run: npm install 43 | - run: node tests/test.js 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.1.1](https://github.com/alexeyraspopov/picocolors/releases/tag/v1.1.1) 4 | 5 | - Moved TypeScript declarations to a `d.ts` file ([#82](https://github.com/alexeyraspopov/picocolors/pull/82)) 6 | - Reworked color detection algorithm to properly work with empty strings in `NO_COLOR` and `FORCE_COLOR` env variables ([#87](https://github.com/alexeyraspopov/picocolors/pull/87)) 7 | - Eliminated `require()` call to make the package compatible with some tools ([#87](https://github.com/alexeyraspopov/picocolors/pull/87)) 8 | 9 | ## [v1.1.0](https://github.com/alexeyraspopov/picocolors/releases/tag/v1.1.0) 10 | 11 | - Added bright color variants ([#55](https://github.com/alexeyraspopov/picocolors/pull/55)) 12 | 13 | ## [v1.0.1](https://github.com/alexeyraspopov/picocolors/releases/tag/v1.0.1) 14 | 15 | - Updated color detection mechanism to work properly on Vercel Edge Runtime ([#64](https://github.com/alexeyraspopov/picocolors/pull/64)) 16 | - Remove use of recursion to avoid possible stack overflow for very long inputs ([#56](https://github.com/alexeyraspopov/picocolors/pull/56)) 17 | 18 | ## [v1.0.0](https://github.com/alexeyraspopov/picocolors/releases/tag/v1.0.0) 19 | 20 | - Removed several code elements to reduce the package size ([#31](https://github.com/alexeyraspopov/picocolors/pull/31)) 21 | - Fixed optional flag for `createColors()` in TypeScript typings ([#34](https://github.com/alexeyraspopov/picocolors/pull/34)) 22 | 23 | ## [v0.2.1](https://github.com/alexeyraspopov/picocolors/releases/tag/v0.2.1) 24 | 25 | - Removed semicolons to reduce the package size ([#28](https://github.com/alexeyraspopov/picocolors/pull/28)) 26 | - Fixed type definitions ([#29](https://github.com/alexeyraspopov/picocolors/pull/29)) 27 | - Made `createColors()` use `isColorSupported` if no flag was provided ([`aaf57e1`](https://github.com/alexeyraspopov/picocolors/commit/aaf57e14b250112c6ad4fbeff08ad78cafc6c887)) 28 | 29 | ## [v0.2.0](https://github.com/alexeyraspopov/picocolors/releases/tag/v0.2.0) 30 | 31 | - Removed ESM Module to fix the rest of compatibility issues and reduce package size ([#26](https://github.com/alexeyraspopov/picocolors/pull/26)) 32 | - Added support for non-string inputs ([`3276400`](https://github.com/alexeyraspopov/picocolors/commit/3276400d5046c93ae56648e3db137a20b1f420b4)) 33 | 34 | ## [v0.1.0](https://github.com/alexeyraspopov/picocolors/releases/tag/v0.1.0) 35 | 36 | - Added CommonJS support ([#7](https://github.com/alexeyraspopov/picocolors/pull/7)) 37 | - Ensured Node.js 6+ support ([#8](https://github.com/alexeyraspopov/picocolors/pull/8)) 38 | - Added Browsers support ([#10](https://github.com/alexeyraspopov/picocolors/pull/10)) 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # picocolors 2 | 3 | The tiniest and the fastest library for terminal output formatting with ANSI colors. 4 | 5 | ```javascript 6 | import pc from "picocolors" 7 | 8 | console.log( 9 | pc.green(`How are ${pc.italic(`you`)} doing?`) 10 | ) 11 | ``` 12 | 13 | - **No dependencies.** 14 | - **14 times** smaller and **2 times** faster than chalk. 15 | - Used by popular tools like PostCSS, SVGO, Stylelint, and Browserslist. 16 | - Node.js v6+ & browsers support. Support for both CJS and ESM projects. 17 | - TypeScript type declarations included. 18 | - [`NO_COLOR`](https://no-color.org/) friendly. 19 | 20 | ## Motivation 21 | 22 | With `picocolors` we are trying to draw attention to the `node_modules` size 23 | problem and promote performance-first culture. 24 | 25 | ## Prior Art 26 | 27 | Credits go to the following projects: 28 | 29 | - [Nanocolors](https://github.com/ai/nanocolors) by [@ai](https://github.com/ai) 30 | - [Colorette](https://github.com/jorgebucaran/colorette) by [@jorgebucaran](https://github.com/jorgebucaran) 31 | - [Kleur](https://github.com/lukeed/kleur) by [@lukeed](https://github.com/lukeed) 32 | - [Colors.js](https://github.com/Marak/colors.js) by [@Marak](https://github.com/Marak) 33 | - [Chalk](https://github.com/chalk/chalk) by [@sindresorhus](https://github.com/sindresorhus) 34 | 35 | ## Benchmarks 36 | 37 | The space in node_modules including sub-dependencies: 38 | 39 | ```diff 40 | $ node ./benchmarks/size.js 41 | Data from packagephobia.com 42 | chalk 101 kB 43 | cli-color 1249 kB 44 | ansi-colors 25 kB 45 | kleur 21 kB 46 | colorette 17 kB 47 | nanocolors 16 kB 48 | + picocolors 7 kB 49 | ``` 50 | 51 | Library loading time: 52 | 53 | ```diff 54 | $ node ./benchmarks/loading.js 55 | chalk 6.167 ms 56 | cli-color 31.431 ms 57 | ansi-colors 1.585 ms 58 | kleur 2.008 ms 59 | kleur/colors 0.773 ms 60 | colorette 2.476 ms 61 | nanocolors 0.833 ms 62 | + picocolors 0.466 ms 63 | ``` 64 | 65 | Benchmark for simple use case: 66 | 67 | ```diff 68 | $ node ./benchmarks/simple.js 69 | chalk 24,066,342 ops/sec 70 | cli-color 938,700 ops/sec 71 | ansi-colors 4,532,542 ops/sec 72 | kleur 20,343,122 ops/sec 73 | kleur/colors 35,415,770 ops/sec 74 | colorette 34,244,834 ops/sec 75 | nanocolors 33,443,265 ops/sec 76 | + picocolors 33,271,645 ops/sec 77 | ``` 78 | 79 | Benchmark for complex use cases: 80 | 81 | ```diff 82 | $ node ./benchmarks/complex.js 83 | chalk 969,915 ops/sec 84 | cli-color 131,639 ops/sec 85 | ansi-colors 342,250 ops/sec 86 | kleur 611,880 ops/sec 87 | kleur/colors 1,129,526 ops/sec 88 | colorette 1,747,277 ops/sec 89 | nanocolors 1,251,312 ops/sec 90 | + picocolors 2,024,086 ops/sec 91 | ``` 92 | 93 | ## Usage 94 | 95 | Picocolors provides an object which includes a variety of text coloring and formatting functions 96 | 97 | ```javascript 98 | import pc from "picocolors" 99 | ``` 100 | 101 | The object includes following coloring functions: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`. 102 | 103 | ```javascript 104 | console.log(`I see a ${pc.red("red door")} and I want it painted ${pc.black("black")}`) 105 | ``` 106 | 107 | The object also includes following background color modifier functions: `bgBlack`, `bgRed`, `bgGreen`, `bgYellow`, `bgBlue`, `bgMagenta`, `bgCyan`, `bgWhite` and bright variants `bgBlackBright`, `bgRedBright`, `bgGreenBright`, `bgYellowBright`, `bgBlueBright`, `bgMagentaBright`, `bgCyanBright`, `bgWhiteBright`. 108 | 109 | ```javascript 110 | console.log( 111 | pc.bgBlack( 112 | pc.white(`Tom appeared on the sidewalk with a bucket of whitewash and a long-handled brush.`) 113 | ) 114 | ) 115 | ``` 116 | 117 | Besides colors, the object includes following formatting functions: `dim`, `bold`, `hidden`, `italic`, `underline`, `strikethrough`, `reset`, `inverse` and bright variants `blackBright`, `redBright`, `greenBright`, `yellowBright`, `blueBright`, `magentaBright`, `cyanBright`, `whiteBright`. 118 | 119 | ```javascript 120 | for (let task of tasks) { 121 | console.log(`${pc.bold(task.name)} ${pc.dim(task.durationMs + "ms")}`) 122 | } 123 | ``` 124 | 125 | The library provides additional utilities to ensure the best results for the task: 126 | 127 | - `isColorSupported` — boolean, explicitly tells whether or not the colors or formatting appear on the screen 128 | 129 | ```javascript 130 | import pc from "picocolors" 131 | 132 | if (pc.isColorSupported) { 133 | console.log("Yay! This script can use colors and formatters") 134 | } 135 | ``` 136 | 137 | - `createColors(enabled)` — a function that returns a new API object with manually defined color support configuration 138 | 139 | ```javascript 140 | import pc from "picocolors" 141 | 142 | let { red, bgWhite } = pc.createColors(options.enableColors) 143 | ``` 144 | 145 | ## Replacing `chalk` 146 | 147 | 1. Replace package name in import: 148 | 149 | ```diff 150 | - import chalk from 'chalk' 151 | + import pico from 'picocolors' 152 | ``` 153 | 154 | 2. Replace variable: 155 | 156 | ```diff 157 | - chalk.red(text) 158 | + pico.red(text) 159 | ``` 160 | 161 | 3. Replace chains to nested calls: 162 | 163 | ```diff 164 | - chalk.red.bold(text) 165 | + pico.red(pico.bold(text)) 166 | ``` 167 | 168 | 4. You can use [`colorize-template`](https://github.com/usmanyunusov/colorize-template) 169 | to replace chalk’s tagged template literal. 170 | 171 | ```diff 172 | + import { createColorize } from 'colorize-template' 173 | 174 | + let colorize = createColorize(pico) 175 | - chalk.red.bold`full {yellow ${"text"}}` 176 | + colorize`{red.bold full {yellow ${"text"}}}` 177 | ``` 178 | -------------------------------------------------------------------------------- /benchmarks/complex.mjs: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | import { run, bench, summary } from "mitata" 3 | 4 | import * as colorette from "colorette" 5 | import kleur from "kleur" 6 | import * as kleurColors from "kleur/colors" 7 | import chalk from "chalk" 8 | import chalk5 from "chalk5" 9 | import ansi from "ansi-colors" 10 | import cliColor from "cli-color" 11 | import picocolors from "../picocolors.js" 12 | import * as nanocolors from "nanocolors" 13 | import * as yoctocolors from "yoctocolors" 14 | 15 | summary(() => { 16 | let index = 1e8 17 | 18 | bench( 19 | "chalk v4", 20 | () => 21 | chalk.red(".") + 22 | chalk.yellow(".") + 23 | chalk.green(".") + 24 | chalk.bgRed(chalk.black(" ERROR ")) + 25 | chalk.red( 26 | " Add plugin " + chalk.yellow("name") + " to use time limit with " + chalk.yellow(++index) 27 | ) 28 | ) 29 | 30 | bench( 31 | "chalk v5", 32 | () => 33 | chalk5.red(".") + 34 | chalk5.yellow(".") + 35 | chalk5.green(".") + 36 | chalk5.bgRed(chalk5.black(" ERROR ")) + 37 | chalk5.red( 38 | " Add plugin " + chalk5.yellow("name") + " to use time limit with " + chalk5.yellow(++index) 39 | ) 40 | ) 41 | 42 | bench( 43 | "yoctocolors", 44 | () => 45 | yoctocolors.red(".") + 46 | yoctocolors.yellow(".") + 47 | yoctocolors.green(".") + 48 | yoctocolors.bgRed(yoctocolors.black(" ERROR ")) + 49 | yoctocolors.red( 50 | " Add plugin " + 51 | yoctocolors.yellow("name") + 52 | " to use time limit with " + 53 | yoctocolors.yellow(++index) 54 | ) 55 | ) 56 | 57 | bench( 58 | "cli-color", 59 | () => 60 | cliColor.red(".") + 61 | cliColor.yellow(".") + 62 | cliColor.green(".") + 63 | cliColor.bgRed(cliColor.black(" ERROR ")) + 64 | cliColor.red( 65 | " Add plugin " + 66 | cliColor.yellow("name") + 67 | " to use time limit with " + 68 | cliColor.yellow(++index) 69 | ) 70 | ) 71 | 72 | bench( 73 | "ansi-colors", 74 | () => 75 | ansi.red(".") + 76 | ansi.yellow(".") + 77 | ansi.green(".") + 78 | ansi.bgRed(ansi.black(" ERROR ")) + 79 | ansi.red( 80 | " Add plugin " + ansi.yellow("name") + " to use time limit with " + ansi.yellow(++index) 81 | ) 82 | ) 83 | 84 | bench( 85 | "kleur", 86 | () => 87 | kleur.red(".") + 88 | kleur.yellow(".") + 89 | kleur.green(".") + 90 | kleur.bgRed(kleur.black(" ERROR ")) + 91 | kleur.red( 92 | " Add plugin " + kleur.yellow("name") + " to use time limit with " + kleur.yellow(++index) 93 | ) 94 | ) 95 | 96 | bench( 97 | "kleur/colors", 98 | () => 99 | kleurColors.red(".") + 100 | kleurColors.yellow(".") + 101 | kleurColors.green(".") + 102 | kleurColors.bgRed(kleurColors.black(" ERROR ")) + 103 | kleurColors.red( 104 | " Add plugin " + 105 | kleurColors.yellow("name") + 106 | " to use time limit with " + 107 | kleurColors.yellow(++index) 108 | ) 109 | ) 110 | 111 | bench( 112 | "colorette", 113 | () => 114 | colorette.red(".") + 115 | colorette.yellow(".") + 116 | colorette.green(".") + 117 | colorette.bgRed(colorette.black(" ERROR ")) + 118 | colorette.red( 119 | " Add plugin " + 120 | colorette.yellow("name") + 121 | " to use time limit with " + 122 | colorette.yellow(++index) 123 | ) 124 | ) 125 | 126 | bench( 127 | "nanocolors", 128 | () => 129 | nanocolors.red(".") + 130 | nanocolors.yellow(".") + 131 | nanocolors.green(".") + 132 | nanocolors.bgRed(nanocolors.black(" ERROR ")) + 133 | nanocolors.red( 134 | " Add plugin " + 135 | nanocolors.yellow("name") + 136 | " to use time limit with " + 137 | nanocolors.yellow(++index) 138 | ) 139 | ) 140 | 141 | bench( 142 | "picocolors", 143 | () => 144 | picocolors.red(".") + 145 | picocolors.yellow(".") + 146 | picocolors.green(".") + 147 | picocolors.bgRed(picocolors.black(" ERROR ")) + 148 | picocolors.red( 149 | " Add plugin " + 150 | picocolors.yellow("name") + 151 | " to use time limit with " + 152 | picocolors.yellow(`${++index}`) 153 | ) 154 | ) 155 | }) 156 | 157 | await run() 158 | -------------------------------------------------------------------------------- /benchmarks/loading.mjs: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | import { run, bench, group } from "mitata" 3 | import { buildSync } from "esbuild" 4 | import { createRequire } from "module" 5 | import { dirname } from "path" 6 | import { createContext, compileFunction } from "vm" 7 | 8 | let filename = new URL(import.meta.url).pathname 9 | 10 | function build(contents) { 11 | let root = dirname(filename) 12 | let result = buildSync({ 13 | bundle: true, 14 | write: false, 15 | platform: "node", 16 | format: "cjs", 17 | target: "es2020", 18 | stdin: { contents, loader: "js", resolveDir: root }, 19 | }) 20 | let code = result.outputFiles[0].text 21 | return code 22 | } 23 | 24 | function compile(code, context) { 25 | return compileFunction(code, [], { parsingContext: context }) 26 | } 27 | 28 | group(() => { 29 | let codeP = build( 30 | `let picocolors = require("../picocolors.js"); console.log(picocolors != null);` 31 | ) 32 | let contextP = createContext({ 33 | require: createRequire(filename), 34 | process: { env: { FORCE_COLOR: 1 } }, 35 | }) 36 | bench("picocolors", () => compile(codeP, contextP)) 37 | 38 | let codeAC = build(`let ansi = require("ansi-colors"); console.log(ansi != null);`) 39 | let contextAC = createContext({ 40 | require: createRequire(filename), 41 | process: { env: { FORCE_COLOR: 1 } }, 42 | }) 43 | bench("ansi-colors", () => compile(codeAC, contextAC)) 44 | 45 | let codeK = build(`let kleur = require("kleur"); console.log(kleur != null);`) 46 | let contextK = createContext({ 47 | require: createRequire(filename), 48 | process: { env: { FORCE_COLOR: 1 } }, 49 | }) 50 | bench("kleur", () => compile(codeK, contextK)) 51 | 52 | let codeKC = build(`let kleurColors = require("kleur/colors"); console.log(kleurColors != null);`) 53 | let contextKC = createContext({ 54 | require: createRequire(filename), 55 | process: { env: { FORCE_COLOR: 1 } }, 56 | }) 57 | bench("kleur/colors", () => compile(codeKC, contextKC)) 58 | 59 | let codeC = build(`let colorette = require("colorette"); console.log(colorette != null);`) 60 | let contextC = createContext({ 61 | require: createRequire(filename), 62 | process: { env: { FORCE_COLOR: 1 } }, 63 | }) 64 | bench("colorette", () => compile(codeC, contextC)) 65 | 66 | let codeN = build(`let nanocolors = require("nanocolors"); console.log(nanocolors != null);`) 67 | let contextN = createContext({ 68 | require: createRequire(filename), 69 | process: { env: { FORCE_COLOR: 1 } }, 70 | }) 71 | bench("nanocolors", () => compile(codeN, contextN)) 72 | 73 | let codeY = build(`import * as yoctocolors from "yoctocolors"; console.log(yoctocolors != null);`) 74 | let contextY = createContext({ 75 | require: createRequire(filename), 76 | process: { env: { FORCE_COLOR: 1 } }, 77 | }) 78 | bench("yoctocolors", () => compile(codeY, contextY)) 79 | 80 | let codeCh = build(`let chalk = require("chalk"); console.log(chalk != null);`) 81 | let contextCh = createContext({ 82 | require: createRequire(filename), 83 | process: { env: { FORCE_COLOR: 1 } }, 84 | }) 85 | bench("chalk v4", () => compile(codeCh, contextCh)) 86 | 87 | let codeCh5 = build(`import * as chalk5 from "chalk5"; console.log(chalk5 != null);`) 88 | let contextCh5 = createContext({ 89 | require: createRequire(filename), 90 | process: { env: { FORCE_COLOR: 1 } }, 91 | }) 92 | bench("chalk v5", () => compile(codeCh5, contextCh5)) 93 | 94 | let codeCC = build(`let cliColor = require("cli-color")`) 95 | let contextCC = createContext({ 96 | require: createRequire(filename), 97 | process: { env: { FORCE_COLOR: 1 } }, 98 | }) 99 | bench("cli-color", () => compile(codeCC, contextCC)) 100 | }) 101 | 102 | await run() 103 | -------------------------------------------------------------------------------- /benchmarks/recursion.mjs: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | import { run, bench, summary } from "mitata" 3 | 4 | import * as colorette from "colorette" 5 | import kleur from "kleur" 6 | import * as kleurColors from "kleur/colors" 7 | import chalk from "chalk" 8 | import chalk5 from "chalk5" 9 | import ansi from "ansi-colors" 10 | import cliColor from "cli-color" 11 | import picocolors from "../picocolors.js" 12 | import * as nanocolors from "nanocolors" 13 | import * as yoctocolors from "yoctocolors" 14 | 15 | let count = 1000 16 | let input = "lorem ipsum dolor sit amet" 17 | 18 | summary(() => { 19 | bench("chalk v4", () => { 20 | return chalk.blue(chalk.red(input).repeat(count)) 21 | }) 22 | 23 | bench("chalk v5", () => { 24 | return chalk5.blue(chalk5.red(input).repeat(count)) 25 | }) 26 | 27 | bench("cli-color", () => { 28 | return cliColor.blue(cliColor.red(input).repeat(count)) 29 | }) 30 | 31 | bench("ansi-colors", () => { 32 | return ansi.blue(ansi.red(input).repeat(count)) 33 | }) 34 | 35 | bench("kleur", () => { 36 | return kleur.blue(kleur.red(input).repeat(count)) 37 | }) 38 | 39 | bench("kleur/colors", () => { 40 | return kleurColors.blue(kleurColors.red(input).repeat(count)) 41 | }) 42 | 43 | bench("colorette", () => { 44 | return colorette.blue(colorette.red(input).repeat(count)) 45 | }) 46 | 47 | bench("nanocolors", () => { 48 | return nanocolors.blue(nanocolors.red(input).repeat(count)) 49 | }) 50 | 51 | bench("yoctocolors", () => { 52 | return yoctocolors.blue(yoctocolors.red(input).repeat(count)) 53 | }) 54 | 55 | bench("picocolors", () => { 56 | return picocolors.blue(picocolors.red(input).repeat(count)) 57 | }) 58 | }) 59 | 60 | await run() 61 | -------------------------------------------------------------------------------- /benchmarks/simple.mjs: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | import { run, bench, boxplot } from "mitata" 3 | 4 | import * as colorette from "colorette" 5 | import kleur from "kleur" 6 | import * as kleurColors from "kleur/colors" 7 | import chalk from "chalk" 8 | import chalk5 from "chalk5" 9 | import ansi from "ansi-colors" 10 | import cliColor from "cli-color" 11 | import picocolors from "../picocolors.js" 12 | import * as nanocolors from "nanocolors" 13 | import * as yoctocolors from "yoctocolors" 14 | 15 | console.log(colorette.green("colorette")) 16 | console.log(kleur.green("kleur")) 17 | console.log(chalk.green("chalk")) 18 | console.log(chalk5.green("chalk5")) 19 | console.log(ansi.green("ansi")) 20 | console.log(cliColor.green("cliColor")) 21 | console.log(picocolors.green("picocolors")) 22 | console.log(nanocolors.green("nanocolors")) 23 | console.log(yoctocolors.green("yoctocolors")) 24 | 25 | boxplot(() => { 26 | bench("chalk v4", () => { 27 | return chalk.red("Add plugin to use time limit") 28 | }) 29 | bench("chalk v5", () => { 30 | return chalk5.red("Add plugin to use time limit") 31 | }) 32 | bench("cli-color", () => { 33 | return cliColor.red("Add plugin to use time limit") 34 | }) 35 | bench("ansi-colors", () => { 36 | return ansi.red("Add plugin to use time limit") 37 | }) 38 | bench("kleur", () => { 39 | return kleur.red("Add plugin to use time limit") 40 | }) 41 | bench("kleur/colors", () => { 42 | return kleurColors.red("Add plugin to use time limit") 43 | }) 44 | bench("colorette", () => { 45 | return colorette.red("Add plugin to use time limit") 46 | }) 47 | bench("nanocolors", () => { 48 | return nanocolors.red("Add plugin to use time limit") 49 | }) 50 | bench("yoctocolors", () => { 51 | return yoctocolors.red("Add plugin to use time limit") 52 | }) 53 | bench("picocolors", () => { 54 | return picocolors.red("Add plugin to use time limit") 55 | }) 56 | }) 57 | 58 | await run() 59 | -------------------------------------------------------------------------------- /benchmarks/size.mjs: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | import { buildSync } from "esbuild" 3 | import { dirname } from "node:path" 4 | 5 | console.table({ 6 | picocolors: build(`export { default as picocolors } from "../picocolors.js"`), 7 | colorette: build(`export * as colorette from "colorette"`), 8 | "chalk v4": build(`export { default as chalk } from "chalk"`), 9 | "chalk v5": build(`export * as chalk from "chalk5"`), 10 | kleur: build(`export { default as kleur } from "kleur"`), 11 | "kleur/colors": build(`export * as kleurColors from "kleur/colors"`), 12 | "ansi-colors": build(`export { default as ansi } from "ansi-colors"`), 13 | "cli-color": build(`export { default as cliColor } from "cli-color"`), 14 | nanocolors: build(`export * as nanocolors from "nanocolors"`), 15 | yoctocolors: build(`export * as yoctocolors from "yoctocolors"`), 16 | }) 17 | 18 | function build(contents) { 19 | let root = dirname(new URL(import.meta.url).pathname) 20 | let result = buildSync({ 21 | bundle: true, 22 | write: false, 23 | minify: false, 24 | platform: "node", 25 | stdin: { contents, loader: "js", resolveDir: root }, 26 | }) 27 | let code = result.outputFiles[0].text 28 | return { "size (KB)": toKB(code.length) } 29 | } 30 | 31 | function toKB(value) { 32 | return (((value / 1024) * 100) | 0) / 100 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "picocolors", 3 | "version": "1.1.1", 4 | "main": "./picocolors.js", 5 | "types": "./picocolors.d.ts", 6 | "browser": { 7 | "./picocolors.js": "./picocolors.browser.js" 8 | }, 9 | "sideEffects": false, 10 | "description": "The tiniest and the fastest library for terminal output formatting with ANSI colors", 11 | "scripts": { 12 | "test": "node tests/test.js" 13 | }, 14 | "files": [ 15 | "picocolors.*", 16 | "types.d.ts" 17 | ], 18 | "keywords": [ 19 | "terminal", 20 | "colors", 21 | "formatting", 22 | "cli", 23 | "console" 24 | ], 25 | "author": "Alexey Raspopov", 26 | "repository": "alexeyraspopov/picocolors", 27 | "license": "ISC", 28 | "devDependencies": { 29 | "ansi-colors": "^4.1.1", 30 | "chalk": "^4.1.2", 31 | "clean-publish": "^3.0.3", 32 | "cli-color": "^2.0.0", 33 | "colorette": "^2.0.12", 34 | "kleur": "^4.1.4", 35 | "mitata": "^1.0.10", 36 | "nanocolors": "^0.2.12", 37 | "prettier": "^3.3.3", 38 | "yoctocolors": "^2.1.1" 39 | }, 40 | "prettier": { 41 | "printWidth": 100, 42 | "useTabs": true, 43 | "tabWidth": 2, 44 | "semi": false, 45 | "arrowParens": "avoid", 46 | "requirePragma": true 47 | }, 48 | "clean-publish": { 49 | "cleanDocs": true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /picocolors.browser.js: -------------------------------------------------------------------------------- 1 | var x=String; 2 | var create=function() {return {isColorSupported:false,reset:x,bold:x,dim:x,italic:x,underline:x,inverse:x,hidden:x,strikethrough:x,black:x,red:x,green:x,yellow:x,blue:x,magenta:x,cyan:x,white:x,gray:x,bgBlack:x,bgRed:x,bgGreen:x,bgYellow:x,bgBlue:x,bgMagenta:x,bgCyan:x,bgWhite:x,blackBright:x,redBright:x,greenBright:x,yellowBright:x,blueBright:x,magentaBright:x,cyanBright:x,whiteBright:x,bgBlackBright:x,bgRedBright:x,bgGreenBright:x,bgYellowBright:x,bgBlueBright:x,bgMagentaBright:x,bgCyanBright:x,bgWhiteBright:x}}; 3 | module.exports=create(); 4 | module.exports.createColors = create; 5 | -------------------------------------------------------------------------------- /picocolors.d.ts: -------------------------------------------------------------------------------- 1 | import { Colors } from "./types" 2 | 3 | declare const picocolors: Colors & { createColors: (enabled?: boolean) => Colors } 4 | 5 | export = picocolors 6 | -------------------------------------------------------------------------------- /picocolors.js: -------------------------------------------------------------------------------- 1 | let p = process || {}, argv = p.argv || [], env = p.env || {} 2 | let isColorSupported = 3 | !(!!env.NO_COLOR || argv.includes("--no-color")) && 4 | (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || ((p.stdout || {}).isTTY && env.TERM !== "dumb") || !!env.CI) 5 | 6 | let formatter = (open, close, replace = open) => 7 | input => { 8 | let string = "" + input, index = string.indexOf(close, open.length) 9 | return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close 10 | } 11 | 12 | let replaceClose = (string, close, replace, index) => { 13 | let result = "", cursor = 0 14 | do { 15 | result += string.substring(cursor, index) + replace 16 | cursor = index + close.length 17 | index = string.indexOf(close, cursor) 18 | } while (~index) 19 | return result + string.substring(cursor) 20 | } 21 | 22 | let createColors = (enabled = isColorSupported) => { 23 | let f = enabled ? formatter : () => String 24 | return { 25 | isColorSupported: enabled, 26 | reset: f("\x1b[0m", "\x1b[0m"), 27 | bold: f("\x1b[1m", "\x1b[22m", "\x1b[22m\x1b[1m"), 28 | dim: f("\x1b[2m", "\x1b[22m", "\x1b[22m\x1b[2m"), 29 | italic: f("\x1b[3m", "\x1b[23m"), 30 | underline: f("\x1b[4m", "\x1b[24m"), 31 | inverse: f("\x1b[7m", "\x1b[27m"), 32 | hidden: f("\x1b[8m", "\x1b[28m"), 33 | strikethrough: f("\x1b[9m", "\x1b[29m"), 34 | 35 | black: f("\x1b[30m", "\x1b[39m"), 36 | red: f("\x1b[31m", "\x1b[39m"), 37 | green: f("\x1b[32m", "\x1b[39m"), 38 | yellow: f("\x1b[33m", "\x1b[39m"), 39 | blue: f("\x1b[34m", "\x1b[39m"), 40 | magenta: f("\x1b[35m", "\x1b[39m"), 41 | cyan: f("\x1b[36m", "\x1b[39m"), 42 | white: f("\x1b[37m", "\x1b[39m"), 43 | gray: f("\x1b[90m", "\x1b[39m"), 44 | 45 | bgBlack: f("\x1b[40m", "\x1b[49m"), 46 | bgRed: f("\x1b[41m", "\x1b[49m"), 47 | bgGreen: f("\x1b[42m", "\x1b[49m"), 48 | bgYellow: f("\x1b[43m", "\x1b[49m"), 49 | bgBlue: f("\x1b[44m", "\x1b[49m"), 50 | bgMagenta: f("\x1b[45m", "\x1b[49m"), 51 | bgCyan: f("\x1b[46m", "\x1b[49m"), 52 | bgWhite: f("\x1b[47m", "\x1b[49m"), 53 | 54 | blackBright: f("\x1b[90m", "\x1b[39m"), 55 | redBright: f("\x1b[91m", "\x1b[39m"), 56 | greenBright: f("\x1b[92m", "\x1b[39m"), 57 | yellowBright: f("\x1b[93m", "\x1b[39m"), 58 | blueBright: f("\x1b[94m", "\x1b[39m"), 59 | magentaBright: f("\x1b[95m", "\x1b[39m"), 60 | cyanBright: f("\x1b[96m", "\x1b[39m"), 61 | whiteBright: f("\x1b[97m", "\x1b[39m"), 62 | 63 | bgBlackBright: f("\x1b[100m", "\x1b[49m"), 64 | bgRedBright: f("\x1b[101m", "\x1b[49m"), 65 | bgGreenBright: f("\x1b[102m", "\x1b[49m"), 66 | bgYellowBright: f("\x1b[103m", "\x1b[49m"), 67 | bgBlueBright: f("\x1b[104m", "\x1b[49m"), 68 | bgMagentaBright: f("\x1b[105m", "\x1b[49m"), 69 | bgCyanBright: f("\x1b[106m", "\x1b[49m"), 70 | bgWhiteBright: f("\x1b[107m", "\x1b[49m"), 71 | } 72 | } 73 | 74 | module.exports = createColors() 75 | module.exports.createColors = createColors 76 | -------------------------------------------------------------------------------- /tests/environments.js: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | let vm = require("vm") 3 | let fs = require("fs") 4 | let pc = require("../picocolors.js") 5 | let assert = require("assert") 6 | let source = fs.readFileSync(__dirname + "/../picocolors.js", "utf-8") 7 | let CI = process.env.CI 8 | 9 | test("ci server", () => { 10 | let pc = initModuleEnv({ env: { TERM: "dumb", CI: "1" } }) 11 | assert.equal(pc.isColorSupported, true) 12 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 13 | }) 14 | 15 | test("arg --color", () => { 16 | let pc = initModuleEnv({ env: { TERM: "dumb" }, argv: ["--color"] }) 17 | assert.equal(pc.isColorSupported, true) 18 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 19 | }) 20 | 21 | test("env NO_COLOR", () => { 22 | let pc = initModuleEnv({ env: { FORCE_COLOR: "1", NO_COLOR: "1" } }) 23 | assert.equal(pc.isColorSupported, false) 24 | assert.equal(pc.red("text"), pc.createColors(false).red("text")) 25 | }) 26 | 27 | test("env NO_COLOR empty", () => { 28 | let pc = initModuleEnv({ env: { NO_COLOR: "", CI } }) 29 | assert.equal(pc.isColorSupported, true) 30 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 31 | }) 32 | 33 | test("env FORCE_COLOR", () => { 34 | let pc = initModuleEnv({ env: { TERM: "dumb", FORCE_COLOR: "1" } }) 35 | assert.equal(pc.isColorSupported, true) 36 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 37 | }) 38 | 39 | test("arg --no-color", () => { 40 | let pc = initModuleEnv({ env: { FORCE_COLOR: "1" }, argv: ["--no-color"] }) 41 | assert.equal(pc.isColorSupported, false) 42 | assert.equal(pc.red("text"), pc.createColors(false).red("text")) 43 | }) 44 | 45 | test("no term", () => { 46 | let pc = initModuleEnv({ env: { TERM: "dumb" } }) 47 | assert.equal(pc.isColorSupported, false) 48 | assert.equal(pc.red("text"), pc.createColors(false).red("text")) 49 | }) 50 | 51 | test("windows", () => { 52 | let pc = initModuleEnv({ env: { TERM: "dumb" }, platform: "win32" }) 53 | assert.equal(pc.isColorSupported, true) 54 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 55 | }) 56 | 57 | test("edge runtime", () => { 58 | let pc = initModuleEnv({ env: { FORCE_COLOR: "1" }, argv: undefined, require: undefined }) 59 | assert.equal(pc.isColorSupported, true) 60 | assert.equal(pc.red("text"), pc.createColors(true).red("text")) 61 | }) 62 | 63 | function test(name, fn) { 64 | try { 65 | fn() 66 | console.log(pc.green("✓ " + name)) 67 | } catch (error) { 68 | console.log(pc.red("✗ " + name)) 69 | throw error 70 | } 71 | } 72 | 73 | function initModuleEnv({ 74 | env, 75 | argv = [], 76 | platform = "darwin", 77 | require = global.require, 78 | stdout = process.stdout, 79 | }) { 80 | let process = { env, argv, platform, stdout } 81 | let context = vm.createContext({ require, process, module: { exports: {} } }) 82 | let script = new vm.Script(source) 83 | script.runInContext(context) 84 | return context.module.exports 85 | } 86 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | /* @prettier */ 2 | let pc = require("../picocolors.js") 3 | let assert = require("assert") 4 | 5 | const FMT = { 6 | reset: ["\x1b[0m", "\x1b[0m"], 7 | bold: ["\x1b[1m", "\x1b[22m"], 8 | dim: ["\x1b[2m", "\x1b[22m"], 9 | italic: ["\x1b[3m", "\x1b[23m"], 10 | underline: ["\x1b[4m", "\x1b[24m"], 11 | inverse: ["\x1b[7m", "\x1b[27m"], 12 | hidden: ["\x1b[8m", "\x1b[28m"], 13 | strikethrough: ["\x1b[9m", "\x1b[29m"], 14 | 15 | black: ["\x1b[30m", "\x1b[39m"], 16 | red: ["\x1b[31m", "\x1b[39m"], 17 | green: ["\x1b[32m", "\x1b[39m"], 18 | yellow: ["\x1b[33m", "\x1b[39m"], 19 | blue: ["\x1b[34m", "\x1b[39m"], 20 | magenta: ["\x1b[35m", "\x1b[39m"], 21 | cyan: ["\x1b[36m", "\x1b[39m"], 22 | white: ["\x1b[37m", "\x1b[39m"], 23 | gray: ["\x1b[90m", "\x1b[39m"], 24 | 25 | bgBlack: ["\x1b[40m", "\x1b[49m"], 26 | bgRed: ["\x1b[41m", "\x1b[49m"], 27 | bgGreen: ["\x1b[42m", "\x1b[49m"], 28 | bgYellow: ["\x1b[43m", "\x1b[49m"], 29 | bgBlue: ["\x1b[44m", "\x1b[49m"], 30 | bgMagenta: ["\x1b[45m", "\x1b[49m"], 31 | bgCyan: ["\x1b[46m", "\x1b[49m"], 32 | bgWhite: ["\x1b[47m", "\x1b[49m"], 33 | 34 | blackBright: ["\x1b[90m", "\x1b[39m"], 35 | redBright: ["\x1b[91m", "\x1b[39m"], 36 | greenBright: ["\x1b[92m", "\x1b[39m"], 37 | yellowBright: ["\x1b[93m", "\x1b[39m"], 38 | blueBright: ["\x1b[94m", "\x1b[39m"], 39 | magentaBright: ["\x1b[95m", "\x1b[39m"], 40 | cyanBright: ["\x1b[96m", "\x1b[39m"], 41 | whiteBright: ["\x1b[97m", "\x1b[39m"], 42 | 43 | bgBlackBright: ["\x1b[100m", "\x1b[49m"], 44 | bgRedBright: ["\x1b[101m", "\x1b[49m"], 45 | bgGreenBright: ["\x1b[102m", "\x1b[49m"], 46 | bgYellowBright: ["\x1b[103m", "\x1b[49m"], 47 | bgBlueBright: ["\x1b[104m", "\x1b[49m"], 48 | bgMagentaBright: ["\x1b[105m", "\x1b[49m"], 49 | bgCyanBright: ["\x1b[106m", "\x1b[49m"], 50 | bgWhiteBright: ["\x1b[107m", "\x1b[49m"], 51 | } 52 | 53 | test("color matching", () => { 54 | for (let format in FMT) { 55 | assert.equal(pc[format]("string"), FMT[format][0] + "string" + FMT[format][1]) 56 | console.log(pc[format]("testing: " + format)) 57 | } 58 | }) 59 | 60 | test("format/color nesting", () => { 61 | assert.equal( 62 | pc.bold(`BOLD ${pc.red(`RED ${pc.dim("DIM")} RED`)} BOLD`), 63 | FMT.bold[0] + 64 | "BOLD " + 65 | FMT.red[0] + 66 | "RED " + 67 | FMT.dim[0] + 68 | "DIM" + 69 | FMT.dim[1] + 70 | FMT.bold[0] + 71 | " RED" + 72 | FMT.red[1] + 73 | " BOLD" + 74 | FMT.bold[1] 75 | ) 76 | }) 77 | 78 | test("proper wrapping", () => { 79 | assert.equal( 80 | pc.red(pc.bold("==TEST==")), 81 | FMT.red[0] + FMT.bold[0] + "==TEST==" + FMT.bold[1] + FMT.red[1] 82 | ) 83 | }) 84 | 85 | test("complex case of wrapping", () => { 86 | assert.equal( 87 | pc.bold(pc.yellow(pc.bgRed(pc.italic("==TEST==")))), 88 | FMT.bold[0] + 89 | FMT.yellow[0] + 90 | FMT.bgRed[0] + 91 | FMT.italic[0] + 92 | "==TEST==" + 93 | FMT.italic[1] + 94 | FMT.bgRed[1] + 95 | FMT.yellow[1] + 96 | FMT.bold[1] 97 | ) 98 | 99 | assert.equal( 100 | pc.cyan(pc.bold(pc.underline("==TEST=="))), 101 | FMT.cyan[0] + 102 | FMT.bold[0] + 103 | FMT.underline[0] + 104 | "==TEST==" + 105 | FMT.underline[1] + 106 | FMT.bold[1] + 107 | FMT.cyan[1] 108 | ) 109 | }) 110 | 111 | test("close sequence replacement", () => { 112 | assert.equal( 113 | pc.red(`foo ${pc.yellow("bar")} baz`), 114 | FMT.red[0] + "foo " + FMT.yellow[0] + "bar" + FMT.red[0] + " baz" + FMT.red[1] 115 | ) 116 | 117 | assert.equal( 118 | pc.bold(`foo ${pc.red(pc.dim("bar"))} baz`), 119 | FMT.bold[0] + 120 | "foo " + 121 | FMT.red[0] + 122 | FMT.dim[0] + 123 | "bar" + 124 | FMT.dim[1] + 125 | FMT.bold[0] + 126 | FMT.red[1] + 127 | " baz" + 128 | FMT.bold[1] 129 | ) 130 | 131 | assert.equal( 132 | pc.yellow(`foo ${pc.red(pc.bold("red"))} bar ${pc.cyan("cyan")} baz`), 133 | FMT.yellow[0] + 134 | "foo " + 135 | FMT.red[0] + 136 | FMT.bold[0] + 137 | "red" + 138 | FMT.bold[1] + 139 | FMT.yellow[0] + 140 | " bar " + 141 | FMT.cyan[0] + 142 | "cyan" + 143 | FMT.yellow[0] + 144 | " baz" + 145 | FMT.yellow[1] 146 | ) 147 | }) 148 | 149 | test("non-string input", () => { 150 | assert.equal(pc.red(), FMT.red[0] + "undefined" + FMT.red[1]) 151 | assert.equal(pc.red(undefined), FMT.red[0] + "undefined" + FMT.red[1]) 152 | assert.equal(pc.red(0), FMT.red[0] + "0" + FMT.red[1]) 153 | assert.equal(pc.red(NaN), FMT.red[0] + "NaN" + FMT.red[1]) 154 | assert.equal(pc.red(null), FMT.red[0] + "null" + FMT.red[1]) 155 | assert.equal(pc.red(true), FMT.red[0] + "true" + FMT.red[1]) 156 | assert.equal(pc.red(false), FMT.red[0] + "false" + FMT.red[1]) 157 | assert.equal(pc.red(Infinity), FMT.red[0] + "Infinity" + FMT.red[1]) 158 | }) 159 | 160 | test("shouldn't overflow when coloring already colored large text", () => { 161 | try { 162 | pc.blue(pc.red("x").repeat(10000)) 163 | assert(true) 164 | } catch (error) { 165 | console.error(error) 166 | assert(false) 167 | } 168 | }) 169 | 170 | function test(name, fn) { 171 | try { 172 | fn() 173 | console.log(pc.green("✓ " + name)) 174 | } catch (error) { 175 | console.log(pc.red("✗ " + name)) 176 | throw error 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | export type Formatter = (input: string | number | null | undefined) => string 2 | 3 | export interface Colors { 4 | isColorSupported: boolean 5 | 6 | reset: Formatter 7 | bold: Formatter 8 | dim: Formatter 9 | italic: Formatter 10 | underline: Formatter 11 | inverse: Formatter 12 | hidden: Formatter 13 | strikethrough: Formatter 14 | 15 | black: Formatter 16 | red: Formatter 17 | green: Formatter 18 | yellow: Formatter 19 | blue: Formatter 20 | magenta: Formatter 21 | cyan: Formatter 22 | white: Formatter 23 | gray: Formatter 24 | 25 | bgBlack: Formatter 26 | bgRed: Formatter 27 | bgGreen: Formatter 28 | bgYellow: Formatter 29 | bgBlue: Formatter 30 | bgMagenta: Formatter 31 | bgCyan: Formatter 32 | bgWhite: Formatter 33 | 34 | blackBright: Formatter 35 | redBright: Formatter 36 | greenBright: Formatter 37 | yellowBright: Formatter 38 | blueBright: Formatter 39 | magentaBright: Formatter 40 | cyanBright: Formatter 41 | whiteBright: Formatter 42 | 43 | bgBlackBright: Formatter 44 | bgRedBright: Formatter 45 | bgGreenBright: Formatter 46 | bgYellowBright: Formatter 47 | bgBlueBright: Formatter 48 | bgMagentaBright: Formatter 49 | bgCyanBright: Formatter 50 | bgWhiteBright: Formatter 51 | } 52 | --------------------------------------------------------------------------------