Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
ArrayIsArray,
ArrayPrototypePop,
ArrayPrototypePush,
ArrayPrototypeReduce,
Error,
ErrorCaptureStackTrace,
FunctionPrototypeBind,
Expand All @@ -36,6 +37,8 @@ const {
ObjectSetPrototypeOf,
ObjectValues,
ReflectApply,
RegExp,
RegExpPrototypeSymbolReplace,
StringPrototypeToWellFormed,
} = primordials;

Expand Down Expand Up @@ -137,8 +140,7 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
// If the format is not an array, convert it to an array
const formatArray = ArrayIsArray(format) ? format : [format];

let left = '';
let right = '';
const codes = [];
for (const key of formatArray) {
if (key === 'none') continue;
const formatCodes = inspect.colors[key];
Expand All @@ -147,11 +149,56 @@ function styleText(format, text, { validateStream = true, stream = process.stdou
validateOneOf(key, 'format', ObjectKeys(inspect.colors));
}
if (skipColorize) continue;
left += escapeStyleCode(formatCodes[0]);
right = `${escapeStyleCode(formatCodes[1])}${right}`;
ArrayPrototypePush(codes, formatCodes);
}

return skipColorize ? text : `${left}${text}${right}`;
if (skipColorize) {
return text;
}

// Build opening codes
let openCodes = '';
for (let i = 0; i < codes.length; i++) {
openCodes += escapeStyleCode(codes[i][0]);
}

// Process the text to handle nested styles
let processedText;
if (codes.length > 0) {
processedText = ArrayPrototypeReduce(
codes,
(text, code) => RegExpPrototypeSymbolReplace(
// Find the reset code
new RegExp(`\\u001b\\[${code[1]}m`, 'g'),
text,
(match, offset) => {
// Check if there's more content after this reset
if (offset + match.length < text.length) {
if (
code[0] === inspect.colors.dim[0] ||
code[0] === inspect.colors.bold[0]
) {
// Dim and bold are not mutually exclusive, so we need to reapply
return `${match}${escapeStyleCode(code[0])}`;
}
return `${escapeStyleCode(code[0])}`;
}
return match;
},
),
text,
);
} else {
processedText = text;
}

// Build closing codes in reverse order
let closeCodes = '';
for (let i = codes.length - 1; i >= 0; i--) {
closeCodes += escapeStyleCode(codes[i][1]);
}

return `${openCodes}${processedText}${closeCodes}`;
}

/**
Expand Down
77 changes: 77 additions & 0 deletions test/parallel/test-util-styletext.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,83 @@ assert.strictEqual(
'\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m',
);

assert.strictEqual(
util.styleText('red',
'A' + util.styleText('blue', 'B', { validateStream: false }) + 'C',
{ validateStream: false }),
'\u001b[31mA\u001b[34mB\u001b[31mC\u001b[39m'
);

assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red',
{ validateStream: false }
),
'\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[39m'
);

assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('red', 'red', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }),
{ validateStream: false }
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[31mred\x1b[31mred\x1b[34mblue\x1b[39m\x1b[39m'
);

assert.strictEqual(
util.styleText('red',
'A' + util.styleText(['bgRed', 'blue'], 'B', { validateStream: false }) +
'C', { validateStream: false }),
'\x1B[31mA\x1B[41m\x1B[34mB\x1B[31m\x1B[49mC\x1B[39m'
);

assert.strictEqual(
util.styleText('dim',
'dim' +
util.styleText('bold', 'bold', { validateStream: false }) +
'dim', { validateStream: false }),
'\x1B[2mdim\x1B[1mbold\x1B[22m\x1B[2mdim\x1B[22m'
);

assert.strictEqual(
util.styleText('blue',
'blue' +
util.styleText('red',
'red' +
util.styleText('green', 'green', { validateStream: false }) +
'red', { validateStream: false }) +
'blue', { validateStream: false }),
'\x1B[34mblue\x1B[31mred\x1B[32mgreen\x1B[31mred\x1B[34mblue\x1B[39m'
);

assert.strictEqual(
util.styleText(
'red',
'red' +
util.styleText(
'blue',
'blue' + util.styleText('red', 'red', {
validateStream: false,
}) + 'blue',
{
validateStream: false,
}
) + 'red', {
validateStream: false,
}
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[39m'
);

assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
util.styleText(
Expand Down
Loading