Skip to content

Commit c46b31f

Browse files
MoLowdanielleadams
authored andcommitted
lib: support FORCE_COLOR for non TTY streams
PR-URL: #48034 Backport-PR-URL: #48684 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent a2bfe02 commit c46b31f

14 files changed

Lines changed: 110 additions & 40 deletions

File tree

‎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
letcliTable;
8282

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

98104
constoptionsMap=newSafeWeakMap();
99-
100105
functionConsole(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
letcolor=this[kColorMode];
317322
if(color==='auto'){
318-
color=stream.isTTY&&(
319-
typeofstream.getColorDepth==='function' ?
320-
stream.getColorDepth()>2 : true);
323+
color=lazyUtilColors().shouldColorize(stream);
321324
}
322325

323326
constoptions=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
returninternalUtilInspect;
191191
}
192192

193+
letutilColors;
194+
functionlazyUtilColors(){
195+
utilColors??=require('internal/util/colors');
196+
returnutilColors;
197+
}
198+
193199
letbuffer;
194200
functionlazyBuffer(){
195201
buffer??=require('buffer').Buffer;
@@ -795,10 +801,7 @@ const fatalExceptionStackEnhancers = {
795801
colors: defaultColors,
796802
},
797803
}=lazyInternalUtilInspect();
798-
constcolors=useColors&&
799-
((internalBinding('util').guessHandleType(2)==='TTY'&&
800-
require('internal/tty').hasColors())||
801-
defaultColors);
804+
constcolors=useColors&&(lazyUtilColors().shouldColorize(process.stderr)||defaultColors);
802805
try{
803806
returninspect(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
constassert=require('assert');
1515
constTransform=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-
constinspectOptions={__proto__: null,colors: hasColors,breakLength: Infinity};
21+
constinspectOptions={__proto__: null,colors: shouldColorize(process.stdout),breakLength: Infinity};
2222

2323
constcolors={
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+
letinternalTTy;
4+
functionlazyInternalTTY(){
5+
internalTTy??=require('internal/tty');
6+
returninternalTTy;
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+
returnlazyInternalTTY().getColorDepth()>2;
20+
}
21+
returnstream?.isTTY&&(
22+
typeofstream.getColorDepth==='function' ?
23+
stream.getColorDepth()>2 : true);
24+
},
1125
refresh(){
1226
if(process.stderr.isTTY){
13-
consthasColors=process.stderr.hasColors();
27+
consthasColors=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
constnoop=()=>{};
4848

49+
letutilColors;
50+
functionlazyUtilColors(){
51+
utilColors??=require('internal/util/colors');
52+
returnutilColors;
53+
}
54+
4955
functiondebuglogImpl(enabled,set){
5056
if(debugImpls[set]===undefined){
5157
if(enabled){
5258
constpid=process.pid;
5359
emitWarningIfNeeded(set);
5460
debugImpls[set]=functiondebug(...args){
55-
constcolors=process.stderr.hasColors&&process.stderr.hasColors();
61+
constcolors=lazyUtilColors().shouldColorize(process.stderr);
5662
constmsg=formatWithOptions({ colors }, ...args);
5763
constcoloredPID=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
constCJSModule=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-
}elseif(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-
asyncfunctionspawnAndAssert(filename,transform=(x)=>x,{ tty =false}={}){
57+
asyncfunctionspawnAndAssert(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
constflags=common.parseTestFlags(filename);
6363
constexecutable=tty ? 'tools/pseudo-tty.py' : process.execPath;
6464
constargs=tty ? [process.execPath, ...flags,filename] : [...flags,filename];
65-
const{ stdout, stderr }=awaitcommon.spawnPromisified(executable,args);
65+
const{ stdout, stderr }=awaitcommon.spawnPromisified(executable,args,options);
6666
awaitassertSnapshot(transform(`${stdout}${stderr}`),filename);
6767
}
6868

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' }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
thrownewError('Should include grayed stack trace')

0 commit comments

Comments
 (0)