Skip to content

Latest commit

 

History

History
190 lines (125 loc) · 6.2 KB

transform.md

File metadata and controls

190 lines (125 loc) · 6.2 KB
execa logo

Transforms

Summary

Transforms map or filter the input or output of a subprocess. They are defined by passing a generator function or a transform options object to the stdin, stdout, stderr or stdio option. It can be async.

import {execa} from 'execa';

const transform = function * (line) {
	const prefix = line.includes('error') ? 'ERROR' : 'INFO';
	yield `${prefix}: ${line}`;
};

const {stdout} = await execa({stdout: transform})`npm run build`;
console.log(stdout); // HELLO

Filtering

yield can be called 0, 1 or multiple times. Not calling yield enables filtering a specific line.

import {execa} from 'execa';

const transform = function * (line) {
	if (!line.includes('secret')) {
		yield line;
	}
};

const {stdout} = await execa({stdout: transform})`echo ${'This is a secret'}`;
console.log(stdout); // ''

Object mode

By default, stdout and stderr's transforms must return a string or an Uint8Array.
However, if the objectMode transform option is true, any type can be returned instead, except null or undefined. The subprocess' result.stdout/result.stderr will be an array of values.

const transform = function * (line) {
	yield JSON.parse(line);
};

const {stdout} = await execa({stdout: {transform, objectMode: true}})`node jsonlines-output.js`;
for (const data of stdout) {
	console.log(stdout); // {...}
}

stdin can also use objectMode: true.

const transform = function * (line) {
	yield JSON.stringify(line);
};

const input = [{event: 'example'}, {event: 'otherExample'}];
await execa({stdin: [input, {transform, objectMode: true}]})`node jsonlines-input.js`;

Sharing state

State can be shared between calls of the transform and final functions.

let count = 0

// Prefix line number
const transform = function * (line) {
	yield `[${count++}] ${line}`;
};

Finalizing

To create additional lines after the last one, a final generator function can be used.

let count = 0;

const transform = function * (line) {
	count += 1;
	yield line;
};

const final = function * () {
	yield `Number of lines: ${count}`;
};

const {stdout} = await execa({stdout: {transform, final}})`npm run build`;
console.log(stdout); // Ends with: 'Number of lines: 54'

Duplex/Transform streams

A Duplex stream, Node.js Transform stream or web TransformStream can be used instead of a generator function.

Like generator functions, web TransformStream can be passed either directly or as a {transform} plain object. But Duplex and Transform must always be passed as a {transform} plain object.

The objectMode transform option can be used, but not the binary nor preserveNewlines options.

import {createGzip} from 'node:zlib';
import {execa} from 'execa';

const {stdout} = await execa({stdout: {transform: createGzip()}})`npm run build`;
console.log(stdout); // `stdout` is compressed with gzip
const {stdout} = await execa({stdout: new CompressionStream('gzip')})`npm run build`;
console.log(stdout); // `stdout` is compressed with gzip

Combining

The stdin, stdout, stderr and stdio options can accept an array of values. While this is not specific to transforms, this can be useful with them too. For example, the following transform impacts the value printed by inherit.

await execa({stdout: [transform, 'inherit']})`npm run build`;

This also allows using multiple transforms.

await execa({stdout: [transform, otherTransform]})`npm run build`;

Or saving to files.

await execa({stdout: [new CompressionStream('gzip'), {file: './output.gz'}]})`npm run build`;

Transform options

A transform or an array of transforms can be passed to the stdin, stdout, stderr or stdio option.

A transform is either a generator function or a plain object with the following members.

transformOptions.transform

Type: GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown>

Map or filter the input or output of the subprocess.

More info here and there.

transformOptions.final

Type: GeneratorFunction<string | Uint8Array | unknown> | AsyncGeneratorFunction<string | Uint8Array | unknown>

Create additional lines after the last one.

More info.

transformOptions.binary

Type: boolean
Default: false

If true, iterate over arbitrary chunks of Uint8Arrays instead of line strings.

More info.

transformOptions.preserveNewlines

Type: boolean
Default: false

If true, keep newlines in each line argument. Also, this allows multiple yields to produces a single line.

More info.

transformOptions.objectMode

Type: boolean
Default: false

If true, allow transformOptions.transform and transformOptions.final to return any type, not just string or Uint8Array.

More info.