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