Skip to content

Commit 499b849

Browse files
George-iamclaude
andcommitted
fix(search-install): skip optional deps (sharp) + augment PATH for npm subprocesses
User report 2026-05-19: with the previous Windows fixes in place, npm install actually started running successfully — got past the EINVAL, downloaded packages — but failed on @huggingface/transformers' optional `sharp` dependency: npm error path C:\...\runtime\node_modules\sharp npm error command C:\WINDOWS\system32\cmd.exe /d /s /c node install/check.js || npm run build npm error 'node' is not recognized as an internal or external command Two underlying issues: (A) sharp is an image-processing library that's an optional dependency of @huggingface/transformers. Our use case is text embeddings only (Xenova MiniLM), so we don't need sharp. Skip it with `--omit=optional`. As a bonus this also avoids pulling onnxruntime-web — we only want onnxruntime-node. (B) sharp's postinstall script shells out to cmd.exe which does its own PATH lookup for `node`. On a Windows machine with no system Node installed (our target user), this fails. The bundled node.exe we're running from isn't on the user's PATH. Fix (B) by augmenting PATH in the spawn env: prepend dirname(process.execPath) to the PATH passed to the npm child process. Now any postinstall script invoked via cmd.exe can resolve `node` and `npm` from the bundled runtime. Belt-and- braces for future deps with similar postinstalls. After this fix the npm install completes with the text-only stack intact. The user can stay on bundled Node, no system Node ever required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9539fbe commit 499b849

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

src/tools/search-install.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ function installTransformers(): { ok: boolean; error?: string } {
9999
"--prefix", dir,
100100
"--no-audit",
101101
"--no-fund",
102+
// @huggingface/transformers lists `sharp` (image processing) as an
103+
// optional dependency. sharp's postinstall script calls `node
104+
// install/check.js` via cmd.exe — and cmd.exe can't find `node` on
105+
// the user's PATH because the user has no system Node (which is
106+
// the whole point of our bundled Node runtime). For our use case
107+
// (text embeddings via @xenova MiniLM), sharp isn't needed.
108+
// Skip it. Also drops onnxruntime-web (we only want -node).
109+
"--omit=optional",
102110
`@huggingface/transformers@${TRANSFORMERS_VERSION}`,
103111
];
104112
// shell:true (the .cmd fallback) does NOT quote argv — Node joins on
@@ -108,9 +116,18 @@ function installTransformers(): { ok: boolean; error?: string } {
108116
const spawnArgs = npm.useShell
109117
? npmArgs.map((a) => (/[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a))
110118
: npmArgs;
119+
// Augment PATH so any subprocess npm spawns (preinstall / postinstall
120+
// scripts of dependencies) can find `node` and `npm` — they shell
121+
// out via cmd.exe which inherits PATH. Without this, even with
122+
// --omit=optional in place a future dependency with a postinstall
123+
// script would fail the same way sharp did. Belt-and-braces.
124+
const nodeDir = dirname(process.execPath);
125+
const sep = process.platform === "win32" ? ";" : ":";
126+
const augmentedPath = `${nodeDir}${sep}${process.env.PATH ?? ""}`;
111127
const result = spawnSync(npm.cmd, spawnArgs, {
112128
stdio: ["ignore", "inherit", "inherit"],
113129
shell: npm.useShell,
130+
env: { ...process.env, PATH: augmentedPath },
114131
});
115132

116133
if (result.error) return { ok: false, error: `npm spawn failed (${npm.cmd}): ${result.error.message}` };

0 commit comments

Comments
 (0)