mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
fix(node): support passing parent stdio streams (#19171)
This is a bit bare bones but gets `npm-run-all` working. For full stdio compatibility with node more work is needed which is probably better done in follow up PRs. Fixes #19159
This commit is contained in:
parent
9dc3ae8523
commit
695b5de6cb
3 changed files with 77 additions and 10 deletions
|
@ -577,3 +577,25 @@ Deno.test(
|
|||
assertStringIncludes(output, "typescript");
|
||||
},
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"[node/child_process spawn] supports stdio array option",
|
||||
async () => {
|
||||
const cmdFinished = deferred();
|
||||
let output = "";
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"testdata",
|
||||
"child_process_stdio.js",
|
||||
);
|
||||
const cp = spawn(Deno.execPath(), ["run", "-A", script]);
|
||||
cp.stdout?.on("data", (data) => {
|
||||
output += data;
|
||||
});
|
||||
cp.on("close", () => cmdFinished.resolve());
|
||||
await cmdFinished;
|
||||
|
||||
assertStringIncludes(output, "foo");
|
||||
assertStringIncludes(output, "close");
|
||||
},
|
||||
);
|
||||
|
|
15
cli/tests/unit_node/testdata/child_process_stdio.js
vendored
Normal file
15
cli/tests/unit_node/testdata/child_process_stdio.js
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
import childProcess from "node:child_process";
|
||||
import process from "node:process";
|
||||
import * as path from "node:path";
|
||||
|
||||
const script = path.join(
|
||||
path.dirname(path.fromFileUrl(import.meta.url)),
|
||||
"node_modules",
|
||||
"foo",
|
||||
"index.js",
|
||||
);
|
||||
|
||||
const child = childProcess.spawn(process.execPath, [script], {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
});
|
||||
child.on("close", () => console.log("close"));
|
|
@ -177,9 +177,9 @@ export class ChildProcess extends EventEmitter {
|
|||
args: cmdArgs,
|
||||
cwd,
|
||||
env: stringEnv,
|
||||
stdin: toDenoStdio(stdin as NodeStdio | number),
|
||||
stdout: toDenoStdio(stdout as NodeStdio | number),
|
||||
stderr: toDenoStdio(stderr as NodeStdio | number),
|
||||
stdin: toDenoStdio(stdin),
|
||||
stdout: toDenoStdio(stdout),
|
||||
stderr: toDenoStdio(stderr),
|
||||
windowsRawArguments: windowsVerbatimArguments,
|
||||
}).spawn();
|
||||
this.pid = this.#process.pid;
|
||||
|
@ -189,6 +189,16 @@ export class ChildProcess extends EventEmitter {
|
|||
this.stdin = Writable.fromWeb(this.#process.stdin);
|
||||
}
|
||||
|
||||
if (stdin instanceof Stream) {
|
||||
this.stdin = stdin;
|
||||
}
|
||||
if (stdout instanceof Stream) {
|
||||
this.stdout = stdout;
|
||||
}
|
||||
if (stderr instanceof Stream) {
|
||||
this.stderr = stderr;
|
||||
}
|
||||
|
||||
if (stdout === "pipe") {
|
||||
assert(this.#process.stdout);
|
||||
this.stdout = Readable.fromWeb(this.#process.stdout);
|
||||
|
@ -285,15 +295,22 @@ export class ChildProcess extends EventEmitter {
|
|||
|
||||
async #_waitForChildStreamsToClose() {
|
||||
const promises = [] as Array<Promise<void>>;
|
||||
if (this.stdin && !this.stdin.destroyed) {
|
||||
// Don't close parent process stdin if that's passed through
|
||||
if (this.stdin && !this.stdin.destroyed && this.stdin !== process.stdin) {
|
||||
assert(this.stdin);
|
||||
this.stdin.destroy();
|
||||
promises.push(waitForStreamToClose(this.stdin));
|
||||
}
|
||||
if (this.stdout && !this.stdout.destroyed) {
|
||||
// Only readable streams need to be closed
|
||||
if (
|
||||
this.stdout && !this.stdout.destroyed && this.stdout instanceof Readable
|
||||
) {
|
||||
promises.push(waitForReadableToClose(this.stdout));
|
||||
}
|
||||
if (this.stderr && !this.stderr.destroyed) {
|
||||
// Only readable streams need to be closed
|
||||
if (
|
||||
this.stderr && !this.stderr.destroyed && this.stderr instanceof Readable
|
||||
) {
|
||||
promises.push(waitForReadableToClose(this.stderr));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
@ -317,9 +334,13 @@ const supportedNodeStdioTypes: NodeStdio[] = ["pipe", "ignore", "inherit"];
|
|||
function toDenoStdio(
|
||||
pipe: NodeStdio | number | Stream | null | undefined,
|
||||
): DenoStdio {
|
||||
if (pipe instanceof Stream) {
|
||||
return "inherit";
|
||||
}
|
||||
|
||||
if (
|
||||
!supportedNodeStdioTypes.includes(pipe as NodeStdio) ||
|
||||
typeof pipe === "number" || pipe instanceof Stream
|
||||
typeof pipe === "number"
|
||||
) {
|
||||
notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`);
|
||||
}
|
||||
|
@ -441,8 +462,17 @@ function normalizeStdioOption(
|
|||
"pipe",
|
||||
"pipe",
|
||||
],
|
||||
) {
|
||||
): [
|
||||
Stream | NodeStdio | number,
|
||||
Stream | NodeStdio | number,
|
||||
Stream | NodeStdio | number,
|
||||
...Array<Stream | NodeStdio | number>,
|
||||
] {
|
||||
if (Array.isArray(stdio)) {
|
||||
// At least 3 stdio must be created to match node
|
||||
while (stdio.length < 3) {
|
||||
ArrayPrototypePush(stdio, undefined);
|
||||
}
|
||||
return stdio;
|
||||
} else {
|
||||
switch (stdio) {
|
||||
|
@ -796,8 +826,8 @@ export function spawnSync(
|
|||
args,
|
||||
cwd,
|
||||
env,
|
||||
stdout: toDenoStdio(normalizedStdio[1] as NodeStdio | number),
|
||||
stderr: toDenoStdio(normalizedStdio[2] as NodeStdio | number),
|
||||
stdout: toDenoStdio(normalizedStdio[1]),
|
||||
stderr: toDenoStdio(normalizedStdio[2]),
|
||||
uid,
|
||||
gid,
|
||||
windowsRawArguments: windowsVerbatimArguments,
|
||||
|
|
Loading…
Add table
Reference in a new issue