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
|
|
@ -69,17 +69,48 @@ export function resolveNodeBin(pkg, binName, fromDir) {
|
|||
* installed and perfectly spawnable — a false "not installed" that would send
|
||||
* someone to `npm install` for a package already on disk. `resolve.paths()`
|
||||
* gives the search dirs without consulting `exports` at all.
|
||||
*
|
||||
* Its list is then filtered to the `node_modules` directories on `fromDir`'s
|
||||
* own ancestor chain, because `resolve.paths()` also appends Node's GLOBAL
|
||||
* FOLDERS (`$HOME/.node_modules`, `$HOME/.node_libraries`, `$PREFIX/lib/node`)
|
||||
* and any `NODE_PATH` entries. Accepting those would quietly break the
|
||||
* guarantee this module exists to keep — that the spawned tsc/vite is the
|
||||
* REPO-PINNED one, exactly as `npx --no-install` promised. A missing repo
|
||||
* install would then find a globally installed TypeScript and measure the
|
||||
* programs with the wrong compiler, instead of failing with the actionable
|
||||
* "run npm install" this helper is supposed to produce.
|
||||
*/
|
||||
function resolveManifest(pkg, fromDir) {
|
||||
const require = createRequire(path.join(fromDir, "package.json"));
|
||||
// `resolve.paths` returns null only for builtins, which have no bin to find.
|
||||
for (const dir of require.resolve.paths(pkg) ?? []) {
|
||||
for (const dir of localSearchDirs(
|
||||
require.resolve.paths(pkg) ?? [],
|
||||
fromDir,
|
||||
)) {
|
||||
const candidate = path.join(dir, ...pkg.split("/"), "package.json");
|
||||
if (existsSync(candidate)) return candidate;
|
||||
}
|
||||
throw new Error(`cannot find ${pkg} from ${fromDir}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* The subset of `resolve.paths()` that is a `node_modules` directory sitting
|
||||
* directly under `fromDir` or one of its ancestors — i.e. the local walk, with
|
||||
* Node's global folders and `NODE_PATH` dropped. Exported for its own test.
|
||||
*/
|
||||
export function localSearchDirs(dirs, fromDir) {
|
||||
const ancestors = new Set();
|
||||
for (let dir = path.resolve(fromDir); ; dir = path.dirname(dir)) {
|
||||
ancestors.add(dir);
|
||||
if (dir === path.dirname(dir)) break;
|
||||
}
|
||||
return dirs.filter(
|
||||
(dir) =>
|
||||
path.basename(dir) === "node_modules" &&
|
||||
ancestors.has(path.dirname(path.resolve(dir))),
|
||||
);
|
||||
}
|
||||
|
||||
/** `@scope/name` → `name`; an unscoped name is returned unchanged. */
|
||||
function unscopedName(name) {
|
||||
return name.startsWith("@") ? name.slice(name.indexOf("/") + 1) : name;
|
||||
|
|
|
|||
Loading…
Reference in a new issue