mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-03 21:58:58 -04:00
fix: use ENOENT check instead of which.sync for command-not-found on Windows (#11004)
Cherry-picked from main (e9318ce974).
This commit is contained in:
6
.changeset/fix-windows-exec-command-not-found.md
Normal file
6
.changeset/fix-windows-exec-command-not-found.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Fixed false "Command not found" errors on Windows when a command exists in PATH but exits with a non-zero code. Also fixed path resolution for `--filter` contexts where the command runs in a different package directory.
|
||||
@@ -304,7 +304,7 @@ export async function handler (
|
||||
result[prefix].status = 'passed'
|
||||
result[prefix].duration = getExecutionDuration(startTime)
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
if (isErrorCommandNotFound(params[0], err, prependPaths)) {
|
||||
if (isErrorCommandNotFound(params[0], err, prefix, prependPaths)) {
|
||||
err.message = `Command "${params[0]}" not found`
|
||||
err.hint = await createExecCommandNotFoundHint(params[0], {
|
||||
implicitlyFellbackFromRun: opts.implicitlyFellbackFromRun ?? false,
|
||||
@@ -398,19 +398,20 @@ interface CommandError extends Error {
|
||||
shortMessage: string
|
||||
}
|
||||
|
||||
function isErrorCommandNotFound (command: string, error: CommandError, prependPaths: string[]): boolean {
|
||||
// Mac/Linux
|
||||
if (process.platform === 'linux' || process.platform === 'darwin') {
|
||||
return error.originalMessage === `spawn ${command} ENOENT`
|
||||
function isErrorCommandNotFound (command: string, error: CommandError, prefix: string, prependPaths: string[]): boolean {
|
||||
if (error.originalMessage === `spawn ${command} ENOENT`) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Windows
|
||||
// On Windows, execa 9.x uses cross-spawn only for command parsing (not spawning),
|
||||
// so cross-spawn's ENOENT hook never fires. Non-existent commands get wrapped as
|
||||
// `cmd.exe /c <command>` which exits with code 1 instead of emitting ENOENT.
|
||||
// Fall back to checking if the command exists in PATH, resolving relative paths
|
||||
// against the exec prefix to correctly handle --filter contexts.
|
||||
if (process.platform === 'win32') {
|
||||
const { value: path } = prependDirsToPath(prependPaths)
|
||||
return !which.sync(command, {
|
||||
nothrow: true,
|
||||
path,
|
||||
})
|
||||
const absolutePrependPaths = prependPaths.map(p => path.resolve(prefix, p))
|
||||
const { value: searchPath } = prependDirsToPath(absolutePrependPaths)
|
||||
return !which.sync(command, { nothrow: true, path: searchPath })
|
||||
}
|
||||
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user