Skip to content

Commit 97737fd

Browse files
committed
repl: fix terminal default setting
This makes sure that the described default behavior for the `terminal` option is actually always used and not only when running the REPL as standalone program. The options code is now logically combined instead of being spread out in the big REPL constructor. PR-URL: #26518 Reviewed-By: Lance Ball <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 96204c3 commit 97737fd

File tree

7 files changed

+80
-64
lines changed

7 files changed

+80
-64
lines changed

doc/api/cli.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ added: v0.1.32
671671
added: v0.3.0
672672
-->
673673

674-
When set to `1` colors will not be used in the REPL.
674+
When set, colors will not be used in the REPL.
675675

676676
### `NODE_EXTRA_CA_CERTS=file`
677677
<!-- YAML

doc/api/repl.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,10 @@ with REPL instances programmatically.
479479
<!-- YAML
480480
added: v0.1.91
481481
changes:
482+
- version: REPLACEME
483+
pr-url: https://github.com/nodejs/node/pull/REPLACEME
484+
description: The `terminal` option now follows the default description in
485+
all cases and `useColors` checks `hasColors()` if available.
482486
- version: v10.0.0
483487
pr-url: https://github.com/nodejs/node/pull/19187
484488
description: The `REPL_MAGIC_MODE` `replMode` was removed.
@@ -495,7 +499,7 @@ changes:
495499
* `output` {stream.Writable} The `Writable` stream to which REPL output will
496500
be written. **Default:** `process.stdout`.
497501
* `terminal` {boolean} If `true`, specifies that the `output` should be
498-
treated as a TTY terminal, and have ANSI/VT100 escape codes written to it.
502+
treated as a TTY terminal.
499503
**Default:** checking the value of the `isTTY` property on the `output`
500504
stream upon instantiation.
501505
* `eval` {Function} The function to be used when evaluating each given line
@@ -504,8 +508,9 @@ changes:
504508
the input was incomplete and prompt for additional lines.
505509
* `useColors` {boolean} If `true`, specifies that the default `writer`
506510
function should include ANSI color styling to REPL output. If a custom
507-
`writer` function is provided then this has no effect. **Default:** the
508-
REPL instances `terminal` value.
511+
`writer` function is provided then this has no effect. **Default:** checking
512+
color support on the `output` stream if the REPL instance's `terminal` value
513+
is `true`.
509514
* `useGlobal` {boolean} If `true`, specifies that the default evaluation
510515
function will use the JavaScript `global` as the context as opposed to
511516
creating a new separate context for the REPL instance. The node CLI REPL

lib/internal/repl.js

+8-12
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ function createRepl(env, opts, cb) {
1414
opts = {
1515
[kStandaloneREPL]: true,
1616
ignoreUndefined: false,
17-
terminal: process.stdout.isTTY,
1817
useGlobal: true,
1918
breakEvalOnSigint: true,
2019
...opts
@@ -23,17 +22,13 @@ function createRepl(env, opts, cb) {
2322
if (parseInt(env.NODE_NO_READLINE)) {
2423
opts.terminal = false;
2524
}
26-
// The "dumb" special terminal, as defined by terminfo, doesn't support
27-
// ANSI color control codes.
28-
// see http://invisible-island.net/ncurses/terminfo.ti.html#toc-_Specials
29-
if (parseInt(env.NODE_DISABLE_COLORS) || env.TERM === 'dumb') {
30-
opts.useColors = false;
31-
}
3225

33-
opts.replMode = {
34-
'strict': REPL.REPL_MODE_STRICT,
35-
'sloppy': REPL.REPL_MODE_SLOPPY
36-
}[String(env.NODE_REPL_MODE).toLowerCase().trim()];
26+
if (env.NODE_REPL_MODE) {
27+
opts.replMode = {
28+
'strict': REPL.REPL_MODE_STRICT,
29+
'sloppy': REPL.REPL_MODE_SLOPPY
30+
}[env.NODE_REPL_MODE.toLowerCase().trim()];
31+
}
3732

3833
if (opts.replMode === undefined) {
3934
opts.replMode = REPL.REPL_MODE_SLOPPY;
@@ -47,5 +42,6 @@ function createRepl(env, opts, cb) {
4742
}
4843

4944
const repl = REPL.start(opts);
50-
repl.setupHistory(opts.terminal ? env.NODE_REPL_HISTORY : '', cb);
45+
const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY;
46+
repl.setupHistory(term ? env.NODE_REPL_HISTORY : '', cb);
5147
}

lib/internal/tty.js

+3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ function getColorDepth(env = process.env) {
114114
if (env.NODE_DISABLE_COLORS !== undefined ||
115115
// See https://no-color.org/
116116
env.NO_COLOR !== undefined ||
117+
// The "dumb" special terminal, as defined by terminfo, doesn't support
118+
// ANSI color control codes.
119+
// See http://invisible-island.net/ncurses/terminfo.ti.html#toc-_Specials
117120
env.TERM === 'dumb') {
118121
return COLORS_2;
119122
}

lib/repl.js

+50-47
Original file line numberDiff line numberDiff line change
@@ -140,47 +140,70 @@ function REPLServer(prompt,
140140
replMode);
141141
}
142142

143-
var options, input, output, dom, breakEvalOnSigint;
143+
let options;
144144
if (prompt !== null && typeof prompt === 'object') {
145-
// an options object was given
146-
options = prompt;
145+
// An options object was given.
146+
options = { ...prompt };
147147
stream = options.stream || options.socket;
148-
input = options.input;
149-
output = options.output;
150148
eval_ = options.eval;
151149
useGlobal = options.useGlobal;
152150
ignoreUndefined = options.ignoreUndefined;
153151
prompt = options.prompt;
154-
dom = options.domain;
155152
replMode = options.replMode;
156-
breakEvalOnSigint = options.breakEvalOnSigint;
157153
} else {
158154
options = {};
159155
}
160156

161-
if (breakEvalOnSigint && eval_) {
157+
if (!options.input && !options.output) {
158+
// Legacy API, passing a 'stream'/'socket' option.
159+
if (!stream) {
160+
// Use stdin and stdout as the default streams if none were given.
161+
stream = process;
162+
}
163+
// We're given a duplex readable/writable Stream, like a `net.Socket`
164+
// or a custom object with 2 streams, or the `process` object.
165+
options.input = stream.stdin || stream;
166+
options.output = stream.stdout || stream;
167+
}
168+
169+
if (options.terminal === undefined) {
170+
options.terminal = options.output.isTTY;
171+
}
172+
options.terminal = !!options.terminal;
173+
174+
if (options.terminal && options.useColors === undefined) {
175+
// If possible, check if stdout supports colors or not.
176+
if (options.output.hasColors) {
177+
options.useColors = options.output.hasColors();
178+
} else if (process.env.NODE_DISABLE_COLORS === undefined) {
179+
options.useColors = true;
180+
}
181+
}
182+
183+
this.inputStream = options.input;
184+
this.outputStream = options.output;
185+
this.useColors = !!options.useColors;
186+
this._domain = options.domain || domain.create();
187+
this.useGlobal = !!useGlobal;
188+
this.ignoreUndefined = !!ignoreUndefined;
189+
this.replMode = replMode || exports.REPL_MODE_SLOPPY;
190+
this.underscoreAssigned = false;
191+
this.last = undefined;
192+
this.underscoreErrAssigned = false;
193+
this.lastError = undefined;
194+
this.breakEvalOnSigint = !!options.breakEvalOnSigint;
195+
this.editorMode = false;
196+
// Context id for use with the inspector protocol.
197+
this[kContextId] = undefined;
198+
199+
if (this.breakEvalOnSigint && eval_) {
162200
// Allowing this would not reflect user expectations.
163201
// breakEvalOnSigint affects only the behavior of the default eval().
164202
throw new ERR_INVALID_REPL_EVAL_CONFIG();
165203
}
166204

167-
var self = this;
168-
169-
self._domain = dom || domain.create();
170-
self.useGlobal = !!useGlobal;
171-
self.ignoreUndefined = !!ignoreUndefined;
172-
self.replMode = replMode || exports.REPL_MODE_SLOPPY;
173-
self.underscoreAssigned = false;
174-
self.last = undefined;
175-
self.underscoreErrAssigned = false;
176-
self.lastError = undefined;
177-
self.breakEvalOnSigint = !!breakEvalOnSigint;
178-
self.editorMode = false;
179-
// Context id for use with the inspector protocol.
180-
self[kContextId] = undefined;
181-
182-
let rli = self;
183-
Object.defineProperty(self, 'rli', {
205+
let rli = this;
206+
Object.defineProperty(this, 'rli', {
184207
get: util.deprecate(() => rli,
185208
'REPLServer.rli is deprecated', 'DEP0124'),
186209
set: util.deprecate((val) => rli = val,
@@ -197,6 +220,8 @@ function REPLServer(prompt,
197220

198221
eval_ = eval_ || defaultEval;
199222

223+
const self = this;
224+
200225
// Pause taking in new input, and store the keys in a buffer.
201226
const pausedBuffer = [];
202227
let paused = false;
@@ -452,21 +477,6 @@ function REPLServer(prompt,
452477
top.displayPrompt();
453478
});
454479

455-
if (!input && !output) {
456-
// legacy API, passing a 'stream'/'socket' option
457-
if (!stream) {
458-
// Use stdin and stdout as the default streams if none were given
459-
stream = process;
460-
}
461-
// We're given a duplex readable/writable Stream, like a `net.Socket`
462-
// or a custom object with 2 streams, or the `process` object
463-
input = stream.stdin || stream;
464-
output = stream.stdout || stream;
465-
}
466-
467-
self.inputStream = input;
468-
self.outputStream = output;
469-
470480
self.resetContext();
471481
self.lines.level = [];
472482

@@ -503,13 +513,6 @@ function REPLServer(prompt,
503513
// Figure out which "writer" function to use
504514
self.writer = options.writer || exports.writer;
505515

506-
if (options.useColors === undefined) {
507-
options.useColors = self.terminal && (
508-
typeof self.outputStream.getColorDepth === 'function' ?
509-
self.outputStream.getColorDepth() > 1 : true);
510-
}
511-
self.useColors = !!options.useColors;
512-
513516
if (self.writer === writer) {
514517
// Conditionally turn on ANSI coloring.
515518
writer.options.colors = self.useColors;

test/parallel/test-repl-colors.js

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ inout._write = function(s, _, cb) {
1919
};
2020

2121
const repl = new REPLServer({ input: inout, output: inout, useColors: true });
22+
inout.isTTY = true;
23+
const repl2 = new REPLServer({ input: inout, output: inout });
2224

2325
process.on('exit', function() {
2426
// https://github.com/nodejs/node/pull/16485#issuecomment-350428638
@@ -28,4 +30,5 @@ process.on('exit', function() {
2830
strictEqual(output.includes(`'\u001b[32m\\'string\\'\u001b[39m'`), false);
2931
strictEqual(inspect.defaultOptions.colors, false);
3032
strictEqual(repl.writer.options.colors, true);
33+
strictEqual(repl2.writer.options.colors, true);
3134
});

test/parallel/test-repl-envvars.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,25 @@ const tests = [
3838
function run(test) {
3939
const env = test.env;
4040
const expected = test.expected;
41+
4142
const opts = {
4243
terminal: true,
4344
input: new stream.Readable({ read() {} }),
4445
output: new stream.Writable({ write() {} })
4546
};
4647

47-
REPL.createInternalRepl(env, opts, function(err, repl) {
48+
Object.assign(process.env, env);
49+
50+
REPL.createInternalRepl(process.env, opts, function(err, repl) {
4851
assert.ifError(err);
4952

5053
assert.strictEqual(repl.terminal, expected.terminal,
5154
`Expected ${inspect(expected)} with ${inspect(env)}`);
5255
assert.strictEqual(repl.useColors, expected.useColors,
5356
`Expected ${inspect(expected)} with ${inspect(env)}`);
57+
for (const key of Object.keys(env)) {
58+
delete process.env[key];
59+
}
5460
repl.close();
5561
});
5662
}

0 commit comments

Comments
 (0)