はじめてのESLint
ESLintの設定、毎回設定の方法を忘れるので、数ヶ月後の自分が困らないように、整理しました。
ESLintとは
- Linter。JavaScript/TypeScriptの構文(コードの書き方)が、スタイルガイドラインなどの指定したルールに違反してないかをチェックして、違反してたら指摘してくれたり、修正してくれたりする仕組みです。
- ルールのチェックをどうやってるかというと、JavaScriptは、ASTという木構造で表現することができるので、ASTの構造に変換し、あらかじめ決めたルールにあってるかチェックする仕組みになってます。 こちら でASTに変換を試したりできます。
- 公式ドキュメントの説明は こちら 。
導入
initコマンドで設定ファイルの雛形の生成と必要なパッケージの導入
$ yarn eslint --init
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JavaScript
scriptsの修正
"scripts": {
"lint": "eslint --ext ts,tsx .",
"lint:fix": "eslint --ext ts,tsx --fix .",
},
設定ファイル .eslintrc.js
が生成されるので、必要なルールを設定します。
.eslintrc.js
) を読む/書くポイント
設定ファイル(ESLintは、ソースコードの構文が、指摘したルールに沿ってるかチェックしてくれる仕組みですが、そのルールを実際に設定するのが .eslintrc.js
です。
.eslintrc.js のサンプル例
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb',
'plugin:@next/next/recommended',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2021,
sourceType: 'module',
},
plugins: [
'react',
'@typescript-eslint',
],
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'no-param-reassign': [2, {
props: true,
ignorePropertyModificationsFor: ['state', 'draftState'],
}],
'import/extensions': [2, 'ignorePackages', {
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
}],
'import/prefer-default-export': 0,
'react/jsx-filename-extension': [2, {
extensions: ['.jsx', '.tsx'],
}],
'react/jsx-props-no-spreading': [2, {
html: 'enforce',
custom: 'ignore',
explicitSpread: 'enforce',
}],
'react/jsx-uses-react': 0,
'react/react-in-jsx-scope': 0,
'react/prop-types': 0,
'react/function-component-definition': [2, { namedComponents: 'arrow-function' }],
},
};
ポイント1. pluginsとextendsとrulesの意味を理解する
インストールしたプラグインを plugins
に追記せずに、extends
に共有設定だけを追記しても、ルールが追加される場合があります。(extends
に指定した共有設定側の plugins
で読み込んでる場合があるため)
この時、ルールを追加するには、plugins
に書かないといけないのでは?と混乱したのですが、はじめてeslintの設定する際、これらのルールを設定する主要なプロパティ(plugins
とextends
とrules
)の意味を理解していないから、このような混乱が起きるかなと感じたので、plugins
とextends
とrules
の各プロパティの意味をまとめました。
(1) plugins
-
pluginsは、 ルール を追加する項目です。
-
npmを通して外部に公開されてるルールのpluginをインストールできます。pluginが持ってるルールを利用する際に、pluginsのプロパティに追記します。(後述しますが、extendsにて指定してる共有設定で、pluginsを指定してる場合もあるので、pluginsに指定しなくてもいい場合があります)
-
ドキュメント には、以下のように書かれてます。
which third-party plugins define additional rules, environments, configs, etc. for ESLint to use.」
(2) extends
- extendsは、 ルールの設定 を拡張してくれる共有設定を指定する項目です。 (ルールのオン/オフの設定してくれる外部の設定ファイルを読み込んでくれます。)
- ルール設定が重複している場合、後から書いた方が優先になります。
(3) rules
- rulesは、 個別のルールの設定 を登録する項目です。
- extendsで指定した共有設定以外で個別に設定したい内容を記載します。
ポイント2. ルールのレベル
ルールのレベルは、なし、警告、エラーの3つあります。慣れたら気にならなくなりましたが、最初、0と1と2の数字の羅列に混乱しました。
意味 | 値 | 値 |
---|---|---|
なし | 0 | off |
警告 | 1 | warn |
エラー | 2 | error |
.eslintrc.js
に記載する際に名前を省略して指定できる
ポイント3. 導入した外部ルールのパッケージの名前を 慣れたらそんなに気にならないし、ドキュメントにもちゃんと書いてますが、名前が省略されてると、導入したパッケージと、設定の内容がどう紐づいているのかわからず、混乱しました。
- 外部のルール、例えば、eslint-plugin-xxx というパッケージを導入して、
eslintrc.js
の pluginsに追記する際に、eslint-plugin-
の部分を省略できます。 - eslint-plugin-reactというパッケージを、extendsに追記する場合は、"plugin:react/recommended" となります。
-
eslint-config-
も同様に省略できます。 - ドキュメント参考
The plugins property value can omit the eslint-plugin- prefix of the package name.
The eslint-config- prefix can be omitted from the configuration name. For example, airbnb resolves as eslint-config-airbnb.
ポイント4. その他のプロパティ
env
- プログラムの実行環境を指定します。
- ブラウザで動かすコードの場合 window オブジェクトとか出てくると思いますが、
browser: true,
にしておくと、未定義だと怒られたりしません。
env: {
browser: true,
es2021: true,
},
parserとparserOptions
- parserは、ESLintがプログラムの構文解析してくれる時に使われます。デフォルトでは、ES5になってます。例えば、TypeScriptが対象の場合は、
parser: '@typescript-eslint/parser',
を指定する必要があります。 - parserOptionsは、parserの細かいオプションを指定するプロパティで、jsxを対象にする場合は、以下のように記載します。
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2021,
sourceType: 'module',
},
settings
- settings は任意の実行ルールに適用される追加の共有設定です。
extendsの設定を追ってみる
extends で指定した共有設定のルールが、具体的にどのような設定になっているか追ってみます。試しに、airbnbの設定を追ってみましょう。
extends: [
'airbnb',
],
extends には、'airbnb' と書かれていますが、eslint-config- の部分は省略できるので、実際のパッケージ名は、 eslint-config-airbnb です。
index.js
は、以下のようになっていて、さらにextendsで設定を読み込んでいます。
module.exports = {
extends: [
'eslint-config-airbnb-base',
'./rules/react',
'./rules/react-a11y',
].map(require.resolve),
rules: {}
};
./rules/react
は、以下のようになっていて、ルールの設定がたくさん書かれてます。
試しに、この中から react/react-in-jsx-scope
のルールを読み進めてみます。 pluginsでの指定は、'react'
と記述されていますが、eslint-plugin の部分が省略できるので、パッケージ名は、eslint-plugin-react になります。
// Prevent missing React when using JSX
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
'react/react-in-jsx-scope': 'error',
eslint-plugin-react のindex.js
は、以下のようになっています。
... 省略
'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
... 省略
やっと、ルール自体が書かれてるコードにたどり着きました。./lib/rules/react-in-jsx-scope
は、以下のようになっています。
このルール自体は、JSXを使うために、Reactをちゃんとimportしてるか、チェックするルールですが、他のルールもどんな感じで実装されてるか確認したい場合は、似たような感じでコードを追っていけば辿り着けます。
Prevent missing React when using JSX
ちなみにですが、トランスパイルの仕組みが新しくなって Reactはimportする必要がなくなりました。eslint-plugin-reactの推奨設定を利用する場合に、上記ルールは不要なので、ルールをoffにしましょう。
ルールの決め方
プロジェクトに導入するルールの決め方(.eslintrc.js
の設定内容の決めた際の考え方)は、ベースとなる extendsを選定し、個別に無効にしたいルールや例外パターンのルールを、rulesに追記する感じで書きます。
eslint-config-airbnb
airbnbは、「Airbnb JavaScript Style Guid」 といったスタイルガイドがあり、このガイドに従ったルールが使えて便利です。
eslint-config-next
nextjsが出してるルールなので、nextjsを利用する場合は導入した方が良さそうです。
eslint-config-prettier
prettierと競合するフォーマットのルールを無効化してくれます。
prettier 導入するときの考え方
prettierとeslintは、それぞれの設定で、ルールが競合する場合があります。 例えば、prettierの設定では、XXXX
なルールでコードをフォーマットしてるが、eslintでは YYYYY
のルールで構文解析をしていて、prettierで自動でコードフォーマットしても、eslintで構文がおかしいって怒られ、それをfixしても、prettierを動かすと、prettierのルールで自動でフォーマットされるというのが競合してると無限に繰り返されという問題があります。
対処方法の一つとして、eslint側に、Prettier と競合する可能性のある eslintのルールを無効にする共有設定を追加することで対応できます。
以下、参考にさせてもらった記事です。
prettier 導入
$ yarn add -D prettier eslint-config-prettier
.prettierrc.json
追加
{
"singleQuote": true
}
script編集
"scripts": {
... 省略
"format": "prettier --write ./src",
... 省略
},
.eslintrc.js
編集
extends: [
... 省略
'prettier',
... 省略
],
参考にした記事と情報
Discussion