1'use strict'; 2const escapeStringRegexp = require('escape-string-regexp'); 3const ansiStyles = require('ansi-styles'); 4const stdoutColor = require('supports-color').stdout; 5 6const template = require('./templates.js'); 7 8const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); 9 10// `supportsColor.level` → `ansiStyles.color[name]` mapping 11const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; 12 13// `color-convert` models to exclude from the Chalk API due to conflicts and such 14const skipModels = new Set(['gray']); 15 16const styles = Object.create(null); 17 18function applyOptions(obj, options) { 19 options = options || {}; 20 21 // Detect level if not set manually 22 const scLevel = stdoutColor ? stdoutColor.level : 0; 23 obj.level = options.level === undefined ? scLevel : options.level; 24 obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; 25} 26 27function Chalk(options) { 28 // We check for this.template here since calling `chalk.constructor()` 29 // by itself will have a `this` of a previously constructed chalk object 30 if (!this || !(this instanceof Chalk) || this.template) { 31 const chalk = {}; 32 applyOptions(chalk, options); 33 34 chalk.template = function () { 35 const args = [].slice.call(arguments); 36 return chalkTag.apply(null, [chalk.template].concat(args)); 37 }; 38 39 Object.setPrototypeOf(chalk, Chalk.prototype); 40 Object.setPrototypeOf(chalk.template, chalk); 41 42 chalk.template.constructor = Chalk; 43 44 return chalk.template; 45 } 46 47 applyOptions(this, options); 48} 49 50// Use bright blue on Windows as the normal blue color is illegible 51if (isSimpleWindowsTerm) { 52 ansiStyles.blue.open = '\u001B[94m'; 53} 54 55for (const key of Object.keys(ansiStyles)) { 56 ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); 57 58 styles[key] = { 59 get() { 60 const codes = ansiStyles[key]; 61 return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); 62 } 63 }; 64} 65 66styles.visible = { 67 get() { 68 return build.call(this, this._styles || [], true, 'visible'); 69 } 70}; 71 72ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); 73for (const model of Object.keys(ansiStyles.color.ansi)) { 74 if (skipModels.has(model)) { 75 continue; 76 } 77 78 styles[model] = { 79 get() { 80 const level = this.level; 81 return function () { 82 const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); 83 const codes = { 84 open, 85 close: ansiStyles.color.close, 86 closeRe: ansiStyles.color.closeRe 87 }; 88 return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); 89 }; 90 } 91 }; 92} 93 94ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); 95for (const model of Object.keys(ansiStyles.bgColor.ansi)) { 96 if (skipModels.has(model)) { 97 continue; 98 } 99 100 const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); 101 styles[bgModel] = { 102 get() { 103 const level = this.level; 104 return function () { 105 const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); 106 const codes = { 107 open, 108 close: ansiStyles.bgColor.close, 109 closeRe: ansiStyles.bgColor.closeRe 110 }; 111 return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); 112 }; 113 } 114 }; 115} 116 117const proto = Object.defineProperties(() => {}, styles); 118 119function build(_styles, _empty, key) { 120 const builder = function () { 121 return applyStyle.apply(builder, arguments); 122 }; 123 124 builder._styles = _styles; 125 builder._empty = _empty; 126 127 const self = this; 128 129 Object.defineProperty(builder, 'level', { 130 enumerable: true, 131 get() { 132 return self.level; 133 }, 134 set(level) { 135 self.level = level; 136 } 137 }); 138 139 Object.defineProperty(builder, 'enabled', { 140 enumerable: true, 141 get() { 142 return self.enabled; 143 }, 144 set(enabled) { 145 self.enabled = enabled; 146 } 147 }); 148 149 // See below for fix regarding invisible grey/dim combination on Windows 150 builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; 151 152 // `__proto__` is used because we must return a function, but there is 153 // no way to create a function with a different prototype 154 builder.__proto__ = proto; // eslint-disable-line no-proto 155 156 return builder; 157} 158 159function applyStyle() { 160 // Support varags, but simply cast to string in case there's only one arg 161 const args = arguments; 162 const argsLen = args.length; 163 let str = String(arguments[0]); 164 165 if (argsLen === 0) { 166 return ''; 167 } 168 169 if (argsLen > 1) { 170 // Don't slice `arguments`, it prevents V8 optimizations 171 for (let a = 1; a < argsLen; a++) { 172 str += ' ' + args[a]; 173 } 174 } 175 176 if (!this.enabled || this.level <= 0 || !str) { 177 return this._empty ? '' : str; 178 } 179 180 // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, 181 // see https://github.com/chalk/chalk/issues/58 182 // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. 183 const originalDim = ansiStyles.dim.open; 184 if (isSimpleWindowsTerm && this.hasGrey) { 185 ansiStyles.dim.open = ''; 186 } 187 188 for (const code of this._styles.slice().reverse()) { 189 // Replace any instances already present with a re-opening code 190 // otherwise only the part of the string until said closing code 191 // will be colored, and the rest will simply be 'plain'. 192 str = code.open + str.replace(code.closeRe, code.open) + code.close; 193 194 // Close the styling before a linebreak and reopen 195 // after next line to fix a bleed issue on macOS 196 // https://github.com/chalk/chalk/pull/92 197 str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); 198 } 199 200 // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue 201 ansiStyles.dim.open = originalDim; 202 203 return str; 204} 205 206function chalkTag(chalk, strings) { 207 if (!Array.isArray(strings)) { 208 // If chalk() was called by itself or with a string, 209 // return the string itself as a string. 210 return [].slice.call(arguments, 1).join(' '); 211 } 212 213 const args = [].slice.call(arguments, 2); 214 const parts = [strings.raw[0]]; 215 216 for (let i = 1; i < strings.length; i++) { 217 parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); 218 parts.push(String(strings.raw[i])); 219 } 220 221 return template(chalk, parts.join('')); 222} 223 224Object.defineProperties(Chalk.prototype, styles); 225 226module.exports = Chalk(); // eslint-disable-line new-cap 227module.exports.supportsColor = stdoutColor; 228module.exports.default = module.exports; // For TypeScript 229