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 RegExpPrototypeTest, 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 ('TRAVIS' in env || 'CIRCLECI' in env || 'APPVEYOR' in env || 163 'GITLAB_CI' in env || env.CI_NAME === 'codeship') { 164 return COLORS_256; 165 } 166 return COLORS_2; 167 } 168 169 if ('TEAMCITY_VERSION' in env) { 170 return RegExpPrototypeTest(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/, env.TEAMCITY_VERSION) ? 171 COLORS_16 : COLORS_2; 172 } 173 174 switch (env.TERM_PROGRAM) { 175 case 'iTerm.app': 176 if (!env.TERM_PROGRAM_VERSION || 177 RegExpPrototypeTest(/^[0-2]\./, env.TERM_PROGRAM_VERSION) 178 ) { 179 return COLORS_256; 180 } 181 return COLORS_16m; 182 case 'HyperTerm': 183 case 'MacTerm': 184 return COLORS_16m; 185 case 'Apple_Terminal': 186 return COLORS_256; 187 } 188 189 if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') { 190 return COLORS_16m; 191 } 192 193 if (env.TERM) { 194 if (RegExpPrototypeTest(/^xterm-256/, env.TERM)) { 195 return COLORS_256; 196 } 197 198 const termEnv = StringPrototypeToLowerCase(env.TERM); 199 200 if (TERM_ENVS[termEnv]) { 201 return TERM_ENVS[termEnv]; 202 } 203 if (ArrayPrototypeSome(TERM_ENVS_REG_EXP, 204 (term) => RegExpPrototypeTest(term, termEnv))) { 205 return COLORS_16; 206 } 207 } 208 // Move 16 color COLORTERM below 16m and 256 209 if (env.COLORTERM) { 210 return COLORS_16; 211 } 212 return COLORS_2; 213} 214 215function hasColors(count, env) { 216 if (env === undefined && 217 (count === undefined || (typeof count === 'object' && count !== null))) { 218 env = count; 219 count = 16; 220 } else { 221 validateInteger(count, 'count', 2); 222 } 223 224 return count <= 2 ** getColorDepth(env); 225} 226 227module.exports = { 228 getColorDepth, 229 hasColors 230}; 231