When can a CommonJS named export be imported by an ES module?
Asked Answered
J

1

13

I have an ES module that uses a named export from a CommonJS module that I authored.

es.mjs

import { MyNamedExport } from './commonjs.cjs';

console.log(MyNamedExport);

commonjs.cjs (good one)

exports.MyNamedExport = 'OK';

When I run the ES module in Node.js like this everything is fine.

> node ./es.mjs
OK

Anyway, if the export part in the CommonJS module is changed in some seemingly irrelevant way, i.e. by adding a pair of parentheses, the named export stops working.

commonjs.cjs (bad one)

(exports).MyNamedExport = 'OK';
> node ./es.mjs
file:///path/to/current/folder/es.mjs:1
import { MyNamedExport } from './commonjs.cjs';
         ^^^^^^^^^^^^^
SyntaxError: Named export 'MyNamedExport' not found. The requested module './commonjs.cjs' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from './commonjs.cjs';
const { MyNamedExport } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:104:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:149:5)
    at async Loader.import (node:internal/modules/esm/loader:166:24)
    at async Object.loadESM (node:internal/process/esm_loader:68:5)

Of course, when my CommonJS named export is imported by another CommonJS module, the parentheses make no difference.

Why is this happening?

What should I do when I write a CommonJS module to make sure that named exports can be imported by ES modules?

Jovian answered 30/12, 2020 at 17:32 Comment(2)
If you look at the two references to "static analysis" in the nodejs module documentation, it seems likely this is the source of your observation. When you put the parens there, then the static analysis fails because it sees a possible expression rather than a straight assignment to module.exports.Propaedeutic
Remember that exports in ESM modules cannot be computed, they must be statically declared so for a CommonJS module to be compatible, it seems that the exports must be findable with static analysis of the code. Exports that appear to be computed would not pass this test. And, putting parens around it makes it an expression that will apparently fail the static analysis.Propaedeutic
I
8

From the docs:

For better compatibility with existing usage in the JS ecosystem, Node.js in addition [to the default import] attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

[…]

The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.

Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See cjs-module-lexer for the exact semantics implemented.

Immanuel answered 30/12, 2020 at 17:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.