Markdown解析与编译(三)

Markdown 解析与编译(三)

前言

在前两个小节中,我分别介绍了 mdx 的实时编译与预编译的案例及用法,这一小节就来具体讲讲如何实现代码高亮、以及如何生成 toc 目录等功能。

其实说到这里,那就不得不提一下 unified 大家族了,可以说它提供的功能是非常全的了,

它能够处理 Markdown、HTML、自然语言等,提供了非常多的插件,其提供的插件生态主要包含如下:

虽然有这么多插件,但是我们平时常用的就两个 remarkrehype,我们以react-markdown 解析编译 markdown 文件为例来看看它们俩分别在什么时候发生作用的。

有了上面知识的铺垫,对于后面我们要实现的功能相信你能更好的理解,接着我们就来完成我们上面说的两个功能。

如何生成 toc 目录(remark)

export const compileMDX = async (source: any) => {
  const toc: Toc = [];
  const { code, frontmatter } = await bundleMDX({
    source,
    cwd: path.join(root, 'components'),
    mdxOptions(options, frontmatter) {
      options.remarkPlugins = [
        ...(options.remarkPlugins ?? []),
        remarkGfm,
        remarkFrontmatter,
        [remarkTocHeadings, { exportRef: toc }],
      ];
      options.rehypePlugins = [
        ...(options.rehypePlugins ?? []),
        rehypeSlug,
        rehypeExternalLinks,
        rehypeAutolinkHeadings,
        [rehypePrismPlus, { ignoreMissing: true }],
      ];
      return options;
    },
    esbuildOptions: (options) => {
      options.loader = {
        ...options.loader,
        '.js': 'jsx',
      };
      return options;
    },
  });

  return {
    mdxSource: code,
    toc,
    frontMatter: frontmatter,
  };
};

上面的代码在前面的章节也提到过,不过这里要注意代码中的 remarkPlugins 中的 [remarkTocHeadings, { exportRef: toc }],它是实现生成 toc 目录的关键,以下是 remarkTocHeadings 的代码

import { Parent } from 'unist';
import { visit } from 'unist-util-visit';
import { slug } from 'github-slugger';
import { toString } from 'mdast-util-to-string';

export default function remarkTocHeadings(options: any) {
  return (tree: Parent) =>
    visit(tree, 'heading', (node: any) => {
      const textContent = toString(node);
      options.exportRef.push({
        text: textContent,
        url: '#' + slug(textContent),
        depth: node.depth,
      });
    });
}

如何实现代码高亮(rehype)

还是上面的第一段代码,我就不重复粘贴了,这里要注意的是 rehypePlugins 中的 [rehypePrismPlus, { ignoreMissing: true }],它是实现代码高亮的关键,当然该插件也支持我们自定义代码高亮的颜色,以下为代码片段

.token.tag,
.token.operator,
.token.keyword {
  color: rgb(127, 219, 202);
}

.token.boolean {
  color: rgb(255, 88, 116);
}

.token.number {
  color: rgb(247, 140, 108);
}

.token.constant,
.token.function,
.token.builtin,
.token.char {
  color: rgb(130, 170, 255);
}