Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to import from libs using ESM #109

Closed
gmencz opened this issue May 3, 2021 · 46 comments
Closed

Unable to import from libs using ESM #109

gmencz opened this issue May 3, 2021 · 46 comments

Comments

@gmencz
Copy link

gmencz commented May 3, 2021

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"
@kentcdodds
Copy link
Member

There are ways to workaround this. I'll try to remember to come give an example later. But I wanted to say that this will become increasingly problematic over time. All that's needed is for remix to configure esbuild to not touch dynamic imports of node_modules. That's it. Then we could dynamically import all native ESM modules without trouble.

@kentcdodds
Copy link
Member

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:

  1. The only way for CJS modules to import ESM modules is via dynamic imports
  2. Currently remix compiles all dynamic imports to CJS requires and that will fail if the module being required is ESM.

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:

// esm-modules.js

module.exports = {
  getUnistUtilVisit: async () => import('unist-util-visit'),
  // other modules would be imported here.
}
// app/utils/compile-mdx.server.ts

import {getUnistUtilVisit} from '../../esm-modules'

async function compileMdx() {
  const {default: visit} = await getUnistUtilVisit()
  // ... etc.
}

I haven't tested that exactly, but I'm 99% that it'll work (perhaps with some small modifications). Note that mdx-bundler uses xdm which 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 in node_modules so 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.js file would be needed and we could use the dynamic import directly:

// app/utils/compile-mdx.server.ts

async function compileMdx() {
  const {default: visit} = await import('unist-util-visit')
  // ... etc.
}

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's package.json, then it would be even easier:

// app/utils/compile-mdx.server.ts
import {visit} from 'unist-util-visit'

async function compileMdx() {
  // ... etc.
}

For now, we're stuck with the esm-modules.js file 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 root package.json?).

Hope that helps!

@kentcdodds
Copy link
Member

I just tried my workaround and it didn't work :( I got this error:

The following error is a bug in Remix, please file an issue! https://remix.run/dashboard/support
Error: Cannot get route for entry point node_modules/p-props/index.js
    at Object.invariant [as default] (/Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/invariant.js:9:11)
    at Object.createAssetsManifest (/Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/compiler/assets.js:42:27)
    at async generateManifests (/Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/compiler.js:253:24)
    at async /Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/compiler.js:153:5
    at async Promise.all (index 0)
    at async Object.watch (/Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/compiler.js:71:37)
    at async Object.watch (/Users/kentcdodds/code/remix-kentcdodds/node_modules/@remix-run/dev/cli/commands.js:56:34)

@jacob-ebey
Copy link
Member

jacob-ebey commented Aug 9, 2021

@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.

@kentcdodds
Copy link
Member

Love it! Thank you for working on this @jacob-ebey 👏

@edmundhung
Copy link
Contributor

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

module.exports = {
  ReactMarkdown: () => import('react-markdown'),
};

/app/routes/index.tsx

import { ReactMarkdown as ReactMarkdownComponent } from '../../react-markdown.js';

const ReactMarkdown = React.lazy(ReactMarkdownComponent);

export default function Index() {
  let data = useRouteData();

  return (
      <ReactMarkdown>{'# test'}</ReactMarkdown>
  );
}

And I end up seeing Error: ReactDOMServer does not yet support Suspense. which seems valid I think...🤔

@Ferdzzzzzzzz
Copy link

Hey, getting an error that looks kind of similar for some ReScript libraries (my knowledge on modules vs commonjs etc is somewhere between very patchy and nonexistent). Has this problem been addressed, and can anyone tell me if I'm facing the same problem as this issue? :)

Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/rescript/lib/es6/caml_exceptions.js from .../build/index.js not supported.
Instead change the require of caml_exceptions.js in .../build/index.js to a dynamic import() which is available in all CommonJS modules.

Changing the Rescript build from es6 to commonjs removes the server error but gives a browser error:

Uncaught Error: Dynamic require of "fs" is not supported

(amongst others)

Thanks in advance! 😄

@Girish21
Copy link
Contributor

