# 機能設計書 40-@babel/plugin-transform-modules-commonjs

## 概要

本ドキュメントは、Babelの変換プラグイン「@babel/plugin-transform-modules-commonjs」の機能設計を記述する。本プラグインはES6モジュール構文（import/export）をCommonJS形式（require/module.exports）に変換する。

### 本機能の処理概要

本プラグインは、ESモジュールのimport/export構文を検出し、Node.jsやその他のCommonJS環境で実行可能な形式に変換する機能を提供する。これはBabelにおけるモジュール変換の中核となるプラグインである。

**業務上の目的・背景**：ES6で導入されたモジュール構文は、多くの環境でネイティブサポートされているが、Node.jsの古いバージョンやバンドラーを使用しないプロジェクトでは、CommonJS形式への変換が必要となる。本プラグインはその変換を担当する。

**機能の利用シーン**：
- Node.js環境でのESモジュール使用
- CommonJS形式への出力が必要なプロジェクト
- `@babel/preset-env`の一部として自動的に適用される場合
- 動的インポート（import()）のCommonJS形式への変換

**主要な処理内容**：
1. pre関数でモジュールタイプ（commonjs）をマーク
2. 動的インポート（import()）の変換
3. Programノードでモジュールステートメントの書き換え
4. require()呼び出しとexports設定の生成
5. lazyオプションによる遅延ロード対応

**関連システム・外部連携**：
- `@babel/helper-module-transforms`: モジュール変換ヘルパー
- `@babel/helper-plugin-utils`: プラグインユーティリティ
- `@babel/core`: Babelコアとの統合
- `@babel/plugin-transform-dynamic-import`: 動的インポート変換との連携

**権限による制御**：特になし。変換はビルド時に実行される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIまたはビルドツール経由で使用されるため、画面との関連なし |

## 機能種別

コード変換処理 / AST操作 / モジュールトランスフォーム

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| allowCommonJSExports | boolean | No | ES6モジュール内でのmodule/exports参照を許可（デフォルト: true） | - |
| allowTopLevelThis | boolean | No | トップレベルthisを許可 | - |
| importInterop | string/function | No | インポートの相互運用モード | - |
| lazy | boolean/string[]/function | No | 遅延インポートの設定 | boolean、string配列、または関数 |
| loose | boolean | No | loosモード（デフォルト: false） | - |
| mjsStrictNamespace | boolean | No | .mjsファイルでの厳格な名前空間モード | boolean |
| noInterop | boolean | No | 相互運用ヘルパーを無効化 | - |
| strict | boolean | No | strictモード | - |
| strictMode | boolean | No | "use strict"の追加制御 | - |
| strictNamespace | boolean | No | 厳格な名前空間モード（デフォルト: false） | boolean |

### 入力データソース

- Babelによってパースされたソースコード（AST形式）
- ImportDeclaration、ExportDeclaration、ImportExpression

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| transformedAST | AST | 変換後のAST（CommonJS形式） |

### 出力先

- 変換されたASTは`@babel/generator`によってJavaScriptコードとして出力

## 処理フロー

### 処理シーケンス

```
1. プラグイン初期化（pre関数）
   ├─ file.set("@babel/plugin-transform-modules-*", "commonjs")
   └─ lazy設定時: defineCommonJSHook(lazyImportsHook)

2. 動的インポートの処理（CallExpression|ImportExpression visitor）
   ├─ @babel/plugin-proposal-dynamic-importのマーカーを確認
   ├─ requireのリネーム（スコープ衝突回避）
   └─ transformDynamicImport()による変換

3. Program.exit
   ├─ isModule()でESモジュールか確認
   ├─ スコープのリネーム（exports, module, require, __filename, __dirname）
   ├─ allowCommonJSExports=false時: moduleExportsVisitorで参照をエラーに
   ├─ rewriteModuleStatementsAndPrepareHeader()でステートメント書き換え
   ├─ 各インポートソースに対してrequire()呼び出し生成
   │   ├─ サイドエフェクトインポート: require(source)のみ
   │   └─ 通常インポート: var name = interop(require(source))
   ├─ lazy設定時: buildRequireWrapper()で関数ラッパー生成
   └─ path.unshiftContainer("body", headers)でヘッダー挿入
```

### フローチャート

