huskyとlint-stagedでいい感じにコードを整える備忘録
huskyとlint-stagedを組み合わせて、git commit直前でlinter、prettierを走らせてますが、huskyがv7になってから導入方法が変わった模様。
huskyとlint-stagedのそれぞれの特徴とかも含めて備忘録として残しとく。
huskyとlint-staged
husky自体はGit Hooksを管理していて、コミットやプッシュなどのアクションが発生した時に特定のスクリプトを実行できます。
git initした際にそのディレクトリ内に.git
ディレクトリが作成されるが、辿っていくと.git/hooks/pre-commit.sample
という構成になっており、hooks内のものが対象のアクションになります。
lint-stagedはGitのステージに移動したファイル(git addしたファイル)を対象として、lintやprettierを実行することができます。
セットアップ
ドキュメント通りだけど、以下の順にコマンドを実行。
husky
- huskyをローカルインストール
npm i -D husky
- Git Hooksの有効化
npx husky install
- npm installをトリガーとしてhusky installを実行するスクリプトを用意する
例えば、新しくプロジェクトに入った人がgit cloneして最初にnpm installする際に、同時にhusky installも叩かれることによりローカルでGit Hooksのアクションを有効化できます。
npm pkg set scripts.prepare="husky install"
実行すると、package.jsonのscriptにprepareが追加される
{
...
"scripts": {
"prepare": "husky install"
},
"dependencies": {
...
},
"devDependencies": {
...
}
}
- npm run prepareの実行
husky installがまだ実行されていないので、npm run prepareを実行。
npm run prepare
実行すると、.husky
ディレクトリが作成され、Git Hooksの参照先の変更、参照されるシェルスクリプトの作成を行います。
既存のGit Hooks(.git/hooks)に修正をかけるわけでなく、既に設定されているGit Hooksをローカルマシンに適用することができます。
つまり、Git Hooksのトリガーとなる操作が実行されると、git/hooks
の代わりに.husky
が参照されるようになる。
こちらを参考にさせてもらいました。より詳しくはこちらをどうぞ。
- Git Hooksをトリガーとして実行するコマンドを追加
.husky
配下にpre-commitというファイル名に(今回はlint-stagedを実行したいので)npx lint-staged
というコマンドを追加します。
これで、pre-commit(コミット直前)をトリガーとしてnpx lint-staged
というコマンドが実行されるわけです。
もちろん、このまま実行しても動かないので、次はlint-stagedの設定を行います。
npx husky add .husky/pre-commit "npx lint-staged"
lint-staged
- lint-stagedをローカルインストール
lint-stagedとついでにESlintとprettierもインストール。
npm i -D lint-staged eslint prettier
以下を実行して、eslintとprettierの設定ファイルを作成。
npm init @eslint/config
echo {}> .prettierrc.json
TypeScriptプロジェクトの場合、こちらも必要になると思うので、こちらもインストール。
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier
- @typescript-eslint/parser
ESLintがTypeScriptをlint できるようにするESLintパーサー - @typescript-eslint/eslint-plugin
TypeScriptコードベースにlintルールを提供するESLintプラグイン - eslint-config-prettier
prettier が整形したコードに対して ESLint がエラーを出力しないようにするプラグイン
- lint-stagedの設定
lint-stagedの設定方法はpackage.json
にlint-stagedの設定するか、.lintstagedrc
という設定ファイルを作成する方法などありますが、今回はpackage.jsonに設定。
{
"script": {
"prepare": "husky install"
},
"dependencies": {
...
},
"devDependencies": {
...
},
"lint-staged": {
"*.{ts, tsx}": "eslint --fix",
"*": "prettier --write"
}
}
この設定をすることで、git addでステージに上ったファイルを対象にgit commit直前にeslintとprettierを走らせることができます。
モノレポのプロジェクトの場合
例えば、モノレポのプロジェクトでバックエンドとフロントのディレクトリが分かれている場合(例: /project/server
、/project/front
)、少し工夫が必要。
モノレポなので、.gitディレクトリは/project
にあるが、package.jsonは/server
と/front
配下にそれぞれある、というパターンになるかと。
その場合、prepareコマンドの中身やpre-commitファイルの中身を変更する必要があります。
-
/server
、/front
の各ディレクトリでprepareスクリプトをセット
/project/front
から/project
(.gitがあるディレクトリ)に移動してhuskyをfront配下にインストール。
npm pkg set scripts.prepare="cd .. && husky install front/.husky"
インストール後、npm run prepare
を実行して、front配下に.huskyディレクトリが作成されることを確認。
- pre-commitファイルの修正
以下のコマンドを実行し、npx lint-staged
というコマンドをpre-commitファイルに追加。
npx husky add .husky/pre-commit "npx lint-staged"
ただし、lint-stagedコマンドは、package.jsonがあるディレクトリで行う必要があるので、cd front
コマンドを追加。
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
cd front
npx lint-staged
これでserver
、front
の各ディレクトリ配下にあるファイルでステージ移動したファイルを対象にgit commitすると、lint-stagedで設定してあるeslintとprettierが動く感じです。
このように、モノレポの場合はprepareスクリプトやpre-commitファイルにディレクトリ移動のコマンドを追加する必要があるという。
設定方法が変わった背景・理由
従来は.huskyrc.js
と.git/hooks
を使ってhooksを動かしていたようで、前者はユーザーが実行したいhooksを定義して、後者はhuskyがhooksを(できるだけ多く?可能な限り?)インストールしていたらしいです。
ユーザーが.huskyrc.js
を自由に追加したり更新かけると、自動で変更内容を反映してくれるのは良いけど、一方で何もしなくてもnodeが起動してしまうとのこと。
例えば、.huskyrc.js
にhooksとしてcommit-msgを追加して変更が反映しても、.git/hooks
にはそのhooksが追加されてないので差分が発生して、その差分を埋めるためのボイラープレートが必要になってしまう(的なことを言ってるんだと思います)。
ところが、Gitがバージョン2.9からcore.hooksPath
を導入し、.git/hooks
を使わずに別のディレクトリを使うように指示することができるようになりました。
これによって、husky installコマンドの実行によって、.git/hooks
を使わず.husky/
を使うようになったとのことです。
↑解釈間違っていたら教えてください。。
おわりに
huskyもlint-stagedも今まで使ってはいましたが、そこまで知らなかったので調べてアウトプットするいい機会でした。そして、zennの初記事でした。
参考
Discussion