Duplex stream #424
Duplex stream #424
Conversation
process end event may come before stdin/stdout streams flush
| failingStream.destroy(new Error('oops')); | ||
| const {message} = await t.throwsAsync(pipeline(failingStream, duplex, makeCollector())); | ||
| t.is(message, 'oops'); | ||
| const err = await t.throwsAsync(duplex); |
sindresorhus
May 16, 2020
Owner
err => error
err => error
|
Thanks for working on this Make sure you add a lot of tests. |
This comment has been minimized.
This comment has been minimized.
|
Do you need to implement https://nodejs.org/api/stream.html#stream_api_for_stream_implementers |
|
No need to implement Do you have an idea on what other tests I could make ? Unless someone can lend me a hand, it will take some times for me to track the test failures on windows ; I don't have access to a windows machine right now. |
|
Okay, found why some tests where failing on Windows and not Linux, but that raises more questions @sindresorhus It’s on unit tests testing this code path : https://github.com/sloonz/execa/blob/28ac048159f6ea1fa2c27499451c014a74e80265/index.js#L268-L287 I tried to trigger an exception on line 266 by passing Two questions :
|
6b273d5
to
7cbc2ad
|
I think the feature is in a good spot for review/possible integration in the current iteration ; should I squash the commits ? |
| @@ -544,6 +544,20 @@ declare const execa: { | |||
| ): execa.ExecaChildProcess<Buffer>; | |||
| node(scriptPath: string, options?: execa.Options): execa.ExecaChildProcess; | |||
| node(scriptPath: string, options?: execa.Options<null>): execa.ExecaChildProcess<Buffer>; | |||
|
|
|||
| /** | |||
| Execute a file as a transform stream | |||
sindresorhus
Jun 1, 2020
Owner
Suggested change
Execute a file as a transform stream
Execute a file as a transform stream.
And it's not a transform stream.
| Execute a file as a transform stream | |
| Execute a file as a transform stream. |
And it's not a transform stream.
| result.then(() => {}).catch(error => error), | ||
| new Promise((resolve, _) => stdin.on('close', resolve)), | ||
| new Promise((resolve, _) => output.on('close', resolve)) | ||
| ]).then(([error]) => { |
sindresorhus
Jun 1, 2020
Owner
Use await.
Use await.
| }); | ||
|
|
||
| Promise.all([ | ||
| result.then(() => {}).catch(error => error), |
sindresorhus
Jun 1, 2020
Owner
Do you really need .then here?
Do you really need .then here?
sloonz
Jun 17, 2020
Author
Yes, otherwise non-error result will be resolved by the complete promise. A more explicit intent would be
result.then(() => { return null; }).catch(error => error)
But anyway this is changed by the use of await instead of then.
Yes, otherwise non-error result will be resolved by the complete promise. A more explicit intent would be
result.then(() => { return null; }).catch(error => error)
But anyway this is changed by the use of await instead of then.
| } | ||
| }); | ||
|
|
||
| const result = processDone.then(({error, exitCode, signal, timedOut}) => { |
sindresorhus
Jun 1, 2020
Owner
Use await.
Use await.
|
|
||
| function makeCollector() { | ||
| const chunks = []; | ||
| const w = new stream.Writable({ |
sindresorhus
Jun 1, 2020
Owner
Use descriptive variable names.
Use descriptive variable names.
| function makeCollector() { | ||
| const chunks = []; | ||
| const w = new stream.Writable({ | ||
| write(chunk, encoding, cb) { |
sindresorhus
Jun 1, 2020
Owner
Suggested change
write(chunk, encoding, cb) {
write(chunk, encoding, callback) {
| write(chunk, encoding, cb) { | |
| write(chunk, encoding, callback) { |
| const duplex = execa.duplexStream('forever'); | ||
| const failingStream = makeEmptyReadableStream(); | ||
| failingStream.destroy(new Error('oops')); | ||
| const {message} = await t.throwsAsync(pipeline(failingStream, duplex, makeCollector())); |
sindresorhus
Jun 1, 2020
Owner
Suggested change
const {message} = await t.throwsAsync(pipeline(failingStream, duplex, makeCollector()));
await t.throwsAsync(pipeline(failingStream, duplex, makeCollector()), {message: 'oops'});
| const {message} = await t.throwsAsync(pipeline(failingStream, duplex, makeCollector())); | |
| await t.throwsAsync(pipeline(failingStream, duplex, makeCollector()), {message: 'oops'}); |
No need. We'll squash when merging. |
|
I would like to see even more tests for this. It's a complicated feature (streams have a lot of edge-cases). |
|
Sorry for the hiatus. New iteration of the branch is there. No new test because my dumb brain can't generate any idea of what to test that isn't currently covered. Do you have any suggestion ? |
|
Small bump ? |
|
Would be nice to DRY up some code between the duplex method and the others. |
| return error; | ||
| } | ||
| })(), | ||
| new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars |
sindresorhus
Aug 10, 2020
Owner
Don't disable the rule. Just adhere to it.
Don't disable the rule. Just adhere to it.
| })(); | ||
|
|
||
| (async () => { | ||
| const promisesResult = (await Promise.all([ |
sindresorhus
Aug 10, 2020
Owner
Use destructuring.
Use destructuring.
| (async () => { | ||
| try { | ||
| await result; | ||
| return null; |
sindresorhus
Aug 10, 2020
Owner
Suggested change
return null;
return;
use undefined
| return null; | |
| return; |
use undefined
| output.on('close', resolve); | ||
| }) | ||
| ])); | ||
| if (promisesResult[0] !== null) { |
sindresorhus
Aug 10, 2020
Owner
Suggested change
if (promisesResult[0] !== null) {
if (promisesResult[0] !== null) {
| if (promisesResult[0] !== null) { | |
| if (promisesResult[0] !== null) { |
| } catch (error) { | ||
| const errorPromise = Promise.reject(makeError({ | ||
| error, | ||
| stdout: undefined, |
sindresorhus
Aug 10, 2020
Owner
This should not be undefined.
This should not be undefined.
| callback(); | ||
| } | ||
| }); | ||
| writableStream.collect = () => Buffer.concat(chunks).toString('utf-8'); |
sindresorhus
Aug 10, 2020
Owner
Suggested change
writableStream.collect = () => Buffer.concat(chunks).toString('utf-8');
writableStream.collect = () => Buffer.concat(chunks).toString('utf8');
| writableStream.collect = () => Buffer.concat(chunks).toString('utf-8'); | |
| writableStream.collect = () => Buffer.concat(chunks).toString('utf8'); |
| t.is(message, 'oops'); | ||
| const error = await t.throwsAsync(duplex); | ||
| t.is(error.isCanceled, true); | ||
| t.is(error.killed, true); |
sindresorhus
Aug 10, 2020
Owner
Suggested change
t.is(error.killed, true);
t.true(error.killed);
| t.is(error.killed, true); | |
| t.true(error.killed); |
|
In general for PRs:
|
|
I would like your opinion on something. I planned to refactor in two parts, but now I wonder if the second part is needed. In the previous implementation the result of Pros: way simpler code and interface I tend to prefer the simpler solution, but at the end it's your call :) |

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

First shot at an implementation for fixes #143