Skip to content

Commit 2ceb670

Browse files
committed
lib: support FORCE_COLOR for non TTY streams
1 parent 32691bd commit 2ceb670

File tree

14 files changed

+99
-40
lines changed

14 files changed

+99
-40
lines changed

lib/internal/console/constructor.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ const kMaxGroupIndentation = 1000;
7979
// Lazy loaded for startup performance.
8080
let cliTable;
8181

82+
let utilColors;
83+
function lazyUtilColors() {
84+
utilColors ??= require('internal/util/colors');
85+
return utilColors;
86+
}
87+
8288
// Track amount of indentation required via `console.group()`.
8389
const kGroupIndent = Symbol('kGroupIndent');
8490
const kGroupIndentationWidth = Symbol('kGroupIndentWidth');
@@ -95,7 +101,6 @@ const kUseStdout = Symbol('kUseStdout');
95101
const kUseStderr = Symbol('kUseStderr');
96102

97103
const optionsMap = new SafeWeakMap();
98-
99104
function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
100105
// We have to test new.target here to see if this function is called
101106
// with new, because we need to define a custom instanceof to accommodate
@@ -314,9 +319,7 @@ ObjectDefineProperties(Console.prototype, {
314319
value: function(stream) {
315320
let color = this[kColorMode];
316321
if (color === 'auto') {
317-
color = stream.isTTY && (
318-
typeof stream.getColorDepth === 'function' ?
319-
stream.getColorDepth() > 2 : true);
322+
color = lazyUtilColors().shouldColorize(stream);
320323
}
321324

322325
const options = optionsMap.get(this);

lib/internal/errors.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@ function lazyInternalUtilInspect() {
190190
return internalUtilInspect;
191191
}
192192

193+
let utilColors;
194+
function lazyUtilColors() {
195+
utilColors ??= require('internal/util/colors');
196+
return utilColors;
197+
}
198+
193199
let buffer;
194200
function lazyBuffer() {
195201
buffer ??= require('buffer').Buffer;
@@ -789,16 +795,8 @@ const fatalExceptionStackEnhancers = {
789795
useColors = false;
790796
}
791797
}
792-
const {
793-
inspect,
794-
inspectDefaultOptions: {
795-
colors: defaultColors,
796-
},
797-
} = lazyInternalUtilInspect();
798-
const colors = useColors &&
799-
((internalBinding('util').guessHandleType(2) === 'TTY' &&
800-
require('internal/tty').hasColors()) ||
801-
defaultColors);
798+
const { inspect } = lazyInternalUtilInspect();
799+
const colors = useColors && lazyUtilColors().shouldColorize(process.stderr);
802800
try {
803801
return inspect(error, {
804802
colors,

lib/internal/test_runner/reporter/spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ const {
1414
const assert = require('assert');
1515
const Transform = require('internal/streams/transform');
1616
const { inspectWithNoCustomRetry } = require('internal/errors');
17-
const { green, blue, red, white, gray, hasColors } = require('internal/util/colors');
17+
const { green, blue, red, white, gray, shouldColorize } = require('internal/util/colors');
1818
const { kSubtestsFailed } = require('internal/test_runner/test');
1919
const { getCoverageReport } = require('internal/test_runner/utils');
2020

21-
const inspectOptions = { __proto__: null, colors: hasColors, breakLength: Infinity };
21+
const inspectOptions = { __proto__: null, colors: shouldColorize(process.stdout), breakLength: Infinity };
2222

2323
const colors = {
2424
'__proto__': null,

lib/internal/util/colors.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
'use strict';
22

3+
let internalTTy;
4+
function lazyInternalTTY() {
5+
internalTTy ??= require('internal/tty');
6+
return internalTTy;
7+
}
8+
39
module.exports = {
410
blue: '',
511
green: '',
@@ -8,9 +14,17 @@ module.exports = {
814
gray: '',
915
clear: '',
1016
hasColors: false,
17+
shouldColorize(stream) {
18+
if (process.env.FORCE_COLOR !== undefined) {
19+
return lazyInternalTTY().getColorDepth() > 2;
20+
}
21+
return stream?.isTTY && (
22+
typeof stream.getColorDepth === 'function' ?
23+
stream.getColorDepth() > 2 : true);
24+
},
1125
refresh() {
1226
if (process.stderr.isTTY) {
13-
const hasColors = process.stderr.hasColors();
27+
const hasColors = module.exports.shouldColorize(process.stderr);
1428
module.exports.blue = hasColors ? '\u001b[34m' : '';
1529
module.exports.green = hasColors ? '\u001b[32m' : '';
1630
module.exports.white = hasColors ? '\u001b[39m' : '';

lib/internal/util/debuglog.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ function emitWarningIfNeeded(set) {
4545

4646
const noop = () => {};
4747

48+
let utilColors;
49+
function lazyUtilColors() {
50+
utilColors ??= require('internal/util/colors');
51+
return utilColors;
52+
}
53+
4854
function debuglogImpl(enabled, set) {
4955
if (debugImpls[set] === undefined) {
5056
if (enabled) {
5157
const pid = process.pid;
5258
emitWarningIfNeeded(set);
5359
debugImpls[set] = function debug(...args) {
54-
const colors = process.stderr.hasColors && process.stderr.hasColors();
60+
const colors = lazyUtilColors().shouldColorize(process.stderr);
5561
const msg = formatWithOptions({ colors }, ...args);
5662
const coloredPID = inspect(pid, { colors });
5763
process.stderr.write(format('%s %s: %s\n', set, coloredPID, msg));

lib/repl.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ const {
121121
commonPrefix,
122122
} = require('internal/readline/utils');
123123
const { Console } = require('console');
124+
const { shouldColorize } = require('internal/util/colors');
124125
const CJSModule = require('internal/modules/cjs/loader').Module;
125126
let _builtinLibs = ArrayPrototypeFilter(
126127
CJSModule.builtinModules,
@@ -270,8 +271,8 @@ function REPLServer(prompt,
270271

271272
if (options.terminal && options.useColors === undefined) {
272273
// If possible, check if stdout supports colors or not.
273-
if (options.output.hasColors) {
274-
options.useColors = options.output.hasColors();
274+
if (shouldColorize(options.output)) {
275+
options.useColors = true;
275276
} else if (process.env.NODE_DISABLE_COLORS === undefined) {
276277
options.useColors = true;
277278
}

test/common/assertSnapshot.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ async function assertSnapshot(actual, filename = process.argv[1]) {
5454
* @param {boolean} [options.tty] - whether to spawn the process in a pseudo-tty
5555
* @returns {Promise<void>}
5656
*/
57-
async function spawnAndAssert(filename, transform = (x) => x, { tty = false } = {}) {
57+
async function spawnAndAssert(filename, transform = (x) => x, { tty = false, ...options } = {}) {
5858
if (tty && common.isWindows) {
5959
test({ skip: 'Skipping pseudo-tty tests, as pseudo terminals are not available on Windows.' });
6060
return;
6161
}
6262
const flags = common.parseTestFlags(filename);
6363
const executable = tty ? 'tools/pseudo-tty.py' : process.execPath;
6464
const args = tty ? [process.execPath, ...flags, filename] : [...flags, filename];
65-
const { stdout, stderr } = await common.spawnPromisified(executable, args);
65+
const { stdout, stderr } = await common.spawnPromisified(executable, args, options);
6666
await assertSnapshot(transform(`${stdout}${stderr}`), filename);
6767
}
6868

test/fixtures/console/force_colors.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
require('../../common');
4+
5+
console.log(123, 'foo', { bar: 'baz' });
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
123 foo { bar: 'baz' }

test/fixtures/errors/force_colors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
throw new Error('Should include grayed stack trace')

0 commit comments

Comments
 (0)