6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Git hookマネージャー"lefthook"でコミット前にeslintとprettierを通す

Last updated at Posted at 2024-06-21

なぜやろうと思ったか?

フロントエンドクラブのdiscordで
「githook使うならlefthookがいいですよ!」という知見をいただき、自分も使いたくなったため。

lefthookとは?

2019年から開発されているGit hookのマネージャーツール。
lefthookのリポジトリ

特徴は、

  • goで書かれていて高速
  • 柔軟に渡すファイルやパラメータをコントロールできる
  • 依存性のない単一バイナリなのでどんな環境でも動く

こと。

husky + lint-stagedとの違い

よくhusky + lint-staged構成と比較されることが多いので解説します。

husky

huskyはGit hookを簡単にするためのツールです。
huskyにも、

Modern native git hooks made easy

という記述があります。
より包括的な対応を行えるleft hookよりも対応範囲は狭いイメージですね。

lint-staged

lint-stagedは、lefthookのstage_fixedプロパティを一つのライブラリにした感じです。
lintやformatの内容はhuskyだけだとステージングに追加されないため、これを利用してステージングにあげる部分を対応しています。

husky + lint-stagedと比べてどこがいいの?

個人設定を作れる

lefthook-local.ymlに個人環境での設定をそれぞれ作ることで、不要なツールの実行をスキップできます。
個人の環境に合わせて、対象ファイルを変更したり、パラメータを変更したりももちろん対応可能です。

huskyでは無理やり対応できないこともないですが、推奨されていないですし、管理が煩雑になります。

monorepoでの設定に向いてる

いわゆるモノレポ構成ですが、

├── /client
│   ├── ...
│   └── ...
└── /server
    ├── ...
    └── ...

huskyで対応しようとすると、cdでサブディレクトリに移動してからコマンドを叩くような記述が必要になるのと、書くディレクトリ内にhuskyの設定ファイルが必要になります。

lefthookで対応すると、lefthook.ymlの変更だけでサクッと対応できます。
rootにmonorepoの各サブディレクトリを設定するだけです。

pre-commit:
  parallel: true
  commands:
    client:
      root: client/
      glob: '*.{ts}'
      run: // ここに実行したいコマンド
    server:
      root: server/
      glob: '*.{ts}'
      run: // ここに実行したいコマンド

依存関係

husky + lint-stagedでは二つのライブラリをインストールする必要がありますし、node.jsランタイムに載せる必要があります。
例えばどっちかが使えなくなった時はまた構成を考え直す必要がありますし、node.js以外の環境での利用はできません。

lefthookは単一のバイナリで他に依存するライブラリがありませんし、多数のインストール方法を兼ね備えています。
https://github.com/evilmartians/lefthook/blob/master/docs/install.md

その他

  • husky + lint-stagedより早い
    • goで書かれている
    • 並列実行ができる(parallel)

実際に使ってみる

ちょうどフォーマット忘れ、lint忘れに悩んでいたので、
対応中のVue製PJに導入して威力を確認してみます。

既存の運用

元々はnpm create vue@latestしたら生成されるpackage.jsonのデフォルトのeslint、prettierを一々叩かなくてはいけませんでした。

"script": {
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "format": "prettier --write src/"
}

なので、コミットした後にフォーマット忘れに気づいてnpm run formatしただけのコミットが生まれたり、ideのエラー警告に頼ってlintを全く使っていなかったりしていました。

lefthookのインストール、設定

npm i -D lefthook

してライブラリを追加します。

lefthookを導入すると、リポジトリ直下にlefthook.ymlが自動で追加されます。やさしい。
ファイル内にコメントで基本的な書き方も教えてくれています。

今回はpre-commitのタイミングでprettierとeslintを通したいため、下記のような設定とします。

pre-commit:
# parallelにすると並列実行される。
  parallel: true
  commands:
    prettier:
# 対象のファイル
      glob: '*.{vue,tsx,ts,mts,mcs,mjs,cjs,js,json,md,yml,yaml}'
      run: |
# 実行コマンド。npmだと動かなかったのでnpxにした
        npx prettier --write {staged_files}
# 修正内容をステージングするかどうか?
      stage_fixed: true
# マージ、リベースの際はこの処理をスキップしている。
      skip:
        - merge
        - rebase
    eslint:
      glob: '*.{vue,js,jsx,cjs,mjs,ts,tsx,cts,mts}'
      run: |
        npx eslint --fix {staged_files} --ignore-path .gitignore
      stage_fixed: true
      skip:
        - merge
        - rebase

これで適当なファイル作って確認してみましょう。
まずprettierの確認のために、

  • ぐちゃぐちゃなインデント
  • 不要なセミコロン

を入れてみます。

<script setup lang="ts">
 const a = 0;
   const b = 1;
</script>

<template>
  <div>
    <p>
       </p>
</div>
</template>

これをコミットしたら自動でこうなりました。
ちゃんと修正されていますね。

<script setup lang="ts">
const a = 0
const b = 1
</script>

<template>
  <div>
    <p></p>
  </div>
</template>

あと、ついでにですがlintから警告も出ていました。
宣言されたのに使われてないことをログでちゃんと教えてくれています。

┃  eslint ❯ 
SampleView.vue
  2:7  warning  'a' is assigned a value but never used  @typescript-eslint/no-unused-vars
  3:7  warning  'b' is assigned a value but never used  @typescript-eslint/no-unused-vars
✖ 2 problems (0 errors, 2 warnings)

次に問題のあるjsを書いてeslintでエラーを出してみます。
constに再代入してエラーが出るか確認してみます。

<script setup lang="ts">
const a = 0
a = 1
</script>

<template>
  <div>
    <p></p>
  </div>
</template>

こちらをコミットしようとすると、エラーになります。
コミット前にエラー終了するため、コミット自体が成功せず、変更はステージングされたままになってくれます。

┃  eslint ❯ 
SampleView.vue
  3:1  error    'a' is constant                         no-const-assign
  3:1  warning  'a' is assigned a value but never used  @typescript-eslint/no-unused-vars
✖ 2 problems (1 error, 1 warning)
⠙
exit status 1                                      

参考文献

各公式ドキュメント
https://github.com/evilmartians/lefthook/tree/master/docs
https://typicode.github.io/husky/
https://github.com/lint-staged/lint-staged

設定はほぼこの方の記述ママで、私はVueやnpm環境に合わせて多少書き方を変えてるだけです。助かりました。
正直これが最適解だと思うので変えるならstage_fixedぐらいかなという感じです。
https://zenn.dev/kimuson/articles/husky_to_lefthook

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?