Linter 架构
本文来自 @leaysgur,首发于 leaysgur.github.io/posts。
apps/oxlint
可执行文件来自 apps/oxlint crate 的 main.rs:
入口解析 CLI 参数后交由 LintRunner。
crates/oxc_diagnostics
LintService 将 mpsc::channel::Sender 交给 oxc_diagnostics,以异步接收 Lint 产物。
随后由 miette crate 格式化输出:
crates/oxc_linter
起点是 LintService:
- 内部持有
Arc<Runtime> Runtime列出待 Lint 的路径- 运行时使用
rayon并行迭代路径 - 结束后发送哨兵
None结束信道
Runtime::process_path()
- 根据扩展名和内容类型判断是否处理文件
.[m|c]?[j|t]s/.[j|t]sx等后缀.vue/.astro/.svelte仅对部分script块提供支持(实验性)- 对源码执行 Lint,并把结果交给
DiagnosticService
Runtime::process_source()
- 使用 Parser 将源码解析为 AST
- 经由
SemanticBuilder组装LintContext,再喂给Linter
crates/oxc_semantic::SemanticBuilder
SemanticBuilder 会根据源码推导符号、节点、类等语义信息:
包括但不限于:
source_text、nodes、classes、scopestrivias(注释)、jsdoc,等等
最终会产出 SemanticBuilderReturn,但交给 LintContext 的只有 Semantic:
crates/oxc_linter::LintContext
以 Semantic 为核心,对各种信息暴露 getter;diagnostic() 等方法供规则报错。
crates/oxc_linter::Linter
run() 是整条流水线核心:
- 在
self.rules持有待执行集合 - 每条规则按需实现 Trait 定义的若干触发回调
- 三类回调按顺序逐个执行
已实现规则请参阅:
新增规则后别忘了更新注册表。
最小 Demo
仓库提供从零搭建 Linter 的示例: