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.
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'
Publish as esm. Add a special instruction to package.json
{
"type": "module"
}
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')
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"
}
}
NOTE .cjs/.mjs
file extension has matter.
This approach requires that your code to be refactored in some way:
- Rewrite by hand with
require
andimport
syntax. - Recompile (TypeScript) separately with
CommonJS
andES6
module
options. - Wrap one version inside the other.
- es6 in es5 (std/esm) as mentioned above
- reexport es5 from es6
import cjsModule from './index.cjs'
export const name = cjsModule.name
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
- EcmaScript-CommonJS Module Interop: The Missing Manual
- Node Modules at War: Why CommonJS and ES Modules Can’t Get Along
- Get Ready For ESM
- Pure ESM package
- Migrating from CommonJS to ESM
- esm/issues/868
- cjs to esm
- https://stackoverflow.com/questions/57169793/error-err-require-esm-how-to-use-es6-modules-in-node-12
- https://blog.logrocket.com/publishing-node-modules-typescript-es-modules/
- https://blog.logrocket.com/es-modules-in-node-today/
Top comments (1)
"allowSyntheticDefaultImports": true, this works as well in tsconfig.json