@Ferdzzzzzzzz try to move the ReScript module to a .server.{ts|js} file so that the module will not be included in client bundle

export async function someFunction() {
  const {default} = await import(/* some_module */)

  /**
  * rest of the function
  */
}

@leepowelldev
Copy link

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_modules to be transpiled to CJS?

@mdingena
Copy link

mdingena commented Dec 2, 2021

our internal UI library is ESM only

We have the same issue with our Google Analytics integration and UI libraries.

@redrockhorse
Copy link

@jacob-ebey Has this work been completed?

@nialldbarber
Copy link

Just to add a +1. I'm having the same issue as @edmundhung with react-markdown

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/niall.barber/Git/projects/react/ndb-remix/node_modules/react-markdown/index.js
require() of ES modules is not supported.
require() of /Users/niall.barber/Git/projects/react/ndb-remix/node_modules/react-markdown/index.js from /Users/niall.barber/Git/projects/react/ndb-remix/api/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 /Users/niall.barber/Git/projects/react/ndb-remix/node_modules/react-markdown/index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/niall.barber/Git/projects/react/ndb-remix/node_modules/react-markdown/package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at Object.<anonymous> (/Users/niall.barber/Git/projects/react/ndb-remix/api/build/index.js:185:40)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at /Users/niall.barber/Git/projects/react/ndb-remix/node_modules/@remix-run/serve/index.js:39:17
    at Layer.handle [as handle_request] (/Users/niall.barber/Git/projects/react/ndb-remix/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/niall.barber/Git/projects/react/ndb-remix/node_modules/express/lib/router/route.js:137:13)
    at next (/Users/niall.barber/Git/projects/react/ndb-remix/node_modules/express/lib/router/route.js:131:14)

@brumm
Copy link

brumm commented Dec 7, 2021

Looks like it's merged: #239 (review)

My working setup

// esm-modules.js
module.exports = {
  importUnified: async () => import('unified'),
  importParseMarkdown: async () => import('remark-parse'),
  importGfm: async () => import('remark-gfm'),
  importEmoji: async () => import('remark-emoji'),
  importSlug: async () => import('remark-slug'),
  importGithub: async () => import('remark-github'),
  importRemarkToRehype: async () => import('remark-rehype'),
  importRehypeStringify: async () => import('rehype-stringify'),
}
import {
  importUnified,
  importParseMarkdown,
  importGfm,
  importEmoji,
  importSlug,
  importGithub,
  importRemarkToRehype,
  importRehypeStringify,
} from './esm-modules'

// in some async context
const { unified } = await importUnified()
const { default: parseMarkdown } = await importParseMarkdown()
const { default: gfm } = await importGfm()
const { default: emoji } = await importEmoji()
const { default: slug } = await importSlug()
const { default: github } = await importGithub()
const { default: remarkToRehype } = await importRemarkToRehype()
const { default: rehypeStringify } = await importRehypeStringify()

it's not pretty, but it works

@pcattori
Copy link
Contributor

pcattori commented Dec 9, 2021

I use const { unified } = await import('unified') (and similar for the other ESM modules) and that has worked for me locally. @brumm : I didn't need to created a separate esm-modules.js like you have above. (I see that you are just following the advice from Kent)

But when I switched my deployment to Netlify I see my ErrorBoundary getting rendered and a console error: Error: Cannot find package 'unified' imported from /var/task/netlify/functions/server/build/index.js.

Some googling pointed me to the Netlify docs on [functions], specifically setting:

[functions]
  node_bundler = "esbuild"
  external_node_modules = ["unified"]

(I actually saw that the Remix Netlify template used to ship with something similar...)

With that^ it looks like now Netlify does find unified, but unfortunately its still an ESM module being import()ed from CJS, so I get Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: like others above. But this shouldn't be a problem, right? Since we are import()ing and not requireing? Not really sure why this works locally but not on Netlify 🤷 . (Note: adding that ^block to netlify.toml breaks things the same way locally as on Netlify)

Looks like the Unified ecosystem is firmly in the "only publish ESM" camp. The options suggested in that thread seem to rely on access to the compiler, which I know Remix doesn't want to open up too soon (which I agree with).

