0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TurborepoにBiomeを導入するための実践的な手順

Last updated at Posted at 2025-03-25

はじめに

  • モダンなWeb開発において、コード品質の維持と開発効率の向上は重要な課題です。本記事では、Turborepoを使用したNext.jsプロジェクトに、高性能な開発ツールチェーンであるBiomeを導入する手順を解説します。

Turborepoとは

  • Turborepoは、JavaScript/TypeScriptプロジェクトのためのハイパフォーマンスなビルドシステムです。

主な特徴

  • フレームワークに依存しない柔軟性(Next.js, React等で利用可能)
  • インテリジェントなタスク実行とキャッシング
  • モノレポ(monorepo)に最適化された設計

Biomeとは

  • Biomeは、Web開発のための次世代統合ツールチェーンです。JavaScript、TypeScript、JSX、JSON、CSS、GraphQLなどの幅広い言語をサポートし、コードの品質管理を効率化します。

主な特徴

1. 高速なフォーマッタ機能

  • コードの自動フォーマットを高速に実行
  • Prettierと比較して数十倍の処理速度
  • Prettierとの高い互換性により、スムーズな移行が可能

2. 強力なリント機能

3. 統合機能

  • フォーマットとリントの同時実行をサポート
  • 単一のツールでの効率的なコード品質管理
  • シンプルな設定と運用

導入手順

1. Turborepoプロジェクトの作成

  • まずは、Turborepoプロジェクトを作成します。
ターミナル
npx create-turbo@latest [プロジェクト名]

パッケージマネージャーはpnpmを選択しました。違うパッケージマネージャーを選択した場合は、コマンドを置き換えてから実行してください。

2. 既存の Linter/Formatter の削除

Biomeに移行するため、既存のPrettierとESLintの設定を削除します。

2.1. package.json の修正

  • 各ディレクトリのpackage.jsonから、Prettier・ESLint関連の設定を削除します。
package.json(/)

  "name": "tureborepo-next-biome-template",
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
-   "lint": "turbo run lint",
-   "format": "prettier --write \"**/*.{ts,tsx,md}\"",
    "check-types": "turbo run check-types"
  },
  "devDependencies": {
-   "prettier": "^3.5.3",
    "turbo": "^2.4.4",
    "typescript": "5.8.2"
  },
  "packageManager": "pnpm@9.0.0",
  "engines": {
    "node": ">=18"
  }
}
turbo.json(/)
{
  "$schema": "https://turbo.build/schema.json",
  "ui": "tui",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
-   "lint": {
-     "dependsOn": ["^lint"]
-   },
    "check-types": {
      "dependsOn": ["^check-types"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
package.json(/apps/web)
{
  "name": "web",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack --port 3000",
    "build": "next build",
    "start": "next start",
-   "lint": "next lint --max-warnings 0",
    "check-types": "tsc --noEmit"
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "next": "^15.2.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
-   "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
-   "eslint": "^9.22.0",
    "typescript": "5.8.2"
  }
}
package.json(/apps/docs)
{
  "name": "docs",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack --port 3000",
    "build": "next build",
    "start": "next start",
-   "lint": "next lint --max-warnings 0",
    "check-types": "tsc --noEmit"
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "next": "^15.2.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
-   "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
-   "eslint": "^9.22.0",
    "typescript": "5.8.2"
  }
}
package.json(/packages/ui)
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    "./*": "./src/*.tsx"
  },
  "scripts": {
-   "lint": "eslint . --max-warnings 0",
    "generate:component": "turbo gen react-component",
    "check-types": "tsc --noEmit"
  },
  "devDependencies": {
-   "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@turbo/gen": "^2.4.4",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
-   "eslint": "^9.22.0",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}

2.2. 不要なファイルの削除

以下のファイルを削除します

  • packages/eslint-config/(ディレクトリごと)
  • apps/web/eslint.config.mjs
  • apps/docs/eslint.config.mjs
  • packages/ui/eslint.config.mjs

2.3. 依存関係の更新

ターミナル
pnpm install

3. VSCode の設定

  • Biomeをエディタに統合するため、.vscode/settings.jsonを作成または修正します。
.vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "biomejs.biome",
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

4. Biome の設定

4.1. 設定パッケージの作成

  • Biomeの設定を一元管理するための内部パッケージを作成します。
ターミナル
# 1. パッケージ作成 
mkdir -p packages/biome-config

# 2. パッケージ移動
cd packages/biome-config

# 3. パッケージ初期化
pnpm init

4.2. Biome のインストールと初期化

ターミナル
# 1. インストール
pnpm add -D @biomejs/biome

