facebook / docusaurus

Easy to maintain open source documentation websites.
https://docusaurus.io
MIT License
56.23k stars 8.44k forks source link

Support ESM config file #5379

Closed lukejgaskell closed 1 year ago

lukejgaskell commented 3 years ago

📚 Documentation

Remark and Rehype are ES6 Modules only now and the documentation imports them as CommonJS modules.

Page that that does work as described: https://docusaurus.io/docs/next/markdown-features/plugins

The documentation just says to install those packages

yarn add remark-images

and import them as

const remarkImages = require('remark-images');`

The remark-images package is ESM only and is not able to be required. The docusaurus.config.js is a CJS module and can't include that package.

If it is just an older version of those plugins need to be specified, then that makes sense, but might be nice to have in the docs.

Edit: added more details

slorber commented 3 years ago

Can you explain exactly what you mean? Please link to external pages that explain the problem if needed.

I'm not able to understand:

Note Docusaurus is not compatible with the latest versions of remark/rehype. For now we stick to MDX 1.6 and Remark 12.

Josh-Cena commented 3 years ago

Here's an unsuccessful version bumping for rehype-katex: https://github.com/Computerization/Computerization-website/pull/408

Build is failing because rehype-katex is now ESM-only (https://github.com/remarkjs/remark-math/releases/tag/rehype-katex%406.0.0). This is not a problem with Remark 12/13, just how imports are handled.

It seems docusaurus.config.js is unhappy about using ESM import/export, so I've just kept the older, CJS rehype-katex instead.

I'm not so familiar with this, but I wonder why would ESM import fail when I'm on Node v16?

The code below uses rehype-katex@6.0.0:

Import method Error
```js import rehypeKatex from 'rehype-katex'; ``` ``` SyntaxError: Cannot use import statement outside a module ```
```js const rehypeKatex = import('rehype-katex'); ``` `rehype-katex` actually not loaded because it's loaded asynchronously
```js const rehypeKatex = require('rehype-katex'); ``` ``` Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: node_modules/rehype-katex/index.js require() of ES modules is not supported. require() of node_modules/rehype-katex/index.js from docusaurus.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 node_modules/rehype-katex/package.json. ```
slorber commented 3 years ago

I'm not so familiar with this, but I wonder why would ESM import fail when I'm on Node v16?

I don't know.

Try running node docusaurus.config.js, if it fails too, then it's an error that is not related to Docusaurus.


It seems docusaurus.config.js is unhappy about using ESM import/export, so I've just kept the older, CJS rehype-katex instead.

Is it really Docusaurus?

Make sure to replace module.exports with export default and all the cjs imports to ESM, not mixing cjs/esm.

Then again if node docusaurus.config.js fails it's not Docusaurus's fault.

I'd try to add a .mjs extension, or add { "type": "module" } in site package.json, or at least make sure you can import that lib in a standalone node script

But it's likely that we'll need to add proper support for ESM in all packages

Josh-Cena commented 3 years ago

It took me much longer than I thought to make node docusaurus.config.js work 😅

But in the end you can't require an ESM module, so Docusaurus still cannot load the config correctly. I find it quite painful that I can't interleave ESM and CJS with each other in Node

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: docusaurus.config.js
require() of ES modules is not supported.

Some day we are going to make all the packages ESM (by changing the TS target), but that will be when we drop Node v10 support (which I hope is soon, it's not even maintained anymore)

lukejgaskell commented 3 years ago

@slorber @Josh-Cena Thanks for your responses. I updated my question to include more details. As you guys said it seems like Docusaurus just doesn't have support for the config being es6 currently. If there are compatible versions of remark/ rehype plugins, is that listed in the docs somewhere that I just missed?

Josh-Cena commented 3 years ago

@lukejgaskell I don't know if the doc's wrong in this sense, but at least here we did explicitly state the version: https://docusaurus.io/docs/next/markdown-features/math-equations#configuration. I guess it makes sense to update the doc section you mentioned and explicitly warn that the version matters.

For your question—https://github.com/remarkjs/remark-images/releases/tag/3.0.0 remark-images became ESM in v3, so you can use ^2.0.0 in your package.json for now.

slorber commented 3 years ago

Yes, we should warn in the doc that users must install plugin versions compatible with cjs (not the latest).

Unfortunately, we can't really maintain an exhaustive compatibility table. But at least the examples should rather follow our own advice show fixed versions like yarn add remark-images@2


@Josh-Cena we already dropped Node.js 10: https://docusaurus.io/docs/next/installation#requirements

But not sure Node 12 exp support is good enough and in sync with Node 16 support so I'd rather wait a bit more and drop Node 12 too before attempting significant changes, otherwise we may need to support both cjs/esm at the same time and it may be more painful.

Josh-Cena commented 2 years ago

After #5812, we are in a good position to consider how we are going to support ESM in our code base.

TypeScript 4.5 is also focusing on prioritizing ESM support, so maybe we can wait till its stable release to test out how that can aid us in the migration.

szhshp commented 2 years ago

Same issue here.

I'm trying to follow the MDX plugin guide in Docusaurus Doc and create a plugin

ESLint popups up and tell me:

'unist-util-visit' should be listed in the project's dependencies. Run 'npm i -S unist-util-visit' to add it

And the latest unist-util-visit is V4.1 but @docusaurus/mdx-loader only support V2 .

Downgrade to V2 solved the issue.

But I still think we may need fully ESM support in future docusaurus

Josh-Cena commented 2 years ago

@szhshp

  1. It's possible to create ESM MDX plugins, but you have to import it using dynamic import because the config file itself has to be CJS. We now have a bit of a guide on this: https://docusaurus.io/docs/next/markdown-features/math-equations#upgrading-rehype-katex-beyond-recommended-version and we have even upgraded our own internal Remark plugins to ESM: #6286
  2. It's fine to disable extraneous dependencies in your website workspace because there's no downstream consumer for you, and if a package exists, it always exists. Our mdx-loader uses "unist-util-visit": "^2.0.2", as well, as you noticed.
cprice404 commented 1 year ago

Just ran into this today when trying to use remark-directive. It's a pretty sharp edge for new folks like me :)

slorber commented 1 year ago

@cprice404 remark-directive is only for MDX 2 (Docusaurus v3):

The PR (https://github.com/facebook/docusaurus/pull/8288) has only been merged recently (you can try a canary version) and uses remark-directive by default this is how we implement admonitions now) so you don't need to add it manually.

If you want to use ESM remark plugins, now or in the future you can turn your config into an async function:

module.exports = async function createConfig() {
  const plugin = (await import('some-remark-esm-plugin')).default;
  return {
    // ...
  };
}

It's not convenient but it should unlock you until we support ESM in the config file.

cprice404 commented 1 year ago

ah! that's really helpful, thanks!