-
-
Notifications
You must be signed in to change notification settings - Fork 860
Description
I am trying to use immer in the backend of a Node.js application. I am currently in the process of migrating my backend to ESM modules, since some dependencies (in particular node-fetch) are starting to ship only ESM modules.
Error description
When I try to import immer in an mjs module using import produce from 'immer';
, produce
will be an object instead of a function, with its default
property being the produce function.
I can access the produce function by using import { produce } from 'immer';
or by using import immer from 'immer';
and then using immer.produce()
. The problem is that the documentation uses import produce from 'immer';
, so there are libraries using this whose code I am not in control of. This means that these libraries break when I migrate to ESM modules.
Reason for the error
immer is bundled in several different formats, among them cjs
and esm
. The bundles are referenced in package.json
in the following way (index.js
being a wrapper that includes the cjs
bundle):
"main": "dist/index.js",
"module": "dist/immer.esm.js",
While the module
property is probably supported by webpack and other bundlers, it does not seem to be supported by Node.js. Instead, Node.js uses the exports
property to support different main files for different environments (see here. Since that is not defined in this case, Node.js requires the file from the main
property, which is a CommonJS bundle.
Even forcing Node.js to use the ESM bundle by doing import produce from 'immer/dist/immer.esm.js';
does not solve the problem. The problem is that Node.js interprets files as CommonJS unless they have a .jsm
file extension or "type": "module"
is defined in package.json
(which then applies to all files in the package) (see here).
Possible solution
Setting "type": "module"
is probably not an option, since that will break the CommonJS files.
The only solution that I can think of is to ship the ESM bundle as an .mjs
file, either by renaming the current one or by creating a copy. The file can then be referenced in package.json
like this:
"exports": {
"import": "./dist/immer.esm.mjs",
"require": "./dist/index.js"
},
Workaround
If you are using immer yourself, use import { produce } from 'immer';
rather than import produce from 'immer';
.
I have not found a workaround for cases where dependencies whose code you don't control are using immer. A last resort is probably yarn patch.