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