Skip to content

Commit a2eda67

Browse files
committed
Fix the verbose option printing colors when stderr is not a terminal
1 parent 6925cc3 commit a2eda67

2 files changed

Lines changed: 19 additions & 8 deletions

File tree

‎lib/verbose/default.js‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
import process from 'node:process';
12
import figures from 'figures';
23
import {
3-
gray,
4-
bold,
5-
redBright,
6-
yellowBright,
4+
gray as grayColor,
5+
bold as boldColor,
6+
redBright as redBrightColor,
7+
yellowBright as yellowBrightColor,
78
} from 'yoctocolors';
89

10+
// `yoctocolors` checks whether the terminal type supports colors, but not whether `stderr`, where this is printed, is a terminal.
11+
// Like `util.styleText()`, colors are not used when `stderr` is redirected, e.g. to a file, unless `FORCE_COLOR` is set.
12+
const hasColors = process.env.FORCE_COLOR !== undefined || process.stderr.isTTY === true;
13+
const identity = string => string;
14+
const getColor = color => hasColors ? color : identity;
15+
const gray = getColor(grayColor);
16+
const bold = getColor(boldColor);
17+
const redBright = getColor(redBrightColor);
18+
const yellowBright = getColor(yellowBrightColor);
19+
920
// Default when `verbose` is not a function
1021
export const defaultVerboseFunction = ({
1122
type,
@@ -43,8 +54,6 @@ const ICONS = {
4354
duration: getFinalIcon,
4455
};
4556

46-
const identity = string => string;
47-
4857
const COLORS = {
4958
command: () => bold,
5059
output: () => identity,

‎test/verbose/log.js‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ test('Logs on stderr not stdout, verbose "none", sync', () => testNoStdout('none
2222
test('Logs on stderr not stdout, verbose "short", sync', () => testNoStdout('short', true));
2323
test('Logs on stderr not stdout, verbose "full", sync', () => testNoStdout('full', true));
2424

25-
const testColor = async (expectedResult, forceColor) => {
26-
const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short'}, {env: {FORCE_COLOR: forceColor, NO_COLOR: undefined}});
25+
const testColor = async (expectedResult, forceColor, term) => {
26+
const {stderr} = await nestedSubprocess('noop.js', [foobarString], {verbose: 'short'}, {env: {FORCE_COLOR: forceColor, NO_COLOR: undefined, TERM: term}});
2727
assert.equal(stderr !== stripVTControlCharacters(stderr), expectedResult);
2828
};
2929

3030
test('Prints with colors if supported', () => testColor(true, '1'));
3131
test('Prints without colors if not supported', () => testColor(false, '0'));
32+
// `stderr` is not a terminal, e.g. when redirected to a file, even though the terminal type supports colors
33+
test('Prints without colors if stderr is not a terminal', () => testColor(false, undefined, 'xterm-256color'));
3234

3335
test('Prints lines in order when interleaved with subprocess stderr', async () => {
3436
const results = await Promise.all(Array.from({length: PARALLEL_COUNT}, () =>

0 commit comments

Comments
 (0)