@@ -114,7 +114,11 @@ const kEscapeEnd = 'm';
114114const kDimCode = 2 ;
115115const kBoldCode = 1 ;
116116
117+ // Close sequence for 24-bit foreground colors (reset to default foreground)
118+ const kHexCloseSeq = kEscape + '39' + kEscapeEnd ;
119+
117120let styleCache ;
121+ const hexStyleCache = { __proto__ : null } ;
118122
119123function getStyleCache ( ) {
120124 if ( styleCache === undefined ) {
@@ -137,6 +141,25 @@ function getStyleCache() {
137141 return styleCache ;
138142}
139143
144+ /**
145+ * Returns the cached ANSI escape sequences for a hex color.
146+ * Computes and caches on first use to avoid repeated Buffer allocations.
147+ * @param {string } hex A valid hex color string (#RGB or #RRGGBB)
148+ * @returns {{openSeq: string, closeSeq: string} }
149+ */
150+ function getHexStyle ( hex ) {
151+ const cached = hexStyleCache [ hex ] ;
152+ if ( cached !== undefined ) return cached ;
153+ const { 0 : r , 1 : g , 2 : b } = hexToRgb ( hex ) ;
154+ const style = {
155+ __proto__ : null ,
156+ openSeq : kEscape + rgbToAnsi24Bit ( r , g , b ) + kEscapeEnd ,
157+ closeSeq : kHexCloseSeq ,
158+ } ;
159+ hexStyleCache [ hex ] = style ;
160+ return style ;
161+ }
162+
140163function replaceCloseCode ( str , closeSeq , openSeq , keepClose ) {
141164 const closeLen = closeSeq . length ;
142165 let index = str . indexOf ( closeSeq ) ;
@@ -163,15 +186,6 @@ function replaceCloseCode(str, closeSeq, openSeq, keepClose) {
163186// Matches #RGB or #RRGGBB
164187const hexColorRegExp = / ^ # (?: [ 0 - 9 a - f A - F ] { 3 } | [ 0 - 9 a - f A - F ] { 6 } ) $ / ;
165188
166- /**
167- * Validates whether a string is a valid hex color code.
168- * @param {string } hex The hex string to validate (e.g., '#fff' or '#ffffff')
169- * @returns {boolean } True if valid hex color, false otherwise
170- */
171- function isValidHexColor ( hex ) {
172- return typeof hex === 'string' && RegExpPrototypeExec ( hexColorRegExp , hex ) !== null ;
173- }
174-
175189/**
176190 * Parses a hex color string into RGB components.
177191 * Supports both 3-digit (#RGB) and 6-digit (#RRGGBB) formats.
@@ -225,6 +239,17 @@ function styleText(format, text, options) {
225239 const processed = replaceCloseCode ( text , style . closeSeq , style . openSeq , style . keepClose ) ;
226240 return style . openSeq + processed + style . closeSeq ;
227241 }
242+
243+ if ( format [ 0 ] === '#' ) {
244+ let hexStyle = hexStyleCache [ format ] ;
245+ if ( hexStyle === undefined && RegExpPrototypeExec ( hexColorRegExp , format ) !== null ) {
246+ hexStyle = getHexStyle ( format ) ;
247+ }
248+ if ( hexStyle !== undefined ) {
249+ const processed = replaceCloseCode ( text , hexStyle . closeSeq , hexStyle . openSeq , false ) ;
250+ return hexStyle . openSeq + processed + hexStyle . closeSeq ;
251+ }
252+ }
228253 }
229254
230255 validateString ( text , 'text' ) ;
@@ -255,24 +280,24 @@ function styleText(format, text, options) {
255280 for ( const key of formatArray ) {
256281 if ( key === 'none' ) continue ;
257282
258- if ( isValidHexColor ( key ) ) {
283+ if ( typeof key === 'string' && key [ 0 ] === '#' ) {
284+ let hexStyle = hexStyleCache [ key ] ;
285+ if ( hexStyle === undefined ) {
286+ if ( RegExpPrototypeExec ( hexColorRegExp , key ) === null ) {
287+ throw new ERR_INVALID_ARG_VALUE ( 'format' , key ,
288+ 'must be a valid hex color (#RGB or #RRGGBB)' ) ;
289+ }
290+ hexStyle = getHexStyle ( key ) ;
291+ }
259292 if ( skipColorize ) continue ;
260- const { 0 : r , 1 : g , 2 : b } = hexToRgb ( key ) ;
261- const openSeq = kEscape + rgbToAnsi24Bit ( r , g , b ) + kEscapeEnd ;
262- const closeSeq = kEscape + '39' + kEscapeEnd ;
263- openCodes += openSeq ;
264- closeCodes = closeSeq + closeCodes ;
265- processedText = replaceCloseCode ( processedText , closeSeq , openSeq , false ) ;
293+ openCodes += hexStyle . openSeq ;
294+ closeCodes = hexStyle . closeSeq + closeCodes ;
295+ processedText = replaceCloseCode ( processedText , hexStyle . closeSeq , hexStyle . openSeq , false ) ;
266296 continue ;
267297 }
268298
269299 const style = cache [ key ] ;
270300 if ( style === undefined ) {
271- // Check if it looks like an invalid hex color (starts with #)
272- if ( typeof key === 'string' && key [ 0 ] === '#' ) {
273- throw new ERR_INVALID_ARG_VALUE ( 'format' , key ,
274- 'must be a valid hex color (#RGB or #RRGGBB)' ) ;
275- }
276301 validateOneOf ( key , 'format' , ObjectGetOwnPropertyNames ( inspect . colors ) ) ;
277302 }
278303 openCodes += style . openSeq ;
0 commit comments