Because npm treats a binding.gyp file as an instruction to compile. The npm scripts documentation says that if a package has a binding.gyp in its root and has not defined its own install or preinstall script, npm defaults the install command to node-gyp rebuild. That compiles native code on your machine at install time.
The rule, in the docs
In the lifecycle section of the npm scripts page, npm describes the order for install: preinstall, install, postinstall, then prepare related steps. A note in that section says: if there is a binding.gyp file in the root of your package and you have not defined your own install or preinstall scripts, npm will default the install command to compile using node-gyp rebuild.
The best practices part of the same page adds that you should not use install, you should use a gyp file for compilation, and the only valid use of install or preinstall scripts is for compilation that must be done on the target architecture.
Why this is easy to miss
A package can have no scripts block at all and still run a compiler at install. A reviewer who searches package.json for postinstall finds nothing. The signal is a file named binding.gyp next to package.json.
| Signal in the package | What npm does at install |
|---|---|
| A postinstall script in package.json | Runs that script |
| An install or preinstall script | Runs it |
| A binding.gyp and no install or preinstall script | Runs node-gyp rebuild |
| None of the above | Installs files only, though lifecycle scripts like prepare may still apply to the root project |
What a compile step can do
The build runs as your user, with your environment. It invokes a compiler on source files that came from the package, so it executes whatever the build instructions say, and the resulting binary is loaded by Node when your code requires it. It is the same trust you give any install script, plus a toolchain.
Two common consequences:
- Install failures when Python, make or a C++ compiler is missing. That is normal, and the error often mentions node-gyp.
- A binary that loads into your process. Native addons run with the full permissions of the Node.js process, a point covered in Node.js native addon security risks.
How to find native packages in a project
- Run an install with foreground scripts so you can see the output. The npm docs note that scripts run in the background since npm 7, and the foreground scripts option shows them.
- Look in node_modules for folders that contain a binding.gyp file.
- Run your dependency scanner or an SBOM tool and list packages that are native code.
- Compare to your allow list if you use one. See npm allowScripts policy.
How to control it
You can turn dependency install scripts off with the ignore scripts setting, covered in how to disable install scripts. Packages that need a build will then fail to load or fall back to a pure JavaScript path, so test the result. Keep an explicit approved list for the few native packages you rely on, and rebuild those on purpose.
Prefer packages that ship prebuilt binaries or a WebAssembly fallback when that matches your needs, and read why sharp installs platform binaries as optional dependencies for how that model works.
Safeguard's SBOM and dependency scanning list native packages with the rest of your tree, and its policy gates can hold a pull request that adds one. A free scan is at gold.safeguard.sh/scan.
Quick check
Can a package compile code without a postinstall script?
Yes. A binding.gyp in the package root, with no install or preinstall script defined, makes npm run node-gyp rebuild.
Does ignore scripts stop node-gyp builds?
It tells npm not to run scripts, which should include that default, but verify on your own project. The package may then not work, so test.
Sources
Last reviewed: 19 October 2026.