-
Notifications
You must be signed in to change notification settings - Fork 379
Description
This code path here:
vscode-languageserver-node/client/src/node/main.ts
Lines 427 to 433 in 0e3fd82
| 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); |
mutates the .env property inside this._serverOptions. Furthermore, the cp.fork below seems to mutate it further (weird, but that's what I saw in the debugger).
The consequence here is that when you run .restart(), it will not remember the .env values you passed in the LanguageClient constructor. And so, when you restart language server with client.restart(), it will have different options from the original launch.
A sketch of an MWE is this:
const serverOptions = {
run: {
module: serverModule,
transport: TransportKind.ipc,
options: {
execArgv: [],
env: {
SOMEOPTION: "some-option-value",
},
},
},
debug: {
module: serverModule,
transport: TransportKind.ipc,
options: {
execArgv: ["--nolazy", "--inspect=6010"],
env: {
DEBUG: "*",
SOMEOPTION: "some-option-value",
},
},
},
};
const client = new LanguageClient(
"my-language-server",
"My Language Server",
deepFreeze(serverOptions) as ServerOptions,
clientOptions
);
await client.start(); // picks up the .env correctly
await client.restart(); // this doesn'tOne possible MWE should be this, where if you freeze the serverOptions object, it will crash:
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
function deepFreeze<T extends object>(obj: T): DeepReadonly<T> {
Object.getOwnPropertyNames(obj).forEach((name) => {
const prop = obj[name as keyof T];
if (prop !== null && typeof prop === "object") {
deepFreeze(prop);
}
});
return Object.freeze(obj) as DeepReadonly<T>;
}
const serverOptions = {
run: {
module: serverModule,
transport: TransportKind.ipc,
options: {
execArgv: [],
env: {
SOMEOPTION: "some-option-value",
MCP_PORT: mcpServerPort.toString(),
},
},
},
debug: {
module: serverModule,
transport: TransportKind.ipc,
options: {
execArgv: ["--nolazy", "--inspect=6010"],
env: {
DEBUG: "*",
SOMEOPTION: "some-option-value",
MCP_PORT: mcpServerPort.toString(),
},
},
},
};
const client = new LanguageClient(
"my-language-server",
"My Language Server",
deepFreeze(serverOptions) as ServerOptions,
clientOptions
);
await client.start(); // should crashChanging the first line there to make a copy of the properties seems to be enough to fix this:
const options: cp.ForkOptions = { ...node.options } ?? Object.create(null);