Skip to content

Latest commit

 

History

History
117 lines (79 loc) · 5.94 KB

streams.md

File metadata and controls

117 lines (79 loc) · 5.94 KB
execa logo

⏳️ Streams

Node.js streams

Input

import {createReadStream} from 'node:fs';
import {once} from 'node:events';
import {execa} from 'execa';

const readable = createReadStream('input.txt');
await once(readable, 'open');
await execa({stdin: readable})`npm run scaffold`;

Output

import {createWriteStream} from 'node:fs';
import {once} from 'node:events';
import {execa} from 'execa';

const writable = createWriteStream('output.txt');
await once(writable, 'open');
await execa({stdout: writable})`npm run build`;

File descriptors

When passing a Node.js stream to the stdin, stdout or stderr option, that stream must have an underlying file or socket, such as the streams created by the fs, net or http core modules. Otherwise the following error is thrown.

TypeError [ERR_INVALID_ARG_VALUE]: The argument 'stdio' is invalid.

This limitation can be worked around by either:

Web streams

Web streams (ReadableStream or WritableStream) can be used instead of Node.js streams.

const response = await fetch('https://example.com');
await execa({stdin: response.body})`npm run build`;

Iterables as input

const getReplInput = async function * () {
	for await (const replLine of getReplLines()) {
		yield replLine;
	}
};

await execa({stdin: getReplInput()})`npm run scaffold`;

Manual streaming

subprocess.stdin is a Node.js Readable stream and subprocess.stdout/subprocess.stderr/subprocess.all are Node.js Writable streams.

They can be used to stream input/output manually. This is intended for advanced situations. In most cases, the following simpler solutions can be used instead:

Converting a subprocess to a stream

Convert

The subprocess.readable(), subprocess.writable() and subprocess.duplex() methods convert the subprocess to a Node.js Readable, Writable and Duplex stream.

This is useful when using a library or API that expects Node.js streams as arguments. In every other situation, the simpler solutions described above can be used instead.

const readable = execa`npm run scaffold`.readable();

const writable = execa`npm run scaffold`.writable();

const duplex = execa`npm run scaffold`.duplex();

Different file descriptor

By default, subprocess.readable(), subprocess.writable() and subprocess.duplex() methods use stdin and stdout. This can be changed using the from and to options.

const readable = execa`npm run scaffold`.readable({from: 'stderr'});

const writable = execa`npm run scaffold`.writable({to: 'fd3'});

const duplex = execa`npm run scaffold`.duplex({from: 'stderr', to: 'fd3'});

Error handling

When using subprocess.readable(), subprocess.writable() or subprocess.duplex(), the stream waits for the subprocess to end, and emits an error event if the subprocess fails. This differs from subprocess.stdin, subprocess.stdout and subprocess.stderr's behavior.

This means you do not need to await the subprocess' promise. On the other hand, you (or the library using the stream) do need to both consume the stream, and handle its error event. This can be done by using await finished(stream), await pipeline(..., stream, ...) or await text(stream) which throw an exception when the stream errors.


Next: 📞 Inter-process communication
Previous: 🔀 Piping multiple subprocesses
Top: Table of contents