# 機能設計書 13-動的インポート

## 概要

本ドキュメントは、webpackにおけるimport()構文による動的インポート・遅延読み込み機能について、その設計と実装の詳細を記述する。

### 本機能の処理概要

動的インポート機能は、ES2020の`import()`構文を解析し、非同期チャンクとして分離してオンデマンドで読み込む仕組みを提供する。これにより、初期バンドルサイズを削減し、必要なタイミングでコードを遅延ロードできる。

**業務上の目的・背景**：大規模なWebアプリケーションでは、全てのコードを初期バンドルに含めると初期ロード時間が著しく増大する。動的インポートにより、ルートベースのコード分割やコンポーネントの遅延読み込みが可能となり、ユーザーが実際に必要とするコードのみを適切なタイミングで取得できる。React.lazyやVueのasync componentsなどのフレームワーク機能の基盤でもある。

**機能の利用シーン**：ルートベースのコード分割（SPAでのページ遷移時）、大型ライブラリの遅延読み込み（チャートライブラリ、エディタ等）、条件付き機能の読み込み（管理者機能等）、AMD形式のrequire([])による非同期モジュール読み込み。

**主要な処理内容**：
1. JavaScriptパーサでimport()式を検出しImportDependencyを生成
2. AsyncDependenciesBlockを作成し、非同期チャンク境界を定義
3. ImportDependencyTemplateによるコード生成（import()をwebpackランタイム呼び出しに置換）
4. Referenced exportsの追跡（Tree Shakingとの連携）
5. チャンクグループの生成と非同期チャンクの管理

**関連システム・外部連携**：JavascriptParser、AsyncDependenciesBlock、ImportParserPlugin、RuntimeTemplate、チャンクローディングランタイム（JSONP/CommonJS/ESM等）と連携する。

**権限による制御**：特になし。ソースコード中のimport()式により自動的に発動。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | Page A (hybrid-routing) | 主機能 | router.jsのimport()による遅延読み込みターゲット |
| 9 | Page B (hybrid-routing) | 主機能 | router.jsのimport()による遅延読み込みターゲット |
| 10 | Login (reexport-components) | 主機能 | import(./pages/Login)で動的に読み込まれる |
| 11 | Dashboard (reexport-components) | 主機能 | import(./pages/Dashboard)で動的に読み込まれる |
| 12 | Page A (multiple-entry-points) | 補助機能 | AMD形式のrequire([./shared])による非同期読み込み |
| 13 | Page B (multiple-entry-points) | 補助機能 | require.ensureによる./sharedの非同期読み込み |
| 18 | Page 1 (code-splitting-depend-on-advanced) | 主機能 | lazy.jsをimport()で動的に遅延読み込み |
| 19 | App (Host) (module-federation) | 補助機能 | ComponentD(mfe-c/Component2)のReact.lazyによる遅延読み込み |
| 21 | MFE-C (Remote) (module-federation) | 補助機能 | LazyComponent(Component2)の遅延読み込み用動的インポート |

## 機能種別

コード分割 / 非同期読み込み

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| request | `string` | Yes | インポート対象のモジュールパス | - |
| range | `Range` | Yes | ソースコード中の式の位置範囲 | - |
| referencedExports | `RawReferencedExports \| null` | No | 参照されるエクスポートのリスト | - |
| phase | `ImportPhaseType` | Yes | インポートフェーズ | - |
| attributes | `ImportAttributes` | No | インポート属性（import assertions） | - |

### 入力データソース

- JavascriptParserによるソースコード解析結果
- ImportParserPluginによるimport()式の検出

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ImportDependency | `Dependency` | 動的インポート依存関係 |
| AsyncDependenciesBlock | `AsyncDependenciesBlock` | 非同期依存ブロック |
| 生成コード | `string` | import()をwebpackランタイム呼び出しに置換したコード |

### 出力先

Compilationのモジュールグラフ（依存関係として）、出力バンドルのJavaScriptコード

## 処理フロー

### 処理シーケンス

