Proposal: Allow CommonJS modules to declare ESM named exports #448
Description
Goal
Allow module authors to support const {named1, named2} = require('pkg1')
and import {named1, named2} from 'pkg1'
in the same version of pkg1
.
Proposed implementation
When CommonJS is loaded by the ESM loader it would use const esmExports = module.esmExports || {default: module.exports};
to determine the intended export structure. The result of Object.entries(esmExports)
would be iterated to set exports on the ES module.
Usage Examples
Named exports only
module.exports.named1 = 'value1';
module.exports.named2 = 'value2';
module.esmExports = module.exports;
Default function with named exports
function defaultFn(...args) {}
defaultFn.namedExport = 'const value';
module.exports = defaultFn;
module.esmExports = {
default: (...args) => defaultFn(...args),
...module.exports
);
With API compatibility
module.esmExports = {
...module.exports,
default: module.exports
);
This compatibility code would ensure import pkg from 'pkg'
will provide the same result in 13.2.0 as it will in a future version supporting esmExports
, so named ESM exports can be added to a package without a semver-major release.
@babel/plugin-transform-modules-commonjs
// ./dist/index.js is generated by babel
module.exports = module.esmExports = require('./dist/index.js');
This shows how to produce correct ESM exports using output of ESM code translated by babel. This does not address API compatibility so it would potentially be a semver-major release. This example does not show fix-ups to the CJS default export, so CJS users would still have to use require('pkg').default
to access the default export.
Why not interpret __esModule
The meaning of import cjsDefault from 'cjs-module'
is already established. Supporting the babel __esModule
attribute now would result in a breaking change to existing modules, authors would be unable to control for this. If this is implemented I think we should ask @babel/plugin-transform-modules-commonjs
if they're willing to add an option to have the transformed code set module.esmExports
in addition to module.exports
.