UNPKG

3.57 kBJavaScriptView Raw
1import process from 'node:process';
2import os from 'node:os';
3import tty from 'node:tty';
4
5// From: https://github.com/sindresorhus/has-flag/blob/main/index.js
6function hasFlag(flag, argv = process.argv) {
7 const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
8 const position = argv.indexOf(prefix + flag);
9 const terminatorPosition = argv.indexOf('--');
10 return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
11}
12
13const {env} = process;
14
15let flagForceColor;
16if (
17 hasFlag('no-color')
18 || hasFlag('no-colors')
19 || hasFlag('color=false')
20 || hasFlag('color=never')
21) {
22 flagForceColor = 0;
23} else if (
24 hasFlag('color')
25 || hasFlag('colors')
26 || hasFlag('color=true')
27 || hasFlag('color=always')
28) {
29 flagForceColor = 1;
30}
31
32function envForceColor() {
33 if ('FORCE_COLOR' in env) {
34 if (env.FORCE_COLOR === 'true') {
35 return 1;
36 }
37
38 if (env.FORCE_COLOR === 'false') {
39 return 0;
40 }
41
42 return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
43 }
44}
45
46function translateLevel(level) {
47 if (level === 0) {
48 return false;
49 }
50
51 return {
52 level,
53 hasBasic: true,
54 has256: level >= 2,
55 has16m: level >= 3,
56 };
57}
58
59function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {
60 const noFlagForceColor = envForceColor();
61 if (noFlagForceColor !== undefined) {
62 flagForceColor = noFlagForceColor;
63 }
64
65 const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
66
67 if (forceColor === 0) {
68 return 0;
69 }
70
71 if (sniffFlags) {
72 if (hasFlag('color=16m')
73 || hasFlag('color=full')
74 || hasFlag('color=truecolor')) {
75 return 3;
76 }
77
78 if (hasFlag('color=256')) {
79 return 2;
80 }
81 }
82
83 if (haveStream && !streamIsTTY && forceColor === undefined) {
84 return 0;
85 }
86
87 const min = forceColor || 0;
88
89 if (env.TERM === 'dumb') {
90 return min;
91 }
92
93 if (process.platform === 'win32') {
94 // Windows 10 build 10586 is the first Windows release that supports 256 colors.
95 // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
96 const osRelease = os.release().split('.');
97 if (
98 Number(osRelease[0]) >= 10
99 && Number(osRelease[2]) >= 10_586
100 ) {
101 return Number(osRelease[2]) >= 14_931 ? 3 : 2;
102 }
103
104 return 1;
105 }
106
107 if ('CI' in env) {
108 if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
109 return 1;
110 }
111
112 return min;
113 }
114
115 if ('TEAMCITY_VERSION' in env) {
116 return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
117 }
118
119 // Check for Azure DevOps pipelines
120 if ('TF_BUILD' in env && 'AGENT_NAME' in env) {
121 return 1;
122 }
123
124 if (env.COLORTERM === 'truecolor') {
125 return 3;
126 }
127
128 if ('TERM_PROGRAM' in env) {
129 const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
130
131 switch (env.TERM_PROGRAM) {
132 case 'iTerm.app':
133 return version >= 3 ? 3 : 2;
134 case 'Apple_Terminal':
135 return 2;
136 // No default
137 }
138 }
139
140 if (/-256(color)?$/i.test(env.TERM)) {
141 return 2;
142 }
143
144 if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
145 return 1;
146 }
147
148 if ('COLORTERM' in env) {
149 return 1;
150 }
151
152 return min;
153}
154
155export function createSupportsColor(stream, options = {}) {
156 const level = _supportsColor(stream, {
157 streamIsTTY: stream && stream.isTTY,
158 ...options,
159 });
160
161 return translateLevel(level);
162}
163
164const supportsColor = {
165 stdout: createSupportsColor({isTTY: tty.isatty(1)}),
166 stderr: createSupportsColor({isTTY: tty.isatty(2)}),
167};
168
169export default supportsColor;