sindresorhus / sindresorhus/execa
Execa 10 - Windows: arguments are double caret-escaped for every batch file, not just cmd-shims
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 7.6k
- Forks
- 570
- PR merge metrics
- No merged PRs in 30d
Description
Draft issue for sindresorhus/execa
Title: Windows: arguments are double caret-escaped for every batch file, not just cmd-shims
Since v10, execa escapes arguments twice for every .cmd/.bat file on Windows. That is right for npm cmd-shims, which re-expand %* through a second cmd.exe parse, but wrong for batch files that read their own arguments: they get the leftover carets and fail. cross-spawn, used through v9, only double-escaped for shims under node_modules/.bin, so this is a regression from #1251.
Steps to reproduce
args.cmd (CRLF):
@echo off
if "%~1" == "-f" (
echo file is %~2
)
import {execa} from 'execa';
await execa('./args.cmd', ['-f', String.raw`C:\repo\pom.xml`], {stdio: 'inherit'});
Expected (execa 9.6.1): file is C:\repo\pom.xml
Actual (execa 10.0.1): The syntax of the command is incorrect., exitCode: 255
Cause
lib/arguments/command-file.js decides from the extension alone:
const batchFileRegExp = /\.(?:bat|cmd)$/i; // line 26
const isDoubleEscape = resolvedFile !== undefined && batchFileRegExp.test(resolvedFile); // line 50
where cross-spawn keyed it off the file being a shim (/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i). The command lines differ by one level of escaping:
9.6.1 cmd.exe /d /s /c "…\args.cmd ^"-f^" ^"C:\repo\pom.xml^""
10.0.1 cmd.exe /q /d /s /c "…\args.cmd ^^^"-f^^^" ^^^"C:\repo\pom.xml^^^""
cmd.exe consumes one level when invoking the batch file; a shim consumes the second while expanding %*, which is why shims still work. A batch file that tests its arguments instead sees %1 as ^"-f^", so if "%~1" == "-f" ( expands to if "^"-f^"" == "-f" ( — a syntax error.
Environment
execa 10.0.1 (works on 9.6.1), Node.js 22/24/26, windows-latest GitHub Actions runner.
Found through Maven's mvn.cmd, which parses its own -f argument exactly like the snippet above: it broke every Windows CI job in Azure/typespec-azure and forced a pin back to execa@^9.6.1.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/arguments/command-file.js, especially the batch-file detection around lines 26 and 50, and reproduce the issue with the shown args.cmd example on Windows. Done means ordinary .cmd/.bat files receive correctly escaped arguments while cmd-shims retain the extra escaping, with the reported regression covered by verification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100