Would love to see a tutorial where unified (or any other ESM-only package) is used within a Remix app and the app is successfully deployed to Netlify!

@pcattori
Copy link
Contributor

pcattori commented Dec 9, 2021

Probably unrelated, but I should mention that with the netlify.toml above, I get a warning:

6:00:35 PM: ❯ The following Node.js modules use dynamic expressions to include files:
6:00:35 PM:    - @remix-run/react
6:00:35 PM: ​
6:00:35 PM:   Because files included with dynamic expressions aren't bundled with your serverless functions by default,
  this may result in an error when invoking a function. To resolve this error, you can mark these Node.js
6:00:35 PM:   modules as external in the [functions] section of your `netlify.toml` configuration file:
6:00:35 PM: ​
6:00:35 PM:   [functions]
6:00:35 PM:     external_node_modules = ["@remix-run/react"]

But adding @remix-run/react to external_node_modules results in a successful deploy, but a site that just displays:

Unexpected Server Error

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

@pcattori
Copy link
Contributor

pcattori commented Dec 10, 2021

Did some more digging. Getting mostly consistent results locally and in Netlify. When I try to hit a route that uses my unified processor in the loader:

  • Without node_bundler = "esbuild"

    • Locally: everything works!
    • Netlify deploy: Error: Cannot find package 'unified' imported from /var/task/netlify/functions/server/build/index.js
  • With node_bundler = "esbuild":

    • Locally & Netlify Deploy: Error: ENOENT: no such file or directory, open '<path to my project>/.netlify/functions-serve/server/src/netlify/functions/languages/abap.tmLanguage.json.
  • With node_bundler = "esbuild" and external_node_modules = ["unified"]

    • Locally & Netlify Deploy: Error: Must use import to load ES Module: <path to my project>/.netlify/functions-serve/server/src/node_modules/unified/index.js

I then looked at <path to my project>/.netlify/functions-serve/server/src/node_modules/unified/index.js and found that some imports of unified had been transpiled into requires:

  • import_unified = __toModule(require("unified")); transpiled from import { unified } from 'unified' within node_modules/@leafac/rehype-shiki/distribution/index.js)
  • import_unified2 = __toModule(require("unified")); transpiled from import { unified } from 'unified' within node_modules/rehype-katex/index.js
  • const { unified: unified3 } = await Promise.resolve().then(() => __toModule(require("unified"))); transpiled from const { unified } = await import('unified') from my code

The .netlify/ directory seems to only be generated when esbuild is enabled, which would explain why locally everything work without esbuild. But seems that Netlify recommends using esbuild with dynamic imports..., otherwise dynamic imports could be missing in Netlify.


Maybe the thing to try next is to use included_files (instead of external_node_modules) as it does not require node_bundler = "esbuild"?

@RazvanRauta
Copy link

@edmundhung & @nialldbarber it worked for me after I downgraded react-markdown to v6.0.3

@tom-sherman
Copy link

tom-sherman commented Dec 20, 2021

@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 transpileModules option?

@Ferdzzzzzzzz
Copy link

@tom-sherman appreciate it! Will give it a go to see just how ugly it is 😅

@pcattori
Copy link
Contributor

pcattori commented Dec 21, 2021

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.tsx trick (neither for Remix Server nor for Netlify), just used await import(...) inside of an async function for all the ESM modules I needed to import.

@ShafSpecs
Copy link

@pcattori How were you able to workaround the dynamic imports error?

@ShafSpecs
Copy link

Looks like it's merged: #239 (review)

My working setup

// esm-modules.js
module.exports = {
  importUnified: async () => import('unified'),
  importParseMarkdown: async () => import('remark-parse'),
  importGfm: async () => import('remark-gfm'),
  importEmoji: async () => import('remark-emoji'),
  importSlug: async () => import('remark-slug'),
  importGithub: async () => import('remark-github'),
  importRemarkToRehype: async () => import('remark-rehype'),
  importRehypeStringify: async () => import('rehype-stringify'),
}
import {
  importUnified,
  importParseMarkdown,
  importGfm,
  importEmoji,
  importSlug,
  importGithub,
  importRemarkToRehype,
  importRehypeStringify,
} from './esm-modules'

