Feature: Named exports when importing CJS #81
Description
Currently in NodeJS, if I import from a CJS module,
import ns from `./cjs-module.js`
It will allow this, but won’t allow named exports, i.e. the following is not allowed:
import {namedSomething} from `./cjs-module.js`.
This is called transparent interop.
The reason that NodeJS doesn’t allow named exports is that determining what those exports are MUST happen in the parsing phase of the ESM Spec (according to some, although there is contention about that too), and executing code is not allowed at or prior to that phase. But a CJS module’s exports can only be determined by evaluating the code in it (static analysis of JS is not an option as it is not determined to always give the correct results).
This would maybe have been cool if NodeJS was the first ESM implementation, but people are used to the babel way of doing modules, and in the babel world, named exports from CJS modules are allowed. This is because babel does not conform to the ESM spec to the letter (it can’t, because it just transpiles ESM to CJS).
And, good or bad, developers expect to use named exports when importing CJS.
I see these options:
- Continue the existing way (no named exports for CJS)
- Don’t conform to the spec when importing CJS
- Do late-linking/shaping of named modules based on late evaluation of CJS module
- Disallow transparent interop, and enable
import.meta.require
(or equivalent) to enable importing CJS from ES modules - Enable metadata in the CJS module that can statically describe the exports for ESM, e.g. something like
//export default; export foo, bar;
at the head of the CJS file, thus enabling named exports when importing the file.
I am sure there are others options, so if you have another option besides those four, please add it here.
It would be great if for each options you specify pros and cons, or at least if you don’t like the option, specify a clear and simple use case that would be problematic if the option was chosen.
Edit by @GeoffreyBooth: Use case 12.