{
"name": "my-lib",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": "./dist/utils.js"
}
}
main sets one entry point. exports sets the full public interface and blocks everything else.
main names the single entry point that Node uses when someone requires the package by name. Every other file stays reachable by deep paths like pkg/lib/internal.js.
exports lists the package's public interface explicitly. In modern Node it takes priority over main. Only the listed subpaths can be imported. Anything else throws ERR_PACKAGE_PATH_NOT_EXPORTED. This lets authors hide internal files and refactor safely.
exports also supports conditional exports. One package can point import at an ESM build and require at a CommonJS build. It can also use keys like types or default.
Adding exports to an existing package can break users who relied on deep imports. So it is treated as a breaking change.
mainis one entry point, and deep paths stay reachable.exportslists the allowed subpaths and blocks the rest.- Conditional exports give separate targets for
importandrequire. exportswins overmainwhen both exist.- Adding
exportsto a package can be a breaking change.