-
Notifications
You must be signed in to change notification settings - Fork 46
Migrate from CommonJS to ES Modules #1430
Description
Background
The codebase currently uses CommonJS throughout. Since we're on Node.js 24, we have access to mature ESM support including require(esm) interop, import.meta.dirname, and automatic module syntax detection.
Scope
~184 JavaScript files need changes:
- Convert
require()toimport - Convert
module.exportstoexport - Add
.jsextensions to all relative imports - Update JSON imports to use
with { type: 'json' }syntax
Migration approach
Node 22+ allows CJS to require() synchronous ESM files, so we can migrate incrementally rather than all at once. Files with ES module syntax are auto-detected without needing "type": "module" in package.json.
Suggested order:
- Replace blocking dependencies first
- Migrate source files (leaf modules first, working inward)
- Migrate test files last
Blocking dependencies
These packages are CJS-only and need replacement:
| Package | Replacement |
|---|---|
proxyquire |
esmock (8 test files affected) |
azure-storage |
@azure/storage-blob |
painless-config |
dotenv or custom solution |
express-routes-versioning |
inline or find alternative |
geit |
find alternative (appears unmaintained) |
proxyquire is the biggest issue. It fundamentally can't work with ESM because it hijacks require(). All 8 test files using it need rewriting.
Files needing manual attention
Dynamic requires in bin/config.js:32
if (!target) target = require(requirePath) // dynamic path from env varsThis needs redesign using import() or a static import map.
__dirname usage (6 test files)
These can use import.meta.dirname directly—no workaround needed on Node 21.2+.
What can be automated
A tool like cjs-to-esm can handle most of the mechanical conversion:
require→importmodule.exports→export- Adding file extensions
The JSON import syntax and dynamic requires need manual work.
Open questions
- Do we add
"type": "module"to package.json, or rely on auto-detection? - Should we convert to TypeScript source files at the same time, or keep that separate?
- Any preference on
proxyquirereplacement (esmockvs dependency injection refactor)?