fix(subprocess): honor Windows environment key casing

This commit is contained in:
Tianyi Cui
2026-07-29 04:19:04 +08:00
parent 03f1c8eb43
commit 3537665806
2 changed files with 13 additions and 3 deletions

View File

@@ -102,9 +102,9 @@ export class LocalSubprocessService extends SubprocessService {
} }
private executableCandidates(command: string, env: NodeJS.ProcessEnv): string[] { private executableCandidates(command: string, env: NodeJS.ProcessEnv): string[] {
const path = env.PATH ?? '' const path = environmentValue(env, 'PATH') ?? ''
const extensions = process.platform === 'win32' && extname(command) === '' const extensions = process.platform === 'win32' && extname(command) === ''
? (env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD').split(';') ? (environmentValue(env, 'PATHEXT') ?? '.COM;.EXE;.BAT;.CMD').split(';')
: [''] : ['']
return path.split(delimiter).flatMap(directory => return path.split(delimiter).flatMap(directory =>
directory === '' ? [] : extensions.map(extension => resolve(this.cwd, directory, command + extension))) directory === '' ? [] : extensions.map(extension => resolve(this.cwd, directory, command + extension)))
@@ -156,4 +156,12 @@ export class LocalSubprocessService extends SubprocessService {
} }
} }
/** Read a Windows environment key using the platform's case-insensitive semantics. */
function environmentValue(env: NodeJS.ProcessEnv, name: 'PATH' | 'PATHEXT'): string | undefined {
const exact = env[name]
if (exact !== undefined || process.platform !== 'win32') return exact
const normalized = name.toUpperCase()
return Object.entries(env).find(([key]) => key.toUpperCase() === normalized)?.[1]
}
export default LocalSubprocessService export default LocalSubprocessService

View File

@@ -62,8 +62,10 @@ describe('LocalSubprocessService', () => {
}).executableCandidates.bind(service) }).executableCandidates.bind(service)
const platform = vi.spyOn(process, 'platform', 'get').mockReturnValue('win32') const platform = vi.spyOn(process, 'platform', 'get').mockReturnValue('win32')
try { try {
expect(candidates('tool', { PATH: `${delimiter}/bin`, PATHEXT: '.EXE;.CMD' })) expect(candidates('tool', { Path: `${delimiter}/bin`, PathExt: '.EXE;.CMD' }))
.toEqual(['/bin/tool.EXE', '/bin/tool.CMD']) .toEqual(['/bin/tool.EXE', '/bin/tool.CMD'])
expect(candidates('tool', { Path: '/ambient', PATH: '/explicit', PATHEXT: '.EXE' }))
.toEqual(['/explicit/tool.EXE'])
expect(candidates('tool.exe', {})).toEqual([]) expect(candidates('tool.exe', {})).toEqual([])
expect(candidates('tool', { PATH: '/bin' })).toHaveLength(4) expect(candidates('tool', { PATH: '/bin' })).toHaveLength(4)
} finally { } finally {