index.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. 'use strict';
  2. const os = require('os');
  3. const tty = require('tty');
  4. const hasFlag = require('has-flag');
  5. const {env} = process;
  6. let flagForceColor;
  7. if (hasFlag('no-color') ||
  8. hasFlag('no-colors') ||
  9. hasFlag('color=false') ||
  10. hasFlag('color=never')) {
  11. flagForceColor = 0;
  12. } else if (hasFlag('color') ||
  13. hasFlag('colors') ||
  14. hasFlag('color=true') ||
  15. hasFlag('color=always')) {
  16. flagForceColor = 1;
  17. }
  18. function envForceColor() {
  19. if ('FORCE_COLOR' in env) {
  20. if (env.FORCE_COLOR === 'true') {
  21. return 1;
  22. }
  23. if (env.FORCE_COLOR === 'false') {
  24. return 0;
  25. }
  26. return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
  27. }
  28. }
  29. function translateLevel(level) {
  30. if (level === 0) {
  31. return false;
  32. }
  33. return {
  34. level,
  35. hasBasic: true,
  36. has256: level >= 2,
  37. has16m: level >= 3
  38. };
  39. }
  40. function supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {
  41. const noFlagForceColor = envForceColor();
  42. if (noFlagForceColor !== undefined) {
  43. flagForceColor = noFlagForceColor;
  44. }
  45. const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
  46. if (forceColor === 0) {
  47. return 0;
  48. }
  49. if (sniffFlags) {
  50. if (hasFlag('color=16m') ||
  51. hasFlag('color=full') ||
  52. hasFlag('color=truecolor')) {
  53. return 3;
  54. }
  55. if (hasFlag('color=256')) {
  56. return 2;
  57. }
  58. }
  59. if (haveStream && !streamIsTTY && forceColor === undefined) {
  60. return 0;
  61. }
  62. const min = forceColor || 0;
  63. if (env.TERM === 'dumb') {
  64. return min;
  65. }
  66. if (process.platform === 'win32') {
  67. // Windows 10 build 10586 is the first Windows release that supports 256 colors.
  68. // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
  69. const osRelease = os.release().split('.');
  70. if (
  71. Number(osRelease[0]) >= 10 &&
  72. Number(osRelease[2]) >= 10586
  73. ) {
  74. return Number(osRelease[2]) >= 14931 ? 3 : 2;
  75. }
  76. return 1;
  77. }
  78. if ('CI' in env) {
  79. if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
  80. return 1;
  81. }
  82. return min;
  83. }
  84. if ('TEAMCITY_VERSION' in env) {
  85. return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
  86. }
  87. if (env.COLORTERM === 'truecolor') {
  88. return 3;
  89. }
  90. if ('TERM_PROGRAM' in env) {
  91. const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
  92. switch (env.TERM_PROGRAM) {
  93. case 'iTerm.app':
  94. return version >= 3 ? 3 : 2;
  95. case 'Apple_Terminal':
  96. return 2;
  97. // No default
  98. }
  99. }
  100. if (/-256(color)?$/i.test(env.TERM)) {
  101. return 2;
  102. }
  103. if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
  104. return 1;
  105. }
  106. if ('COLORTERM' in env) {
  107. return 1;
  108. }
  109. return min;
  110. }
  111. function getSupportLevel(stream, options = {}) {
  112. const level = supportsColor(stream, {
  113. streamIsTTY: stream && stream.isTTY,
  114. ...options
  115. });
  116. return translateLevel(level);
  117. }
  118. module.exports = {
  119. supportsColor: getSupportLevel,
  120. stdout: getSupportLevel({isTTY: tty.isatty(1)}),
  121. stderr: getSupportLevel({isTTY: tty.isatty(2)})
  122. };