- Translates ESM syntax to CommonJS when
target: 'commonjs'withtransformSyntaxenabled. - Assumes Node 22.21+ runtime with
__filename/__dirnameshims supplied by the host (matches package engines). - Keeps optional live-binding behavior via
liveBindings(strict/loose/off). - Top-level await (TLA) handling is controlled by
topLevelAwait: 'error' | 'wrap' | 'preserve'when lowering to CommonJS. - Optional import.meta.main gating via
importMetaMain: 'shim' | 'warn' | 'error'.
importstatements becomerequirecalls; side-effect imports become barerequire()calls.- Default imports use the bundler-style helper
__interopDefault = mod => (mod && mod.__esModule ? mod.default : mod)whencjsDefault: 'auto'; otherwise they can targetmodule.exportsor.defaultdirectly per option. - Namespace imports bind to the full
requireresult; named imports destructure from the same binding. - Re-exporting a default (
export { default as x } from) routes through the interop helper to match bundler behavior for CJS sources. - When the interop helper is emitted,
exports.__esModule = trueis also seeded for downstream consumers.
- Local named exports emit to
exports.<name>(or bracket access) withObject.definePropertygetters whenliveBindings: 'strict'; snapshot assignment is used forloose/off. export defaultwrites tomodule.exports = <expr>in the common case; when TLA is present and allowed (wrap/preserve), default exports write toexports.default = <expr>so the exports object stays stable for async wrapping.export * fromlowers to afor...inover the required module, skippingdefault, guarding onhasOwnProperty, and defining getters to mirror live bindings.- Named re-exports from another module (
export { foo as bar } from) use getters whenliveBindings: 'strict', otherwise snapshot assignment;defaultre-exports still use the interop helper.
topLevelAwait: 'error'(default) throws during transform when a TLA is found while targeting CommonJS.topLevelAwait: 'wrap'wraps the entire CJS output inconst __tla = (async () => { ...; return module.exports; })();and attaches__tlato bothmodule.exportsand the resolved value. Consumers can awaitrequire('./file').__tlafor readiness.topLevelAwait: 'preserve'runs the lowered code inside an immediately-invoked async function but does not attach a promise; consumers must rely on the natural scheduling of async effects.- In both allowed modes, default exports write to
exports.defaultinstead of replacingmodule.exportsto keep the exports object consistent while async initialization completes. - Fixture:
test/fixtures/topLevelAwait.mjsplus tests intest/module.tscover bothwrapandpreservebehaviors.
import.meta.url→require("node:url").pathToFileURL(__filename).hrefimport.meta.filename→__filenameimport.meta.dirname→__dirnameimport.meta.resolve→require.resolveimport.meta.main→ configurable: default shim toprocess.argv[1] === __filename; withimportMetaMain: 'warn'a warning is embedded for Node <22.18/24.2; withimportMetaMain: 'error'the transform throws on those runtimes.- Bare
import.metabecomesmoduleso property accesses stay valid in CJS output.
test/fixtures/esmDefault.mjs: default import from the CJS provider, re-exported as default for interop checks (default import yields the CJS module object).test/fixtures/esmNamed.mjs: named import and aliasing from the CJS provider, re-exported for assertions.test/fixtures/esmNamespace.mjs: namespace import shape from the CJS provider, re-exported asns.test/fixtures/esmReexport.mjs: named + star re-exports from the CJS provider (includes live binding passthrough).test/fixtures/liveReexport.mjs: re-export from a mutating CJS source; exercised whenliveBindings: 'strict'to verify getter-based live bindings.test/fixtures/esmProvider.cjs: CJS source exposing default/named exports plus a mutatinglivecounter for binding checks (interval isunref()ed; tests wait ≥30ms to observe changes).