in client/src/node/main.ts [243:503]
protected createMessageTransports(encoding: string): Promise<MessageTransports> {
function getEnvironment(env: any, fork: boolean): any {
if (!env && !fork) {
return undefined;
}
const result: any = Object.create(null);
Object.keys(process.env).forEach(key => result[key] = process.env[key]);
if (fork) {
result['ELECTRON_RUN_AS_NODE'] = '1';
result['ELECTRON_NO_ASAR'] = '1';
}
if (env) {
Object.keys(env).forEach(key => result[key] = env[key]);
}
return result;
}
const debugStartWith: string[] = ['--debug=', '--debug-brk=', '--inspect=', '--inspect-brk='];
const debugEquals: string[] = ['--debug', '--debug-brk', '--inspect', '--inspect-brk'];
function startedInDebugMode(): boolean {
let args: string[] = (process as any).execArgv;
if (args) {
return args.some((arg) => {
return debugStartWith.some(value => arg.startsWith(value)) ||
debugEquals.some(value => arg === value);
});
}
return false;
}
function assertStdio(process: cp.ChildProcess): asserts process is cp.ChildProcessWithoutNullStreams {
if (process.stdin === null || process.stdout === null || process.stderr === null) {
throw new Error('Process created without stdio streams');
}
}
const server = this._serverOptions;
// We got a function.
if (Is.func(server)) {
return server().then((result) => {
if (MessageTransports.is(result)) {
this._isDetached = !!result.detached;
return result;
} else if (StreamInfo.is(result)) {
this._isDetached = !!result.detached;
return { reader: new StreamMessageReader(result.reader), writer: new StreamMessageWriter(result.writer) };
} else {
let cp: ChildProcess;
if (ChildProcessInfo.is(result)) {
cp = result.process;
this._isDetached = result.detached;
} else {
cp = result;
this._isDetached = false;
}
cp.stderr!.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return { reader: new StreamMessageReader(cp.stdout!), writer: new StreamMessageWriter(cp.stdin!) };
}
});
}
let json: NodeModule | Executable;
let runDebug = <{ run: any; debug: any }>server;
if (runDebug.run || runDebug.debug) {
if (this._forceDebug || startedInDebugMode()) {
json = runDebug.debug;
this._isInDebugMode = true;
} else {
json = runDebug.run;
this._isInDebugMode = false;
}
} else {
json = server as NodeModule | Executable;
}
return this._getServerWorkingDir(json.options).then(serverWorkingDir => {
if (NodeModule.is(json) && json.module) {
let node = json;
let transport = node.transport || TransportKind.stdio;
if (node.runtime) {
const args: string[] = [];
const options: ForkOptions = node.options ?? Object.create(null);
if (options.execArgv) {
options.execArgv.forEach(element => args.push(element));
}
args.push(node.module);
if (node.args) {
node.args.forEach(element => args.push(element));
}
const execOptions: cp.SpawnOptionsWithoutStdio = Object.create(null);
execOptions.cwd = serverWorkingDir;
execOptions.env = getEnvironment(options.env, false);
const runtime = this._getRuntimePath(node.runtime, serverWorkingDir);
let pipeName: string | undefined = undefined;
if (transport === TransportKind.ipc) {
// exec options not correctly typed in lib
execOptions.stdio = <any>[null, null, null, 'ipc'];
args.push('--node-ipc');
} else if (transport === TransportKind.stdio) {
args.push('--stdio');
} else if (transport === TransportKind.pipe) {
pipeName = generateRandomPipeName();
args.push(`--pipe=${pipeName}`);
} else if (Transport.isSocket(transport)) {
args.push(`--socket=${transport.port}`);
}
args.push(`--clientProcessId=${process.pid.toString()}`);
if (transport === TransportKind.ipc || transport === TransportKind.stdio) {
const serverProcess = cp.spawn(runtime, args, execOptions);
if (!serverProcess || !serverProcess.pid) {
return Promise.reject<MessageTransports>(`Launching server using runtime ${runtime} failed.`);
}
this._serverProcess = serverProcess;
serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
if (transport === TransportKind.ipc) {
serverProcess.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return Promise.resolve({ reader: new IPCMessageReader(serverProcess), writer: new IPCMessageWriter(serverProcess) });
} else {
return Promise.resolve({ reader: new StreamMessageReader(serverProcess.stdout), writer: new StreamMessageWriter(serverProcess.stdin) });
}
} else if (transport === TransportKind.pipe) {
return createClientPipeTransport(pipeName!).then((transport) => {
const process = cp.spawn(runtime, args, execOptions);
if (!process || !process.pid) {
return Promise.reject<MessageTransports>(`Launching server using runtime ${runtime} failed.`);
}
this._serverProcess = process;
process.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
process.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return transport.onConnected().then((protocol) => {
return { reader: protocol[0], writer: protocol[1] };
});
});
} else if (Transport.isSocket(transport)) {
return createClientSocketTransport(transport.port).then((transport) => {
const process = cp.spawn(runtime, args, execOptions);
if (!process || !process.pid) {
return Promise.reject<MessageTransports>(`Launching server using runtime ${runtime} failed.`);
}
this._serverProcess = process;
process.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
process.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return transport.onConnected().then((protocol) => {
return { reader: protocol[0], writer: protocol[1] };
});
});
}
} else {
let pipeName: string | undefined = undefined;
return new Promise<MessageTransports>((resolve, reject) => {
const args = (node.args && node.args.slice()) ?? [];
if (transport === TransportKind.ipc) {
args.push('--node-ipc');
} else if (transport === TransportKind.stdio) {
args.push('--stdio');
} else if (transport === TransportKind.pipe) {
pipeName = generateRandomPipeName();
args.push(`--pipe=${pipeName}`);
} else if (Transport.isSocket(transport)) {
args.push(`--socket=${transport.port}`);
}
args.push(`--clientProcessId=${process.pid.toString()}`);
const options: cp.ForkOptions = node.options ?? Object.create(null);
options.env = getEnvironment(options.env, true);
options.execArgv = options.execArgv || [];
options.cwd = serverWorkingDir;
options.silent = true;
if (transport === TransportKind.ipc || transport === TransportKind.stdio) {
const sp = cp.fork(node.module, args || [], options);
assertStdio(sp);
this._serverProcess = sp;
sp.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
if (transport === TransportKind.ipc) {
sp.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
resolve({ reader: new IPCMessageReader(this._serverProcess), writer: new IPCMessageWriter(this._serverProcess) });
} else {
resolve({ reader: new StreamMessageReader(sp.stdout), writer: new StreamMessageWriter(sp.stdin) });
}
} else if (transport === TransportKind.pipe) {
createClientPipeTransport(pipeName!).then((transport) => {
const sp = cp.fork(node.module, args || [], options);
assertStdio(sp);
this._serverProcess = sp;
sp.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
sp.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
transport.onConnected().then((protocol) => {
resolve({ reader: protocol[0], writer: protocol[1] });
}, reject);
}, reject);
} else if (Transport.isSocket(transport)) {
createClientSocketTransport(transport.port).then((transport) => {
const sp = cp.fork(node.module, args || [], options);
assertStdio(sp);
this._serverProcess = sp;
sp.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
sp.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
transport.onConnected().then((protocol) => {
resolve({ reader: protocol[0], writer: protocol[1] });
}, reject);
}, reject);
}
});
}
} else if (Executable.is(json) && json.command) {
const command: Executable = <Executable>json;
const args: string[] = json.args !== undefined ? json.args.slice(0) : [];
let pipeName: string | undefined = undefined;
const transport = json.transport;
if (transport === TransportKind.stdio) {
args.push('--stdio');
} else if (transport === TransportKind.pipe) {
pipeName = generateRandomPipeName();
args.push(`--pipe=${pipeName}`);
} else if (Transport.isSocket(transport)) {
args.push(`--socket=${transport.port}`);
} else if (transport === TransportKind.ipc) {
throw new Error(`Transport kind ipc is not support for command executable`);
}
const options = Object.assign({}, command.options);
options.cwd = options.cwd || serverWorkingDir;
if (transport === undefined || transport === TransportKind.stdio) {
const serverProcess = cp.spawn(command.command, args, options);
if (!serverProcess || !serverProcess.pid) {
return Promise.reject<MessageTransports>(`Launching server using command ${command.command} failed.`);
}
serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
this._serverProcess = serverProcess;
this._isDetached = !!options.detached;
return Promise.resolve({ reader: new StreamMessageReader(serverProcess.stdout), writer: new StreamMessageWriter(serverProcess.stdin) });
} else if (transport === TransportKind.pipe) {
return createClientPipeTransport(pipeName!).then((transport) => {
const serverProcess = cp.spawn(command.command, args, options);
if (!serverProcess || !serverProcess.pid) {
throw new Error(`Launching server using command ${command.command} failed.`);
}
this._serverProcess = serverProcess;
this._isDetached = !!options.detached;
serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
serverProcess.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return transport.onConnected().then((protocol) => {
return { reader: protocol[0], writer: protocol[1] };
});
});
} else if (Transport.isSocket(transport)) {
return createClientSocketTransport(transport.port).then((transport) => {
const serverProcess = cp.spawn(command.command, args, options);
if (!serverProcess || !serverProcess.pid) {
throw new Error(`Launching server using command ${command.command} failed.`);
}
this._serverProcess = serverProcess;
this._isDetached = !!options.detached;
serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
serverProcess.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
return transport.onConnected().then((protocol) => {
return { reader: protocol[0], writer: protocol[1] };
});
});
}
}
return Promise.reject<MessageTransports>(new Error(`Unsupported server configuration ` + JSON.stringify(server, null, 4)));
});
}