advanced

Package exports

Design package public APIs with exports maps, conditional exports, subpath exports, types, and backwards compatibility.

`package.json` field `exports` defines the public entry surface — only listed paths are importable, which prevents accidental deep imports and enables conditional targets.

					{
  "name": "my-lib",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    },
    "./utils": "./dist/utils.js"
  }
}
				

Conditions include `import`, `require`, `node`, `default`, and custom names tools may pass. Subpath patterns (`./features/*`) scale public APIs without exporting the whole tree.

Publishing both ESM and CJS requires distinct files or careful dual build — `exports` documents which file each consumer gets.

On interviews: explain how `exports` blocks `pkg/src/internal`; design backwards-compatible subpath additions; relate `types` condition to TS 4.7+ resolution.

Common pitfalls: forgetting `types` in exports; breaking consumers by removing undocumented paths that were relied upon; mismatched `import`/`require` targets.

The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.

Checklist:

  • Explicit `exports` for libraries.
  • Add subpaths instead of deep src imports.
  • Test both ESM and CJS consumers if dual.
  • Document public API surface in changelog.