UNPKG

supports-color

Version:

Detect whether a terminal supports color

213 lines (168 loc) 4.47 kB
import process from 'node:process'; import os from 'node:os'; import tty from 'node:tty'; // From: https://github.com/sindresorhus/has-flag/blob/main/index.js /// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) { function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process.argv) { const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf('--'); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } const {env} = process; let flagForceColor; if ( hasFlag('no-color') || hasFlag('no-colors') || hasFlag('color=false') || hasFlag('color=never') ) { flagForceColor = 0; } else if ( hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always') ) { flagForceColor = 1; } function envForceColor() { if (!('FORCE_COLOR' in env)) { return; } if (env.FORCE_COLOR === 'true') { return 1; } if (env.FORCE_COLOR === 'false') { return 0; } if (env.FORCE_COLOR.length === 0) { return 1; } if (!/^\d+$/v.test(env.FORCE_COLOR)) { return; } const level = Math.min(Number(env.FORCE_COLOR), 3); if (![0, 1, 2, 3].includes(level)) { return; } return level; } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3, }; } // eslint-disable-next-line complexity -- Color support detection necessarily handles multiple environments. function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== undefined) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag('color=16m') || hasFlag('color=full') || hasFlag('color=truecolor')) { return 3; } if (hasFlag('color=256')) { return 2; } } // A numeric `FORCE_COLOR` requests an exact level, while `FORCE_COLOR=true` and `FORCE_COLOR=` only enable color and let the level be detected. if (forceColor !== undefined && /^\d+$/v.test(env.FORCE_COLOR)) { return forceColor; } // Check for Azure DevOps pipelines. // Has to be above the `!streamIsTTY` check. if ('TF_BUILD' in env && 'AGENT_NAME' in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === undefined) { return 0; } const min = forceColor || 0; if (env.TERM === 'dumb') { return min; } if (process.platform === 'win32') { // Windows 10 build 10586 is the first Windows release that supports 256 colors. // Windows 10 build 14931 is the first release that supports 16m/TrueColor. const osRelease = os.release().split('.'); if ( Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10_586 ) { return Number(osRelease[2]) >= 14_931 ? 3 : 2; } return 1; } if ('CI' in env) { if (['GITHUB_ACTIONS', 'GITEA_ACTIONS', 'CIRCLECI'].some(key => Object.hasOwn(env, key))) { return 3; } if (['TRAVIS', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => Object.hasOwn(env, sign)) || env.CI_NAME === 'codeship') { return 1; } return min; } if ('TEAMCITY_VERSION' in env) { return /^(?:9\.0*[1-9]\d*\.|\d{2,}\.)/v.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === 'truecolor') { return 3; } if (env.TERM === 'xterm-kitty') { return 3; } if (env.TERM === 'xterm-ghostty') { return 3; } if (env.TERM === 'wezterm') { return 3; } if ('TERM_PROGRAM' in env) { const version = Number((env.TERM_PROGRAM_VERSION || '').split('.', 1)[0]); switch (env.TERM_PROGRAM) { case 'iTerm.app': { return version >= 3 ? 3 : 2; } case 'Apple_Terminal': { return 2; } // No default } } if (/-256(?:color)?$/iv.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/iv.test(env.TERM)) { return 1; } if ('COLORTERM' in env) { return 1; } return min; } export function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, { streamIsTTY: stream && stream.isTTY, ...options, }); return translateLevel(level); } const supportsColor = { stdout: createSupportsColor({isTTY: tty.isatty(1)}), stderr: createSupportsColor({isTTY: tty.isatty(2)}), }; export default supportsColor;