From 261e117bba4bb3df7bc74f3dcfc74eaba43f12b6 Mon Sep 17 00:00:00 2001 From: puneetdixit200 <236133619+puneetdixit200@users.noreply.github.com> Date: Fri, 22 May 2026 12:02:46 +0530 Subject: [PATCH] Fix input with inherited stdin --- lib/stdio/handle.js | 13 +++++++++++-- test/fixtures/nested-input-inherit.js | 17 +++++++++++++++++ test/io/input-option.js | 15 +++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100755 test/fixtures/nested-input-inherit.js diff --git a/lib/stdio/handle.js b/lib/stdio/handle.js index eeeb220b04..903134fe00 100644 --- a/lib/stdio/handle.js +++ b/lib/stdio/handle.js @@ -61,9 +61,10 @@ const getFileDescriptor = ({stdioOption, fdNumber, options, isSync}) => { // For example, `stdout: ['ignore']` behaves the same as `stdout: 'ignore'`. const initializeStdioItems = ({stdioOption, fdNumber, options, optionName}) => { const values = Array.isArray(stdioOption) ? stdioOption : [stdioOption]; + const inputStdioItems = handleInputOptions(options, fdNumber); const initialStdioItems = [ - ...values.map(value => initializeStdioItem(value, optionName)), - ...handleInputOptions(options, fdNumber), + ...omitInheritedStdin(values.map(value => initializeStdioItem(value, optionName)), inputStdioItems), + ...inputStdioItems, ]; const stdioItems = filterDuplicates(initialStdioItems); @@ -73,6 +74,14 @@ const initializeStdioItems = ({stdioOption, fdNumber, options, optionName}) => { return {stdioItems, isStdioArray}; }; +const omitInheritedStdin = (stdioItems, inputStdioItems) => inputStdioItems.length > 0 && isInheritedStdinOnly(stdioItems) + ? [] + : stdioItems; + +const isInheritedStdinOnly = stdioItems => stdioItems.length === 1 + && stdioItems[0].type === 'native' + && stdioItems[0].value === 'inherit'; + const initializeStdioItem = (value, optionName) => ({ type: getStdioItemType(value, optionName), value, diff --git a/test/fixtures/nested-input-inherit.js b/test/fixtures/nested-input-inherit.js new file mode 100755 index 0000000000..7c7acb35de --- /dev/null +++ b/test/fixtures/nested-input-inherit.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +import process from 'node:process'; +import {execa, execaSync} from '../../index.js'; +import {foobarString} from '../helpers/input.js'; + +const [optionName, stdioOptionString, isSyncString] = process.argv.slice(2); +const execaMethod = isSyncString === 'true' ? execaSync : execa; +const options = { + input: foobarString, + [optionName]: JSON.parse(stdioOptionString), +}; + +if (optionName === 'stdin') { + options.stdout = 'inherit'; +} + +await execaMethod('stdin.js', options); diff --git a/test/io/input-option.js b/test/io/input-option.js index 1aa3726353..a277b9828e 100644 --- a/test/io/input-option.js +++ b/test/io/input-option.js @@ -9,6 +9,7 @@ import { runScriptSync, } from '../helpers/run.js'; import { + foobarString, foobarUint8Array, foobarBuffer, foobarArrayBuffer, @@ -32,6 +33,20 @@ test('input option can be a Buffer - sync', testInput, foobarBuffer, runExecaSyn test('input option can be used with $', testInput, 'foobar', runScript); test('input option can be used with $.sync', testInput, 'foobar', runScriptSync); +const testInputIgnoresInheritedStdin = async (t, optionName, stdioOption, isSync) => { + const {stdout} = await execa('nested-input-inherit.js', [optionName, JSON.stringify(stdioOption), `${isSync}`], {input: foobarString}); + t.is(stdout, foobarString); +}; + +test('input option ignores stdin "inherit"', testInputIgnoresInheritedStdin, 'stdin', 'inherit', false); +test('input option ignores stdin ["inherit"]', testInputIgnoresInheritedStdin, 'stdin', ['inherit'], false); +test('input option ignores stdio "inherit"', testInputIgnoresInheritedStdin, 'stdio', 'inherit', false); +test('input option ignores stdio[0] "inherit"', testInputIgnoresInheritedStdin, 'stdio', ['inherit', 'inherit', 'pipe'], false); +test.serial('input option ignores stdin "inherit" - sync', testInputIgnoresInheritedStdin, 'stdin', 'inherit', true); +test.serial('input option ignores stdin ["inherit"] - sync', testInputIgnoresInheritedStdin, 'stdin', ['inherit'], true); +test.serial('input option ignores stdio "inherit" - sync', testInputIgnoresInheritedStdin, 'stdio', 'inherit', true); +test.serial('input option ignores stdio[0] "inherit" - sync', testInputIgnoresInheritedStdin, 'stdio', ['inherit', 'inherit', 'pipe'], true); + const testInvalidInput = async (t, input, execaMethod) => { t.throws(() => { execaMethod('empty.js', {input});