Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve default value of the stdin option with $ #742

Closed
ehmicky opened this issue Jan 26, 2024 · 4 comments
Closed

Improve default value of the stdin option with $ #742

ehmicky opened this issue Jan 26, 2024 · 4 comments

Comments

@ehmicky
Copy link
Collaborator

ehmicky commented Jan 26, 2024

Background

Some commands need their stdin to be interactive. For example, script prompts.

Other commands need it to be programmatic. For example when:

  • piping processes, childProcess.pipeStdout(otherChildProcess)
  • using childProcess.stdin.write()
  • using stream.pipe(childProcess.stdin)
  • passing the input or inputFile option

And then many commands allow both, such as most Unix utilities like cat, grep or rg.

Previous default value

Initially, we've started with $ having the same default value for the stdin option as execa(), i.e. "pipe".

With #548, #549 and #550, we switched the default value to "inherit".

New default value

However, with the recent additions to the stdin option, we can now have the best of both worlds! We could switch the default value for the stdin option with $ to ['pipe', 'inherit'] instead.

This means users would be allowed to use both interactive and programmatic stdin.

One of the upsides is to be able to use:

$`command ...`.pipe($`otherCommand ...`)

Instead of the following, which is currently making piping processes more verbose and error-prone:

$`command ...`.pipe($({ stdin: 'pipe' })`otherCommand ...`)

It also means users would be able to use childProcess.stdin.write() and stream.pipe(childProcess.stdin) without setting any stdin option.

What do you think?

@sindresorhus
Copy link
Owner

Are there any downsides?

@ehmicky
Copy link
Collaborator Author

ehmicky commented Jan 26, 2024

The only one I can think of is that when using { stdin: ['pipe', 'inherit'] }, the child process' stdin will use a non-interactive TTY. It will still get interactive input though, if the parent process stdin is in an interactive TTY.

One thing to consider is that stdout/stderr are not interactive by default, since their default value is pipe, not inherit. In general, using interactive input and interactive output tend to go together. For example, confirmation prompts do not make sense if the user cannot see the question that is being prompted. So in most cases, users would need to do this anyway:

$({ stdio: 'inherit' })`command ...`

If they don't, this means they are letting the parent print the output, which would work when using { stdin: ['pipe', 'inherit'] }.

Also, even though the child process' stdin.isTTY will be false, it will still get interactive input from the parent process. So in many cases, it will just work without even having to opt in.

@sindresorhus
Copy link
Owner

Ok, let's do it.

@ehmicky
Copy link
Collaborator Author

ehmicky commented Feb 7, 2024

I implemented it with #758.

However, it has performance issues. It makes simple commands twice slower.
The core problem is not really the implementation itself, but the fact that:

  • 'inherit' uses efficient OS-optimized logic in C by libuv
  • ['pipe', 'inherit'] basically does process.stdin.pipe(childProcess.stdin). The .pipe() method spends most of its time calling .write() when the data event is emitted. It is efficient but much less than the above.

I don't think there is a way to solve this. There is also the non-interactive TTY issue mentioned above, so I am going to close this issue and try to solve the underlying problem of passing stdin programmatically differently:

  • For most use cases, users can use the stdin/input/inputFile options, which work despite the default value of stdin: 'inherit'
  • If this is not enough, for direct childProcess.stdin.write() and stream.pipe(childProcess.stdin) calls, users will still need to set the {stdin: 'pipe'} option.
  • For piping processes, I have opened .pipe() shortcut with $ #797 to discuss a possible solution.

@ehmicky ehmicky closed this as completed Feb 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants