refactor: migrate linting to Oxlint
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
2026-06-11-quality-gates.md: e1af110387936d644208dc1829fde4a4fdf8a3f9
|
||||
2026-06-11-quality-gates.zh.md: a4e57b7a08ecf20babb33b55d8c94414df1b10b1
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-11-quality-gates.md
|
||||
2026-06-11-quality-gates.md: 320ef1ac6a6a47cd5bf9c458f0c918ed3cccf594
|
||||
2026-06-11-quality-gates.zh.md: bf28b09e027eebdcf18aecde2e76e782667d72e6
|
||||
|
||||
@@ -15,11 +15,11 @@ This codebase is developed primarily by coding agents. Agents follow enforced ga
|
||||
Every mechanically checkable AGENTS.md promise gets a command that exits non-zero. CI invokes the exhaustive set, while Git hooks reserve their latency budget for cheap local defects:
|
||||
|
||||
- Max-strict TypeScript (`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, …); examples, tests, and scripts typecheck in CI via the root no-emit `tsconfig.json` while package/vendor code stays behind its own project-reference boundary.
|
||||
- ESLint strict-type-checked + @stylistic (the house style, enforced), including file-local duplicated logic checks; vendored code excluded.
|
||||
- [Oxlint](2026-07-29-oxlint-linter.md) with type-aware TypeScript rules plus the @stylistic and SonarJS compatibility plugins, enforcing the house style and file-local duplicated-logic checks; vendored code excluded.
|
||||
- jscpd detects cross-file clones in package production TypeScript and repository scripts; narrow source-range exceptions document deliberately parallel implementations.
|
||||
- Per-file 100% coverage on `packages/*/*/src` (v8); unreachable defensive guards carry `/* v8 ignore */ ` with stated reasons instead of deletion.
|
||||
- knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM), and a NodeNext consumer typecheck for built package declarations.
|
||||
- lefthook pre-commit fixes staged lint, rejects staged whitespace, and checks the vendor manifest; pre-push runs incremental typecheck. CI runs the full matrix on node 22.19/24/26 plus built application smokes for the Headless, TUI, ACP, JSON-RPC, workflow, and code-runtime entry paths.
|
||||
- lefthook pre-commit applies staged Oxlint fixes, rejects staged whitespace, and checks the vendor manifest; pre-push runs incremental typecheck. CI runs the full matrix on node 22.19/24/26 plus built application smokes for the Headless, TUI, ACP, JSON-RPC, workflow, and code-runtime entry paths.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@ Status: implemented
|
||||
每条可机械检查的 AGENTS.md 承诺都有一个以非零状态退出的命令。CI 执行完整集合,而 Git 钩子将延迟预算留给可低成本发现的本地缺陷:
|
||||
|
||||
- 最严格的 TypeScript 配置(`noUncheckedIndexedAccess`、`exactOptionalPropertyTypes` 等);示例、测试和脚本通过根目录的 no-emit `tsconfig.json` 在 CI 中进行类型检查,而包(package)/vendor 代码保持在各自 project-reference 边界之后。
|
||||
- ESLint strict-type-checked + @stylistic(作为强制执行的统一代码风格),包括文件内重复逻辑检查;vendor 代码排除在外。
|
||||
- [Oxlint](2026-07-29-oxlint-linter.md) 配合类型感知的 TypeScript 规则以及 @stylistic 和 SonarJS 兼容插件,强制执行统一代码风格和文件内重复逻辑检查;vendor 代码排除在外。
|
||||
- jscpd 检测包的生产 TypeScript 代码与仓库脚本中的跨文件克隆;窄范围的源码区间例外用于记录有意为之的并行实现。
|
||||
- `packages/*/*/src` 下按文件 100% 覆盖率(v8);不可达的防御性守卫使用 `/* v8 ignore */ ` 并注明理由,而非删除。
|
||||
- knip(死代码/依赖)、publint(包的正确性)、workspace 约束(workspace 规则:private、cordis peer+dev、统一版本、ESM),以及对构建出的包声明文件进行 NodeNext 消费方类型检查。
|
||||
- lefthook pre-commit 修复已暂存文件的 lint 问题、拒绝已暂存的空白问题并检查 vendor manifest;pre-push 运行增量类型检查。CI 在 Node 22.19/24/26 上运行完整矩阵,并对 Headless、TUI、ACP(Agent Client Protocol)、JSON-RPC、工作流和代码运行时入口路径执行已构建应用的冒烟测试。
|
||||
- lefthook pre-commit 对暂存文件应用 Oxlint 修复、拒绝已暂存的空白问题并检查 vendor manifest;pre-push 运行增量类型检查。CI 在 Node 22.19/24/26 上运行完整矩阵,并对 Headless、TUI、ACP(Agent Client Protocol)、JSON-RPC、工作流和代码运行时入口路径执行已构建应用的冒烟测试。
|
||||
|
||||
## 后果
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-06-parallel-pre-push-gates.md
|
||||
2026-07-06-parallel-pre-push-gates.md: d86642b7feb82908ec792db0c6a3da403cfc79fd
|
||||
2026-07-06-parallel-pre-push-gates.zh.md: 0425cf1a01b56604a07be366dd46d3920c5fb487
|
||||
2026-07-06-parallel-pre-push-gates.md: 538e52c5318fb6d4eab2e8786513a08c1ff0ec55
|
||||
2026-07-06-parallel-pre-push-gates.zh.md: e93eec8757c20c8154703d9bdfd2f0c805e6a26c
|
||||
|
||||
@@ -14,7 +14,7 @@ Aggregate jobs such as documentation synchronization hide long sequential chains
|
||||
|
||||
[scripts/run-gates.ts](../../../../scripts/run-gates.ts) owns the bounded scheduler used by CI, `doc-sync`, and the opt-in `check:all` command. It expands named modes into leaf gates, rejects empty or ambiguous dependency graphs before starting a child, respects artifact dependencies, buffers attributable output, reports exit and signal outcomes independently, and accepts `DSH_GATE_CONCURRENCY` when a caller needs a different worker bound.
|
||||
|
||||
The Node 24 consumer job is one seven-gate mode rather than a shell-owned process pool. Its default worker count equals its gate count while dependencies control readiness: `publint` precedes built-package invariant validation, and snapshot replay, NodeNext type checks, built-bin smokes, and lint wait for that validation. Lint waits because the invariant verifier temporarily stages package views that ESLint must not traverse; source compatibility checks can overlap the validation chain.
|
||||
The Node 24 consumer job is one seven-gate mode rather than a shell-owned process pool. Its default worker count equals its gate count while dependencies control readiness: `publint` precedes built-package invariant validation, and snapshot replay, NodeNext type checks, built-bin smokes, and lint wait for that validation. Lint waits because the invariant verifier temporarily stages package views that the linter must not traverse; source compatibility checks can overlap the validation chain.
|
||||
|
||||
[scripts/publint-all.ts](../../../../scripts/publint-all.ts) discovers packages from `packages/<group>/<pkg>` and runs `publint` with a worker pool sized from `availableParallelism()`. `DSH_PUBLINT_CONCURRENCY` can cap or raise the worker count for local machines and CI runners with different resource profiles. Results are buffered per package and printed in deterministic package order, so parallel execution does not scramble each package's log block.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Status: implemented
|
||||
|
||||
[scripts/run-gates.ts](../../../../scripts/run-gates.ts) 拥有 CI、`doc-sync` 和选择启用的 `check:all` 命令所使用的有界调度器。它将具名模式展开为叶子门禁,在启动子进程前拒绝空的或有歧义的依赖图,遵守产物依赖,缓冲可归因的输出,分别报告退出结果与信号结果,并在调用方需要不同 worker 上限时接受 `DSH_GATE_CONCURRENCY`。
|
||||
|
||||
Node 24 消费方 job 采用单个包含七道门禁的模式,而非由 shell 管理的进程池。其默认 worker 数等于门禁数,但门禁是否就绪由依赖关系控制:`publint` 先于已构建包不变式验证运行,快照回放、NodeNext 类型检查、built-bin 冒烟测试和 lint 则等待该验证完成。lint 之所以等待,是因为不变式验证器会临时暂存包视图,而 ESLint 不得遍历这些视图;源码兼容性检查可以与这条验证链重叠运行。
|
||||
Node 24 消费方 job 采用单个包含七道门禁的模式,而非由 shell 管理的进程池。其默认 worker 数等于门禁数,但门禁是否就绪由依赖关系控制:`publint` 先于已构建包不变式验证运行,快照回放、NodeNext 类型检查、built-bin 冒烟测试和 lint 则等待该验证完成。lint 之所以等待,是因为不变式验证器会临时暂存包视图,而 linter 不得遍历这些视图;源码兼容性检查可以与这条验证链重叠运行。
|
||||
|
||||
[scripts/publint-all.ts](../../../../scripts/publint-all.ts) 从 `packages/<group>/<pkg>` 发现包,并以根据 `availableParallelism()` 确定大小的 worker 池运行 `publint`。`DSH_PUBLINT_CONCURRENCY` 可以针对资源配置不同的本地机器和 CI runner 限制或提高 worker 数量。结果按包缓冲,并按确定性的包顺序打印,因此并行执行不会打乱各包的日志块。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md
|
||||
2026-07-22-evidence-based-larger-hosted-runners.md: 67fc7ded5cffc6a219665f135a4c9e1cc4752691
|
||||
2026-07-22-evidence-based-larger-hosted-runners.zh.md: 71c5c067361b57fab5aae9e9ffa3850a30609db3
|
||||
2026-07-22-evidence-based-larger-hosted-runners.md: 983d5520bd73fc3cf82c37bf0d4a9ff1c6e6f51c
|
||||
2026-07-22-evidence-based-larger-hosted-runners.zh.md: a86dcf2c60d7b950e7557e84ef6993e712a2ce09
|
||||
|
||||
@@ -18,7 +18,7 @@ The required primary path depends on those enterprise pools. Standard GitHub-hos
|
||||
|
||||
The former gate-level and coarse primary shard jobs are absent from the workflow. Their static, lint, coverage, snapshot, and scenario shard selectors are also absent from the repository, so an unused diagnostic path cannot preserve a second CI architecture.
|
||||
|
||||
Linux primary work uses three independent 32-core jobs. Coverage runs alone with its own worker bound, and the static scheduler runs alone so its result has no post-build consumer tail. After static gates finish, that job publishes its emitted `apps/*/lib`, `packages/*/*/lib`, and `vendor/*/lib` tree as a run-scoped artifact. The third job restores that exact tree, then starts lint, Node 24 runtime compatibility, build-backed snapshots, and all artifact consumers without repeating the build. Generated NodeNext consumer directories are excluded from ESLint discovery because the artifact check removes them while these processes overlap. The pnpm store and ESLint cache are restored without putting cache uploads on the pull-request critical path. Performance reports use each job's `startedAt` to `completedAt` interval; runner queue delay is capacity evidence, not repository execution time.
|
||||
Linux primary work uses three independent 32-core jobs. Coverage runs alone with its own worker bound, and the static scheduler runs alone so its result has no post-build consumer tail. After static gates finish, that job publishes its emitted `apps/*/lib`, `packages/*/*/lib`, and `vendor/*/lib` tree as a run-scoped artifact. The third job restores that exact tree, then starts lint, Node 24 runtime compatibility, build-backed snapshots, and all artifact consumers without repeating the build. Generated NodeNext consumer directories are excluded from Oxlint discovery because the artifact check removes them while these processes overlap. The pnpm store is restored without putting cache uploads on the pull-request critical path; Oxlint has no repository-managed result cache. Performance reports use each job's `startedAt` to `completedAt` interval; runner queue delay is capacity evidence, not repository execution time.
|
||||
|
||||
The gate dependencies remain explicit. Coverage consumes source and does not wait for build. Documentation typechecking builds its complete project-reference graph once. Snapshot replay and publication consumers wait for emitted output, while Node-version compatibility jobs exercise runtime-sensitive source loading without repeating the primary source-graph typecheck. PTY and subprocess suites keep their bounded inner concurrency rather than inheriting the runner's core count.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Status: implemented
|
||||
|
||||
原有的门禁级和粗粒度主流程分片作业已从工作流中移除。相应的静态、lint、覆盖率、快照和场景分片选择器也已从仓库中移除,因此未使用的诊断路径无法继续维系第二套 CI 架构。
|
||||
|
||||
Linux 主流程使用 3 个相互独立的 32 核作业。覆盖率单独运行,并设有自己的工作线程上限;静态调度器也单独运行,因此构建后的消费方不会拖延其结果。静态门禁完成后,该作业将其生成的 `apps/*/lib`、`packages/*/*/lib` 和 `vendor/*/lib` 目录树作为仅供本次运行使用的产物发布。第三个作业恢复完全相同的目录树,再让 lint、Node 24 运行时兼容性、依赖构建产物的快照和所有产物消费方基于构建完成后的工作树启动,而不重复构建。生成的 NodeNext 消费方目录不会纳入 ESLint 的文件发现范围,因为这些进程重叠执行时,产物检查会删除这些目录。pnpm store 和 ESLint 缓存会得到恢复,但缓存上传不会进入拉取请求关键路径。性能报告采用每个作业从 `startedAt` 到 `completedAt` 的区间;运行器排队延迟是容量证据,而非仓库执行时间。
|
||||
Linux 主流程使用 3 个相互独立的 32 核作业。覆盖率单独运行,并设有自己的工作线程上限;静态调度器也单独运行,因此构建后的消费方不会拖延其结果。静态门禁完成后,该作业将其生成的 `apps/*/lib`、`packages/*/*/lib` 和 `vendor/*/lib` 目录树作为仅供本次运行使用的产物发布。第三个作业恢复完全相同的目录树,再让 lint、Node 24 运行时兼容性、依赖构建产物的快照和所有产物消费方基于构建完成后的工作树启动,而不重复构建。生成的 NodeNext 消费方目录不会纳入 Oxlint 的文件发现范围,因为这些进程重叠执行时,产物检查会删除这些目录。pnpm store 会得到恢复,但缓存上传不会进入拉取请求关键路径;Oxlint 没有由仓库管理的结果缓存。性能报告采用每个作业从 `startedAt` 到 `completedAt` 的区间;运行器排队延迟是容量证据,而非仓库执行时间。
|
||||
|
||||
门禁依赖关系保持显式。覆盖率消费源码,不等待构建。文档类型检查只构建一次完整的 project-reference 图。快照回放和发布消费方等待生成的输出,而 Node 版本兼容性作业会验证对运行时敏感的源码加载,且不重复主源码项目图的类型检查。PTY 和子进程套件继续使用自身有界的内部并发,不继承运行器的核心数。
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
2026-07-22-fast-local-git-hooks.md: a07af1cd424c86f7fa80ea946cd5012362cc66eb
|
||||
2026-07-22-fast-local-git-hooks.zh.md: 78d4ea8980476609a9140737a75152eba123b308
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-22-fast-local-git-hooks.md
|
||||
2026-07-22-fast-local-git-hooks.md: cfb8e5f6bbab5ba6b2243b310c1b2ba12039df68
|
||||
2026-07-22-fast-local-git-hooks.zh.md: b78a82371124d1503f95bd3287241b68aaa3bb17
|
||||
|
||||
@@ -12,7 +12,7 @@ Fast hooks still need to reject cheap, high-confidence defects before work leave
|
||||
|
||||
## Decision
|
||||
|
||||
[lefthook.yml](../../../../lefthook.yml) keeps both hooks as bounded local checkpoints. Pre-commit runs sequentially: ESLint fixes and re-stages changed JavaScript and TypeScript, `git diff --cached --check` rejects staged whitespace errors, and the vendor manifest guard checks vendored-source metadata. Pre-push invokes the repository TypeScript binary directly in incremental build mode.
|
||||
[lefthook.yml](../../../../lefthook.yml) keeps both hooks as bounded local checkpoints. Pre-commit runs sequentially: [Oxlint](2026-07-29-oxlint-linter.md) fixes and re-stages changed JavaScript and TypeScript, `git diff --cached --check` rejects staged whitespace errors, and the vendor manifest guard checks vendored-source metadata. Pre-push invokes the repository TypeScript binary directly in incremental build mode.
|
||||
|
||||
Neither hook runs tests, snapshots, documentation checks, builds, hygiene, or the gate scheduler. The opt-in `check:all` package script selects the `check-all` scheduler inventory in [scripts/run-gates.ts](../../../../scripts/run-gates.ts) independently of the hooks; it is a contributor command, not an agent instruction.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ agent(智能体)已经会运行能够覆盖自身改动的测试和检查,
|
||||
|
||||
## 决策
|
||||
|
||||
[lefthook.yml](../../../../lefthook.yml) 将两个钩子都保留为有界的本地检查点。Pre-commit 按顺序运行:ESLint 修复改动过的 JavaScript 和 TypeScript 文件并重新暂存,`git diff --cached --check` 拒绝暂存 diff 中的空白错误,vendor manifest(元数据清单)守卫检查 vendor 源码元数据。Pre-push 直接调用仓库内的 TypeScript 二进制,并启用增量构建模式。
|
||||
[lefthook.yml](../../../../lefthook.yml) 将两个钩子都保留为有界的本地检查点。Pre-commit 按顺序运行:[Oxlint](2026-07-29-oxlint-linter.md) 修复改动过的 JavaScript 和 TypeScript 文件并重新暂存,`git diff --cached --check` 拒绝暂存 diff 中的空白错误,vendor manifest(元数据清单)守卫检查 vendor 源码元数据。Pre-push 直接调用仓库内的 TypeScript 二进制,并启用增量构建模式。
|
||||
|
||||
两个钩子都不运行测试、快照、文档检查、构建、`hygiene` 或门禁调度器。可选运行的 `check:all` 包脚本独立于这些钩子,从 [scripts/run-gates.ts](../../../../scripts/run-gates.ts) 中选择 `check-all` 调度器清单;它是贡献者命令,而非对 agent 的指令。
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-29-oxlint-linter.md
|
||||
2026-07-29-oxlint-linter.md: 5bf7bd417860b1bd9eb9fd421d871fbc4d5256ea
|
||||
2026-07-29-oxlint-linter.zh.md: 56bdbdb598f13bf69049dc818fb5129fa6a987e2
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: Oxlint as the repository linter
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-29-oxlint-linter.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The repository needs type-aware TypeScript correctness rules, consistent formatting, and file-local duplicate-logic checks across its owned source. ESLint supplied those checks through a JavaScript parser, a project service, and multiple plugins, but a clean lint run spent about one minute on the local migration baseline and required an 8 GiB Node heap, CI result caches, and separately tuned ESLint concurrency.
|
||||
|
||||
A faster runner cannot justify losing rules. The migration must preserve the strict type-checked preset, repository overrides, inline suppressions, @stylistic fixes, SonarJS checks, host/client TypeScript separation, and the vendor exclusion.
|
||||
|
||||
## Decision
|
||||
|
||||
The root [`.oxlintrc.json`](../../../../.oxlintrc.json) is the lint configuration, and `oxlint` is the only lint command used by package scripts, the gate scheduler, CI, and lefthook. The direct `eslint` and `typescript-eslint` development dependencies and `eslint.config.mjs` are absent.
|
||||
|
||||
`options.typeAware` enables `oxlint-tsgolint`. The configuration explicitly carries the migrated strict-type-checked rules and repository overrides instead of enabling broad Oxlint categories whose contents may change. `typescript/no-unnecessary-condition` remains enabled from Oxlint's nursery set because it was an enforced repository rule before migration. Oxlint discovers each file's TypeScript project; the existing package projects and separate host/client aggregates remain the source of type context.
|
||||
|
||||
Oxlint's JavaScript-plugin compatibility layer runs `@stylistic/eslint-plugin` and `eslint-plugin-sonarjs` so the existing formatting and file-local duplicate-logic rules remain enforced. These packages retain their ESLint peer dependency transitively, but the repository neither configures nor invokes the ESLint runner. Owned-source suppressions use `oxlint-*` directives and the `typescript/*` namespace; vendored sources keep their upstream directives because Oxlint excludes `vendor/**`.
|
||||
|
||||
CI does not restore or save a lint-result cache. `DSH_OXLINT_THREADS` optionally bounds Oxlint's native worker count in the gate scheduler for shared or benchmark runners; ordinary local runs use Oxlint's default. Pre-commit applies safe Oxlint fixes to staged JavaScript and TypeScript files and re-stages them through lefthook.
|
||||
|
||||
## Verification
|
||||
|
||||
The migrated configuration reports the same clean owned-source baseline after resolving two analyzer differences: one redundant test assertion was removed, while one structural cast required by `tsc` carries a narrow Oxlint suppression. The repository lint command exercises type-aware rules and both JavaScript compatibility plugins. Focused gate-scheduler execution covers the explicit thread-bound path, while typecheck confirms that migration-driven source edits preserve the TypeScript programs.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Run Oxlint before a reduced ESLint fallback.** This is the recommended incremental path when required rules are unsupported, but every enforced repository rule is available through Oxlint's native rules, nursery rule, or JavaScript-plugin compatibility layer. Keeping both runners would preserve the slower program setup and two configurations without adding a check.
|
||||
|
||||
**Drop @stylistic or SonarJS rules that are not native.** This would remove dependencies but weaken the mechanical quality contract. The compatibility layer preserves those rules until native replacements can be evaluated as a separate decision.
|
||||
|
||||
**Replace @stylistic with Oxfmt during the migration.** A formatter migration would change output beyond the lint-engine boundary and create a repository-wide formatting diff. Keeping the established rules makes this change reviewable and leaves formatter selection independent.
|
||||
|
||||
## Consequences
|
||||
|
||||
Local migration measurements reduced a clean type-aware lint run from about 61 seconds to about 8 seconds without a result cache. The exact ratio is host-dependent and is not a performance guarantee.
|
||||
|
||||
Type-aware diagnostics now come from the TypeScript Go analyzer bundled through `oxlint-tsgolint`, so edge-case inference can differ from typescript-eslint even when `tsc` accepts the same program. Lint and typecheck remain separate required evidence.
|
||||
|
||||
The JavaScript-plugin compatibility API is an additional boundary to maintain, and its peer graph still includes ESLint packages. The executable lint path, configuration ownership, cache policy, worker control, and inline directives are nevertheless Oxlint-only.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Agent Note: 使用 Oxlint 作为仓库 linter
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-29-oxlint-linter.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
仓库的自有源码需要类型感知的 TypeScript 正确性规则、一致的格式,以及文件内重复逻辑检查。ESLint 通过 JavaScript 解析器、项目服务和多个插件提供这些检查,但在本地迁移基线上,一次无问题的 lint 运行约需 1 分钟,并且需要 8 GiB Node 堆、CI 结果缓存和单独调优的 ESLint 并发度。
|
||||
|
||||
不能以提高运行速度为由丢失规则。迁移必须保留严格类型检查预设、仓库覆盖配置、内联抑制指令、@stylistic 修复、SonarJS 检查、host/client TypeScript 隔离和 vendor 排除规则。
|
||||
|
||||
## 决策
|
||||
|
||||
根目录的 [`.oxlintrc.json`](../../../../.oxlintrc.json) 是 lint 配置;包(package)脚本、门禁调度器、CI 和 lefthook 使用的唯一 lint 命令均为 `oxlint`。直接的 `eslint` 和 `typescript-eslint` 开发依赖以及 `eslint.config.mjs` 均不存在。
|
||||
|
||||
`options.typeAware` 启用 `oxlint-tsgolint`。该配置显式载入迁移后的严格类型检查规则和仓库覆盖配置,而不启用内容可能发生变化的 Oxlint 宽泛类别。`typescript/no-unnecessary-condition` 仍从 Oxlint 的 nursery 规则集中启用,因为它在迁移前就是仓库强制执行的规则。Oxlint 会发现每个文件所属的 TypeScript 项目;现有包项目及相互独立的 host/client 聚合项目仍是类型上下文的真源。
|
||||
|
||||
Oxlint 的 JavaScript 插件兼容层运行 `@stylistic/eslint-plugin` 和 `eslint-plugin-sonarjs`,从而继续强制执行现有的格式和文件内重复逻辑规则。这些包的 ESLint 对等依赖(peer dependency)仍会作为传递依赖保留,但仓库既不配置也不调用 ESLint 运行器。自有源码中的抑制指令使用 `oxlint-*` 指令和 `typescript/*` 命名空间;vendor 源码保留其上游指令,因为 Oxlint 会排除 `vendor/**`。
|
||||
|
||||
CI 不恢复或保存 lint 结果缓存。`DSH_OXLINT_THREADS` 可以在门禁调度器中限制 Oxlint 的原生工作线程数,供共享运行器或基准测试运行器使用;普通本地运行使用 Oxlint 默认值。Pre-commit 对暂存的 JavaScript 和 TypeScript 文件应用安全的 Oxlint 修复,并由 lefthook 重新暂存这些文件。
|
||||
|
||||
## 验证
|
||||
|
||||
解决两处分析器差异后,迁移后的配置报告与迁移前一致的自有源码无问题基线:移除了一项冗余测试断言,而 `tsc` 要求的一处结构性类型转换使用了窄范围的 Oxlint 抑制指令。仓库 lint 命令会运行类型感知规则和两个 JavaScript 兼容插件。针对门禁调度器的聚焦执行覆盖显式线程上限路径,类型检查则确认迁移引发的源码改动没有破坏 TypeScript 程序。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**先运行 Oxlint,再使用精简的 ESLint 回退。** 当必要规则尚未得到支持时,这是推荐的渐进迁移路径;但仓库强制执行的所有规则均可通过 Oxlint 原生规则、nursery 规则或 JavaScript 插件兼容层获得。保留两个运行器会继续承担较慢的程序初始化和两套配置,却不会增加任何检查。
|
||||
|
||||
**移除尚无原生实现的 @stylistic 或 SonarJS 规则。** 这会移除依赖,但也会削弱机械质量契约。兼容层会保留这些规则,直到能够通过单独决策评估原生替代规则。
|
||||
|
||||
**迁移期间用 Oxfmt 替换 @stylistic。** 格式化器迁移会产生超出 lint 引擎边界的输出变化,并带来全仓库格式 diff。保留既有规则可使本次变更便于评审,并让格式化器选择保持独立。
|
||||
|
||||
## 结果
|
||||
|
||||
本地迁移测量显示,不使用结果缓存时,一次无问题的类型感知 lint 运行从约 61 秒缩短至约 8 秒。确切比例因主机而异,不构成性能保证。
|
||||
|
||||
类型感知诊断现在来自通过 `oxlint-tsgolint` 捆绑的 TypeScript Go 分析器,因此即使 `tsc` 接受同一程序,边界场景下的类型推断也可能与 typescript-eslint 不同。lint 与类型检查仍是两项相互独立的必要证据。
|
||||
|
||||
JavaScript 插件兼容 API 是需要维护的额外边界,其对等依赖图中仍包含 ESLint 包。不过,可执行 lint 路径、配置所有权、缓存政策、工作线程控制和内联指令已全部只使用 Oxlint。
|
||||
Reference in New Issue
Block a user