tauri-apps / tauri-apps/plugins-workspace
[feat] Make stdin closable via JS `shell` API
- Dominant language
- Rust
- Stars
- 1.8k
- Forks
- 602
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 9
Description
### Describe the problem
@FabianLars asked me to open a feature request, so here we are. As elaborated in #4440, there is an issue when using Tauri's JS API method [`shell.Child.write`](https://tauri.app/v1/api/js/shell/#write): stdin is not being closed. This can make command line interfaces that depend on stdin unusable, for example [`esbuild`](https://esbuild.github.io/api/#transform-api) in this code snippet:
```js
const command = Command.sidecar("../binaries/esbuild", "--loader=ts")
command.stdout.on("data", data => console.log(data))
command.stderr.on("data", data => console.log(data))
command.on("error", data => console.log(data))
command.on("close", data => console.log(data))
const child = await command.spawn()
console.log(child.pid)
await child.write("echo 'let x: number = 1'")
// => Only the pid is logged, nothing else
```
The expected output here would the transformed code or an error, but since stdin is not closed, no stdout/stderr is ever produced.
A workaround (credit to @lanc33llis, who faced the same issue) is this:
```rs
#[tauri::command]
async fn function(text: &str) -> Result {
let (mut rx, mut child) = Command::new_sidecar("my-sidecar")
.expect("failed to create `my-sidecar` binary command")
.spawn()
.expect("Failed to spawn sidecar");
child.write(text.as_bytes()).expect("Failed to write to sidecar");
drop(child);
let mut output = String::new();
while let Some(event) = rx.recv().await {
if let CommandEvent::Stdout(line) = event {
output.push_str(&line);
}
}
Ok(output)
}
```
In my personal opinion, that workaround shouldn't be neccessary, hence this feature request.
### Describe the solution you'd like
Possibly the easiest fix would be adding a `closeAfter` flag to the `shell.Child.write` method. This is backwards compatible and follows a similar pattern to other parts of the API.
**New signature:**
```ts
type WriteOptions = {closeAfter?: boolean}
write(data: string | Uint8Array, options?: WriteOptions): Promise
```
**Semantics:**
If set to true, it would close stdin after writing. This would avoid adding extra methods and still allow usage such as this:
```js
child.write("my first stdin message") // stdin is open
child.write("my second stdin message") // still open...
child.write("my final stdin message", {closeAfter: true}) // stdin is closed
```
### Alternatives considered
A separate method for closing stdin on `shell.Child` would be possible, but this would increase the API surface.
### Additional context
I don't know if this problem would also appear in other parts of the JS API and would need more adjustments.
Contributor guide
Assessment
This issue has not been assessed yet.