```
1. JavascriptParserがimport()式を検出
   └─ ImportParserPluginのimportCallフックが発火
2. import()のモードを判定
   └─ eager / weak / lazy / lazy-once のいずれか
3. ImportDependencyの生成
   └─ request, range, referencedExports, phase, attributesを設定
4. AsyncDependenciesBlockの生成
   └─ チャンク名、groupOptions（prefetchOrder, preloadOrder等）を設定
5. ブロックへの依存関係追加
   └─ AsyncDependenciesBlock.addDependency(importDependency)
6. モジュールへのブロック追加
   └─ module.addBlock(asyncDependenciesBlock)
7. コード生成時
   └─ ImportDependencyTemplateがimport()をruntimeTemplate.moduleNamespacePromise()に置換
```

### フローチャート

```mermaid
flowchart TD
    A[JavascriptParser: import式検出] --> B[ImportParserPlugin]
    B --> C{モード判定}
    C -->|lazy| D[ImportDependency生成]
    C -->|eager| E[ImportEagerDependency生成]
    C -->|weak| F[ImportWeakDependency生成]
    D --> G[AsyncDependenciesBlock生成]
    G --> H[block.addDependency]
    H --> I[module.addBlock]
    I --> J[コード生成フェーズ]
    J --> K[ImportDependencyTemplate.apply]
    K --> L[runtimeTemplate.moduleNamespacePromise]
    L --> M[import()がランタイム呼び出しに置換]
    E --> N[同期的なモジュール解決]
    F --> O[弱参照として記録]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | lazyモード | デフォルト。新しい非同期チャンクを生成し、オンデマンドで読み込む | import()のデフォルト |
| BR-02 | eagerモード | 別チャンクを生成せず、モジュールをPromiseとして解決 | webpackMode: "eager" |
| BR-03 | weakモード | モジュールが既にロード済みの場合のみ解決 | webpackMode: "weak" |
| BR-04 | referenced exports追跡 | import().then(m => m.foo)パターンでfooのみを参照として記録 | Tree Shaking連携時 |
| BR-05 | import属性 | import("./mod", { with: { type: "json" } })によるアサーション | import attributes使用時 |
| BR-06 | チャンク名指定 | webpackChunkName マジックコメントによるチャンク名の明示的指定 | コメント指定時 |

### 計算ロジック

- **getResourceIdentifier**: リソース識別子にimport属性のJSON文字列を付加して一意性を確保
- **getReferencedExports**: "default"エクスポートの場合、インポート先モジュールのexportsTypeに応じて参照範囲を決定

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | UnsupportedFeatureWarning | 非リテラルのimport()引数 | 警告として報告 |
| - | CommentCompilationWarning | マジックコメントの解析失敗 | 警告として報告 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- パーサフック内での軽量な依存関係生成
- コード生成はテンプレート置換のみで高速

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

- 動的インポートのパスが変数の場合はContextDependencyとなり、意図しないモジュールがバンドルに含まれる可能性がある

## 備考

- `webpackPrefetch: true`マジックコメントでプリフェッチ、`webpackPreload: true`でプリロードが可能
- `webpackChunkName: "name"`でチャンク名をカスタマイズ可能
- require.ensure()はレガシー構文だが同様の機能を提供

---

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

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

### 推奨読解順序

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

動的インポートの依存関係クラスを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ImportDependency.js | `lib/dependencies/ImportDependency.js` | ImportDependencyクラスの構造（request, range, referencedExports, phase, attributes） |
| 1-2 | AsyncDependenciesBlock.js | `lib/AsyncDependenciesBlock.js` | 非同期依存ブロックのデータ構造 |

**読解のコツ**: ImportDependencyはModuleDependencyを継承し、type="import()"、category="esm"を返す。referencedExportsはTree Shaking連携用。

#### Step 2: パーサプラグインを理解する

import()式の検出と依存関係生成のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ImportParserPlugin.js | `lib/dependencies/ImportParserPlugin.js` | import()式のパースとDependency生成ロジック |

**主要処理フロー**:
1. **53-58行目**: getState関数でパーサ状態管理
2. **65-78行目**: tagDynamicImportReferencedで動的インポートの参照エクスポートを追跡
3. **84-96行目**: getFulfilledCallbackNamespaceObjでimport().then()のコールバック引数を解析

#### Step 3: テンプレートによるコード生成を理解する

import()がどのようにランタイムコードに変換されるかを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ImportDependency.js | `lib/dependencies/ImportDependency.js` | 125-156行目: ImportDependencyTemplateクラス |

**主要処理フロー**:
- **134-155行目**: apply()メソッドでimport()式をruntimeTemplate.moduleNamespacePromise()の結果で置換
- **140-142行目**: moduleGraph.getParentBlock()でAsyncDependenciesBlockを取得
- **143-152行目**: moduleNamespacePromiseにchunkGraph, block, module, request, strict等を渡す
- **154行目**: source.replace()でソースコードの該当範囲を置換

#### Step 4: referenced exportsの解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ImportDependency.js | `lib/dependencies/ImportDependency.js` | 68-98行目: getReferencedExports()メソッド |

**主要処理フロー**:
- **69行目**: referencedExportsがnullの場合はEXPORTS_OBJECT_REFERENCED（オブジェクト全体を参照）
- **73-91行目**: "default"エクスポートの場合、importedModuleのexportsTypeを確認し、"default-only"/"default-with-named"の場合はオブジェクト全体を参照
- **92-96行目**: それ以外の場合はcanMangle: falseで個別参照を記録

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

```
JavascriptParser
    |
    +-- ImportParserPlugin (import()式検出)
    |       |
    |       +-- new ImportDependency(request, range, referencedExports, phase, attributes)
    |       +-- new ImportEagerDependency(...)  [eagerモード]
    |       +-- new ImportWeakDependency(...)   [weakモード]
    |       +-- new AsyncDependenciesBlock(groupOptions, loc, request)
    |       +-- block.addDependency(importDep)
    |       +-- module.addBlock(block)
    |
    +-- Compilation (コード生成フェーズ)
            |
            +-- ImportDependencyTemplate.apply()
                    |
                    +-- runtimeTemplate.moduleNamespacePromise()
                    +-- source.replace(range[0], range[1]-1, content)