# 2. 初期化
pnpm biome init

4.3. 設定ファイルの変更

package.json(/packages/biome-config)
{
  "name": "@repo/biome-config",
  "version": "1.0.0",
  "type": "module",
  "private": true,
  "exports": {
    "./biome": "./biome.json"
  },
  "devDependencies": {
    "@biomejs/biome": "^1.9.4"
  }
}
biome.json(/packages/biome-config)
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": ["./tsconfig.json"]
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  }
}

5. ワークスペース(apps)と内部パッケージ(packages)にbiomeを導入する

5.1 package.jsonの変更とbiome.jsonの追加

  • 今回はプロジェクト作成時に生成されるapps/web, apps/docs, packages/uiに導入します。
packages.json(apps/web)
{
  "name": "web",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack --port 3000",
    "build": "next build",
    "start": "next start",
    "check-types": "tsc --noEmit",
+   "biome:check": "biome check .",
+   "biome:fix": "biome check --fix ."
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "next": "^15.2.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
+   "@biomejs/biome": "^1.9.4",
+   "@repo/biome-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "typescript": "5.8.2"
  }
}
biome.json(apps/web)
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "extends": ["../../packages/biome-config/biome.json"]
}
packages.json(apps/docs)
{
  "name": "docs",
  "version": "0.1.0",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack --port 3000",
    "build": "next build",
    "start": "next start",
    "check-types": "tsc --noEmit",
+   "biome:check": "biome check .",
+   "biome:fix": "biome check --fix ."
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "next": "^15.2.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
+   "@biomejs/biome": "^1.9.4",
+   "@repo/biome-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "typescript": "5.8.2"
  }
}
biome.json(apps/docs)
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "extends": ["../../packages/biome-config/biome.json"]
}
packages.json(/packages/ui)
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    "./*": "./src/*.tsx"
  },
  "scripts": {
    "generate:component": "turbo gen react-component",
    "check-types": "tsc --noEmit",
+   "biome:check": "biome check .",
+   "biome:fix": "biome check --fix ."
  },
  "devDependencies": {
+   "@biomejs/biome": "^1.9.4",
+   "@repo/biome-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@turbo/gen": "^2.4.4",
    "@types/node": "^22.13.10",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "typescript": "5.8.2"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}

biome.json(/packages/ui)
{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "extends": ["../biome-config/biome.json"]
}

注意
「extents」に関して、現状だと@repo/biome-config/biome.jsonのような指定方法は対応されてないため、相対パスで指定しています。
なので、"@repo/biome-config": "workspace:*",がなくてもbiomeは動作します。
(早く対応してほしいです…)

5.2 依存関係の更新

ターミナル(/)
# 1. ルートディレクトリに移動
cd ../..

# 2. インストール
pnpm install

6. turboの設定ファイル更新

  • Biomeを使用している全ワークスペースおよび内部パッケージを一括で実行できるようにpakcage.jsonおよびturbo.jsonファイルを更新する。
package.json(/)
{
  "name": "tureborepo-next-biome-template",
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "check-types": "turbo run check-types",
+   "biome:check": "turbo run biome:check",
+   "biome:fix": "turbo run biome:fix"
  },
  "devDependencies": {
    "turbo": "^2.4.4",
    "typescript": "5.8.2"
  },
  "packageManager": "pnpm@9.0.0",
  "engines": {
    "node": ">=18"
  }
}
turbo.json(/)
{
  "$schema": "https://turbo.build/schema.json",
  "ui": "tui",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "check-types": {
      "dependsOn": ["^check-types"]
    },
+   "biome:check": {
+     "dependsOn": ["^biome:check"]
+   },
+   "biome:fix": {
+     "dependsOn": ["^biome:fix"]
+   },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

動作確認

1. ファイル保存時にフォーマット整形およびインポートの並び替えがされるか

page.tsx(apps/web/app/page.tsx)
+ import { Button } from "@repo/ui/button";
  import Image, { type ImageProps } from "next/image";
- import { Button } from "@repo/ui/button";
  import styles from "./page.module.css";

2. ルート配下で、pnpm biome:checkを実行したときに正しくチェックされるか

コマンド
pnpm biome:check
# プロジェクト作成で生成されたファイルのままだと、ここでチェックエラーになります。
# 次の 'pnpm biome:fix' コマンドで解消することができます。

3. ルート配下で、pnpm biome:fixを実行したときに正しく修正されるか

コマンド
pnpm run biome:fix

注意
packages/ui//button.tsxで「Provide an explicit type prop for the button element.」というエラーが発生した場合は、buttonタグにtype属性を追加することでpnpm run biome:fixが成功するようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?