Typst 贡献指南详解:从 Fork 到进入 Changelog 的完整流程与配套 CI、测试体系
本文基于 Typst 仓库的官方贡献文档 CONTRIBUTING.md,系统讲解在 Typst 项目中提交代码的完整路径:如何选择贡献目标、六步落地一个贡献、什么样的 PR 才被视为"好 PR",以及 PR 背后的 CI 检查与集成测试体系。读完本文,你可以按仓库真实的命令与流程完成一次贡献准备,并理解每个环节(CI 矩阵、clippy 规则、参考图测试)在源码与配置中的对应位置。
一、从哪里开始贡献
官方贡献文档的第一条建议是:找到一件你自己真正感兴趣的事情。仓库中提供了带标签的 issue 分类作为入口:good first issue(适合首次贡献者的入门问题)与 good contribution(体量更大的贡献主题)。但文档同时强调,比起标签,更推荐选择你自己希望被改进的部分——开源贡献应该是一件有乐趣的事。
对新贡献者最重要的两条原则:
- 从小的改动开始。 文档原话强调("start with something small"):小改动能帮助你熟悉项目、流程与设计哲学;大型架构提案或来自新贡献者的大型 PR 很难获得推动力。开源开发是高度协作的过程,信任需要逐步建立。
- 先讨论,再动手。 在开始一个较大的功能或重构之前,请先开一个 issue,或在 Discord 的
#contributors频道发起讨论,确认设计方向。文档提醒:Typst 是一个具有长期愿景的复杂项目,"实现完之后"才发现自己的想法与项目愿景不符,是一件令人沮丧的事。
文档中给出的讨论入口(issue 列表、Discord 频道)属于外部协作渠道,本文不展开外部链接;重点是这个"先对齐设计"的纪律本身。
二、落地一个贡献的六个步骤
CONTRIBUTING.md 将贡献落地过程拆分为六个步骤,下面逐步展开,并结合仓库中的实际配置补充每个步骤背后的工程事实。
第 1 步:Fork 仓库,理解代码库
Fork Typst 仓库后开始你的贡献。文档建议:如果在任何时候不确定代码库中某件事该怎么做,请联系维护者或更有经验的贡献者;同时推荐阅读 docs/dev/architecture.md,它概述了编译器的整体工作方式。
这份架构文档是理解"改动会影响哪个 crate"的关键。从 docs/dev/architecture.md 可以看到仓库的目录职责划分(每个 crate 的 Cargo.toml 位于 crates/ 下,工作区定义在 Cargo.toml):
crates/typst:主编译器 crate,定义完整的语言与库;crates/typst-cli:命令行界面,是编译器与导出器之上相对薄的一层;crates/typst-eval:Typst 语言的解释器;crates/typst-syntax:解析器与语法树定义;crates/typst-layout:排版引擎;crates/typst-realize:realization(展示规则展开)子系统;crates/typst-library:标准库;crates/typst-pdf/crates/typst-svg/crates/typst-html:PDF、SVG、HTML 导出器;crates/typst-render:帧渲染器;crates/typst-ide:IDE 功能(自动补全、跳转等);crates/typst-macros、crates/typst-utils、crates/typst-timing、crates/typst-kit:过程宏、通用工具、计时与 CLI 默认实现。
编译流程分四个阶段:Parsing(源字符串到语法树)→ Evaluation(语法树与依赖到内容)→ Layout(内容到每页一帧)→ Export(帧到 PDF/SVG 等输出),编译器基于 comemo 增量编译框架。理解这条管线有助于你把改动放在正确的 crate 里。
第 2 步:实现改动(明确不接受 AI 代写的贡献)
文档在此步有一条非常醒目的红线:"Do not vibecode the change! Contributions that were implemented by an AI model will not be accepted."(不要凭 AI 辅助"感觉"直接写代码;由 AI 模型实现的贡献不会被接受。)文档给出的理由值得注意:
- 在维护者与 AI 模型之间加一个外部贡献者作为"中介"价值很有限;
- 所有贡献都由时间有限的真人审阅;
- AI 驱动的贡献容易造成危险的失衡——维护者花在某个 PR 上的时间超过贡献者本人投入的时间。
第 3 步:创建 PR 并写清技术理由
提交 Pull Request 时,需要说明贡献内容、其背后的技术理由(technical rationale),以及如果包含新功能,用户将如何使用它。最好在此处链接到对应的 issue。文档同时明确要求:不要用 AI 撰写 PR 描述,要用自己的话描述想法——这既帮助你梳理思路,也让维护者看到变更背后的人类思考过程。如果英文表达有困难,可以使用翻译工具。
第 4 步:通过自动化 CI 检查
提交 PR 后 CI 会自动运行,且只有在 CI 通过、PR 获得绿色勾之后,才经常才会进入第一轮人工评审。如果遇到 CI 失败不知道如何处理,可以直接在 PR 中留言求助。
CI 的具体内容定义在 .github/workflows/ci.yml 中,从源码与配置可以确认以下检查项:
- 测试矩阵:
cargo test --workspace在ubuntu-latest(64 位 + 32 位 i686 目标)与windows-latest上并行运行;全局设置RUSTFLAGS: "-Dwarnings",即任何警告都会导致编译失败。矩阵失败时 CI 还会上传tests/store/下的测试产物与tests/store/report.html测试报告作为工件; - 静态检查 job(checks):
cargo clippy --workspace --all-targets --all-features、--no-default-features各一遍,cargo fmt --check --all(rustfmt),以及cargo doc --workspace --no-deps --document-private-items——最后一条意味着所有 Rust 文档注释(包括私有项)都必须能无错生成,这直接呼应下文"新 Rust 类型必须有文档注释"的要求; - 文档 job(docs):
cargo docit compile --format website --deny-warnings与--format pdf,即官网文档与 PDF 文档都必须无警告地编译通过(docit别名定义在 .cargo/config.toml); - 最低 Rust 版本 job(min-version):用 1.92.0 工具链跑
cargo check --workspace,与 Cargo.toml 中rust-version = "1.92"的声明一致; - fuzz 与 miri job:构建 tests/fuzz 下的模糊测试器;用 Miri 检查 unsafe 代码。
代码风格层面,Cargo.toml 的 [workspace.lints.clippy] 段启用了约 30 条 warn 级 clippy 规则(如 get_unwrap、implicit_clone、redundant_type_annotations 等),配合 CI 的 -Dwarnings,实际上等价于强制。此外 clippy.toml 与 rustfmt.toml 进一步约束了风格细节。
第 5 步:维护者评审
维护者在评审中会检查代码质量、潜在 bug、以及贡献是否与之前的讨论一致。文档鼓励:如果你觉得某条评审评论遗漏了什么或不够准确,请大胆质疑("please challenge it!")——这体现了该项目对协作沟通的预期。
第 6 步:合并并进入下一个版本的 Changelog
评审通过后,PR 将被合并并随 Typst 的下一个版本发布;贡献者会出现在 changelog 中。从 docs/content/changelog/index.typ 的源码可以看到,每个版本的 changelog 页面在 HTML 目标下会渲染一个 Contributors 章节,通过 slot 机制列出该版本区间内的所有贡献者——这就是"你的名字进入 changelog"的具体实现位置。
三、什么样的 PR 是一个"好 PR"
文档给出了"好 PR"的五条具体特征,每一条都可以对照仓库结构落地:
- 实现单一、自包含、且事先讨论过的功能或 bug 修复。
- 改动尽量小。 增加/修改的代码与接口尽量少;若必须改动较大范围的抽象,应在实现过程中持续讨论。
- 在合适处添加测试(视觉/HTML 测试需附参考输出),详见 tests/README.md。
- 所有新的 Rust 类型都带文档注释。 这一点由 CI 的
cargo doc --document-private-items步骤强制保障。 - 所有新的 Typst 定义(元素/函数)附带简要文档,理想情况是配一个约 5–10 行、宽度小于 38 列的简洁示例(可参考现有示例的写法)。文档注明这部分"不是太关键",因为发布前官方会统一润色文档。
关于测试的补充要求,tests/README.md 提供了完整细节,这里给出与贡献直接相关的要点:
- 集成测试输入位于
tests/suite/(按 crate 平行组织),参考输出位于tests/ref/;每个测试以--- {name} {attr}+ ---声明,例如 tests/suite/math/attach.typ 中的--- math-attach-mixed paged html ---;测试名必须全局唯一; - 属性至少包含一个测试目标:
eval(纯脚本断言,不产生输出)、paged(分页输出,含render/pdf/pdftags/svg阶段)、html(对比参考 HTML 文件)等;辅助属性如large(允许参考图超过 20 KiB,应谨慎使用)、empty(声明不应产生非平凡输出); - 本地运行用仓库内置的 cargo 别名
cargo testit(定义于 .cargo/config.toml,其本质是运行test-wrapper包),例如:cargo testit math:名称中含 "math" 的所有测试;cargo testit --exact math-attach-mixed:精确名称匹配;cargo testit --stages html,pdftags:只跑部分阶段以加快速度;cargo testit --exact my-test-name --update:更新/生成某测试的参考输出;
- 测试分三类:断言类(调用
test或assert.eq校验脚本行为)、诊断类(用// Error: 2-7 ...行内注解校验诊断信息与代码跨度)、输出类(视觉渲染图、参考 HTML、PDF 标签树对比)。文档建议:能在断言与参考图之间二选一时,优先断言,便于独立理解且避免图片膨胀。
四、评审周期与项目愿景
评审周期(Review cycle): 贡献者在评审过程中长期无响应是可以理解的,但维护者会在一段时间后将"等待贡献者响应"的 PR 关闭,以免 PR 追踪区堆满陈旧 PR。同理,维护者评审也需要时间——如果超过约一个月没有回应,可以 ping 一位维护者。
愿景契合度(What fits with the vision): Typst 虽然是开源项目,同时也是一家创业公司的产品。技术贡献始终基于其技术价值来判断;但作为公司,其短期优先级会变化(有时不预先通知),这会影响设计与决策流程以及开发/评审的速度;某些提案若直接影响公司存续,会从商业角度审慎考虑。文档给出的核心判断问题是:"这个想法是否有助于让 Typst 成为首要的技术排版应用?" 若答案是肯定的,这个想法大概率适合 Typst;拿不准时,先来讨论。
五、贡献前自检清单
综合 CONTRIBUTING.md 与配套文档,提交 PR 前可以对照以下清单:
- 改动是否单一、自包含,且事先与社区讨论过?
- 新增 Rust 类型是否都有文档注释(CI 会执行
cargo doc --document-private-items)? - 是否添加了合适的测试;视觉/HTML 测试是否用
cargo testit --exact <name> --update生成了参考输出? - 新 Typst 定义是否附上了简短文档与 5–10 行、<38 列的示例?
- 本地
cargo fmt --check --all、cargo clippy --workspace是否干净(CI 以-Dwarnings运行)? - PR 描述是否用自己的话写明了技术理由与用户视角的使用方式,并链接了相关 issue?
附:关键文件索引
| 文件 | 说明 |
|---|---|
| CONTRIBUTING.md | 官方贡献流程(本文主文档) |
| docs/dev/architecture.md | 编译器架构与 crate 目录总览 |
| tests/README.md | 集成测试套件:目录结构、testit 命令、参考图策略 |
| .github/workflows/ci.yml | PR 触发的 CI:测试矩阵、checks、docs、min-version、fuzz、miri |
| Cargo.toml | 工作区定义:版本 0.15.1、最低 Rust 1.92、clippy 规则集 |
| .cargo/config.toml | cargo testit / cargo docit 别名定义 |
| docs/content/changelog/index.typ | 版本 changelog 与贡献者名单的生成逻辑 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00