ESLint + Prettier + husky + lint-staged を Biome + Lefthook で置き換える

## この記事の概要

JavaScript のプロジェクトを始めるとき、以下のようなことを実施することが多いですよね。

- linter と formatter を導入する
- pre-commit（または pre-push）のタイミングで lint や format のチェックをする

この記事を書いている 2024 年 6 月現在、一番よく使われているのは次の構成ではないでしょうか。

- ESLint
- Prettier
- husky
- lint-staged

これらのツール、補助ツールの割には導入する数が多いし、設定することも多いように思います。

私自身がプロジェクトの依存関係は少ない方が好きなのもあり、もっとシンプルにできないものか……と考えていました。

そこで、以下の 2 つに統合できるのではないか？と思い記事にしてみました。

- Biome
- Lefthook

## インストールと初期化

### Biome

Biome は`--save-exact`をつけてのインストールが推奨されています[^biome-save-exact]

[^biome-save-exact]: https://biomejs.dev/internals/versioning/

```sh
npm install --save-dev --save-exact @biomejs/biome
```

設定ファイルである`biome.json`を作成するため`init`を実施しておきます。

```sh
npx @biomejs/biome init
```

最初はこのようなファイルが生成されます。
lint や format のルールに特段のこだわりがなければこのルールのままで問題ありません。

```json
{
	"$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
	"organizeImports": {
		"enabled": true
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true
		}
	}
}
```

### Lefthook

Lefthook は色々なインストール方法がありますが、ここでは npm を使います。

```sh
npm install lefthook --save-dev
```

設定ファイルである`lefthook.yml`を作成するため`install`を実施しておきます

```sh
npx lefthook install
```

見本としてコメントアウトされたコードがたくさん並んでいます。
こちらを参考にしながら後で設定していきます。

```yml
# EXAMPLE USAGE:
#
#   Refer for explanation to following link:
#   https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md
#
# pre-push:
#   commands:
#     packages-audit:
#       tags: frontend security
#       run: yarn audit
#     gems-audit:
#       tags: backend security
#       run: bundle audit
#
# pre-commit:
#   parallel: true
#   commands:
#     eslint:
#       glob: "*.{js,ts,jsx,tsx}"
#       run: yarn eslint {staged_files}
#     rubocop:
#       tags: backend style
#       glob: "*.rb"
#       exclude: '(^|/)(application|routes)\.rb$'
#       run: bundle exec rubocop --force-exclusion {all_files}
#     govet:
#       tags: backend style
#       files: git ls-files -m
#       glob: "*.go"
#       run: go vet {files}
#   scripts:
#     "hello.js":
#       runner: node
#     "any.go":
#       runner: go run
```

## 具体的な設定

### Biome

ESLint と Prettier のルールがたくさんある場合は migration 用のコマンドを実行するのが楽です。
使っているプラグインなどによって上手く移行できるできないがあるとは思いますが……とりあえず試してみるのが良いと思います。

```sh
npx biome migrate eslint --write
npx biome migrate prettier --write
```

公式ドキュメントに詳しく記載されています。

https://biomejs.dev/guides/migrate-eslint-prettier/

### Lefthook

（おそらく）最もよくあるパターンである、以下を想定して設定をします。

- commit 前に lint と format を実施する
- staged ファイルだけを対象にする

```yml
pre-commit:
  commands:
    biome:
      glob: "*.{js,ts,jsx,tsx}"
      run: npx @biomejs/biome check --write {staged_files}
```

これで完了です。

Biome では lint と format を両方実行する check というコマンドがあるので、それを実施しています。[^biome-check]

[^biome-check]: https://biomejs.dev/guides/getting-started/#usage

また、対象とするファイルの種類も、デフォルトで`{staged_files}`というものが用意されているのでそれを指定するだけです。[^left-hook-files]

[^left-hook-files]: `{staged_files}` に指定できるもの、指定の仕方は公式ドキュメントに記載されています。 https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#run

## 最後に

ESLint と Prettier の競合を回避するために `eslint-config-prettier` を入れるとか、husky を使うために package.json に`"prepare": "husky install"`をするとか、微妙に煩雑だったプロセスがなくなりました。

業務レベルでは試せていませんが、個人で進める小さなプロジェクトでは十分な気がしています。