11import { LEVEL_UNDEFINED , LEVEL_BW , LEVEL_16COLORS , LEVEL_256COLORS , LEVEL_TRUECOLOR } from './color-levels.js' ;
22import { keys , SEPARATOR } from './constants.js' ;
33
4- // Optimisation: declare variables here for more compact code shape after compilation
4+ // Optimisation: declare variables here for more compact code shape after Terser compilation
55let term ;
66
77/**
@@ -21,64 +21,69 @@ let term;
2121 * @return {number }
2222 */
2323let autoDetectLevel = ( proc , env , envKeys ) => {
24- // Optimisation: The Terser inlines a function at use place, so we can split the logic on small functions.
24+ // Optimisation: The Terser inlines a function at use place, so in the source we can split the logic on small functions.
25+
26+ let isWin = ( ) => proc . platform === 'win32' ;
27+
28+ // In Node.js, `process.stdout.isTTY` is `true` for TTY streams and `undefined` for non-TTY output.
29+ // Other runtimes may not expose `isTTY`.
30+ let isTTY = ( ) => proc . stdout ?. isTTY ;
31+
32+ // Detect CI environments.
33+ // Most CI tools set the `CI` ENV variable:
34+ // Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari, etc.
35+ // https://github.com/watson/ci-info/blob/master/vendors.json
36+ let isCI = ( ) => env . CI ;
2537
2638 // PM2 does not set process.stdout.isTTY, but color output may still be supported, depends on the actual terminal.
2739 // PM2 always sets PM2_HOME to a non-empty (truthy) value when running in either fork or cluster mode.
2840 let detectPM2 = ( ) => env . PM2_HOME ;
2941
30- // In the Next.js `edge` runtime, process.stdout is undefined, but colored output is still supported .
42+ // In the Next.js `edge` runtime, process.stdout is undefined, but the output destination may still support colors .
3143 // Runtime values that support colors: `nodejs`, `edge`, `experimental-edge`.
3244 let detectNextJs = ( ) => / e d g e / . test ( env . NEXT_RUNTIME ) ;
3345
34- // Size optimization: intentionally returns a falsy/truthy value instead of a boolean.
35- let isTTY = ( ) => detectPM2 ( ) || detectNextJs ( ) || proc . stdout ?. isTTY ;
36-
37- let isWin = ( ) => proc . platform === 'win32' ;
46+ term = env . TERM ;
3847
3948 // Note: the order of checks is important!
4049
41- // 1) Detect terminals supporting TrueColor by COLORTERM
50+ // 1) A dumb terminal is not expected to render ANSI escape sequences (e.g. in Emacs M-x compile).
51+ // TERM=dumb takes precedence over COLORTERM, CI detection, and platform-specific defaults.
52+ if ( term === 'dumb' ) return LEVEL_BW ;
53+
54+ // 2) Detect color support using the COLORTERM hint.
4255 // Most modern terminals use `TERM=xterm-256color` with `COLORTERM=truecolor`.
43- // COLORTERM values: `truecolor` or `24bit`, `ansi256`, `ansi`
56+ // COLORTERM values: `truecolor` or `24bit`, `ansi256`, `ansi`.
4457 // Terminals that set COLORTERM=truecolor: iTerm, VSCode, `xterm-kitty`, KDE Konsole.
45-
4658 let level = {
4759 '24bit' : LEVEL_TRUECOLOR ,
4860 truecolor : LEVEL_TRUECOLOR ,
4961 ansi256 : LEVEL_256COLORS ,
5062 ansi : LEVEL_16COLORS ,
5163 } [ env . COLORTERM ] ;
5264
53- term = env . TERM ;
54-
5565 if ( level ) return level ;
5666
57- // 2) Detect color support in CI.
58- // Note: CI environments are not TTY and often advertise themselves as `dumb` terminals.
59-
60- // CI tools
61- // https://github.com/watson/ci-info/blob/master/vendors.json
62- if ( env . CI ) {
67+ // 3) Detect color support in CI.
68+ // CI environments can render ANSI colors even when TTY is undefined.
69+ if ( isCI ( ) ) {
6370 // CI supports truecolor: GITHUB_ACTIONS
6471 if ( / , G I T H U B / . test ( envKeys ) ) return LEVEL_TRUECOLOR ;
6572
66- // others CI supports only 16 colors, e.g. when env contains :
67- // - CI_NAME === codeship | sourcehut
73+ // Default to 16 colors for other CI environments, including those identified by environment variables :
74+ // - CI_NAME: codeship | sourcehut
6875 // - GITLAB_CI | CIRCLECI | TRAVIS | APPVEYOR | BUILDKITE | DRONE | BITBUCKET_BUILD_NUMBER | AZURE_HTTP_USER_AGENT
6976
7077 return LEVEL_16COLORS ;
7178 }
7279
73- // 3 ) Detect unknown output or colors are not supported
74- if ( ! isTTY ( ) || term === 'dumb' ) return LEVEL_BW ;
80+ // 4 ) Detect unknown output environments.
81+ if ( ! ( isTTY ( ) || detectPM2 ( ) || detectNextJs ( ) ) ) return LEVEL_BW ;
7582
76- // 4 ) Truecolor support starts from Windows 10 build 14931 (2016-09-21), today we assume modern Windows is used
83+ // 5 ) Truecolor support since Windows 10 build 14931 (2016-09-21), today we assume modern Windows is used.
7784 if ( isWin ( ) ) return LEVEL_TRUECOLOR ;
7885
79- // 5) Detect terminals supporting 256 colors
80-
81- // Note: check for 256 colors after ENV variables such as TERM, COLORTERM.
86+ // 6) Detect 256-color support from TERM after COLORTERM and CI.
8287 // Terminals, that support 256 colors:
8388 // - screen-256color
8489 // - xterm-256color
@@ -90,16 +95,16 @@ let autoDetectLevel = (proc, env, envKeys) => {
9095 // - ansi-256color
9196 if ( / - 2 5 6 / . test ( term ) ) return LEVEL_256COLORS ;
9297
93- // 6) Defaults, 16-color output for unknown terminals,
94- // as all known terminals supporting 256 colors or truecolor have already been detected above .
95- // To enable truecolor in unknown terminals, set the ` COLORTERM=24bit` environment variable .
98+ // 7) Default to 16-color for the other supported output environments.
99+ // No previous check identified 256-color or truecolor support .
100+ // To enable truecolor in unknown terminals, set COLORTERM=24bit.
96101
97102 // Known terminals supporting 16 colors:
98103 // - xterm
99104 // - xterm-color
100105 // - screen-color
101106 // - ansi, ansi-x3.64, ansi.sysk
102- // - linux - Linux virtual console (tty1, tty2, SSH, etc.)
107+ // - linux - Linux virtual console (tty1, tty2, etc.)
103108 // - tmux - Terminal emulator
104109 // - tmux - Terminal tmux installed on macOS has `tmux-256color` name
105110 // - cygwin - Cygwin terminal
@@ -123,13 +128,13 @@ export const getLevel = (thisRef) => {
123128 let colorLevel = LEVEL_BW ;
124129
125130 try {
126- // keys(env) triggers a Deno permission request; throws if access is denied
127- // stringify environment variable keys to check for specific ones using a RegExp
131+ // keys(env) triggers a Deno permission request. Throws if access is denied.
132+ // Stringify environment variable keys to check for specific ones using a RegExp.
128133 let envKeys = SEPARATOR + keys ( env ) . join ( SEPARATOR ) ;
129134 colorLevel = autoDetectLevel ( proc , env , envKeys ) ;
130135 } catch ( error ) {
131- // if the permission is not granted, environment variables have no effect, even variables like FORCE_COLOR will be ignored
132- // env now points to a new empty object to avoid Deno requests for every env access in code below
136+ // If the permission is not granted, environment variables have no effect, even variables like FORCE_COLOR will be ignored.
137+ // ` env` now points to a new empty object to avoid Deno requests for every env access in code below.
133138 env = { } ;
134139 }
135140
0 commit comments