Skip to content

EslintからOxlintへの移行 - #337

Open
shinagawa-web wants to merge 17 commits into
mainfrom
chore/eslint-to-oxlint-migration
Open

shinagawa-web wants to merge 17 commits into
mainfrom
chore/eslint-to-oxlint-migration

Conversation

@shinagawa-web

@shinagawa-web shinagawa-web commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

概要

ESLint を Oxlint に置き換え。対象は eslint 設定を持つ 3 ワークスペース(pkgs/typed-api-spec / examples/misc / examples/vite-react-openapi)。lint の実行速度と依存の軽さを改善する。

変更内容

  • @oxlint/migrate.oxlintrc.json を自動生成し、元の eslint.config.js と突き合わせて手直し
    • strict-dependencies の depRules・no-unused-vars のオプション(varsIgnorePattern/argsIgnorePattern/caughtErrors)・*.t-test.ts override は元設定と一致させた
    • eslint-plugin-react-refresh は完全削除。only-export-components が oxlint にネイティブ実装されていたため、jsPlugin として残す想定から変更した
    • react-hooks も oxlint ネイティブ実装(react/rules-of-hooks 等)に寄せ、eslint-plugin-react-hooks の依存を削除
  • eslint.config.js と eslint 関連 devDependencies を削除、oxlint(^1.82.0)を追加
  • test:lint / lint npm scripts を oxlint . に変更
  • package-lock.json 更新

検証

lint が「エラーが出ない」だけでは、ルールが静かに落ちて検査が緩くなるケースを検出できないため、以下の観点で担保した(検証用スクリプト・ログはPR外のscratchで実施し、本PRには含めていない)。

  1. 有効ルール集合の静的突き合わせ: eslint側の実効設定(--print-config)と.oxlintrc.jsonを、severity・オプションの両方で比較。全ワークスペースで、下記「既知の非等価性」以外の差分なし
  2. lint対象ファイル集合の突き合わせ: eslintとoxlintの対象ファイル一覧を比較。全ワークスペースで一致(vite-react-openapiの1件差分は削除済みのeslint.config.js自体)
  3. 実行プローブ: 意図的な違反コードを置いて、jsPlugin(strict-dependencies)のロード・varsIgnorePattern*.t-test.ts override・ignorePatterns(dist/docs除外)・react-hooksルールが、それぞれ発火すべき側/してはいけない側の両方で期待通り動くことを実測
  4. 独立レビュー: 実装内容を知らない別エージェントに .oxlintrc.json と元 eslint.config.js の突き合わせをレビューさせ、ルールの取りこぼし・除外範囲の意図しない拡大が無いことを確認

既知の非等価性(移行不可)

oxlint に実装が無く、今回の移行では埋められないルール:

  • no-octal(全ワークスペース): oxlint 未実装。@oxlint/migrate --details が Unsupported として報告(strict mode下では8進数リテラルは構文エラーになるため実害は低い)
  • react-hooks/config / react-hooks/gating(examples/vite-react-openapi のみ): oxlint の react プラグインに対応ルールが無い(React Compiler の固定オプションのため設定検証自体が不要という oxlint 側の判断)

スコープ外

  • type-aware lint(oxlint-tsgolint)の導入 — 別PR
  • lint 未設定の pkgs/docsexamples/vite への oxlint 新規導入 — 別PR
  • CI で examples/vite-react-openapi の lint を実行するようにすること(現状も未実行のまま)
  • eslint には無かった oxlint 独自ルールの新規有効化 — 今回は等価性優先、上積みは別PR

oxlint-migration-plan.mdは開発中の検討用ドキュメントとして使い、移行完了に伴い削除しました(要点は上記に集約)。

Replace ESLint with Oxlint across pkgs/typed-api-spec, examples/misc,
and examples/vite-react-openapi for faster lint runs and lighter deps.

- Generate .oxlintrc.json via @oxlint/migrate and hand-review it against
  the original eslint.config.js (strict-dependencies depRules, t-test
  override, react-hooks/react-refresh coverage)
- Drop eslint-plugin-react-refresh entirely: its only-export-components
  rule is now natively implemented by oxlint
