MDX 插件
有时候,你可能想要扩展或者修改你的 Markdown 语法。 举个例子:
- 怎么用图像语法嵌入 YouTube 视频 (
![](https://youtu.be/yKNxeF4KMsY)
)? - 怎么对单独处于一行的链接添加不同的样式,比如把它变成社交卡片的样子?
- 怎么让每一页的开头都包含一个版权声明?
答案是:做一个 MDX 插件! MDX 内置了一个插件系统,可以用来自定义 Markdown 文件是如何被如何解析并转换到 JSX 的。 MDX 插件有三种典型使用场景:
- Using existing remark plugins or rehype plugins;
- 创建 Remark/Rehype 插件来转换现有的 MDX 语法生成的元素;
- 创建 Remark/Rehype 插件来引入新的 MDX 语法。
如果你在 MDX 实时编辑器上尝试过,你就会注意到 MDX 转译包括了两个中间步骤: Markdown AST (MDAST), 和超文本 AST (HAST),然后才会到达最终的 JSX 输出。 MDX 插件也有两种形式:
你可以用插件来为项目中最常用的 JSX 元素创建简短的语法。 我们提供的告示语法也是由一个 Remark 插件生成的。你可以根据自己的用途仿照着来做。
默认插件
Docusaurus 会在处理 Markdown 时插入一些默认的 Remark 插件。 这些插件会:
- 生成目录;
- 给每个标题添加 ID;
- 把图像和链接转化为
require()
导入。 - ……
这些都是 Remark 插件的典型使用场景。如果你想要实现自己的插件,它们也可以成为你的灵感来源。
安装插件
每个 MDX 插件一般是一个 npm 包,所以你可以像其他包一样通过 npm 安装。 以公式插件为例。
- npm
- Yarn
- pnpm
npm install --save remark-math@3 rehype-katex@4
yarn add remark-math@3 rehype-katex@4
pnpm add remark-math@3 rehype-katex@4
最近 Remark/Rehype 生态系统总体有向 ES 模块迁移的趋势,这是一个新的 JavaScript 模块系统,Docusaurus 还不支持它。 在我们正式支持 ESM 之前,请确保你安装的插件版本是 CommonJS 版本的。 或者,你可以在安装 rehype-katex
的教程中阅读关于使用动态 import()
的方案。
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 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 to parse and render the content to actual HTML.
接下来,在 docusaurus.config.js
中通过插件或预设配置把它们添加到插件选项中:
const math = require('remark-math');
const katex = require('rehype-katex');
module.exports = {
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [math],
rehypePlugins: [katex],
},
},
],
],
};
配置插件
Some plugins can be configured and accept their own options. In that case, use the [plugin, pluginOptions]
syntax, like this:
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [math],
rehypePlugins: [
[katex, {strict: false}],
],
},
},
],
],
};
You should check your plugin's documentation for the options it supports.
创建新的 rehype/remark 插件
If there isn't an existing package that satisfies your customization need, you can create your own MDX plugin.
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.
const visit = require('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;
};
module.exports = plugin;
You can now import your plugin in docusaurus.config.js
and use it just like an installed plugin!
const sectionPrefix = require('./src/remark/section-prefix');
module.exports = {
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
.
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};
This would make the table of contents generated contain the Section X.
prefix as well.