fix(core/shell): speedup Command.execute & fix extra new lines (#1299)

* fix(core/shell): speedup `Command.execute` & fix extra new lines

The speed gains comes from running the Command in Rust fully and returning the result in one go instead of using events.

The extra new lines was a regression from https://github.com/tauri-apps/tauri/pull/6519
ref: https://github.com/tauri-apps/tauri/issues/7684#issuecomment-2100897383

* fmt

* dedup code
This commit is contained in:
Amr Bashir
2024-05-09 18:15:03 +03:00
committed by GitHub
parent 5c1b7917e4
commit eb1679b997
12 changed files with 197 additions and 94 deletions
+43 -86
View File
@@ -99,39 +99,6 @@ interface ChildProcess<O extends IOPayload> {
stderr: O;
}
/**
* Spawns a process.
*
* @ignore
* @param program The name of the scoped command.
* @param onEventHandler Event handler.
* @param args Program arguments.
* @param options Configuration for the process spawn.
* @returns A promise resolving to the process id.
*
* @since 2.0.0
*/
async function execute<O extends IOPayload>(
onEventHandler: (event: CommandEvent<O>) => void,
program: string,
args: string | string[] = [],
options?: InternalSpawnOptions,
): Promise<number> {
if (typeof args === "object") {
Object.freeze(args);
}
const onEvent = new Channel<CommandEvent<O>>();
onEvent.onmessage = onEventHandler;
return await invoke<number>("plugin:shell|execute", {
program,
args,
options,
onEvent,
});
}
/**
* @since 2.0.0
*/
@@ -513,27 +480,38 @@ class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
* @since 2.0.0
*/
async spawn(): Promise<Child> {
return await execute<O>(
(event) => {
switch (event.event) {
case "Error":
this.emit("error", event.payload);
break;
case "Terminated":
this.emit("close", event.payload);
break;
case "Stdout":
this.stdout.emit("data", event.payload);
break;
case "Stderr":
this.stderr.emit("data", event.payload);
break;
}
},
this.program,
this.args,
this.options,
).then((pid) => new Child(pid));
const program = this.program;
const args = this.args;
const options = this.options;
if (typeof args === "object") {
Object.freeze(args);
}
const onEvent = new Channel<CommandEvent<O>>();
onEvent.onmessage = (event) => {
switch (event.event) {
case "Error":
this.emit("error", event.payload);
break;
case "Terminated":
this.emit("close", event.payload);
break;
case "Stdout":
this.stdout.emit("data", event.payload);
break;
case "Stderr":
this.stderr.emit("data", event.payload);
break;
}
};
return await invoke<number>("plugin:shell|spawn", {
program,
args,
options,
onEvent,
}).then((pid) => new Child(pid));
}
/**
@@ -553,40 +531,19 @@ class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
* @since 2.0.0
*/
async execute(): Promise<ChildProcess<O>> {
return await new Promise((resolve, reject) => {
this.on("error", reject);
const program = this.program;
const args = this.args;
const options = this.options;
const stdout: O[] = [];
const stderr: O[] = [];
this.stdout.on("data", (line: O) => {
stdout.push(line);
});
this.stderr.on("data", (line: O) => {
stderr.push(line);
});
this.on("close", (payload: TerminatedPayload) => {
resolve({
code: payload.code,
signal: payload.signal,
stdout: this.collectOutput(stdout) as O,
stderr: this.collectOutput(stderr) as O,
});
});
this.spawn().catch(reject);
});
}
/** @ignore */
private collectOutput(events: O[]): string | Uint8Array {
if (this.options.encoding === "raw") {
return events.reduce<Uint8Array>((p, c) => {
return new Uint8Array([...p, ...(c as Uint8Array), 10]);
}, new Uint8Array());
} else {
return events.join("\n");
if (typeof args === "object") {
Object.freeze(args);
}
return await invoke<ChildProcess<O>>("plugin:shell|execute", {
program,
args,
options,
});
}
}