Package Publishing
Overview
Covers modern npm package authoring: package.json configuration with the exports field, dual ESM/CJS builds, TypeScript type declarations, and secure publishing workflows with provenance.
When to use: Configuring package entry points, setting up conditional exports, building dual-format packages, publishing scoped packages, or troubleshooting module resolution.
When NOT to use: Application-level bundling (Vite/webpack app configs), monorepo workspace orchestration (Turborepo/Nx), private registry setup (Verdaccio/Artifactory).
Quick Reference
| Pattern |
Field / Command |
Key Points |
| Entry point |
exports in package.json |
Replaces main/module; encapsulates internals |
| CJS fallback |
main |
Legacy consumers without exports support |
| ESM entry |
module |
Bundler convention; not used by Node.js |
| Type declarations |
types condition in exports |
Must be listed first in each condition block |
| Subpath exports |
"./utils": { ... } |
Clean public API; blocks deep imports |
| Conditional exports |
import/require conditions |
Toggle ESM vs CJS per consumer |
| Package type |
"type": "module" |
Makes .js files ESM; use .cjs for CommonJS |
| Side effects |
"sideEffects": false |
Enables tree-shaking in bundlers |
| Peer deps |
peerDependencies |
Shared runtime deps (React, Vue, etc.) |
| Engine constraints |
"engines": { "node": ">=18" } |
Document minimum Node.js version |
| Files allowlist |
"files": ["dist"] |
Controls what gets published to npm |
| Prepublish check |
"prepublishOnly": "npm run build" |
Ensure build runs before publish |
| Dry run |
npm pack --dry-run |
Preview package contents before publishing |
| Provenance |
--provenance flag or trusted publishers |
Cryptographic build attestation |
| Scoped publish |
--access public |
Required for first publish of scoped packages |
Common Mistakes
| Mistake |
Correct Pattern |
Putting types after default in exports |
types must be the first condition in every export block |
Missing "./package.json" in exports |
Include "./package.json": "./package.json" for tooling compatibility |
Different APIs for import vs require |
Same API surface; write ESM source, transpile to CJS |
Using main without exports for new packages |
Use exports as the primary entry point definition |
Forgetting "type": "module" with .js ESM output |
Set "type": "module" or use .mjs extension explicitly |
Publishing src/ or node_modules/ |
Use "files" allowlist to include only dist/ |
No prepublishOnly script |
Add build step to prevent publishing stale artifacts |
Using default export for libraries |
Prefer named exports for consistent cross-tooling behavior |
Not testing with npm pack before publish |
Always dry-run to verify package contents and size |
Omitting peerDependencies for framework plugins |
Declare shared runtime dependencies as peers |
| Publishing without provenance |
Enable provenance for supply-chain transparency |
Using .d.ts for CJS when package is "type": "module" |
Use .d.cts for CJS type declarations, .d.ts or .d.mts for ESM |
Delegation
- Build tooling setup: Use
Explore agent to examine tsup/unbuild/rollup configs
- Type resolution debugging: Use
Task agent with "Are the Types Wrong?" (attw)
- Publish pipeline review: Delegate to
code-reviewer agent
References
- Package.json configuration: exports, main, types, files, engines, peerDependencies
- Build scripts, prepublishOnly, npm pack, provenance, scoped packages
- Dual ESM/CJS builds, conditional exports, type declarations
1---2name: package-publishing3description: npm package publishing patterns for modern TypeScript libraries. Use when configuring package.json exports, setting up dual ESM/CJS builds, or publishing to npm. Use for npm-publish, package-json, exports, main, module, types, dual-package, provenance, prepublishOnly.4license: MIT5---6
7# Package Publishing
8
9## Overview
10
11Covers modern npm package authoring: `package.json` configuration with the `exports` field, dual ESM/CJS builds, TypeScript type declarations, and secure publishing workflows with provenance.
12
13**When to use:** Configuring package entry points, setting up conditional exports, building dual-format packages, publishing scoped packages, or troubleshooting module resolution.
14
15**When NOT to use:** Application-level bundling (Vite/webpack app configs), monorepo workspace orchestration (Turborepo/Nx), private registry setup (Verdaccio/Artifactory).
16
17## Quick Reference
18
19| Pattern | Field / Command | Key Points |
20| ------------------- | ----------------------------------------- | ------------------------------------------------ |
21| Entry point | `exports` in package.json | Replaces `main`/`module`; encapsulates internals |
22| CJS fallback | `main` | Legacy consumers without `exports` support |
23| ESM entry | `module` | Bundler convention; not used by Node.js |
24| Type declarations | `types` condition in `exports` | Must be listed first in each condition block |
25| Subpath exports | `"./utils": { ... }` | Clean public API; blocks deep imports |
26| Conditional exports | `import`/`require` conditions | Toggle ESM vs CJS per consumer |
27| Package type | `"type": "module"` | Makes `.js` files ESM; use `.cjs` for CommonJS |
28| Side effects | `"sideEffects": false` | Enables tree-shaking in bundlers |
29| Peer deps | `peerDependencies` | Shared runtime deps (React, Vue, etc.) |
30| Engine constraints | `"engines": { "node": ">=18" }` | Document minimum Node.js version |
31| Files allowlist | `"files": ["dist"]` | Controls what gets published to npm |
32| Prepublish check | `"prepublishOnly": "npm run build"` | Ensure build runs before publish |
33| Dry run | `npm pack --dry-run` | Preview package contents before publishing |
34| Provenance | `--provenance` flag or trusted publishers | Cryptographic build attestation |
35| Scoped publish | `--access public` | Required for first publish of scoped packages |
36
37## Common Mistakes
38
39| Mistake | Correct Pattern |
40| -------------------------------------------------------- | ---------------------------------------------------------------------- |
41| Putting `types` after `default` in exports | `types` must be the first condition in every export block |
42| Missing `"./package.json"` in exports | Include `"./package.json": "./package.json"` for tooling compatibility |
43| Different APIs for `import` vs `require` | Same API surface; write ESM source, transpile to CJS |
44| Using `main` without `exports` for new packages | Use `exports` as the primary entry point definition |
45| Forgetting `"type": "module"` with `.js` ESM output | Set `"type": "module"` or use `.mjs` extension explicitly |
46| Publishing `src/` or `node_modules/` | Use `"files"` allowlist to include only `dist/` |
47| No `prepublishOnly` script | Add build step to prevent publishing stale artifacts |
48| Using `default` export for libraries | Prefer named exports for consistent cross-tooling behavior |
49| Not testing with `npm pack` before publish | Always dry-run to verify package contents and size |
50| Omitting `peerDependencies` for framework plugins | Declare shared runtime dependencies as peers |
51| Publishing without provenance | Enable provenance for supply-chain transparency |
52| Using `.d.ts` for CJS when package is `"type": "module"` | Use `.d.cts` for CJS type declarations, `.d.ts` or `.d.mts` for ESM |
53
54## Delegation
55
56- **Build tooling setup**: Use `Explore` agent to examine tsup/unbuild/rollup configs
57- **Type resolution debugging**: Use `Task` agent with "Are the Types Wrong?" (`attw`)
58- **Publish pipeline review**: Delegate to `code-reviewer` agent
59
60## References
61
62- [Package.json configuration: exports, main, types, files, engines, peerDependencies](references/package-json-config.md)
63- [Build scripts, prepublishOnly, npm pack, provenance, scoped packages](references/build-and-publish.md)
64- [Dual ESM/CJS builds, conditional exports, type declarations](references/dual-format.md)