T-CREATOR

Turbopack のプラグイン開発入門

Turbopack のプラグイン開発入門

Next.js の高速化を実現する Turbopack は、従来の Webpack を大幅に上回るパフォーマンスを提供しています。しかし、開発者の多くが疑問に思うのは「Turbopack でプラグイン開発はできるのか?」ということでしょう。

本記事では、2025 年 6 月現在の Turbopack におけるカスタマイズ機能の現状と制限、そして将来のプラグインシステムへの展望について詳しく解説いたします。現在利用可能な実践的な手法から、将来の開発に向けた準備まで、包括的にご紹介します。

Turbopack の現在のカスタマイズ機能

現在のカスタマイズ可能範囲

Turbopack は 2025 年 6 月時点で、Next.js 15.3 での開発モードが安定版となっています。プロダクションビルドはまだアルファ版の段階ですが、開発環境では十分実用的なカスタマイズが可能です。

サポート状況一覧

機能開発モードプロダクションビルド状況
webpack ローダー部分サポート開発中コアローダーのみ
next.config.js 設定完全サポート完全サポート安定
ファイル拡張子設定完全サポート完全サポート安定
エイリアス設定完全サポート完全サポート安定
カスタムプラグイン未サポート未サポート開発予定

現在の制限事項

Turbopack には以下の重要な制限があります:

javascript// ❌ サポートされていない機能
module.exports = {
  webpack(config) {
    // webpack設定は無視される
    config.module.rules.push({
      test: /\.custom$/,
      use: 'custom-loader',
    });
    return config;
  },
};
bash# よく発生するエラー
Warning: webpack() configuration found in next.config.js.
Turbopack replaces Webpack, so webpack configuration is not supported.
Use the turbopack configuration instead.

webpack ローダーとの連携

サポート済みローダーの活用

現在 Turbopack でテスト済みのローダーは限定的ですが、重要なローダーは動作します。

動作確認済みローダー一覧

javascript// 2025年6月現在、以下のローダーが動作確認済み
const supportedLoaders = [
  'babel-loader',
  '@svgr/webpack',
  'svg-inline-loader',
  'yaml-loader',
  'string-replace-loader',
  'raw-loader',
  'sass-loader',
  'graphql-tag/loader',
];

実際の設定例

SVG ファイルを React コンポーネントとして使用する例

javascript// next.config.js
module.exports = {
  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
};

YAML ファイルの処理例

javascript// next.config.js
module.exports = {
  turbopack: {
    rules: {
      '*.yaml': {
        loaders: [
          {
            loader: 'yaml-loader',
            options: {
              asJSON: true,
            },
          },
        ],
        as: '*.js',
      },
    },
  },
};

カスタムローダーの設定方法

基本的な設定パターン

javascript// next.config.js
module.exports = {
  turbopack: {
    rules: {
      // パターン1: シンプルなローダー指定
      '*.md': {
        loaders: ['raw-loader'],
        as: '*.js',
      },

      // パターン2: 設定オプション付き
      '*.graphql': {
        loaders: [
          {
            loader: 'graphql-tag/loader',
            options: {
              schema: 'schema.graphql',
            },
          },
        ],
        as: '*.js',
      },

      // パターン3: 複数ローダーの連携
      '*.scss': {
        loaders: [
          'sass-loader',
          {
            loader: 'string-replace-loader',
            options: {
              search: '{{BASE_URL}}',
              replace: process.env.BASE_URL || '',
            },
          },
        ],
        as: '*.css',
      },
    },
  },
};

TypeScript での型安全な設定

typescript// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  turbopack: {
    rules: {
      '*.proto': {
        loaders: [
          {
            loader: 'protobuf-loader',
            options: {
              json: true,
              includeDirs: ['./proto'],
            },
          },
        ],
        as: '*.js',
      },
    },
  },
};

export default nextConfig;

制限事項と対処法

現在の主要な制限

1. JavaScript のみ返すローダーの制限

bash# よく遭遇するエラー
Error: Only loaders that return JavaScript code are supported.
Loaders that transform files like stylesheets or images are not currently supported.

対処法:

javascript// ❌ 動作しない例
'*.png': {
  loaders: ['url-loader'], // 画像ローダーは未サポート
  as: '*.js',
}

// ✅ 動作する例
'*.svg': {
  loaders: ['@svgr/webpack'], // JSコードを返すため動作
  as: '*.js',
}

