husky+lint-stagedでコミットする前に自動整形を行ってくれるようにする

# はじめに
よくメンターの人から「コードフォーマット効いていない!!」と言われていました。
毎回「ごめんなさい ごめんなさい」と思いながら再度pushする日々でした。
自動でGitにPushする際にフォーマッターが動けばいいと思い調べてみたら、huskyとlint-stagedで自動整形を行うようにできるらしい。

今回は、huskyとlint-stagedの実装の仕方やつまずいたところを紹介します！

# そもそもhuskyとlint-stagedとは？

huskyとはnpm パッケージの１つで、commitやpushなどのGitの操作を行う際に自動でコマンドを実行できるもの。
lint-stagedとは、変更したファイルに対してフォーマットをかけることができるもの。
今回は、フォーマッターを実行しコードの整形を行ってからGitにPushを行うようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3464628/31c1cabe-223b-2eeb-5c56-7b656d84b54b.png)

# 導入方法
※ huskyやpre-commitのバージョンによってはNode.jsのバージョンをアップデートする必要がある

yarnの導入方法を記述する
### 1. Prettierのインストール
Prettierとは自動でコードを成形してくれるツール
```sh:ターミナル
$ yarn add --dev --exact prettier
```
### 2. ESLintのインストール (任意)
ESLintとはコードルールを検証するツール
```sh:ターミナル
$ yarn add eslint
$ yarn add --dev eslint-config-prettier eslint-plugin-import
```
`eslint-config-prettier` : ESLintフォーマットルールを無効化し、バグを検出するルールのみを有効にするプラグイン
`eslint-plugin-import` : importの順番を定義するプラグイン
### 3. huskyとlist-stagedのインストール
```sh:ターミナル
$ yarn add husky lint-staged
```
### 4. huskyのセットアップ
以下のコマンドを実行することで、`.husky`ディレクトリとその中に`.husky.sh`というファイルが作成される。
![スクリーンショット 2024-06-10 23.28.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3464628/829a89ec-ff82-c3e7-d026-fd2b580270c8.png)

```sh:ターミナル
$ yarn husky
```
※ huskyのバージョンv9以前の場合
```sh:ターミナル
$ yarn husky install
```
### 5. hooksを作成
pre-commitの設定ファイルを追加
```sh:ターミナル
$ echo "npx lint-staged" > .husky/pre-commit
```
### 6. package.jsonに以下のようなものを記述
sacriptsに記述:yarnを実行した際に".hosky/_"というGitHooksの設定を作成するため。
※husky V9以前の場合は`husky install`と記述
`lint-staged`の要素を追加: ファイルの拡張子を指定してeslint,prettierを実行している

```text:package.json
"scripts": {
    ~~省略~~
    #"prepare": "husky"　# ← yarn2以降を使用している場合は使えない
	"postinstall": "husky"# ← yarn2以降でscriptsを書きたい場合はこちらを用いる
  },
~~省略~~
"lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
```
lint-stagedの説明
- `eslint --fix`:Eslintのルールに従ってコードを整形し、エラーを解消するスクリプト
- `prettier --write`:フォーマッターを行いコードを上書きするスクリプト

## 複数プロジェクト(monorepo環境)の場合
※ `.git`を参照するためrootディレクトリにhuskyをダウンロードする
自分はmonorepo環境での実装でつまずいた。


構成
```
├─package.json
├── /frontend
│   ├── ...
│   └── package.json
└── /backend
    ├── ...
    └── package.json
```
### 1. 個々のプロジェクトに移動する
```sh:ターミナル
$ cd frontend もしくは backend
```
### 2. Prettierのインストール(プロジェクト内で)
```sh:ターミナル
$ yarn add --dev --exact prettier
```
### 3. ESLintのインストール(プロジェクト内で) 任意
```sh:ターミナル
$ yarn add eslint
$ yarn add --dev eslint-config-prettier eslint-plugin-import
```
### 4. huskyのインストール(プロジェクト内で)
```sh:ターミナル
$ yarn add husky lint-staged
```
### 5. package.jsonに以下のようなものを記述(プロジェクト内で)
```plaintext:package.json
"lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
```
1~5までの操作をfrontend,backendどちらでも行う
### 6. rootディレクトリにhusky設定ファイルを作成
```sh:ターミナル
$ yarn husky
```
※husky V9以前の場合は`yarn husky install`と記述
```sh: ターミナル
$ yarn husky install
```
### 7. `package.json`にhuskyの設定を記述 (rootディレクトリー)
```plaintext:package.json
"scripts": {
	~~~~~
    "postinstall": "husky"
}
```
### 8. hooksの作成
```sh:ターミナル
$ echo "npx lint-staged" > .husky/pre-commit
```

# monorepo環境で実装する際につまずいたこと

### huskyの初期設定をどこに記述するか

1. rootディレクトリに記述する場合 → **今回採用**
   - 問題なく機能するが、rootのpackage.jsonに記述したくない
2. 各プロジェクトのpackage.jsonに記述する場合
   - hooksPathの問題により、最終的にはどちらか一方のプロジェクトの設定しか適用されない [参考](https://www.tee-talog.com/entry/2023/11/19/145218)

### 2番ができない理由、hooksPathの制約

- core.hooksPathには1つのパスしか指定できないため、最終的にどちらか一方のworkspaceのパスしか記載されない
- hooksPathに指定された.husky/_の設定しか動作しない


### Lefthookを用いることで上記の問題を解決することができる

- 個々のプロジェクト毎にhookするファイルを制限できる
- 設定ファイルがローカル、グローバルどこにあっても参照可能
- 並列実行が可能
- Node.js以外のプロジェクトも管理可能
- Lefthookは、lefthookバイナリがシステムのどこかにインストールされていれば認識される
# まとめ
huskとlint-stagedをモノレポ環境で使うとなると少し制限があり使いずらいので、Lefthookで実装することをお勧めする。

これで、自信を持って「**お願いします!!**」という感じにレビューをお願いすることができる。
今回の記事のモノレポ問題は、メンターと一緒に討論をしここまで調べた。これを一人でできるようになりたいそう日々頑張っている自分である。



# 参考URL
[モノレポ構成で husky 設定方法](https://gist.github.com/x7ddf74479jn5/59b51fb4023a9e581185f0aa9c30e17d)
[コードを綺麗に保ちたい？それhuskyでできるよ！](https://qiita.com/mu-suke08/items/43a492fda5cd71a31506)
[husky公式](https://typicode.github.io/husky/)
[lefthook公式](https://github.com/evilmartians/lefthook)
[Lefthook: 多機能Gitフックマネージャ](https://techracho.bpsinc.jp/hachi8833/2019_10_16/79052)
[Git Hooks を複数実行したかった](https://www.tee-talog.com/entry/2023/11/19/145218)