ゼロからESLintを理解する

# はじめに

自身のメモとして本記事ではESLintについての概要を記述します．ESLintの概要、Lintingの歴史、ESLintの全体像、そしてコマンドラインベースでSTEP by STEPでのESLintの利用についての解説を行います．別記事でReactの開発環境における[ESLint](https://eslint.org/)/[Prettyer](https://prettier.io/)/[stylelint](https://stylelint.io/)の設定を記述いたします．

# ESLintの概要

[ESLint](https://eslint.org/)とはJavasScriptのためのlinterです．[linter](https://ja.wikipedia.org/wiki/Wikipedia:Linter)とはコードを静的に解析しコンパイラーで検出されない潜在的なバグを警告するプログラムです．linterを導入することでコーディングの品質を高めることが出来ます．静的にコードを解析する行為をLintingといいます．ESLintを利用することで、JavaScriptを静的に検証し、潜在的なバグを早期発見することが出来ます．特にJavaScritpは歴史的背景からバグを生みやすい言語であるためlinterで潜在的なバグを潰しておくことは重要です．

公式サイトにも記述されていますが、ESLintには３つの特徴があります．

**①問題発見**
ESLintはコードを静的に分析して、問題をすばやく見つけます．ESLintはほとんどのテキストエディタに組み込まれており、[CI/CDパイプライン](https://www.ashisuto.co.jp/enishi/service_quality/cicd.html)の一部としてESLintを実行できます．

**②自動修正**
ESLintが見つけた多くの問題は自動的に修正できます． ESLintの修正は構文に対応しているため、従来の検索と置換のアルゴリズムによって発生するエラーは発生しません．

**③カスタマイズ**
コードを前処理し、カスタムパーサーを使用して、ESLintの組み込みルールと一緒に機能する独自のルールを記述します．ESLintをカスタマイズして、プロジェクトに必要な方法で正確に機能させることができます．

![_2021-06-23_7.26.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/00a87d31-479f-1f79-3101-cfe143417170.png)

２つ目の特徴として自動修正があげられていますが、Prettierのほうがコードのフォーマッターとしては強力なため、Prettierを併用してESLintは利用されます．

# Linterの歴史

JavaScriptのLinterとしては2021年6月現在[npmのパッケージダウンロード数](https://www.npmtrends.com/eslint-vs-jscs-vs-jshint-vs-jslint-vs-tslint)から見て**ESLintが一強で、ESLintの利用がデファクト**だという事がわかります．

![_(3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/9cc4f4b8-2634-7e48-c4eb-cd3b0074dc27.png)

JavaScriptのLinterの登場人物のざっくり説明です．

- JSLint（[公式サイト](https://www.jslint.com/)、[wiki](https://ja.wikipedia.org/wiki/JSLint)）
    - 最初のJavaScriptのlinterと言われています．（[記事](https://web.archive.org/web/20180226015758/https://codekitapp.com/help/jslint/)）．
    - 2002年にのDouglas Crockford（ダグラス・クロックフォード）が作成しました．
    - Douglas Crockfordは「[JavaScript: The Good Parts](https://www.amazon.co.jp/JavaScript-Parts-%E2%80%95%E3%80%8C%E8%89%AF%E3%81%84%E3%83%91%E3%83%BC%E3%83%84%E3%80%8D%E3%81%AB%E3%82%88%E3%82%8B%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9-Douglas-Crockford/dp/4873113911)」の著者です．（[wiki](https://en.wikipedia.org/wiki/Douglas_Crockford)、[Douglas Crockfordの書籍](https://www.amazon.co.jp/%E6%9C%AC-Douglas-Crockford/s?rh=n%3A465392%2Cp_27%3ADouglas+Crockford)）
- JSHint（[公式サイト](https://jshint.com/)、[wiki](https://ja.wikipedia.org/wiki/JSHint)）
    - 2011年にAnton KovalyovがJSLintをフォークして作成しました．
    - JSLintの問題として構文ルールはDouglas Crockfordの個人のコーディングスタイルの思想が強く反映されており、他の開発者が意見できない仕様になっているため、構文チェックのルールをオフにしたり設定変更ができない問題がありました．JSHintではこれを解消しました．（[記事：Anton Kovalyov：Why I forked JSLint to JSHint](https://web.archive.org/web/20110224022052/http://anton.kovalyov.net/2011/02/20/why-i-forked-jslint-to-jshint/)）
    - 設定ファイルを使用して構文チェックのルールのON/OFFの切り替えが可能です．
    - 開発者コミュニティから支援されて開発された初めてのJavaScriptのlinterです．（[記事：JSHint: A Community Driven Fork of JSLint](https://web.archive.org/web/20110221102102/http://badassjs.com/post/3364925033/jshint-an-community-driven-fork-of-jslint)）
- ESLint（[公式サイト](https://eslint.org/)、[wiki](https://en.wikipedia.org/wiki/ESLint)）
    - 2013年にNicholas C. Zakasが作成しました．（[記事](https://humanwhocodes.com/blog/2013/07/16/introducing-eslint/)、[Nicolas C. Zakasの書籍](https://www.amazon.co.jp/%E6%9C%AC-Nicholas-C-Zakas/s?rh=n%3A465392%2Cp_27%3ANicholas+C.+Zakas)）
    - JSHintでは構文チェックのルールを拡張できない課題がありましたが、ESLintでは独自でルールを追加する可能にしこの問題を解消しました．独自ルールの追加以外にJSHintでも可能であった構文チェックのルールのON/OFFの切り替えも可能です．（[記事：Understanding the Real Advantages of Using ESLint](https://rangle.io/blog/understanding-the-real-advantages-of-using-eslint/)）
    - 現在JavaScriptのlinterとして最も使われています．
- JSCS（[公式サイト](https://jscs-dev.github.io/)）
    - 2014年にMarat Dulinが作成しました．
    - 2016年にJSCSチームはESLintプロジェクトに合流し、JSCSのメンテナンスを中止しました．（[記事：JSCS - End of Life](https://medium.com/@markelog/jscs-end-of-the-line-bc9bf0b3fdb2#.x0o1xqkk9)、[記事：Welcoming JSCS to ESLint](https://eslint.org/blog/2016/04/welcoming-jscs-to-eslint)）
- TSLint（[公式サイト](https://palantir.github.io/tslint/)）
    - 2016年にPalantir TechnologiesがESLintに相当するTypeScriptのlinterを作成しました．JSLint、JSHint、ESLint、JSCSはJavaScriptのlinterですが、TSLintはTypeScriptのlinterです．
    - 2019年にTypeScriptの構文チェック機能を備えたESLintの利用をTSLint公式として推奨し、TSLintは非推奨となりました．

# ESLintの仕組みのざっくり理解する

ここではESLintの仕組みをざっくり解説します．具体的にはルールとはなにか、Shareable Configs（共有設定）とは何かを解説します．

## ルールによる構文チェック

ESLintではコーディングの記述の正しさをルールを用いて判定しています．ルールに準拠することで、統一感のあるコーディングが実現でき、コードの品質を高めることが出来ます．

たとえば、空のブロックの仕様を禁止するルールの[no-empty](https://eslint.org/docs/rules/no-empty)を見ましょう．no-emptyのルール上、下記の記述は正しくないため、ESLint上ではエラーがでます．

```javascript
/*eslint no-empty: "error"*/
if (foo) {
}

while (foo) {
}

switch(foo) {
}

try {
    doSomething();
}catch(ex) {

}finally {

}
```

no-emptyのルール上、下記の記述は正しいです．

```javascript
/*eslint no-empty: "error"*/
if (foo) {
// empty
}

while (foo) {
/* empty */
}

try {
    doSomething();
}catch (ex) {
// continue regardless of error
}

try {
    doSomething();
}finally {
/* continue regardless of error */
}
```

別のルールも見てみましょう．[semi](https://eslint.org/docs/rules/semi)はセミコロンを強制するルールです．semiのルール上、下記の記述は正しくないため、ESLint上ではエラーがでます．

```javascript
/*eslint semi: "error"*/

var name = "ESLint"

object.method = function() {
    // ...
}
```

semiのルール上、下記の記述は正しいです．

```javascript
/*eslint semi: "error"*/
var name = "ESLint";

object.method = function() {
    // ...
};
```

先程の[no-empty](https://eslint.org/docs/rules/no-empty)や[semi](https://eslint.org/docs/rules/semi)を含めて公式のESLintから膨大なルールが提供されています．ルール一覧は[サイト](https://eslint.org/docs/rules/)で確認できます．これらルールを組み合わせて利用することで統一感のあるコーディングが実現でき、コードの品質を高めることが出来ます．

![_2021-06-28_5.44.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/58350bc3-7107-1641-9d26-456fdbf0b0e7.png)

ルールはデフォルトで有効化されていないため、どのルールを有効化するかを利用者が指定する必要があります．また、ルールは独自でも作成可能なため、公式のESLintが提供するルールを使わなくてもlintingはできます．開発者が独自でルールを作成する方法について下記を参照ください．

- [ESLintのルールを自作しよう！](https://techblog.yahoo.co.jp/javascript/how-to-create-eslint-rules/)
- [1分でわかるVSCodeとESLintを使用したカスタムルール作成方法](https://iwb.jp/vscode-eslint-javascript-custom-rules-howto/)

## Shareable Config（共有設定）

利用するルールを一つ一つ設定ファイルに記述するのは手間がかかります．ESLintでは[Shareable Config（共有設定）](https://eslint.org/docs/developer-guide/shareable-configs)という仕組みを使い、npmにパッケージを公開することで誰でも自分が設定しているルールを他のユーザに共有することができます．別の言い方をすると、誰かが作ってくれた設定を使うことで、自身でルールの設定をすることなく、統一感のあるコーディングができます．

[npmjs.com](https://www.npmjs.com/search?q=eslint-config)にてeslint-configと検索すると、ESLintで利用可能なShareable Config（共有設定）を検索することができます．

![_2021-06-29_4.56.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/7fc233c5-a944-2ea6-dc3b-f4c202b2cc62.png)

良いコーディングを実現するためにAirbnbやGoogleなどがコーディングスタイルを提供しています．これらコーディングスタイルはESLintにてShareable Config（共有設定）として利用が可能です．ESLintで利用される代表的なコーディングスタイルは下記です．

- Airbnb JavaScript Style Guide（[GitHub](https://github.com/airbnb/javascript)）
    - 民泊サービスで知られるAirbnbが作成しているコーディングスタイルガイドです．ESLintで利用する際に候補としてあがる代表的なコーディングスタイルです．
- Google JavaScript Style Guide（[公式ページ](https://google.github.io/styleguide/jsguide.html)、[GitHub](https://github.com/google/eslint-config-google)、[公式ページ和訳](https://cou929.nu/data/google_javascript_style_guide/)）
    - Googleが提供するコーディングスタイルガイドです．こちらもESLintで利用する際に候補としてあがる代表的なコーディングスタイルです．
- JavaScript Standard Style Guide（[公式ページ](https://standardjs.com/)、[GitHub](https://github.com/standard/standard)）
    - AirbnbやGoogle以外で代表的なコーディングスタイルガイドです．

[npm trends](https://www.npmtrends.com/eslint-config-airbnb-vs-eslint-config-google-vs-eslint-config-standard)でパッケージダウンロード数で見るとAirbnbが人気なのがわかります．

![_2021-06-30_11.02.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/bc5048e7-0740-e618-987c-d6e604061adc.png)

スタイルガイドというのが適切かわからないですが、[eslint-config-eslint](https://www.npmjs.com/package/eslint-config-eslint)にて[公式のESLintが推奨するルール](https://eslint.org/docs/rules/)が提供されています．チェックマークが入っているのはESLintの公式が推奨しているルールです．これらルールの塊もShaerable Config（共有設定）として利用が可能です．

![_2021-06-29_6.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/edf3a45b-7ecd-d410-9d51-af1d3c278040.png)

## 自動修正されるルール

ESLintの２つ目の特徴として自動修正がありましたが、すべてのルールが自動で修正されるわけではありません．ESLintの公式が提供するルールの中で、🔧（レンチ）のアイコンが記載されているルールに限り、自動でコードの修正が可能です．

![_2021-06-29_6.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/edf3a45b-7ecd-d410-9d51-af1d3c278040.png)

## 動作確認

ESLintは自由度が高い構文チェックツールですので、手間を掛けずに挙動を確認できる環境（[デモサイト](https://eslint.org/demo)）が公式から提供されています．ESLintには大量のルールがあるため、自身にあったルール探したり、ルールごとの挙動を確認するのに役立ちます．

![_2021-06-29_8.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/ebf2f21e-5189-e85e-0846-9cb1d427cb22.png)

# さわって理解する

ここでは実際にESLintを使ってざっくりどういうことができるか理解したいと思います．

## 動作環境の確認

環境を確認します．

```bash
$ npm -v
7.18.1

$ node -v
v16.4.0
```

## ESLintのインストール

`yarn`を利用して[eslint](https://www.npmjs.com/package/eslint)のパッケーをインストールします．

```bash
$ yarn add -D eslint
$ yarn eslint -v
v7.29.0
```

## 設定ファイルの確認

`package.json`を確認します．

```json:package.json
{
  "devDependencies": {
    "eslint": "^7.29.0"
  }
}
```

`.eslintrc.json`を確認します．`.eslintrc.json`は[ESLintの設定ファイル](https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-file-formats)です．どのような共有設定を読み込むか、ルールのチューニングなどを行います．

```json:.eslintrc.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {},
    "globals": {},
    "rules": {}
}
```

記述されている内容だけ説明します．

- `"extends": ["eslint:recommended"]`
    - 先程インストールした[eslint](https://www.npmjs.com/package/eslint)に含まれるShareable Configを利用しています．
    - `"eslint:recommended"`を読み込むことで、[チェックマークが入っているルール一覧](https://eslint.org/docs/rules/)がlintingに適応されます．

## チャレンジ１：global変数を理解する

ESLintの動作確認する`console_example.js`を作成します．

```bash:console_example.js
console.log("hello world");
```

フォルダ構成は下記になります．

```bash
.
├── node_modules
├── package.json
├── console_example.js
├── yarn.lock
└── .eslintrc.json
```

実行するとメッセージが表示されます．

```bash
$ node console_example.js
hello world
```

ESLintをコマンドラインで利用します．

```bash
$ yarn eslint console_example.js

/Users/hayato94087/temp/console_example.js
  1:1  error  'console' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

error Command failed with exit code 1.
```

下記がメッセージの読み方になります．

```text
行数:列数　[error/warning] [エラー内容] [構文チェックに使用したルールの名称]
```

今回は1つのエラーが出ました．

- [no-undef](https://eslint.org/docs/rules/no-undef)
    - 宣言されていない変数が使われているか検知するルールです．
    - `console`の変数が認識されていないためエラーが出ています．

動作環境にあわせてESLintに認識すべきglobal変数を設定しておく必要があります．ESLintでは動作する環境ごとに定義されているglobal変数を`.eslintrc.json`に設定します．`env`で動作環境を指定することでglobal変数の指定が出来ます．指定可能な変数は[ESLintのサイト](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments)に記載があります．

```diff:.eslintrc.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
-   "env": {},
+   "env": {"browser": true},
    "globals": {},
    "rules": {}
}
```

設定内容の説明です．

- `browser:true` - ブラウザで動作するため`browser`を有効化します．これにより`window`や`console`などのglobal変数をESLintが認識できるようになります．

ESLintをコマンドラインで利用します．何もエラーが出ないことを確認します．

```bash
$ yarn eslint console_example.js
```

## チャレンジ２：エラーメッセージを理解し対処する

ESLintの動作確認する`add_example.js`を作成します．わざと２行目を`b`ではなく`c`の変数を記載しておきます．

```javascript:add_example.js
function add(a,b) { return a+c }
var result = add(1,2)
console.log(result)
```

フォルダ構成は下記になります．

```text
.
├── node_modules
├── package.json
├── add_example.js
├── console_example.js
├── yarn.lock
└── .eslintrc.json
```

実行してみる．実行時にランタイムエラーになる．

```bash
$ node add.js
/Users/hayato94087/temp/add_example.js:1
function add(a,b) { return a+c }
                             ^

ReferenceError: c is not defined
    at add (/Users/hayato94087/temp/add_example.js:1:30)
    at Object.<anonymous> (/Users/hayato94087/temp/add_example.js:2:14)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:816:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:79:12)
    at node:internal/main/run_main_module:17:47
```

ESLintをコマンドラインで利用します．

```bash
$ yarn eslint add.js

/Users/hayato94087/temp/add_example.js
  1:16  error  'b' is defined but never used  no-unused-vars
  1:30  error  'c' is not defined             no-undef

✖ 2 problems (2 errors, 0 warnings)

error Command failed with exit code 1.
```

今回は2つのエラーが出ました．

- [no-unused-vars](https://eslint.org/docs/rules/no-unused-vars)
    - 宣言されているが使われていない変数があるか検知するルールです．
    - `b`は宣言されているが利用されていないためエラーを出しています．
- [no-undef](https://eslint.org/docs/rules/no-undef)
    - 宣言されていない変数が使われているか検知するルールです．
    - `c`は宣言されていないが使われているためエラーを出しています．

コードを修正します．

```diff:add_example.js
- function add(a,b) { return a+c }
+ function add(a,b) { return a+b }
var result = add(1,2)
console.log(result)
```

構文チェックを行います．

```bash
$ yarn eslint add_example.js
```

何もエラーが出ないことを確認します．

ランタイムエラーが無いことも確認します．

```bash
$ node add_example.js
3
```

ESLintを利用することでランタイムでしか検知できない問題点を事前に把握することができます．

## チャレンジ３：ルールを追加して自分の構文チェック環境を構築する

ESLintの動作確認する`control_example.js`を作成します．

```javascript:control_example.js
var foo =1
if (  foo) { foo++}
 
var bar = true
while (  bar) { bar=false }

if (foo) { 
    console.log("foo");
} else {
console.log("not "); 
}
```

ここでは上記のような統一感の無いコードをESLintの自動修正機能を使って修正して下記のような形に変換します．

```javascript:control_example.js
var foo =1
if (foo) {
    foo++
}
 
var bar = true
while (bar) {
    bar=false 
}

if (foo) { 
    console.log("foo");
} else {
    console.log("not "); 
}
```

３つのルールを追加します．

- [brace-style](https://eslint.org/docs/rules/brace-style#require-brace-style-brace-style)を有効化し、{} の使い方に統一感をもたせます．
- [indent](https://eslint.org/docs/rules/indent)を有効化し、インデントの幅を揃えます．
- [space-in-parens](https://eslint.org/docs/rules/space-in-parens)を有効化し、() 内のスペースの利用を統一化します．

現状のESLintファイル（`.eslintrc.json`）の設定を確認します．

```json:.eslintrc.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {}
}
```

フォルダ構成は下記になります．

```text
.
├── node_modules
├── package.json
├── add_example.js
├── console_example.js
├── control_example.js
├── yarn.lock
└── .eslintrc.json
```


構文チェックを行います．特にエラーは出ないです．

```bash
$ yarn eslint control_example.js
```

実行してみます．ランタイムエラーは起きません．

```bash
$ node control_example.js
foo
```

### brace-styleの適応

[brace-style](https://eslint.org/docs/rules/brace-style#require-brace-style-brace-style)を有効化し、下記のような｛｝のスタイルを実現する．brace-style上、下記は正しいです．

```javascript
/*eslint brace-style: "error"*/
if (foo) {
  bar();
}
```

brace-style上、下記は正しくありません．

```javascript
/*eslint brace-style: "error"*/
if (foo)
{
  bar();
}
```

`package.json`にbrace-styleのルールを追加します．

```diff:package.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
-    "rules": {}
+    "rules": {
+       "brace-style": "error"
+    }
}
```

構文チェックを行います．

```bash
$ yarn eslint control_example.js

/Users/hayato94087/temp/control_example.js
  2:12  error  Statement inside of curly braces should be on next line                                                        brace-style
  2:19  error  Closing curly brace should be on the same line as opening curly brace or on the line after the previous block  brace-style
  5:15  error  Statement inside of curly braces should be on next line                                                        brace-style
  5:27  error  Closing curly brace should be on the same line as opening curly brace or on the line after the previous block  brace-style

✖ 4 problems (4 errors, 0 warnings)
  4 errors and 0 warnings potentially fixable with the `--fix` option.
```

4つのエラーがでました．上のメッセージの中に`potentially fixable with the `--fix` option`という文言があります．すでに紹介したとおり、ルールによっては自動修正してくれるルールも存在します．brace-styleは自動修正可能なルールなため、コマンドを使って自動修正します．

```bash
$ yarn eslint --fix control_example.js
```

自動修正されたソースコードは下記になります．

```javascript:control_example.js
var foo =1
if (  foo) {
 foo++
}
 
var bar = true
while (  bar) {
 bar=false 
}

if (foo) { 
    console.log("foo");
} else {
console.log("not "); 
}
```

構文チェックでエラーが出ないことを確認しておきます．

```bash
$ yarn eslint control_example.js
```

### indentの適応

[indent](https://eslint.org/docs/rules/indent)を有効化し、インデントの幅を揃えます．indent上、下記は正しいです．

```javascript
/*eslint indent: "error"*/
if (a) {
    b=c;
    function foo(d) {
        e=f;
    }
}
```

indent上、下記は正しくありません．

```javascript
/*eslint indent: "error"*/
if (a) {
b=c;
function foo(d) {
e=f;
}
}
```

`package.json`にindentのルールを追加します．

```diff:package.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {
       "brace-style": "error",
+        "indent": "error"
    }
}
```

構文チェックを行います．

```bash
$ yarn eslint control_example.js

/Users/hayato94087/temp/control_example.js
   3:1  error  Expected indentation of 4 spaces but found 1  indent
   8:1  error  Expected indentation of 4 spaces but found 1  indent
  14:1  error  Expected indentation of 4 spaces but found 0  indent

✖ 3 problems (3 errors, 0 warnings)
  3 errors and 0 warnings potentially fixable with the `--fix` option.

error Command failed with exit code 1.
```

3つのエラーがでました．indentは自動修正可能なルールなため、コマンドを使って自動修正します．

```bash
$ yarn eslint --fix control_example.js
```

自動修正されたソースコードは下記になります．

```javascript:control_example.js
var foo =1
if (  foo) {
    foo++
}
 
var bar = true
while (  bar) {
    bar=false 
}

if (foo) { 
    console.log("foo");
} else {
    console.log("not "); 
}
```

構文チェックでエラーが出ないことを確認しておきます．

```bash
$ yarn eslint control_example.js
```

### space-in-parensの適応

[space-in-parens](https://eslint.org/docs/rules/space-in-parens)を有効化し、、() 内のスペースの利用を統一化します．space-in-parens上、下記は正しいです．

```javascript
/*eslint space-in-parens: "error"*/
foo( 'bar' );
```

space-in-parens上、下記は正しくありません．

```javascript
/*eslint space-in-parens: "error"*/
foo('bar');
```

`package.json`にindentのルールを追加します．

```diff:package.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {
       "brace-style": "error",
       "indent": "error",
+      "space-in-parens": "error"
    }
}
```

構文チェックを行います．

```bash
$ yarn eslint control_example.js

/Users/hayato94087/temp/control_example.js
  2:5  error  There should be no space after this paren  space-in-parens
  7:8  error  There should be no space after this paren  space-in-parens

✖ 2 problems (2 errors, 0 warnings)
  2 errors and 0 warnings potentially fixable with the `--fix` option.
```

2つのエラーがでました．space-in-parensは自動修正可能なルールなため、コマンドを使って自動修正します．

```bash
$ yarn eslint --fix control_example.js
```

自動修正されたソースコードは下記になります．

```javascript:control_example.js
var foo =1
if (foo) {
    foo++
}
 
var bar = true
while (bar) {
    bar=false 
}

if (foo) { 
    console.log("foo");
} else {
    console.log("not "); 
}
```

構文チェックでエラーが出ないことを確認しておきます．

```bash
$ yarn eslint control_example.js
```

このように`yarn eslint —fix`のコマンドを実行することで自動修正を行うことができます．

## チャレンジ４：npm-scriptsを利用しショートカット作成する

package.jsonファイル内にscriptsにコマンドを記載することで、いちいちコマンドを記載しなくても事項できる環境を作ります．`package.json`にscriptsを追加します．`yarn lint`で構文チェックのみ行い、`yarn lint:fix`で構文チェックした後に自動修正を行えるようにします．

```diff:package.json
{
+    "scripts": {
+      "lint": "eslint *.js",
+      "lint:fix": "eslint *.js"
+    },
  "devDependencies": {
    "eslint": "^7.29.0"
  }
}
```

実際に動かしてみます．

```bash
$ yarn lint

/Users/hayato94087/temp/add_example.js
  1:19  error  Statement inside of curly braces should be on next line                                                        brace-style
  1:32  error  Closing curly brace should be on the same line as opening curly brace or on the line after the previous block  brace-style

✖ 2 problems (2 errors, 0 warnings)
  2 errors and 0 warnings potentially fixable with the `--fix` option.
```

構文サンプル3で追加したbrace-styleに構文サンプル2のサンプルコード（`add_example.js`）が準拠していないと怒られます．`yarn lint:fix`で修正します．

```bash
$ yarn lint:fix
```

`add_example.js`は修正されました．

```bash:add_example.js
function add(a,b) {
    return a+b 
}
var result = add(1,2)
console.log(result)
```

このように、ショートカットを利用することで、いちいちコマンド入力しなくても自動修正ができるものは修正可能になります．

念の為、現時点の`package.json`を記載します．

```json:package.json
{
  "scripts": {
    "lint": "eslint *.js",
    "lint:fix": "eslint --fix *.js"
  },
  "devDependencies": {
    "eslint": "^7.29.0",
    "eslint-config-airbnb-base": "^14.2.1",
    "eslint-plugin-import": "^2.23.4"
  }
}
```

下記は現時点の`.eslintrc.json`です．

```json:.eslintrc.json
{
    "extends": ["eslint:recommended"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {
        "brace-style": "error",
        "indent": "error",
        "space-in-parens": "error"
    }
}
```

## チャレンジ５：Airbnbのスタイルガイドを適応する

自分にあったルールを探して適応させていくのは一苦労です．Shareable Configsで説明しましたが、すでにAirbnbやGoogleがスタイルガイドを公開しているため、それをそのまま利用することで、品質の高いコーディングを実現できます．

今回はReactベースではないため、[eslint-config-airbnb-base](https://www.npmjs.com/package/eslint-config-airbnb-base)のパケージをインストールします．依存関係を解消するためeslint-plugin-importもあわせてインストールします．

```bash
$ yarn add -D eslint-config-airbnb-base eslint-plugin-import
```

`package.json`に追加されました

```diff:package.json
{
  "scripts": {
    "lint": "eslint *.js",
    "lint:fix": "eslint --fix *.js"
  },
  "devDependencies": {
    "eslint": "^7.29.0",
+    "eslint-config-airbnb-base": "^14.2.1",
+    "eslint-plugin-import": "^2.23.4"
  }
}
```

`.eslintrc.json`を修正し[eslint-config-airbnb-base](https://www.npmjs.com/package/eslint-config-airbnb-base)をextendsに追記しています．参考までに、記述方法としてeslint-config-は省略が可能です．

```json:.eslintrc.json
{
    "extends": ["airbnb-base"],
    "plugins": [],
    "parserOptions": {},
    "env": {"browser": true},
    "globals": {},
    "rules": {
    }
}
```

構文チェックします．

```bash
$ yarn lint

/Users/hayato94087/temp/add_example.js
  1:15  error    A space is required after ','                                    comma-spacing
  2:1   error    Expected indentation of 2 spaces but found 4                     indent
  2:13  error    Operator '+' must be spaced                                      space-infix-ops
  2:15  error    Trailing spaces not allowed                                      no-trailing-spaces
  2:15  error    Missing semicolon                                                semi
  4:1   error    All 'var' declarations must be at the top of the function scope  vars-on-top
  4:1   error    Unexpected var, use let or const instead                         no-var
  4:19  error    A space is required after ','                                    comma-spacing
  4:22  error    Missing semicolon                                                semi
  5:1   warning  Unexpected console statement                                     no-console
  5:20  error    Newline required at end of file but not found                    eol-last
  5:20  error    Missing semicolon                                                semi

/Users/hayato94087/temp/console_example.js
  1:1   warning  Unexpected console statement                   no-console
  1:13  error    Strings must use singlequote                   quotes
  1:28  error    Newline required at end of file but not found  eol-last

/Users/hayato94087/temp/control_example.js
   1:1   error    Unexpected var, use let or const instead                         no-var
   1:9   error    Operator '=' must be spaced                                      space-infix-ops
   1:11  error    Missing semicolon                                                semi
   3:1   error    Expected indentation of 2 spaces but found 4                     indent
   3:5   error    Unary operator '++' used                                         no-plusplus
   3:10  error    Missing semicolon                                                semi
   5:1   error    Trailing spaces not allowed                                      no-trailing-spaces
   6:1   error    All 'var' declarations must be at the top of the function scope  vars-on-top
   6:1   error    Unexpected var, use let or const instead                         no-var
   6:15  error    Missing semicolon                                                semi
   8:1   error    Expected indentation of 2 spaces but found 4                     indent
   8:8   error    Operator '=' must be spaced                                      space-infix-ops
   8:14  error    Trailing spaces not allowed                                      no-trailing-spaces
   8:14  error    Missing semicolon                                                semi
  11:11  error    Trailing spaces not allowed                                      no-trailing-spaces
  12:1   error    Expected indentation of 2 spaces but found 4                     indent
  12:5   warning  Unexpected console statement                                     no-console
  12:17  error    Strings must use singlequote                                     quotes
  14:1   error    Expected indentation of 2 spaces but found 4                     indent
  14:5   warning  Unexpected console statement                                     no-console
  14:17  error    Strings must use singlequote                                     quotes
  14:25  error    Trailing spaces not allowed                                      no-trailing-spaces
  15:2   error    Newline required at end of file but not found                    eol-last

✖ 38 problems (34 errors, 4 warnings)
  31 errors and 0 warnings potentially fixable with the `--fix` option.

error Command failed with exit code 1.
```

`yarn lint:fix`で修正する前にそれぞれのファイルの内容を記述しておきます．

`add_example.js`は下記です．

```javascript:add_example.js
function add(a,b) {
    return a+b 
}
var result = add(1,2)
console.log(result)
```

`console_example.js`は下記です．

```javascript:console_example.js
console.log("hello_world");
```

`control_example.js`は下記です．

```javascript:control_example.js
var foo =1
if (foo) {
    foo++
}
 
var bar = true
while (bar) {
    bar=false 
}

if (foo) { 
    console.log("foo");
} else {
    console.log("not "); 
}
```

はい、めちゃくちゃ怒られました．`yarn lint:fix`で修正します．

```bash
$ yarn lint:fix

/Users/hayato94087/temp/add_example.js
  5:1  warning  Unexpected console statement  no-console

/Users/hayato94087/temp/console_example.js
  1:1  warning  Unexpected console statement  no-console

/Users/hayato94087/temp/control_example.js
   3:3  error    Unary operator '++' used      no-plusplus
  12:3  warning  Unexpected console statement  no-console
  14:3  warning  Unexpected console statement  no-console

✖ 5 problems (1 error, 4 warnings)

error Command failed with exit code 1.
```

5件まで問題を減らすことができました．メッセージを見る限り、2種類のメッセージがあることがわかります．まずは、エラーになっている[no-plusplus](https://eslint.org/docs/rules/no-plusplus#disallow-the-unary-operators-and-no-plusplus)を修正します．

[no-plusplus](https://eslint.org/docs/rules/no-plusplus#disallow-the-unary-operators-and-no-plusplus)は空白の位置によって解釈が変わるため、++や—の利用を禁止するルールです．言葉だとわかりにくいのでこういう問題になります．

```javascript
var i = 10;
var j = 20;

i ++
j
// i = 11, j = 20
```

```javascript
var i = 10;
var j = 20;

i
++
j
// i = 10, j = 21
```

`control_example.js`を下記の通り修正しました．

```diff:control_example.js
let foo = 1;
if (foo) {
- foo++;
+  foo += 1;
}

let bar = true;
while (bar) {
  bar = false;
}

if (foo) {
  console.log('foo');
} else {
  console.log('not ');
}
```

これでエラーメッセージは解消できました．

```bash
$ yarn lint

/Users/hayato94087/temp/add_example.js
  5:1  warning  Unexpected console statement  no-console

/Users/hayato94087/temp/console_example.js
  1:1  warning  Unexpected console statement  no-console

/Users/hayato94087/temp/control_example.js
  12:3  warning  Unexpected console statement  no-console
  14:3  warning  Unexpected console statement  no-console

✖ 4 problems (0 errors, 4 warnings)
```

次は、4つのwarningの対処を行います．

JavaScriptはブラウザで利用するように設計されているため、デバッグで記述したと思われるconsoleの利用に対して警告文を[no-console](https://eslint.org/docs/rules/no-console)では出しています．

次の行に対して特定のルールを無効化することができます．下記が特定のルールを無効化する記述方法です．

```text
// eslint-disable-next-line [無効化したいルール名称]
```

[無効化したいルール名称]を記載しない場合はすべてのルールが無効化されます．

`console_example.js`を修正しました．

```javascript:console_example.js
let foo = 1;
if (foo) {
  foo += 1;
}

let bar = true;
while (bar) {
  bar = false;
}

if (foo) {
  // eslint-disable-next-line no-console
  console.log('foo');
} else {
  // eslint-disable-next-line no-console
  console.log('not ');
}
```

`add_example.js`も同様に修正します．

```javascript:add_example.js
function add(a, b) {
  return a + b;
}
const result = add(1, 2);
// eslint-disable-next-line no-console
console.log(result);
```

`console_example.js`も修正しますが、`no-console`の記載を省略します．これはすべてのルールを無効化する意味があります．

```javascript:console_example.js
// eslint-disable-next-line
console.log('hello_world');
```

これでerror/warningともに解消できました．

```bash
$ yarn lint
```

Airbnbのルールを適応することでいい感じに統一感のあるコーディングが実現できました！

## チャレンジ６：エディター連携する

チャンレジ１〜５でESLintの基礎知識はだいぶ溜まったと思います．最後にエディターと連携させて、実用的な開発環境を整備したいと思います．

ESLintが連携可能なエディターの情報としては[公式サイト](https://eslint.org/docs/user-guide/integrations)に記述があります．今回はVisual Studio Code（以降VSCode）での設定を記述します．

VSCodeは[サイト](https://code.visualstudio.com/download)から取得してPCに設定してください．詳細は割愛します．

![_2021-06-30_15.52.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/1734bf63-e6c8-51ad-2395-51be16deb716.png)


プロジェクトディレクトリに遷移し、`code .`でVSCodeを開きます．

```bash
$ cd /Users/hayato94087/temp
$ code .
```

VSCodeが開いたら、左下の拡張機能（Extensions）をクリックしてください．

![_2021-06-30_15.55.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/6a626256-1380-b0da-ba0b-e4d693daf672.png)

ESLintの拡張機能を検索します．「eslint」と入力して検索してください．検索結果を選択したら、「Install」をクリックして拡張機能のESLint（`dbaeumer.vscode-eslint`）をインストールします．

![_2021-06-30_15.57.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/fb69971a-58a4-2fea-8a4c-2ffdfbe6cff6.png)

動作確認のため`if_example.js`のファイルを作成します．

```javascript:if_example.js
var foo =1
if (  foo) { foo += 1}
```

フォルダ構成は下記になります．

```bash
.
├── node_modules
├── package.json
├── add_example.js
├── if_example.js
├── console_example.js
├── control_example.js
├── yarn.lock
└── .eslintrc.json
```

ESLintでのerrorは赤い波線で、warningは黄色い波線で表示されます．

![_2021-06-30_16.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/15f5fc23-2e14-daaa-50de-fd4e0c372e99.png)

VSCodeの設定を変更することで、ソースコードの保存時に自動修正できるようになります．`command`+`,`でSettings画面を開く、右上の赤枠をクリックし設定情報をJSONで編集する画面に遷移する．

![_2021-06-30_16.06.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/28bd070a-14bf-1971-1120-94d0940b6b8d.png)

`settings.json`に下記を追加します．

```json:settings.json
{
    // 保存時にコードアクションを実施
    "editor.codeActionsOnSave": {
        // ESLintの設定．VSCodeではESLintの自動修正をコードアクションとして実行
        "source.fixAll.eslint": true,
    },
    // [editor.formatOnSave]と[source.fixAll.eslint]の設定が競合して正しく自動修正ができなくなることがあるため、保存時にフォーマットは無効化しておく
    // https://qiita.com/musatarosu/items/a49ed897009ea272e24a
    // https://sunday-morning.app/posts/2020-10-29-vscode-disable-editor-formatonsave
    "editor.formatOnSave": false,
}
```

`command`+`s`でソースコードを保存し、保存時にESLintの自動修正を実行する．

![20210630_180058.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/f12f7e16-37ed-3551-ff3b-3a3ece097351.gif)

ESLintをエディターと連携させることで、コーディングをしながらリアルタイムにlintingできる環境が構築でき、潜在的なバグの早期回収が可能になりました．

# 困ったことがあったら

[ESLint 公式Discord](https://discord.com/invite/wfTnWvE)に質問をすることでサポートを受けることができます．わからないことあったらここで聞きましょう．日本語のチャンネルもあります．

![_2021-06-29_6.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920063/2382a5c0-95ef-1518-6d44-822b1363e892.png)

# 日本語の情報収集

公式の英語ドキュメントに抵抗がある方は下記でルールが日本語で解説されています．

- [ESLintのエラールール。日本語ざっくり解説[可能性あるエラー編] - Qiita](https://qiita.com/M-ISO/items/f9097a75b362206c2a99)
- [ESLintのエラールール。日本語ざっくり解説[ベストプラクティス編] - Qiita](https://qiita.com/M-ISO/items/4cd183e2496c2937a53e)
- [ESLintのエラールール。日本語ざっくり解説[スタイル編] - Qiita](https://qiita.com/M-ISO/items/113ddd448bdc496af783)
- [ESLintのエラールール。日本語ざっくり解説[ES6編] - Qiita](https://qiita.com/M-ISO/items/f0c2f0e669db32cf4efb)

ESLintの最新の情報を取得する方法として

- [@mysticatea](https://qiita.com/mysticatea)がESLintの中の人で、ESLintでアップデートがあるとQiitaで情報発信してくれるウォッチするのがいいと思います．

# 最後に

本記事では、ESLintの概要、Lintingの歴史、ESLintの全体像、そしてコマンドラインベースでSTEP by STEPでESLintの仕組みについて解説しました．最後にエディターとの連携についても記述しました．別記事でReactの開発環境における[ESLint](https://eslint.org/)/[Prettyer](https://prettier.io/)/[stylelint](https://stylelint.io/)の設定を記述いたします．

# 参考

- [https://blog.natade.net/2018/11/03/web開発-歴史-用語-javascript-html5/](https://blog.natade.net/2018/11/03/web%E9%96%8B%E7%99%BA-%E6%AD%B4%E5%8F%B2-%E7%94%A8%E8%AA%9E-javascript-html5/)
- [https://blog.ojisan.io/eslint-prettier](https://blog.ojisan.io/eslint-prettier)
- [https://blog.ojisan.io/eslint-plugin-and-extend](https://blog.ojisan.io/eslint-plugin-and-extend)
- [https://blog.ojisan.io/prettier-eslint-cli](https://blog.ojisan.io/prettier-eslint-cli)
- [https://eslint.org/docs/developer-guide/shareable-configs](https://eslint.org/docs/developer-guide/shareable-configs)
- [https://garafu.blogspot.com/2017/02/eslint-rules-jp.html](https://garafu.blogspot.com/2017/02/eslint-rules-jp.html)
- [https://jtfal.com/docs/eslint/user-guide/configuring/](https://jtfal.com/docs/eslint/user-guide/configuring/)
- [https://qiita.com/howdy39/items/6e2c75861bc5a14b2acf](https://qiita.com/howdy39/items/6e2c75861bc5a14b2acf)
- [https://qiita.com/khsk/items/0f200fc3a4a3542efa90](https://qiita.com/khsk/items/0f200fc3a4a3542efa90)
- [https://qiita.com/M-ISO/items/f9097a75b362206c2a99](https://qiita.com/M-ISO/items/f9097a75b362206c2a99)
- [https://qiita.com/mysticatea/items/dc35ced6bd5e782f50cd](https://qiita.com/mysticatea/items/dc35ced6bd5e782f50cd)
- [https://qiita.com/mysticatea/items/f523dab04a25f617c87d](https://qiita.com/mysticatea/items/f523dab04a25f617c87d)
- [https://qiita.com/takeharu/items/dee0972e5f39bfd4d7c8](https://qiita.com/takeharu/items/dee0972e5f39bfd4d7c8)
- [https://www.tam-tam.co.jp/tipsnote/javascript/post11934.html](https://www.tam-tam.co.jp/tipsnote/javascript/post11934.html)
- [https://www.tam-tam.co.jp/tipsnote/javascript/post9944.html](https://www.tam-tam.co.jp/tipsnote/javascript/post9944.html)