ADR-001: Use ESM over CommonJS
Status: accepted
Deciders: Richard Attermeyer
Date: 2025-07-01
Context and Problem Statement
Node.js supports two module systems: CommonJS (CJS, require/module.exports) and ECMAScript Modules (ESM, import/export).
The project needs to choose one as its primary module format.
CJS is the legacy standard with the widest ecosystem compatibility.
ESM is the forward-looking standard aligned with browser JavaScript and the ECMAScript specification.
The decision affects tooling, dependency compatibility, and future maintenance burden.
Decision Drivers
-
Future-proofness: the choice should age well over the project’s lifespan
-
Antora compatibility: the extension is loaded by Antora’s
require(), which expects CJS — ESM creates friction at the integration point -
Ecosystem compatibility: npm packages used by the project (commander, chalk, mustache) must work
-
Developer experience: module resolution, tooling support, debugging
Decision Outcome
Chosen option: ESM with "type": "module", despite knowing it creates friction with Antora’s CJS extension loader.
The project accepted the cost of .cjs wrappers as a one-time investment in exchange for long-term alignment with the ECMAScript standard and Node.js’s stated direction.
CJS is in maintenance mode; ESM is where the ecosystem is heading.
Positive Consequences
-
Modern import/export syntax, better static analysis, tree-shaking support
-
Aligned with browser JavaScript — reduces mental model switching
-
node:protocol for built-in imports makes dependencies explicit
Negative Consequences
-
Antora loads extensions via
require()which expects CJS. Every ESM.jsfile that Antora’s extension loader touches needs a.cjswrapper — not only third-party packages. This is the primary integration friction: the project’s own extension entry point must be wrapped. -
Third-party CJS packages (
@antora/pdf-extension) require additional.cjswrappers, compounding the issue. -
__dirnameandrequirenot available without workarounds in ESM modules. -
Stricter module resolution can surface edge cases in test runners (e.g., Mocha + ESM needed
tsxworkaround).