@@ -137,8 +137,7 @@ export async function activate(context: ExtensionContext) {
137
137
outputChannel ,
138
138
) ;
139
139
140
- async function findBinary ( ) : Promise < string > {
141
- let bin = configService . getUserServerBinPath ( ) ;
140
+ async function findBinary ( bin : string | undefined ) : Promise < string > {
142
141
if ( workspace . isTrusted && bin ) {
143
142
try {
144
143
await fsPromises . access ( bin ) ;
@@ -155,10 +154,20 @@ export async function activate(context: ExtensionContext) {
155
154
) ;
156
155
}
157
156
158
- const command = await findBinary ( ) ;
157
+ const userDefinedBinary = configService . getUserServerBinPath ( ) ;
158
+ const command = await findBinary ( userDefinedBinary ) ;
159
159
const run : Executable = {
160
160
command : command ! ,
161
161
options : {
162
+ // On Windows we need to run the binary in a shell to be able to execute the shell npm bin script.
163
+ // This is only needed when the user explicitly configures the binary to point to the npm bin script.
164
+ // The extension is shipped with the `.exe` file, we don't need to run it in a shell.
165
+ // Searching for the right `.exe` file inside `node_modules/` is not reliable as it depends on
166
+ // the package manager used (npm, yarn, pnpm, etc) and the package version.
167
+ // The npm bin script is a shell script that points to the actual binary.
168
+ // Security: We validated the userDefinedBinary in `configService.getUserServerBinPath()`.
169
+ shell : process . platform === 'win32' && command === userDefinedBinary &&
170
+ userDefinedBinary ?. endsWith ( 'node_modules\\.bin\\oxc_language_server' ) ,
162
171
env : {
163
172
...process . env ,
164
173
RUST_LOG : process . env . RUST_LOG || 'info' ,
0 commit comments