はじめに
こんにちは。新卒一年目新米エンジニアの伊藤と言います。
今回は業務において初めて導入したツール「Biome」についてご紹介したいと思います。
Biomeは、LinterとFormatterの機能を併せ持つ強力なツールであり、コードの品質向上や開発プロセスの効率化に大いに役立ちます。
v1.0.0が2023年10月にリリースという比較的新しいツールですが、既に最新バージョンはv1.9.4と進化しており、その勢いが止まらないツールです。
本記事では導入・運用の過程で明らかになったメリットや、その一方で工夫が必要だったポイントについても詳しくお伝えします。
導入の背景
私たちのグループは、主要プロダクトであるSynergy!を拡張する小・中規模のシステムやアプリケーションの開発を主に行っています。使用している技術スタックは、Node.jsとJavaScript/TypeScriptによるサーバーレス開発です。
そんな中で、Synergy!のフロントエンドチームがESLintとPrettierからBiomeに乗り換えるという話を聞きました。これを受けて、私たちのグループでも「それならば、私たちもESLintとPrettierからBiomeに乗り換えよう」という流れになり、導入を決定しました。
Biomeとは
Biomeは、JavaScriptおよびTypeScript向けの静的解析ツールで、コード整形(Format)と構文エラーチェック(Lint)を行います。現在、構文エラーチェックにはESLintが一般的に使われており、コード整形にはPrettierが広く利用されています。Biomeはこれら二つの機能を一つのツールで実現するという特徴があります。
他ツールとの違い
圧倒的な速さ
BiomeはRustで書かれており、非常に高速です。公式ベンチマークによれば、同一条件下でPrettierより最大25倍、ESLintより15倍速いと記載されています。
Biome's ~25 times faster than Prettier
Biome's ~15x times faster than ESLint (without any plugins)
設定ファイルの一元化
PrettierとESLintを併用すると設定ファイルが二つになり管理が煩雑になりますが、Biomeでは設定ファイルが一つで済みます。
以下は最小設定の例です。
biome.json
{ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, "files": { "ignoreUnknown": false, "ignore": [] }, "formatter": { "enabled": true, "indentStyle": "tab" }, "organizeImports": { "enabled": true }, "linter": { "enabled": true, "rules": { "recommended": true } }, "javascript": { "formatter": { "quoteStyle": "double" } } }
VSCodeなどで編集する際も、
JSON Schemaが用意されているので、自動補完が効きます。
高い互換性
BiomeはPrettierとの高い互換性が謳われています。実際、RustでPrettierに準拠したコード整形ツールを作成するコンテストでは互換性が96.10%という高い成績を収めています。また、PrettierやESLintからの設定移行も1コマンドで簡単に行えます。
豊富な日本語ドキュメント
Lintのルール設定など、Biomeでは公式ドキュメントが大変豊富です。さらに、日本語版の公式ドキュメントも充実しており、導入や簡単な設定レベルであれば、すべて日本語で読むことができます。
導入方法
では、導入手順を簡単にご説明します。
導入手順の公式ドキュメントはこちらです。
インストール
# install npm install --save-dev --save-exact @biomejs/biome # init(カレントディレクトリにbiome.jsonができる) npx @biomejs/biome init
Prettier・ESLintから設定を移行
こちらは、すでにプロジェクト内でPrettierとESLintを使用している場合に参考にしてください。ただし、完全に移行できるわけではないため、私が参加したプロジェクトでは次のように進めました。一旦移行を試み、コードを書き進める中で違和感のあるルールがあれば都度チーム内で相談し、そのルールを除外するか受け入れるかを決定しました。例えば、ForEach文を禁止するnoForEachなどのルールです。
公式ドキュメントはこちらです。
# ESLint # インスパイアされたルールは移行しない npx @biomejs/biome migrate eslint --write # インスパイアされたルールも移行する npx @biomejs/biome migrate eslint --write --include-inspired # Prettier npx @biomejs/biome migrate prettier --write
インスパイアされたルールとはBiomeがESLintのプラグインやESLintからインスパイアを受け、意図的に動作を変更した物を指します。公式サイトにESLintやプラグインのルールとの対応表がありますので合わせてご確認ください。(inspired)
とついているものがインスパイアされたルールです。
多くのBiomeリンタのルールはESLintやESLintのプラグインのルールからインスパイアされて作られています。BiomeにはTypeScript ESLint、ESLint JSX A11y、ESLint React、ESLint UnicornなどのESLintプラグインのルールがあります。
一つ例を挙げます。BiomeのnoUselessElse
ルールはESLintのno-else-return
ルールとRustのLinterであるClippyのredundant_else
ルールからインスパイアされて作成されました。
Sources:
Inspired from: no-else-return
Inspired from: redundant_else
ESLintのno-else-return
とBiomeのnoUselessElse
はどちらも以下のような早期リターンが可能なif-else文においてelseを使うことを禁止するルールです。
function foo1() { if (x) { return y; } else { // ESLint: Unnecessary 'else' after 'return'. (no-else-return) // Biome: This else clause can be omitted because previous branches break early return z; } }
ただしBiomeのnoUselessElse
ルールでは以下のようなコードも禁止されます。
ESLintのno-else-return
では問題なしと判断されます。
function foo2() { if (x) { console.log("hoge"); throw new Error("Error"); } else {// Biome: This else clause can be omitted because previous branches break early return z; } }
BiomeのnoUselessElse
ルールはreturn句だけでなく、throwなどでのif文の離脱も監視します。
このようにインスパイアされたルールというのはあくまでも対応しているというだけで、実際の挙動は少し異なっているものを指します。
Format・Lint方法
Biomeではformatとlintを同時に行ってくれるcheck
コマンドがあります。--write
オプションを外すと、フォーマット箇所やlintエラー部分を表示するだけで修正は行いません。
# --writeを付けると変更まで行います # --unsafeを付けると破壊的な変更も行います # format npx @biomejs/biome format --write # lint npx @biomejs/biome lint --write # check(lint+format) npx @biomejs/biome check --write
実際に導入・運用してみてわかった事
VSCodeの拡張機能でBiomeを使用するときの工夫
Biomeは公式でVSCode用の拡張機能を提供しています。この拡張機能を使用する際、プロジェクトのルートディレクトリ直下にBiomeがインストールされたnode_modulesがないと拡張機能側でインストールされたデフォルトのBiomeが使用されます。これを回避するためにはsettings.jsonに工夫が必要です。
例を提示します。以下のようなディレクトリ構造のプロジェクト(AWS SAMのテンプレートプロジェクト)にBiomeを導入するとします。
. ├── README.md ├── .vscode │ └── settings.json ├── events │ └── event.json ├── hello-world │ ├── node_modules │ ├── app.ts │ ├── jest.config.ts │ ├── package.json │ ├── tests │ │ └── unit │ │ └── test-handler.test.ts │ └── tsconfig.json ├── samconfig.toml └── template.yaml
この場合、プロジェクトルートにnode_modulesが存在しません。そのため、settings.jsonでBiomeの実体を相対パスで指定する必要があります。以下の設定例をご覧ください。
{ "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "biome.lspBin": "hello-world/node_modules/@biomejs/cli-linux-x64/biome", "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }, "[javascript]": { "editor.defaultFormatter": "biomejs.biome" } }
biome.lspBin
プロパティに対して、相対パスで/node_modules/@biomejs/cli-linux-x64/biome
を指定します。これにより拡張機能側でbiomeの実体を把握することができます。(公式ドキュメント)
終わりに
今回、実際にBiomeを実際に導入・運用してみて、特に印象的だったのは処理速度の速さです。特に、IDEを使用しているときに保存時にフォーマットがかかっても一瞬で終わるため、作業が滞らなくなりました。Prettier/ESLintからも簡単に移行できるので、ぜひ一度お試しください!

趣味はダーツとアーチェリー。
アーチェリーも始めたばかりの新米です。