🐳

ESlint で import を自動ソートする

2022/04/28に公開

import が増えてくると、その順序が気になってきます。

外部ライブラリからのインポートは上にあって欲しいし、reactからのインポートはさらに上に固定したくなりませんか?

ESlint のeslint-plugin-importというプラグインを使えば簡単にソートできます。

使うのはeslint-plugin-importimport/orderというルールです。

設定も色々あって、ある程度の自由度で順序を決めることもできます。このルールは fixable なのでeslint --fixコマンドで自動ソートが可能です。

公式の解説もあるのですが分かりにくい個所や誤っている個所もあるためこの記事を作成しました。

ソートした例

早速eslint-plugin-importimport/orderを使ってソートした例です。

上が下のようにきれいになります。

ソート後はreactからのインポートが最上部あって、次にその他の外部ライブラリ、エイリアスからのインポート(@src/**)、親ディレクトリからのインポートとなっています。
これらの順序やエイリアスも自分の環境や好みによって自由に設定可能です。

最後に typescript の型インポートが別に分かれているのは別プラグインとの組み合わせで実現しています。

詳細な設定方法は下で解説します。

ソート前

import { TextField, Box, BoxProps } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import { useAddAppNotification } from '@src/common/hooks/useAppNotifications';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';
import { useSignIn } from '../hooks/useSignIn';

ソート後

import { useEffect } from 'react';

import { LoadingButton } from '@mui/lab';
import { TextField, Box } from '@mui/material';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import { useAddAppNotification } from '@src/common/hooks/useAppNotifications';

import { useSignIn } from '../hooks/useSignIn';

import type { BoxProps } from '@mui/material';
import type { SubmitHandler } from 'react-hook-form';

設定の方法

上記のソートは下記のような設定で実現しています。
ソートの順序は ESlint ルールのオプションで設定します。

.eslintrc.js
{
  rules:{
    'import/order': [
      'error',
      {
        groups: ['builtin', 'external', 'parent', 'sibling', 'index', 'object', 'type'],
        pathGroups: [
          {
            pattern: '{react,react-dom/**,react-router-dom}',
            group: 'builtin',
            position: 'before',
          },
          {
            pattern: '@src/**',
            group: 'parent',
            position: 'before',
          },
        ],
        pathGroupsExcludedImportTypes: ['builtin'],
        alphabetize: {
          order: 'asc',
        },
        'newlines-between': 'always',
      },
    ],
    '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
    // その他のルール
  }
}

設定の解説

上記の設定例を使って各項目ごとの意味と設定方法を解説します。

より詳細が知りたい場合は以下の github の解説ページを見てください。

https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md#importorder-enforce-a-convention-in-module-import-order

groups

グループごとのインポート順を設定できます。グループとは「外部ライブラリ」や「親ディレクトリ」などの既定の分類です。以下が全てのグループの名称です。名前でだいたい想像できますが詳細な説明は github で確認できます。

['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object', 'type']

pathGroups

上記groupsに加え独自のグループを path を基準に自分で作り任意の場所に配置することができます。これによってかなり自由度の高いソートが可能になっています。

例えば下記は@src/**ではじまるパスからのインポートをグループにして、parentグループの前に持ってくるという設定です。

新しく作ったグループはgrouppositionを使って配置個所を指定します。
groupには位置の基準となる既定のグループ名を指定します。positionafterbeforeを設定すれば新規グループは基準となるグループの後か前に配置されます。positionを選択しないと、新規グループは基準となるグループに含まれます。

@src/**は私が別途設定しているエイリアスなのですが、設定しないと@からはじまるパスは外部ライブラリとして処理されてしまいます。そのためこの設定が必要なのです。

{
  pattern: '@src/**',
  group: 'parent',
  position: 'before',
}

また、以下の設定ではreact,react-dom/**,react-router-domからのインポートを 1 つのグループにしてbuiltinよりも前に配置するよう設定しています。

ここでpattern{}**を使っています。patternminimatchで解釈されるのでminimatchで使えるパターンが使用できます。

{
  pattern: '{react,react-dom/**,react-router-dom}',
  group: 'builtin',
  position: 'before',
}

pathGroupsExcludedImportTypes

pathGroupsの pattern の判定が影響されないグループを設定することができます。
デフォルト値は['builtin', 'external', 'object']です。

react関連のインポートはexternalなので、pathGroupsExcludedImportTypesがデフォルト値のままだとpathGroupsが適用されません。
pathGroupsExcludedImportTypes['builtin', 'object']にすることで上記で紹介したpathGroupsが効果を発揮します。

ちなみに github の docs では"pathGroupsExcludedImportTypes": ["react"]のような例がありますが、これは使い方を間違っています。docs を修正するプルリクエストも出ているようです。

alphabetize

これはグループ内の順序づけの方法です。グループ内は順序つけしないか、アルファベット昇順、降順が選べます。

newlines-between

グループ間のスペースの設定です。今回の設定では必ずスペースを配置しています。

型のインポートを分ける

typescript の型のインポートをimport type ...のように Type-only imports の形式で分けるには別の ESlint ルールが必要です。

上記のルール設定例の最後にある@typescript-eslint/consistent-type-importsがそれです。

これで型インポートをimport type ...の形式で分離され、import/orderでソートが可能になります。

Discussion