Can't require remark and rehype plugins for gatsby-plugin-mdx
Asked Answered
V

2

8

I was trying to follow the documentation on including rehype plugins for gatsby-plugin-mdx. Specifically I was trying to use the rehype-slug plugin. I installed the packaged with npm and set my gatsby.config.js file to

module.exports = {
  siteMetadata: {
    siteUrl: "https://www.yourdomain.tld",
    title: "test Website",
  },
  plugins: [
    {
      resolve: "gatsby-plugin-mdx",
      options:{
        rehypePlugins: [
          require("rehype-slug")
        ]
      }
    }
  ],
};
However upon running gatsby develop I encouter the following error: Error: [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\Users\User\Documents\test-site\node_modules\rehype-slug\index.js require() of ES modules is not supported.

I encouter similar problems when trying to use the remark-math and rehype-katex plugins. I'm using version 3.13.0 of the Gatsby CLI. The problem persists even with a completely new website. Any help with this issue would be much appreciated.

Vita answered 13/9, 2021 at 16:54 Comment(2)
Until gatsby resolves the issue with esm, downgrading rehype-slug to 4.0.1 make it work.Sucking
@Nik Sumeiko you should add that as an answerFlowers
M
2

Not sure if it will work but, instead of using require from ES modules have you tried something like:

import slug from 'rehype-slug'

module.exports = {
  siteMetadata: {
    siteUrl: "https://www.yourdomain.tld",
    title: "test Website",
  },
  plugins: [
    {
      resolve: "gatsby-plugin-mdx",
      options:{
        rehypePlugins: [
          slug 
        ]
      }
    }
  ],
};

Based on: https://github.com/rehypejs/rehype-slug

Or directly importing in inside rehypePlugins as a dynamic import.


I've made a little bit of research and I found that dynamic imports are not supported because you can't access the value in the callback, so waiting for the import is not a solution either nor using an ES module.

However, the final solution (or at least the temporary working one) with your exact same use-case can be found in this GitHub discussion:

Update: I got the update to [email protected] in my gatsby-config.js working by installing esm and patch-package and:

  1. Modifying the gatsby-config.js as follows (to allow require() with the pure ES Modules)

    require = require('esm')(module);
    
    module.exports = {
      plugins: [
        {
          resolve: `gatsby-plugin-mdx`,
          options: {
            rehypePlugins: [
              require('rehype-slug').default,
    
  2. Try running the Gatsby dev server and see what breaks. It will throw an error either like this:

      Error: Must use import to load ES Module: /Users/k/p/project/node_modules/rehype-slug/index.js
      require() of ES modules is not supported.
      require() of /Users/k/p/project/node_modules/rehype-slug/index.js from /Users/k/p/project/gatsby-config.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 index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/k/p/project/node_modules/rehype-slug/package.json.
    

    ...or like this:

      Error: /Users/k/p/project/gatsby-config.js:1
      Error: Must use import to load ES Module: /Users/k/p/project/node_modules/rehype-slug/index.js
    
  3. Make a note of the location of the pure ES Modules package so that you can locate the package.json file corresponding to the pure ESM package and manually remove the line with "type": "module", like this:

    -  "type": "module",
    
  4. Run yarn patch-package <pkg name> --exclude '^$', which will record the changes that you have made in the package.json file (to be reapplied by patch-package every time that you run yarn or npm install). Examples:

    # For a top-level dependency
    yarn patch-package rehype-slug --exclude '^$'
    
    # For a transitive dependency
    yarn patch-package rehype-slug/unist-util-visit --exclude '^$'
    
  5. Run the Gatsby dev server again, and repeat step 3 and 4 for every pure ESM package.

At that point, I could start the Gatsby dev server with the new pure ESM dependency versions.

cc @Ir1d @kimbaudi @wardpeet @LekoArts

Caveat: In my specific case, I needed to also patch one of the dependencies (hast-util-heading-rank) because I was getting an undefined node.tagName, but I think this is unrelated to this problem - probably related to a different issue.

All credits to the magician involved

Miniver answered 13/9, 2021 at 18:31 Comment(0)
M
0

There is a simpler and more elegant solution in the same GitHub discussion

Create require-esm.js in the root folder (same place as package.json):

// Source: https://mcmap.net/q/1372844/-can-39-t-require-remark-and-rehype-plugins-for-gatsby-plugin-mdx

const esm = require('esm')
const fs = require('fs')
const Module = require('module')

// Node: bypass [ERR_REQUIRE_ESM]
const orig = Module._extensions['.js']
Module._extensions['.js'] = function (module, filename) {
  try {
    return orig(module, filename)
  } catch (e) {
    const content = fs.readFileSync(filename, 'utf8')
    module._compile(content, filename)
  }
}

const _esmRequire = esm(module, {
  cjs: true,
  mode: 'all',
})

// don't pollute Module
Module._extensions['.js'] = orig

module.exports = function esmRequire(id) {
  return _esmRequire(id).default
}

Then use it in gatsby-config.js like this:

require.esm = require('./require-esm')

module.exports = {
        .......
        {
            resolve: `gatsby-plugin-mdx`,
            options: {
                extensions: ['.mdx', '.md'],
                rehypePlugins: [
                    // Generate heading ids for rehype-autolink-headings
                    [require.esm('rehype-slug')],
                    // To pass options, use a 2-element array with the
                    // configuration in an object in the second element
                    [require.esm('rehype-autolink-headings'), { behavior: "wrap" }],
                ],
            }
        },
        .......
}

Update

After some testing I've simplified code above to couple lines. It still works in my setup.

Use require.esm(...) in gatsby-config.js like this:

const requireEsm = require('esm')(module)
require.esm = id => requireEsm(id).default

module.exports = {
                .......
                rehypePlugins: [
                    // Generate heading ids for rehype-autolink-headings
                    [require.esm('rehype-slug')],
                    .......
}

Much answered 3/3, 2022 at 22:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.