Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tty: add NO_COLOR and FORCE_COLOR support #26485

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions doc/api/tty.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ position.
added: v9.9.0
-->

* `env` {Object} An object containing the environment variables to check.
**Default:** `process.env`.
* `env` {Object} An object containing the environment variables to check. This
enables simulating the usage of a specific terminal. **Default:**
`process.env`.
* Returns: {number}

Returns:
Expand All @@ -159,11 +160,18 @@ Use this to determine what colors the terminal supports. Due to the nature of
colors in terminals it is possible to either have false positives or false
negatives. It depends on process information and the environment variables that
may lie about what terminal is used.
To enforce a specific behavior without relying on `process.env` it is possible
to pass in an object with different settings.
It is possible to pass in an `env` object to simulate the usage of a specific
terminal. This can be useful to check how specific environment settings behave.

To enforce a specific color support, use one of the below environment settings.

* 2 colors: `FORCE_COLOR = 0` (Disables colors)
* 16 colors: `FORCE_COLOR = 1`
* 256 colors: `FORCE_COLOR = 2`
* 16,777,216 colors: `FORCE_COLOR = 3`

Use the `NODE_DISABLE_COLORS` environment variable to enforce this function to
always return 1.
Disabling color support is also possible by using the `NO_COLOR` and
`NODE_DISABLE_COLORS` environment variables.

### writeStream.getWindowSize()
<!-- YAML
Expand Down
40 changes: 39 additions & 1 deletion lib/internal/tty.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,49 @@ const TERM_ENVS_REG_EXP = [
/^vt100/
];

function warnOnDeactivatedColors(env) {
let name;
if (env.NODE_DISABLE_COLORS !== undefined)
name = 'NODE_DISABLE_COLORS';
if (env.NO_COLOR !== undefined)
name = 'NO_COLOR';

if (name !== undefined) {
process.emitWarning(
`The '${name}' env is ignored due to the 'FORCE_COLOR' env being set.`,
'Warning'
);
}
}

// The `getColorDepth` API got inspired by multiple sources such as
// https://github.com/chalk/supports-color,
// https://github.com/isaacs/color-support.
function getColorDepth(env = process.env) {
if (env.NODE_DISABLE_COLORS || env.TERM === 'dumb') {
// Use level 0-3 to support the same levels as `chalk` does. This is done for
// consistency throughout the ecosystem.
if (env.FORCE_COLOR !== undefined) {
switch (env.FORCE_COLOR) {
case '':
case '1':
case 'true':
warnOnDeactivatedColors(env);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Place this above the switch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When moved above the switch, I'll have to exclude a lot of values. That's why it's inside.

return COLORS_16;
case '2':
warnOnDeactivatedColors(env);
return COLORS_256;
case '3':
warnOnDeactivatedColors(env);
return COLORS_16m;
default:
return COLORS_2;
}
}

if (env.NODE_DISABLE_COLORS !== undefined ||
// See https://no-color.org/
env.NO_COLOR !== undefined ||
env.TERM === 'dumb') {
return COLORS_2;
}

Expand Down
11 changes: 9 additions & 2 deletions test/pseudo-tty/test-tty-color-support.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,18 @@ const writeStream = new WriteStream(fd);
[{ TERM: 'color' }, 4],
[{ TERM: 'linux' }, 4],
[{ TERM: 'fail' }, 1],
[{ NODE_DISABLE_COLORS: '1' }, 1],
[{ TERM: 'color', NODE_DISABLE_COLORS: '1' }, 1],
[{ TERM: 'dumb' }, 1],
[{ TERM: 'dumb', COLORTERM: '1' }, 1],
[{ TERM: 'terminator' }, 24],
[{ TERM: 'console' }, 4]
[{ TERM: 'console' }, 4],
[{ COLORTERM: '24bit', FORCE_COLOR: '' }, 4],
[{ NO_COLOR: '1', FORCE_COLOR: '2' }, 8],
[{ NODE_DISABLE_COLORS: '1', FORCE_COLOR: '3' }, 24],
[{ NO_COLOR: '1', COLORTERM: '24bit' }, 1],
[{ NO_COLOR: '', COLORTERM: '24bit' }, 1],
[{ TMUX: '1', FORCE_COLOR: 0 }, 1],
[{ NO_COLOR: 'true', FORCE_COLOR: 0, COLORTERM: 'truecolor' }, 1],
].forEach(([env, depth], i) => {
const actual = writeStream.getColorDepth(env);
assert.strictEqual(
Expand Down
8 changes: 8 additions & 0 deletions test/pseudo-tty/test-tty-color-support.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(node:*) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NODE_DISABLE_COLORS' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NODE_DISABLE_COLORS' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NODE_DISABLE_COLORS' env is ignored due to the 'FORCE_COLOR' env being set.
(node:*) Warning: The 'NODE_DISABLE_COLORS' env is ignored due to the 'FORCE_COLOR' env being set.