预处理器是在书籍加载后、渲染前立即运行的一段代码,允许您更新和修改书籍。可能的用例包括:
- 创建自定义助手,如
{``{#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 作为库引入,预处理器可以访问处理书籍的现有基础设施。
基本流程:
- 使用
CmdPreprocessor::parse_input()
反序列化写入stdin
的 JSON - 通过
Book::for_each_mut()
原地修改每个章节 - 使用
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"