1'use strict'; 2const ansiStyles = require('ansi-styles'); 3const {stdout: stdoutColor, stderr: stderrColor} = require('supports-color'); 4const { 5 stringReplaceAll, 6 stringEncaseCRLFWithFirstIndex 7} = require('./util'); 8 9const {isArray} = Array; 10 11// `supportsColor.level` → `ansiStyles.color[name]` mapping 12const levelMapping = [ 13 'ansi', 14 'ansi', 15 'ansi256', 16 'ansi16m' 17]; 18 19const styles = Object.create(null); 20 21const applyOptions = (object, options = {}) => { 22 if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { 23 throw new Error('The `level` option should be an integer from 0 to 3'); 24 } 25 26 // Detect level if not set manually 27 const colorLevel = stdoutColor ? stdoutColor.level : 0; 28 object.level = options.level === undefined ? colorLevel : options.level; 29}; 30 31class ChalkClass { 32 constructor(options) { 33 // eslint-disable-next-line no-constructor-return 34 return chalkFactory(options); 35 } 36} 37 38const chalkFactory = options => { 39 const chalk = {}; 40 applyOptions(chalk, options); 41 42 chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_); 43 44 Object.setPrototypeOf(chalk, Chalk.prototype); 45 Object.setPrototypeOf(chalk.template, chalk); 46 47 chalk.template.constructor = () => { 48 throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.'); 49 }; 50 51 chalk.template.Instance = ChalkClass; 52 53 return chalk.template; 54}; 55 56function Chalk(options) { 57 return chalkFactory(options); 58} 59 60for (const [styleName, style] of Object.entries(ansiStyles)) { 61 styles[styleName] = { 62 get() { 63 const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); 64 Object.defineProperty(this, styleName, {value: builder}); 65 return builder; 66 } 67 }; 68} 69 70styles.visible = { 71 get() { 72 const builder = createBuilder(this, this._styler, true); 73 Object.defineProperty(this, 'visible', {value: builder}); 74 return builder; 75 } 76}; 77 78const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256']; 79 80for (const model of usedModels) { 81 styles[model] = { 82 get() { 83 const {level} = this; 84 return function (...arguments_) { 85 const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler); 86 return createBuilder(this, styler, this._isEmpty); 87 }; 88 } 89 }; 90} 91 92for (const model of usedModels) { 93 const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); 94 styles[bgModel] = { 95 get() { 96 const {level} = this; 97 return function (...arguments_) { 98 const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler); 99 return createBuilder(this, styler, this._isEmpty); 100 }; 101 } 102 }; 103} 104 105const proto = Object.defineProperties(() => {}, { 106 ...styles, 107 level: { 108 enumerable: true, 109 get() { 110 return this._generator.level; 111 }, 112 set(level) { 113 this._generator.level = level; 114 } 115 } 116}); 117 118const createStyler = (open, close, parent) => { 119 let openAll; 120 let closeAll; 121 if (parent === undefined) { 122 openAll = open; 123 closeAll = close; 124 } else { 125 openAll = parent.openAll + open; 126 closeAll = close + parent.closeAll; 127 } 128 129 return { 130 open, 131 close, 132 openAll, 133 closeAll, 134 parent 135 }; 136}; 137 138const createBuilder = (self, _styler, _isEmpty) => { 139 const builder = (...arguments_) => { 140 if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) { 141 // Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}` 142 return applyStyle(builder, chalkTag(builder, ...arguments_)); 143 } 144 145 // Single argument is hot path, implicit coercion is faster than anything 146 // eslint-disable-next-line no-implicit-coercion 147 return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); 148 }; 149 150 // We alter the prototype because we must return a function, but there is 151 // no way to create a function with a different prototype 152 Object.setPrototypeOf(builder, proto); 153 154 builder._generator = self; 155 builder._styler = _styler; 156 builder._isEmpty = _isEmpty; 157 158 return builder; 159}; 160 161const applyStyle = (self, string) => { 162 if (self.level <= 0 || !string) { 163 return self._isEmpty ? '' : string; 164 } 165 166 let styler = self._styler; 167 168 if (styler === undefined) { 169 return string; 170 } 171 172 const {openAll, closeAll} = styler; 173 if (string.indexOf('\u001B') !== -1) { 174 while (styler !== undefined) { 175 // Replace any instances already present with a re-opening code 176 // otherwise only the part of the string until said closing code 177 // will be colored, and the rest will simply be 'plain'. 178 string = stringReplaceAll(string, styler.close, styler.open); 179 180 styler = styler.parent; 181 } 182 } 183 184 // We can move both next actions out of loop, because remaining actions in loop won't have 185 // any/visible effect on parts we add here. Close the styling before a linebreak and reopen 186 // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 187 const lfIndex = string.indexOf('\n'); 188 if (lfIndex !== -1) { 189 string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); 190 } 191 192 return openAll + string + closeAll; 193}; 194 195let template; 196const chalkTag = (chalk, ...strings) => { 197 const [firstString] = strings; 198 199 if (!isArray(firstString) || !isArray(firstString.raw)) { 200 // If chalk() was called by itself or with a string, 201 // return the string itself as a string. 202 return strings.join(' '); 203 } 204 205 const arguments_ = strings.slice(1); 206 const parts = [firstString.raw[0]]; 207 208 for (let i = 1; i < firstString.length; i++) { 209 parts.push( 210 String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'), 211 String(firstString.raw[i]) 212 ); 213 } 214 215 if (template === undefined) { 216 template = require('./templates'); 217 } 218 219 return template(chalk, parts.join('')); 220}; 221 222Object.defineProperties(Chalk.prototype, styles); 223 224const chalk = Chalk(); // eslint-disable-line new-cap 225chalk.supportsColor = stdoutColor; 226chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap 227chalk.stderr.supportsColor = stderrColor; 228 229module.exports = chalk; 230