```

### データフロー図

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

ソースコード中の         --> JavascriptParser           +--> ImportDependency
import("./module")           |                          |
                             v                          |
                        ImportParserPlugin          +--> AsyncDependenciesBlock
                             |                          |
                             v                          |
                        モード判定                  +--> 新しい非同期チャンク
                        (lazy/eager/weak)               |
                             |                          |
                             v                          |
                        ImportDependencyTemplate    +--> ランタイム呼び出しコード
                             |                          (__webpack_require__.e)
                             v
                        出力バンドル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ImportDependency.js | `lib/dependencies/ImportDependency.js` | ソース | 動的インポート依存関係クラス |
| ImportEagerDependency.js | `lib/dependencies/ImportEagerDependency.js` | ソース | eagerモード用依存関係 |
| ImportWeakDependency.js | `lib/dependencies/ImportWeakDependency.js` | ソース | weakモード用依存関係 |
| ImportParserPlugin.js | `lib/dependencies/ImportParserPlugin.js` | ソース | import()式のパースプラグイン |
| ImportContextDependency.js | `lib/dependencies/ImportContextDependency.js` | ソース | 動的パスのコンテキスト依存関係 |
| AsyncDependenciesBlock.js | `lib/AsyncDependenciesBlock.js` | ソース | 非同期依存ブロック |
| RuntimeTemplate.js | `lib/RuntimeTemplate.js` | ソース | ランタイムコードテンプレート |
| JavascriptParser.js | `lib/javascript/JavascriptParser.js` | ソース | JavaScriptパーサ |
| ImportPhase.js | `lib/dependencies/ImportPhase.js` | ソース | インポートフェーズ定義 |
