3

First off for a year+ I've written all my code using esm and with the std/esm package I am able to seamlessly use dependent packages with cjs no issues without the need of babel.

In nodejs V14 mixed esm/cjs support is now included without a flag and the std/esm project seems to be winding down so I thought I should attempt to migrate to see what the issues may be. Well I found one.

https://github.com/standard-things/esm

https://nodejs.org/api/esm.html#esm_ecmascript_modules

The issue I am having is that unlike std/esm the esm/cjs support now included with V14 nodejs is breaking the named imports (from I assume commonjs module exports) that worked fine with std/esm.

Take for example https://github.com/sindresorhus/make-dir/blob/978bee9186bf0c41640ed21567921daf8c303225/index.js#L106

as packages is uses cjs. Here is the export

module.exports.sync = (input, options) => {
    checkPath(input);
    options = processOptions(options);

Using "type":"module" in my package.json. I have in my code the import import { sync as mkdir } from 'make-dir' which works fine using std/esm. But using in nodejs 14 it says it can't find the named export sync.**

import { sync as mkdir } from 'make-dir'
         ^^^^
SyntaxError: The requested module 'make-dir' does not provide an export named 'sync'

Am I stuck here? Do I need to stay with std/esm? (but it looks like the project is over now) I can't/shouldn't go through my entire code base accommodating cjs modules with

import mk from 'make-dir'
const mkdir = mk.sync

To make this easy for anyone to recreate I have made a repo one can clone and run to see this difference

https://github.com/dkebler/core-esm-named-import-error

Anyway was under the assumption that esm in v14 was going to be a drop in replacement for using std/esm. Apparently not :(.

9
  • CommonJS modules do not statically name their exports in the same way that ESM modules do and, as such, there are some extra steps required when importing CJS into an ESM module. Right now, the interoperability of CommonJS with ESM modules contains some difficulties. The challenge stems from the fact that ESM modules require exports to be statically defined (can be determined by only running the parser, not actually running the code) whereas CommonJS modules don't know their exports until the module has been run and module.exports has been assigned. Commented May 29, 2020 at 15:21
  • ESM modules are NOT a drop-in replacement for CommonJS modules. They work differently. Please don't use the word "native" when referring to either one of these types of modules as it's completely unclear what that means and it's not a standard term with a standard meaning. There are ECMAScript modules (often abbreviated as ESM - the new type of module built into JS) and there are CommonJS modules (the original node.js module type). Commented May 29, 2020 at 15:23
  • the question here is the differences between the std/esm package and the way esm support within nodejs 14 handles the same. I don't need help on the differences between esm and commonjs. By "native" I mean the now included esm handler in nodejs (still experimental but now does not need a flag). Please read see the link I gave which talks about methods of invocation such as "type":"module". I am trying to get John Dalton or others in the know to explain why with their package things are fine but with the now "included/native" esm in nodejs14 the same approach fails. Commented May 30, 2020 at 15:21
  • I am looking for some help from someone who has experience using std/esm and has attempted to use the esm support in V14. Commented May 30, 2020 at 15:45
  • What is "std/esm package" that you refer to in your comment? Again, your terminology is not clear. Please refer to "ESM support in the browser" or "ESM support in node.js" or if you're referring to something else, then use a more descriptive phrase so we can know what you are talking about. Commented May 30, 2020 at 15:53

1 Answer 1

4

EDIT: As of Node.js 14.13.0 (and Node.js 12.19.0 LTS), importing a CJS module parses the file using https://github.com/guybedford/cjs-module-lexer to detect most common named exports, so you can use those from ESM. The original answer is out-of-date, but is still relevant to understand why Node.js doesn't detect some CJS named exports.

The technical reason Node.js only supports default exports for CJS scripts is because ESM import is static (it is parsed prior to module execution) and CJS module.exports is dynamic (you can do something crazy like module.exports[Date.now()] = 0 and get away with it). The only way be know for sure what "names" a CJS module exports is to parse and execute it, that seems to be incompatible with ES6 specs.

Some bundlers (such as std/esm) use a regex on the CJS modules code to find instances of module.exports.<named export> =, and use that to define the named export list for the module. The downside of this method is that you won't find all the exports, and the parsing of the large files can be very memory intensive. Also that means parsing CJS modules twice, and while the performance hit might be OK for bundlers, for a runtime such as Node.js it is not worth it.

So what can be done? Here's what I have been doing when I encounter this issue:

Sign up to request clarification or add additional context in comments.

1 Comment

Another option is to try out Node 14.13.0+; it apparently adds (partial) support for named-imports from commonjs packages: simonplend.com/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.