Refactor evaluations (#285)
* Replace evaluations with proxies and messages * Return proxies synchronously Otherwise events can be lost. * Ensure events cannot be missed * Refactor remaining fills * Use more up-to-date version of util For callbackify. * Wait for dispose to come back before removing This prevents issues with the "done" event not always being the last event fired. For example a socket might close and then end, but only if the caller called end. * Remove old node-pty tests * Fix emitting events twice on duplex streams * Preserve environment when spawning processes * Throw a better error if the proxy doesn't exist * Remove rimraf dependency from ide * Update net.Server.listening * Use exit event instead of killed Doesn't look like killed is even a thing. * Add response timeout to server * Fix trash * Require node-pty & spdlog after they get unpackaged This fixes an error when running in the binary. * Fix errors in down emitter preventing reconnecting * Fix disposing proxies when nothing listens to "error" event * Refactor event tests to use jest.fn() * Reject proxy call when disconnected Otherwise it'll wait for the timeout which is a waste of time since we already know the connection is dead. * Use nbin for binary packaging * Remove additional module requires * Attempt to remove require for local bootstrap-fork * Externalize fsevents
This commit is contained in:
126
packages/protocol/src/browser/modules/child_process.ts
Normal file
126
packages/protocol/src/browser/modules/child_process.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import * as cp from "child_process";
|
||||
import * as net from "net";
|
||||
import * as stream from "stream";
|
||||
import { callbackify } from "util";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { ChildProcessModuleProxy, ChildProcessProxy, ChildProcessProxies } from "../../node/modules/child_process";
|
||||
import { Readable, Writable } from "./stream";
|
||||
|
||||
export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.ChildProcess {
|
||||
public readonly stdin: stream.Writable;
|
||||
public readonly stdout: stream.Readable;
|
||||
public readonly stderr: stream.Readable;
|
||||
public readonly stdio: [stream.Writable, stream.Readable, stream.Readable];
|
||||
|
||||
private _connected: boolean = false;
|
||||
private _killed: boolean = false;
|
||||
private _pid = -1;
|
||||
|
||||
public constructor(proxyPromises: Promise<ChildProcessProxies>) {
|
||||
super(proxyPromises.then((p) => p.childProcess));
|
||||
this.stdin = new Writable(proxyPromises.then((p) => p.stdin!));
|
||||
this.stdout = new Readable(proxyPromises.then((p) => p.stdout!));
|
||||
this.stderr = new Readable(proxyPromises.then((p) => p.stderr!));
|
||||
this.stdio = [this.stdin, this.stdout, this.stderr];
|
||||
|
||||
this.proxy.getPid().then((pid) => {
|
||||
this._pid = pid;
|
||||
this._connected = true;
|
||||
});
|
||||
this.on("disconnect", () => this._connected = false);
|
||||
this.on("exit", () => {
|
||||
this._connected = false;
|
||||
this._killed = true;
|
||||
});
|
||||
}
|
||||
|
||||
public get pid(): number {
|
||||
return this._pid;
|
||||
}
|
||||
|
||||
public get connected(): boolean {
|
||||
return this._connected;
|
||||
}
|
||||
|
||||
public get killed(): boolean {
|
||||
return this._killed;
|
||||
}
|
||||
|
||||
public kill(): void {
|
||||
this._killed = true;
|
||||
this.proxy.kill();
|
||||
}
|
||||
|
||||
public disconnect(): void {
|
||||
this.proxy.disconnect();
|
||||
}
|
||||
|
||||
public ref(): void {
|
||||
this.proxy.ref();
|
||||
}
|
||||
|
||||
public unref(): void {
|
||||
this.proxy.unref();
|
||||
}
|
||||
|
||||
public send(
|
||||
message: any, // tslint:disable-line no-any
|
||||
sendHandle?: net.Socket | net.Server | ((error: Error) => void),
|
||||
options?: cp.MessageOptions | ((error: Error) => void),
|
||||
callback?: (error: Error) => void): boolean {
|
||||
if (typeof sendHandle === "function") {
|
||||
callback = sendHandle;
|
||||
sendHandle = undefined;
|
||||
} else if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
if (sendHandle || options) {
|
||||
throw new Error("sendHandle and options are not supported");
|
||||
}
|
||||
|
||||
callbackify(this.proxy.send)(message, (error) => {
|
||||
if (callback) {
|
||||
callback(error);
|
||||
}
|
||||
});
|
||||
|
||||
return true; // Always true since we can't get this synchronously.
|
||||
}
|
||||
}
|
||||
|
||||
export class ChildProcessModule {
|
||||
public constructor(private readonly proxy: ChildProcessModuleProxy) {}
|
||||
|
||||
public exec = (
|
||||
command: string,
|
||||
options?: { encoding?: string | null } & cp.ExecOptions | null
|
||||
| ((error: cp.ExecException | null, stdout: string | Buffer, stderr: string | Buffer) => void),
|
||||
callback?: ((error: cp.ExecException | null, stdout: string | Buffer, stderr: string | Buffer) => void),
|
||||
): cp.ChildProcess => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess(this.proxy.exec(command, options, callback));
|
||||
}
|
||||
|
||||
public fork = (modulePath: string, args?: string[] | cp.ForkOptions, options?: cp.ForkOptions): cp.ChildProcess => {
|
||||
if (!Array.isArray(args)) {
|
||||
options = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess(this.proxy.fork(modulePath, args, options));
|
||||
}
|
||||
|
||||
public spawn = (command: string, args?: string[] | cp.SpawnOptions, options?: cp.SpawnOptions): cp.ChildProcess => {
|
||||
if (!Array.isArray(args)) {
|
||||
options = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess(this.proxy.spawn(command, args, options));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user