AngularプロジェクトにPrettier導入

コードフォーマットは個人の好みが出るし、「ここは改行すべきか」など考えるより、コードフォーマッターに全て任せてしまった方が圧倒的に楽です。
Angularプロジェクトに今人気のPrettierを導入し、CIでフォーマットチェックするようにしました。

[Prettier | Opinionated Code Formatter](https://prettier.io/)

# インストール
```
$ yarn add prettier -D
```

# フォーマットルール
プロジェクトのルートディレクトリに `.prettierrc` というコンフィグファイルを作り、フォーマットルールを記載します。

Prettierには[デフォルトのフォーマットルール](https://prettier.io/docs/en/options.html)があるので、上書きしたいルールだけを書けば良いです。

僕は全てデフォルトのルールを使っていますが、どんなルールが使われているのか誰でもぱっと見で分かるように、あえてデフォルトのルールも記載しています。

```json:.prettierrc
{
  "printWidth": 80,
  "singleQuote": true,
  "useTabs": false,
  "tabWidth": 2,
  "semi": true,
  "bracketSpacing": true
}
```

# VSCodeの設定
VSCodeに[PrettierのExtension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)をインストールし、ユーザー設定に以下の1行を追加します。これで、ファイルを保存するたびに自動的にファイルをフォーマットしてくれます。

```json
"editor.formatOnSave": true
```
![prettier.gif](https://qiita-image-store.s3.amazonaws.com/0/47414/8f586ea9-88be-25f2-efa1-01d417986f2e.gif)

特定の言語だけ設定したい場合は、以下のように書きます。

```json
"[typescript]": {
    "editor.formatOnSave": true
},
"[javascript]": {
    "editor.formatOnSave": true
},
"[scss]": {
    "editor.formatOnSave": true
}
```
ちなみに、プロジェクトのルートディレクトリに `.vscode/settings.json` を作ってVSCodeの設定を書いておけばプロジェクトメンバーで設定を共有できますが、`editor.formatOnSave` はユーザーごとに設定しないと効きませんでした。

VSCode以外のエディタを使ってる方はこちら
-> [Editor Integration | Prettier](https://prettier.io/docs/en/editors.html)

# TSLintのフォーマットルールを削除
`ng new` でAngularプロジェクトを作成すると、デフォルトでTSLintがついてきます。
TSLintにもコードフォーマットの機能がありますが、フォーマットに関してはPrettierの方が優れているので、Prettierと併用する場合はTSLintのフォーマット機能は使わない方が良いです。
Prettierはコードフォーマット、TSLintはそれ以外のクオリティチェックというように役割を分けましょう。

ということで、TSLintのコンフィグからフォーマットルールを削除します。

```diff:tslint.json:
{
  "rulesDirectory": ["node_modules/codelyzer"],
  "rules": {
    "callable-types": true,
    "class-name": true,
--  "comment-format": [true, "check-space"],
--  "curly": true,
--  "eofline": true,
    "forin": true,
    "import-blacklist": [true],
--  "import-spacing": true,
--  "indent": [true, "spaces"],
    "interface-over-type-literal": true,
    "label-position": true,
--  "max-line-length": [true, 140],
    "member-access": false,
    "member-ordering": [
      true,
      "static-before-instance",
      "variables-before-functions"
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [true, "ignore-params"],
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
--  "no-trailing-whitespace": true,
    "no-unused-expression": true,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
--  "one-line": [
--    true,
--    "check-open-brace",
--    "check-catch",
--    "check-else",
--    "check-whitespace"
--  ],
    "prefer-const": true,
--  "quotemark": [true, "single"],
    "radix": true,
--  "semicolon": ["always"],
    "triple-equals": [true, "allow-null-check"],
--  "typedef-whitespace": [
--    true,
--    {
--      "call-signature": "nospace",
--      "index-signature": "nospace",
--      "parameter": "nospace",
--      "property-declaration": "nospace",
--      "variable-declaration": "nospace"
--    }
--  ],
    "typeof-compare": true,
    "unified-signatures": true,
    "variable-name": false,
--  "whitespace": [
--    true,
--    "check-branch",
--    "check-decl",
--    "check-operator",
--    "check-separator",
--    "check-type"
--  ],
    "directive-selector": [true, "attribute", "", "camelCase"],
    "component-selector": [true, "element", "", "kebab-case"],
    "use-input-property-decorator": true,
    "use-output-property-decorator": true,
    "use-host-property-decorator": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-life-cycle-interface": true,
    "use-pipe-transform-interface": true,
    "component-class-suffix": true,
    "directive-class-suffix": true,
    "no-access-missing-member": true,
    "templates-use-public": true,
    "invoke-injectable": true
  }
}

```

# CIでフォーマットチェック
せっかくプロジェクトでコードフォーマットを統一できるようになったので、CIでフォーマットをチェックするようにしたいですよね。
やり方は簡単で、以下のようなnpm scriptを用意して、CIで `npm run format:check` を実行してやるだけです。

```json:package.json
"scripts": {
  "format:check": "prettier --config ./.prettierrc --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\""
}
```

CircleCIで実行した場合、コードフォーマットのチェックに引っかかったらこんな感じでFailします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/47414/a227ab56-623c-9d2e-a86d-c0c1793681a5.png)

そのうちAngularが公式にPrettierを採用してほしい。

# 参考
- [Setting up Prettier in an Angular CLI Project](https://medium.com/@victormejia/setting-up-prettier-in-an-angular-cli-project-2f50c3b9a537)