What broke
PR #4660 (closing #4621, "remove unused npm dependencies" from the review-stack architecture audit)
removed node-addon-api and node-gyp from the root package.json's devDependencies, based on a
source-grep finding zero import statements for either package.
That grep was correct about application-code imports, but missed that sharp's own install script
needs node-addon-api as a build-from-source fallback when no matching prebuilt native binary is
available for the current platform/arch/Node version combination. node-gyp performs that build.
Neither is ever imported by source code — npm's own postinstall/build machinery pulls them in, which a
source grep can't see.
Impact
Confirmed as a live, reproducible regression: npm ci on a clean checkout of current main fails
outright with:
npm error sharp: Attempting to build from source via node-gyp
npm error sharp: Please add node-addon-api to your dependencies
on macOS arm64 / Node 26 (this development machine). CI apparently didn't catch it because its runner has
a matching prebuilt sharp binary cached/available, so the source-build fallback path never triggers
there — but any local dev setup or self-hoster building on a platform without a matching prebuilt binary
hits this immediately, blocking npm ci/npm install entirely.
Fix
Restore both dependencies. Fixed directly (not filed as a "please fix" issue) — this tracking issue
exists for the audit trail and to record the lesson for #4619/#4611/future dead-dependency-removal work:
a source-grep is not sufficient to prove an npm dependency is unused — native-module build-fallback
chains, postinstall scripts, and other npm-internal dependency resolution can pull a package in with zero
application-code references. Verify by actually running rm -rf node_modules && npm ci on more than one
platform (or at minimum, don't trust a grep-only check for anything a native/build-tooling package like
node-gyp/node-addon-api/sharp/etc. might transitively need) before removing a dependency purely on
import-grep evidence.
@hookform/resolvers's removal (the third dependency #4660 removed) is unaffected and stays removed.
What broke
PR #4660 (closing #4621, "remove unused npm dependencies" from the review-stack architecture audit)
removed
node-addon-apiandnode-gypfrom the rootpackage.json's devDependencies, based on asource-grep finding zero import statements for either package.
That grep was correct about application-code imports, but missed that
sharp's own install scriptneeds
node-addon-apias a build-from-source fallback when no matching prebuilt native binary isavailable for the current platform/arch/Node version combination.
node-gypperforms that build.Neither is ever
imported by source code — npm's own postinstall/build machinery pulls them in, which asource grep can't see.
Impact
Confirmed as a live, reproducible regression:
npm cion a clean checkout of currentmainfailsoutright with:
on macOS arm64 / Node 26 (this development machine). CI apparently didn't catch it because its runner has
a matching prebuilt
sharpbinary cached/available, so the source-build fallback path never triggersthere — but any local dev setup or self-hoster building on a platform without a matching prebuilt binary
hits this immediately, blocking
npm ci/npm installentirely.Fix
Restore both dependencies. Fixed directly (not filed as a "please fix" issue) — this tracking issue
exists for the audit trail and to record the lesson for #4619/#4611/future dead-dependency-removal work:
a source-grep is not sufficient to prove an npm dependency is unused — native-module build-fallback
chains, postinstall scripts, and other npm-internal dependency resolution can pull a package in with zero
application-code references. Verify by actually running
rm -rf node_modules && npm cion more than oneplatform (or at minimum, don't trust a grep-only check for anything a native/build-tooling package like
node-gyp/node-addon-api/sharp/etc. might transitively need) before removing a dependency purely onimport-grep evidence.
@hookform/resolvers's removal (the third dependency #4660 removed) is unaffected and stays removed.