```mermaid
flowchart TD
    A[プラグイン初期化] --> B[pre: modulesタイプをcommonjsにマーク]
    B --> C{lazy設定?}
    C -->|Yes| D[lazyImportsHookを登録]
    C -->|No| E[Program.exitへ]
    D --> E
    E --> F{isModule?}
    F -->|No| Z[変換なし]
    F -->|Yes| G[スコープ変数のリネーム]
    G --> H{allowCommonJSExports?}
    H -->|No| I[moduleExportsVisitorでエラー注入]
    H -->|Yes| J[rewriteModuleStatementsAndPrepareHeader]
    I --> J
    J --> K[各インポートソースをループ]
    K --> L{サイドエフェクト?}
    L -->|Yes| M[require(source)のみ]
    L -->|No| N[var name = interop(require(source))]
    M --> O{lazy && wrap?}
    N --> O
    O -->|Yes| P[buildRequireWrapper()で関数化]
    O -->|No| Q[ヘッダーに追加]
    P --> Q
    Q --> R[ensureStatementsHoisted]
    R --> S[path.unshiftContainer]
    S --> T[変換完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | モジュールタイプマーキング | pre関数で"commonjs"をマーク（dynamic-importプラグインとの連携用） | 常時 |
| BR-40-02 | スコープリネーム | exports, module, require, __filename, __dirnameの衝突回避 | ESモジュール処理時 |
| BR-40-03 | 遅延インポート | lazy=trueで依存モジュールを関数でラップし遅延ロード | lazy設定時 |
| BR-40-04 | 相互運用ヘルパー | noInterop=falseでinteropRequireWildcard/Default使用 | デフォルト |
| BR-40-05 | CommonJS参照禁止 | allowCommonJSExports=falseでmodule/exports参照をエラー化 | 設定時 |

### 計算ロジック

- 変換例（基本）: `import foo from "mod"` → `var _foo = _interopRequireDefault(require("mod"))`
- 変換例（名前付き）: `import { bar } from "mod"` → `var _mod = require("mod")`
- 変換例（エクスポート）: `export { foo }` → `Object.defineProperty(exports, "foo", { ... })`
- 変換例（遅延）: `import foo from "mod"` (lazy) → `function _foo() { const data = require("mod"); _foo = () => data; return data; }`

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

該当なし（コンパイル時変換のため）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| LAZY_VALIDATION | Error | lazyオプションの型が不正 | boolean、string配列、または関数を指定 |
| STRICTNAMESPACE_VALIDATION | Error | strictNamespaceがboolean以外 | booleanまたはundefinedを指定 |
| MJS_STRICTNAMESPACE_VALIDATION | Error | mjsStrictNamespaceがboolean以外 | booleanまたはundefinedを指定 |
| COMMONJS_NOT_AVAILABLE | Error (runtime) | allowCommonJSExports=false時にmodule/exports参照 | sourceTypeをscriptに変更 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- 変換処理は同期的に実行される
- lazyオプションにより実行時のモジュールロードを遅延可能
- constantReexports assumptionで再エクスポートを最適化

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

- 特になし

## 備考

- 本プラグインは`@babel/preset-env`に含まれており、ターゲット環境に応じて自動的に適用される
- Node.js 12以降ではESモジュールがネイティブサポートされているため、新しい環境では不要な場合がある
- `@babel/plugin-transform-dynamic-import`と連携して動的インポートを処理

---

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

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

### 推奨読解順序

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

本プラグインはOptionsインターフェースで豊富な設定オプションを持つ。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/babel-plugin-transform-modules-commonjs/src/index.ts` | Optionsインターフェース（21-32行目）、各オプションの意味 |
| 1-2 | hooks.ts | `packages/babel-plugin-transform-modules-commonjs/src/hooks.ts` | CommonJSHookインターフェース（12-29行目） |

