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 ERR_INVALID_ARG_TYPE, 27 ERR_OUT_OF_RANGE 28} = require('internal/errors').codes; 29 30let OSRelease; 31 32const COLORS_2 = 1; 33const COLORS_16 = 4; 34const COLORS_256 = 8; 35const COLORS_16m = 24; 36 37// Some entries were taken from `dircolors` 38// (https://linux.die.net/man/1/dircolors). The corresponding terminals might 39// support more than 16 colors, but this was not tested for. 40// 41// Copyright (C) 1996-2016 Free Software Foundation, Inc. Copying and 42// distribution of this file, with or without modification, are permitted 43// provided the copyright notice and this notice are preserved. 44const TERM_ENVS = { 45 'eterm': COLORS_16, 46 'cons25': COLORS_16, 47 'console': COLORS_16, 48 'cygwin': COLORS_16, 49 'dtterm': COLORS_16, 50 'gnome': COLORS_16, 51 'hurd': COLORS_16, 52 'jfbterm': COLORS_16, 53 'konsole': COLORS_16, 54 'kterm': COLORS_16, 55 'mlterm': COLORS_16, 56 'mosh': COLORS_16m, 57 'putty': COLORS_16, 58 'st': COLORS_16, 59 // https://github.com/da-x/rxvt-unicode/tree/v9.22-with-24bit-color 60 'rxvt-unicode-24bit': COLORS_16m, 61 // https://gist.github.com/XVilka/8346728#gistcomment-2823421 62 'terminator': COLORS_16m 63}; 64 65const TERM_ENVS_REG_EXP = [ 66 /ansi/, 67 /color/, 68 /linux/, 69 /^con[0-9]*x[0-9]/, 70 /^rxvt/, 71 /^screen/, 72 /^xterm/, 73 /^vt100/ 74]; 75 76let warned = false; 77function warnOnDeactivatedColors(env) { 78 if (warned) 79 return; 80 let name = ''; 81 if (env.NODE_DISABLE_COLORS !== undefined) 82 name = 'NODE_DISABLE_COLORS'; 83 if (env.NO_COLOR !== undefined) { 84 if (name !== '') { 85 name += "' and '"; 86 } 87 name += 'NO_COLOR'; 88 } 89 90 if (name !== '') { 91 process.emitWarning( 92 `The '${name}' env is ignored due to the 'FORCE_COLOR' env being set.`, 93 'Warning' 94 ); 95 warned = true; 96 } 97} 98 99// The `getColorDepth` API got inspired by multiple sources such as 100// https://github.com/chalk/supports-color, 101// https://github.com/isaacs/color-support. 102function getColorDepth(env = process.env) { 103 // Use level 0-3 to support the same levels as `chalk` does. This is done for 104 // consistency throughout the ecosystem. 105 if (env.FORCE_COLOR !== undefined) { 106 switch (env.FORCE_COLOR) { 107 case '': 108 case '1': 109 case 'true': 110 warnOnDeactivatedColors(env); 111 return COLORS_16; 112 case '2': 113 warnOnDeactivatedColors(env); 114 return COLORS_256; 115 case '3': 116 warnOnDeactivatedColors(env); 117 return COLORS_16m; 118 default: 119 return COLORS_2; 120 } 121 } 122 123 if (env.NODE_DISABLE_COLORS !== undefined || 124 // See https://no-color.org/ 125 env.NO_COLOR !== undefined || 126 // The "dumb" special terminal, as defined by terminfo, doesn't support 127 // ANSI color control codes. 128 // See https://invisible-island.net/ncurses/terminfo.ti.html#toc-_Specials 129 env.TERM === 'dumb') { 130 return COLORS_2; 131 } 132 133 if (process.platform === 'win32') { 134 // Lazy load for startup performance. 135 if (OSRelease === undefined) { 136 const { release } = require('os'); 137 OSRelease = release().split('.'); 138 } 139 // Windows 10 build 10586 is the first Windows release that supports 256 140 // colors. Windows 10 build 14931 is the first release that supports 141 // 16m/TrueColor. 142 if (+OSRelease[0] >= 10) { 143 const build = +OSRelease[2]; 144 if (build >= 14931) 145 return COLORS_16m; 146 if (build >= 10586) 147 return COLORS_256; 148 } 149 150 return COLORS_16; 151 } 152 153 if (env.TMUX) { 154 return COLORS_256; 155 } 156 157 if (env.CI) { 158 if ('TRAVIS' in env || 'CIRCLECI' in env || 'APPVEYOR' in env || 159 'GITLAB_CI' in env || env.CI_NAME === 'codeship') { 160 return COLORS_256; 161 } 162 return COLORS_2; 163 } 164 165 if ('TEAMCITY_VERSION' in env) { 166 return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 167 COLORS_16 : COLORS_2; 168 } 169 170 switch (env.TERM_PROGRAM) { 171 case 'iTerm.app': 172 if (!env.TERM_PROGRAM_VERSION || 173 /^[0-2]\./.test(env.TERM_PROGRAM_VERSION)) { 174 return COLORS_256; 175 } 176 return COLORS_16m; 177 case 'HyperTerm': 178 case 'MacTerm': 179 return COLORS_16m; 180 case 'Apple_Terminal': 181 return COLORS_256; 182 } 183 184 if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') { 185 return COLORS_16m; 186 } 187 188 if (env.TERM) { 189 if (/^xterm-256/.test(env.TERM)) 190 return COLORS_256; 191 192 const termEnv = env.TERM.toLowerCase(); 193 194 if (TERM_ENVS[termEnv]) { 195 return TERM_ENVS[termEnv]; 196 } 197 for (const term of TERM_ENVS_REG_EXP) { 198 if (term.test(termEnv)) { 199 return COLORS_16; 200 } 201 } 202 } 203 // Move 16 color COLORTERM below 16m and 256 204 if (env.COLORTERM) { 205 return COLORS_16; 206 } 207 return COLORS_2; 208} 209 210function hasColors(count, env) { 211 if (env === undefined && 212 (count === undefined || (typeof count === 'object' && count !== null))) { 213 env = count; 214 count = 16; 215 } else { 216 if (typeof count !== 'number') { 217 throw new ERR_INVALID_ARG_TYPE('count', 'number', count); 218 } 219 if (count < 2) { 220 throw new ERR_OUT_OF_RANGE('count', '>= 2', count); 221 } 222 } 223 return count <= 2 ** getColorDepth(env); 224} 225 226module.exports = { 227 getColorDepth, 228 hasColors 229}; 230