remix: Unable to import from libs using ESM
The following error is thrown when trying to import from libs using ESM (like unist-util-visit):
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /my-app/node_modules/unist-util-visit/index.js
require() of ES modules is not supported.
require() of /my-app/node_modules/unist-util-visit/index.js from /my-app/build/index.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 /my-app/node_modules/unist-util-visit/index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /my-app/node_modules/unist-util-visit/package.json.
Code which caused the error to be thrown:
// mdx.server.ts
import { visit } from "unist-util-visit"
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 62
- Comments: 46 (17 by maintainers)
Looks like it’s merged: https://github.com/remix-run/remix/pull/239#pullrequestreview-726906749
My working setup
it’s not pretty, but it works
Just to add a +1. I’m having the same issue as @edmundhung with react-markdown
Is there anything we can do as a community to help Remix have support for ESM modules? From a framework like this that advocates for using standards, the expectation from the community is for it to support ESM modules without any workarounds. We should be able to just do:
And it should just work™. Maybe a first good step would be to make remix itself an ESM module (
"type": "module") and if folks actually want to use CommonJS, the can simply change the extension of their files to.cjs.@Ferdzzzzzzzz I have ReScript working with Remix here: https://github.com/tom-sherman/remix-rescript-example but the solution was not pretty…
It required a patch to Remix’s esbuild config to exclude rescript from a specific transpilation step: https://github.com/tom-sherman/remix-rescript-example/blob/7625c5562b0bbe7a5cd435693ac2bb69c58b2b66/patches/@remix-run+dev+1.1.1.patch
After applying this patch, you can set
transpileModules: ["rescript"](or, in fact, any ES module you have in node_modules) in the remix config and everything should work…This is essentially a port of next-transpile-modules but for Remix. Judging by the number of downloads of that package (and my anecdotal experience) it’s a very common requirement.
@kentcdodds What are your thoughts on supporting something like a
transpileModulesoption?@kentcdodds That error cause by the dynamic import will be fixed soon, I have a PR out that will allow for dynamic imports to be processed correctly.
Also been giving this some thought in general and one thing we could do is add some sort of “allowList” to the config that would bundle those node modules with the server build, removing the concern of cjs vs esm the same as a browser build for those specific modules.
I created a workaround that uses @kentcdodds’s method https://github.com/abc3354/remix-esm-workaround
It works with a custom hook + a react context and does not need a lot of modification to the code ! It also runs react-flow 😄
I have the same problem with
react-markdown. Version downgrade didn’t work for me.But also other packages like
@apollo/clientare suggesting me to add it toserverDependenciesToBundleinremix.config.js. If i do so, more and more packages show up – additionally to the ones already added toserverDependenciesToBundle.@Girish21 thanks a lot for that
serverDependenciesToBundleoption. I was looking for something like that and couldn’t find anything! … I understand Node’s limitations with CJS/ESM (one of my personal packages is still in CJS because ESLint, Prettier and similar packages haven’t migrated yet to ESM), but as I mentioned in my previous comment, is kinda weird to have to update a config inremix.config.jsto make ESM work properly when the idea of Remix is to use standards. Don’t take this as a critique to Remix itself, but just as an observation of something that’s “unintuitive” about it. Hope the ecosystem keeps evolving so we don’t have to deal with this kind of things in the future.@edmundhung & @nialldbarber it worked for me after I downgraded react-markdown to v6.0.3
Just hit the same issue with react-markdown which is also developed by the same team.
I was thinking about trying the solution from Kent like this:
react-markdown.js/app/routes/index.tsxAnd I end up seeing
Error: ReactDOMServer does not yet support Suspense.which seems valid I think…🤔Don’t know about
react-markdownbut I gotmicromark(with GitHub Flavoured Markdown) to work by adding each of the packages there was an error for and ended up with this:And now it works like a charm both in the browser and on the server!
I also had the issue with
react-markdownand the workaround of @RazvanRauta worked for me (https://github.com/remix-run/remix/issues/109#issuecomment-996203134). But I wonder why the newer versions of the package won’t, even when adding it toremix.config.js(serverDependenciesToBundle: ["react-markdown"])? See https://remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.Circling back around. I’m pretty sure this is a workaround that’ll work for most folks (kinda depends on how you deploy and stuff). Basically it’s important to know that:
So the solution is to create a CJS module that does the dynamic import and is not compiled by remix.
One approach to this would be to create a CJS module (so it can be required by the compiled remix code) at the root of your repo (so it’s not compiled by remix code) and put all the dynamic imports in there. Then your remix code can reference that module instead.
So, for example:
I haven’t tested that exactly, but I’m 99% that it’ll work (perhaps with some small modifications). Note that
mdx-bundlerusesxdmwhich is a native ESM module package via dynamic imports (checkout line 66 here: https://unpkg.com/browse/mdx-bundler@5.2.1/dist/index.js). The reason this works with remix is because remix’s server build tells esbuild to skip compiling files innode_modulesso the dynamic import remains in place (note, this only works with the server, on the client everything is compiled).If remix’s compiler was configured to leave dynamic imports alone, then it would be much simpler. No middle-man
esm-modules.jsfile would be needed and we could use the dynamic import directly:If remix’s compiler was configured to leave all imports alone (not sure what this would take), then we could set
"type": "module"in a remix app’spackage.json, then it would be even easier:For now, we’re stuck with the
esm-modules.jsfile until Remix’s compiler supports either ignoring dynamic imports or supports ignoring all imports (perhaps it could do this automatically if we set"type": "module"in the rootpackage.json?).Hope that helps!
My work around for now is to downgrade jsdom to 19.0.0 and @types/jsdom to 16.2.15 in my package.json, before they upgraded to parse5 v7, and remove node_modules and package-lock.json before running
npm iagain, to ensure that parse5 v6 is at the root of my node_modules so that the “serverDependenciesToBundle”-edrehype-parsemodule can find it.This seemed to work. However, a bit later on I realized I needed to install
remark-gfm. By the way, a cool trick I’ve found is to runnpm run buildafter adding a dependency, and it’ll list packages to add to “serverDependenciesToBundle”. After you add those packages and you runnpm run buildagain, it’ll detect additional packages (basically doing a breadth first search, one layer at a time). This is faster (O(log n) vs O(n)?) than potentially seeing each package fail individually.But now I have a new problem, that “serverDependenciesToBundle” seems to ignore “mdast-util-to-markdown”. I put it in
remix.config.js:But even after
rm -rf build node_modules package-lock.json && npm i, I still get the following error onnpm run build:And the following on
npm run dev:Update: Bundling all packages using a blacklist instead of a whitelist fixed my issue.
@Chensokheng All I did was to stick to an older version of react-markdown (6.0.3) as suggested by another commenter (https://github.com/remix-run/remix/issues/109#issuecomment-996203134)
@codejet
Do you have the code that work with react-markdown? I can’t make it works
@lukeshiru Remix already exports ESM modules for the browser bundle. The problem is with the server bundle. Still, Node’s ESM vs CJS situation is a bit iffy, and some of the dependencies still don’t support native ESM yet. Remix has a workaround, though; you can list the ESM only packages to be compiled to CJS during the build for the server bundle using serverdependenciestobundle. You can also take a look at importing ESM packages
+1 I have the same issue with React-Flow. I’ve tried multiple workarounds but unsuccessful so far…
https://github.com/wbkd/react-flow/issues/1953
Finally got things working with Netlify deploy. See netlify/zip-it-and-ship-it#869 for the solution.
One thing to note is that I did not need to use the
esm-modules.tsxtrick (neither for Remix Server nor for Netlify), just usedawait import(...)inside of anasyncfunction for all the ESM modules I needed to import.Hitting this same issue … our internal UI library is ESM only, so no option to use the CJS version. Is there any kind of option available at the moment to allow certain dependencies in
node_modulesto be transpiled to CJS?