Every dependency in dependencies is installed by every consumer, even in production. TypeScript, @types/*, test runners, and build tools in dependencies instead of devDependencies inflate the install footprint of every project that depends on yours. node-fetch as a runtime dependency in a package that declares engines: { node: '>=18' } installs a redundant polyfill of a built-in. CWE-1357 covers use of unnecessarily broad components; SLSA L1 and SSDF PO.3.2 both require that the published artifact's dependency tree is intentional and minimal.
Medium because misplaced or unnecessary runtime dependencies increase install size and supply chain surface area for all consumers, without providing any functional benefit.
Audit dependencies and move build tools, type packages, and test frameworks to devDependencies. Replace convenience packages with built-in equivalents.
// Before — misplaced deps:
{
"dependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"node-fetch": "^3.0.0",
"vitest": "^1.0.0"
}
}
// After — clean runtime deps:
{
"dependencies": {},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"vitest": "^1.0.0"
}
}
Run npx depcheck to surface unused dependencies. Replace node-fetch with built-in fetch (Node 18+) and uuid with crypto.randomUUID(). Target: no more than 10 runtime dependencies for a typical SDK.
ID: sdk-package-quality.build-distribution.minimal-deps
Severity: medium
What to look for: Count all runtime dependencies. For each dependency, count the runtime dependencies in package.json (not devDependencies). For each dependency, assess whether it's:
node-fetch on Node.js 18+, uuid when crypto.randomUUID() exists)dependencies instead of devDependencies.Pass criteria: Every runtime dependency serves an essential purpose. No obviously misplaced devDependencies, no unused dependencies, no convenience packages that duplicate built-in functionality — no more than 10 runtime dependencies for a typical SDK to minimize consumer bundle impact. Report: "X runtime dependencies found."
Fail criteria: One or more runtime dependencies are clearly misplaced (test frameworks, build tools, or linters in dependencies), unused (never imported), or unnecessary (duplicating built-in Node.js functionality available in the package's minimum supported Node.js version).
Skip (N/A) when: The package has zero runtime dependencies. Also skip for Go and Rust where dependency management conventions differ significantly.
Detail on fail: "typescript and @types/node are in dependencies instead of devDependencies. vitest is in dependencies — should be devDependencies. node-fetch is in dependencies but package.json engines requires Node.js >= 18 which has built-in fetch."
Remediation: Every runtime dependency is a liability — it increases install size, expands the supply chain attack surface, and may conflict with consumer versions.
// Before — misplaced and unnecessary deps:
{
"dependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"node-fetch": "^3.0.0",
"vitest": "^1.0.0"
}
}
// After — only what consumers need at runtime:
{
"dependencies": {},
"devDependencies": {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"vitest": "^1.0.0"
}
}
Use the built-in fetch on Node.js 18+ instead of node-fetch. Use crypto.randomUUID() instead of the uuid package. Check npx depcheck to find unused dependencies.