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>
54 lines
2.1 KiB
JavaScript
54 lines
2.1 KiB
JavaScript
// 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"'],
|
|
);
|
|
});
|