跳到主要内容
版本:3.2.1

MDX 插件

有时候,你可能想要扩展或者修改你的 Markdown 语法。 举个例子:

  • How do I embed youtube videos using the image syntax (![](https://youtu.be/yKNxeF4KMsY))?
  • 怎么对单独处于一行的链接添加不同的样式,比如把它变成社交卡片的样子?
  • 怎么让每一页的开头都包含一个版权声明?

答案是:做一个 MDX 插件! MDX has a built-in plugin system that can be used to customize how the Markdown files will be parsed and transformed to JSX. MDX 插件有三种典型使用场景:

  • Using existing remark plugins or rehype plugins;
  • 创建 Remark/Rehype 插件来转换现有的 MDX 语法生成的元素;
  • 创建 Remark/Rehype 插件来引入新的 MDX 语法。

If you play with the MDX playground, you would notice that the MDX transpilation has two intermediate steps: Markdown AST (MDAST), and Hypertext AST (HAST), before arriving at the final JSX output. MDX 插件也有两种形式:

  • Remark: processes the Markdown AST.
  • Rehype: processes the Hypertext AST.
提示

你可以用插件来为项目中最常用的 JSX 元素创建简短的语法。 The admonition syntax that we offer is also generated by a Remark plugin, and you could do the same for your own use case.

Default plugins

Docusaurus injects some default Remark plugins during Markdown processing. 这些插件会:

  • 生成目录;
  • 给每个标题添加 ID;
  • Transform images and links to require() calls.
  • ……

这些都是 Remark 插件的典型使用场景。如果你想要实现自己的插件,它们也可以成为你的灵感来源。

Installing plugins

每个 MDX 插件一般是一个 npm 包,所以你可以像其他包一样通过 npm 安装。 Take the math plugins as an example.

npm install --save remark-math@5 rehype-katex@6
How are remark-math and rehype-katex different?

In case you are wondering how Remark and Rehype are different, here is a good example. remark-math operates on the Markdown AST, where it sees text like $...$, and all it does is transform that to the JSX <span class="math math-inline">...</span> without doing too much with the content. This decouples the extraction of math formulae from their rendering, which means you can swap KaTeX\\KaTeX out with other math renderers, like MathJax (with rehype-mathjax), just by replacing the Rehype plugin.

Next, the rehype-katex operates on the Hypertext AST where everything has been converted to HTML-like tags already. It traverses all the elements with math class and uses KaTeX\\KaTeX to parse and render the content to actual HTML.

warning

Many official Remark/Rehype plugins are ES Modules only, a JavaScript module system, which Docusaurus supports. We recommend using an ES Modules config file to make it easier to import such packages.

Next, import your plugins and add them to the plugin options through plugin or preset config in docusaurus.config.js:

docusaurus.config.js
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
},
},
],
],
};
Using a CommonJS config file?

If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:

docusaurus.config.js
module.exports = async function createConfigAsync() {
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default],
},
},
],
],
};
};

Configuring plugins

Some plugins can be configured and accept their own options. In that case, use the [plugin, pluginOptions] syntax, like this:

docusaurus.config.js
import rehypeKatex from 'rehype-katex';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
rehypePlugins: [
[rehypeKatex, {strict: false}],
],
},
},
],
],
};

You should check your plugin's documentation for the options it supports.

Creating new rehype/remark plugins

If there isn't an existing package that satisfies your customization need, you can create your own MDX plugin.

备注

The writeup below is not meant to be a comprehensive guide to creating a plugin, but just an illustration of how to make it work with Docusaurus. Visit the Remark or Rehype documentation for a more in-depth explanation of how they work.

For example, let's make a plugin that visits every h2 heading and adds a Section X. prefix. First, create your plugin source file anywhere—you can even publish it as a separate npm package and install it like explained above. We would put ours at src/remark/section-prefix.js. A remark/rehype plugin is just a function that receives the options and returns a transformer that operates on the AST.

import visit from 'unist-util-visit';

const plugin = (options) => {
const transformer = async (ast) => {
let number = 1;
visit(ast, 'heading', (node) => {
if (node.depth === 2 && node.children.length > 0) {
node.children.unshift({
type: 'text',
value: `Section ${number}. `,
});
number++;
}
});
};
return transformer;
};

export default plugin;

You can now import your plugin in docusaurus.config.js and use it just like an installed plugin!

docusaurus.config.js
import sectionPrefix from './src/remark/section-prefix';

export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [sectionPrefix],
},
},
],
],
};
提示

The transformer has a second parameter vfile which is useful if you need to access the current Markdown file's path.

const plugin = (options) => {
const transformer = async (ast, vfile) => {
ast.children.unshift({
type: 'text',
value: `The current file path is ${vfile.path}`,
});
};
return transformer;
};

Our transformImage plugin uses this parameter, for example, to transform relative image references to require() calls.

备注

The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with require() calls already. For example, in the example above, the table of contents generated is still the same even when all h2 headings are now prefixed by Section X., because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the beforeDefaultRemarkPlugins and beforeDefaultRehypePlugins.

docusaurus.config.js
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};

This would make the table of contents generated contain the Section X. prefix as well.