Skip to content

Commit 322c95f

Browse files
committed
chore: refactor code, update readme
1 parent 736e0ba commit 322c95f

File tree

5 files changed

+45
-27
lines changed

5 files changed

+45
-27
lines changed

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Both are [recommended](https://github.com/es-tooling/module-replacements/blob/ma
105105
The package size in `node_modules` directory:
106106

107107
- `picocolors`: [6.37 kB][npm-picocolors] (not minimized) - A micro library with basic features.
108-
- `аnsis`: [5.76 kB][npm-ansis] (minimized) - A powerful library with a rich set of features.
108+
- `аnsis`: [5.71 kB][npm-ansis] (minimized) - A powerful library with a rich set of features.
109109
- `chalk`: [44.2 kB][npm-chalk] (not minimized) - Provides similar functionality to Ansis.
110110

111111
### ⚡ Performance
@@ -214,7 +214,7 @@ As of 2025, only **Ansis**, **Chalk**, and **Picocolors** are actively maintaine
214214
- ☑️ Picocolors: `CJS` only
215215
- ☑️ Chalk: `ESM` only
216216
- Does it matter the unpacked size?
217-
-[Ansis - 5.76 kB][npm-ansis]
217+
-[Ansis - 5.71 kB][npm-ansis]
218218
-[Picocolors - 6.37 kB][npm-picocolors]
219219
-[Chalk - 44.2 kB][npm-chalk]
220220
- Does it matter if a library performs [~60 million](#bench-simple) or [~100 million](#bench-simple) **ops/sec** when outputting to the terminal?
@@ -230,10 +230,6 @@ As of 2025, only **Ansis**, **Chalk**, and **Picocolors** are actively maintaine
230230
- ✅ Ansis
231231
- ☑️ Chalk
232232
- ❌ Picocolors
233-
- Does supporting a wide range of [environments](#color-support) matter?
234-
- ✅ Ansis
235-
- ✅ Chalk
236-
- ❌ Picocolors
237233
- Does keeping your code clean and readable matter?
238234
- ✅ Ansis ([default and named import](#import), [chained syntax](#chained-syntax), [nested **template strings**](#nested-syntax))
239235
- ✅ Chalk (default import, chained syntax)
@@ -877,10 +873,20 @@ import ansis from 'ansis';
877873
console.log('Color output: ', ansis.isSupported());
878874
```
879875

880-
There is no standard way to detect which color level is supported.
881-
The most common way to detect color support is to check the `TERM` and `COLORTERM` environment variables.
882-
CI systems can be detected by checking for the existence of the `CI` and other specifically environment variables.
883-
Combine that with the knowledge about which operating system the program is running on, and we have a decent enough way to detect colors.
876+
There is no standard way to detect terminal color support.
877+
The most common method is to check the `TERM` and `COLORTERM` environment variables, which often indicate the supported color level.
878+
879+
Most standard CI systems can be identified by the presence of the `CI` environment variable.
880+
While some CI uses their own specific environment variables, they are inconsistent and not widely adopted.
881+
882+
883+
Ansis provides basic support for standard CI environments by checking the commonly used `CI` environment variable.
884+
In such cases, Ansis assumes support for at least 16 colors.
885+
If your code uses 256-color or truecolor, Ansis automatically [fallback](#fallback) to 16 colors, or to black and white if no color support is detected.
886+
887+
> Ansis explicitly detects `GitHub Actions` as supporting `truecolor`, as most Ansis users rely on GitHub CI.
888+
889+
Combined with information about the operating system, this approach provides a practical and lightweight method for detecting color support in most environments.
884890

885891
| Terminal | ANSI 16<br>colors | ANSI 256<br>colors | True<br>Color | env.<br>TERM | env.<br>COLORTERM | Specifically ENV variables |
886892
|:---------------------------------|-------------------|:-------------------|:--------------|:---------------:|:-----------------:|:---------------------------------------|
@@ -1158,7 +1164,7 @@ c.red(1/0) // 'Infinity' in red
11581164

11591165
| Package | Dependencies | Minified | Unpacked Size | Tarball size |
11601166
|:-----------------------------|:------------------------------:|------------------|---------------------------------------------------------:|-----------------------------------------------------------------------:|
1161-
| [`ansis`][ansis] | [0][npm-ansis] | uglified & minified | [5.76 kB][npm-ansis] | [3.4 kB](https://arve0.github.io/npm-download-size/#ansis) |
1167+
| [`ansis`][ansis] | [0][npm-ansis] | uglified & minified | [5.71 kB][npm-ansis] | [3.4 kB](https://arve0.github.io/npm-download-size/#ansis) |
11621168
| [`picocolors`][picocolors] | [0][npm-picocolors] | no | [6.37 kB][npm-picocolors] | [2.6 kB](https://arve0.github.io/npm-download-size/#picocolors) |
11631169
| [`tinyrainbow`][tinyrainbow] | [0][npm-tinyrainbow] | uglified | [8.1 kB][npm-tinyrainbow] | [3.2 kB](https://arve0.github.io/npm-download-size/#tinyrainbow) |
11641170
| [`colorette`][colorette] | [0][npm-colorette] | no | [17.0 kB][npm-colorette] | [4.9 kB](https://arve0.github.io/npm-download-size/#colorette) |

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ansis",
3-
"version": "4.0.0-rc.8",
4-
"description": "A small and fast library for applying ANSI colors in terminal or browser console",
3+
"version": "4.0.0",
4+
"description": "A small and fast ANSI color library",
55
"keywords": [
66
"ansi",
77
"color",
@@ -79,19 +79,19 @@
7979
"devDependencies": {
8080
"@rollup/plugin-replace": "^6.0.2",
8181
"@rollup/plugin-terser": "^0.4.4",
82-
"@types/node": "^22.13.1",
82+
"@types/node": "^22.15.17",
8383
"@vitest/coverage-v8": "^3.1.1",
8484
"ansis": "file:dist",
8585
"esbuild": "^0.25.2",
8686
"prettier": "^3.5.3",
8787
"rimraf": "^6.0.1",
88-
"rollup": "^4.40.0",
88+
"rollup": "^4.40.2",
8989
"rollup-plugin-copy": "^3.5.0",
9090
"swc": "^1.0.11",
9191
"terser": "^5.39.0",
9292
"tsup": "^8.3.6",
9393
"typescript": "5.4.5",
94-
"vitest": "^3.1.1"
94+
"vitest": "^3.1.3"
9595
},
9696
"overrides": {
9797
"rollup-plugin-copy": {

package.npm.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name":"ansis",
3-
"version":"4.0.0-rc.8",
3+
"version":"4.0.0",
44
"description":"ANSI color lib",
55
"keywords":["ansi","colors","cli"],
66
"license":"ISC",

src/color-support.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,22 @@ export const getLevel = (mockThis) => {
130130
colorLevel = LEVEL_BW;
131131
}
132132

133+
// Optimisation: The Terser inlines a function at use place, so we can split the logic on small parts in source code.
134+
133135
// PM2 does not set process.stdout.isTTY, but colors may be supported (depends on actual terminal)
134136
// PM2_HOME is always set by PM2, whether running in fork or cluster mode
135-
let isPM2 = !!env.PM2_HOME;
137+
let isPM2 = () => !!env.PM2_HOME;
136138

137-
// when Next.JS runtime is `edge`, process.stdout is undefined, but colors output is supported
139+
// When Next.JS runtime is `edge`, process.stdout is undefined, but colors output is supported
138140
// runtime values supported colors: `nodejs`, `edge`, `experimental-edge`
141+
let isNextJs = () => env.NEXT_RUNTIME?.includes('edge');
142+
143+
// Whether the output is supported
144+
let isTTY = () => isPM2() || isNextJs() || !!proc.stdout?.isTTY;
145+
146+
let isWin = () => proc.platform === 'win32';
139147

140-
// whether the output is supported
141-
let isTTY = isPM2 || env.NEXT_RUNTIME?.includes('edge') || !!proc.stdout?.isTTY;
148+
let isBrowser = () => !!thisRef.window?.chrome;
142149

143150
// enforce a specific color support:
144151
// FORCE_COLOR=false // disables colors
@@ -171,7 +178,7 @@ export const getLevel = (mockThis) => {
171178
if (isForced) colorLevel = forcedLevel;
172179

173180
// if colorLevel === LEVEL_UNDEFINED, attempt to detect color level, returns 0, 1, 2 or 3
174-
if (!~colorLevel) colorLevel = autoDetectLevel(env, isTTY, proc.platform === 'win32');
181+
if (!~colorLevel) colorLevel = autoDetectLevel(env, isTTY(), isWin());
175182

176183
// if force disabled: FORCE_COLOR=0 or FORCE_COLOR=false
177184
if (!forcedLevel
@@ -180,15 +187,13 @@ export const getLevel = (mockThis) => {
180187
|| hasFlag(/^--(no-color|color=(false|never))$/)) return LEVEL_BW;
181188

182189
// Detect browser support
183-
if (!!thisRef.window?.chrome) return LEVEL_TRUECOLOR;
190+
if (isBrowser()) return LEVEL_TRUECOLOR;
184191

185192
// API Rule: If color output is force enabled but the color level is detected as B&W (e.g., TERM is dumb),
186193
// enable truecolor since color depth support does not matter.
187194

188-
// Optimisation: `!colorLevel` is equivalent to `colorLevel === LEVEL_BW`
189-
//return isForced && !colorLevel ? LEVEL_TRUECOLOR : colorLevel;
190-
191195
// If color output is forced but the environment doesn't support it, allow truecolor anyway,
192196
// for example, when saving CLI output snapshots to a file.
197+
// Optimisation: `!colorLevel` is equivalent to `colorLevel === LEVEL_BW`
193198
return isForced && !colorLevel ? LEVEL_TRUECOLOR : colorLevel;
194199
};

test/functional.test.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ describe('handling numbers', () => {
201201
});
202202
});
203203

204-
describe('style tests', () => {
204+
describe('color tests', () => {
205205
test(`visible with template literal`, () => {
206206
const received = ansis.visible`foo ${green`bar ${red`baz`} bar`} foo`;
207207
const expected = 'foo \x1b[32mbar \x1b[31mbaz\x1b[32m bar\x1b[39m foo';
@@ -250,6 +250,13 @@ describe('style tests', () => {
250250
expect(esc(received)).toEqual(esc(expected));
251251
});
252252

253+
test(`hex() Truecolor samples`, () => {
254+
const received = `${hex('#FF701F')`Orange`}, ${hex('#FF007F')`Rose`}, ${hex('#5C0120')`Bordeaux`}`;
255+
const expected = '\x1b[38;2;255;112;31mOrange\x1b[39m, \x1b[38;2;255;0;127mRose\x1b[39m, \x1b[38;2;92;1;32mBordeaux\x1b[39m';
256+
console.log('Truecolor: ', received);
257+
expect(esc(received)).toEqual(esc(expected));
258+
});
259+
253260
test(`ansis.fg(97)`, () => {
254261
const received = ansis.fg(97)('foo');
255262
const expected = '\x1b[38;5;97mfoo\x1b[39m';

0 commit comments

Comments
 (0)