fix(scripts): keep the resolver local, complete the cmd.exe quoting
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>
This commit is contained in:
parent
b399bc64e1
commit
c93552139c
1 changed files with 207 additions and 21 deletions
54
scripts/lib/win-shell-args.test.mjs
Normal file
54
scripts/lib/win-shell-args.test.mjs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Tests for `win-shell-args.mjs` (#1939). The platform is a parameter rather
|
||||
// than a read of `process.platform`, so the Windows behaviour is exercised on
|
||||
// the Linux/macOS machines that actually run this suite — otherwise every
|
||||
// assertion below would be skipped exactly where the bug lives.
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { quoteWinArg, winShellArgs } from "./win-shell-args.mjs";
|
||||
|
||||
test("passes args through untouched off Windows", () => {
|
||||
const args = ["pack", "--pack-destination", "/tmp/dir with spaces"];
|
||||
assert.deepEqual(winShellArgs(args, "linux"), args);
|
||||
assert.deepEqual(winShellArgs(args, "darwin"), args);
|
||||
});
|
||||
|
||||
test("quotes a path with a space (the user-profile tmpdir case)", () => {
|
||||
assert.equal(
|
||||
quoteWinArg("C:\\Users\\First Last\\AppData\\Local\\Temp"),
|
||||
'"C:\\Users\\First Last\\AppData\\Local\\Temp"',
|
||||
);
|
||||
});
|
||||
|
||||
test("quotes cmd.exe metacharacters, parentheses included", () => {
|
||||
// Parentheses group in cmd.exe, so `C:\Temp(1)` is syntax, not a path — the
|
||||
// case a space-only predicate misses.
|
||||
for (const meta of ["(", ")", "&", "|", "<", ">", "^", "!", ";", ",", "="]) {
|
||||
const arg = `C:\\Temp${meta}1`;
|
||||
assert.equal(quoteWinArg(arg), `"${arg}"`, `unquoted: ${arg}`);
|
||||
}
|
||||
});
|
||||
|
||||
test("leaves an inert argument unquoted", () => {
|
||||
for (const arg of ["pack", "--json", "C:\\Users\\dev\\AppData\\Temp"]) {
|
||||
assert.equal(quoteWinArg(arg), arg);
|
||||
}
|
||||
});
|
||||
|
||||
test("doubles an embedded quote", () => {
|
||||
assert.equal(quoteWinArg('a"b c'), '"a""b c"');
|
||||
});
|
||||
|
||||
test("throws on `%` rather than silently mis-expanding it", () => {
|
||||
// cmd.exe expands %NAME% before it processes quotes, so quoting cannot save
|
||||
// this one — the child would receive a different path than we passed.
|
||||
assert.throws(() => quoteWinArg("C:\\%TEMP%\\pack"), /cannot safely pass/);
|
||||
assert.throws(() => winShellArgs(["ok", "%X%"], "win32"), /cannot safely/);
|
||||
});
|
||||
|
||||
test("winShellArgs maps every element on win32", () => {
|
||||
assert.deepEqual(
|
||||
winShellArgs(["pack", "--pack-destination", "C:\\a b\\c"], "win32"),
|
||||
["pack", "--pack-destination", '"C:\\a b\\c"'],
|
||||
);
|
||||
});
|
||||
Loading…
Reference in a new issue