34 lines
1.3 KiB
TypeScript
34 lines
1.3 KiB
TypeScript
// What the child process call sites share: the exit status, read right after
|
|
// `spawn` so the exit event cannot pass unseen while the caller reads the
|
|
// streams, and echoing output without outrunning a slow reader.
|
|
import type { ChildProcess } from "node:child_process";
|
|
import { once } from "node:events";
|
|
import { constants } from "node:os";
|
|
import type { Writable } from "node:stream";
|
|
|
|
// Resolves once the process exits, before its streams close, with its exit
|
|
// code, or with 128 plus the signal number if a signal killed it, as a shell
|
|
// reports. A process that fails to start rejects with the spawn error.
|
|
export function exitCode(child: ChildProcess): Promise<number> {
|
|
const code = new Promise<number>((resolve, reject) => {
|
|
child.once("error", reject);
|
|
child.once("exit", (exit, signal) => {
|
|
resolve(exit ?? (signal === null ? 1 : 128 + constants.signals[signal]));
|
|
});
|
|
});
|
|
// Callers await it after draining the streams; a failed start must not be
|
|
// reported as an unhandled rejection before then.
|
|
code.catch(() => {});
|
|
return code;
|
|
}
|
|
|
|
// Writes a chunk, waiting for the stream to drain when its buffer is full.
|
|
export async function echo(
|
|
stream: Writable,
|
|
chunk: string | Uint8Array,
|
|
): Promise<void> {
|
|
if (!stream.write(chunk)) {
|
|
await once(stream, "drain");
|
|
}
|
|
}
|