Modern exports field defined
Why it matters
Without a modern exports field, Node.js 16+ consumers get inconsistent resolution across bundlers and runtimes. Tools like webpack 5 and Vite treat the exports map as authoritative — packages that rely on legacy main/module fields alone can fail outright under strict resolution or expose internal files via unguarded deep imports. ISO 25010 modifiability and interoperability both degrade when entry points are implicit rather than declared. The practical result: import errors in production that only surface at the consumer's build step, not yours.
Severity rationale
Critical because misconfigured or absent entry-point declarations cause outright import failures at build time for Node.js 16+ and all strict-resolution bundlers.
Remediation
Add the exports field to package.json mapping the root entry point with import, require, and types conditions. Keep legacy main, module, and types fields as fallbacks for older tooling.
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
Use a build tool like tsup (tsup src/index.ts --format esm,cjs --dts) to generate both formats and the exports map automatically. Verify the referenced files exist in dist/ before publishing.
Detection
-
ID:
exports-field -
Severity:
critical -
What to look for: List all export paths configured in package.json. For each export path, check
package.jsonfor theexportsfield. This is the modern way to define package entry points and controls what consumers can import. Look for a structure that maps import paths to file locations, with conditions forimport(ESM),require(CJS), andtypes(TypeScript). Example patterns:"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" }, "./utils": { "types": "./dist/utils.d.ts", "import": "./dist/utils.mjs", "require": "./dist/utils.cjs" } }Also check that the files referenced in
exportsactually exist or would be produced by the build script. If onlymainandmoduleare used (legacy approach), note this but check that they point to valid paths. -
Pass criteria: The
exportsfield is present inpackage.jsonand maps at least the"."entry point with appropriate conditions (import,require, and/ortypes). OR the package uses legacymain/modulefields that correctly point to built output AND the package targets a single runtime (e.g., CJS-only for Node.js CLI tools) — 100% of public entry points must be declared in the exports field. Report the count: "X export paths found, all Y correctly configured." -
Fail criteria: No
exportsfield AND nomain/modulefields, ORexports/main/modulepoint to source files (e.g.,src/index.ts) instead of built output, OR referenced files clearly do not exist and no build script would produce them. -
Skip (N/A) when: Python package (uses
pyproject.toml[project.scripts]orsetup.pyentry_points), Rust crate (usesCargo.toml[lib]), or Go module (uses package directory structure). For these ecosystems, check that the equivalent entry point mechanism is properly configured — if it is, mark as pass; if not, mark as fail. -
Cross-reference: The
dual-formatcheck verifies that exports include both ESM and CJS paths. -
Detail on fail: Describe what's missing or misconfigured. Example:
"No exports field in package.json. main points to src/index.ts (source file, not built output). Consumers using Node.js 16+ with exports-aware resolution will fail to resolve imports." -
Remediation: The
exportsfield is the modern standard for defining what your package exposes. Without it, consumers may get inconsistent resolution behavior across Node.js versions and bundlers.// Before — legacy only, no exports map: { "main": "src/index.js" } // After — modern exports with dual format: { "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs" } }, "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts" }Keep
main,module, andtypesas fallbacks for older tools that don't supportexports. Use a build tool like tsup that generates the exports map automatically.
External references
- iso-25010:2011 · maintainability.modifiability — Modifiability — package entry-point contracts
- iso-25010:2011 · compatibility.interoperability — Interoperability — CJS/ESM consumer compatibility
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sdk-package-quality·automated