2. require() プラグインの制限

bash# エラー例
Error: Options passed to webpack loaders must be plain JavaScript primitives, objects, and arrays.
It's not possible to pass require() plugin modules as option values.

対処法:

javascript// ❌ 動作しない例
{
  loader: 'some-loader',
  options: {
    plugin: require('some-plugin'), // require()は使用不可
  },
}

// ✅ 動作する例
{
  loader: 'some-loader',
  options: {
    plugin: 'some-plugin', // 文字列で指定
    config: './config.json', // ファイルパスで指定
  },
}

3. 複雑な webpack API の制限

bash# よく発生するエラー
Error: Only a core subset of the webpack loader API is implemented.
Currently, there is enough coverage for some popular loaders.

対処法として、代替アプローチを検討:

javascript// Turbopack用の最適化された設定
module.exports = {
  turbopack: {
    // シンプルな変換ルールに限定
    rules: {
      '*.env': {
        loaders: ['raw-loader'],
        as: '*.js',
      },
    },
    // より複雑な処理は別の方法で実装
    resolveAlias: {
      '@env': './config/environment.js',
    },
  },
};

設定ベースのカスタマイズ

next.config.js での設定

基本設定オプション

javascript// next.config.js - 完全設定例
module.exports = {
  turbopack: {
    // ルートディレクトリの設定
    root: path.join(__dirname, '..'),

    // ファイル変換ルール
    rules: {
      '*.custom': {
        loaders: ['custom-loader'],
        as: '*.js',
      },
    },

    // モジュール解決の設定
    resolveAlias: {
      '@': './src',
      '@components': './src/components',
      '@utils': './src/utils',
    },

    // 解決可能な拡張子
    resolveExtensions: [
      '.tsx',
      '.ts',
      '.jsx',
      '.js',
      '.mjs',
      '.json',
      '.wasm',
    ],

    // モジュールID生成方式
    moduleIds: 'named', // 'named' | 'deterministic'

    // Tree shakingの設定
    treeShaking: true,

    // メモリ制限(バイト単位)
    memoryLimit: 1024 * 1024 * 1024, // 1GB
  },
};

環境別設定の実装

javascript// next.config.js - 環境別設定
const isDev = process.env.NODE_ENV === 'development';
const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  turbopack: {
    // 開発環境でのみ詳細なデバッグ情報
    moduleIds: isDev ? 'named' : 'deterministic',

    // 開発環境でのメモリ制限を緩く設定
    memoryLimit: isDev
      ? 2 * 1024 * 1024 * 1024 // 2GB
      : 1024 * 1024 * 1024, // 1GB

    resolveAlias: {
      // 環境別のAPI設定
      '@api': isDev
        ? './src/api/development'
        : './src/api/production',
    },

    rules: {
      // 開発環境でのみソースマップ付きローダー
      ...(isDev && {
        '*.ts': {
          loaders: [
            {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-typescript'],
                sourceMaps: true,
              },
            },
          ],
          as: '*.js',
        },
      }),
    },
  },
};

エイリアス設定とモジュール解決

パスエイリアスの活用

javascript// next.config.js
module.exports = {
  turbopack: {
    resolveAlias: {
      // 基本的なエイリアス
      '@': './src',
      '@components': './src/components',
      '@hooks': './src/hooks',
      '@utils': './src/utils',
      '@styles': './src/styles',
      '@public': './public',

      // ライブラリの置き換え
      lodash: 'lodash-es', // ES modules版を使用
      moment: 'dayjs', // より軽量な代替

      // 条件付きエイリアス(環境別)
      config: {
        browser: './src/config/browser.js',
        node: './src/config/server.js',
      },
    },
  },
};

tsconfig.json との連携

json// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@hooks/*": ["./src/hooks/*"],
      "@utils/*": ["./src/utils/*"]
    }
  }
}
javascript// next.config.js - tsconfig.jsonと同期
const path = require('path');

module.exports = {
  turbopack: {
    // TypeScriptのpathsと同期
    resolveAlias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(
        __dirname,
        'src/components'
      ),
      '@hooks': path.resolve(__dirname, 'src/hooks'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
  },
};

拡張子とファイル変換

カスタム拡張子の処理

javascript// next.config.js
module.exports = {
  turbopack: {
    // 解決可能な拡張子を拡張
    resolveExtensions: [
      '.tsx',
      '.ts',
      '.jsx',
      '.js',
      '.mjs',
      '.cjs',
      '.json',
      '.wasm',
      '.css',
      '.scss',
      // カスタム拡張子
      '.vue',
      '.svelte',
      '.mdx',
    ],

    rules: {
      // MDXファイルの処理
      '*.mdx': {
        loaders: [
          {
            loader: '@mdx-js/loader',
            options: {
              remarkPlugins: [],
              rehypePlugins: [],
            },
          },
        ],
        as: '*.jsx',
      },

      // GraphQLスキーマファイル
      '*.graphql': {
        loaders: ['graphql-tag/loader'],
        as: '*.js',
      },

      // プロトコルバッファー
      '*.proto': {
        loaders: [
          {
            loader: 'protobuf-loader',
            options: {
              json: true,
            },
          },
        ],
        as: '*.js',
      },
    },
  },
};

ファイルタイプ別の最適化

javascript// next.config.js - ファイルタイプ別設定
module.exports = {
  turbopack: {
    rules: {
      // テキストファイルの最適化
      '*.txt': {
        loaders: [
          {
            loader: 'raw-loader',
            options: {
              esModule: true,
            },
          },
        ],
        as: '*.js',
      },

      // 設定ファイルの動的読み込み
      '*.config.js': {
        loaders: [
          {
            loader: 'string-replace-loader',
            options: {
              search: '{{ENV}}',
              replace: process.env.NODE_ENV,
            },
          },
        ],
        as: '*.js',
      },

      // SVGアイコンの最適化
      '*.icon.svg': {
        loaders: [
          {
            loader: '@svgr/webpack',
            options: {
              icon: true,
              svgo: true,
              svgoConfig: {
                plugins: [
                  {
                    name: 'removeViewBox',
                    active: false,
                  },
                ],
              },
            },
          },
        ],
        as: '*.js',
      },
    },
  },
};

将来のプラグイン API への準備

開発予定のプラグインシステム

現在の開発状況

Turbopack チームは、完全なプラグインエコシステムの構築を進めています。2025 年後半から 2026 年にかけて、以下の機能が予定されています:

rust// 将来予定されているRustベースプラグインAPI(概念図)
use turbopack_plugin::{Plugin, Context, Result};

#[turbopack_plugin]
pub struct CustomTransformPlugin {
    options: CustomOptions,
}

impl Plugin for CustomTransformPlugin {
    fn name(&self) -> &str {
        "custom-transform"
    }

    async fn transform(
        &self,
        ctx: &Context,
        source: &str
    ) -> Result<String> {
        // カスタム変換ロジック
        Ok(transform_source(source, &self.options))
    }
}

JavaScript/TypeScript API の提供予定

typescript// 将来予定されているJavaScript API(概念図)
import { definePlugin } from '@turbopack/plugin-api';

export default definePlugin({
  name: 'custom-transform',
  transform: {
    include: /\.(custom)$/,
    exclude: /node_modules/,
    async handler(code: string, id: string) {
      // 変換ロジック
      return {
        code: transformedCode,
        map: sourceMap,
      };
    },
  },
  resolveId: {
    async handler(id: string, importer?: string) {
      // モジュール解決ロジック
      return resolvedId;
    },
  },
  buildStart: {
    async handler() {
      // ビルド開始時の処理
    },
  },
});

現在の Rust ベースアーキテクチャ

Turbopack の内部構造

Turbopack は Rust で構築された高性能なビルドシステムです。その基盤となるアーキテクチャを理解しておくことは、将来のプラグイン開発に重要です。

rust// Turbopackの核となるアーキテクチャ(簡略化)
pub struct TurboEngine {
    graph: IncrementalGraph,
    cache: FunctionLevelCache,
    runtime: AsyncRuntime,
}

impl TurboEngine {
    pub async fn compile(&self, entry: &Path) -> Result<Bundle> {
        // インクリメンタルコンパイル
        let nodes = self.graph.get_affected_nodes(entry)?;

        for node in nodes {
            if !self.cache.is_valid(&node) {
                let result = self.compile_node(&node).await?;
                self.cache.store(&node, result);
            }
        }

        self.generate_bundle().await
    }
}

パフォーマンス特性の理解

javascript// 現在のTurbopackの設計思想
const turbopackPrinciples = {
  incremental: {
    description: '関数レベルでのキャッシュ',
    benefit: '変更箇所のみ再処理',
  },
  parallel: {
    description: 'マルチコア並列処理',
    benefit: 'CPUリソースの最大活用',
  },
  lazy: {
    description: '必要な時のみコンパイル',
    benefit: '初期起動時間の短縮',
  },
  unified: {
    description: '単一の依存関係グラフ',
    benefit: 'サーバー・クライアント間の一貫性',
  },
};

コミュニティへの貢献方法

GitHub でのコントリビューション

bash# Turbopackリポジトリのクローン
git clone https://github.com/vercel/turbo.git
cd turbo

# 開発環境のセットアップ
cargo install --path crates/turbopack-cli

# テストの実行
cargo test -p turbopack

# ベンチマークの実行
cargo bench -p turbopack-bench

プラグイン API 設計への参加

現在、Turbopack チームはコミュニティからのフィードバックを積極的に募集しています:

typescript// 望ましいプラグインAPI設計についてのフィードバック例
interface PluginFeedback {
  // どのような機能が必要か
  requiredFeatures: string[];

  // 既存webpack pluginからの移行要件
  migrationNeeds: {
    plugin: string;
    features: string[];
    complexity: 'simple' | 'moderate' | 'complex';
  }[];

  // パフォーマンス要件
  performanceRequirements: {
    buildTime: string;
    memoryUsage: string;
    cacheEfficiency: string;
  };
}

ディスカッションと RFC 参加

markdown<!-- GitHub Discussionsでの提案例 -->

## Plugin API RFC: Custom File Transformations

### 背景

現在の webpack ローダーシステムから移行する際の課題

### 提案

```typescript
interface TransformPlugin {
  name: string;
  test: RegExp;
  transform(
    code: string,
    id: string
  ): Promise<TransformResult>;
}
```

利用例

  • TypeScript 以外の言語サポート
  • カスタムファイル形式の処理
  • コード生成ツールとの連携
arduino
# 実践的なカスタマイズ例

## 実際のプロジェクトでの活用事例

### ケース1: マルチテナント SaaS での設定管理

```javascript
// next.config.js - テナント別設定
const tenantConfig = require('./config/tenants.json');

module.exports = {
  turbopack: {
    resolveAlias: {
      // テナント別の設定ファイル
      '@tenant-config': `./config/tenants/${process.env.TENANT_ID || 'default'}.js`,

      // テナント別のテーマ
      '@theme': `./src/themes/${process.env.TENANT_ID || 'default'}`,
    },

    rules: {
      // テナント別カスタムコンポーネント
      '*.tenant.tsx': {
        loaders: [
          {
            loader: 'string-replace-loader',
            options: {
              multiple: [
                {
                  search: '{{TENANT_NAME}}',
                  replace: process.env.TENANT_NAME || 'Default',
                },
                {
                  search: '{{TENANT_COLOR}}',
                  replace: tenantConfig[process.env.TENANT_ID]?.primaryColor || '#000000',
                },
              ],
            },
          },
        ],
        as: '*.tsx',
      },
    },
  },
};

実際の使用例:

typescript// src/components/Header.tenant.tsx
import React from 'react';

const Header: React.FC = () => {
  return (
    <header style={{ backgroundColor: '{{TENANT_COLOR}}' }}>
      <h1>{{ TENANT_NAME }} Dashboard</h1>
    </header>
  );
};

export default Header;

ケース 2: 国際化(i18n)対応での動的翻訳

javascript// next.config.js - 多言語対応
const supportedLocales = ['ja', 'en', 'ko', 'zh'];

module.exports = {
  turbopack: {
    rules: {
      // 翻訳ファイルの動的生成
      '*.i18n.json': {
        loaders: [
          {
            loader: 'string-replace-loader',
            options: {
              search: '{{LOCALE}}',
              replace: process.env.NEXT_LOCALE || 'ja',
            },
          },
        ],
        as: '*.json',
      },

      // 多言語テンプレート
      '*.intl.tsx': {
        loaders: [
          {
            loader: '@formatjs/ts-transformer',
            options: {
              overrideIdFn: '[sha512:contenthash:base64:6]',
            },
          },
        ],
        as: '*.tsx',
      },
    },

    resolveAlias: {
      // ロケール別メッセージファイル
      '@messages': `./src/locales/${
        process.env.NEXT_LOCALE || 'ja'
      }/messages.json`,
    },
  },
};

ケース 3: GraphQL スキーマの型安全性確保

javascript// next.config.js - GraphQL統合
module.exports = {
  turbopack: {
    rules: {
      // GraphQLスキーマファイル
      '*.graphql': {
        loaders: ['graphql-tag/loader'],
        as: '*.js',
      },

      // GraphQL型定義生成
      '*.gql.ts': {
        loaders: [
          {
            loader: 'string-replace-loader',
            options: {
              search:
                /\/\* GENERATED_TYPES_START \*\/([\s\S]*?)\/\* GENERATED_TYPES_END \*\//,
              replace: function () {
                // 実際のプロジェクトでは外部スクリプトで型生成
                return '/* GENERATED_TYPES_START */\n// Generated types here\n/* GENERATED_TYPES_END */';
              },
            },
          },
        ],
        as: '*.ts',
      },
    },

    resolveAlias: {
      '@schema': './src/graphql/schema.graphql',
      '@queries': './src/graphql/queries',
    },
  },
};

デバッグとトラブルシューティング

パフォーマンス測定

bash# Turbopackトレースファイルの生成
NEXT_TURBOPACK_TRACING=1 yarn dev --turbo

# 生成されるファイル
# .next/trace-turbopack - パフォーマンス情報

よくあるエラーと対処法

1. ローダーが見つからないエラー

bashError: Cannot resolve loader 'custom-loader'

対処法:

bash# ローダーをインストール
yarn add -D custom-loader

# または、フルパスで指定
javascriptmodule.exports = {
  turbopack: {
    rules: {
      '*.custom': {
        loaders: [require.resolve('custom-loader')],
        as: '*.js',
      },
    },
  },
};

2. メモリ不足エラー

bashError: Process ran out of memory
Error: Cannot allocate memory for compilation

対処法:

javascriptmodule.exports = {
  turbopack: {
    // メモリ制限を増加
    memoryLimit: 4 * 1024 * 1024 * 1024, // 4GB
  },
};

3. 循環依存エラー

bashError: Circular dependency detected
Module A -> Module B -> Module A

対処法:

javascript// 問題のある循環依存の例
// file-a.ts
import { funcB } from './file-b';
export function funcA() {
  return funcB();
}

// file-b.ts
import { funcA } from './file-a';
export function funcB() {
  return funcA();
}
javascript// 解決策: 共通モジュールの分離
// shared.ts
export function sharedLogic() {
  return 'shared implementation';
}

// file-a.ts
import { sharedLogic } from './shared';
export function funcA() {
  return sharedLogic();
}

// file-b.ts
import { sharedLogic } from './shared';
export function funcB() {
  return sharedLogic();
}

設定の検証方法

javascript// next.config.js - 設定検証
const { z } = require('zod');

const turbopackConfigSchema = z.object({
  rules: z
    .record(
      z.object({
        loaders: z.array(
          z.union([
            z.string(),
            z.object({
              loader: z.string(),
              options: z.record(z.any()).optional(),
            }),
          ])
        ),
        as: z.string(),
      })
    )
    .optional(),
  resolveAlias: z.record(z.string()).optional(),
  resolveExtensions: z.array(z.string()).optional(),
});

const config = {
  turbopack: {
    // 設定内容
  },
};

// 設定の検証
try {
  turbopackConfigSchema.parse(config.turbopack);
  console.log('✅ Turbopack設定は有効です');
} catch (error) {
  console.error('❌ Turbopack設定エラー:', error.message);
}

module.exports = config;

まとめ

2025 年 6 月現在、Turbopack のプラグイン開発は過渡期にあります。完全なプラグイン API はまだ開発中ですが、webpack ローダーとの限定的な連携や、設定ベースでのカスタマイズによって、実用的な拡張が可能です。

重要なポイント

  1. 現在利用可能な機能を活用し、段階的にカスタマイズを進める
  2. Web 標準に基づいた設計を心がけ、将来の移行に備える
  3. コミュニティへの参加を通じて、プラグイン API の設計に貢献する
  4. パフォーマンスを重視し、Turbopack の利点を最大限に活用する

今後の展望

Turbopack は Web 開発の未来を形作る重要なツールです。現在の制限を理解しつつ、将来のプラグインエコシステムに向けて準備を進めることで、次世代の高速 Web 開発を実現できるでしょう。

プラグイン API の正式リリースまでは、本記事で紹介した手法を活用し、Turbopack の恩恵を受けながら開発を進めることをお勧めいたします。新しい情報が公開され次第、本記事も更新していく予定です。

関連リンク