• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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