The package that linked you here is now pure ESM. It cannot be require()
'd from CommonJS.
This means you have the following choices:
- Use ESM yourself. (preferred)
Useimport foo from 'foo'
instead ofconst foo = require('foo')
to import the package. You also need to put"type": "module"
in your package.json and more. Follow the below guide. - If the package is used in an async context, you could use
await import(…)
from CommonJS instead ofrequire(…)
. - Stay on the existing version of the package until you can move to ESM.
You also need to make sure you're on the latest minor version of Node.js. At minimum Node.js 16.
I would strongly recommend moving to ESM. ESM can still import CommonJS packages, but CommonJS packages cannot import ESM packages synchronously.
My repos are not the place to ask ESM/TypeScript/Webpack/Jest/ts-node/CRA support questions.
- Add
"type": "module"
to your package.json. - Replace
"main": "index.js"
with"exports": "./index.js"
in your package.json. - Update the
"engines"
field in package.json to Node.js 16:"node": ">=16"
. - Remove
'use strict';
from all JavaScript files. - Replace all
require()
/module.export
withimport
/export
. - Use only full relative file paths for imports:
import x from '.';
→import x from './index.js';
. - If you have a TypeScript type definition (for example,
index.d.ts
), update it to use ESM imports/exports. - Use the
node:
protocol for Node.js built-in imports.
Sidenote: If you're looking for guidance on how to add types to your JavaScript package, check out my guide.
Yes, but you need to convert your project to output ESM. See below.
Quick steps:
- Make sure you are using TypeScript 4.7 or later.
- Add
"type": "module"
to your package.json. - Replace
"main": "index.js"
with"exports": "./index.js"
in your package.json. - Update the
"engines"
field in package.json to Node.js 16:"node": ">=16"
. - Add
"module": "node16", "moduleResolution": "node16"
to your tsconfig.json. (Example) - Use only full relative file paths for imports:
import x from '.';
→import x from './index.js';
. - Remove
namespace
usage and useexport
instead. - Use the
node:
protocol for Node.js built-in imports. - You must use a
.js
extension in relative imports even though you're importing.ts
files.
If you use ts-node
, follow this guide. Example config.
Electron supports ESM as of Electron 28 (not out yet as of this writing). Please read this.
The problem is either Webpack or your Webpack configuration. First, ensure you are on the latest version of Webpack. Please don't open an issue on my repo. Try asking on Stack Overflow or open an issue the Webpack repo.
Upgrade to Next.js 12 which has full ESM support.
If you have decided to make your project ESM ("type": "module"
in your package.json), make sure you have "module": "node16"
in your tsconfig.json and that all your import statements to local files use the .js
extension, not .ts
or no extension.
Follow this guide and ensure you are on the latest version of ts-node
.
Create React App doesn't yet fully support ESM. I would recommend opening an issue on their repo with the problem you have encountered. One known issue is #10933.
Follow this guide.
We got you covered with this ESLint rule. You should also use this rule.
import {fileURLToPath} from 'node:url';
import path from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
However, in most cases, this is better:
import {fileURLToPath} from 'node:url';
const foo = fileURLToPath(new URL('foo.js', import.meta.url));
And many Node.js APIs accept URL directly, so you can just do this:
const foo = new URL('foo.js', import.meta.url);
There's no good way to do this yet. Not until we get ESM loader hooks. For now, this snippet can be useful:
const importFresh = async modulePath => import(`${modulePath}?x=${new Date()}`);
const chalk = (await importFresh('chalk')).default;
Note: This will cause memory leaks, so only use it for testing, not in production. Also, it will only reload the imported module, not its dependencies.
JavaScript Modules will eventually get native support for JSON, but for now, you can do this:
import fs from 'node:fs/promises';
const packageJson = JSON.parse(await fs.readFile('package.json'));
My general rule is that if something exports a single main thing, it should be a default export.
Keep in mind that you can combine a default export with named exports when it makes sense:
import readJson, {JSONError} from 'read-json';
Here, we had exported the main thing readJson
, but we also exported an error as a named export.
If your package has both an asynchronous and synchronous main API, I would recommend using named exports:
import {readJson, readJsonSync} from 'read-json';
This makes it clear to the reader that the package exports multiple main APIs. We also follow the Node.js convention of suffixing the synchronous API with Sync
.
I have noticed a bad pattern of packages using overly generic names for named exports:
import {parse} from 'parse-json';
This forces the consumer to either accept the ambiguous name (which might cause naming conflicts) or rename it:
import {parse as parseJson} from 'parse-json';
Instead, make it easy for the user:
import {parseJson} from 'parse-json';
With ESM, I now prefer descriptive named exports more often than a namespace default export:
CommonJS (before):
const isStream = require('is-stream');
isStream.writable(…);
ESM (now):
import {isWritableStream} from 'is-stream';
isWritableStream(…);
Betting on CJS is like betting on Adobe Flash a few years ago. At the time people couldn't imagine Flash would be abandoned by every website and wouldn't even be installable in browsers. Flash devs made a lot of salty arguments, some similar to comments made here. In the end, proper web standards that are supported by browser runtimes win every time.
It most certainly is, in many common senses. Sure, the Node.js team have not declared the format deprecated by the runtime (yet). Deprecating the format isn't solely in the hands of the Node.js team though, individual package authors can also deprecate support for CJS consumers one a per-package basis when they republish as pure ESM. CJS "deprecation" is also decided by the entire JS community, which includes runtimes other than Node.js. CJS is garbage in the eyes of the Deno community, and even if they get Node.js CJS compatibility layers working it will still be considered a second-class format to standard ESM.
With the huge caveat discussed here; the "dual" packages must avoid the dual package hazard. Barely any of the "dual" packages today successfully do so. Most people have a naive impression that dual packages ship all the code as both ESM and CJS, which is probably worse all things considered that just picking pure CJS or ESM.
Hard disagree.
Most developers are so dependant on build tooling they don't actually know how to write a CJS module; millions of developers have their source as ESM. To them, CJS is an implementation detail of Babel, Next.js and Node.js. Most of the installs on npm flow from a few popular libraries/frameworks that suck in an ungodly amount of dependencies on install. If one or two switch to pure ESM and update their dependencies to newer pure ESM versions, overnight millions of CJS installations evaporate.
There is a network effect where once packages start flipping to pure ESM, CJS packages that consume it also have to flip. The only reason this hasn't spread like wildfire already is because a few dev tools (namely TypeScript, Next.js, and Jest) have refused or delayed Node.js ESM support, and once they support ESM properly we'll be unblocked from a mad scramble to ESM over the next year or so. In 2 years, we might have an ecosystem of maintained packages that are almost entirely pure ESM.
It's frustrating to see people defend lazy billion dollar corporations (Microsoft, Vercel ($2.5 billion in funding), Facebook) who throw their weight around by either spreading FUD or inaction, instead of supporting the passionate open source package authors that want to publish, promulgate, and use the best technology possible. Once these corporations have been forced to take standard ESM seriously, the fixes don't take that long in the scheme of things to work out. They could have gone to the same effort years earlier and saved the ecosystem so much grief. It's like getting mega corporations off fossil fuels; imagine if everyone waited for the worlds richest corporations to change their policy first before everyone else follows and invests in and uses renewables. Progress would never happen. People need to stop putting big brand names on a pedestal; their policies are not always the best either for the world or for you. Sometimes you have to publicly challenge the narrative, lead by example, and inspire positive change. In this regard, @sindresorhus has the respect of many of us.
TypeScript have mostly figured out ESM/
.mjs
/.cjs
compat but got cold feet and dropped most of it for the TS v4.5 release; they plan to publish the fixes in v4.6 which is scheduled for February 22, 2022. It's outrageous that Microsoft isn't treating blatant Node.js incompatibilities as emergencies to be patched sooner, but being Christmas the next 2 months will zoom by.Next.js and their inability to process pure ESM dependencies was the reason I didn't migrate a few of my own Next.js / front end related packages to pure ESM. They still haven't fixed bugs relating to ESM project modules (e.g. vercel/next.js#17806), but they have fixed support for pure ESM dependencies which was the main problem. Accordingly, I've gone ahead the past few weeks and converted several more packages to pure ESM. In time, others will do the same too.
I've got zero sympathy for people that were relying on Jest's ability to transpile source on the fly and screw around with concerns of the runtime by hacking the imports, instead of testing the actual distribution code properly via tests that are regular JS modules that don't need a special engine other than Node.js to run. Regardless, one way or another Jest will resolve all the Node.js ESM compat issues as it's an existential issue for the viability for the tool, or people can rewrite their tests.
The ESM flippening has already begun, but in a few months there will be no brakes on the train.