ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

mdbook-preprocessor:用 Rust 实现 mdBook 预处理器扩展的官方开发库

mdbook-preprocessor:用 Rust 实现 mdBook 预处理器扩展的官方开发库 开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载本篇技术指南围绕 mdBook 官方扩展库mdbook-preprocessor展开讲解如何借助它编写自定义预处理器Preprocessor在书籍加载后、渲染前对 Markdown 内容进行程序化改写。读者读完将掌握Preprocessortrait 的完整 API、PreprocessorContext上下文结构、stdin/stdout 进程协议以及book.toml中的配置与排序机制并能够独立实现一个可发布的 mdBook 预处理器。什么是 mdbook-preprocessormdbook-preprocessor是 mdBook 官方维护的 Rust 库 crate其定位在 crates/mdbook-preprocessor/README.md 中写得很明确它是用来实现 mdBook 预处理器的 Rust 库。所谓预处理器是一段在书籍被加载进内存之后、交给渲染器之前立即执行的代码允许开发者对整本书进行更新和改写。常见的用途包括实现自定义的{{#include /path/to/file.md}}之类的辅助标签把 LaTeX 风格表达式如$$ \frac{1}{3} $$替换为 MathJax 等价写法统一注入页眉、清理 Markdown 格式、按章节做统计分析等任何文本级变换。该 crate 由 mdBook 团队维护供更广泛的生态使用。根据 README 中的声明其 API 遵循 semver 兼容。从 crates/mdbook-preprocessor/Cargo.toml 可以看到当前版本为0.5.4它的依赖非常精简仅包含anyhow、mdbook-core、serde与serde_json这也决定了它作为薄封装库的设计取向——真正的书籍模型与配置解析能力来自mdbook-core本库只负责暴露实现预处理器所需的 trait 与输入解析。核心 APIPreprocessortrait预处理器最核心的抽象是 crates/mdbook-preprocessor/src/lib.rs 中定义的Preprocessortrait它由三个方法构成pub trait Preprocessor { /// 获取 Preprocessor 的名称。 fn name(self) - str; /// 运行此 Preprocessor允许它在书籍被交给渲染器之前更新书籍。 fn run(self, ctx: PreprocessorContext, book: Book) - ResultBook; /// 提示 MDBook 该 preprocessor 是否兼容某个特定渲染器。 /// 默认实现恒返回 true。 fn supports_renderer(self, _renderer: str) - Resultbool { Ok(true) } }三个方法各司其职name()返回预处理器的标识名它会被用作book.toml中[preprocessor.name]的键名并用于内部排序与日志输出。run()真正的核心逻辑。接收一个PreprocessorContext上下文和一个Book整本书的内容模型返回处理后的Book。Book中的每个Chapter都持有content字符串即章节的 Markdown 原文因此处理的本质就是对这一字符串做转换。supports_renderer()声明本预处理器是否支持某个渲染器。默认实现无条件返回true即对所有渲染器都生效。若某个预处理器只该在 HTML 渲染时运行可在此根据渲染器名返回false。实现Preprocessortrait 的类型既可以在独立二进制中使用配合parse_input与 stdin/stdout 协议也可以通过MDBook::with_preprocessor在 Rust 程序内部以编程方式注册。该注册入口定义在 crates/mdbook-driver/src/mdbook.rspub fn with_preprocessorP: Preprocessor static(mut self, preprocessor: P) - mut Self { self.preprocessors .insert(preprocessor.name().to_string(), Box::new(preprocessor)); self }也就是说开发者完全可以在自己的 Rust 工具链里直接构造MDBook把预处理器作为对象注入而不必走命令行进程协议。PreprocessorContext预处理器的上下文信息run()接收的PreprocessorContextcrates/mdbook-preprocessor/src/lib.rs在运行时提供了以下字段字段类型含义rootPathBuf书籍目录在磁盘上的位置configConfig完整的书籍配置即book.toml解析结果rendererString当前正在使用本预处理器配合的渲染器名称mdbook_versionString调用方 mdBook 的版本号chapter_titlesRefCellHashMapPathBuf, String章节标题的内部映射mdBook 内部用于计算自定义章节标题外部不应使用其中config字段最为常用预处理器可以在运行时读取book.toml中自己配置段下的任意键值对从而实现行为可配置。例如 examples/nop-preprocessor.rs 中的演示实现就通过ctx.config.get::bool(preprocessor.nop-preprocessor.blow-up)读取自己的布尔配置项若用户把该键设为true预处理器便故意报错退出用于测试失败路径。值得注意的是结构体标注了#[non_exhaustive]并且chapter_titles字段带有#[serde(skip)]这意味着该上下文是刻意保持可扩展的未来可能增加字段同时章节标题映射不会参与序列化、不会在进程协议中传递仅存在于内存中。构造上下文可使用PreprocessorContext::new(root, config, renderer)便捷方法mdbook_version会自动取当前 crate 版本crates/mdbook-preprocessor/src/lib.rs。parse_input与 stdin/stdout 进程协议当预处理器以独立进程形式被 mdBook 调用时通信完全依赖标准输入输出parse_input()就是读取侧的核心函数crates/mdbook-preprocessor/src/lib.rspub fn parse_inputR: Read(reader: R) - Result(PreprocessorContext, Book) { serde_json::from_reader(reader).with_context(|| Unable to parse the input) }它从stdin反序列化出一个二元组(PreprocessorContext, Book)恰好对应 mdBook 写入进程的 JSON 结构。整个调用协议分为两个阶段详见 guide/src/for_developers/preprocessors.md能力探测阶段mdBook 以supports renderer两个参数调用预处理器进程。预处理器应检查自己是否支持该渲染器支持则退出码为 0不支持则返回非零退出码。注意此阶段 stdin 为空只依赖命令行参数与退出码。正式处理阶段若上一步确认支持mdBook 第二次运行该进程向 stdin 写入 JSON 数组[context, book]其中context是序列化后的PreprocessorContextbook是序列化后的Book对象。预处理器将修改后的Book以 JSON 形式写回 stdout。服务端一侧的实现可以参考 crates/mdbook-driver/src/builtin_preprocessors/cmd.rs其中的CmdPreprocessor正是按上述协议工作的supports_renderer()通过cmd.arg(supports).arg(renderer)探测退出码run()则用serde_json::to_writer把(ctx, book)写入子进程 stdin见write_input方法等待子进程结束后用serde_json::from_slice解析 stdout 中的处理结果并对非零退出码与 JSON 解析失败分别给出明确错误信息。cmd.rs 的测试模块还提供了一个round_trip_write_and_parse_input测试验证write_input写出后再用mdbook_preprocessor::parse_input读回Book与PreprocessorContext均与原始值一致——这从侧面印证了parse_input与序列化格式的对称性。一个可运行的读取端模板如下与官方 no-op 示例同构let (ctx, book) mdbook_preprocessor::parse_input(io::stdin())?; let processed_book pre.run(ctx, book)?; serde_json::to_writer(io::stdout(), processed_book)?;在 book.toml 中配置预处理器预处理器通过book.toml中的[preprocessor.name]配置段启用完整配置语法见 guide/src/format/configuration/preprocessors.md。mdBook 默认会尝试调用与名称匹配的mdbook-name可执行程序例如配置了[preprocessor.example]就会执行mdbook-example。自定义命令与参数若可执行文件名称不同或需要传参可用command键覆盖例如使用 Python 脚本[preprocessor.random] command python random.py绑定到特定渲染器renderers键可声明预处理器只在哪些渲染器下运行[preprocessor.example] renderers [html] # example 预处理器只在 HTML 渲染器下运行这与supports_renderer方法相对应renderers配置最终会传导为对预处理器supports_renderer的能力询问。可选预处理器默认情况下若启用的预处理器未安装构建会报错。标记optional true可将错误降级为警告适合有则增强、无则不干扰的渐进增强场景[preprocessor.example] optional true控制执行顺序预处理器之间可以声明先后依赖。例如要求自定义linenos预处理器在 mdBook 内置的links预处理器之后运行因为{{#include}}展开后才会出现待加行号的代码[preprocessor.linenos] after [ links ]等价地也可以反过来写[preprocessor.links] before [ linenos ]两种写法同时出现虽然冗余但合法。具有相同优先级的预处理器按名称排序任何循环依赖都会被检测并报错。实战示例一no-op 预处理器最小骨架仓库根目录的 examples/nop-preprocessor.rs 是官方推荐的入门模板——一个什么都不做的预处理器但骨架五脏俱全包含 clap 命令行解析、版本兼容检查和supports子命令处理fn make_app() - Command { Command::new(nop-preprocessor) .about(A mdbook preprocessor which does precisely nothing) .subcommand( Command::new(supports) .arg(Arg::new(renderer).required(true)) .about(Check whether a renderer is supported by this preprocessor), ) }主流程按协议分派如果子命令是supports调用handle_supports并依据pre.supports_renderer(renderer)的结果以 0/1 退出码结束进程否则进入handle_preprocessing。后者还演示了官方推荐的版本兼容检查模式——用semver比较调用方版本ctx.mdbook_version与库编译时版本mdbook_preprocessor::MDBOOK_VERSION若不匹配则打印警告而非直接失败let book_version Version::parse(ctx.mdbook_version)?; let version_req VersionReq::parse(mdbook_preprocessor::MDBOOK_VERSION)?; if !version_req.matches(book_version) { eprintln!( Warning: The {} plugin was built against version {} of mdbook, \ but were being called from version {}, pre.name(), mdbook_preprocessor::MDBOOK_VERSION, ctx.mdbook_version ); }示例中的Nop实现还演示了如何通过ctx.config读取自定义配置项来制造可控失败preprocessor.nop-preprocessor.blow-up以及supports_renderer如何拒绝名为not-supported的渲染器。其内嵌测试直接以一段 JSON 字符串模拟 mdBook 的 stdin 输入验证处理后书籍与输入书籍完全一致assert_eq!(actual_book, expected_book)这是编写预处理器单元测试的极佳范式。实战示例二用 pulldown-cmark 删除全部强调单纯对chapter.content字符串做正则替换容易破坏 Markdown 结构。更稳妥的做法是把内容解析成事件流再过滤重建。examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs 演示了完整的处理链fn remove_emphasis(num_removed_items: mut usize, chapter: mut Chapter) - ResultString { 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)?) }核心思路用pulldown_cmark::Parser把 Markdown 解析为事件流Event用filter丢弃Tag::Emphasis/Tag::Strong的起始与结束事件并计数用pulldown_cmark_to_cmark::cmark把剩余事件流重新序列化为合法的 Markdown。这样**加粗**、*斜体*会被无痕移除而文档其余结构代码块、链接、列表保持原样。该示例的main同样遵循双阶段协议收到supports参数时直接正常返回即支持所有渲染器随后在handle_preprocessing中调用parse_input→run→serde_json::to_writer。遍历章节使用的是book.for_each_chapter_mut便捷方法每个章节可原地修改ch.content。实战示例三用其他语言实现预处理器由于 mdBook 与预处理器之间只通过 stdin/stdout 交换 JSON实现语言完全不受限制。官方文档 guide/src/for_developers/preprocessors.md 给出了一个 Python 版本配合command 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 context, book json.load(sys.stdin) # 修改第一章的内容 book[items][0][Chapter][content] # Hello # 处理完毕将 book 以 JSON 打印到 stdout print(json.dumps(book))要点在于收到supports参数时直接sys.exit(0)否则从sys.stdin解析[context, book]按 JSON 结构修改章节内容book[items][0][Chapter][content]再把修改后的book用json.dumps输出。理解Book的 JSON 结构items数组、Chapter枚举标签、content字段是这类实现的关键。内置预处理器与禁用方式mdBook 自带两个内置预处理器定义在 crates/mdbook-driver/src/builtin_preprocessors/mod.rslinks展开章节中的{{#playground}}、{{#include}}与{{#rustdoc_include}}辅助标签把对应文件内容嵌入章节index将所有名为README.md的章节文件转换为index.md使渲染输出为index.html。它们随构建默认启用可通过build.use-default-preprocessors配置项整体关闭。这两个内置实现与CmdPreprocessor一起构成了理解预处理器在构建管线中的位置的完整参照书籍从磁盘加载 → 依次运行预处理器含自定义项→ 交给渲染器。小结从库到生态的完整闭环mdbook-preprocessor是一个刻意保持小而专的官方库trait 定义Preprocessor、上下文PreprocessorContext与输入解析parse_input三者构成了 Rust 侧的全部核心 API其余能力书籍模型、配置解析、版本常量由mdbook-core提供。配合book.toml的配置段、可选的supports能力探测协议以及不限语言的 stdin/stdout JSON 通道任何开发者都能以极低的成本为 mdBook 生态贡献一个可复用、可排序、可配置的预处理器扩展。建议从 examples/nop-preprocessor.rs 起步搭建骨架再参照 examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs 引入真正的 Markdown 解析与改写逻辑最后用optional、renderers、before/after等配置打磨其集成体验。赞分享开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载相关推荐mdBook高级功能与扩展开发mdBook高级功能与扩展开发 本文深入探讨了mdBook的高级功能与扩展开发能力涵盖了预处理器机制、渲染器后端扩展、搜索功能集成以及数学公式与代码高亮支持。开发工具文档mdBook 预处理器Preprocessor配置完全指南book.toml 配置项、内置插件与源码原理mdBook 预处理器Preprocessor配置完全指南book.toml 配置项、内置插件与源码原理 Preprocessor预处理器是 mdBo开发工具文档Druid 文档工程实践使用 mdBook 编写、预览与发布官方文档Druid 文档工程实践使用 mdBook 编写、预览与发布官方文档 本文以 Druid 仓库中的 docs/README.md https://link.g跨平台桌面应用UI组件上一篇如何实现微前端架构中的结构化日志与日志聚合MFE-starter完整指南下一篇React Starter Kit 安全策略模板落地指南从占位模板到可执行的漏洞披露与事件响应体系创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表