Memory-efficient utilities to deal with child_process
native Node.js package.
npm i child-process-utilities
This method does not use any sort of buffering. Which means that we do not cache the entire output into memory.
The values returned by stdout
and stderr
can be iterated directly by default.
import { spawn } from "child-process-utilities";
const childProcess = spawn(/* ... */, /* ... */, /* ... */);
for await (const chunk of childProcess.output().stdout()) {
// ^ Uint8Array
console.log("Chunk: %s", chunk);
}
In the example below, the output is streamed line by line, since we're using the \n
character, but any other character can be passed to split
.
import { spawn } from "child-process-utilities";
const lines = spawn
.pipe(bin.curl, ["-L", project.license.url])
.output()
.stdout()
.split("\n"); // Returns an AsyncIterableIterator<string>
for await (const line of lines) {
console.log("This is a line: %s", line);
}
// Or
const chunks = spawn
.pipe(bin.curl, ["-L", project.license.url])
.output()
.stdout(); // `IReadableHelper` is an async iterator by itself
for await (const chunk of chunks) {
// ^ Uint8Array
console.log("Chunk: %s", line);
}
Please note that for performance reasons any decisive output functions can only be called once.
const childProcess = spawn(/* ... */, /* ... */, /* ... */);
childProcess.output().stderr().raw() // Do
childProcess.output().stderr().raw() // Don't
// stderr ^^^^^^
// stdout
childProcess.output().stdout().raw() // Do
childProcess.output().stdout().raw() // Don't
// stdout ^^^^^^
import { spawn } from "child-process-utilities";
export default async function () {
await spawn("npx", ["ts-node", "src"]).wait();
}
import { spawn } from "child-process-utilities";
export default async function () {
await spawn.pipe("npx", ["ts-node", "src"]).wait();
}
import { spawn } from "child-process-utilities";
export default async function () {
const { stdout, stderr } = await spawn("npx", ["ts-node", "src"]).output();
console.log(await stdout().decode("UTF-8")); // Returns a string
console.error(await stderr().decode("UTF-8")); // Returns a string
}
import { spawn } from "child-process-utilities";
export default async function () {
const { stdout, stderr } = await spawn("npx", ["ts-node", "src"]).output();
console.log(await stdout().raw()); // Returns an Uint8Array
console.error(await stderr().raw()); // Returns an Uint8Array
}
import { spawn } from "child-process-utilities";
export default async function () {
const { stdout, stderr } = await spawn("npx", ["ts-node", "src"]).output();
console.log(await stdout().json()); // Parses the stdout as JSON
console.error(await stderr().json()); // Parses the stderr as JSON
}
You can pass a type to the spawn
function to infer the return type of the output
method. Currently, we only support defining a type for the json
output
property.
// (method) IReadableHelper<{ json: number; }>.json<number>(): Promise<number>
spawn</* Using TypeScript inline types */ { json: number }>("x")
.output()
.stdout()
.json(); // Promise<number>
interface IVideoMetadata {
duration: number;
fileName: string;
}
interface IGetVideoMetadataTypes {
json: IVideoMetadata;
}
// (method) IReadableHelper<IVideoMetadata>.json<IVideoMetadata>(): Promise<IVideoMetadata>
spawn<IGetVideoMetadataTypes>("/home/user/get-video-metadata.sh", [
"video.mp4" /* ... */,
])
.output()
.stdout()
.json(); // Promise<IVideoMetadata>
// (method) IReadableHelper<{ json: unknown; }>.json<unknown>(): Promise<unknown>
spawn("x").output().stdout().json(); // Promise<unknown>
Advantages on this approach is that you can define a spawn method that returns a predefined type:
import { spawn } from "child-process-utilities";
export interface IVideoMetadata {
duration: number;
fileName: string;
}
export interface IGetVideoMetadataTypes {
json: IVideoMetadata;
}
const getVideoMetadata = (url: string) =>
spawn<IGetVideoMetadataTypes>("/home/user/get-video-metadata.sh", [url]);
export default getVideoMetadata;
import getVideoMetadata from "./getVideoMetadata";
export default async function askForVideoMetadata() {
const pendingVideoMetadata = getVideoMetadata("video.mp4");
const error = await pendingVideoMetadata.output().stderr().raw();
try {
const videoMetadata = await pendingVideoMetadata.output().stdout().json();
} catch (error) {
process.stderr.write(error);
}
}