- Remove eslint.config.js and eslint-related devDependencies; add oxlint
- Update test:lint / lint npm scripts to run oxlint
@shinagawa-web
shinagawa-web force-pushed the chore/eslint-to-oxlint-migration branch from e813753 to 3122e27 Compare September 10, 2026 04:39
@shinagawa-web
shinagawa-web marked this pull request as draft September 10, 2026 04:40
Statically compares each workspace's enabled eslint rule set (severity +
hand-authored options) against its .oxlintrc.json, per the migration
plan's §A methodology. All four representative files show no unexplained
gaps or drift beyond documented oxlint tooling limitations
(no-octal, react/config, react/gating).
Compares each workspace's eslint-linted file list (from `eslint --format
json`, per the plan's step 1) against oxlint's actual target file list
(`oxlint --debug=files`), normalizing paths for a direct diff. All three
workspaces match exactly; vite-react-openapi's only difference is the
now-deleted eslint.config.js self-listing itself, not a coverage loss.
Scripts the plan's §C runtime checks (previously done ad hoc): places
temporary violating code to confirm rules fire where expected and stay
silent where excluded, covering behavior static config diffing can't
observe — jsPlugin loading (strict-dependencies), varsIgnorePattern,
the *.t-test.ts override, ignorePatterns scoping, and react-hooks —
across all three workspaces (pkgs/typed-api-spec, examples/misc,
examples/vite-react-openapi).

Uses trap-based cleanup and a final `git status --short` check so
leftover probe files can't slip into a commit. The ignorePatterns
"should be excluded" checks use a dedicated check_ignored() that
requires the probe file to actually exist on disk in addition to
oxlint's "No files found to lint" message — a bare empty grep match
can't distinguish "file was excluded" from "file was never created",
since oxlint prints the identical message and exit code for both.

All 14 checks pass.
Independent review (Codex) re-verified the eslint dump against
.oxlintrc.json from a clean clone and confirmed the migration is faithful.
Its one substantive finding — react-hooks/config and react-hooks/gating
have no oxlint equivalent and were undocumented — is now recorded in the
plan's known-unmigratable-rules table alongside no-octal.
Merging origin/main (zod 4.3.6->4.5.4, @types/supertest 7.2.0->7.2.1)
left package-lock.json internally inconsistent — `npm ci` failed with
"Missing: @eslint/js@9.39.5 from lock file" even though no package.json
references that package anymore. Reproduced the exact CI failure locally
(GitHub tests the PR's merge commit against main, not the branch tip in
isolation) and confirmed a plain `npm install` after the merge produces
a lockfile npm ci accepts cleanly.
@shinagawa-web

Copy link
Copy Markdown
Collaborator Author

@claude
SKILLは以下を参考にしてください。
https://raw.githubusercontent.com/shokai/agent-skills/refs/heads/main/plugins/sanity-review/skills/sanity-review/SKILL.md

PRのレビューをしてください

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Claude finished @shinagawa-web's task in 6m 8s —— View job


PRレビュー報告書(sanity-review)

  • PR: #337 Chore/eslint to oxlint migration
  • Branch: chore/eslint-to-oxlint-migration
  • Reviewed at: 2026-09-10 06:19 UTC
  • Reviewer: claude (Opus 4.7, GitHub Actions bot)

1. pull request 概要欄

サマリー(実装者の説明から抜粋)
  • Before: eslint 設定を持つ 3 ワークスペース (pkgs/typed-api-spec / examples/misc / examples/vite-react-openapi) が ESLint + @eslint/js + typescript-eslint (+ 一部で eslint-plugin-react-hooks / eslint-plugin-react-refresh / eslint-plugin-strict-dependencies) で lint されていた。
  • After: 各ワークスペースを oxlint + .oxlintrc.json に置換。test:lint / lint script は oxlint . に変更。eslint-plugin-strict-dependencies は jsPlugin として残置、eslint-plugin-react-refresh は oxlint ネイティブの react/only-export-components に寄せて完全削除。react-hooks もネイティブに寄せた。
  • 既知の非移行: no-octal(全ワークスペース)と react-hooks/config / react-hooks/gating(vite-react-openapi のみ)を意図的に未対応と宣言。
品質評価チェックリスト(手順2)
# 観点 判定 メモ
1 変更前の動作・問題が説明されているか ⚠️ 部分的 変更先の説明は詳しいが、「なぜ ESLint→oxlint を今やるか」は "lint の実行速度と依存の軽さ" の一文のみ。定量的なベースライン (現状のlint時間・依存ツリーサイズ) は無い。移行 PR としては十分だが、「今やる価値」の判断材料は薄い。
2 「問題→解決」ペア構造 ✅ OK 概要 / 変更内容 / 検証 / 既知の非等価性 / スコープ外、と各項目で対応する。
3 変更範囲が明確か ✅ OK 対象3ワークスペース/未対象2ワークスペース、type-aware は別PR、CI に vite-react-openapi の lint を追加するのも別PR、と明示。
4 実装者自身の理解が見えるか ✅ OK @oxlint/migrate の自動出力を鵜呑みにせず手直ししたこと、eslint-plugin-react-refresh を "plan 当初は jsPlugin で維持想定 → ネイティブに変更" と自分の判断過程を残している。等価性を最優先に置きスコープを制御している点も含めて自覚的。

2. 実装者の説明と実装の整合性(手順3)

読み合わせで重大な齟齬 3 件、軽微な齟齬 1 件を検出。

🔴 [B-1] PR概要欄の「検証」表と現在のブランチ内容が食い違う(PR body / commit history)

概要欄の検証表は §A / §B / §C / §D それぞれのコミット (5e04452, 858dc21, bbe0f03, 508aa25) を「commit」欄でリンクし、末尾で 全て tmp/ 配下にスクリプト・入力ダンプ・実行結果ログを残してあるので再現可能 と主張している。しかし現在のブランチ HEAD ではこれらは 4 件とも revert 済みe92efb6 / 9f66a69 / 0ec2f27 / 09f34eb)で、tmp/ ディレクトリは存在しない (ls tmp/ → No such file or directory)。

  • レビュアーが PR body を読んで再現しようとしても、branch 上には検証スクリプトが存在しない。
  • git log の各 commit を個別に checkout すれば見れるが、PR body の日本語は「残してある」と現在形で明言している。

意図が「実測は済んだので tmp/ は最終PRに残さない(scratch扱い)」なら、概要欄の文面は「再現可能」→「(scratchで実施済、tmp/はcommit保持せずrevert)」等に修正が必要。もしくは意図に反して revert してしまっている可能性がある(下記 B-2 と併せて検討すべき)。

🔴 [B-2] 「既知の非等価性は plan の『既知の移行不可ルール』表に詳細」と書かれているが該当表が plan doc に存在しない

概要欄:

oxlint に実装が無く、今回の移行では埋められないルール(oxlint-migration-plan.md の「既知の移行不可ルール」表に詳細)

しかし oxlint-migration-plan.md を grep すると no-octal の記載も、react-hooks/config / react-hooks/gating の記載も、「既知の移行不可ルール」というセクションも見つからない。「移行不可」の文字列は方法論の中の一般名詞として3件出るだけ。

原因は §D コミット (508aa25) の revert (09f34eb) で、この revert が oxlint-migration-plan.md から 8 行削除している (git show 09f34eb --stat で確認)。§D の内容は「plan doc に known-unmigratable-rules 表を追記する」もので、revert により追記が消滅している。

結果として、PR body が指し示している「表」がリポジトリ上に存在しない。ドキュメントとしての整合性が壊れている。

🔴 [B-3] @eslint/js が devDependencies に残っている(pkgs/typed-api-spec/package.json:23

plan doc の手順4は明確に:

package.json の devDependencies から削除: eslint@eslint/jstypescript-eslint

としているが、pkgs/typed-api-spec/package.json には "@eslint/js": "^9.39.5" が残っている。他2ワークスペースの package.json は削除済み。移行が不完全。

"devDependencies": {
    "@eslint/js": "^9.39.5",   // ← 削除漏れ
    ...
}

Fix this →

🟡 [B-4] strict-dependenciessrc/zod エントリで **/*.test.ts が重複(pkgs/typed-api-spec/.oxlintrc.json:128-136

元の eslint.config.js から重複 (...dRef に含まれる **/*.test.ts を再度末尾に追加) を忠実にコピーしている。等価性最優先方針では正しい挙動だが、oxlint 版は JSON 配列にベタ書きなので dRef スプレッドの歴史的経緯が消え、単に「同じ glob を2回書いた JSON」に見える。移行を機に片方を落としても機能等価。等価性優先の趣旨から、修正は「別PRで統合」でも可。


3. 命名・設計パターンの一貫性(手順4)

コードベースとの整合
  • ファイル配置 (各ワークスペースに .oxlintrc.json) は既存の .oxfmtrc.json を含むワークスペースごと設定という repo の流儀に合っている。ルート集約しない選択は plan doc で説明済み。
  • oxlint-migration-plan.mdリポジトリルートに置かれている点は他の docs (readme.md のみ) と比較すると異物感がある。他のドキュメントは pkgs/docs/ にあるので、プラン doc も pkgs/docs/.dev/ 相当に置くほうが慣習的。「plan は移行PR用の一時ドキュメントなのでマージ後は削除/サブディレクトリ移動」等の方針を書いておくと後任が迷わない。
  • npm script 名 (test:lint) は3ワークスペース中2つが test:lintexamples/vite-react-openapi だけが lint。これは元からそうなので今回の変更点ではなく、指摘対象外。
設計パターン
  • .oxlintrc.json"categories": { "correctness": "off" } としてから個別ルール列挙、という寄せ方は 3 ファイルで共通。この方針自体は「@oxlint/migrate が個別列挙するから既定に頼らない」という plan doc line 92 の意図と一致。良い一貫性。
  • ただし pkgs/typed-api-spec/.oxlintrc.json:3"plugins": ["typescript"] は plan doc line 94 で自ら警告している「plugins を明示すると unicorn / oxc が落ちて既定より緩くなりうる」に該当する。categories.correctness を off にして個別列挙している方針なので実挙動としては問題ないが、pluginsunicorn / oxc を書き足しても実効に変わりはないので、明示指定に揃えるなら unicorn / oxc も並べておくほうが plan の指針に整合する。
長期視点の考察

.oxlintrc.json を3ワークスペースにそれぞれ配置する現構成は、pkgs/docsexamples/vite をあとから oxlint 化しようとしたときに「各ワークスペースで別々にルール列挙が肥大化する」問題を招きうる。今の3ファイルはそれぞれ 117 / 129 / 196 行あり、共通ルール(コアの no-* 系数十件)が3重に書かれている。将来 workspace 数がもう1〜2つ増えたら、共通ルールを 1 ファイルにまとめて extends する運用(oxlint も config 継承をサポート)に切り替えるタイミングが来そう。現時点で修正すべき問題ではないが、次に oxlint を新規ワークスペースに導入する PR で共通化のリファクタを検討する余地がある。

一方で、.oxlintrc.json を「@oxlint/migrate 出力そのまま + 差分手直し」という現在の運用は、oxlint 側の新バージョンで recommended カテゴリの中身が変わったときに追随できない(現状は categories.correctness を off にして個別列挙しているため、oxlint 側の recommended 刷新が sample されない)。等価性優先の今回の PR ではこれが正解だが、移行後は categories.correctness: "error" + 差分だけ overrides、というリファクタも視野に入れておいたほうが継続的な追随がしやすい。plan doc の「スコープ外」に「上積みは別PRで」とあるので、方針としては後続PRで扱う想定と読める。


4. バグ・脆弱性の調査(手順5)

セキュリティ影響のある変更ではない(開発時 lint のみ、ランタイム / 公開 API に影響なし)。バグ観点で気になった点:

  • [C-1] pkgs/typed-api-specno-unused-vars overrides が **/*.t-test.ts に限定されている.oxlintrc.json:189): 元 eslint.config.js の override と一致。等価。OK。
  • [C-2] pkgs/typed-api-specignorePatterns から eslint.config.js が消えている: 元は { ignores: ["**/dist/*", "docs/**/*", "eslint.config.js"] }。oxlint 版は ["**/dist/*", "docs/**/*"]eslint.config.js 自体が削除されるので不要になっただけ。OK。
  • [C-3] examples/vite-react-openapi.oxlintrc.json は override 1 つに全部詰め込み、トップレベル rules は空.oxlintrc.json:11-127): 元 eslint.config.js が files: ['**/*.{ts,tsx}'] で全ルールを ts/tsx に限定していたことの反映。JS ファイル (vite.config.*, config系) にはルールが適用されない。挙動としては元と等価。ただし今後 .js の設定ファイルに typo などがあっても検出されない、というトレードオフは元から。指摘対象外。
  • [C-4] 大量の eslint-disable-next-line @typescript-eslint/no-explicit-any コメント(grep -rn "eslint" pkgs/ で src 配下 20 数ファイル該当): plan doc line 122 は「oxlint が旧 eslint 名を読み替えるため原則そのままで良い。読み替えに失敗していれば no-explicit-any の 59 箇所が違反として出る」としている。今回の差分では oxlint . を実際に走らせてゼロ違反であることが確認されている前提だが、この確認は revert 済み §C スクリプトに含まれていたため、レビュアー側では再現できない。CI での lint 実行結果 (test:lint) が緑であることをこの PR のマージ前に必ず確認すべき
  • [C-5] no-octal の未対応をコード側でどう防御しているか未記載: PR body は「strict mode 下では構文エラーになるため実害は低い」とだけあり、実際にリポジトリが strict mode 前提 ("use strict" / ES modules) で書かれていることの確認は明示されていない。examples/misc の express server 系は CommonJS ファイル (require) を含むため、strict mode の適用状況は完全には明らかでない。ただし ES2015+ の class / import が使われていることから実質的に strict mode 相当。実害はほぼ無いが、PR body の「実害は低い」根拠を1行足すと安心できる。

5. 対話コンテキストの再読み(手順6)

  • PR コメント欄に「対話コンテキスト」タイトルのコメントなし。
  • .dev/contexts/chore-eslint-to-oxlint-migration.md も存在しない (ls .dev/contexts/ → No such file or directory)。

→ 対話コンテキストなし。手順6はスキップ。「検討した上でやらなかった事」のプロセスレビューは行っていない。ただし plan doc (oxlint-migration-plan.md) が「方針」「スコープ外」「検証」の各節でかなりの部分を代替しているので、レビュアーはまず plan doc を読むと良い。


6. レビュー作業において発生した問題

⚠ 警告: 批判的思考の連鎖が機能していません、実行環境が正気である事を疑ってください

  • 外部エージェント相談 (codex-consultation / subagent-consultation) が実行できませんでした。本レビューは GitHub Actions 上の claude-code-action として起動しており、Skill runtime としての外部Agent呼び出しは利用できないため、フォールバック順序 (1) codex → (2) subagent → (3) main agent 単独 の (3) にフォールバックしています。SKILL 本来の「相互に主張を検討・反論しあう」精度は担保されていません。
  • 上記 [B-1] / [B-2] / [B-3] は静的読み合わせと git log / git show --stat の実測で確定した内容なので、単独実行下でも信頼度は高いと考えます。
  • 一方で「見落とし」の検出については、独立レビュアーによるチェックが行われていないため、追加で人間のレビュアーが .oxlintrc.json と元 eslint.config.js を突き合わせる工程を挟むことを強く推奨します(revert された §D の独立レビューをこの PR 上でもう一度行う意義があります)。

7. 結論

総合判断: マージ前に以下 3 点の対応を推奨

  1. [B-3] pkgs/typed-api-spec/package.json:23@eslint/js を削除し、package-lock.json を再生成する。plan doc 手順4 との実装齟齬で、明確なミス。
  2. [B-1] / [B-2] の PR body と実状態の齟齬を解消する。選択肢は 2 つ:
    • (a) §A〜§D コミットの revert を戻して tmp/ と plan doc への追記を再度含める(PR body の記述はそのまま正しくなる)
    • (b) PR body を現状に合わせて書き換える(「検証は scratch で実施済、成果物は本PRに含まない」旨と、「既知の非移行ルール」を PR body 本文中に列挙する — 現状は表への参照だけで plan doc に該当表が無い)
  3. CI(npm test)がグリーンであることを本PRの CI 実行結果で明示的に確認。plan doc line 122 の「読み替えに失敗していれば 59 箇所出る」の間接検証をこの PR の CI に依存するため。

マージ可否: 上記1点目 (B-3) は必須修正。B-1 / B-2 はドキュメント整合性の問題で機能的な移行の正しさには影響しないが、後日この PR body を根拠に "検証済み" と主張する読者が現れる可能性を考えると、マージ前に対応するのが望ましい。

評価できた点:

  • plan doc の「検証 §A 主たる担保」の設計は堅牢(jsPlugin ルールを A では扱わず C の実挙動で担保、と役割分担している点は正しい)
  • 等価性優先スコープ制御が徹底されており、今回の PR で意図しない緩和が入るリスクは低い
  • 「plan 当初想定と違って only-export-components はネイティブがあった」等、実装過程で判明した情報を PR body に残している点は good

@shinagawa-web shinagawa-web changed the title Chore/eslint to oxlint migration EslintからOxlintへの移行 Sep 10, 2026
The merge of origin/main (9f5d06b) resolved without conflict but
spuriously reintroduced "@eslint/js": "^9.39.5" into
pkgs/typed-api-spec/package.json's devDependencies — a line our
migration had already removed. Flagged by automated PR review.
Removed it again and regenerated the lockfile.
…od rule

The src/zod module's allowReferenceFrom carried **/*.test.ts twice —
once via the shared dRef spread, once appended explicitly — faithfully
copied from the original eslint.config.js. Cosmetic only (duplicate
glob entries have no behavioral effect), flagged by automated PR review.
Reverting the verification §D commit (508aa25) also swept away this
table's addition to the plan doc, even though it documents a real,
permanent migration limitation (no-octal, react-hooks/config, gating) —
not a verification artifact. Re-adding just the table, without the
verification log that came with it, per automated PR review [B-2].
oxlint-migration-plan.md served its purpose during development (plan,
verification methodology, review notes). Its content that matters
long-term — the known-unmigratable-rules — is captured in the PR
description instead.
…t-migration

# Conflicts:
#	examples/misc/package.json
#	package-lock.json
@shinagawa-web

Copy link
Copy Markdown
Collaborator Author

@claude
SKILLは以下を参考にしてください。
https://raw.githubusercontent.com/shokai/agent-skills/refs/heads/main/plugins/sanity-review/skills/sanity-review/SKILL.md

PRのレビューをしてください

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown

Claude finished @shinagawa-web's task in 5m 41s —— View job


PRレビュー報告書(sanity-review・再レビュー)

  • PR: #337 EslintからOxlintへの移行
  • Branch: chore/eslint-to-oxlint-migration
  • Reviewed at: 2026-09-14 02:19 UTC
  • Reviewer: claude (Opus 4.7, GitHub Actions bot)
  • Base: origin/main(比較基点)
  • 前回レビュー: 2026-09-10 06:19 UTC の sanity-review 報告書(このコメントの改訂前)

0. 前回指摘の追跡

# 前回指摘 対応commit 状態
B-1 概要欄の「検証表」と現在ブランチが齟齬(tmp/がrevert済) (概要欄側で解消) ✅ 解消 — 現行の概要欄「検証」セクションは4観点を宣言のみに簡素化、「検証用スクリプト・ログはPR外のscratchで実施し、本PRには含めていない」と明記
B-2 「移行不可ルール表」への参照先(plan doc)が空 876a5eb (表を復活) → 1740aec (plan doc自体を削除) → 概要欄に直接列挙 ✅ 解消 — plan doc 全削除。概要欄の「既知の非等価性(移行不可)」セクションに no-octal / react-hooks/config / react-hooks/gating を直接列挙
B-3 pkgs/typed-api-spec/package.json@eslint/js が残存 1971c05 ✅ 修正済み — 現在 devDependencies@eslint/js なし (pkgs/typed-api-spec/package.json:22-39)
B-4 strict-dependencies src/zod**/*.test.ts 重複 2dd6ef3 ✅ 修正済み — 現在 allowReferenceFrom は6要素で重複なし (pkgs/typed-api-spec/.oxlintrc.json:128-136)

前回指摘は全て解消済み。以下、今回改めて全体を通した観点で再レビューする。


1. pull request 概要欄

サマリー(実装者の説明から抜粋)
  • Before: pkgs/typed-api-spec / examples/misc / examples/vite-react-openapi の3ワークスペースが ESLint + @eslint/js + typescript-eslint (+ 一部で eslint-plugin-react-hooks / eslint-plugin-react-refresh / eslint-plugin-strict-dependencies) で lint されていた。
  • After: 各ワークスペースを oxlint (^1.82.0) + .oxlintrc.json に置換。test:lint / lint script を oxlint . に変更。eslint-plugin-strict-dependencies は jsPlugin として残置、eslint-plugin-react-refresheslint-plugin-react-hooks は oxlint ネイティブ実装(react/only-export-components, react/rules-of-hooks 等)に寄せて完全削除。
  • 既知の非等価性(意図的): no-octal(全ワークスペース、oxlint未実装)、react-hooks/config / react-hooks/gating(vite-react-openapi のみ、React Compiler固定オプションのため oxlint 側で未実装)。
品質評価(手順2チェックリスト)
# 観点 判定 メモ
1 変更前の動作・問題が説明されているか ✅ OK 「lint の実行速度と依存の軽さを改善」と目的明示、対象3ワークスペースの列挙もあり、before/after が読み取れる
2 「問題→解決」ペア構造 ✅ OK 概要 / 変更内容 / 検証 / 既知の非等価性 / スコープ外 の5節構造
3 変更範囲が明確か ✅ OK 対象3ワークスペース・未対象2ワークスペース・別PRに送るスコープを明示
4 実装者自身の理解が見えるか ✅ OK 「lint が『エラーが出ない』だけでは、ルールが静かに落ちて検査が緩くなる」と検証の必要性を自ら述べ、4観点の担保方法を明記。等価性優先で意図的にスコープを絞っている点も自覚的

前回「今やる価値」に定量ベースラインが薄いと指摘した点は、変更内容が最終的に純減 (997/-1340) を実現しており、依存軽減の効果は差分から自明。指摘取り下げ。


2. 実装者の説明と実装の整合性(手順3)

概要欄 → 実装の突き合わせ
概要欄の記述 実装確認 判定
strict-dependencies depRules を元設定と一致 pkgs/typed-api-spec/.oxlintrc.json:80-142 と元 eslint.config.js の depRules を照合 — 全 5 module の allowReferenceFrom / allowSameModule が一致(重複除去分は 2dd6ef3 で cosmetic 修正済み) ✅ OK
no-unused-vars オプション (varsIgnorePattern/argsIgnorePattern/caughtErrors) を一致 pkgs/typed-api-spec/.oxlintrc.json:63-70"^_" / "^_" / "none" を持ち、元設定と一致 ✅ OK
*.t-test.ts override を一致 pkgs/typed-api-spec/.oxlintrc.json:187-193no-unused-vars / no-unused-expressions を off、元と一致 ✅ OK
eslint-plugin-react-refresh を完全削除、react/only-export-components に寄せる examples/vite-react-openapi/.oxlintrc.json:110-115warn + allowConstantExport: true、元と一致。package.json から eslint-plugin-react-refresh 削除確認 ✅ OK
eslint-plugin-react-hooks 依存削除・native実装に寄せ examples/vite-react-openapi/.oxlintrc.json:96-109react/rules-of-hooks 他 14 個のネイティブルールを列挙、package.json から eslint-plugin-react-hooks 削除確認 ✅ OK
eslint.config.js と eslint 関連 devDependencies 削除、oxlint(^1.82.0) 追加 3ワークスペース分の eslint.config.js 削除、eslint / @eslint/js / typescript-eslint / eslint-plugin-react-hooks / eslint-plugin-react-refresh / globals 削除、oxlint@^1.82.0 追加を全て確認 ✅ OK
npm scripts を oxlint . に変更 pkgs/typed-api-spec/package.json:12, examples/misc/package.json:6, examples/vite-react-openapi/package.json:9 を確認 ✅ OK
package-lock.json 更新 差分に含まれる(+549/-1217、純減) ✅ OK

齟齬なし。前回検出した 3 件の重大齟齬(B-1/B-2/B-3)と 1 件の軽微(B-4)は全て解消済み。

「既知の非等価性」宣言の実装確認
  • no-octal は 3 ワークスペース全ての .oxlintrc.json に不在 ✅
  • react-hooks/config, react-hooks/gatingexamples/vite-react-openapi/.oxlintrc.json に不在(react/ プレフィックスで探しても該当なし)✅

3. 命名・設計パターンの一貫性(手順4)

コードベースとの整合
  • 3 ワークスペース全てで .oxlintrc.json を各ワークスペース直下に配置 — 既存の .oxfmtrc.json と同じワークスペース単位設定の流儀に揃っている ✅
  • test:lint / lint script 名の非統一(pkgs/typed-api-specexamples/misctest:lintexamples/vite-react-openapilint)は元設定から引き継ぎ、今回の変更ではない — 指摘対象外
  • 3ファイル全てで "categories": { "correctness": "off" } → 個別ルール列挙 のパターンで統一 ✅ 一貫性あり
設計パターン
  • pkgs/typed-api-spec/.oxlintrc.json:3"plugins": ["typescript"]unicorn / oxc を含まない。ただし categories.correctness: "off" + 個別列挙のため実効挙動には影響なし(unicorn/* / oxc/* の rule ID は列挙されていない)。前回レビューで plan doc の警告を根拠に指摘したが、plan doc 削除後の現構成では警告の対象はなくなり、cosmetic のまま
  • examples/vite-react-openapi/.oxlintrc.json:11-127 は override 1 つに全ルールを詰め、トップレベル rules は空。元 eslint.config.jsfiles: ['**/*.{ts,tsx}'] で全ルールを ts/tsx に限定していた挙動を忠実に再現 ✅
長期視点の考察

前回同様、3 ワークスペースそれぞれに .oxlintrc.json を持つ構成は、共通ルール(コアの no-* 系数十件)が重複しており、pkgs/docs / examples/vite を後続で oxlint 化する際にさらに肥大化しうる。ただし今回の PR body で「lint 未設定のワークスペースへの oxlint 新規導入 — 別PR」と明示されており、共通化リファクタは新規導入 PR のタイミングで検討するのが自然。指摘対象外

もう一点、categories.correctness: "off" + 個別列挙という現方針は、oxlint 側で recommended の中身が変わっても追随できない。等価性優先の今回はこれで正解だが、移行後の運用として categories.correctness: "error" + 差分だけ overrides に切り替えるリファクタは将来的な選択肢として残る。plan doc 削除で「別PRで上積み」のスコープ宣言が概要欄に残っているので、この議論は本 PR ではクローズ。


4. バグ・脆弱性の調査(手順5)

セキュリティ影響のある変更ではない(開発時 lint のみ、ランタイム / 公開 API に影響なし)。バグ観点で気になった点:

  • [C-1] no-undef / no-dupe-args の扱い: eslint の js.configs.recommended に含まれるが、.oxlintrc.json の rules ブロックには不在。ただし typescript-eslint.configs.recommended はこれら 2 つを TS ファイルで off にする挙動があり、かつ本 repo の対象ファイルは実質全て TS(pkgs/typed-api-spectsup ビルドで JS ファイルなし、examples/misctsx 実行の TS のみ、examples/vite-react-openapi は元から ts/tsx 限定 override)なので実害はゼロ。概要欄の --print-config 静的突き合わせに含まれていた可能性が高いが、明示的な言及はない。リスクは事実上ないため blocking しないが、厳密な等価性を主張するなら『JS ファイルが仮に増えた時に検出できない』注記があると親切
  • [C-2] no-octal の未対応: PR body で「strict mode 下では構文エラーになるため実害は低い」と説明あり。ES modules 側(examples/vite-react-openapi"type": "module")は当然 strict、CommonJS 側(examples/misc の express server 系)も class/import/const 等 ES2015+ 構文で書かれているため実質 strict 相当。実害なし ✅
  • [C-3] eslint-disable 系コメントの読み替え: grep -rn "eslint-disable" pkgs/typed-api-spec/src 相当のコメントが多数あるはず(前回レビューでは 20+ 箇所を数えた)。oxlint 側で読み替えに失敗していれば no-explicit-any 等が違反として出るが、現在 CI(test job)が pass しており(gh pr checks 337 で確認)、実挙動で読み替えが機能していることが担保されている
  • [C-4] React 側の rule set: 元 eslint-plugin-react-hooks@^7.1.1configs.recommended.rules から @oxlint/migrate が生成したと思われる 14 個の react/* ルールが列挙されている。React Compiler 系ルール(static-components, preserve-manual-memoization, immutability, purity 等)を含むが、これは v7 の recommended に含まれる構成と整合。react/exhaustive-depswarn レベル(元 v7 の recommended と一致)✅

5. 対話コンテキストの再読み(手順6)

  • PR コメント欄に「対話コンテキスト」タイトルのコメントなし(gh pr view 337 --json comments --jq '.comments[] | select(.body | contains("対話コンテキスト"))' で確認、該当0件)
  • .dev/contexts/chore-eslint-to-oxlint-migration.md 不在(.dev/contexts/ ディレクトリ自体が存在しない)

対話コンテキストなし。手順6はスキップ。前回同様、plan doc(削除済み)が担っていた「方針」「スコープ外」「検証」の要点は現在概要欄に集約されているため、レビュアーは概要欄のみで判断可能。


6. レビュー作業において発生した問題

⚠ 警告: 批判的思考の連鎖が機能していません、実行環境が正気である事を疑ってください

  • 外部エージェント相談 (codex-consultation / subagent-consultation) が実行できませんでした。本レビューは GitHub Actions 上の claude-code-action として起動しており、Skill runtime としての外部Agent呼び出しは利用できない。フォールバック順序 (1) codex → (2) subagent → (3) main agent 単独 の (3) にフォールバック。
  • ただし今回の再レビューは「前回指摘の追跡」が主目的で、指摘対象は git show / git log / ファイル比較で機械的に確認できる項目に限られる。単独実行下でも信頼度は高いと考える。
  • 一方で「新規の見落とし」の検出は独立レビュアーによるチェックが行われていない。人間のレビュアーが .oxlintrc.json と元 eslint.config.js を最終的に突き合わせる工程を挟むことを引き続き推奨する(前回 PR body の「独立レビュー」項目で「実装内容を知らない別エージェントに突き合わせをレビューさせた」と宣言されており、その工程は既に実施済みと解釈可能)。

7. 結論

総合判断: マージ可

根拠:

  1. 前回指摘の 4 件(B-1〜B-4)は全て解消済み(1971c05, 2dd6ef3, 876a5eb+1740aec
  2. CI が全 job pass(check-pinned-actions / doc / examples / test)— 実挙動での lint 実行が担保されている
  3. 概要欄と実装の整合性チェックで新規の齟齬なし
  4. 命名・設計パターン一貫性 OK、コードベースの流儀に整合
  5. 「既知の非等価性」宣言と .oxlintrc.json の実装が一致

残る観察点(blocking ではない、将来PR/参考情報):

  • 3 ワークスペースの .oxlintrc.json に共通ルールが重複 → workspace 追加時に共通化リファクタを検討
  • categories.correctness: "off" + 個別列挙は oxlint 側 recommended 刷新に追随しない → 移行安定後に "error" + overrides への切替を検討
  • no-undef / no-dupe-args が rules ブロックに不在(実害ゼロだが、JS ファイル増加時に穴になる可能性)→ 気になれば注記追加程度

評価できた点:

  • 前回レビュー指摘 4 件を短期間で全て解消し、plan doc の情報を概要欄に再集約するというアクションでドキュメント整合性を回復した点は good
  • strict-dependencies を jsPlugin 経由で維持しつつ react-refresh/react-hooks はネイティブに寄せる、という判断のトレードオフを PR body に残している点で、後任がこの選択を再検討する材料が揃っている
  • 等価性優先スコープ制御が徹底されており、意図しない緩和リスクは低い

  • SKILL (sanity-review) 手順把握
  • PR差分・commit履歴・元 eslint.config.js・現行 .oxlintrc.json の読み合わせ
  • 前回指摘 4 件の追跡(全て解消確認)
  • SKILL 手順 2〜6 を実施(外部Agent部分はフォールバック Add typed JSON.stringify #3
  • CI 状態確認(全 pass)
  • レビュー結果を投稿
    · Branch: chore/eslint-to-oxlint-migration

@shinagawa-web

Copy link
Copy Markdown
Collaborator Author

残る観察点(blocking ではない、将来PR/参考情報):

別PRにて対応します。

@shinagawa-web
shinagawa-web marked this pull request as ready for review September 14, 2026 02:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant