Архитектура линтера
Статья впервые опубликована на leaysgur.github.io/posts автором @leaysgur.
apps/oxlint
Бинарник oxlint получается сборкой main.rs из крейта apps/oxlint.
Здесь разбираются аргументы командной строки и запускается LintRunner.
crates/oxc_diagnostics
LintService передаёт в oxc_diagnostics отправитель mpsc::channel, чтобы принимать результаты линтинга.
Сообщения форматируются и показываются; форматирование делает крейт miette.
crates/oxc_linter
От LintService:
- держит
self.runtimeкакArc<Runtime> - в
Runtimeхранятся пути для линтинга - при запуске параллельно (через
rayon) обходит пути изRuntime - в конце отправляет
None
Runtime: process_path()
- по пути определяет расширение и содержимое
- поддерживает
.[m|c]?[j|t]sи.[j|t]sx - для
.vue,.astro,.svelte— исключения с частичной поддержкой блоковscript - обрабатывает источники JavaScript и TypeScript
- выполняет линтинг и отправляет результат в
DiagnosticService
Runtime: process_source()
- прогоняет исходник через парсер в AST
- из
SemanticBuilderсоздаётLintContextи передаёт вLinter
crates/oxc_semantic: SemanticBuilder
SemanticBuilder собирает семантику из исходного кода.
source_text: исходный текстnodes: узлы ASTclasses: классыscopes: области видимостиtrivias: комментарииjsdoc: JSDoc- и др.
При сборке возвращается SemanticBuilderReturn, но в LintContext передаётся только Semantic.
crates/oxc_linter: LintContext
Контекст линтинга; ядро — Semantic. Есть геттеры для данных и методы вроде diagnostic() для сообщений о проблемах.
crates/oxc_linter: Linter
Центральная точка — run() этого Linter.
- правила для целевого кода хранятся в
self.rules - каждое правило по трейту может реализовать три вида обработки
- эти три варианта выполняются последовательно
Список уже реализованных правил:
При добавлении правил этот список нужно обновлять.
Пример линтера
В репозитории есть минимальный пример конфигурации линтера.