-
Notifications
You must be signed in to change notification settings - Fork 45
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.