PKG003-dual-package-hazard
Dimension: packaging
Default severity: Medium
Languages: JavaScript / TypeScript (npm)
Kind: ProjectLevel
CWE: (none)
OWASP: —
What it detects
Fires when a package.json exposes both CommonJS and ESM entry points without
a proper conditional exports map. Specifically, the rule fires when either of
these conditions holds:
- Both
main(CJS) andmodule/exports(ESM) are declared, but noexportsmap covers at least bothimportandrequireconditions. type: "module"is declared alongside.cjsfiles at the project root (a common mis-configuration where the CJS build was not isolated).
Why it matters
Dual CJS+ESM packages can cause two copies of the same package to be loaded in
the same process — one by require, one by import. This breaks:
- Singleton assumptions (e.g. React context, store instances).
WeakMapkeys — two copies means two sets of prototype chains.instanceofchecks — objects from one copy fail checks against the other.
Example — flagged
{
"main": "dist/index.cjs",
"module": "dist/index.esm.js"
}
Both main (CJS) and module (ESM) are declared, but no exports map provides
import/require conditions.
Example — not flagged
{
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs"
}
}
}
The exports map covers both import and require conditions — consumers load
the correct variant.
How to fix
Add a conditional exports map that explicitly routes import and require to
their respective builds:
{
"main": "dist/index.cjs",
"module": "dist/index.esm.js",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
}
}
Suppression
[ignore]
rules = ["PKG003-dual-package-hazard"]