Viteのプラグインアーキテクチャを探求し、開発ワークフローを強化するカスタムプラグインの作成方法を学びます。実践的な例で主要な概念を習得しましょう。
Viteプラグインアーキテクチャの解明:カスタムプラグイン作成のためのグローバルガイド
超高速ビルドツールであるViteは、フロントエンド開発に革命をもたらしました。その速度とシンプルさは、主にその強力なプラグインアーキテクチャによるものです。このアーキテクチャにより、開発者はViteの機能を拡張し、特定のプロジェクトのニーズに合わせて調整できます。このガイドでは、Viteのプラグインシステムを包括的に探求し、独自のカスタムプラグインを作成して開発ワークフローを最適化する力を与えます。
Viteのコア原則を理解する
プラグイン作成に入る前に、Viteの基本原則を把握することが不可欠です:
- オンデマンドコンパイル: Viteはブラウザからリクエストされたときにのみコードをコンパイルするため、起動時間が大幅に短縮されます。
- ネイティブESM: Viteは開発にネイティブECMAScriptモジュール(ESM)を活用し、開発中のバンドルを不要にします。
- Rollupベースのプロダクションビルド: プロダクションビルドでは、Viteは高度に最適化されたバンドラーであるRollupを利用して、効率的で本番環境に対応したコードを生成します。
Viteエコシステムにおけるプラグインの役割
Viteのプラグインアーキテクチャは、高度に拡張可能に設計されています。プラグインは以下のことができます:
- コードの変換(例:TypeScriptのトランスパイル、プリプロセッサの追加)。
- カスタムファイルの提供(例:静的アセットの処理、仮想モジュールの作成)。
- ビルドプロセスの変更(例:画像の最適化、サービスワーカーの生成)。
- ViteのCLIの拡張(例:カスタムコマンドの追加)。
プラグインは、単純な変更から複雑な統合まで、Viteをさまざまなプロジェクト要件に適応させるための鍵となります。
Viteプラグインアーキテクチャ:詳細解説
Viteプラグインは、その動作を定義する特定のプロパティを持つJavaScriptオブジェクトです。主要な要素を見ていきましょう:
プラグインの設定
vite.config.js
(またはvite.config.ts
)ファイルは、使用するプラグインの指定を含む、Viteプロジェクトを設定する場所です。plugins
オプションは、プラグインオブジェクトまたはプラグインオブジェクトを返す関数の配列を受け入れます。
// vite.config.js
import myPlugin from './my-plugin';
export default {
plugins: [
myPlugin(), // プラグイン関数を呼び出してプラグインインスタンスを作成
],
};
プラグインオブジェクトのプロパティ
Viteプラグインオブジェクトは、ビルドプロセスのさまざまなフェーズでの動作を定義するいくつかのプロパティを持つことができます。最も一般的なプロパティの内訳は次のとおりです:
- name: プラグインの一意の名前。これは必須であり、デバッグや競合解決に役立ちます。 例:
'my-custom-plugin'
- enforce: プラグインの実行順序を決定します。可能な値は
'pre'
(コアプラグインの前に実行)、'normal'
(デフォルト)、'post'
(コアプラグインの後に実行)です。 例:'pre'
- config: Viteの設定オブジェクトを変更できます。ユーザー設定と環境(モードとコマンド)を受け取ります。 例:
config: (config, { mode, command }) => { ... }
- configResolved: Vite設定が完全に解決された後に呼び出されます。最終的な設定オブジェクトにアクセスするのに役立ちます。例:
configResolved(config) { ... }
- configureServer: 開発サーバーインスタンス(Connect/Expressライク)へのアクセスを提供します。カスタムミドルウェアの追加やサーバーの動作の変更に役立ちます。例:
configureServer(server) { ... }
- transformIndexHtml:
index.html
ファイルを変換できます。スクリプト、スタイル、またはメタタグの注入に役立ちます。例:transformIndexHtml(html) { ... }
- resolveId: モジュール解決をインターセプトして変更できます。カスタムモジュール解決ロジックに役立ちます。例:
resolveId(source, importer) { ... }
- load: カスタムモジュールを読み込んだり、既存のモジュールコンテンツを変更したりできます。仮想モジュールやカスタムローダーに役立ちます。例:
load(id) { ... }
- transform: モジュールのソースコードを変換します。BabelプラグインやPostCSSプラグインに似ています。例:
transform(code, id) { ... }
- buildStart: ビルドプロセスの開始時に呼び出されます。例:
buildStart() { ... }
- buildEnd: ビルドプロセスが完了した後に呼び出されます。例:
buildEnd() { ... }
- closeBundle: バンドルがディスクに書き込まれた後に呼び出されます。例:
closeBundle() { ... }
- writeBundle: バンドルをディスクに書き込む前に呼び出され、変更が可能です。例:
writeBundle(options, bundle) { ... }
- renderError: 開発中にカスタムエラーページをレンダリングできます。例:
renderError(error, req, res) { ... }
- handleHotUpdate: HMRを細かく制御できます。例:
handleHotUpdate({ file, server }) { ... }
プラグインフックと実行順序
Viteプラグインは、ビルドプロセスのさまざまな段階でトリガーされる一連のフックを通じて動作します。これらのフックが実行される順序を理解することは、効果的なプラグインを作成するために不可欠です。
- config: Vite設定を変更します。
- configResolved: 解決された設定にアクセスします。
- configureServer: 開発サーバーを変更します(開発時のみ)。
- transformIndexHtml:
index.html
ファイルを変換します。 - buildStart: ビルドプロセスを開始します。
- resolveId: モジュールIDを解決します。
- load: モジュールコンテンツを読み込みます。
- transform: モジュールコードを変換します。
- handleHotUpdate: ホットモジュールリプレースメント(HMR)を処理します。
- writeBundle: ディスクに書き込む前に出力バンドルを変更します。
- closeBundle: 出力バンドルがディスクに書き込まれた後に呼び出されます。
- buildEnd: ビルドプロセスを終了します。
最初のカスタムViteプラグインを作成する
プロダクションビルドの各JavaScriptファイルの先頭にバナーを追加する簡単なViteプラグインを作成してみましょう。このバナーには、プロジェクト名とバージョンが含まれます。
プラグインの実装
// banner-plugin.js
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
export default function bannerPlugin() {
return {
name: 'banner-plugin',
apply: 'build',
transform(code, id) {
if (!id.endsWith('.js')) {
return code;
}
const packageJsonPath = resolve(process.cwd(), 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;
return banner + code;
},
};
}
解説:
- name: プラグインの名前「banner-plugin」を定義します。
- apply: このプラグインがビルドプロセス中にのみ実行されることを指定します。「build」に設定すると、本番環境専用になり、開発中の不要なオーバーヘッドを回避できます。
- transform(code, id):
- これはプラグインの中核です。各モジュールのコード(
code
)とID(id
)をインターセプトします。 - 条件付きチェック:
if (!id.endsWith('.js'))
は、変換がJavaScriptファイルにのみ適用されることを保証します。これにより、エラーや予期しない動作を引き起こす可能性のある他のファイルタイプ(CSSやHTMLなど)の処理を防ぎます。 - Package.jsonへのアクセス:
resolve(process.cwd(), 'package.json')
はpackage.json
ファイルへの絶対パスを構築します。process.cwd()
は現在の作業ディレクトリを返し、コマンドがどこで実行されても正しいパスが使用されるようにします。JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
はpackage.json
ファイルを読み込んで解析します。readFileSync
はファイルを同期的に読み込み、'utf-8'
はUnicode文字を正しく処理するためのエンコーディングを指定します。同期的な読み込みは、変換の開始時に一度だけ発生するため、ここでは許容されます。
- バナーの生成:
const banner = `/**\n * Project: ${packageJson.name}\n * Version: ${packageJson.version}\n */\n`;
はバナー文字列を作成します。テンプレートリテラル(バッククォート)を使用して、package.json
ファイルからプロジェクト名とバージョンを簡単に埋め込みます。\n
シーケンスは、バナーを正しくフォーマットするために改行を挿入します。
- コード変換:
return banner + code;
は、元のJavaScriptコードの先頭にバナーを追加します。これがtransform関数から返される最終結果です。
- これはプラグインの中核です。各モジュールのコード(
プラグインの統合
プラグインをvite.config.js
ファイルにインポートし、plugins
配列に追加します:
// vite.config.js
import bannerPlugin from './banner-plugin';
export default {
plugins: [
bannerPlugin(),
],
};
ビルドの実行
ここで、npm run build
(またはプロジェクトのビルドコマンド)を実行します。ビルドが完了したら、dist
ディレクトリ内の生成されたJavaScriptファイルを確認してください。各ファイルの先頭にバナーが表示されます。
高度なプラグインテクニック
単純なコード変換を超えて、Viteプラグインはより高度なテクニックを活用してその機能を強化できます。
仮想モジュール
仮想モジュールを使用すると、プラグインはディスク上に実際のファイルとして存在しないモジュールを作成できます。これは、動的なコンテンツを生成したり、アプリケーションに設定データを提供したりするのに役立ちます。
// virtual-module-plugin.js
export default function virtualModulePlugin(options) {
const virtualModuleId = 'virtual:my-module';
const resolvedVirtualModuleId = '\0' + virtualModuleId; // Rollupによる処理を防ぐため \0 を接頭辞として付与
return {
name: 'virtual-module-plugin',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export default ${JSON.stringify(options)};`;
}
},
};
}
この例では:
virtualModuleId
は、仮想モジュールの識別子を表す文字列です。resolvedVirtualModuleId
は、Rollupがそれを実際のファイルとして処理するのを防ぐために\0
で始まっています。これはRollupプラグインで使用される規約です。resolveId
はモジュール解決をインターセプトし、リクエストされたIDがvirtualModuleId
と一致する場合に解決済みの仮想モジュールIDを返します。load
はモジュールの読み込みをインターセプトし、リクエストされたIDがresolvedVirtualModuleId
と一致する場合にモジュールのコードを返します。この場合、options
をデフォルトエクスポートとしてエクスポートするJavaScriptモジュールを生成します。
仮想モジュールの使用
// vite.config.js
import virtualModulePlugin from './virtual-module-plugin';
export default {
plugins: [
virtualModulePlugin({ message: 'Hello from virtual module!' }),
],
};
// main.js
import message from 'virtual:my-module';
console.log(message.message); // 出力: Hello from virtual module!
Index HTMLの変換
transformIndexHtml
フックを使用すると、index.html
ファイルを変更できます。例えば、スクリプト、スタイル、またはメタタグを注入することができます。これは、分析トラッキングの追加、ソーシャルメディアのメタデータの設定、またはHTML構造のカスタマイズに役立ちます。
// inject-script-plugin.js
export default function injectScriptPlugin() {
return {
name: 'inject-script-plugin',
transformIndexHtml(html) {
return html.replace(
'