Skip to content

Commit dd55ced

Browse files
CopilotSidnioulz
andcommitted
Address review feedback: simplify error checking, reduce redundancy, prioritize .cmd
Co-authored-by: Sidnioulz <5108577+Sidnioulz@users.noreply.github.com>
1 parent 22c5ce6 commit dd55ced

File tree

2 files changed

+32
-43
lines changed

2 files changed

+32
-43
lines changed

code/core/src/common/utils/command.test.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe('command', () => {
4646
});
4747
});
4848

49-
it('should try .exe first for pnpm and succeed', async () => {
49+
it('should try .cmd first for pnpm and succeed', async () => {
5050
mockedExeca.mockResolvedValueOnce({
5151
stdout: 'success',
5252
stderr: '',
@@ -59,7 +59,7 @@ describe('command', () => {
5959

6060
expect(mockedExeca).toHaveBeenCalledTimes(1);
6161
expect(mockedExeca).toHaveBeenCalledWith(
62-
'pnpm.exe',
62+
'pnpm.cmd',
6363
['--version'],
6464
expect.objectContaining({
6565
encoding: 'utf8',
@@ -68,12 +68,12 @@ describe('command', () => {
6868
);
6969
});
7070

71-
it('should try .cmd after .exe fails with "not recognized" error for pnpm', async () => {
71+
it('should try .exe after .cmd fails with "not recognized" error for pnpm', async () => {
7272
// First call fails with "not recognized" error
7373
mockedExeca.mockRejectedValueOnce({
7474
stderr:
75-
"'pnpm.exe' is not recognized as an internal or external command,\r\noperable program or batch file.",
76-
message: 'Command failed: pnpm.exe --version',
75+
"'pnpm.cmd' is not recognized as an internal or external command,\r\noperable program or batch file.",
76+
message: 'Command failed: pnpm.cmd --version',
7777
});
7878

7979
// Second call succeeds
@@ -88,19 +88,19 @@ describe('command', () => {
8888
});
8989

9090
expect(mockedExeca).toHaveBeenCalledTimes(2);
91-
expect(mockedExeca).toHaveBeenNthCalledWith(1, 'pnpm.exe', ['--version'], expect.anything());
92-
expect(mockedExeca).toHaveBeenNthCalledWith(2, 'pnpm.cmd', ['--version'], expect.anything());
91+
expect(mockedExeca).toHaveBeenNthCalledWith(1, 'pnpm.cmd', ['--version'], expect.anything());
92+
expect(mockedExeca).toHaveBeenNthCalledWith(2, 'pnpm.exe', ['--version'], expect.anything());
9393
});
9494

95-
it('should try .ps1 after .cmd fails with "not recognized" error for pnpm', async () => {
95+
it('should try .ps1 after .exe fails with "not recognized" error for pnpm', async () => {
9696
// First two calls fail with "not recognized" error
9797
mockedExeca.mockRejectedValueOnce({
98-
stderr: "'pnpm.exe' is not recognized as an internal or external command",
98+
stderr: "'pnpm.cmd' is not recognized as an internal or external command",
9999
message: 'Command failed',
100100
});
101101

102102
mockedExeca.mockRejectedValueOnce({
103-
stderr: "'pnpm.cmd' is not recognized as an internal or external command",
103+
stderr: "'pnpm.exe' is not recognized as an internal or external command",
104104
message: 'Command failed',
105105
});
106106

@@ -116,20 +116,20 @@ describe('command', () => {
116116
});
117117

118118
expect(mockedExeca).toHaveBeenCalledTimes(3);
119-
expect(mockedExeca).toHaveBeenNthCalledWith(1, 'pnpm.exe', ['--version'], expect.anything());
120-
expect(mockedExeca).toHaveBeenNthCalledWith(2, 'pnpm.cmd', ['--version'], expect.anything());
119+
expect(mockedExeca).toHaveBeenNthCalledWith(1, 'pnpm.cmd', ['--version'], expect.anything());
120+
expect(mockedExeca).toHaveBeenNthCalledWith(2, 'pnpm.exe', ['--version'], expect.anything());
121121
expect(mockedExeca).toHaveBeenNthCalledWith(3, 'pnpm.ps1', ['--version'], expect.anything());
122122
});
123123

124124
it('should try bare command after all extensions fail with "not recognized" error', async () => {
125125
// First three calls fail with "not recognized" error
126126
mockedExeca.mockRejectedValueOnce({
127-
stderr: "'pnpm.exe' is not recognized as an internal or external command",
127+
stderr: "'pnpm.cmd' is not recognized as an internal or external command",
128128
message: 'Command failed',
129129
});
130130

131131
mockedExeca.mockRejectedValueOnce({
132-
stderr: "'pnpm.cmd' is not recognized as an internal or external command",
132+
stderr: "'pnpm.exe' is not recognized as an internal or external command",
133133
message: 'Command failed',
134134
});
135135

@@ -201,7 +201,7 @@ describe('command', () => {
201201
args: ['--version'],
202202
});
203203

204-
expect(mockedExeca).toHaveBeenCalledWith('npm.exe', ['--version'], expect.anything());
204+
expect(mockedExeca).toHaveBeenCalledWith('npm.cmd', ['--version'], expect.anything());
205205
});
206206

207207
it('should work for yarn command', async () => {
@@ -215,7 +215,7 @@ describe('command', () => {
215215
args: ['--version'],
216216
});
217217

218-
expect(mockedExeca).toHaveBeenCalledWith('yarn.exe', ['--version'], expect.anything());
218+
expect(mockedExeca).toHaveBeenCalledWith('yarn.cmd', ['--version'], expect.anything());
219219
});
220220

221221
it('should not modify unknown commands on Windows', async () => {
@@ -301,7 +301,7 @@ describe('command', () => {
301301
});
302302
});
303303

304-
it('should try .exe first for pnpm and succeed', () => {
304+
it('should try .cmd first for pnpm and succeed', () => {
305305
mockedExecaCommandSync.mockReturnValueOnce({
306306
stdout: '10.0.0',
307307
stderr: '',
@@ -315,19 +315,19 @@ describe('command', () => {
315315
expect(result).toBe('10.0.0');
316316
expect(mockedExecaCommandSync).toHaveBeenCalledTimes(1);
317317
expect(mockedExecaCommandSync).toHaveBeenCalledWith(
318-
'pnpm.exe --version',
318+
'pnpm.cmd --version',
319319
expect.objectContaining({
320320
encoding: 'utf8',
321321
cleanup: true,
322322
})
323323
);
324324
});
325325

326-
it('should try .cmd after .exe fails with "not recognized" error', () => {
326+
it('should try .exe after .cmd fails with "not recognized" error', () => {
327327
// First call fails
328328
mockedExecaCommandSync.mockImplementationOnce(() => {
329329
throw {
330-
stderr: "'pnpm.exe' is not recognized as an internal or external command",
330+
stderr: "'pnpm.cmd' is not recognized as an internal or external command",
331331
message: 'Command failed',
332332
};
333333
});
@@ -347,12 +347,12 @@ describe('command', () => {
347347
expect(mockedExecaCommandSync).toHaveBeenCalledTimes(2);
348348
expect(mockedExecaCommandSync).toHaveBeenNthCalledWith(
349349
1,
350-
'pnpm.exe --version',
350+
'pnpm.cmd --version',
351351
expect.anything()
352352
);
353353
expect(mockedExecaCommandSync).toHaveBeenNthCalledWith(
354354
2,
355-
'pnpm.cmd --version',
355+
'pnpm.exe --version',
356356
expect.anything()
357357
);
358358
});

code/core/src/common/utils/command.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,13 @@ function isCommandNotFoundError(error: any): boolean {
105105
return false;
106106
}
107107

108-
// Check for Windows-specific "command not recognized" error
109108
const stderr = error.stderr || '';
110-
const message = error.message || '';
109+
return stderr.includes('is not recognized as an internal or external command');
110+
}
111111

112-
return (
113-
stderr.includes('is not recognized as an internal or external command') ||
114-
message.includes('is not recognized as an internal or external command')
115-
);
112+
/** Helper to check if we should continue trying command variations. */
113+
function shouldRetry(error: any, isLastVariation: boolean): boolean {
114+
return isCommandNotFoundError(error) && !isLastVariation;
116115
}
117116

118117
/**
@@ -142,12 +141,10 @@ function tryCommandVariations(
142141
} catch (error: any) {
143142
lastError = error;
144143

145-
// If this is not a "command not found" error, or we're on the last variation, re-throw
146-
if (!isCommandNotFoundError(error) || index === commandVariations.length - 1) {
144+
if (!shouldRetry(error, index === commandVariations.length - 1)) {
147145
throw error;
148146
}
149147

150-
// Otherwise, try the next variation
151148
logger.debug(`Command "${cmd}" not found, trying next variation...`);
152149
return tryNext(index + 1);
153150
}
@@ -179,17 +176,14 @@ function tryCommandVariationsSync(
179176
} catch (error: any) {
180177
lastError = error;
181178

182-
// If this is not a "command not found" error, or we're on the last variation, re-throw
183-
if (!isCommandNotFoundError(error) || i === commandVariations.length - 1) {
179+
if (!shouldRetry(error, i === commandVariations.length - 1)) {
184180
throw error;
185181
}
186182

187-
// Otherwise, try the next variation
188183
logger.debug(`Command "${cmd}" not found, trying next variation...`);
189184
}
190185
}
191186

192-
// This should never be reached, but TypeScript needs it
193187
throw lastError;
194188
}
195189

@@ -213,8 +207,8 @@ function tryCommandVariationsSync(
213207
*
214208
* - If on Windows:
215209
*
216-
* - For known shim-based commands, return an array of variations to try in order: [command.exe,
217-
* command.cmd, command.ps1, command]
210+
* - For known shim-based commands, return an array of variations to try in order: [command.cmd,
211+
* command.exe, command.ps1, command]
218212
* - For everything else, return the name unchanged.
219213
* - On non-Windows, return command unchanged.
220214
*
@@ -243,12 +237,7 @@ function resolveCommand(command: string): string[] {
243237
}
244238

245239
if (WINDOWS_SHIM_COMMANDS.has(command)) {
246-
// On Windows, try multiple variations in order of likelihood:
247-
// 1. .exe - native executable (e.g., pnpm installed via Scoop/Mise)
248-
// 2. .cmd - CMD shim (most common for npm-installed packages)
249-
// 3. .ps1 - PowerShell shim (less common but possible)
250-
// 4. bare command - fallback
251-
return [`${command}.exe`, `${command}.cmd`, `${command}.ps1`, command];
240+
return [`${command}.cmd`, `${command}.exe`, `${command}.ps1`, command];
252241
}
253242

254243
return [command];

0 commit comments

Comments
 (0)