// in some async context
const { unified } = await importUnified()
const { default: parseMarkdown } = await importParseMarkdown()
const { default: gfm } = await importGfm()
const { default: emoji } = await importEmoji()
const { default: slug } = await importSlug()
const { default: github } = await importGithub()
const { default: remarkToRehype } = await importRemarkToRehype()
const { default: rehypeStringify } = await importRehypeStringify()

it's not pretty, but it works

I get an error that it can't get the remark modules after importing them

@pcattori
Copy link
Contributor

pcattori commented Feb 1, 2022

@ShafSpecs I reproduced my issues/solution here: https://github.com/pcattori/remix-netlify-dynamic-imports . Specifically, I got everything working in my setup in the shiki branch of that repo. YMMV if you use different unified plugins than I do, but should be a good reference nonetheless.

@codejet
Copy link

codejet commented Feb 21, 2022

I also had the issue with react-markdown and the workaround of @RazvanRauta worked for me (#109 (comment)). But I wonder why the newer versions of the package won't, even when adding it to remix.config.js (serverDependenciesToBundle: ["react-markdown"])? See https://remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.

@ikawka
Copy link

ikawka commented Feb 23, 2022

I also had the issue with react-markdown and the workaround of @RazvanRauta worked for me (#109 (comment)). But I wonder why the newer versions of the package won't, even when adding it to remix.config.js (serverDependenciesToBundle: ["react-markdown"])? See https://remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.

Having the same issue with remark-gfm

@hgeldenhuys
Copy link

hgeldenhuys commented Mar 27, 2022

+1 I have the same issue with React-Flow. I've tried multiple workarounds but unsuccessful so far..

xyflow/xyflow#1953

@arnopensource
Copy link

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 😄

@loucyx
Copy link
Contributor

loucyx commented Apr 15, 2022

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:

import { somthing } from "an-esm-module";

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.

@Girish21
Copy link
Contributor

Girish21 commented Apr 15, 2022

@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

@loucyx
Copy link
Contributor

loucyx commented Apr 15, 2022

@Girish21 thanks a lot for that serverDependenciesToBundle option. 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 in remix.config.js to 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.

@codejet
Copy link

codejet commented Apr 16, 2022

@Girish21 the thing is, that for some packages the serverDependenciesToBundle option also didn't seem fix the issue (see #109 (comment))

@Girish21
Copy link
Contributor

@codejet yup, Remix does not know the dependency graph of modules included in serverDependenciesToBundle. For now, we have to manually add it as shown in this example stackblitz

@codejet
Copy link

codejet commented Apr 16, 2022

@Girish21 thanks, but could you elaborate on that? what apart from adding the dep to serverDependenciesToBundle is actually happening there?

@chaance
Copy link
Collaborator

chaance commented Apr 20, 2022

Closing as we have guidance on dealing with ESM packages in our docs: https://remix.run/docs/en/v1/pages/gotchas#importing-esm-packages

@Chensokheng
Copy link

@codejet

I also had the issue with react-markdown and the workaround of @RazvanRauta worked for me (#109 (comment)). But I wonder why the newer versions of the package won't, even when adding it to remix.config.js (serverDependenciesToBundle: ["react-markdown"])? See https://remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.

Do you have the code that work with react-markdown? I can't make it works

@joakimbeng
Copy link

I also had the issue with react-markdown and the workaround of @RazvanRauta worked for me (#109 (comment)). But I wonder why the newer versions of the package won't, even when adding it to remix.config.js (serverDependenciesToBundle: ["react-markdown"])? See remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.

Do you have the code that work with react-markdown? I can't make it works

Don't know about react-markdown but I got micromark (with GitHub Flavoured Markdown) to work by adding each of the packages there was an error for and ended up with this:

{
  serverDependenciesToBundle: [
    "character-entities",
    "decode-named-character-reference",
    "micromark",
    "micromark-core-commonmark",
    "micromark-extension-frontmatter",
    "micromark-extension-gfm",
    "micromark-extension-gfm-autolink-literal",
    "micromark-extension-gfm-footnote",
    "micromark-extension-gfm-strikethrough",
    "micromark-extension-gfm-table",
    "micromark-extension-gfm-tagfilter",
    "micromark-extension-gfm-task-list-item",
    "micromark-extension-mdx-expression",
    "micromark-extension-mdx-jsx",
    "micromark-extension-mdx-md",
    "micromark-extension-mdxjs",
    "micromark-extension-mdxjs-esm",
    "micromark-factory-destination",
    "micromark-factory-label",
    "micromark-factory-mdx-expression",
    "micromark-factory-space",
    "micromark-factory-title",
    "micromark-factory-whitespace",
    "micromark-util-character",
    "micromark-util-chunked",
    "micromark-util-classify-character",
    "micromark-util-combine-extensions",
    "micromark-util-decode-numeric-character-reference",
    "micromark-util-decode-string",
    "micromark-util-encode",
    "micromark-util-events-to-acorn",
    "micromark-util-html-tag-name",
    "micromark-util-normalize-identifier",
    "micromark-util-resolve-all",
    "micromark-util-sanitize-uri",
    "micromark-util-subtokenize",
    "micromark-util-symbol",
    "micromark-util-types",
  ],
}

And now it works like a charm both in the browser and on the server!

@codejet
Copy link

codejet commented Oct 13, 2022

@Chensokheng All I did was to stick to an older version of react-markdown (6.0.3) as suggested by another commenter (#109 (comment))

@DPangerl
Copy link

I have the same problem with react-markdown. Version downgrade didn't work for me.

But also other packages like @apollo/client are suggesting me to add it to serverDependenciesToBundle in remix.config.js. If i do so, more and more packages show up – additionally to the ones already added to serverDependenciesToBundle.

@Fenwick17
Copy link

I have tried react-markdown 6.0.3 but the issue persists.
I have also tried adding it to serverDependenciesToBundle but still trows the same error Error [ERR_REQUIRE_ESM]: require() of ES Module
Not entirely sure how best to handle this?

@emipc
Copy link

emipc commented Jan 14, 2023

One of the libs I've added to serverDependenciesToBundle is using import.meta.url, making it to throw an error because it's undefined. This is preventing me to use this dependency just because of that.

@fourcolors
Copy link

Yeah I'm getting this too after adding it to serverDependenciesToBundle doesn't solve the issue

@fourcolors
Copy link

I also had the issue with react-markdown and the workaround of @RazvanRauta worked for me (#109 (comment)). But I wonder why the newer versions of the package won't, even when adding it to remix.config.js (serverDependenciesToBundle: ["react-markdown"])? See remix.run/docs/en/v1/pages/gotchas#importing-esm-packages.

Do you have the code that work with react-markdown? I can't make it works

Don't know about react-markdown but I got micromark (with GitHub Flavoured Markdown) to work by adding each of the packages there was an error for and ended up with this:

{
  serverDependenciesToBundle: [
    "character-entities",
    "decode-named-character-reference",
    "micromark",
    "micromark-core-commonmark",
    "micromark-extension-frontmatter",
    "micromark-extension-gfm",
    "micromark-extension-gfm-autolink-literal",
    "micromark-extension-gfm-footnote",
    "micromark-extension-gfm-strikethrough",
    "micromark-extension-gfm-table",
    "micromark-extension-gfm-tagfilter",
    "micromark-extension-gfm-task-list-item",
    "micromark-extension-mdx-expression",
    "micromark-extension-mdx-jsx",
    "micromark-extension-mdx-md",
    "micromark-extension-mdxjs",
    "micromark-extension-mdxjs-esm",
    "micromark-factory-destination",
    "micromark-factory-label",
    "micromark-factory-mdx-expression",
    "micromark-factory-space",
    "micromark-factory-title",
    "micromark-factory-whitespace",
    "micromark-util-character",
    "micromark-util-chunked",
    "micromark-util-classify-character",
    "micromark-util-combine-extensions",
    "micromark-util-decode-numeric-character-reference",
    "micromark-util-decode-string",
    "micromark-util-encode",
    "micromark-util-events-to-acorn",
    "micromark-util-html-tag-name",
    "micromark-util-normalize-identifier",
    "micromark-util-resolve-all",
    "micromark-util-sanitize-uri",
    "micromark-util-subtokenize",
    "micromark-util-symbol",
    "micromark-util-types",
  ],
}

And now it works like a charm both in the browser and on the server!

this worked for me. I need to add all reps to the list, not just the parent one. Bravo!

@ariofrio
Copy link

ariofrio commented Jun 8, 2023

I'm getting the following error when trying to use rehype-parse with the serverDependenciesToBundle workaround:

Error: Package subpath './lib/parser/index.js' is not defined by "exports" in .../node_modules/parse5/package.json
    at new NodeError (node:internal/errors:377:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:440:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:719:3)
    at resolveExports (node:internal/modules/cjs/loader:483:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:523:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:925:27)
    at Function.Module._load (node:internal/modules/cjs/loader:780:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (.../build/index.js:1623:29)

This parse5 issue suggests that it's a bundler or mismatched versions problem. In particular, rehype-parse depends on parse5": "^6.0.0" which has that file, but jsdom depends on "parse5": "^7.0.0". It seems like somehow serverDependenciesToBundle is making node forget to use the correct version of parse5 (which is in ./node_modules/rehype-parse/node_modules/parse5).

My package.json:

{
  "dependencies": {
    "jsdom": "^22.0.0",
    "rehype-parse": "^8.0.4",
    "rehype-remark": "^9.1.2",
    "remark-stringify": "^10.0.3",
  }
}

My remix.config.js:

{
  "serverDependenciesToBundle": [
    "bail",
    "character-entities",
    "comma-separated-tokens",
    "decode-named-character-reference",
    "hast-util-embedded",
    "hast-util-from-parse5",
    "hast-util-has-property",
    "hast-util-is-body-ok-link",
    "hast-util-is-element",
    "hast-util-parse-selector",
    "hast-util-phrasing",
    "hast-util-to-mdast",
    "hast-util-to-text",
    "hast-util-whitespace",
    "hastscript",
    "is-plain-obj",
    "longest-streak",
    "mdast-util-phrasing",
    "mdast-util-to-markdown",
    "mdast-util-to-string",
    "micromark-util-decode-numeric-character-reference",
    "micromark-util-decode-string",
    "property-information",
    "rehype-minify-whitespace",
    "rehype-parse",
    "rehype-remark",
    "remark-stringify",
    "space-separated-tokens",
    "trim-trailing-lines",
    "trough",
    "unified",
    "unist-util-find-after",
    "unist-util-is",
    "unist-util-stringify-position",
    "unist-util-visit-parents",
    "unist-util-visit",
    "vfile-location",
    "vfile-message",
    "vfile",
    "web-namespaces",
    "zwitch",
  ],
}

I continue to get the same error even if I add "parse5" to "serverDependenciesToBundle".

@ariofrio
Copy link

ariofrio commented Jun 8, 2023

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 i again, to ensure that parse5 v6 is at the root of my node_modules so that the "serverDependenciesToBundle"-ed rehype-parse module 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 run npm run build after adding a dependency, and it'll list packages to add to "serverDependenciesToBundle". After you add those packages and you run npm run build again, 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:

{
  serverDependenciesToBundle: [
    "bail",
    "ccount",
    "character-entities",
    "comma-separated-tokens",
    "decode-named-character-reference",
    "escape-string-regexp",
    "hast-util-embedded",
    "hast-util-from-parse5",
    "hast-util-has-property",
    "hast-util-is-body-ok-link",
    "hast-util-is-element",
    "hast-util-parse-selector",
    "hast-util-phrasing",
    "hast-util-to-mdast",
    "hast-util-to-text",
    "hast-util-whitespace",
    "hastscript",
    "is-plain-obj",
    "longest-streak",
    "markdown-table",
    "mdast-util-find-and-replace",
    "mdast-util-gfm-autolink-literal",
    "mdast-util-gfm-footnote",
    "mdast-util-gfm-strikethrough",
    "mdast-util-gfm-table",
    "mdast-util-gfm-task-list-item",
    "mdast-util-gfm",
    "mdast-util-phrasing",
    "mdast-util-to-markdown", // <=====
    "mdast-util-to-string",
    "micromark-core-commonmark",
    "micromark-extension-gfm-autolink-literal",
    "micromark-extension-gfm-footnote",
    "micromark-extension-gfm-strikethrough",
    "micromark-extension-gfm-table",
    "micromark-extension-gfm-tagfilter",
    "micromark-extension-gfm-task-list-item",
    "micromark-extension-gfm",
    "micromark-factory-destination",
    "micromark-factory-label",
    "micromark-factory-space",
    "micromark-factory-title",
    "micromark-factory-whitespace",
    "micromark-util-character",
    "micromark-util-chunked",
    "micromark-util-classify-character",
    "micromark-util-combine-extensions",
    "micromark-util-decode-numeric-character-reference",
    "micromark-util-decode-string",
    "micromark-util-encode",
    "micromark-util-html-tag-name",
    "micromark-util-normalize-identifier",
    "micromark-util-resolve-all",
    "micromark-util-sanitize-uri",
    "micromark-util-subtokenize",
    "parse5",
    "property-information",
    "rehype-minify-whitespace",
    "rehype-parse",
    "rehype-remark",
    "remark-gfm",
    "remark-stringify",
    "space-separated-tokens",
    "trim-trailing-lines",
    "trough",
    "unified",
    "unist-util-find-after",
    "unist-util-is",
    "unist-util-stringify-position",
    "unist-util-visit-parents",
    "unist-util-visit",
    "vfile-location",
    "vfile-message",
    "vfile",
    "web-namespaces",
    "zwitch",
  ],
}

But even after rm -rf build node_modules package-lock.json && npm i, I still get the following error on npm run build:

mdast-util-to-markdown is possibly an ESM only package and should be bundled with "serverDependenciesToBundle" in remix.config.js.

And the following on npm run dev:


.../build/index.js:8360
var import_association = require("mdast-util-to-markdown/lib/util/association.js"), import_container_flow = require("mdast-util-to-markdown/lib/util/container-flow.js"), import_indent_lines = require("mdast-util-to-markdown/lib/util/indent-lines.js"), import_safe = require("mdast-util-to-markdown/lib/util/safe.js"), import_track = require("mdast-util-to-markdown/lib/util/track.js");
                         ^
Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/mdast-util-to-markdown/lib/util/association.js from .../build/index.js not supported.
Instead change the require of association.js in .../build/index.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (.../build/index.js:8360:26)
    at Server.<anonymous> (.../build/server.js:44855:3)
    at Object.onceWrapper (node:events:641:28)
    at Server.emit (node:events:527:28)
    at emitListeningNT (node:net:1448:10)
ERROR: "dev:server" exited with 1.

Update: Bundling all packages using a blacklist instead of a whitelist fixed my issue.

@SangeetAgarwal
Copy link

SangeetAgarwal commented Jul 2, 2023

@ariofrio take a look at what I've done with dynamic imports at https://github.com/SangeetAgarwal/tailwind-remix-run-mdxjs-typescript-starter-blog/blob/main/app/lib/mdx.server.ts. This uses the indie stack and I constructed a corresponding repository that uses the blues stack and this is how I do it there.

Both these are deployed to here and here.

I did try to go the serverDependenciesToBundle route per the docs but saw the exact same issue you are experiencing.

I would have liked to call these ESM modules at compile time via serverDependenciesToBundle but just couldn't get it to work so I had to resort to dynamic imports even though (I'm assuming) they'll be slower.

@n8agrin
Copy link
Contributor

n8agrin commented Nov 23, 2023

@ariofrio thanks for posting that. Saved me a couple hours. I had the exact same issue, downgrading jsdom, sadly, was the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests