Copilot review round 3, all three real:
- `require.resolve.paths()` also appends Node's GLOBAL FOLDERS
($HOME/.node_modules, $HOME/.node_libraries, $PREFIX/lib/node) and any
NODE_PATH entries, so round 2's lookup could satisfy a missing repo
install from a GLOBALLY installed typescript/vite — measuring the
programs with the wrong compiler instead of producing the actionable
"run npm install" this helper promises. Filter to the node_modules
directories on fromDir's own ancestor chain.
- The quoting predicate missed characters cmd.exe still parses, notably
parentheses (`C:\Temp(1)`), and quoting cannot suppress `%VAR%` at all
— expansion happens before quotes are processed. Extracted to
lib/win-shell-args.mjs with a broad predicate and a hard error on `%`
rather than a silently different path. `platform` is a parameter so the
Windows behavior is exercised on the POSIX machines that run the suite.
- AGENTS.md claimed pack-and-verify spawns via `process.execPath`; it does
not, and cannot — its children are `npm` and the installed `.bin` shim.
State the exception and why.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01387r3MotGk52hrkfjzHFsN
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot review round 2 (suppressed comments), all three real:
- `require.resolve("<pkg>/package.json")` is governed by the package's
`exports`, and Node keeps no special case for `./package.json` — so a
package declaring an `exports` map without it threw
ERR_PACKAGE_PATH_NOT_EXPORTED even though its bin was installed and
spawnable, a false "not installed" pointing at `npm install` for a
package already on disk. Walk `resolve.paths()` instead, which never
consults `exports`. The docblock claiming the opposite is corrected.
- `shell: true` (needed at all on Windows to start the `.cmd` shims) makes
Node hand `cmd.exe` one space-joined string, so an argument holding a
path with a space splits in two. Every generated path in
pack-and-verify lives under `tmpdir()`, which sits beneath the user
profile. Quote them via `shellArgs()` at all four spawn sites, the
command path included — the hazard predates this PR, so fix the class.
- Document the new module: `scripts/lib/resolve-node-bin.mjs` in AGENTS.md's
scripts/ tree, its tests in the README's `test:scripts` row.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01387r3MotGk52hrkfjzHFsN
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot review on #1997:
- A string-form `bin` is npm's shorthand for ONE command, named after the
package, so ignoring `binName` let a typo silently resolve the package's
only executable while bypassing the documented "declares no such bin"
failure. Match it against the manifest's unscoped name instead.
- A declared bin whose file is absent (partial install) was returned
unchecked. `process.execPath <missing>` still spawns and exits 1 with
empty stdout, which `rawProjectListing` records as "no diagnostic
captured" — reproducing the bogus every-file-uncovered report this
helper exists to eliminate. Throw here, where the remedy is actionable.
Also restores `rawProjectListing`'s docblock, which the new `tscEntry`
block had orphaned above itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01387r3MotGk52hrkfjzHFsN
Signed-off-by: cliffhall <cliff@futurescale.com>
On Windows npx/npm are .cmd shims a shell-free execFileSync/spawnSync
cannot start (ENOENT since the CVE-2024-27980 hardening), so
verify:typecheck-coverage died at validate's second step — doubly
silently, echoing "(no diagnostic captured)" per project and then
reporting all 918 tracked files as getting no tsc pass — and
verify:build-gate, the smoke test-server bootstraps, and pack:verify's
npm pack failed the same way.
Add scripts/lib/resolve-node-bin.mjs: resolves the JS entry behind a
package's bin from <pkg>/package.json (deep resolution is blocked by
Vite 8's exports map) and spawns it via process.execPath — the same
walk npx --no-install did, cross-platform and shell-free. Switch the
six npx call sites to it; the npm pack call gets the existing
shell-on-win32 idiom instead (npm has no in-tree package to resolve).
An unresolvable tsc is now a hard "cannot measure" error with
actionable stderr, pinned by a new main() regression test.
Closes#1939
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>