Turbopack の設定ファイル(turbo.config.js)徹底ガイド

Turbopack の急速な普及に伴い、開発チームの生産性を左右する重要な要素として turbo.config.js
の設定ファイルが注目されています。適切な設定により、ビルド時間を劇的に短縮し、開発者体験を飛躍的に向上させることができるのです。
本記事では、turbo.config.js
のすべての設定オプションを体系的に解説し、各項目の詳細な仕様と実際の効果について深く掘り下げます。設定ファイルを完全にマスターすることで、あなたのプロジェクトのパフォーマンスを最大限に引き出せるようになるでしょう。
基本設定構造
設定ファイルの基本構造と必須項目
turbo.config.js
は、Turbopack の動作を制御する中核的な設定ファイルです。まず、基本的な構造を理解することから始めましょう。
javascript// turbo.config.js の基本構造
module.exports = {
// パイプライン設定(必須)
pipeline: {
// タスク定義
},
// グローバル設定(オプション)
globalDependencies: [],
globalEnv: [],
// 実験的機能(オプション)
experimental: {},
};
必須項目の詳細理解
pipeline
は唯一の必須項目であり、プロジェクト内のタスクとその依存関係を定義します:
javascript// 最小限の pipeline 設定例
module.exports = {
pipeline: {
// ビルドタスクの定義
build: {
// 出力ファイルの指定
outputs: ['dist/**', '.next/**'],
// 依存するタスク
dependsOn: ['^build'],
},
// 開発サーバータスク
dev: {
// キャッシュなし(常に実行)
cache: false,
// 永続的タスク
persistent: true,
},
},
};
設定ファイルの検索と優先順位
Turbopack は以下の順序で設定ファイルを検索します:
# | ファイル名 | 形式 | 優先度 | 用途 |
---|---|---|---|---|
1 | turbo.json | JSON | 最高 | 標準設定ファイル |
2 | turbo.config.js | JavaScript | 高 | 動的設定・環境変数使用 |
3 | package.json | JSON | 中 | turbo セクション内設定 |
4 | .turbo/config.json | JSON | 低 | ローカル一時設定 |
javascript// 動的設定の活用例
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
pipeline: {
build: {
outputs: [
isProduction ? 'dist/**' : 'dev-dist/**',
'.next/**',
],
env: isProduction
? ['NODE_ENV', 'API_URL', 'CDN_URL']
: ['NODE_ENV', 'DEV_API_URL'],
},
},
};
コア設定オプション
pipeline:タスク実行の中核設定
pipeline
は Turbopack の心臓部であり、各タスクの実行方法を詳細に制御します。
基本的なタスク定義
javascriptmodule.exports = {
pipeline: {
// ビルドタスクの詳細設定
build: {
// 依存関係の定義
dependsOn: [
'^build', // 依存パッケージのbuildタスク
'typecheck', // 同一パッケージのtypecheckタスク
'lint', // 同一パッケージのlintタスク
],
// 出力ファイルの指定
outputs: ['dist/**', 'build/**', '.next/**'],
// 入力ファイルの指定(キャッシュキーに使用)
inputs: [
'src/**/*.{js,ts,jsx,tsx}',
'public/**',
'!**/*.test.{js,ts}',
'!**/node_modules/**',
],
// 環境変数の依存関係
env: ['NODE_ENV', 'API_URL', 'DATABASE_URL'],
},
},
};
outputs の詳細設定
outputs
の設定により、キャッシュの効率性が大きく左右されます:
javascript// 出力パターンの最適化
const optimizedOutputs = {
// 基本的なビルド出力
build: {
outputs: [
// Next.js 出力
'.next/**',
'!.next/cache/**', // キャッシュディレクトリは除外
// 静的アセット
'public/static/**',
// 型定義ファイル
'dist/**/*.d.ts',
// ソースマップ(本番環境のみ)
...(process.env.NODE_ENV === 'production'
? ['dist/**/*.map']
: []),
],
},
// テストタスクは出力なし
test: {
outputs: [], // テスト結果はキャッシュに含めない
},
// Storybook ビルド
'build-storybook': {
outputs: [
'storybook-static/**',
'!storybook-static/node_modules/**',
],
},
};
inputs の高度な設定
入力ファイルの指定により、不要な再ビルドを防止できます:
javascript// 入力ファイルの精密制御
module.exports = {
pipeline: {
build: {
inputs: [
// ソースコード
'src/**/*.{js,ts,jsx,tsx,vue}',
// 設定ファイル
'package.json',
'tsconfig.json',
'next.config.js',
'tailwind.config.js',
// 環境ファイル
'.env.local',
'.env.production',
// 静的アセット
'public/**',
'assets/**',
// 除外パターン
'!**/*.test.{js,ts}',
'!**/*.spec.{js,ts}',
'!**/node_modules/**',
'!**/.git/**',
'!**/coverage/**',
],
},
// TypeScript 型チェック
typecheck: {
inputs: [
'src/**/*.{ts,tsx}',
'types/**/*.ts',
'tsconfig.json',
'tsconfig.*.json',
],
outputs: [],
},
},
};
dependsOn:タスク依存関係の管理
複雑なタスク依存関係を適切に管理することで、効率的なビルドパイプラインを構築できます:
javascript// 複雑な依存関係の例
module.exports = {
pipeline: {
// 基盤となるタスク
'gen:types': {
outputs: ['src/types/generated/**'],
},
// コード品質チェック
lint: {
dependsOn: ['gen:types'],
outputs: [],
},
typecheck: {
dependsOn: ['gen:types', 'lint'],
outputs: ['dist/**/*.d.ts'],
},
// テスト実行
test: {
dependsOn: ['typecheck'],
outputs: ['coverage/**'],
},
// ビルド実行
build: {
dependsOn: [
'^build', // 依存パッケージのビルド
'gen:types', // 型生成
'test', // テスト通過
],
outputs: ['dist/**', '.next/**'],
},
// デプロイ準備
package: {
dependsOn: ['build'],
outputs: ['package/**'],
},
},
};
パフォーマンス最適化設定
cache:キャッシュ戦略の最適化
キャッシュ設定は Turbopack のパフォーマンスに最も大きな影響を与えます:
javascript// キャッシュ戦略の詳細設定
module.exports = {
pipeline: {
// 高頻度で変更されるタスク
dev: {
cache: false, // キャッシュを無効化
persistent: true, // プロセスを維持
},
// 安定したビルドタスク
build: {
cache: true, // キャッシュを有効化(デフォルト)
outputs: ['dist/**'],
},
// 長時間実行されるタスク
test: {
cache: true,
// キャッシュキーに影響する追加要素
inputs: [
'src/**/*.{js,ts}',
'test/**/*.{js,ts}',
'jest.config.js',
],
},
},
};
// キャッシュパフォーマンスの監視
const cacheMonitoring = {
// キャッシュヒット率の計測
measureCacheEfficiency: async function () {
const cacheStats = await this.getCacheStatistics();
return {
hitRate: (cacheStats.hits / cacheStats.total) * 100,
missRate:
(cacheStats.misses / cacheStats.total) * 100,
avgBuildTime: cacheStats.avgBuildTime,
cacheSize: cacheStats.totalCacheSize,
};
},
// キャッシュ最適化の提案
optimizationSuggestions: {
lowHitRate:
'inputs の設定を見直し、不要なファイルを除外してください',
largeCacheSize:
'outputs の設定を最適化し、不要なファイルを除外してください',
slowBuild: '並列実行の設定を確認してください',
},
};
parallel:並列実行の制御
並列実行により、マルチコア CPU の性能を最大限活用できます:
javascript// 並列実行設定の最適化
module.exports = {
// グローバル並列設定
globalDependencies: ['package.json'],
pipeline: {
// 並列実行可能なタスク
lint: {
outputs: [],
// 他のタスクと並列実行可能
cache: true,
},
typecheck: {
outputs: ['dist/**/*.d.ts'],
// lint と並列実行される
cache: true,
},
test: {
dependsOn: [], // 依存関係なし
outputs: ['coverage/**'],
cache: true,
},
// 依存関係のあるタスク
build: {
dependsOn: ['lint', 'typecheck', 'test'], // 上記3つの完了を待つ
outputs: ['dist/**'],
},
},
};
// 並列実行効率の最適化
const parallelOptimization = {
// CPU コア数に基づく最適化
getCoreOptimizedConfig: function () {
const cores = require('os').cpus().length;
return {
// CPU集約的タスクの分散
maxParallelTasks: Math.max(1, cores - 1),
// メモリ使用量の考慮
memoryLimitPerTask:
process.env.NODE_ENV === 'production'
? '2GB'
: '1GB',
};
},
};
env:環境変数の管理
環境変数の適切な管理により、セキュアで効率的なビルドを実現できます:
javascript// 環境変数の戦略的管理
module.exports = {
pipeline: {
build: {
env: [
// 必須の環境変数
'NODE_ENV',
'NEXT_PUBLIC_API_URL',
// 条件付き環境変数
...(process.env.NODE_ENV === 'production'
? ['DATABASE_URL', 'REDIS_URL', 'CDN_URL']
: ['DEV_DATABASE_URL', 'DEV_API_KEY']),
],
outputs: ['dist/**', '.next/**'],
},
// 環境別のタスク
'build:production': {
env: [
'NODE_ENV',
'DATABASE_URL',
'REDIS_URL',
'CDN_URL',
'SENTRY_DSN',
],
outputs: ['dist/**'],
},
'build:staging': {
env: [
'NODE_ENV',
'STAGING_DATABASE_URL',
'STAGING_API_URL',
],
outputs: ['staging-dist/**'],
},
},
};
// 環境変数の検証と管理
const envValidation = {
// 必須環境変数のチェック
validateRequiredEnv: function (requiredVars) {
const missing = requiredVars.filter(
(varName) => !process.env[varName]
);
if (missing.length > 0) {
throw new Error(
`Missing required environment variables: ${missing.join(
', '
)}`
);
}
},
// 環境変数の型変換と検証
parseEnvConfig: function () {
return {
nodeEnv: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT || '3000', 10),
isDevelopment: process.env.NODE_ENV !== 'production',
// API 設定
apiUrl:
process.env.NEXT_PUBLIC_API_URL ||
'http://localhost:8000',
// データベース設定
databaseUrl: process.env.DATABASE_URL,
// Redis 設定
redisUrl: process.env.REDIS_URL,
// 監視設定
sentryDsn: process.env.SENTRY_DSN,
};
},
};
高度な設定オプション
extends:設定の継承と拡張
設定ファイルの継承により、チーム全体で一貫した設定を維持できます:
javascript// ベース設定ファイル(turbo-base.config.js)
module.exports = {
pipeline: {
lint: {
outputs: [],
},
typecheck: {
outputs: ['dist/**/*.d.ts'],
},
test: {
outputs: ['coverage/**'],
},
},
};
// プロジェクト固有の設定(turbo.config.js)
module.exports = {
// ベース設定を継承
extends: ['./turbo-base.config.js'],
pipeline: {
// ベース設定に追加
build: {
dependsOn: ['^build', 'lint', 'typecheck', 'test'],
outputs: ['dist/**', '.next/**'],
},
// Storybook 固有のタスク
'build-storybook': {
dependsOn: ['build'],
outputs: ['storybook-static/**'],
},
// E2E テスト
'test:e2e': {
dependsOn: ['build'],
outputs: ['test-results/**'],
},
},
};
// チーム共通設定の管理
const teamConfig = {
// 共通のコーディング規約
commonLinting: {
lint: {
inputs: [
'src/**/*.{js,ts,jsx,tsx}',
'.eslintrc.json',
'prettier.config.js',
],
outputs: [],
},
},
// 共通のテスト設定
commonTesting: {
test: {
inputs: [
'src/**/*.{js,ts}',
'test/**/*.{js,ts}',
'jest.config.js',
'setup-tests.ts',
],
outputs: ['coverage/**'],
},
},
};
globalDependencies:グローバル依存関係
プロジェクト全体に影響する依存関係を管理します:
javascriptmodule.exports = {
// 全タスクに影響するファイル
globalDependencies: [
'package.json',
'yarn.lock',
'tsconfig.json',
'.env',
'turbo.config.js',
],
// 全タスクで利用可能な環境変数
globalEnv: [
'NODE_ENV',
'CI',
'TURBO_TOKEN',
'TURBO_TEAM',
],
pipeline: {
build: {
// globalDependencies が変更された場合、
// 自動的にキャッシュが無効化される
outputs: ['dist/**'],
},
},
};
// グローバル依存関係の動的管理
const dynamicGlobalDeps = {
// 環境に応じた依存関係
getEnvironmentDependencies: function () {
const baseDeps = ['package.json', 'turbo.config.js'];
// 開発環境
if (process.env.NODE_ENV === 'development') {
return [...baseDeps, '.env.local', 'jest.config.js'];
}
// 本番環境
return [
...baseDeps,
'.env.production',
'next.config.js',
];
},
// プロジェクトタイプに応じた依存関係
getProjectTypeDependencies: function (projectType) {
const commonDeps = ['package.json', 'tsconfig.json'];
switch (projectType) {
case 'nextjs':
return [
...commonDeps,
'next.config.js',
'tailwind.config.js',
];
case 'react':
return [
...commonDeps,
'vite.config.ts',
'postcss.config.js',
];
case 'node':
return [
...commonDeps,
'nodemon.json',
'dockerfile',
];
default:
return commonDeps;
}
},
};
experimental:実験的機能の活用
最新の実験的機能を安全に導入し、パフォーマンスをさらに向上させます:
javascriptmodule.exports = {
// 実験的機能の有効化
experimental: {
// 分散キャッシュの有効化
remoteCache: {
enabled: true,
url: 'https://cache.company.com',
// 認証設定
auth: {
token: process.env.TURBO_TOKEN,
team: process.env.TURBO_TEAM,
},
},
// ビルド分析の有効化
buildAnalysis: {
enabled: true,
outputPath: 'turbo-analysis.json',
},
// 高速ハッシュアルゴリズム
fastHashing: {
enabled: true,
algorithm: 'xxhash',
},
// インクリメンタルタイプチェック
incrementalTypeCheck: {
enabled: true,
tsconfigPath: './tsconfig.json',
},
},
pipeline: {
build: {
outputs: ['dist/**'],
},
},
};
// 実験的機能の安全な導入
const experimentalFeatures = {
// 段階的機能有効化
enableFeatureGradually: function (
featureName,
rolloutPercentage = 10
) {
const hash = require('crypto')
.createHash('md5')
.update(process.env.USER || 'default')
.digest('hex');
const hashNumber = parseInt(hash.substring(0, 8), 16);
const userPercentage = hashNumber % 100;
return userPercentage < rolloutPercentage;
},
// 機能フラグ管理
getExperimentalConfig: function () {
return {
remoteCache: {
enabled: this.enableFeatureGradually(
'remoteCache',
25
),
},
buildAnalysis: {
enabled:
process.env.NODE_ENV === 'development' ||
process.env.CI === 'true',
},
fastHashing: {
enabled: this.enableFeatureGradually(
'fastHashing',
50
),
},
};
},
};
設定検証と型安全性
設定ファイルの妥当性チェック
適切な検証により、設定ミスを未然に防ぎます:
javascript// 設定検証スキーマ
const configValidation = {
// 基本スキーマ定義
schema: {
pipeline: {
type: 'object',
required: true,
properties: {
'*': {
type: 'object',
properties: {
dependsOn: {
type: 'array',
items: { type: 'string' },
},
outputs: {
type: 'array',
items: { type: 'string' },
},
inputs: {
type: 'array',
items: { type: 'string' },
},
env: {
type: 'array',
items: { type: 'string' },
},
cache: { type: 'boolean' },
persistent: { type: 'boolean' },
},
},
},
},
globalDependencies: {
type: 'array',
items: { type: 'string' },
},
globalEnv: {
type: 'array',
items: { type: 'string' },
},
},
// 設定検証関数
validateConfig: function (config) {
const errors = [];
// pipeline の必須チェック
if (!config.pipeline) {
errors.push('pipeline is required');
}
// タスク設定の検証
if (config.pipeline) {
Object.keys(config.pipeline).forEach((taskName) => {
const task = config.pipeline[taskName];
// outputs の妥当性チェック
if (task.outputs) {
task.outputs.forEach((output) => {
if (
!output.includes('*') &&
!output.includes('/')
) {
errors.push(
`Invalid output pattern: ${output} in task ${taskName}`
);
}
});
}
// dependsOn の循環参照チェック
if (task.dependsOn) {
const visited = new Set();
const visiting = new Set();
const hasCycle = (taskName) => {
if (visiting.has(taskName)) return true;
if (visited.has(taskName)) return false;
visiting.add(taskName);
const deps =
config.pipeline[taskName]?.dependsOn || [];
for (const dep of deps) {
if (dep.startsWith('^')) continue; // 外部依存は除外
if (hasCycle(dep)) return true;
}
visiting.delete(taskName);
visited.add(taskName);
return false;
};
if (hasCycle(taskName)) {
errors.push(
`Circular dependency detected in task: ${taskName}`
);
}
}
});
}
return {
isValid: errors.length === 0,
errors,
};
},
};
// 使用例
const config = require('./turbo.config.js');
const validation = configValidation.validateConfig(config);
if (!validation.isValid) {
console.error('Configuration errors:');
validation.errors.forEach((error) =>
console.error(`- ${error}`)
);
process.exit(1);
}
TypeScript 対応
TypeScript による型安全な設定管理:
typescript// turbo.config.ts(TypeScript版設定ファイル)
import { Config } from '@turbo/types';
const config: Config = {
pipeline: {
build: {
dependsOn: ['^build', 'typecheck', 'lint'],
outputs: ['dist/**', '.next/**'],
env: ['NODE_ENV', 'API_URL'],
},
typecheck: {
outputs: ['dist/**/*.d.ts'],
inputs: ['src/**/*.{ts,tsx}', 'tsconfig.json'],
},
lint: {
outputs: [],
inputs: [
'src/**/*.{js,ts,jsx,tsx}',
'.eslintrc.json',
],
},
test: {
outputs: ['coverage/**'],
inputs: ['src/**/*.{js,ts}', 'test/**/*.{js,ts}'],
},
},
globalDependencies: ['package.json', 'turbo.config.ts'],
globalEnv: ['NODE_ENV', 'CI'],
};
export default config;
// 型定義の拡張
interface CustomConfig extends Config {
// カスタム設定の追加
customSettings?: {
buildAnalytics?: boolean;
performanceTracking?: boolean;
};
}
// 設定ビルダーパターン
class TurboConfigBuilder {
private config: Config = { pipeline: {} };
addTask(
name: string,
task: TaskConfig
): TurboConfigBuilder {
this.config.pipeline[name] = task;
return this;
}
addGlobalDependency(dep: string): TurboConfigBuilder {
if (!this.config.globalDependencies) {
this.config.globalDependencies = [];
}
this.config.globalDependencies.push(dep);
return this;
}
build(): Config {
return this.config;
}
}
// 使用例
const dynamicConfig = new TurboConfigBuilder()
.addTask('build', {
dependsOn: ['^build'],
outputs: ['dist/**'],
})
.addTask('test', {
outputs: ['coverage/**'],
})
.addGlobalDependency('package.json')
.build();
まとめ
turbo.config.js
の完全な理解と適切な設定は、Turbopack のポテンシャルを最大限に引き出すための重要な鍵となります。本記事で解説した設定オプションを段階的に実装することで、あなたのプロジェクトのビルドパフォーマンスは劇的に向上するでしょう。
設定ファイルマスターへの道筋
段階的な設定改善アプローチ
まずは基本的な pipeline
設定から始めて、プロジェクトの成長に合わせて高度な機能を段階的に導入していくことをお勧めします。特に cache
と outputs
の最適化は即座に効果を実感できるでしょう。
継続的な監視と改善 設定ファイルは一度作成して終わりではありません。プロジェクトの変化に応じて継続的に見直し、ビルド時間やキャッシュヒット率を監視しながら最適化を続けることが重要です。
チーム全体での知識共有 設定ファイルの理解はチーム全体の生産性に直結します。本記事の内容をチームメンバーと共有し、みんなで設定ファイルの品質向上に取り組むことで、開発体験の大幅な改善を実現できるはずです。
適切に設定された turbo.config.js
は、単なる設定ファイルを超えて、チームの開発効率を支える重要なインフラストラクチャーとなります。継続的な学習と改善を通じて、Turbopack の真の力を引き出してください。
関連リンク
- blog
「アジャイルコーチ」って何する人?チームを最強にする影の立役者の役割と、あなたがコーチになるための道筋
- blog
ペアプロって本当に効果ある?メリットだけじゃない、現場で感じたリアルな課題と乗り越え方
- blog
TDDって結局何がいいの?コードに自信が持てる、テスト駆動開発のはじめの一歩
- blog
「昨日やったこと、今日やること」の報告会じゃない!デイリースクラムをチームのエンジンにするための3つの問いかけ
- blog
燃え尽きるのは誰だ?バーンダウンチャートでプロジェクトの「ヤバさ」をチームで共有する方法
- blog
「誰が、何を、なぜ」が伝わらないユーザーストーリーは無意味。開発者が本当に欲しいストーリーの書き方