ADR-0005: ESM-Only Distribution
ADR-0005: ESM-Only Distribution
Status
Accepted
Context
Node.js supports two module systems:
- CommonJS (CJS): Traditional
require()/module.exports - ES Modules (ESM): Modern
import/export
We need to decide which format(s) to distribute:
Options:
- CJS only: Maximum compatibility, but outdated
- ESM only: Modern, tree-shakeable, but excludes old CJS consumers
- Dual (CJS + ESM): Maximum compatibility, but complex build, potential dual-package hazard
Decision
We will distribute ESM only.
Rationale:
- Target audience: Modern TypeScript/JavaScript developers
- Node.js requirement: We already require Node.js 22+
- Simplicity: Single format, simpler build configuration
- Tree-shaking: ESM enables better dead code elimination
- Future-proof: ESM is the standard going forward
Configuration:
{ "type": "module", "exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }, "./runtime": { "import": "./dist/runtime/index.js", "types": "./dist/runtime/index.d.ts" } }}// tsconfig.json{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" }}{ format: ['esm'], // ESM only target: 'node22',}Consequences
Positive
- Simpler build configuration
- Smaller package size (single format)
- Better tree-shaking for consumers
- No dual-package hazard
- Cleaner imports with
.jsextensions
Negative
- Cannot be
require()-ed from CJS code - Excludes projects stuck on old Node.js versions
- Some bundlers may need configuration for ESM
Migration Path for CJS Users
Users with CJS codebases can:
-
Use dynamic
import():const { transpile } = await import("python2ts") -
Upgrade to ESM (recommended)
-
Use a bundler that handles ESM→CJS conversion
File Extensions
All internal imports use .js extensions as required by ESM:
// Correctimport { parse } from "./parser/index.js"
// Incorrect (won't work in ESM)import { parse } from "./parser/index"