# 機能設計書 67-DllPlugin / DllReferencePlugin

## 概要

本ドキュメントは、webpackの組込みプラグインである `DllPlugin` と `DllReferencePlugin` の機能設計を記述する。これらのプラグインは、事前ビルド済みDLLの生成・参照によりビルド時間を短縮する仕組みを提供する。

### 本機能の処理概要

DllPlugin/DllReferencePluginは、変更頻度の低いライブラリ（vendor）を事前にDLLとしてビルドし、メインアプリケーションのビルド時にはDLLのマニフェストファイルを参照するだけで済むようにすることで、ビルド時間を大幅に短縮する機能を提供する。

**業務上の目的・背景**：大規模プロジェクトでは、React、lodash、momentjsなどの外部ライブラリのビルドに多くの時間がかかる。これらのライブラリは頻繁に変更されないため、毎回ビルドするのは非効率である。DLLパターンにより、ライブラリを一度だけビルドし、マニフェストファイルを通じて参照することでインクリメンタルビルドの速度を向上させる。

**機能の利用シーン**：開発時のビルド高速化、CI/CDパイプラインでのvendorキャッシュ活用、マイクロフロントエンド環境での共有ライブラリの事前ビルドに利用される。

**主要な処理内容**：

**DllPlugin（DLLビルド側）**:
1. `entryOption` フックで各エントリに対してDllEntryPluginを適用する
2. LibManifestPluginでマニフェストJSONファイル（モジュールID・エクスポート情報）を生成する
3. entryOnlyオプションでエントリポイントのみまたは全モジュールをマニフェストに含めるかを制御する

**DllReferencePlugin（DLL参照側）**:
1. `beforeCompile` フックでマニフェストファイルを非同期に読み込む
2. `compile` フックでExternalModuleFactoryPluginとDelegatedModuleFactoryPluginを適用する
3. DLL内のモジュールへの参照をDelegatedModuleとして解決し、外部変数（DLL名）経由でアクセスする
4. マニフェストファイルをファイル依存関係として登録し、変更時に再ビルドする

**関連システム・外部連携**：DllEntryPlugin、LibManifestPlugin、DelegatedModuleFactoryPlugin、ExternalModuleFactoryPlugin、DelegatedSourceDependencyと連携する。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 24 | App (dll-app-and-vendor) | 主機能 | DllReferencePluginにより事前コンパイル済みvendorバンドルのマニフェストを参照 |

## 機能種別

データ連携 / ビルド最適化（DLLビルド・参照）

## 入力仕様

### 入力パラメータ（DllPlugin）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | `string` | Yes | マニフェストJSONファイルの出力パス | 絶対パス |
| name | `string` | No | DLL関数名（外部名）。output.libraryの値を使用 | - |
| context | `string` | No | マニフェスト内のリクエストのコンテキスト | デフォルト: webpackのcontext |
| entryOnly | `boolean` | No | エントリポイントのみをマニフェストに含める | デフォルト: true |
| format | `boolean` | No | マニフェストJSONを整形出力するか | デフォルト: false |
| type | `string` | No | DLLバンドルの外部型（libraryTarget） | - |

### 入力パラメータ（DllReferencePlugin）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| manifest | `string \| DllReferencePluginOptionsManifest` | No* | マニフェストファイルパスまたはオブジェクト | *contentと排他 |
| content | `DllReferencePluginOptionsContent` | No* | マニフェストのcontent部分 | *manifestと排他 |
| name | `string` | No | DLL変数名 | マニフェストから自動取得可 |
| sourceType | `string` | No | 外部モジュールのソースタイプ | デフォルト: "var" |
| context | `string` | No | リクエストのコンテキスト | デフォルト: webpackのcontext |
| type | `string` | No | DelegatedModuleの型 | - |
| scope | `string` | No | スコーププレフィックス | - |
| extensions | `string[]` | No | 拡張子の配列 | - |

### 入力データソース

- webpack設定ファイルのpluginsセクション
- マニフェストJSONファイル（DllReferencePlugin使用時）
- エントリポイント定義（DllPlugin使用時）

## 出力仕様

### 出力データ（DllPlugin）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| manifest.json | `JSON` | DLLのモジュールID、ビルドメタ、エクスポート情報を含むマニフェストファイル |
| DLLバンドル | `JS` | コンパイル済みのDLLバンドルファイル |

### 出力データ（DllReferencePlugin）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ExternalModule | `Module` | DLL変数への参照を提供する外部モジュール |
| DelegatedModule | `Module` | DLL内の個別モジュールへの委譲モジュール |

### 出力先

DllPlugin: マニフェストJSONファイル（intermediateFileSystem経由）
DllReferencePlugin: コンパイル結果のモジュールグラフ内

## 処理フロー

### 処理シーケンス

```
DllPlugin:
1. entryOptionフックでエントリを処理
   └─ 各エントリに対してDllEntryPlugin.apply()を呼び出す
2. DllEntryPluginがmakeフックでDllEntryDependencyを追加
   └─ 各エントリモジュールをEntryDependencyとして登録
3. LibManifestPluginがemitフックでマニフェストを生成
   └─ チャンクのモジュールをイテレートしJSONファイルを出力

DllReferencePlugin:
1. beforeCompileフックでマニフェストを読み込む
   └─ マニフェストがファイルパスの場合、readFileで非同期読み込み
2. compileフックでモジュール解決プラグインを適用
   └─ ExternalModuleFactoryPluginでDLL変数を外部モジュールとして登録
   └─ DelegatedModuleFactoryPluginでDLL内モジュールを委譲モジュールとして解決
3. compilationフックでエラー処理とファイル依存登録
   └─ マニフェスト解析エラーの報告、マニフェストのファイル依存登録
```

### フローチャート

```mermaid
flowchart TD
    subgraph DllPlugin
        A1[entryOption フック] --> B1[DllEntryPlugin.apply]
        B1 --> C1[make フックでエントリ追加]
        C1 --> D1[LibManifestPlugin]
        D1 --> E1[マニフェストJSON出力]
    end

    subgraph DllReferencePlugin
        A2[beforeCompile フック] --> B2[マニフェスト読み込み]
        B2 --> C2{解析成功?}
        C2 -->|Yes| D2[compile フック]
        C2 -->|No| E2[DllManifestError]
        D2 --> F2[ExternalModuleFactoryPlugin適用]
        D2 --> G2[DelegatedModuleFactoryPlugin適用]
        E2 --> H2[compilation.errors に追加]
    end

    E1 -.->|マニフェストファイル| B2
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | エントリオンリー | entryOnly=true（デフォルト）の場合、エントリポイントのモジュールのみマニフェストに含まれる | DllPlugin使用時 |
| BR-02 | 動的エントリ非対応 | DllPluginは関数型のエントリ定義をサポートしない | entry が関数の場合 |
| BR-03 | マニフェスト解析エラー | マニフェストJSONの解析に失敗した場合、DllManifestErrorとしてコンパイルエラーに追加 | manifest解析失敗時 |
| BR-04 | ファイル依存登録 | マニフェストファイルはコンパイルのファイル依存関係として登録される | manifestがファイルパスの場合 |
| BR-05 | スコープ解決 | scopeが指定されている場合、`scope/xxx` 形式のリクエストをDLL内モジュールとして解決する | DllReferencePluginでscope指定時 |
| BR-06 | 一意パスチェック | 各チャンクのマニフェスト出力パスは一意でなければならない | LibManifestPlugin |

### 計算ロジック

特筆すべき計算ロジックは存在しない。

## データベース操作仕様

データベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DllManifestError | コンパイルエラー | マニフェストJSONの解析に失敗した場合 | マニフェストファイルの内容を確認する |
| Error | 設定エラー | DllPluginで動的エントリ（関数）が指定された場合 | 静的なエントリオブジェクトを使用する |
| Error | 一意性エラー | 各チャンクのマニフェスト出力パスが重複した場合 | パステンプレートにチャンク固有の値を使用する |

### リトライ仕様

リトライ仕様は存在しない。

## トランザクション仕様

トランザクション仕様は存在しない。

## パフォーマンス要件

- DLLビルドは一度だけ実行し、マニフェストファイルを再利用することで、以降のビルドでvenorライブラリの再処理を回避する
- マニフェストファイルは軽量なJSONであり、読み込みコストは低い

## セキュリティ考慮事項

- マニフェストファイルはファイルシステム上に保存されるため、改竄されないよう適切なアクセス制御が必要
- DLLバンドルは信頼できるビルドプロセスで生成されたものを使用すること

## 備考

- DllPlugin/DllReferencePluginは、Module Federation（No.37）の登場により使用頻度は減少しているが、シンプルなvendor分離には依然として有用
- マニフェストの `name` フィールドはグローバル変数名として使用されるため、一意である必要がある
- `entryOnly: false` とした場合、FlagAllModulesAsUsedPluginが適用され、全モジュールがTree Shakingの対象外となる

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | マニフェストJSONの構造: `{ name, type, content: { [ident]: { id, buildMeta, exports } } }` |
| 1-2 | DllReferencePlugin.js | `lib/DllReferencePlugin.js` | `CompilationDataItem` 型（33行目）: `{ path, data, error }` |

**読解のコツ**: マニフェストのcontent部分は `モジュール識別子 -> { id, buildMeta, exports }` のマッピング。DllReferencePluginはこのマッピングを使ってDelegatedModuleを生成する。

#### Step 2: DllPlugin（ビルド側）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DllPlugin.js | `lib/DllPlugin.js` | `apply()` メソッド（46-70行目） |
| 2-2 | DllEntryPlugin.js | `lib/DllEntryPlugin.js` | DLLエントリの登録処理（37-73行目） |
| 2-3 | LibManifestPlugin.js | `lib/LibManifestPlugin.js` | マニフェスト生成処理（52-141行目） |

**主要処理フロー**:
1. **DllPlugin.js 47行目**: `entryOption` フックで各エントリにDllEntryPluginを適用
2. **DllPlugin.js 66行目**: LibManifestPluginを適用
3. **DllEntryPlugin.js 52行目**: `make` フックでDllEntryDependencyを追加
4. **LibManifestPlugin.js 53行目**: `emit` フック（stage:110）でマニフェストJSONを出力

#### Step 3: DllReferencePlugin（参照側）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DllReferencePlugin.js | `lib/DllReferencePlugin.js` | `apply()` メソッド（53-173行目） |
| 3-2 | DelegatedModuleFactoryPlugin.js | `lib/DelegatedModuleFactoryPlugin.js` | DLL内モジュールの委譲解決 |

**主要処理フロー**:
1. **DllReferencePlugin.js 64行目**: `beforeCompile` でマニフェスト非同期読み込み
2. **DllReferencePlugin.js 105行目**: `compile` でExternalModuleFactoryPlugin/DelegatedModuleFactoryPlugin適用
3. **DllReferencePlugin.js 155行目**: `compilation` でエラー報告・ファイル依存登録

### プログラム呼び出し階層図

```
DllPlugin.apply(compiler)
    |
    +-- compiler.hooks.entryOption.tap()
    |       |
    |       +-- DllEntryPlugin.apply(compiler)
    |               |
    |               +-- compiler.hooks.make.tapAsync()
    |                       +-- compilation.addEntry(DllEntryDependency)
    |
    +-- LibManifestPlugin.apply(compiler)
    |       |
    |       +-- compiler.hooks.emit.tapAsync(stage:110)
    |               +-- マニフェストJSON出力
    |
    +-- [entryOnly=false] FlagAllModulesAsUsedPlugin.apply(compiler)

