importに関連するOxlintのルール設定を見直そう
これ何
このブログではLinterとしてESLintとOxlintを併用している。
ESLintの扱いにはもう慣れっこだが(大嘘)、Oxlintはまだまだ雰囲気で使っている。
その結果import周りのルール設定が競合してぐちゃぐちゃになり全部offにするという事態になっている。
なので一度見直そうと思う。
事前知識の整理 - ルールに関連する3つの概念
Oxlintのルールを見直す前に、ルールに関する3つの概念を正しく整理しておく。
- Category
- Plugin
- Automatic Fixes
Category
Oxlintのルールについて詳細にみていく前に、OxlintにおけるCategoryという概念をきちんと理解しておく。
Oxlintの1つ1つのルールはそれぞれ一つのカテゴリー(Category)に属している。
ではどういった基準でカテゴリー分けされているのかというと、それはそれぞれのルールの目的及び振る舞いと関係がある。
名称 | 説明 | 代表的なルール |
---|---|---|
correctness | 明らかに間違っている、あるいは無駄なコードを検出する | eslint/no-debugger , jsx_a11y/alt-text |
suspicious | 間違っている、あるいは無駄な「可能性がある」コードを検出する | typescript/no-unnecessary-type-arguments , react/style-prop-object |
pedantic | 厳格だけれどもユーザーやプロジェクトによっては厳しすぎるかもしれない | eslint/eqeqeq , unicorn/prefer-date-now |
perf | コードのパフォーマンスを向上させる目的がある | eslint/no-await-in-loop , react/no-array-index-key |
style | 一貫したスタイルを維持したり、慣用的な構文を強制したりするのに役立つ | eslint/arrow-body-style , typescript/no-empty-interface |
restriction | 特定のパターン、構文、機能の使用を一旦禁止させ、ケースバイケースで有効化させる | eslint/no-alert , typescript/no-require-imports |
nursery | 開発中だったり、大幅に変更される可能性があったり、厳しすぎるかもしれない | eslint/no-unreachable |
Oxlintはcategory単位でルールに反するコードに対するアクションを許容(Allow, -A
)/警告(Warn, -W
)/拒否(Deny, -D
)の中から指定できる。
以下の例では、correctness
及びpedantic
なルールに反するコードを全て拒否している。
# Enable all correctness and suspicious rules
oxlint -D correctness -D pedantic
Plugin
OxlintはOxlint単体でESLintの様々なPluginの機能をサポートしている。
ただし唯一oxc
だけはちょっと毛色が異なっており、これはOxlintをメンテナンスしているoxc固有のルールとdeepscan
というツールから移植されたものが含まれている。
名称 | 対応するESLintのPlugin名 |
---|---|
typescript | typescript-eslint |
unicorn | eslint-plugin-unicorn |
react | eslint-plugin-react , eslint-plugin-react-hooks |
react-perf | eslint-plugin-react-perf |
nextjs | eslint-plugin-next |
oxc | N/A |
import | eslint-plugin-import |
jsdoc | eslint-plugin-jsdoc |
jsx-a11y | eslint-plugin-jsx-a11y |
node | eslint-plugin-node |
promise | eslint-plugin-promise |
jest | eslint-plugin-jest |
vitest | @vitest/eslint-plugin |
CategoryとPluginの関係
Oxlintの各ルールは先述のPluginのいずれかに由来するものであり、その挙動に応じてそれぞれ1つのCategoryというラベルが付与されている、と考えるのがわかりやすいかも。
例としてjsx_a11y/alt-text
は以下のように考えることができる。
jsx_a11y/alt-text
はeslint-plugin-jsx-a11y
由来のルールである<img>
要素にalt属性で適切な文字列を設定することはアクセシビリティ上必須である。
逆に言えば<img>
要素に適切なalt属性が付与されていないのは間違っているので、jsx_a11y/alt-text
はcorrectness
に分類される
Automatic Fixes
Oxlintのルールの一部はautofixが可能だが、このautofixの挙動にも2種類ある。
名称 | 説明 | CLI実行時のオプション |
---|---|---|
Fixes | コードの挙動に影響を与えず安全である | --fix |
Suggestions | コードの挙動を変えたり、意図しない変更を加える可能性がある | --fix-suggestions |
また、FixesとSuggestionsなautofixの中には更に危険(Dangerous)なものがあり、--fix-dangerously
オプションで有効化できる。
つまり、--fix
/--fix-suggestions
/--fix-dangerously
オプションの挙動は以下のようにまとめられる。
オプション | Fixesなautofix | Suggestionsなautofix | Dangerousなautofix |
---|---|---|---|
--fix | する | しない | しない |
--fix-dangerously | する | しない | する |
--fix-suggestions --fix-dangerously | しない | する | する |
--fix --fix-suggestions --fix-dangerously | する | する | する |
import周りのルールの整理
ここでやっと本題に入る。
今自分が頭を抱えているのは以下のルール。
import/exports-last
import/group-exports
import/prefer-default-export
import/no-duplicates
sort-imports
no-duplicate-imports
import/exports-last
(category: Style
)
このルールはモジュールのexport
宣言をファイルの末尾に記述することを強制する。
export
は変数・モジュール宣言と一緒に書いてあって欲しい派閥なのでこのルールは無効化する。
import/group-exports
(category: Style
)
このルールはexport
宣言は1箇所にまとめてグループ化しろ、ということを強制する。
例えばこれはNGで
export const first = true;
export const second = true;
これはOKになる。
const first = true;
const second = true;
export { first, second };
export
は変数・モジュール宣言と一緒に書いてあって欲しい派閥なのでこのルールは無効化する(2回目)。
import/prefer-default-export
(category: Style
)
export
宣言が1箇所しかない時にnamed exportよりもdefault exportの使用を優先することを強制するルール。
これちょっと邪魔だな…無効化する。
import/no-duplicates
(category: Style
)
同じモジュールから複数回importすることを禁止するルール。
これの是非については後述する。
sort-imports
(category: Style
)
import
宣言のソート順を一定のルールになるように強制するルール。
ESLintのsort-imports
も参考になる。
例えば、自分のソースコードではこのようになっているimport
宣言があるのだが、
import { css } from '../../../styled-system/css';
import type { JSX } from 'solid-js/jsx-runtime';
import type { ComponentProps } from 'solid-js';
import { splitProps } from 'solid-js';
こう直せば良い。型のimport
宣言を先に書くことが要注意ポイント。
import type { ComponentProps } from 'solid-js';
import type { JSX } from 'solid-js/jsx-runtime';
import { css } from '../../../styled-system/css';
import { splitProps } from 'solid-js';
これは全然あって良いルールなので有効化する。
no-duplicate-imports
(category: Style
)
同じモジュールから複数回importすることを禁止するルール。
import/no-duplicates
とは何が違うかというと、以下のようなimport
宣言があった時、
import type { ComponentProps, JSX } from 'solid-js';
import { css } from '../../../styled-system/css';
import { splitProps } from 'solid-js';
import/no-duplicates
はこれを許容する一方でno-duplicate-imports
は拒否する。
no-duplicate-imports
は以下のようにモジュールと型のimport
はまとめることを強制する。
import { type ComponentProps, type JSX, splitProps } from 'solid-js';
ESLintのno-duplicate-imports
も参考になる。
これの是非については後述する。
import/no-duplicates
vs no-import-duplicates
ただこれをやると、今度はimport/consistent-type-specifier-style
と競合する。
import/consistent-type-secifier-style
では以下のように型とモジュールのimportは分けて記述することを強制する。
import type { ComponentProps, JSX } from 'solid-js';
import { splitProps } from 'solid-js';
つまり、import/no-duplicates
とno-duplicate-imports
のどちらを採用するかによってOxlintの設定ファイルの修正内容がちょっと変わる。
import/no-duplicates
を採用する場合、no-duplicate-imports
はoff
にして無効化するno-duplicate-imports
を採用する場合、import/no-duplicates
とimport/consistent-type-specifier-style
をoff
にして無効化する
型のimportはモジュールのimportとは分けたい派なので、import/no-duplicates
側を採用することにする。
まとめ
今回言及したものだけ取り上げてoxlintrc.json
を整理するとこうなった。
{
"plugins": ["import"],
"overrides": {
"files": ["src/**/*.ts", "src/**/*.tsx"],
"rules": {
"import/exports-last": "off",
"import/group-exports": "off",
"import/prefer-default-export": "off",
"no-duplicate-imports": "off"
}
}
}