Package entry points
How conditional exports choose a package entry
Understand import, require, node, and default export conditions, why their order matters, and what metadata cannot prove.
The core answer
A package's exports map defines public entry points and can select different targets for import and require(). Node evaluates matching conditions in object order, so the first applicable branch matters. Read the selected path together with its file extension, package type, and the exact Node version.
The branch and the module format are separate
The import condition matches imports, including dynamic import(). The require condition matches require() calls; its name does not force the target file to be CommonJS. Modern Node can load some synchronous ESM targets through require().
The node condition can apply to either importing style. default is the generic fallback and should come last. Nested objects preserve condition ordering, so adding a broad branch before a more specific one can change which target is selected.
{
"name": "example-package",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
}
}
}This synthetic manifest routes imports to index.js and require() to index.cjs. Under type: module, .js is ESM; .cjs explicitly remains CommonJS. The example does not establish that either file exists or that the APIs match.
Public subpaths and extra conditions also matter
An exports map can expose the root entry, named subpaths, or patterns. A file existing inside a package does not necessarily make it publicly importable. An unexported subpath can produce ERR_PACKAGE_PATH_NOT_EXPORTED.
Node also defines conditions such as node-addons and module-sync. The latter targets an ESM graph without top-level await. Custom conditions and build tools can alter resolution, so a root-entry check should be treated as one specific environment, not a complete export-map audit.
What to check next
- 1
Start with the exact specifier and consumer
Distinguish the package root from a subpath, and import from require(). Record the Node version and any custom conditions or resolver used by the application.
- 2
Follow conditions in declaration order
Inspect the installed package's exports map, including nested conditions. Check the chosen target's extension and nearest package type; do not infer CommonJS support from the require label alone.
- 3
Verify the selected API in your environment
Use documented public entry points and test the actual import or require() call. If a bundler is involved, confirm its condition and platform settings separately.
What a declaration cannot establish
- A root-entry declaration check does not verify every exported subpath, custom condition, or browser branch.
- Selected entry paths do not prove that the files are shipped, synchronous, or free of runtime errors.
- Separate CommonJS and ESM entries can expose different APIs or module instances; test shared state and interop behavior.