Skip to content

Commit d7d3c0a

Browse files
committed
lib: support FORCE_COLOR for non TTY streams
PR-URL: nodejs#48034 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 2262653 commit d7d3c0a

File tree

14 files changed

+110
-40
lines changed

14 files changed

+110
-40
lines changed

lib/internal/console/constructor.js

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

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

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

323326
const options = optionsMap.get(this);

lib/internal/errors.js

Lines changed: 7 additions & 4 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;
@@ -795,10 +801,7 @@ const fatalExceptionStackEnhancers = {
795801
colors: defaultColors,
796802
},
797803
} = lazyInternalUtilInspect();
798-
const colors = useColors &&
799-
((internalBinding('util').guessHandleType(2) === 'TTY' &&
800-
require('internal/tty').hasColors()) ||
801-
defaultColors);
804+
const colors = useColors && (lazyUtilColors().shouldColorize(process.stderr) || defaultColors);
802805
try {
803806
return inspect(error, {
804807
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
@@ -46,13 +46,19 @@ function emitWarningIfNeeded(set) {
4646

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

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

lib/repl.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ const {
122122
commonPrefix,
123123
} = require('internal/readline/utils');
124124
const { Console } = require('console');
125+
const { shouldColorize } = require('internal/util/colors');
125126
const CJSModule = require('internal/modules/cjs/loader').Module;
126127
let _builtinLibs = ArrayPrototypeFilter(
127128
CJSModule.builtinModules,
@@ -272,11 +273,7 @@ function REPLServer(prompt,
272273

273274
if (options.terminal && options.useColors === undefined) {
274275
// If possible, check if stdout supports colors or not.
275-
if (options.output.hasColors) {
276-
options.useColors = options.output.hasColors();
277-
} else if (process.env.NODE_DISABLE_COLORS === undefined) {
278-
options.useColors = true;
279-
}
276+
options.useColors = shouldColorize(options.output) || process.env.NODE_DISABLE_COLORS === undefined;
280277
}
281278

282279
// TODO(devsnek): Add a test case for custom eval functions.

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)