Skip to content

Commit 0726126

Browse files
authored
Add inputFile option (#542)
1 parent 5320fef commit 0726126

File tree

7 files changed

+101
-14
lines changed

7 files changed

+101
-14
lines changed

docs/scripts.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ await cat
635635
636636
```js
637637
// Execa
638-
await $({input: fs.createReadStream('file.txt')})`cat`
638+
await $({inputFile: 'file.txt'})`cat`
639639
```
640640
641641
### Silent stderr

index.d.ts

+18
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,33 @@ export type CommonOptions<EncodingType> = {
258258
export type Options<EncodingType = string> = {
259259
/**
260260
Write some input to the `stdin` of your binary.
261+
262+
If the input is a file, use the `inputFile` option instead.
261263
*/
262264
readonly input?: string | Buffer | ReadableStream;
265+
266+
/**
267+
Use a file as input to the the `stdin` of your binary.
268+
269+
If the input is not a file, use the `input` option instead.
270+
*/
271+
readonly inputFile?: string;
263272
} & CommonOptions<EncodingType>;
264273

265274
export type SyncOptions<EncodingType = string> = {
266275
/**
267276
Write some input to the `stdin` of your binary.
277+
278+
If the input is a file, use the `inputFile` option instead.
268279
*/
269280
readonly input?: string | Buffer;
281+
282+
/**
283+
Use a file as input to the the `stdin` of your binary.
284+
285+
If the input is not a file, use the `input` option instead.
286+
*/
287+
readonly inputFile?: string;
270288
} & CommonOptions<EncodingType>;
271289

272290
export type NodeOptions<EncodingType = string> = {

index.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {makeError} from './lib/error.js';
1010
import {normalizeStdio, normalizeStdioNode} from './lib/stdio.js';
1111
import {spawnedKill, spawnedCancel, setupTimeout, validateTimeout, setExitHandler} from './lib/kill.js';
1212
import {addPipeMethods} from './lib/pipe.js';
13-
import {handleInput, getSpawnedResult, makeAllStream, validateInputSync} from './lib/stream.js';
13+
import {handleInput, getSpawnedResult, makeAllStream, handleInputSync} from './lib/stream.js';
1414
import {mergePromise, getSpawnedPromise} from './lib/promise.js';
1515
import {joinCommand, parseCommand, parseTemplates, getEscapedCommand} from './lib/command.js';
1616
import {logCommand, verboseDefault} from './lib/verbose.js';
@@ -159,7 +159,7 @@ export function execa(file, args, options) {
159159

160160
const handlePromiseOnce = onetime(handlePromise);
161161

162-
handleInput(spawned, parsed.options.input);
162+
handleInput(spawned, parsed.options);
163163

164164
spawned.all = makeAllStream(spawned, parsed.options);
165165

@@ -174,11 +174,11 @@ export function execaSync(file, args, options) {
174174
const escapedCommand = getEscapedCommand(file, args);
175175
logCommand(escapedCommand, parsed.options);
176176

177-
validateInputSync(parsed.options);
177+
const input = handleInputSync(parsed.options);
178178

179179
let result;
180180
try {
181-
result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options);
181+
result = childProcess.spawnSync(parsed.file, parsed.args, {...parsed.options, input});
182182
} catch (error) {
183183
throw makeError({
184184
error,

index.test-d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ execa('unicorns', {buffer: false});
134134
execa('unicorns', {input: ''});
135135
execa('unicorns', {input: Buffer.from('')});
136136
execa('unicorns', {input: process.stdin});
137+
execa('unicorns', {inputFile: ''});
137138
execa('unicorns', {stdin: 'pipe'});
138139
execa('unicorns', {stdin: 'overlapped'});
139140
execa('unicorns', {stdin: 'ipc'});

lib/stream.js

+40-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,47 @@
1+
import {createReadStream, readFileSync} from 'node:fs';
12
import {isStream} from 'is-stream';
23
import getStream from 'get-stream';
34
import mergeStream from 'merge-stream';
45

5-
// `input` option
6-
export const handleInput = (spawned, input) => {
6+
const validateInputOptions = input => {
7+
if (input !== undefined) {
8+
throw new TypeError('The `input` and `inputFile` options cannot be both set.');
9+
}
10+
};
11+
12+
const getInputSync = ({input, inputFile}) => {
13+
if (typeof inputFile !== 'string') {
14+
return input;
15+
}
16+
17+
validateInputOptions(input);
18+
return readFileSync(inputFile);
19+
};
20+
21+
// `input` and `inputFile` option in sync mode
22+
export const handleInputSync = options => {
23+
const input = getInputSync(options);
24+
25+
if (isStream(input)) {
26+
throw new TypeError('The `input` option cannot be a stream in sync mode');
27+
}
28+
29+
return input;
30+
};
31+
32+
const getInput = ({input, inputFile}) => {
33+
if (typeof inputFile !== 'string') {
34+
return input;
35+
}
36+
37+
validateInputOptions(input);
38+
return createReadStream(inputFile);
39+
};
40+
41+
// `input` and `inputFile` option in async mode
42+
export const handleInput = (spawned, options) => {
43+
const input = getInput(options);
44+
745
if (input === undefined) {
846
return;
947
}
@@ -79,9 +117,3 @@ export const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer,
79117
]);
80118
}
81119
};
82-
83-
export const validateInputSync = ({input}) => {
84-
if (isStream(input)) {
85-
throw new TypeError('The `input` option cannot be a stream in sync mode');
86-
}
87-
};

readme.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ await execa('echo', ['unicorns'], {all:true}).pipeAll('all.txt');
128128
import {execa} from 'execa';
129129

130130
// Similar to `cat < stdin.txt` in Bash
131-
const {stdout} = await execa('cat', {input:fs.createReadStream('stdin.txt')});
131+
const {stdout} = await execa('cat', {inputFile:'stdin.txt'});
132132
console.log(stdout);
133133
//=> 'unicorns'
134134
```
@@ -497,6 +497,16 @@ Type: `string | Buffer | stream.Readable`
497497
Write some input to the `stdin` of your binary.\
498498
Streams are not allowed when using the synchronous methods.
499499

500+
If the input is a file, use the [`inputFile` option](#inputfile) instead.
501+
502+
#### inputFile
503+
504+
Type: `string`
505+
506+
Use a file as input to the the `stdin` of your binary.
507+
508+
If the input is not a file, use the [`input` option](#input) instead.
509+
500510
#### stdin
501511

502512
Type: `string | number | Stream | undefined`\

test/stream.js

+26
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ test('input can be a Stream', async t => {
7171
t.is(stdout, 'howdy');
7272
});
7373

74+
test('inputFile can be set', async t => {
75+
const inputFile = tempfile();
76+
fs.writeFileSync(inputFile, 'howdy');
77+
const {stdout} = await execa('stdin.js', {inputFile});
78+
t.is(stdout, 'howdy');
79+
});
80+
81+
test('inputFile and input cannot be both set', t => {
82+
t.throws(() => execa('stdin.js', {inputFile: '', input: ''}), {
83+
message: /cannot be both set/,
84+
});
85+
});
86+
7487
test('you can write to child.stdin', async t => {
7588
const subprocess = execa('stdin.js');
7689
subprocess.stdin.end('unicorns');
@@ -105,6 +118,19 @@ test('helpful error trying to provide an input stream in sync mode', t => {
105118
);
106119
});
107120

121+
test('inputFile can be set - sync', t => {
122+
const inputFile = tempfile();
123+
fs.writeFileSync(inputFile, 'howdy');
124+
const {stdout} = execaSync('stdin.js', {inputFile});
125+
t.is(stdout, 'howdy');
126+
});
127+
128+
test('inputFile and input cannot be both set - sync', t => {
129+
t.throws(() => execaSync('stdin.js', {inputFile: '', input: ''}), {
130+
message: /cannot be both set/,
131+
});
132+
});
133+
108134
test('maxBuffer affects stdout', async t => {
109135
await t.notThrowsAsync(execa('max-buffer.js', ['stdout', '10'], {maxBuffer: 10}));
110136
const {stdout, all} = await t.throwsAsync(execa('max-buffer.js', ['stdout', '11'], {maxBuffer: 10, all: true}), {message: /max-buffer.js stdout/});

0 commit comments

Comments
 (0)