1'use strict'; 2const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; 3const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; 4const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; 5const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; 6 7const ESCAPES = new Map([ 8 ['n', '\n'], 9 ['r', '\r'], 10 ['t', '\t'], 11 ['b', '\b'], 12 ['f', '\f'], 13 ['v', '\v'], 14 ['0', '\0'], 15 ['\\', '\\'], 16 ['e', '\u001B'], 17 ['a', '\u0007'] 18]); 19 20function unescape(c) { 21 if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { 22 return String.fromCharCode(parseInt(c.slice(1), 16)); 23 } 24 25 return ESCAPES.get(c) || c; 26} 27 28function parseArguments(name, args) { 29 const results = []; 30 const chunks = args.trim().split(/\s*,\s*/g); 31 let matches; 32 33 for (const chunk of chunks) { 34 if (!isNaN(chunk)) { 35 results.push(Number(chunk)); 36 } else if ((matches = chunk.match(STRING_REGEX))) { 37 results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); 38 } else { 39 throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); 40 } 41 } 42 43 return results; 44} 45 46function parseStyle(style) { 47 STYLE_REGEX.lastIndex = 0; 48 49 const results = []; 50 let matches; 51 52 while ((matches = STYLE_REGEX.exec(style)) !== null) { 53 const name = matches[1]; 54 55 if (matches[2]) { 56 const args = parseArguments(name, matches[2]); 57 results.push([name].concat(args)); 58 } else { 59 results.push([name]); 60 } 61 } 62 63 return results; 64} 65 66function buildStyle(chalk, styles) { 67 const enabled = {}; 68 69 for (const layer of styles) { 70 for (const style of layer.styles) { 71 enabled[style[0]] = layer.inverse ? null : style.slice(1); 72 } 73 } 74 75 let current = chalk; 76 for (const styleName of Object.keys(enabled)) { 77 if (Array.isArray(enabled[styleName])) { 78 if (!(styleName in current)) { 79 throw new Error(`Unknown Chalk style: ${styleName}`); 80 } 81 82 if (enabled[styleName].length > 0) { 83 current = current[styleName].apply(current, enabled[styleName]); 84 } else { 85 current = current[styleName]; 86 } 87 } 88 } 89 90 return current; 91} 92 93module.exports = (chalk, tmp) => { 94 const styles = []; 95 const chunks = []; 96 let chunk = []; 97 98 // eslint-disable-next-line max-params 99 tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { 100 if (escapeChar) { 101 chunk.push(unescape(escapeChar)); 102 } else if (style) { 103 const str = chunk.join(''); 104 chunk = []; 105 chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); 106 styles.push({inverse, styles: parseStyle(style)}); 107 } else if (close) { 108 if (styles.length === 0) { 109 throw new Error('Found extraneous } in Chalk template literal'); 110 } 111 112 chunks.push(buildStyle(chalk, styles)(chunk.join(''))); 113 chunk = []; 114 styles.pop(); 115 } else { 116 chunk.push(chr); 117 } 118 }); 119 120 chunks.push(chunk.join('')); 121 122 if (styles.length > 0) { 123 const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; 124 throw new Error(errMsg); 125 } 126 127 return chunks.join(''); 128}; 129