Skip to content

Commit

Permalink
Add ipc option
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Feb 7, 2024
1 parent 4080212 commit 8fffc29
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 146 deletions.
32 changes: 17 additions & 15 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ type CommonOptions<IsSync extends boolean = boolean> = {
- `'pipe'`: Sets [`childProcess.stdin`](https://nodejs.org/api/child_process.html#subprocessstdin) stream.
- `'overlapped'`: Like `'pipe'` but asynchronous on Windows.
- `'ignore'`: Do not use `stdin`.
- `'ipc'`: Sets an [IPC channel](https://nodejs.org/api/child_process.html#subprocesssendmessage-sendhandle-options-callback). You can also use `execaNode()` instead.
- `'inherit'`: Re-use the current process' `stdin`.
- an integer: Re-use a specific file descriptor from the current process.
- a Node.js `Readable` stream.
Expand All @@ -332,7 +331,6 @@ type CommonOptions<IsSync extends boolean = boolean> = {
- `'pipe'`: Sets `childProcessResult.stdout` (as a string or `Uint8Array`) and [`childProcess.stdout`](https://nodejs.org/api/child_process.html#subprocessstdout) (as a stream).
- `'overlapped'`: Like `'pipe'` but asynchronous on Windows.
- `'ignore'`: Do not use `stdout`.
- `'ipc'`: Sets an [IPC channel](https://nodejs.org/api/child_process.html#subprocesssendmessage-sendhandle-options-callback). You can also use `execaNode()` instead.
- `'inherit'`: Re-use the current process' `stdout`.
- an integer: Re-use a specific file descriptor from the current process.
- a Node.js `Writable` stream.
Expand All @@ -353,7 +351,6 @@ type CommonOptions<IsSync extends boolean = boolean> = {
- `'pipe'`: Sets `childProcessResult.stderr` (as a string or `Uint8Array`) and [`childProcess.stderr`](https://nodejs.org/api/child_process.html#subprocessstderr) (as a stream).
- `'overlapped'`: Like `'pipe'` but asynchronous on Windows.
- `'ignore'`: Do not use `stderr`.
- `'ipc'`: Sets an [IPC channel](https://nodejs.org/api/child_process.html#subprocesssendmessage-sendhandle-options-callback). You can also use `execaNode()` instead.
- `'inherit'`: Re-use the current process' `stderr`.
- an integer: Re-use a specific file descriptor from the current process.
- a Node.js `Writable` stream.
Expand All @@ -374,7 +371,7 @@ type CommonOptions<IsSync extends boolean = boolean> = {
A single string can be used as a shortcut. For example, `{stdio: 'pipe'}` is the same as `{stdin: 'pipe', stdout: 'pipe', stderr: 'pipe'}`.
The array can have more than 3 items, to create additional file descriptors beyond `stdin`/`stdout`/`stderr`. For example, `{stdio: ['pipe', 'pipe', 'pipe', 'ipc']}` sets a fourth file descriptor `'ipc'`.
The array can have more than 3 items, to create additional file descriptors beyond `stdin`/`stdout`/`stderr`. For example, `{stdio: ['pipe', 'pipe', 'pipe', 'pipe']}` sets a fourth file descriptor.
@default 'pipe'
*/
Expand Down Expand Up @@ -558,7 +555,12 @@ type CommonOptions<IsSync extends boolean = boolean> = {
readonly all?: IfAsync<IsSync, boolean>;

/**
Specify the kind of serialization used for sending messages between processes when using the `stdio: 'ipc'` option or `execaNode()`:
Sets an [IPC channel](https://nodejs.org/api/child_process.html#subprocesssendmessage-sendhandle-options-callback) between the current process and its child process.
*/
readonly ipc?: IfAsync<IsSync, boolean>;

/**
Specify the kind of serialization used for sending messages between processes when using the `ipc` option:
- `json`: Uses `JSON.stringify()` and `JSON.parse()`.
- `advanced`: Uses [`v8.serialize()`](https://nodejs.org/api/v8.html#v8_v8_serialize_value)
Expand Down Expand Up @@ -657,21 +659,21 @@ type ExecaCommonReturnValue<IsSync extends boolean = boolean, OptionsType extend
/**
The output of the process on `stdout`.
This is `undefined` if the `stdout` option is set to only [`'inherit'`, `'ipc'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). This is an array if the `lines` option is `true, or if the `stdout` option is a transform in object mode.
This is `undefined` if the `stdout` option is set to only [`'inherit'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). This is an array if the `lines` option is `true, or if the `stdout` option is a transform in object mode.
*/
stdout: StdioOutput<'1', OptionsType>;

/**
The output of the process on `stderr`.
This is `undefined` if the `stderr` option is set to only [`'inherit'`, `'ipc'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). This is an array if the `lines` option is `true, or if the `stderr` option is a transform in object mode.
This is `undefined` if the `stderr` option is set to only [`'inherit'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). This is an array if the `lines` option is `true, or if the `stderr` option is a transform in object mode.
*/
stderr: StdioOutput<'2', OptionsType>;

/**
The output of the process on `stdin`, `stdout`, `stderr` and other file descriptors.
Items are `undefined` when their corresponding `stdio` option is set to only [`'inherit'`, `'ipc'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). Items are arrays when their corresponding `stdio` option is a transform in object mode.
Items are `undefined` when their corresponding `stdio` option is set to only [`'inherit'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio). Items are arrays when their corresponding `stdio` option is a transform in object mode.
*/
stdio: StdioArrayOutput<OptionsType>;

Expand Down Expand Up @@ -723,7 +725,7 @@ type ExecaCommonReturnValue<IsSync extends boolean = boolean, OptionsType extend
This is `undefined` if either:
- the `all` option is `false` (default value)
- both `stdout` and `stderr` options are set to [`'inherit'`, `'ipc'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio)
- both `stdout` and `stderr` options are set to [`'inherit'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio)
This is an array if the `lines` option is `true, or if either the `stdout` or `stderr` option is a transform in object mode.
*/
Expand Down Expand Up @@ -801,7 +803,7 @@ export type ExecaChildPromise<OptionsType extends Options = Options> = {
This is `undefined` if either:
- the `all` option is `false` (the default value)
- both `stdout` and `stderr` options are set to [`'inherit'`, `'ipc'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio)
- both `stdout` and `stderr` options are set to [`'inherit'`, `'ignore'`, `Stream` or `integer`](https://nodejs.org/api/child_process.html#child_process_options_stdio)
*/
all: AllStream<OptionsType>;

Expand Down Expand Up @@ -938,7 +940,7 @@ export function execa<OptionsType extends Options = {}>(
/**
Same as `execa()` but synchronous.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `ipc`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Returns or throws a `childProcessResult`. The `childProcess` is not returned: its methods and properties are not available. This includes [`.kill()`](https://nodejs.org/api/child_process.html#subprocesskillsignal), [`.pid`](https://nodejs.org/api/child_process.html#subprocesspid), `.pipe()` and the [`.stdin`/`.stdout`/`.stderr`](https://nodejs.org/api/child_process.html#subprocessstdout) streams.
Expand Down Expand Up @@ -1039,7 +1041,7 @@ export function execaCommand<OptionsType extends Options = {}>(
/**
Same as `execaCommand()` but synchronous.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `ipc`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Returns or throws a `childProcessResult`. The `childProcess` is not returned: its methods and properties are not available. This includes [`.kill()`](https://nodejs.org/api/child_process.html#subprocesskillsignal), [`.pid`](https://nodejs.org/api/child_process.html#subprocesspid), `.pipe()` and the [`.stdin`/`.stdout`/`.stderr`](https://nodejs.org/api/child_process.html#subprocessstdout) streams.
Expand Down Expand Up @@ -1098,7 +1100,7 @@ type Execa$<OptionsType extends CommonOptions = {}> = {
/**
Same as $\`command\` but synchronous.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `ipc`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Returns or throws a `childProcessResult`. The `childProcess` is not returned: its methods and properties are not available. This includes [`.kill()`](https://nodejs.org/api/child_process.html#subprocesskillsignal), [`.pid`](https://nodejs.org/api/child_process.html#subprocesspid), `.pipe()` and the [`.stdin`/`.stdout`/`.stderr`](https://nodejs.org/api/child_process.html#subprocessstdout) streams.
Expand Down Expand Up @@ -1152,7 +1154,7 @@ type Execa$<OptionsType extends CommonOptions = {}> = {
/**
Same as $\`command\` but synchronous.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Cannot use the following options: `all`, `cleanup`, `buffer`, `detached`, `ipc`, `serialization`, `signal` and `lines`. Also, the `stdin`, `stdout`, `stderr`, `stdio` and `input` options cannot be an array, an iterable or a web stream. Node.js streams must have a file descriptor unless the `input` option is used.
Returns or throws a `childProcessResult`. The `childProcess` is not returned: its methods and properties are not available. This includes [`.kill()`](https://nodejs.org/api/child_process.html#subprocesskillsignal), [`.pid`](https://nodejs.org/api/child_process.html#subprocesspid), `.pipe()` and the [`.stdin`/`.stdout`/`.stderr`](https://nodejs.org/api/child_process.html#subprocessstdout) streams.
Expand Down Expand Up @@ -1269,7 +1271,7 @@ This is the preferred method when executing Node.js files.
Like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options):
- the current Node version and options are used. This can be overridden using the `nodePath` and `nodeOptions` options.
- the `shell` option cannot be used
- an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to `stdio`
- the `ipc` option defaults to `true`
@param scriptPath - Node.js script to execute, as a string or file URL
@param arguments - Arguments to pass to `scriptPath` on execution.
Expand Down
31 changes: 13 additions & 18 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {npmRunPathEnv} from 'npm-run-path';
import {makeError} from './lib/error.js';
import {handleInputAsync, pipeOutputAsync, cleanupStdioStreams} from './lib/stdio/async.js';
import {handleInputSync, pipeOutputSync} from './lib/stdio/sync.js';
import {normalizeStdioNode} from './lib/stdio/normalize.js';
import {spawnedKill, validateTimeout, normalizeForceKillAfterDelay, cleanupOnExit} from './lib/kill.js';
import {pipeToProcess} from './lib/pipe.js';
import {getSpawnedResult, makeAllStream} from './lib/stream.js';
Expand Down Expand Up @@ -86,6 +85,7 @@ const addDefaultOptions = ({
killSignal = 'SIGTERM',
forceKillAfterDelay = true,
lines = false,
ipc = false,
...options
}) => ({
...options,
Expand All @@ -106,6 +106,7 @@ const addDefaultOptions = ({
killSignal,
forceKillAfterDelay,
lines,
ipc,
});

// Prevent passing the `timeout` option directly to `child_process.spawn()`
Expand Down Expand Up @@ -223,6 +224,7 @@ const handlePromise = async ({spawned, options, stdioStreamsGroups, originalStre

export function execaSync(rawFile, rawArgs, rawOptions) {
const {file, args, command, escapedCommand, options} = handleArguments(rawFile, rawArgs, rawOptions);
validateSyncOptions(options);

const stdioStreamsGroups = handleInputSync(options);

Expand Down Expand Up @@ -286,6 +288,12 @@ export function execaSync(rawFile, rawArgs, rawOptions) {
};
}

const validateSyncOptions = ({ipc}) => {
if (ipc) {
throw new TypeError('The "ipc: true" option cannot be used with synchronous methods.');
}
};

const normalizeScriptStdin = ({input, inputFile, stdio}) => input === undefined && inputFile === undefined && stdio === undefined
? {stdin: 'inherit'}
: {};
Expand Down Expand Up @@ -332,34 +340,21 @@ export function execaCommandSync(command, options) {
return execaSync(file, args, options);
}

export function execaNode(scriptPath, args, options = {}) {
if (args && !Array.isArray(args) && typeof args === 'object') {
export function execaNode(scriptPath, args = [], options = {}) {
if (!Array.isArray(args)) {
options = args;
args = [];
}

const stdio = normalizeStdioNode(options);
const defaultExecArgv = process.execArgv.filter(arg => !arg.startsWith('--inspect'));

const {
nodePath = process.execPath,
nodeOptions = defaultExecArgv,
} = options;

return execa(
nodePath,
[
...nodeOptions,
getFilePath(scriptPath),
...(Array.isArray(args) ? args : []),
],
{
...options,
stdin: undefined,
stdout: undefined,
stderr: undefined,
stdio,
shell: false,
},
[...nodeOptions, getFilePath(scriptPath), ...args],
{ipc: true, ...options, shell: false},
);
}
2 changes: 2 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,8 @@ expectError(execaSync('unicorns', {stdio: [[[new Uint8Array(), new Uint8Array()]
expectError(execaSync('unicorns', {stdio: [[[{}, {}]]]}));
expectError(execaSync('unicorns', {stdio: [[emptyStringGenerator()]]}));
expectError(execaSync('unicorns', {stdio: [[asyncStringGenerator()]]}));
execa('unicorns', {ipc: true});
expectError(execaSync('unicorns', {ipc: true}));
execa('unicorns', {serialization: 'advanced'});
expectError(execaSync('unicorns', {serialization: 'advanced'}));
execa('unicorns', {detached: true});
Expand Down
19 changes: 7 additions & 12 deletions lib/stdio/normalize.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {STANDARD_STREAMS_ALIASES} from './utils.js';

// Add support for `stdin`/`stdout`/`stderr` as an alias for `stdio`
export const normalizeStdio = options => {
if (!options) {
return [undefined, undefined, undefined];
}

const {stdio} = options;
export const normalizeStdio = ({stdio, ipc, ...options}) => {
const stdioArray = getStdioArray(stdio, options);
return ipc && !stdioArray.includes('ipc')
? [...stdioArray, 'ipc']
: stdioArray;
};

const getStdioArray = (stdio, options) => {
if (stdio === undefined) {
return STANDARD_STREAMS_ALIASES.map(alias => options[alias]);
}
Expand All @@ -29,9 +30,3 @@ export const normalizeStdio = options => {
};

const hasAlias = options => STANDARD_STREAMS_ALIASES.some(alias => options[alias] !== undefined);

// Same but for `execaNode()`, i.e. push `ipc` unless already present
export const normalizeStdioNode = options => {
const stdio = normalizeStdio(options);
return stdio.includes('ipc') ? stdio : [...stdio, 'ipc'];
};

0 comments on commit 8fffc29

Please sign in to comment.