DEV Community



Anton Golub
System Architect at QIWI
Updated on ・3 min read

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 =
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

Discussion (0)