# 機能設計書 59-BannerPlugin

## 概要

本ドキュメントは、webpackにおけるBannerPlugin機能の設計仕様を記述する。生成されたバンドルファイルの先頭または末尾にバナー文字列（ライセンス情報、ビルド情報等）を挿入する機能である。

### 本機能の処理概要

**業務上の目的・背景**：オープンソースライブラリやエンタープライズソフトウェアでは、バンドルファイルにライセンス表記や著作権情報を含める必要がある。BannerPluginは、指定された文字列をJavaScriptコメント形式でバンドルの先頭（または末尾）に挿入する。テンプレート変数（ハッシュ、チャンク名等）も使用でき、動的なバナー生成も可能である。

**機能の利用シーン**：MITライセンス等の表記挿入、ビルド日時・バージョン情報の埋め込み、著作権コメントの自動挿入、特定ファイルパターンへの選択的適用に利用される。

**主要な処理内容**：
1. バナー文字列またはバナー生成関数を受け取り、設定を検証する
2. compilation.hooks.processAssetsフックにタップして、アセット処理フェーズで実行する
3. 各チャンクのファイルに対して、ファイル名パターンマッチングを行う
4. マッチしたファイルに対して、バナー文字列をConcatSourceで先頭（または末尾）に結合する
5. WeakMapキャッシュにより、同一ソースへの重複バナー挿入を防止する

**関連システム・外部連携**：webpack-sourcesライブラリのConcatSourceを使用してソース結合を行う。ModuleFilenameHelpersでファイルパターンマッチングを行う。

**権限による制御**：特になし。webpack設定のpluginsで設定する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はUIを持たない内部処理であり、直接関連する画面は存在しない |

## 機能種別

アセット後処理 / テキスト挿入

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| banner | string \| function | Yes | バナー文字列またはバナー生成関数 | JSON Schemaバリデーション |
| raw | boolean | No | trueの場合コメントラッピングしない（デフォルト: false） | - |
| entryOnly | boolean | No | trueの場合エントリチャンクのみに適用（デフォルト: false） | - |
| footer | boolean | No | trueの場合ファイル末尾に挿入（デフォルト: false、先頭に挿入） | - |
| test | string \| RegExp | No | マッチするファイル名パターン | - |
| include | string \| RegExp | No | 含めるファイル名パターン | - |
| exclude | string \| RegExp | No | 除外するファイル名パターン | - |
| stage | number | No | processAssetsのステージ（デフォルト: PROCESS_ASSETS_STAGE_ADDITIONS） | - |

### 入力データソース

- webpack設定: `new BannerPlugin({ banner: '/* MIT License */', raw: true })`
- バナー関数: `new BannerPlugin({ banner: (data) => \`/* Build: ${data.hash} */\` })`

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| modifiedSource | ConcatSource | バナーが先頭/末尾に結合されたソース |

### 出力先

compilation.updateAsset()によりアセットのソースが更新される

## 処理フロー

### 処理シーケンス

```
1. BannerPlugin.apply() → compiler.hooks.compilation にタップ
   └─ processAssetsフック（PROCESS_ASSETS_STAGE_ADDITIONS）に登録
2. processAssetsフック発火時
   └─ 全チャンクを走査
3. entryOnlyオプション判定
   └─ entryOnly=true の場合、initialチャンクのみ処理
4. 各チャンクの各ファイルに対してパターンマッチ
   └─ ModuleFilenameHelpers.matchObject で test/include/exclude 判定
5. バナー文字列を生成
   └─ 関数の場合は {hash, chunk, filename} を引数に呼び出し
6. ConcatSourceでバナーを結合
   └─ footer=true: new ConcatSource(old, "\n", comment)
   └─ footer=false: new ConcatSource(comment, "\n", old)
7. WeakMapキャッシュで重複防止
   └─ 同一ソース・同一コメントならキャッシュを使用
```

### フローチャート

