Prettier 入門 ～ESLintとの違いを理解して併用する～

# お知らせ（2021/05/26 追記）

以前は`eslint --fix`などで ESLint を実行時に Prettier でコードを整形し、整形したコードに対して構文チェックが実行されるようにすることが推奨されていました。

ESLint で Prettier を実行するためには、ESLint の Plugin が必要でしたが、これを利用することが公式で推奨されなくなりました（[詳細はこちら](https://prettier.io/docs/en/integrating-with-linters.html)）。

そのため、記事を更新して Prettier と ESLint をそれぞれ実行させるような内容に変更しました（ついでに husky のバージョンも上げており、それに関する内容も更新しています）。

更新前のコードや記事は以下にありますので、必要に応じてご確認ください。

https://github.com/hira777/integrate-prettier-into-eslint

# はじめに

Prettier（v.2.3.0） に関しての備忘録です。

- 「Prettier の何が便利なのかよくわからない」
- 「ESLint と何が違うのかよくわからない」
- 「`eslint --fix`でコード整形ができるから Prettier の必要性が感じられない」

といった人達向けに書いた記事であり

- Prettier の基本情報、利用目的、利用方法を理解する
- 何故 ESLint だけではなく Prettier も利用するのかを理解する
- Prettier と ESLint の併用方法を理解する

ことを目的としています。

ESLint 自体の説明は記載しておりませんので、ご注意ください。

解説に利用しているコードの最終形態は GitHub 上にあります。

https://github.com/hira777/integrate-prettier-with-eslint

# Prettier とは？

コードフォーマッター（ソースコードを整形してくれるツール）のこと。読み方はプリティア。

以下のような様々な形式にサポートしている。

- JavaScript
- JSX
- Flow
- TypeScript
- JSON
- HTML
- Vue
- Angular
- Ember / Handlebars
- CSS
- Less
- SCSS
- styled-components 💅
- styled-jsx
- GraphQL
- Markdown
- YAML

## 何故コードフォーマッタを利用するのか（コードを整形するのか）

- ソースコードの品質を保つため（コードのスタイルの一貫性を保つため）
- コードレビュー時に、設計や命名などの重要な箇所に集中するため（コードのスタイルの指摘に時間を割くのを防ぐため）
- 複数のメンバーが各自の整形ルールを適用し、更新する度に余計な差分が発生することを防ぐため
- ソースコードを綺麗にするための労力（スタイル定義の議論や時間）を費やさなくて済むため
- ツールに任せられることはツールに任せてしまった方が今後楽になるため

# Prettier を利用してみる

Prettier を利用してソースコードが整形されたファイルを出力してみる。

## ディレクトリ構成

今回 Prettier を利用するディレクトリ構成は以下を前提とする。

```
integrate-prettier-with-eslint
├── .prettierrc
├── package.json
└── app.js
```

### .prettierrc

Prettier の設定ファイル。

設定を記述することで、Prettier のデフォルトの設定を上書きできる。

```json
{
  "singleQuote": true
}
```

指定できるオプションは以下を参照。

[Options · Prettier](https://prettier.io/docs/en/options.html)

### `package.json`

パッケージをローカルインストールするため、`package.json`は以下のコマンドで生成する。

```shell
npm init -y
```

### `app.js`

ソースコードを整形するファイル。

スペースの数が統一されておらず、無駄な改行が存在したりとコードが汚い状態。

```js:整形前のapp.js
class Person{
  constructor(name) {
    this.name =   name;
  }

}


const profile = {
  name: 'soarflat', sex: 'male', location: 'Tokyo'

};

const hoge = (message)=>{
  console.log(message);
}

// hoge(new Person('Person').name);

const fooBar = (a, b, c) =>{
    console.log(a);
  console.log(b)
   console.log(c);
};

fooBar(111,
    {
        hoge: 'hoge!'
    },profile
)
```

## Prettier のインストール

```shell
npm install prettier@2.3.0 --save-dev
```

### ローカルインストールした Prettier を実行するために PATH を通す

`prettier`コマンドを実行できるようにするため、以下のように PATH を通す。

```sh
export PATH=$PATH:./node_modules/.bin
```

※「PATH を通す」が不明な方は以下をご覧ください。

- [PATH を通すとは？ (Mac OS X)](http://qiita.com/soarflat/items/09be6ab9cd91d366bf71)
- [PATH を通すために環境変数の設定を理解する (Mac OS X)](http://qiita.com/soarflat/items/d5015bec37f8a8254380)

## Prettier の実行

`app.js`に対して`prettier`コマンドを実行する。ファイルを上書きするために`--write`オプションをつける。

```shell
prettier app.js --write
```

コマンドを実行すれば、整形されたファイルが出力される。

```js:整形後のapp.js
class Person {
  constructor(name) {
    this.name = name;
  }
}

const profile = {
  name: 'soarflat',
  sex: 'male',
  location: 'Tokyo',
};

const hoge = (message) => {
  console.log(message);
};

// hoge(new Person('Person').name);

const fooBar = (a, b, c) => {
  console.log(a);
  console.log(b);
  console.log(c);
};

fooBar(
  111,
  {
    hoge: 'hoge!',
  },
  profile
);
```

### 整形されたファイルを出力できたが

ESLint でもコード整形はできるし、結局 Prettier の何が良いのかわからないため、Prettier は ESLint より何が優れており、何故利用（併用）すべきかを説明していく。

# 何故 Prettier を利用（ESLint と併用）するのか

ESLint でも`eslint --fix`でコード整形ができるが、Prettier の方がコード整形が優れているから。

具体的には以下の点が優れている。

- ESLint では整形できないコードを整形できる
- ESLint と比べて手軽で確実に整形できる

## ESLint では整形できないコードを整形できる

以下のような一行の文字数が長いコードは ESLint でエラーが出力されるが、整形はされない。

```js
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
```

一方、Prettier では以下のように整形される。

```js
foo(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne()
);
```

上記のような整形をしてくれれば、複数のメンバーがエラーに対する独自の修正を適用し、更新するたびに差分が出てしまうような事態を防ぐことができる。

そのため、Prettier を利用した方がコードのスタイルの一貫性は保たれる。

## ESLint と比べて手軽で確実に整形できる

`eslint --fix`は設定に該当したエラーのみを整形する。そのため、**設定次第ではコードを整形しきれない**。

整形を厳密に行うためには、多くの設定を指定する必要があり、設定自体が複雑になる（設定ファイルがすごい行数になる）可能性がある。

一方、Prettier はデフォルトのスタイル（整形ルール）が存在するため、`prettier`コマンドを実行するだけでコードが整形される。

スタイルの変更も可能だが、こだわりがなければデフォルトのままで問題ないし、確実に整形してくれる。

また、Prettier は良くも悪くも設定できる項目が少ないため、設定が複雑になる可能性が低い。

そのため、Prettier を利用した方が手軽で確実に整形できる。

## Prettier は ESLint よりもコード整形が優れているが

上記の理由で Prettier を利用すべきだが、Prettier はコードフォーマッタのため、ESLint のような構文チェック機能はない。

そのため、コードの整形は Prettier が行い、コードの構文チェックは ESLint が行うように併用する。

# ESLint との併用

ESLint にもフォーマットのルールが存在するため、ESLint の整形と Prettier の整形が競合する可能性がある。

そのため、ESLint のフォーマットのルールを無効化にして、Prettier と ESLint を実行できるようにする。

## 併用に必要なパッケージのインストール

以下のパッケージをインストールする。

```shell
npm install eslint@7.26.0 eslint-config-prettier@8.3.0 --save-dev
```

それぞれのパッケージの詳細は以下の通り。

- `eslint`（ESLint 本体）
- `eslint-config-prettier`（ESLint のフォーマット関連のルールを全て無効にする、要は Prettier が整形した箇所に関してエラーを出さなくなる）

## `.eslintrc`の設定

先ほど Prettier を実行したディレクトリに`.eslintrc`（ESLint の設定ファイル）を作成する。

そのため、ディレクトリ構成は以下のようになる。

```
integrate-prettier-with-eslint
├── .eslintrc
├── .prettierrc
├── package.json
└── app.js
```

`.eslintrc`の内容は以下の通り。

```json:.eslintrc
{
  "root": true,
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": ["eslint:recommended", "prettier"]
}
```

Prettier に関連する記述は

```json
"extends": ["eslint:recommended", "prettier"]
```

の`"prettier"`の箇所のみ。

この記述で、ESLint のフォーマット関連のルールが全て無効になる。

また、この記述は必ず`extends`配列内の最後に記述する。

以下のような記述だとうまく動作しないことがあるため注意。

```json
{
  "extends": ["prettier", "eslint:recommended"]
}
```

## npm scripts で Prettier と ESLint を実行できるようにする

以下のように、`package.json`の`scripts`フィールドに Prettier と ESLint を実行する記述を追加する。

```json
{
  "name": "integrate-prettier-with-eslint",
  "scripts": {
    "format": "prettier --write app.js",
    "lint": "eslint --fix app.js",
    "fix": "npm run format && npm run lint"
  },
  "devDependencies": {
    "eslint": "^7.26.0",
    "eslint-config-prettier": "^8.3.0",
    "prettier": "^2.3.0"
  }
}
```

`npm run fix`を実行すれば、`npm run format`と`npm run lint`が順番に実行されるので、Prettier と ESLint が実行される。

## `npm run fix`を実行してみる

![fix.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69667/dde44942-905a-f63c-20c2-c3904bd4ecb8.gif)

コードの整形、構文チェックができた。

# `git commit`時に Prettier と ESLint が実行されるようにする

上記の方法でコード整形と構文チェックが可能になったが、Prettier と ESLint を実行しない限りコードは整形されないため、整形されていないコードがコミットされてしまう可能性がある。

これを防ぐために、エディタと連携してファイル保存時に Prettier と ESLint を実行する手段がある。

個人での開発ではそれで問題ないが、複数人開発の場合、全員がそれぞれのエディタプラグインを導入するコストがかかる。

そのため、整形したコードのみを確実にコミットしたいのであれば`git commit`時に Prettier と ESLint が実行されるようにする。

Git にはコミット前に指定のスクリプトを実行できる pre-commit フックと言う仕組みがあるため、それを利用する。

## pre-commit フックに必要なパッケージをインストール

```shell
npm install husky@6.0.0 lint-staged@11.0.0 --save-dev
```

それぞれのパッケージの詳細は以下の通り。

- `husky`（Git のコミット時やプッシュ時に指定したコマンドを実行できる）
- `lint-staged`（Git のステージングに追加されたファイルにのみに ESLint などのリントを実行できる）

## husky を利用するためのセットアップ

husky のインストール後は以下のコマンドで、Git フックを有効にする（`.husky`ディレクトリと`husky.sh`などが生成される）。

```
npx husky@6.0.0 install
```

そして以下のコマンドを実行する。

```
npm set-script prepare "husky install"
```

このコマンドを実行することで、以下のように`scripts`フィールドに`prepare`が追加される。

```json
{
  "name": "integrate-prettier-with-eslint",
  "scripts": {
    "format": "prettier --write app.js",
    "lint": "eslint --fix app.js",
    "fix": "npm run format && npm run lint",
    "prepare": "husky install"
  },
  "devDependencies": {
    "eslint": "^7.26.0",
    "eslint-config-prettier": "^8.3.0",
    "prettier": "^2.3.0"
  }
}
```

`prepare`は`npm install`などを実行時に自動で実行される。

そのため、自分以外の開発環境などでも`npm install`を実行すれば`husky install`が実行され、Git フックが有効になる。

## pre-commit フックを追加する

```
npx husky@6.0.0 add .husky/pre-commit "lint-staged"
```

これで、コミット時に`lint-staged`が実行されるようになった。

## `package.json`に lint-staged の設定を追加する

以下のように、`package.json`の`lint-staged`フィールドに lint-staged の設定を追加する。

```json:package.json
{
  "name": "integrate-prettier-with-eslint",
  "scripts": {
    "format": "prettier --write app.js",
    "lint": "eslint --fix app.js",
    "fix": "npm run format && npm run lint",
    "prepare": "husky install"
  },
  "lint-staged": {
    "*.js": [
      "prettier --write",
      "eslint --fix"
    ]
  },
  "devDependencies": {
    "eslint": "^7.26.0",
    "eslint-config-prettier": "^8.3.0",
    "husky": "^6.0.0",
    "lint-staged": "^11.0.0",
    "prettier": "^2.3.0"
  }
}
```

上記のようにすることで、ステージングに追加された JavaScript ファイルに対して、`prettier --write`と`eslint --fix`が実行される。

これで`git commit`時に`lint-staged`が実行され、Prettier と ESLint が実行されるようになった。

## `git commit`時に Prettier と ESLint が実行されるか確認する

![pre-commit.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69667/3c219788-677f-84c9-8470-9ffe36568c2a.gif)

`git commit`時に Prettier と ESLint が実行され、エラーを解消しなければコミットできなくなった。

# 終わり

コードは綺麗に越したことはないし、手軽に導入できるので少しでも魅力に感じたら利用してみましょう。

pre-commit フックまで利用するのは堅苦しく感じる方もいると思いますので、とりあえずはコマンドで整形するかエディタと連携させれば良いと思います。

## お知らせ

Udemy で webpack の講座を公開したり、Kindle で技術書を出版しています。

Udemy:
[webpack 最速入門](https://www.udemy.com/course/practical-webpack/?couponCode=B6D5AE3897554CBBDAD7)（**~~10,800 円~~ -> 2,000 円**）

Kindle（Kindle Unlimited だったら無料）:
[React Hooks 入門](https://www.amazon.co.jp/gp/product/B08JTXQ2M6)（500 円）

興味を持ってくださった方はご購入いただけると大変嬉しいです。よろしくお願いいたします。