From 8e6ef2e735f52d08c3b47e384e0d1c82d138c98c Mon Sep 17 00:00:00 2001 From: Tiancheng Xu <44307608+Tiancheng-Xu@users.noreply.github.com> Date: Wed, 16 Sep 2026 06:56:50 -0400 Subject: [PATCH 1/2] fix(node): preserve stdio fds for sync child processes --- ext/node/polyfills/internal/child_process.ts | 11 ++++++++--- tests/unit_node/child_process_test.ts | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index d9f71bbdc66629..dcc23192a191fe 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -959,6 +959,9 @@ function streamHandleFd(stream) { if (handle && typeof handle.fd === "number" && handle.fd >= 0) { return handle.fd; } + if (typeof stream.fd === "number" && stream.fd >= 0) { + return stream.fd; + } return -1; } @@ -997,9 +1000,11 @@ function toDenoStdio( // another child's stdin shares the underlying OS pipe. const fd = streamHandleFd(pipe); if (fd >= 0) { - pipe[kChildStdioUsedAsInput] = true; - pipe.pause(); - pipe._handle?.readStop?.(); + if (typeof pipe.pause === "function") { + pipe[kChildStdioUsedAsInput] = true; + pipe.pause(); + pipe._handle?.readStop?.(); + } return fd; } // For streams without a usable fd, create a pipe and set up JS-level diff --git a/tests/unit_node/child_process_test.ts b/tests/unit_node/child_process_test.ts index 40047745eeebea..214cbc3fa8b6a6 100644 --- a/tests/unit_node/child_process_test.ts +++ b/tests/unit_node/child_process_test.ts @@ -1050,6 +1050,16 @@ Deno.test(function spawnSyncStdioUndefined() { assertEquals(ret.stderr.toString("utf-8").trim(), "world"); }); +Deno.test(function spawnSyncProcessStdioUsesFileDescriptors() { + const ret = spawnSync(Deno.execPath(), ["eval", ""], { + stdio: [process.stdin, process.stdout, process.stderr], + }); + + assertEquals(ret.status, 0); + assertEquals(ret.stdout, null); + assertEquals(ret.stderr, null); +}); + Deno.test(function spawnSyncExitNonZero() { const ret = spawnSync( `"${Deno.execPath()}" eval "Deno.exit(22)"`, From 5e8f2ddadf3c2bc9856f7bb8f47590212095d32c Mon Sep 17 00:00:00 2001 From: Tiancheng-Xu <44307608+Tiancheng-Xu@users.noreply.github.com> Date: Mon, 21 Sep 2026 02:08:47 -0400 Subject: [PATCH 2/2] fix(node): run piped stdin as CommonJS --- ext/node/ops/node_cli_parser.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ext/node/ops/node_cli_parser.rs b/ext/node/ops/node_cli_parser.rs index 3aa6c3c7894069..2d4ea8835d04b8 100644 --- a/ext/node/ops/node_cli_parser.rs +++ b/ext/node/ops/node_cli_parser.rs @@ -109,12 +109,17 @@ pub fn op_node_translate_cli_args( script_in_npm_package: bool, wrap_eval: bool, ) -> Result { - // If no args, return early with run -A - + // If no args, return early with run -A --ext=cjs - // `-` tells Deno to read from stdin, matching Node.js behavior where - // `node` with no args reads and executes piped stdin. + // `node` with no args reads and executes piped stdin as CommonJS. if args.is_empty() { return Ok(TranslatedArgs { - deno_args: vec!["run".to_string(), "-A".to_string(), "-".to_string()], + deno_args: vec![ + "run".to_string(), + "-A".to_string(), + "--ext=cjs".to_string(), + "-".to_string(), + ], node_options: vec![], ca_stores: None, use_openssl_ca: false,