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