husky + lint-staged で品質を保つ

リントツールをインストールしても使わなければ意味がない。

というわけで人的なリント忘れを未然に防ぐためにも、huskey と lint-staged と 各種リントツールを組み合わせてコミット前に自動的にリントが行われる環境を作りましょう。リントエラー時にコミットを弾くことによって git 上のコードの品質を保つことができます。

環境

husky

git hooks の設定を package.json などからできるようにしてくれるツールです。
生の git hooks だとチーム内で共有とかがしにくいですが、husky を使って設定を package.json などに書けば簡単に共有できます。

pre-commit フックを使うことが多いと思いますが、クライアントサイドのフックであればすべてサポートしているみたいです。

lint-staged

ステージングしたファイルに対して特定のコマンドを実行できるツールです。

流れとしては、husky で pre-commit フックに lint-staged を設定し、lint-staged から各種リントツールを実行する感じになります。

インストールと設定

では適当なディレクトリを作って husky と lint-staged をインストールします。git リポジトリとして初期化して node_modules/ を .gitignore に入れときましょう。

$ mkdir hooktest && cd hooktest
$ yarn add -D husky lint-staged
$ git init
$ echo node_modules/ > .gitignore

設定は package.json に記述します。

{
  "devDependencies": {
    "husky": "^1.1.0",
    "lint-staged": "^7.3.0"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
  }
}

基本的な設定は上のようになります。ここに各種リントツールを追加していきます。

HTMLHint

まずは HTMLHint を追加しましょう。

$ yarn add -D htmlhint

package.json の lint-staged 部分を以下のように変更します。

  "lint-staged": {
    "*.html": "htmlhint"
  }

左側にファイルのマッチング条件、右側に実行するコマンドを書きます。
lint-staged が実行されるとステージングされたファイルの中からマッチングするファイルが抽出され、指定したコマンドに引数として渡されます。指定したコマンドはマッチングするファイルが存在するときのみ実行されます。

リントでエラーが発生した場合はコミットは行われません。

試しに index.html を以下の内容で作成し、コミットしてみましょう。

<html>
</html>
$ git add index.html
$ git commit
husky > pre-commit (node v10.1.0)
 ❯ Running tasks for *.html
   ✖ htmlhint
✖ "htmlhint" found some errors. Please fix them and try committing again.

/Users/user/Desktop/hooktest/index.html
      L2 |<html>
          ^ Doctype must be declared first. (doctype-first)

Scanned 1 files, found 1 errors in 1 files (17 ms)
husky > pre-commit hook failed (add --no-verify to bypass)

エラーが出てコミットはキャンセルされました。
index.html を以下のように修正してもう一度コミットしてみます。

<!doctype html>
<html>
</html>
$ git add index.html
$ git commit
husky > pre-commit (node v10.1.0)
 ✔ Running tasks for *.html
[master e899d8a] fix
 1 file changed, 1 insertion(+)

コミットメッセージを入力する画面に遷移して無事コミットできました。

stylelint

stylelint で CSS のリントもしてみます。

$ yarn add -D stylelint

.stylelintrc に適当にルールを書きます。

{
  "rules": {
    "block-no-empty": true
  }
}

package.json の lint-staged を書き加えます。

  "lint-staged": {
    "*.html": "htmlhint",
    "*.css": "stylelint"
  }

ルールに違反する style.css を作ってコミットしてみます。

div {
}
$ git add style.css
$ git commit
husky > pre-commit (node v10.1.0)
 ↓ Running tasks for *.html [skipped]
   → No staged files match *.html
 ❯ Running tasks for *.css
   ✖ stylelint
✖ "stylelint" found some errors. Please fix them and try committing again.

style.css
1:5  ✖  Unexpected empty block   block-no-empty
husky > pre-commit hook failed (add --no-verify to bypass)

ちゃんとエラーとなりました。

ESLint

最後に JavaScript もリントできるようにしましょう。
ESLint を使用します。

$ yarn add -D eslint

.eslintrc に適当にルールを書いて、script.js にルールに違反するスクリプトを書きます。

{
  "rules": {
    "quotes": ["error", "single"]
  }
}
var a = "hello";

.package.json の lint-staged を更新します。

  "lint-staged": {
    "*.html": "htmlhint",
    "*.css": "stylelint",
    "*.js": "eslint"
  }

コミットしてみます。

$ git add script.js
$ git commit
husky > pre-commit (node v10.1.0)
 ↓ Running tasks for *.html [skipped]
   → No staged files match *.html
 ↓ Running tasks for *.css [skipped]
   → No staged files match *.css
 ❯ Running tasks for *.js
   ✖ eslint
✖ "eslint" found some errors. Please fix them and try committing again.

/Users/user/Desktop/hooktest/script.js
1:9  error  Strings must use singlequote  quotes

✖ 1 problem (1 error, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

husky > pre-commit hook failed (add --no-verify to bypass)

ちゃんとエラーとなります。


ESLint の --fix オプションを合わせて使うこともできます。
その場合は修正後ファイルをもう一度ステージングするために git add コマンドの実行も必要となります。

  "lint-staged": {
    "*.html": "htmlhint",
    "*.css": "stylelint",
    "*.js": ["eslint --fix", "git add"]
  }

git add コマンドを忘れると修正前のファイルがコミットされてしまうので注意が必要です。

$ git commit
husky > pre-commit (node v10.1.0)
 ↓ Running tasks for *.html [skipped]
   → No staged files match *.html
 ↓ Running tasks for *.css [skipped]
   → No staged files match *.css
 ✔ Running tasks for *.js
[master d95e410] fix
 1 file changed, 1 insertion(+), 1 deletion(-)

すべての違反が自動修正で解決できる場合はそのままコミットされます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です