diff --git a/source/vendor/supports-color/index.js b/source/vendor/supports-color/index.js index 265d7f8..dff5a9e 100644 --- a/source/vendor/supports-color/index.js +++ b/source/vendor/supports-color/index.js @@ -91,7 +91,15 @@ function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) { return 0; } - const min = forceColor || 0; + // When `FORCE_COLOR` is set to a specific level, return that level + // directly instead of using it as a minimum. This ensures that + // e.g. FORCE_COLOR=1 gives exactly level 1, not a higher level + // based on terminal capabilities. + if (forceColor !== undefined) { + return forceColor; + } + + const min = 0; if (env.TERM === 'dumb') { return min; diff --git a/test/_force-color-fixture.js b/test/_force-color-fixture.js new file mode 100644 index 0000000..5afe665 --- /dev/null +++ b/test/_force-color-fixture.js @@ -0,0 +1,4 @@ +import {createSupportsColor} from '../source/vendor/supports-color/index.js'; + +const result = createSupportsColor({isTTY: true}); +console.log(JSON.stringify(result)); diff --git a/test/force-color.js b/test/force-color.js new file mode 100644 index 0000000..fce9638 --- /dev/null +++ b/test/force-color.js @@ -0,0 +1,45 @@ +import {fileURLToPath} from 'node:url'; +import test from 'ava'; +import {execaNode} from 'execa'; + +const fixturePath = fileURLToPath(new URL('_force-color-fixture.js', import.meta.url)); + +test('FORCE_COLOR=0 disables color support', async t => { + const {stdout} = await execaNode(fixturePath, { + env: {FORCE_COLOR: '0', COLORTERM: 'truecolor'}, + }); + t.is(stdout, 'false'); +}); + +test('FORCE_COLOR=1 gives exactly level 1 even with truecolor terminal', async t => { + const {stdout} = await execaNode(fixturePath, { + env: {FORCE_COLOR: '1', COLORTERM: 'truecolor'}, + }); + const result = JSON.parse(stdout); + t.is(result.level, 1); + t.true(result.hasBasic); + t.false(result.has256); + t.false(result.has16m); +}); + +test('FORCE_COLOR=2 gives exactly level 2 even with truecolor terminal', async t => { + const {stdout} = await execaNode(fixturePath, { + env: {FORCE_COLOR: '2', COLORTERM: 'truecolor'}, + }); + const result = JSON.parse(stdout); + t.is(result.level, 2); + t.true(result.hasBasic); + t.true(result.has256); + t.false(result.has16m); +}); + +test('FORCE_COLOR=3 gives exactly level 3', async t => { + const {stdout} = await execaNode(fixturePath, { + env: {FORCE_COLOR: '3'}, + }); + const result = JSON.parse(stdout); + t.is(result.level, 3); + t.true(result.hasBasic); + t.true(result.has256); + t.true(result.has16m); +});