DllReferencePlugin.apply(compiler)
    |
    +-- compiler.hooks.beforeCompile.tapAsync()
    |       +-- readFile(manifest) → parseJson()
    |
    +-- compiler.hooks.compile.tap()
    |       +-- ExternalModuleFactoryPlugin.apply(nmf)
    |       +-- DelegatedModuleFactoryPlugin.apply(nmf)
    |
    +-- compiler.hooks.compilation.tap()
            +-- compilation.errors.push(DllManifestError)
            +-- compilation.fileDependencies.add(manifest)
```

### データフロー図

```
[DLLビルド]
エントリモジュール ──▶ DllEntryPlugin ──▶ コンパイル ──▶ LibManifestPlugin ──▶ manifest.json + dll.js

[アプリビルド]
manifest.json ──▶ DllReferencePlugin ──▶ DelegatedModuleFactoryPlugin ──▶ DelegatedModule
                                        ExternalModuleFactoryPlugin     ──▶ ExternalModule (dll変数参照)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DllPlugin.js | `lib/DllPlugin.js` | ソース | DLLビルド側のメインプラグイン |
| DllReferencePlugin.js | `lib/DllReferencePlugin.js` | ソース | DLL参照側のメインプラグイン |
| DllEntryPlugin.js | `lib/DllEntryPlugin.js` | ソース | DLLエントリの登録処理 |
| DllModuleFactory.js | `lib/DllModuleFactory.js` | ソース | DLLモジュールのファクトリ |
| LibManifestPlugin.js | `lib/LibManifestPlugin.js` | ソース | マニフェストJSON生成 |
| DelegatedModuleFactoryPlugin.js | `lib/DelegatedModuleFactoryPlugin.js` | ソース | DLL内モジュールの委譲解決 |
| DelegatedModule.js | `lib/DelegatedModule.js` | ソース | DLL内の個別モジュールを表す委譲モジュール |
| ExternalModuleFactoryPlugin.js | `lib/ExternalModuleFactoryPlugin.js` | ソース | DLL変数の外部モジュール登録 |
| FlagAllModulesAsUsedPlugin.js | `lib/FlagAllModulesAsUsedPlugin.js` | ソース | entryOnly=false時に全モジュールを使用済みにマーク |
| DllPlugin.json | `schemas/plugins/DllPlugin.json` | 設定 | DllPluginのバリデーションスキーマ |
| DllReferencePlugin.json | `schemas/plugins/DllReferencePlugin.json` | 設定 | DllReferencePluginのバリデーションスキーマ |
