Skip to content

fix(dev): spawn .cmd/.bat shims through cmd.exe on Windows - #579

Open
yzxcj797 wants to merge 1 commit into
yc-software:mainfrom
yzxcj797:fix/windows-cmd-spawn-551
Open

fix(dev): spawn .cmd/.bat shims through cmd.exe on Windows#579
yzxcj797 wants to merge 1 commit into
yc-software:mainfrom
yzxcj797:fix/windows-cmd-spawn-551

Conversation

@yzxcj797

@yzxcj797 yzxcj797 commented Aug 18, 2026

Copy link
Copy Markdown

Summary

Fixes #551.

scripts/dev cannot boot a dev pool on Windows: child_process.spawn can't execute the batch shims that npm/tsx install on PATH (spawn("npm") → ENOENT, spawn("npm.cmd") → EINVAL on current Node), which breaks deps.ts's npm install / npm run build and any other bare-command child spawn.

Fix — resolution at the single spawn choke point

scripts/dev/lib/proc.ts now resolves commands on win32 before spawning (both run() and spawnDetached() route through it):

  1. PATH lookup with PATHEXT-first ordering. Node's install dir ships an extensionless npm sh-script for bash environments next to npm.cmd; trying the extensionless name first hands spawn a non-executable file, so PATHEXT extensions are tried before the bare name (matching cmd.exe semantics).
  2. .cmd/.bat shims run through cmd.exe /d /s /c with one pre-quoted command line ("C:\Program Files\nodejs\npm.cmd" --version) wrapped in an outer quote pair that /s strips, passed with windowsVerbatimArguments: true — Node's default backslash-escaping of embedded quotes is something cmd.exe cannot parse, and the shim path almost always contains a space.
  3. .exe resolves to its full path and spawns directly; non-Windows platforms and non-batch commands are untouched (identity passthrough asserted by tests).

Verification (on Windows, Node 24.14)

  • run("npm", ["--version"]) → exit 0, version printed — the exact call that produced deps: fail -- npm install failed ... spawn npm ENOENT before.
  • spawnDetached runs a node child through the same choke point and writes its marker file.
  • New test/dev-proc-windows-spawn.test.ts: batch-shim routing (asserts the cmd.exe invocation shape), plain-executable resolution, non-Windows passthrough, the real npm --version round-trip, and the detached spawn. Windows-only assertions are skip-gated on other platforms; the passthrough test runs everywhere.

Notes

  • portHolders() still uses lsof and is a silent no-op on Windows — left out of scope (it fails safe: stale-port cleanup just doesn't free anything).

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

child_process.spawn cannot run the batch shims npm/tsx install on PATH:
the bare name fails with ENOENT and the explicit .cmd fails with EINVAL
on current Node (yc-software#551), so scripts/dev (deps.ts npm install/build, the
detached supervisor spawn) cannot boot a dev pool on Windows.

Resolve the command against PATH at the single spawn choke point in
scripts/dev/lib/proc.ts:

- PATHEXT extensions are tried before the extensionless name -- Node's
  install dir ships an extensionless 'npm' sh-script for bash that would
  otherwise win over npm.cmd;
- .cmd/.bat shims run via cmd.exe /d /s /c with one pre-quoted command
  line (shim paths contain spaces, e.g. C:\Program Files\nodejs\npm.cmd)
  and windowsVerbatimArguments so Node's backslash quote-escaping does
  not reach cmd.exe;
- .exe resolves to its full path and spawns directly; non-Windows
  platforms and non-batch commands are untouched.

run() and spawnDetached() both route through the resolver. Tests cover
batch-shim routing, plain-executable resolution, non-Windows passthrough,
a real npm --version round-trip on Windows, and a detached child.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: dev pool fails to boot — spawn npm ENOENT / npm.cmd EINVAL (Node 24.18)

1 participant