Astro 修改(2) -- 为 Astro 中的代码块增加 Figure 标签


由于非常喜欢 Zed 博客 中图片块支持说明的样式,我决定给我的 Blog 也加上这个功能。

一番搜寻,网上的解决方案基本都是在 MDX 中实现的。

如果你看过我的上一篇帖子 Astro 修改(1) — 增加 MarkDown Mermaid 支持
你应该可以发现我有一点赛博洁癖,不喜欢引入额外的 JS,当然放在这里,我也不会引入 MDX。

我目前的 Astro 渲染流程

Mermaid 图表 亮色Mermaid 图表 暗色

由于我更改了 Astro 的渲染流程,我没法在 Shiki 渲染后再通过 Rehype 来给 Shiki 高亮过的代码块包裹一层 <figure>

而在 Shiki 之前通过 Rehype 包裹一层 <figure> 的话,Shiki 就完全没法工作了,查看 rehype-shiki 的源代码可以看到:

部分 Rehype-Shiki 的代码
export async function createShikiHighlighter({}): {
  async function highlight() {}

  code = code.replace(/(?:\r\n|\r|\n)$/, "");

  return highlighter[to === "html" ? "codeToHtml" : "codeToHast"](code, {
    transformers: [
      {
        pre(node) {
          // 略
        },
        line(node) {
          // 略
        },
        code(node) {
          // 略
        },
      },
    ],
  });
}

return {
  codeToHast(code, lang, options = {}) {
    return highlight(code, lang, options, "hast") as Promise<Root>;
  },
  codeToHtml(code, lang, options = {}) {
    return highlight(code, lang, options, "html") as Promise<string>;
  },
};
}

Shiki 只会处理 code line pre 这三个 Node,如果换成其他标签那么 Shiki 就直接跳过不处理了。
但是非常幸运的是,Shiki 提供了 Transformers(变形金刚) 来作为拓展 Shiki 处理流程的方法。

了解 Shiki 的渲染流程

Mermaid 图表 亮色Mermaid 图表 暗色
  • PREprocess - 在代码标签化之前调用,你可以使用它在代码渲染为标签之前进行修改。
  • tokens - 在代码标签化之后调用,你可以使用它来修改标签。
  • span - 对每个 <span> 标签及每个标记都调用。
  • line - 对每行 <span> 标签调用。
  • code - 对每个 <code> 标签调用,这将会包裹所有的行。
  • pre - 对每个 <pre> 标签调用,并将其包裹在 <code> 标签中。
  • root - HAST 树的根,通常情况下只有 <pre> 一个子标签。
  • POSTprocess - 在 HTML 生成后调用,用来修改最终输出,在 codeToHast 中不会被调用。

Transformers 可以在任意节点介入对 Shiki 代码做出相应处理,所以我的实现便是在 root 节点做出对 HTML 的改动,如果有简介,则包裹一层 <figure>

而如何判断是否有简介呢?Shiki 提供了一个方法,this.options.meta.__raw 可以获取反引号之间除了语言类别之外的内容。

实现

shikiTransformerCodeBlockFigure

它已经在 GitHub 了,非常简单的实现。



评论加载中……