Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 2 additions & 32 deletions editors/vscode/client/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { join } from 'node:path';
import { ConfigService } from './ConfigService';
import { VSCodeConfig } from './VSCodeConfig';
import { OxcCommands } from './commands';
import { runExecutable } from './lsp_helper';

const languageClientName = 'oxc';

Expand Down Expand Up @@ -90,40 +91,9 @@ export async function activate(
return process.env.SERVER_PATH_DEV ?? join(context.extensionPath, `./target/release/oxc_language_server${ext}`);
}

const nodePath = configService.vsCodeConfig.nodePath;
const serverEnv: Record<string, string> = {
...process.env,
RUST_LOG: process.env.RUST_LOG || 'info',
};
if (nodePath) {
serverEnv.PATH = `${nodePath}${process.platform === 'win32' ? ';' : ':'}${process.env.PATH ?? ''}`;
}

const path = await findBinary();
const isNode = path.endsWith('.js') || path.endsWith('.cjs') || path.endsWith('.mjs');

const run: Executable = isNode
? {
command: 'node',
args: [path!, '--lsp'],
options: {
env: serverEnv,
},
}
: {
command: path!,
args: ['--lsp'],
options: {
// On Windows we need to run the binary in a shell to be able to execute the shell npm bin script.
// Searching for the right `.exe` file inside `node_modules/` is not reliable as it depends on
// the package manager used (npm, yarn, pnpm, etc) and the package version.
// The npm bin script is a shell script that points to the actual binary.
// Security: We validated the userDefinedBinary in `configService.getUserServerBinPath()`.
shell: process.platform === 'win32',
env: serverEnv,
},
};

const run: Executable = runExecutable(path, configService.vsCodeConfig.nodePath);
const serverOptions: ServerOptions = {
run,
debug: run,
Expand Down
34 changes: 34 additions & 0 deletions editors/vscode/client/lsp_helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Executable } from 'vscode-languageclient/node';

export function runExecutable(path: string, nodePath?: string): Executable {
const serverEnv: Record<string, string> = {
...process.env,
RUST_LOG: process.env.RUST_LOG || 'info',
};
if (nodePath) {
serverEnv.PATH = `${nodePath}${process.platform === 'win32' ? ';' : ':'}${process.env.PATH ?? ''}`;
}
const isNode = path.endsWith('.js') || path.endsWith('.cjs') || path.endsWith('.mjs');

return isNode
? {
command: 'node',
args: [path, '--lsp'],
options: {
env: serverEnv,
},
}
: {
command: path,
args: ['--lsp'],
options: {
// On Windows we need to run the binary in a shell to be able to execute the shell npm bin script.
// Searching for the right `.exe` file inside `node_modules/` is not reliable as it depends on
// the package manager used (npm, yarn, pnpm, etc) and the package version.
// The npm bin script is a shell script that points to the actual binary.
// Security: We validated the userDefinedBinary in `configService.getUserServerBinPath()`.
shell: process.platform === 'win32',
env: serverEnv,
},
};
}
61 changes: 61 additions & 0 deletions editors/vscode/tests/lsp_helper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { strictEqual } from 'assert';
import { runExecutable } from '../client/lsp_helper';

suite('runExecutable', () => {
const originalPlatform = process.platform;
const originalEnv = process.env;

teardown(() => {
Object.defineProperty(process, 'platform', { value: originalPlatform });
process.env = originalEnv;
});

test('should create Node.js executable for .js files', () => {
const result = runExecutable('/path/to/server.js');

strictEqual(result.command, 'node');
strictEqual(result.args?.[0], '/path/to/server.js');
strictEqual(result.args?.[1], '--lsp');
});

test('should create Node.js executable for .cjs files', () => {
const result = runExecutable('/path/to/server.cjs');

strictEqual(result.command, 'node');
strictEqual(result.args?.[0], '/path/to/server.cjs');
strictEqual(result.args?.[1], '--lsp');
});

test('should create Node.js executable for .mjs files', () => {
const result = runExecutable('/path/to/server.mjs');

strictEqual(result.command, 'node');
strictEqual(result.args?.[0], '/path/to/server.mjs');
strictEqual(result.args?.[1], '--lsp');
});

test('should create binary executable for non-Node files', () => {
const result = runExecutable('/path/to/oxc-language-server');

strictEqual(result.command, '/path/to/oxc-language-server');
strictEqual(result.args?.[0], '--lsp');
strictEqual(result.options?.shell, false);
});

test('should use shell on Windows for binary executables', () => {
Object.defineProperty(process, 'platform', { value: 'win32' });

const result = runExecutable('/path/to/oxc-language-server');

strictEqual(result.options?.shell, true);
});

test('should prepend nodePath to PATH', () => {
Object.defineProperty(process, 'platform', { value: 'linux' });
process.env.PATH = '/usr/bin:/bin';

const result = runExecutable('/path/to/server', '/custom/node/path');

strictEqual(result.options?.env?.PATH, '/custom/node/path:/usr/bin:/bin');
});
});
Loading