/ 自定义 / 多样化的插件

MoPress 提供三种互不排斥的方式来扩展内容的处理与展现方式,分别对应三种不同的扩展环境:独立于 MoPress 进程之外的外部程序、浏览器运行时标准、以及 MoonBit 生态内的代码库。选择哪一种,取决于想在什么层面、用什么语言解决问题。

形式扩展环境适用场景
外部 stdin、stdout 程序任意语言的独立进程用熟悉的语言编写文本、语法树处理逻辑,且需要跨项目复用,请阅读 外部插件协议
Web Components浏览器运行时需要在页面上呈现交互式、富媒体内容,且逻辑运行在客户端,请阅读 Web Components
MoonBit 模块MoonBit 生态,仅 Site 模式将一组可复用的 step 组合打包为库,在多个 Site 模式项目间共享,请阅读 MoonBit 模块插件

这是与 mdBook 的 preprocessor 机制最为接近的扩展方式:编写一个独立的可执行程序或脚本,MoPress 在构建过程中把内容通过标准输入传给它,读取它的标准输出作为处理结果。

与 mdBook 不同的是,MoPress 把这一机制拆分为两个阶段。对应的调用函数签名:

pub async fn run_markdown_preprocessors(
  config : @config.BookConfig,
  data : String,
) -> String raise

pub async fn run_markdown_transformers(
  config : @config.BookConfig,
  data : Array[@markdown.Block],
) -> Array[@markdown.Block] raise

预处理器(preprocessor)操作原始 Markdown 文本,在解析为语法树之前运行,对应 run_markdown_preprocessors转换器(transformer)操作已经解析好的 Markdown 语法树,在渲染为 HTML 之前运行,对应 run_markdown_transformers。这让可以根据实际需求,选择在文本层面还是结构层面介入处理流程。两者都通过配置中的 preprocessorstransformers 字段声明,具体的通信协议请阅读 外部插件协议

失败时统一抛出以下错误类型:

pub suberror ProcessorError {
  NonZeroExit(command~ : String, exit_code~ : Int, stderr~ : String)
  StderrReported(command~ : String, message~ : String)
  InvalidStdout(command~ : String, raw~ : String, reason~ : String)
} derive(Eq, @debug.Debug)
  • NonZeroExit:命令以非零状态码退出。

  • StderrReported:命令退出码为零,但标准错误非空,被视为业务层面主动上报的错误。

  • InvalidStdout:标准输出非空,但无法解析为预期的响应结构。

如果需要的不是“改写内容”,而是“让某段内容在页面上具备交互能力或特殊呈现效果”,Web Components 通常是比外部程序更合适的选择。MoPress 的 Markdown 解析器在遇到无法识别的 HTML 标签时,会将其原样保留在语法树中,而不会报错或丢弃:

pub(all) enum Block {
  Heading(Int, Array[Inline])
  Paragraph(Array[Inline])
  ThematicBreak
  CodeBlock(String?, String)
  BlockQuote(Array[Block])
  List(ListBlock)
  HtmlBlock(String)
  Table(TableBlock)
} derive(Eq, ToJson, @debug.Debug, @json.FromJson)

其中 HtmlBlock(String) 就是原始 HTML 块被保留下来的形态;行内的原始 HTML 则对应 Inline 枚举中的 Html(String)。这意味着可以直接在 Markdown 正文中写自定义元素标签,只要配合注入脚本引入对应的自定义元素定义脚本,浏览器就会在渲染时原生地接管这部分内容,不需要 MoPress 在构建期做任何额外处理。详情请阅读 Web Components

如果扩展逻辑是用 MoonBit 编写的、只在 Site 模式的构建脚本内运行,最自然的做法是把它写成一组可复用的 step 函数,打包发布为一个独立的 MoonBit 包,供其他构建脚本通过包管理器引入。这一形式仅适用于 Site 模式,Book 模式的配置文件无法引用 MoonBit 代码。详情请阅读 MoonBit 模块插件

三种形式的取舍可以归结为两个问题:这段逻辑应该跑在哪里、以及希望用什么语言编写它。

  • 需要独立于 MoPress 主进程运行、且可能用非 MoonBit 语言编写,选择外部程序。

  • 需要运行在浏览器端、为读者提供交互,选择 Web Components。

  • 需要运行在构建期、且愿意用 MoonBit 编写、追求进程内调用的性能与可复用性,选择 MoonBit 模块。

这三者并不互斥,一个站点完全可以同时使用多种形式,用外部程序处理某种自定义 Markdown 语法,同时用 Web Components 渲染交互式图表。