1// MIT License 2 3// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) 4 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to deal 7// in the Software without restriction, including without limitation the rights 8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9// copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21// SOFTWARE. 22 23'use strict'; 24 25const { 26 ArrayPrototypeSome, 27 RegExpPrototypeExec, 28 StringPrototypeSplit, 29 StringPrototypeToLowerCase, 30} = primordials; 31 32const { validateInteger } = require('internal/validators'); 33 34let OSRelease; 35 36const COLORS_2 = 1; 37const COLORS_16 = 4; 38const COLORS_256 = 8; 39const COLORS_16m = 24; 40 41// Some entries were taken from `dircolors` 42// (https://linux.die.net/man/1/dircolors). The corresponding terminals might 43// support more than 16 colors, but this was not tested for. 44// 45// Copyright (C) 1996-2016 Free Software Foundation, Inc. Copying and 46// distribution of this file, with or without modification, are permitted 47// provided the copyright notice and this notice are preserved. 48const TERM_ENVS = { 49 'eterm': COLORS_16, 50 'cons25': COLORS_16, 51 'console': COLORS_16, 52 'cygwin': COLORS_16, 53 'dtterm': COLORS_16, 54 'gnome': COLORS_16, 55 'hurd': COLORS_16, 56 'jfbterm': COLORS_16, 57 'konsole': COLORS_16, 58 'kterm': COLORS_16, 59 'mlterm': COLORS_16, 60 'mosh': COLORS_16m, 61 'putty': COLORS_16, 62 'st': COLORS_16, 63 // https://github.com/da-x/rxvt-unicode/tree/v9.22-with-24bit-color 64 'rxvt-unicode-24bit': COLORS_16m, 65 // https://gist.github.com/XVilka/8346728#gistcomment-2823421 66 'terminator': COLORS_16m, 67}; 68 69const TERM_ENVS_REG_EXP = [ 70 /ansi/, 71 /color/, 72 /linux/, 73 /^con[0-9]*x[0-9]/, 74 /^rxvt/, 75 /^screen/, 76 /^xterm/, 77 /^vt100/, 78]; 79 80let warned = false; 81function warnOnDeactivatedColors(env) { 82 if (warned) 83 return; 84 let name = ''; 85 if (env.NODE_DISABLE_COLORS !== undefined) 86 name = 'NODE_DISABLE_COLORS'; 87 if (env.NO_COLOR !== undefined) { 88 if (name !== '') { 89 name += "' and '"; 90 } 91 name += 'NO_COLOR'; 92 } 93 94 if (name !== '') { 95 process.emitWarning( 96 `The '${name}' env is ignored due to the 'FORCE_COLOR' env being set.`, 97 'Warning', 98 ); 99 warned = true; 100 } 101} 102 103// The `getColorDepth` API got inspired by multiple sources such as 104// https://github.com/chalk/supports-color, 105// https://github.com/isaacs/color-support. 106function getColorDepth(env = process.env) { 107 // Use level 0-3 to support the same levels as `chalk` does. This is done for 108 // consistency throughout the ecosystem. 109 if (env.FORCE_COLOR !== undefined) { 110 switch (env.FORCE_COLOR) { 111 case '': 112 case '1': 113 case 'true': 114 warnOnDeactivatedColors(env); 115 return COLORS_16; 116 case '2': 117 warnOnDeactivatedColors(env); 118 return COLORS_256; 119 case '3': 120 warnOnDeactivatedColors(env); 121 return COLORS_16m; 122 default: 123 return COLORS_2; 124 } 125 } 126 127 if (env.NODE_DISABLE_COLORS !== undefined || 128 // See https://no-color.org/ 129 env.NO_COLOR !== undefined || 130 // The "dumb" special terminal, as defined by terminfo, doesn't support 131 // ANSI color control codes. 132 // See https://invisible-island.net/ncurses/terminfo.ti.html#toc-_Specials 133 env.TERM === 'dumb') { 134 return COLORS_2; 135 } 136 137 if (process.platform === 'win32') { 138 // Lazy load for startup performance. 139 if (OSRelease === undefined) { 140 const { release } = require('os'); 141 OSRelease = StringPrototypeSplit(release(), '.'); 142 } 143 // Windows 10 build 10586 is the first Windows release that supports 256 144 // colors. Windows 10 build 14931 is the first release that supports 145 // 16m/TrueColor. 146 if (+OSRelease[0] >= 10) { 147 const build = +OSRelease[2]; 148 if (build >= 14931) 149 return COLORS_16m; 150 if (build >= 10586) 151 return COLORS_256; 152 } 153 154 return COLORS_16; 155 } 156 157 if (env.TMUX) { 158 return COLORS_256; 159 } 160 161 if (env.CI) { 162 if ([ 163 'APPVEYOR', 164 'BUILDKITE', 165 'CIRCLECI', 166 'DRONE', 167 'GITHUB_ACTIONS', 168 'GITLAB_CI', 169 'TRAVIS', 170 ].some((sign) => sign in env) || env.CI_NAME === 'codeship') { 171 return COLORS_256; 172 } 173 return COLORS_2; 174 } 175 176 if ('TEAMCITY_VERSION' in env) { 177 return RegExpPrototypeExec(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/, env.TEAMCITY_VERSION) !== null ? 178 COLORS_16 : COLORS_2; 179 } 180 181 switch (env.TERM_PROGRAM) { 182 case 'iTerm.app': 183 if (!env.TERM_PROGRAM_VERSION || 184 RegExpPrototypeExec(/^[0-2]\./, env.TERM_PROGRAM_VERSION) !== null 185 ) { 186 return COLORS_256; 187 } 188 return COLORS_16m; 189 case 'HyperTerm': 190 case 'MacTerm': 191 return COLORS_16m; 192 case 'Apple_Terminal': 193 return COLORS_256; 194 } 195 196 if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') { 197 return COLORS_16m; 198 } 199 200 if (env.TERM) { 201 if (RegExpPrototypeExec(/^xterm-256/, env.TERM) !== null) { 202 return COLORS_256; 203 } 204 205 const termEnv = StringPrototypeToLowerCase(env.TERM); 206 207 if (TERM_ENVS[termEnv]) { 208 return TERM_ENVS[termEnv]; 209 } 210 if (ArrayPrototypeSome(TERM_ENVS_REG_EXP, 211 (term) => RegExpPrototypeExec(term, termEnv) !== null)) { 212 return COLORS_16; 213 } 214 } 215 // Move 16 color COLORTERM below 16m and 256 216 if (env.COLORTERM) { 217 return COLORS_16; 218 } 219 return COLORS_2; 220} 221 222function hasColors(count, env) { 223 if (env === undefined && 224 (count === undefined || (typeof count === 'object' && count !== null))) { 225 env = count; 226 count = 16; 227 } else { 228 validateInteger(count, 'count', 2); 229 } 230 231 return count <= 2 ** getColorDepth(env); 232} 233 234module.exports = { 235 getColorDepth, 236 hasColors, 237}; 238