【mdBook】7.1 预处理器

预处理器是在书籍加载后、渲染前立即运行的一段代码,允许您更新和修改书籍。可能的用例包括:

  • 创建自定义助手,如 {``{#include /path/to/file.md}}
  • 将 LaTeX 风格的表达式($$ \frac{1}{3} $$)替换为它们的 MathJax 等效形式

有关使用预处理器的更多信息,请参阅"配置预处理器"章节。

集成到 MDBook

MDBook 使用相当简单的机制来发现第三方插件。在 book.toml 中添加一个新表(例如,对于 foo 预处理器使用 [preprocessor.foo]),然后 mdbook 将尝试调用 mdbook-foo 程序作为构建过程的一部分。

执行流程

一旦定义了预处理器并且构建过程开始,mdBook 会执行 preprocessor.foo.command 键中定义的命令两次:

第一次调用:检查渲染器支持

bash 复制代码
mdbook-foo supports [渲染器名称]
  • 预处理器应以状态码 0 退出(如果支持给定的渲染器)
  • 或以非零退出代码返回(如果不支持)

第二次调用:实际处理

bash 复制代码
mdbook-foo
  • mdbook 将 JSON 数据传入 stdin
  • JSON 格式为 [context, book] 数组
    • context 是序列化的 PreprocessorContext 对象
    • book 是包含书籍内容的 Book 对象
  • 预处理器应将修改后的 Book 对象的 JSON 格式返回到 stdout

实现预处理器

Rust 实现方式

最简单的方法是创建自己的 Preprocessor trait 实现,然后创建一个 shell 二进制文件来将输入转换为正确的 Preprocessor 方法。

便利工具

  • examples/ 目录中有一个无操作预处理器示例
  • 可轻松适配用于其他预处理器

实现提示

通过将 mdbook 作为库引入,预处理器可以访问处理书籍的现有基础设施。

基本流程

  1. 使用 CmdPreprocessor::parse_input() 反序列化写入 stdin 的 JSON
  2. 通过 Book::for_each_mut() 原地修改每个章节
  3. 使用 serde_json crate 将结果写入 stdout

章节访问方式

  • 直接访问(通过递归迭代章节)
  • 使用便利方法 Book::for_each_mut()

Markdown 处理

chapter.content 只是一个恰好是 Markdown 的字符串。虽然完全可以使用正则表达式或手动查找替换,但您可能希望将输入处理为更计算机友好的格式。

推荐工具

  • pulldown-cmark:生产级的事件驱动 Markdown 解析器
  • pulldown-cmark-to-cmark:将事件转换回 Markdown 文本

示例代码:从 Markdown 中移除所有强调,同时避免意外破坏文档

rust 复制代码
fn remove_emphasis(num_removed_items: &mut usize, chapter: &mut Chapter) -> Result<String, Error> {
    let mut buf = String::with_capacity(chapter.content.len());

    let events = Parser::new(&chapter.content).filter(|e| match e {
        Event::Start(Tag::Emphasis) | Event::Start(Tag::Strong) => {
            *num_removed_items += 1;
            false
        }
        Event::End(TagEnd::Emphasis) | Event::End(TagEnd::Strong) => false,
        _ => true,
    });

    Ok(pulldown_cmark_to_cmark::cmark(events, &mut buf).map(|_| buf)?)
}

使用其他语言实现预处理器

mdBook 使用 stdin 和 stdout 与预处理器通信的事实使得用 Rust 以外的语言实现它们变得容易。

Python 示例:修改第一章内容的简单预处理器

python 复制代码
import json
import sys

if __name__ == '__main__':
    if len(sys.argv) > 1:  # 检查是否收到任何参数
        if sys.argv[1] == "supports": 
            # 返回退出状态码 0,因为另一个参数只是渲染器的名称
            sys.exit(0)

    # 从 stdin 加载上下文和书籍表示
    context, book = json.load(sys.stdin)
    # 现在,我们可以修改第一章的内容
    book['sections'][0]['Chapter']['content'] = '# Hello'
    # 完成书籍修改后,只需将其打印到 stdout
    print(json.dumps(book))

配置对应

toml 复制代码
[preprocessor.foo]
command = "python preprocessor.py"
相关推荐
suaizai_21 小时前
HTML为何碾压Markdown?AI时代新选择
markdown
前端逗比逗3 天前
流式 Markdown 解析 + 代码块防截断闪烁
markdown·webassembly
清风小道君4 天前
手搓一个零依赖的 Markdown 静态站点生成器,我学到了什么
markdown
X档案库6 天前
【开源】我做了一套可以 AI 托管的 Markdown 博客与知识库
rust·博客·markdown·marksharex
DeMinds8 天前
内容没有丢,我为什么总在重新整理?|DeMinds 如何让工作接着继续
ios·github·markdown
acheding9 天前
File System Access API 实战:让网页真正读写本地文件
前端·javascript·vue.js·编辑器·markdown
acheding9 天前
把 CodeMirror 6 调教成 Markdown 编辑器:扩展、装饰与门面
javascript·vue.js·编辑器·markdown
卷无止境11 天前
Quarkdown:赋予 Markdown 超能力的现代排版系统
前端·markdown
DeMinds12 天前
这篇文章,真的有“结构”吗?
markdown
特立独行的猫a13 天前
Markmap 入门到精通:从一段 Markdown 到一张可交互思维导图
markdown·工具·思维导图·markmap