开源项目
bruits/satteri avatar
bruits/satteri

Sätteri:用 Rust 重写 Markdown 处理管线的另一种尝试

适用于 JavaScript 生态系统的高性能 Markdown 和 MDX 处理。

1,257 个 Star34 个 ForkRustMIT

秒懂

它是什么?
Sätteri 是一个用 Rust 实现解析与编译、用 JavaScript 运行插件的 Markdown 和 MDX 处理项目。它面向需要更高吞吐量的 Node 和 Vite 用户,但插件生态和迁移成本是必须权衡的现实。
适合谁用?
Sätteri 适合那些已经使用 unified 生态但遇到性能瓶颈的 Node 项目,尤其是 Vite 构建场景,因为 vite-plugin-satteri 可以直接替换 .md/.mdx 的导入方式。不适合依赖大量 remark 或 rehype 插件的项目,因为那些插件无法直接运行,除非你愿意用 Rust 重写或改用其提供的 JavaScript 插件 API。
能商用吗?
可以。MIT 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
还在维护吗?
在维护。仓库最近一次提交在 2 天前。
用什么语言写的?
主要是 Rust(依据 GitHub 的语言统计)。

以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。

开源项目深度解析

它解决什么问题

JavaScript 生态里的 Markdown 处理长期由 unified 系工具主导,remark 和 rehype 负责解析与转换,但每一步都涉及 JavaScript 对象和 AST 的反复创建,吞吐量有上限。Sätteri 把解析和编译挪到 Rust 里,只在插件层保留 JavaScript。它的定位很明确:给那些处理大量 Markdown 或 MDX 文件、对构建时间敏感的项目一条新路。仓库描述直接说“Parses and compiles in Rust, runs your plugins in JavaScript”,这句话点出了它的核心分工。它不是一个通用框架,而是面向 Node 和 Vite 用户的性能取向方案。

管线如何分层

Sätteri 是一个 Rust 与 TypeScript 的 monorepo,Rust 侧提供 satteri、satteri-ast、satteri-plugin-api 等 crate。satteri-ast 定义了 MDAST 和 HAST 节点类型,以及树操作和转换逻辑,这是 AST 层的基石。satteri-pulldown-cmark 是 CommonMark 解析器的 fork,加了 MDX 扩展支持,负责把文本变成语法树。satteri-mdxjs-rs 是 MDX 编译器,fork 自 mdxjs-rs 并改用 OXC 做 JavaScript 解析。JavaScript 侧通过 satteri-napi-binding 暴露 Rust 管线,npm 包 satteri 提供 TypeScript 插件 API 和顶层函数。插件运行在 JavaScript 里,但 AST 操作在 Rust 侧完成,数据通过 NAPI 边界传递。这个设计避免了每次转换都重建 AST 的开销,但代价是插件无法直接访问 Rust 内部结构,只能通过类型化 visitor 操作。

安装与基本用法

安装方式取决于入口。如果只用 npm 包,文档指向 satteri 的 README,但这里没有给出具体命令,需要去文档站点的 installation 页面查看。Rust crate 则可以直接从 crates.io 拉取,比如 satteri、satteri-ast、satteri-plugin-api 都已发布。对于 Vite 用户,vite-plugin-satteri 是最直接的入口,最近几个版本(v0.3.3 到 v0.3.5)都在 2026 年 8 月密集发布,说明还在快速迭代。仓库没有给出示例代码,但根据包描述,vite-plugin-satteri 的作用是让你在 Vite 里直接 import .md 和 .mdx 文件。实际使用时需要先安装插件,然后在 vite.config 里注册,具体配置键没有在 README 中列出。建议直接查看文档的 quick-start 页面,那里有 usage examples。

插件机制的边界

Sätteri 的插件 API 是它和 unified 生态最大的分水岭。unified 的插件是纯 JavaScript 函数,直接操作 MDAST 或 HAST 对象,而 Sätteri 的 JavaScript 插件必须通过 satteri 包提供的类型化 visitor 来工作,底层树操作在 Rust 侧完成。这意味着你不能直接把现有的 remark 插件搬过来,它们依赖 unified 的节点遍历和修改语义。satteri-plugin-api 是 Rust 侧的 Plugin trait,面向 Rust 插件,但大多数用户不会用 Rust 写插件。这个设计带来性能收益,但也锁死了插件的表达范围:如果某个插件需要非标准的树操作,可能无法在 visitor 模型里实现。仓库没有列出可用的插件列表,除了 satteri-expressive-code 这个 HAST 插件,专门用来渲染代码块。

一个明确的局限:生态迁移成本

最直接的失败模式是:你的项目依赖 remark 或 rehype 的现成插件,比如语法高亮、脚注、目录生成。这些插件在 unified 生态里是标准件,但 Sätteri 不兼容 unified 的插件接口。README 的致谢部分明确提到它借鉴了 unified 的语法树概念,但没有提供兼容层。这意味着迁移不是改一行 import 的事,而是要重新实现或寻找等价插件。对于只处理标准 Markdown 的项目,这个成本可能不高;但对于依赖丰富插件链的内容站点,迁移工作量会很大。另一个局限是 MDX 编译器的 fork 性质,satteri-mdxjs-rs 基于 mdxjs-rs 但改用 pulldown-cmark 和 OXC,这意味着它可能没有跟上上游 MDX 规范的所有变化,边界情况需要自己测试。

与 unified 的实质差异

替代方案就是 unified 本身,它是 Sätteri 的直接参照物。unified 的架构是纯 JavaScript,解析器如 remark-parse 生成 MDAST,插件按顺序修改树,最后通过 remark-stringify 或 rehype 系列输出。每一步都有完整的 JavaScript 对象,调试容易,插件生态庞大。Sätteri 把解析和编译下沉到 Rust,插件只能在受限的 visitor 模型里运行,这是性能与灵活性的取舍。unified 的优点是任何 JavaScript 开发者都能写插件,缺点是大量对象分配导致吞吐量瓶颈。Sätteri 试图用 Rust 消除这个瓶颈,但代价是插件开发门槛升高,而且无法复用 unified 的数千个插件。如果你不需要那些插件,Sätteri 的架构更干净;如果需要,unified 依然是更稳妥的选择。

维护与许可证考量

Sätteri 采用 MIT 许可证,这对商业使用友好,没有 copyleft 义务。但许可证只是起点,维护成本要看项目活跃度。最近一次推送是 2026 年 8 月,vite-plugin-satteri 在三天内发布了三个补丁版本,说明开发还在进行。不过 monorepo 包含多个 crate 和 npm 包,每个都有独立的版本号,升级时需要同步关注。satteri-pulldown-cmark 和 satteri-mdxjs-rs 都是 fork,这意味着上游 pulldown-cmark 和 mdxjs-rs 的安全修复或新特性不会自动同步,你需要手动跟进。NAPI 绑定层也可能因为 Node 版本升级而需要重新编译,这是所有 native 模块的共性。在采用前,检查这些 fork 的上游同步频率,以及 satteri-napi-binding 是否支持你的 Node 版本。

编辑结论

Sätteri 适合那些已经使用 unified 生态但遇到性能瓶颈的 Node 项目,尤其是 Vite 构建场景,因为 vite-plugin-satteri 可以直接替换 .md/.mdx 的导入方式。不适合依赖大量 remark 或 rehype 插件的项目,因为那些插件无法直接运行,除非你愿意用 Rust 重写或改用其提供的 JavaScript 插件 API。在采用前,先确认你需要的插件是否有对应的 HAST 或 MDAST 操作能力,并检查 satteri 的 npm 包是否支持你的 Node 版本和平台。还要验证 MDX 编译的兼容性,因为 satteri-mdxjs-rs 是一个 fork,可能没有覆盖所有 MDX 语法边界。最终判断:如果你能接受插件生态的迁移成本,Sätteri 的架构值得一试;如果你依赖 unified 的现成插件,它目前不是替代品。

官方来源

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
社区笔记

社区笔记