kollog

フロントエンドの波に浮かぶ
あざらしの技術ブログ

importに関連するOxlintのルール設定を見直そう

history_anticlockwise_line 最終更新日: 2025/08/31

これ何

このブログではLinterとしてESLintOxlintを併用している。
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というツールから移植されたものが含まれている。

CategoryとPluginの関係

Oxlintの各ルールは先述のPluginのいずれかに由来するものであり、その挙動に応じてそれぞれ1つのCategoryというラベルが付与されている、と考えるのがわかりやすいかも。

例としてjsx_a11y/alt-textは以下のように考えることができる。

  • jsx_a11y/alt-texteslint-plugin-jsx-a11y由来のルールである
  • <img>要素にalt属性で適切な文字列を設定することはアクセシビリティ上必須である。
    逆に言えば<img>要素に適切なalt属性が付与されていないのは間違っているので、jsx_a11y/alt-textcorrectnessに分類される

Automatic Fixes

Oxlintのルールの一部はautofixが可能だが、このautofixの挙動にも2種類ある。

名称説明CLI実行時のオプション
Fixesコードの挙動に影響を与えず安全である--fix
Suggestionsコードの挙動を変えたり、意図しない変更を加える可能性がある--fix-suggestions

また、FixesとSuggestionsなautofixの中には更に危険(Dangerous)なものがあり、--fix-dangerouslyオプションで有効化できる。

つまり、--fix/--fix-suggestions/--fix-dangerouslyオプションの挙動は以下のようにまとめられる。

オプションFixesなautofixSuggestionsなautofixDangerousなautofix
--fixするしないしない
--fix-dangerouslyするしないする
--fix-suggestions --fix-dangerouslyしないするする
--fix --fix-suggestions --fix-dangerouslyするするする

import周りのルールの整理

ここでやっと本題に入る。

今自分が頭を抱えているのは以下のルール。

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-duplicatesno-duplicate-importsのどちらを採用するかによってOxlintの設定ファイルの修正内容がちょっと変わる。

  • import/no-duplicatesを採用する場合、no-duplicate-importsoffにして無効化する
  • no-duplicate-importsを採用する場合、import/no-duplicatesimport/consistent-type-specifier-styleoffにして無効化する

型の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"
    }
  }
}