Not by label. A bundler includes code your application imports, whether the package is listed under dependencies or devDependencies. A devDependency nothing imports stays out of the bundle. Tree shaking trims unused exports, but it does not remove everything, so check what your build actually ships.
Two different questions
People mix up two things. The first is what gets installed on a server or in a container image. The second is what a bundler packs into files sent to browsers.
For installs, the npm install documentation says omitted dev dependencies are still resolved and recorded in the lockfile, but they are not physically installed on disk. That is the production flag at work, and it matters for server images.
For bundles, the webpack guide explains that tree shaking works from the static import and export structure of ES modules. The label in package.json is not part of that logic. If your source imports a package, it is bundled. If nothing imports it, it is not.
Where tree shaking stops
The webpack guide is clear that tree shaking does not remove all unused code. Its analysis is conservative with dynamic JavaScript, and modules that might have side effects are kept. Code transpiled to CommonJS is not tree shaken. The sideEffects field in package.json tells the bundler which files are safe to skip when their exports are unused, but setting it wrongly can drop code you needed, such as CSS imported only for styles.
So a vulnerable function inside a package you import may still ship even if you call only one other function.
How to check what shipped
- Build for production with the same settings as your release.
- Run your bundler's analysis or stats output to list included modules.
- Compare the module list to your lockfile. Anything in the bundle is in scope for vulnerability triage.
- Move build-only tools such as test runners and linters out of your production install with the omit flag for dev dependencies.
- Keep the list as an SBOM so it is easy to answer "were we exposed?" later.
| Question | Where to look |
|---|---|
| What runs on my server? | Installed production dependencies |
| What do browsers download? | Bundle module list |
| What might be vulnerable? | Lockfile, narrowed by what is imported or reachable |
Try it yourself
Click through it at your own pace, or open it full screen.
Compare the full finding list with the reachable subset. Shipped code and reachable code are not the same thing, and both matter.
Quick check
If a package is in devDependencies, can it still reach users?
Yes, if your application code imports it and your bundler includes it. The category does not control bundling.
Does tree shaking remove all unused code?
No. The webpack guide says it is conservative and keeps modules it cannot prove are free of side effects.
Safeguard pairs dependency scanning with reachability analysis, so you can focus on vulnerable code your application can actually call. Try the free scan at gold.safeguard.sh/scan. Related: does npm audit flag a dev dependency, and do I need to fix it.
Sources
Last reviewed: 19 October 2026.