**読解のコツ**: Optionsインターフェースで利用可能なオプションを把握し、CommonJSHookでカスタマイズポイントを理解する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/babel-plugin-transform-modules-commonjs/src/index.ts` | pre関数（169-173行目）、visitor定義（175-290行目） |

**主要処理フロー**:
1. **34-35行目**: declare関数とAPIバージョン検証
2. **37-55行目**: オプションの分解代入とデフォルト値設定
3. **57-60行目**: assumption取得（constantReexports, enumerableModuleMeta, noIncompleteNsImportDetection）
4. **62-75行目**: オプションのバリデーション
5. **77-84行目**: getAssertion関数（CommonJS変数アクセスエラー用）
6. **86-164行目**: moduleExportsVisitor（module/exports参照のエラー化）
7. **169-173行目**: pre関数でモジュールタイプマーキングとlazy hook登録
8. **176-190行目**: 動的インポートvisitor
9. **192-289行目**: Program.exit visitorでメイン変換処理

#### Step 3: 遅延インポート機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lazy.ts | `packages/babel-plugin-transform-modules-commonjs/src/lazy.ts` | lazyImportsHook実装（7-41行目） |

**主要処理フロー**:
- **7-25行目**: getWrapperPayloadで遅延対象を判定
- **26-36行目**: buildRequireWrapperで関数ラッパー生成
- **38-40行目**: wrapReferenceで参照を関数呼び出しに変換

#### Step 4: 動的インポート変換を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | dynamic-import.ts | `packages/babel-plugin-transform-modules-commonjs/src/dynamic-import.ts` | transformDynamicImport実装（16-28行目） |

**主要処理フロー**:
- **8-14行目**: requireNoInterop/requireInteropヘルパー
- **16-28行目**: transformDynamicImport関数でimport()をPromise.resolve().then(() => require())に変換

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

```
@babel/plugin-transform-modules-commonjs/src/index.ts
    │
    ├─ pre()
    │      ├─ file.set("@babel/plugin-transform-modules-*", "commonjs")
    │      └─ defineCommonJSHook(lazyImportsHook) [lazy設定時]
    │
    ├─ visitor.CallExpression|ImportExpression()
    │      └─ transformDynamicImport()
    │             └─ dynamic-import.ts
    │                    └─ buildDynamicImport()
    │
    └─ visitor.Program.exit()
           │
           ├─ path.scope.rename()
           │
           ├─ moduleExportsVisitor [allowCommonJSExports=false時]
           │      ├─ ReferencedIdentifier()
           │      ├─ UpdateExpression()
           │      └─ AssignmentExpression()
           │
           ├─ makeInvokers()
           │      └─ hooks.ts
           │
           ├─ rewriteModuleStatementsAndPrepareHeader()
           │      └─ @babel/helper-module-transforms
           │
           ├─ require()呼び出し生成
           │      ├─ サイドエフェクト: t.expressionStatement(loadExpr)
           │      ├─ 通常: wrapInterop()
           │      └─ lazy: buildRequireWrapper()
           │
           ├─ buildNamespaceInitStatements()
           │
           └─ path.unshiftContainer("body", headers)
```

### データフロー図

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

import foo from "mod"  ─▶  Program.exit visitor          ─▶  var _foo = _interopRequireDefault(
                                │                              require("mod"));
                                ├─ rewriteModuleStatementsAndPrepareHeader
                                ├─ wrapInterop(loadExpr)
                                └─ path.unshiftContainer

export { bar }         ─▶  rewriteModuleStatementsAndPrepareHeader  ─▶  Object.defineProperty(
                                │                                         exports, "bar", {...});
                                └─ buildNamespaceInitStatements

import("mod")          ─▶  CallExpression|ImportExpression  ─▶  Promise.resolve().then(
                               visitor                            () => _interopRequireWildcard(
                                │                                    require("mod")));
                                └─ transformDynamicImport

import x from "dep"    ─▶  lazy=true                        ─▶  function _x() {
                                │                                  const data = require("dep");
                                └─ lazyImportsHook                 _x = () => data;
                                       .buildRequireWrapper        return data;
                                                                 }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/babel-plugin-transform-modules-commonjs/src/index.ts` | ソース | プラグインエントリーポイント、メイン変換ロジック |
| dynamic-import.ts | `packages/babel-plugin-transform-modules-commonjs/src/dynamic-import.ts` | ソース | 動的インポート変換 |
| lazy.ts | `packages/babel-plugin-transform-modules-commonjs/src/lazy.ts` | ソース | 遅延インポート機能 |
| hooks.ts | `packages/babel-plugin-transform-modules-commonjs/src/hooks.ts` | ソース | CommonJSフックインターフェース |
| package.json | `packages/babel-plugin-transform-modules-commonjs/package.json` | 設定 | パッケージ依存関係定義 |
| test/index.js | `packages/babel-plugin-transform-modules-commonjs/test/index.js` | テスト | テストランナー設定 |
| fixtures/ | `packages/babel-plugin-transform-modules-commonjs/test/fixtures/` | テスト | 変換テストケース |
