DEV Community

Anton Golub
Anton Golub

Posted on • Edited on

ERR_REQUIRE_ESM

To save anybody's googling time

Once you update you package deps you may face with the error mentioned in this note title:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ~/projects/semantic-release-toolkit/node_modules/zz/cjs.js require() of ES modules is not supported. require() of ~/projects/semantic-release-toolkit/node_modules/zz/cjs.js from ~/projects/semantic-release-toolkit/tt.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename cjs.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ~/projects/semantic-release-toolkit/node_modules/zz/package.json. 
Enter fullscreen mode Exit fullscreen mode

Even though your code stopped working, this issue has positive context: the future is coming and some of your project's deps has been shipped as ES module.

What's wrong?

The short answer — es module brings top-level await, but require has no idea how to deal with it. Detailed explanation — announcing a new --experimental-modules.

How to fix?

There're several strategies:

1. Follow the trend

Migrate to ES modules. Replace require with import.

// before const { sync } = require('read-pkg-up') // after import { readPackageUpSync } from 'read-pkg-up' 
Enter fullscreen mode Exit fullscreen mode

Publish as esm. Add a special instruction to package.json

{ "type": "module" } 
Enter fullscreen mode Exit fullscreen mode

2. Stay on ES5

Wrap ES6 module(s) with esm adapter. Just like the documentation says:

// read-pkg-up-wrapper.js require = require('esm')(module, { mode: 'all', cjs: { ... // }, force: false, cache: false, await: false }) module.exports = require('read-pkg-up') 
Enter fullscreen mode Exit fullscreen mode

NOTE It's advisable to use patched esm that properly handles legacy (.js) extensions. See esm/issues/868 for details.

3. Take both.

Provide conditional export.
You're able to split your code into esm and cjs versions.

// package.json { ... "exports": { "import": "./es6entry.mjs", "require": "./es5entry.cjs" } } 
Enter fullscreen mode Exit fullscreen mode

NOTE .cjs/.mjs file extension has matter.

This approach requires that your code to be refactored in some way:

  • Rewrite by hand with require and import syntax.
  • Recompile (TypeScript) separately with CommonJS and ES6 module options.
  • Wrap one version inside the other.
import cjsModule from './index.cjs' export const name = cjsModule.name 
Enter fullscreen mode Exit fullscreen mode

Conditional export is especially important for a situation when you're developing a package that needs ES6 deps, but its called by an external utility that uses legacy ES5 loading. For example, some-semrel-plugin depends from read-pkg-up@8 (es6), and is being invoked through require directive in semantic-release.

Related refs

Top comments (1)

Collapse
 
justinechternach profile image
Justin Echternach

"allowSyntheticDefaultImports": true, this works as well in tsconfig.json