Astro プロジェクトの ESLint と Prettier を Biome に置き換える

## この記事の概要

JavaScript や TypeScript を扱う中で lint と format を考える際、ESLint と Prettier を組み合わせるのが鉄板[^eslint-and-prettier]かと思います。

[^eslint-and-prettier]: 少なくともこの記事を書いている 2024 年 6 月現在はシェア 1 位の組み合わせのはずです。

ですが、[Biome](https://biomejs.dev/) というツールが最近[^release-date-of-biome]出てきました。
どうやら Biome 単体で lint も format も担えるようです。

[^release-date-of-biome]: 2023 年 8 月 29 日に v1 がリリースされたようです。 https://biomejs.dev/blog/biome-v1/

私は面倒くさがりなので使うツールや設定はできるだけ減らしておきたく、ESLint と Prettier を Biome に置き換えてみようと思いました。

試してみた際の備忘録の記事です。

## 前提

- 既存プロジェクトの ESLint と Prettier を Biome に置き換える
    - できるだけ設定が複雑にならないことが優先
    - 既存のルールから変わっても良しとする
- 置き換える対象のプロジェクトでは Astro を使っている
    - `.js` と `.ts` だけでなく `.astro` ファイルの設定もする
- `biome migrate` は使わない
    - 自分の勉強がてらの取り組みなので、せっかくだから大変な思いをしておく

## 環境

置き換える前の状態はこのようになっていました。

| 既存の依存関係 | バージョン |
| --- | --- |
| astro | 3.6.4 |
| @typescript-eslint/eslint-plugin | 6.21.0 |
| eslint | 8.57.0 |
| eslint-config-prettier | 9.1.0 |
| eslint-plugin-astro | 0.31.4 |
| eslint-plugin-jsx-a11y | 6.8.0 |
| prettier | 3.2.5 |
| prettier-plugin-astro | 0.13.0 |

置き換えた後はこうなっています。

| 新しい依存関係 | バージョン |
| --- | --- |
| astro | 3.6.4 |
| @biomejs/biome | 1.8.1 |

lint, format 関連で 7 つあった依存関係が 1 つにまとまるのは嬉しいです。

## 既存の ESLint, Prettier の設定

```javascript:.eslintrc.cjs
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:astro/recommended",
    "plugin:astro/jsx-a11y-recommended",
    "prettier",
  ],
  overrides: [
    {
      files: ["*.astro"],
      parser: "astro-eslint-parser",
      parserOptions: {
        parser: "@typescript-eslint/parser",
        extraFileExtensions: [".astro"],
      },
    },
  ],
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
};
```

```javascript:.prettierrc.cjs
module.exports = {
  plugins: [
    require.resolve("prettier-plugin-astro"),
  ],
  overrides: [
    {
      files: "*.astro",
      options: {
        parser: "astro",
      },
    },
  ],
};
```

ご覧の通り、デフォルト設定や推奨設定を適用する以外のことはしていません。

Biome でもこういった具合で、推奨設定を適用して終わりとするつもりでいます。

## インストール

ターミナルで次のコマンドを叩きます。

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

lint や format の修正によりスクリプトが通らなくなるかもしれないとのことで、`--save-exact`をつけてのインストールが推奨されています。[^biome-versioning]

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

## 設定ファイルの初期化

次のコマンドを叩くことで`biome.jsonc`が作成されます。

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

なお`--jsonc`はオプションで、無しだと`biome.json`が生成されます。
なんだかんだとコメントを書きたいことは多いので、今回は`--jsonc`を適用しました。

作成された`biome.jsonc`の中身はこのようになっています。[^default-configuration]

[^default-configuration]: かなりどうでも良い話ですが、2024 年 6 月 16 日現在は init コマンドを適用した時点で`organizeImports`が`"enabled": true`になっているものの、ドキュメントに記載されているデフォルト設定は`"enabled": false`です。 https://biomejs.dev/guides/getting-started/#configuration

```json-doc:biome.jsonc
{
	"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
	"organizeImports": {
		"enabled": true
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true
		}
	}
}
```

format 関連の設定は記載されていませんが、デフォルトでオン[^formatter-enabled-default-value]になっているようです。

[^formatter-enabled-default-value]: https://biomejs.dev/reference/configuration/#formatterenabled

とは言えドキュメントをしっかり読んでいないと分かりづらいので、記載しておいても良いのかなと思いました。

```diff_jsondoc:biome.jsonc
 {
 	"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
 	"organizeImports": {
 		"enabled": true
 	},
 	"linter": {
 		"enabled": true,
 		"rules": {
 			"recommended": true
 		}
 	},
+	"formatter": {
+		"enabled": true
+	}
 }
```

## Astro の設定の追加

Biome は v1.6 にて Astro を部分的にサポートしました。[^partial-support-for-astro]

[^partial-support-for-astro]: https://biomejs.dev/blog/biome-v1-6/

そのうち不要になるのかもしれませんが、[^partially-supported-with-some-caveats]現状は次のような設定を追加する必要があります。

[^partially-supported-with-some-caveats]: いくつかの lint ルールと競合するためこういった記載が必要になるそうで、完全なパーサーが提供されれば不要になるかもしれない、と予想しているだけです。 https://biomejs.dev/internals/language-support/#html-super-languages-support

```diff_jsondoc:biome.jsonc
 {
 	"$schema": "https://biomejs.dev/schemas/1.8.1/schema.json",
 	"organizeImports": {
 		"enabled": true
 	},
 	"linter": {
 		"enabled": true,
 		"rules": {
 			"recommended": true
 		}
 	},
 	"formatter": {
 		"enabled": true
 	},
+	"javascript": {
+		"globals": ["Astro"]
+	}
 }
```

## package.json のスクリプト変更

勝手に書き換えるのがあまり好きではなかったので、もともとチェックだけにしていました。
Biome には lint と format を両方実行する`check`というコマンドがもともと用意されているので、そちらに置き換えます。

```diff_json:package.json
 "scripts": {
-	"lint": "eslint ./src && prettier --check ./src"
+	"check": "biome check ./src"
 }
```

変更した後には一度実行しました。

一番大きく変わったのはインデントのスタイルです。
Prettier ではデフォルトがスペースですが、Biome ではタブです[^indent-style-default-tab]。
そのため、インデント変更の差分はかなりたくさん出ました。

[^indent-style-default-tab]: アクセシビリティ的な観点でタブの方が優れているという話もあり、それが理由でデフォルトがタブなんだろうか、と予想しています。ただしあくまで予想です。 https://alexandersandberg.com/articles/default-to-tabs-instead-of-spaces-for-an-accessible-first-environment/

スペースかタブかの好みは人によって結構分かれると思うのですが、私は正直あまり気にしていません。
プロジェクトで統一されていて、設定ファイルがシンプルであればどちらでも良いです。

というわけで Biome のデフォルトであるタブのインデントに置き換えました。

## ESLint と Prettier のアンインストール

ESLint と Prettier、そしてそれらのプラグインなどをアンインストールして完了です。

## 最後に

私がもともと設定ファイルにこだわっておらず、導入後のコード変更も許容したので作業自体はすぐに終わりました。

導入して lint や format した後のコードが一切変わらないようにすると多少大変かもしれませんが、 migration 用のコマンドも用意されている[^migrate-from-eslint-and-prettier]ので、ある程度は自動で済ませられるだろうと思っています。

[^migrate-from-eslint-and-prettier]: https://biomejs.dev/guides/migrate-eslint-prettier/

また、2024 年 6 月 16 日 は v1.8 のブログ記事が出ていないのですが、X のポストによれば v1.8 から CSS の lint, format ができるようになったとのことです。

https://twitter.com/biomejs/status/1797963672004043040

今後も対応する範囲が広がっていきそうなので、取り入れていきたいと思っています。