fix(process): resolve npm/pnpm spawn ENOENT on Windows

On Windows, non-.exe commands like npm, pnpm, yarn, npx require
their .cmd extension when using spawn(). This adds a resolveCommand()
helper that automatically appends .cmd on Windows for these commands.

Fixes #5773
This commit is contained in:
Jhin 2026-02-01 01:03:35 +00:00 committed by Tak Hoffman
parent 01d76e4799
commit 5c8880ed3f

View file

@ -7,6 +7,23 @@ import { resolveCommandStdio } from "./spawn-utils.js";
const execFileAsync = promisify(execFile); const execFileAsync = promisify(execFile);
/**
* Resolves a command for Windows compatibility.
* On Windows, non-.exe commands (like npm, pnpm) require their .cmd extension.
*/
function resolveCommand(command: string): string {
if (process.platform !== "win32") {
return command;
}
const basename = path.basename(command).toLowerCase();
// Common npm-related commands that need .cmd extension on Windows
const cmdCommands = ["npm", "pnpm", "yarn", "npx"];
if (cmdCommands.includes(basename)) {
return `${command}.cmd`;
}
return command;
}
// Simple promise-wrapped execFile with optional verbosity logging. // Simple promise-wrapped execFile with optional verbosity logging.
export async function runExec( export async function runExec(
command: string, command: string,
@ -22,7 +39,7 @@ export async function runExec(
encoding: "utf8" as const, encoding: "utf8" as const,
}; };
try { try {
const { stdout, stderr } = await execFileAsync(command, args, options); const { stdout, stderr } = await execFileAsync(resolveCommand(command), args, options);
if (shouldLogVerbose()) { if (shouldLogVerbose()) {
if (stdout.trim()) { if (stdout.trim()) {
logDebug(stdout.trim()); logDebug(stdout.trim());
@ -89,7 +106,7 @@ export async function runCommandWithTimeout(
} }
const stdio = resolveCommandStdio({ hasInput, preferInherit: true }); const stdio = resolveCommandStdio({ hasInput, preferInherit: true });
const child = spawn(argv[0], argv.slice(1), { const child = spawn(resolveCommand(argv[0]), argv.slice(1), {
stdio, stdio,
cwd, cwd,
env: resolvedEnv, env: resolvedEnv,