util: respect nested formats in styleText · nodejs/node@075d196
@@ -25,6 +25,7 @@ const {
2525 ArrayIsArray,
2626 ArrayPrototypePop,
2727 ArrayPrototypePush,
28+ ArrayPrototypeReduce,
2829 Error,
2930 ErrorCaptureStackTrace,
3031 FunctionPrototypeBind,
@@ -36,6 +37,8 @@ const {
3637 ObjectSetPrototypeOf,
3738 ObjectValues,
3839 ReflectApply,
40+ RegExp,
41+ RegExpPrototypeSymbolReplace,
3942 StringPrototypeToWellFormed,
4043} = primordials;
4144@@ -137,8 +140,7 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
137140// If the format is not an array, convert it to an array
138141const formatArray = ArrayIsArray(format) ? format : [format];
139142140-let left = '';
141-let right = '';
143+const codes = [];
142144for (const key of formatArray) {
143145if (key === 'none') continue;
144146const formatCodes = inspect.colors[key];
@@ -147,11 +149,56 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
147149validateOneOf(key, 'format', ObjectKeys(inspect.colors));
148150}
149151if (skipColorize) continue;
150-left += escapeStyleCode(formatCodes[0]);
151-right = `${escapeStyleCode(formatCodes[1])}${right}`;
152+ArrayPrototypePush(codes, formatCodes);
152153}
153154154-return skipColorize ? text : `${left}${text}${right}`;
155+if (skipColorize) {
156+return text;
157+}
158+159+// Build opening codes
160+let openCodes = '';
161+for (let i = 0; i < codes.length; i++) {
162+openCodes += escapeStyleCode(codes[i][0]);
163+}
164+165+// Process the text to handle nested styles
166+let processedText;
167+if (codes.length > 0) {
168+processedText = ArrayPrototypeReduce(
169+codes,
170+(text, code) => RegExpPrototypeSymbolReplace(
171+// Find the reset code
172+new RegExp(`\\u001b\\[${code[1]}m`, 'g'),
173+text,
174+(match, offset) => {
175+// Check if there's more content after this reset
176+if (offset + match.length < text.length) {
177+if (
178+code[0] === inspect.colors.dim[0] ||
179+code[0] === inspect.colors.bold[0]
180+) {
181+// Dim and bold are not mutually exclusive, so we need to reapply
182+return `${match}${escapeStyleCode(code[0])}`;
183+}
184+return `${escapeStyleCode(code[0])}`;
185+}
186+return match;
187+},
188+),
189+text,
190+);
191+} else {
192+processedText = text;
193+}
194+195+// Build closing codes in reverse order
196+let closeCodes = '';
197+for (let i = codes.length - 1; i >= 0; i--) {
198+closeCodes += escapeStyleCode(codes[i][1]);
199+}
200+201+return `${openCodes}${processedText}${closeCodes}`;
155202}
156203157204/**