JavaScript/TypeScriptの開発では、ESLint(lint)とPrettier(format)を別々に入れて設定ファイルを揃えるのがここ数年の定番構成でした。ただ、設定ファイルが増えるほど「ESLintとPrettierのルールが競合する」「どちらも別プロセスで動くので実行が遅い」といった悩みも出てきます。この隙間を、1本のRust製CLIでlint・format・import整理までまとめてやってしまおう、というツールが「Biome」です。
この記事は、Biome公式リポジトリのREADME(公式一次情報)と、実際にこのクラウド環境へ@biomejs/biomeをインストールして動かした結果をもとに書いています。公式サイト(biomejs.dev)はこの検証環境のネットワークポリシー上直接アクセスできなかったため、サイト上の案内については未確認としています。
Biomeとは
GitHub公式READMEによると、Biomeは「a toolchain for the web」をうたうツールチェーンで、フォーマッタとリンタを1つのCLI/LSPに統合しています。ライセンスはMIT OR Apache-2.0のデュアルライセンスです。README上の主な主張は次のとおりです。
- リンタには500以上のルールがあり、ESLintやtypescript-eslintなど複数のプロジェクトのルールを取り込んでいる
- フォーマッタはPrettierとの互換性を「97%」としている(README自身の表記で、実測による比較はこの記事では行っていません)
- 対応フォーマットはJavaScript・TypeScript・JSX・TSX・JSON・JSONC・CSS・GraphQL・Markdown・YAML・SCSSなど
- エディタ向けにLanguage Server Protocol(LSP)を一次サポートしており、VS Code拡張・Open VSX向けの配布もある
npmパッケージ側の実体を見ると、リポジトリにはCargo.toml・Cargo.lock・rust-toolchain.tomlなどRustのビルド関連ファイルが置かれており、Rustで実装されたツールだとわかります。配布されるnpmパッケージ(@biomejs/biome)自体はOS・CPUアーキテクチャ別のネイティブバイナリをoptionalDependenciesとして持つラッパーで、Windows・macOS・Linux・Androidの主要な組み合わせに対応していました。
インストールして実際に動かす
README記載のとおり、npmでdevDependenciesとして追加します。
npm install --save-dev --save-exact @biomejs/biome
npx biome --version
# Version: 2.6.3
このクラウド環境でも案内どおりそのままインストールでき、検証時点の最新版は2.6.3でした。npx biome initを実行すると、ASCII artのバナーとともに既定の設定ファイルbiome.jsonが生成されます。
{
"$schema": "https://biomejs.dev/schemas/2.6.3/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
"files": { "ignoreUnknown": false },
"formatter": { "enabled": true, "indentStyle": "tab" },
"linter": { "enabled": true, "rules": { "preset": "recommended" } },
"javascript": { "formatter": { "quoteStyle": "double" } },
"assist": {
"enabled": true,
"actions": { "source": { "organizeImports": "on" } }
}
}
既定ではインデントはタブ、JavaScriptの文字列はダブルクオート、リンタはrecommendedプリセットが有効、import整理(organizeImports)を行うassist機能もオンになっています。
わざと問題のあるコードにcheckをかけてみる
var宣言、スペースの崩れたオブジェクトリテラル、==比較、未使用変数を含むサンプルファイルを用意し、lint・format・assistをまとめて実行するbiome checkコマンドを走らせました。
var x = 1
let y = {a:1,b:2,c:3}
function foo(a,b) {
if (a==b) {
console.log( "equal" )
}
return a+b
}
const unused = 42;
実行結果(抜粋)は次のとおりです。
src/sample.js:1:5 lint/correctness/noUnusedVariables FIXABLE
! This variable x is unused.
i Unsafe fix: If this is intentional, prepend x with an underscore.
src/sample.js:2:1 lint/style/useConst FIXABLE
! This let declares a variable that is only assigned once.
i Safe fix: Use const instead.
src/sample.js:4:8 lint/suspicious/noDoubleEquals FIXABLE
× Using == may be unsafe if you are relying on type coercion.
i Unsafe fix: Use === instead.
src/sample.js format
× Formatter would have printed different content (diff omitted here)
Checked 1 file in 5ms. No fixes applied.
Found 2 errors.
Found 5 warnings.
未使用変数(noUnusedVariables)、不要なletの使用(useConst)、==の使用(noDoubleEquals)、フォーマット崩れがそれぞれ個別の診断として出ました。終了コードは1で、CIでそのまま失敗判定に使える形です。
「safeな修正」と「unsafeな修正」で挙動が変わる
それぞれの診断にはSafe fixまたはUnsafe fixというラベルが付いています。これが実際の--writeの挙動にどう影響するかを確かめました。
# --writeだけ(safeな修正のみ適用)
npx biome check --write src/sample.js
var x = 1;
const y = { a: 1, b: 2, c: 3 };
function foo(a, b) {
if (a == b) {
console.log("equal");
}
return a + b;
}
const unused = 42;
フォーマット(セミコロン・スペースの付与)とlet→const(useConstはsafe判定)は書き換わりましたが、==はそのまま残っています。出力にも「Skipped 10 suggested fixes. If you wish to apply the suggested (unsafe) fixes, use the command biome check --write --unsafe」と明示されており、--unsafeを付けて実行し直すと==が===に変わり、未使用変数には先頭に_が自動で付きました。
var _x = 1;
const _y = { a: 1, b: 2, c: 3 };
function _foo(a, b) {
if (a === b) {
console.log("equal");
}
return a + b;
}
const _unused = 42;
「意味が変わらないと確信できる修正」と「挙動が変わる可能性のある修正」を明確に分けて、既定では前者しか自動適用しない設計だと確認できました。CIに組み込む際に--unsafeを付けるかどうかは、チームの合意次第で選べる形です。参考に、biome explain noDoubleEqualsを実行すると、このルールが「Available from version: 1.0.0」「Default severity: error」「This rule is recommended」といった情報とあわせて説明文を表示してくれました。
importの並び替えも標準機能
並び順がバラバラなimport文に対して、--unsafeを付けずに--writeだけを実行すると、アルファベット順に並び替えられました。
// before
import c from "./c"
import a from "./a"
import b from "./b"
// npx biome check --write 実行後
import a from "./a";
import b from "./b";
import c from "./c";
これはlintルールではなく、前述のbiome.jsonにあったassist.actions.source.organizeImportsという別カテゴリの機能です。import整理はsafeな修正として扱われているらしく、--unsafeなしでも適用されることを確認しました。
JS/TSだけでなくCSSにも対応
READMEにある対応フォーマットの記載どおり、CSSに対してもフォーマットが効くかを確認しました。
.foo{color:red;background:blue;}
.bar {
color: red;;
}
biome checkを実行すると、1行にまとめられたセレクタをプロパティごとに改行し、二重セミコロンを1つに正規化する差分が提示されました。JS/TS専用のツールではなく、CSS・JSON・GraphQLなども含めた「Web開発全般のフォーマッタ」という位置づけが、実際の挙動でも確認できます。
このリポジトリ(128ファイル)で速度を試す
LEXIAブログが動いているこのNext.jsリポジトリのapp・components・lib配下に対して、書き込みなしのbiome checkを実行してみました(ファイルは変更していません)。
biome check --no-errors-on-unmatched app components lib
Checked 128 files in 111ms. No fixes applied.
Found 264 errors.
Found 73 warnings.
128ファイルの走査が111msで終わっており、READMEが謳う速度面の主張は実感として裏付けられました。なお、このリポジトリはBiomeの設定もESLint/Prettierとの統一もまだ行っていないため、エラー件数自体はBiomeの既定ルールをそのまま当てた結果であり、リポジトリの品質を示すものではありません。
ESLint設定からの移行コマンドも試した
biome migrate eslintというサブコマンドがあり、既存の.eslintrc系の設定を読み込んでBiome側のルールに変換できます。eqeqeqとno-unused-varsだけを有効にした簡単な.eslintrc.jsonを用意して試しました。
npx biome migrate eslint --write
2 ESLint rules found
- 2 have been migrated to Biome's rules
- 100% (2) of your ESLint rules are fully covered by Biome
- 100% (2) via direct migration to Biome rules
Migration results:
- biome.json: configuration successfully migrated.
eqeqeqはlinter.rules.suspicious.noDoubleEqualsに、no-unused-varsはlinter.rules.correctness.noUnusedVariablesに直接マッピングされ、biome.jsonのプリセットが"recommended"から"none"に切り替わって、移行した2つのルールだけが明示的に書き込まれました。すべてのESLintルールが1対1で移行できるとは限らないはずですが、少なくとも今回試した範囲では、設定ファイルの変換まで自動化されていることが確認できました。
向いている場面・向いていない場面
- 向いている:新規プロジェクトで、ESLint+Prettierの二重設定や実行時間を避けたい場合。
biome --helpには、ファイルを書き換えずチェックのみ行うciサブコマンドの案内もあり、CIでの一括チェックに使える設計です。 - 向いていない:既存のESLintプラグイン(フレームワーク専用ルールなど)に強く依存している場合。
migrate eslintはルールを移行しますが、Biome側に同等のルールが存在しないプラグイン独自ルールまではカバーできません。移行前に、使っているプラグインのルールがBiome側にあるか確認したほうが安全です。
まとめ
Biomeは、ESLintとPrettierが別々に担っていたlint・format・import整理を1本のRust製CLIにまとめたツールです。実際にインストールして動かした範囲では、診断ごとに「safe」「unsafe」の修正区分が明確で--writeだけでは意味の変わらない修正しか適用されないこと、CSSなどJS/TS以外のファイルもフォーマット対象になること、migrate eslintで既存設定をある程度自動変換できることを確認できました。128ファイルの走査が111msで終わる速度も体感どおりで、ESLint+Prettierの二重構成を整理したいタイミングで検討する価値のあるツールだと感じました。
参考にした一次情報
- GitHub:biomejs/biome(公式README、ライセンス、対応言語、ビルド構成)
- npm:@biomejs/biome(公式パッケージメタデータ、バージョン、optionalDependencies)
- 本文中のコマンド出力・設定ファイルの内容はすべて、この記事の検証時点で実際にBiome 2.6.3をこのクラウド環境にインストールして得たものです。