```mermaid
flowchart TD
    A[processAssetsフック発火] --> B[全チャンク走査]
    B --> C{entryOnly?}
    C -->|Yes| D{initialチャンク?}
    D -->|No| B2[次のチャンク]
    D -->|Yes| E[チャンクのファイル走査]
    C -->|No| E
    E --> F{ファイルパターンマッチ?}
    F -->|No| E2[次のファイル]
    F -->|Yes| G[バナー文字列生成]
    G --> H{キャッシュあり?}
    H -->|Yes| I[キャッシュ使用]
    H -->|No| J{footer?}
    J -->|Yes| K[末尾にConcatSource]
    J -->|No| L[先頭にConcatSource]
    K --> M[compilation.updateAsset]
    L --> M
    I --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-59-01 | コメントラッピング | raw=falseの場合、バナー文字列を`/*! ... */`形式のJavaScriptコメントでラッピングする | デフォルト動作 |
| BR-59-02 | 複数行コメント | バナーに改行が含まれる場合、複数行コメント形式（`/*!\n * line1\n * line2\n */`）に変換する | wrapComment関数 |
| BR-59-03 | エントリ限定 | entryOnly=trueの場合、chunk.canBeInitial()がfalseのチャンクはスキップする | entryOnly設定時 |
| BR-59-04 | キャッシュ | WeakMapキャッシュにより、同一ソースに同一コメントを再度結合することを防止する | 常時 |
| BR-59-05 | テンプレート変数 | バナー関数には{hash, chunk, filename}が渡され、compilation.getPath()でテンプレート展開される | 常時 |
| BR-59-06 | コメント内エスケープ | バナー文字列内の`*/`は`* /`にエスケープされる | wrapComment関数内 |

### 計算ロジック

コメントラッピング: 単一行→`Template.toComment(str)`、複数行→`/*!\n * ${str.split("\n").join("\n * ")}\n */`。末尾空白は`trimEnd()`で除去。

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

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本機能はデータベースを使用しない |

### テーブル別操作詳細

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ValidationError | オプションがJSON Schemaに違反した場合 | createSchemaValidation関数によるバリデーションエラー |

### リトライ仕様

該当なし。

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

該当なし（同期的なアセット処理）

## パフォーマンス要件

- ConcatSourceはソースの結合を遅延評価するため、バナー挿入自体のコストは低い
- WeakMapキャッシュにより再ビルド時の重複計算を防止

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

特になし。バナーに含める情報は開発者が制御する。

## 備考

- 全138行のコンパクトなプラグインである
- processAssetsのステージはデフォルトでPROCESS_ASSETS_STAGE_ADDITIONS（アセット追加後）
- stageオプションで実行タイミングを変更可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | BannerPlugin.js | `lib/BannerPlugin.js` | プラグインの全体構造（138行の小規模プラグイン） |

**読解のコツ**: BannerPluginは非常にシンプルな構造で、wrapComment→コンストラクタ→apply()の順に読み進めるのが効率的。

- **21行目**: BannerFunction型 - `(data: { hash?, chunk, filename }) => string`
- **23-31行目**: JSON Schemaバリデーション設定
- **37-47行目**: `wrapComment`関数 - 単一行/複数行コメントラッピング
- **51-79行目**: コンストラクタ - バナー関数の構築（文字列/関数/raw対応）

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | BannerPlugin.js | `lib/BannerPlugin.js` | apply()メソッドの全体フロー |

**主要処理フロー**:
1. **87-93行目**: apply()メソッドの初期化 - matchObject, cache, stageの設定
2. **96-97行目**: processAssetsフックのステージ決定（デフォルト: PROCESS_ASSETS_STAGE_ADDITIONS）
3. **99-100行目**: `compilation.hooks.processAssets`にタップ
4. **101行目**: `compilation.chunks`を走査
5. **102-104行目**: entryOnly判定 - `chunk.canBeInitial()`
6. **106-108行目**: ファイル走査 + `matchObject(file)`でパターンマッチ
7. **114-118行目**: バナー文字列生成 - `compilation.getPath(banner, data)`でテンプレート展開
8. **120-130行目**: `compilation.updateAsset()`でソース更新 - WeakMapキャッシュ + ConcatSource結合

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

```
BannerPlugin.apply(compiler)
    │
    └─ compiler.hooks.compilation
           └─ compilation.hooks.processAssets (STAGE_ADDITIONS)
                  │
                  ├─ compilation.chunks 走査
                  │      ├─ chunk.canBeInitial() 判定
                  │      └─ chunk.files 走査
                  │             └─ ModuleFilenameHelpers.matchObject()
                  │
                  ├─ compilation.getPath(banner, {chunk, filename})
                  │      └─ テンプレート変数展開
                  │
                  └─ compilation.updateAsset(file, old => ...)
                         └─ new ConcatSource(comment, "\n", old)
                            または new ConcatSource(old, "\n", comment)
```

### データフロー図

```
[入力]                         [処理]                              [出力]

banner: "MIT License" ─────▶ wrapComment() ────────────────▶ "/*! MIT License */"
                                                                    │
compilation.chunks ────────▶ チャンク走査                           │
                               └─ chunk.files走査                   │
                                   └─ matchObject(file)             │
                                                                    │
old source ────────────────▶ ConcatSource(comment, "\n", old) ─▶ 更新済みソース
                                                                    │
                              compilation.updateAsset() ───────▶ アセット更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| BannerPlugin.js | `lib/BannerPlugin.js` | ソース | バナー挿入プラグイン本体（138行） |
| Compilation.js | `lib/Compilation.js` | ソース | processAssetsフック、updateAsset API |
| ModuleFilenameHelpers.js | `lib/ModuleFilenameHelpers.js` | ソース | ファイルパターンマッチング（test/include/exclude） |
| Template.js | `lib/Template.js` | ソース | toComment関数（単一行コメント生成） |
| BannerPlugin.json | `schemas/plugins/BannerPlugin.json` | スキーマ | オプションバリデーションスキーマ |
| BannerPlugin.check.js | `schemas/plugins/BannerPlugin.check.js` | スキーマ | バリデーション関数 |
