diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js index 45e5efddb6f8..f18f58323d7c 100644 --- a/lib/internal/main/worker_thread.js +++ b/lib/internal/main/worker_thread.js @@ -99,6 +99,8 @@ port.on('message', (message) => { filename, hasStdin, publicPort, + stderrIsTTY, + stdoutIsTTY, workerData, webWorkerData, mainThreadPort, @@ -117,6 +119,36 @@ port.on('message', (message) => { require('internal/worker').assignEnvironmentData(environmentData); setupMainThreadPort(mainThreadPort); + if (stdoutIsTTY || stderrIsTTY) { + const { getColorDepth, hasColors } = require('internal/tty'); + const setupTTYProperties = (stream) => { + ObjectDefineProperty(stream, 'isTTY', { + __proto__: null, + configurable: true, + enumerable: true, + value: true, + writable: true, + }); + ObjectDefineProperty(stream, 'getColorDepth', { + __proto__: null, + configurable: true, + enumerable: true, + value: getColorDepth, + writable: true, + }); + ObjectDefineProperty(stream, 'hasColors', { + __proto__: null, + configurable: true, + enumerable: true, + value: hasColors, + writable: true, + }); + }; + + if (stdoutIsTTY) setupTTYProperties(process.stdout); + if (stderrIsTTY) setupTTYProperties(process.stderr); + } + if (webWorkerData !== undefined) { require('internal/webworker') .installDedicatedWorkerGlobalScope(webWorkerData.url, webWorkerData); diff --git a/lib/internal/worker.js b/lib/internal/worker.js index f3f3c23a5abe..81d5bbf84f7f 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -367,6 +367,8 @@ class Worker extends EventEmitter { webWorkerData: options[kWebWorkerData], environmentData, hasStdin: !!options.stdin, + stdoutIsTTY: !options.stdout && !!process.stdout.isTTY, + stderrIsTTY: !options.stderr && !!process.stderr.isTTY, publicPort: publicPortToWorker, mainThreadPort: mainThreadPortToWorker, }, transferList); diff --git a/test/parallel/test-util-styletext-worker.js b/test/parallel/test-util-styletext-worker.js new file mode 100644 index 000000000000..805250953b39 --- /dev/null +++ b/test/parallel/test-util-styletext-worker.js @@ -0,0 +1,104 @@ +'use strict'; + +const common = require('../common'); +const assert = require('node:assert'); +const { once } = require('node:events'); +const { Worker } = require('node:worker_threads'); + +const styled = '\u001b[31mhello\u001b[39m'; +const plain = 'hello'; + +const workerCode = ` + const { parentPort, workerData } = require('node:worker_threads'); + const { styleText } = require('node:util'); + + const stream = process[workerData.stream]; + parentPort.postMessage({ + hasColorDepth: typeof stream.getColorDepth === 'function', + hasColors: typeof stream.hasColors === 'function', + isTTY: stream.isTTY, + result: styleText('red', 'hello', { stream }), + }); +`; + +const env = { ...process.env, TERM: 'xterm-256color' }; +delete env.FORCE_COLOR; +delete env.NO_COLOR; +delete env.NODE_DISABLE_COLORS; + +async function runWorker(stream, options = {}) { + const worker = new Worker(workerCode, { + env, + eval: true, + workerData: { stream }, + ...options, + }); + const exit = once(worker, 'exit'); + const [actual] = await once(worker, 'message'); + + assert.deepStrictEqual(await exit, [0]); + return actual; +} + +function setIsTTY(stream, value) { + Object.defineProperty(stream, 'isTTY', { + configurable: true, + value, + }); +} + +function restoreIsTTY(stream, descriptor) { + if (descriptor === undefined) { + delete stream.isTTY; + } else { + Object.defineProperty(stream, 'isTTY', descriptor); + } +} + +const stdoutIsTTY = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY'); +const stderrIsTTY = Object.getOwnPropertyDescriptor(process.stderr, 'isTTY'); + +common.mustCall(async () => { + try { + setIsTTY(process.stdout, true); + assert.deepStrictEqual(await runWorker('stdout'), { + hasColorDepth: true, + hasColors: true, + isTTY: true, + result: styled, + }); + + setIsTTY(process.stderr, true); + assert.deepStrictEqual(await runWorker('stderr'), { + hasColorDepth: true, + hasColors: true, + isTTY: true, + result: styled, + }); + + assert.deepStrictEqual(await runWorker('stdout', { stdout: true }), { + hasColorDepth: false, + hasColors: false, + isTTY: undefined, + result: plain, + }); + + assert.deepStrictEqual(await runWorker('stderr', { stderr: true }), { + hasColorDepth: false, + hasColors: false, + isTTY: undefined, + result: plain, + }); + + setIsTTY(process.stdout, false); + assert.deepStrictEqual(await runWorker('stdout'), { + hasColorDepth: false, + hasColors: false, + isTTY: undefined, + result: plain, + }); + } finally { + restoreIsTTY(process.stdout, stdoutIsTTY); + restoreIsTTY(process.stderr, stderrIsTTY); + } +})();