# 機能設計書 31-requireオプション

## 概要

本ドキュメントは、CoffeeScript CLIの`--require`（`-r`）オプションの機能設計を記述する。このオプションは、REPL（対話型実行環境）やeval実行前に指定したモジュールを事前にrequireし、グローバルスコープで利用可能にする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：開発者がREPLやevalでCoffeeScriptコードを実行する際、頻繁に使用するモジュール（例：lodash、moment等のユーティリティライブラリ）を毎回手動でrequireする手間を省くことを目的とする。また、グローバルスコープにモジュールを設定することで、対話的なコード実験やデバッグ作業を効率化する。

**機能の利用シーン**：
- REPLセッション開始時に、よく使うライブラリを事前読み込みして即座に利用可能にしたい場合
- `--eval`オプションでワンライナーを実行する際、外部モジュールを使用したい場合
- 複数のモジュールを一括で読み込んで、開発・デバッグ環境をセットアップしたい場合

**主要な処理内容**：
1. コマンドライン引数から`-r`または`--require`オプションを解析
2. 指定されたモジュール名（複数指定可能）を取得
3. `makePrelude`関数でモジュールをrequireするプレリュードコードを生成
4. 生成されたプレリュードコードをREPL/eval実行前に評価
5. モジュールをグローバルオブジェクトに設定して、以降のコード実行で参照可能にする

**関連システム・外部連携**：Node.jsの`require()`関数を使用してモジュールを読み込む。npm/yarnでインストールされたパッケージ、ローカルモジュール、組み込みモジュールすべてに対応する。

**権限による制御**：特になし。ファイルシステムやモジュール解決はNode.jsのネイティブ動作に依存する。

## 関連画面

本機能はCLI機能のため、画面との直接的な関連はない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

環境設定 / モジュール読み込み

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --require / -r | String（リスト形式） | No | 読み込むモジュール名。複数指定可能。`name=module`形式でエイリアス指定可 | モジュールが存在しない場合はNode.jsがエラーを出力 |

### 入力データソース

コマンドライン引数（`process.argv`経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| global[name] | any | 読み込まれたモジュールのエクスポート内容 |

### 出力先

Node.jsのグローバルオブジェクト（`global`）

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数の解析
   └─ optionParserがprocess.argv[2..]を解析、opts.requireに配列として格納
2. makePrelude関数の呼び出し
   └─ opts.requireが存在する場合、makePrelude(opts.require)を呼び出し
3. プレリュードコードの生成
   └─ 各モジュール指定を解析し、global[name] = require('module')形式のコードを生成
4. プレリュードコードの実行
   └─ REPL起動時: runInContextでrepl.contextにてプレリュードを実行
   └─ eval実行時: CoffeeScript.evalでプレリュードを事前評価
5. モジュールがグローバルスコープで利用可能に
```

### フローチャート

```mermaid
flowchart TD
    A[coffee コマンド実行] --> B{--require オプションあり?}
    B -->|No| C[通常処理へ]
    B -->|Yes| D[makePrelude関数呼び出し]
    D --> E{エイリアス指定あり?<br/>name=module形式}
    E -->|Yes| F[指定されたnameを使用]
    E -->|No| G[モジュール名からbaseFileNameを抽出]
    F --> H[global設定コード生成]
    G --> H
    H --> I[セミコロンで結合]
    I --> J[opts.preludeに格納]
    J --> K{REPL or eval?}
    K -->|REPL| L[runInContextでプレリュード実行]
    K -->|eval| M[CoffeeScript.evalでプレリュード実行]
    L --> N[モジュールがグローバルで利用可能]
    M --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-31-01 | モジュール名抽出 | エイリアスが指定されない場合、モジュールパスのベースファイル名（拡張子除く）を変数名として使用 | エイリアス未指定時 |
| BR-31-02 | 複数モジュール対応 | -rオプションは複数回指定可能で、すべてのモジュールが順番に読み込まれる | 複数の-rオプション指定時 |
| BR-31-03 | エイリアス形式 | `name=module`形式で指定すると、moduleをrequireしてglobal[name]に設定 | =を含む引数 |

### 計算ロジック

**モジュール名の決定ロジック（makePrelude関数内）**:
```
if (引数に`=`を含む)
  name = `=`の左側
  module = `=`の右側
else
  module = 引数全体
  name = helpers.baseFileName(module, yes, useWinPathSep)
```

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| MODULE_NOT_FOUND | Node.js Error | 指定されたモジュールが見つからない | モジュール名を確認し、npm installでインストールする |
| SYNTAX_ERROR | SyntaxError | プレリュードコードの構文エラー（内部的にはほぼ発生しない） | 開発者に報告 |

### リトライ仕様

リトライは行わない。モジュール読み込みに失敗した場合は即座にエラーとなる。

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

該当なし。

## パフォーマンス要件

特になし。モジュール読み込みは起動時に1回のみ実行される。

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

- 任意のモジュールを読み込むため、信頼できないモジュールを指定しないよう注意が必要
- グローバルオブジェクトを変更するため、既存のグローバル変数を上書きする可能性がある

## 備考

- Windows環境ではパス区切り文字の扱いが異なる（`useWinPathSep`フラグで対応）
- `--transpile`オプションと併用する場合、プレリュードコードもトランスパイルされる

---

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

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

### 推奨読解順序

#### Step 1: オプション定義を理解する

まず、--requireオプションがどのように定義されているかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | command.coffee | `src/command.coffee` | SWITCHESでの-r/--requireオプション定義、isList形式（`[MODULE*]`）の意味 |

**主要処理フロー**:
- **50行目**: `-r`/`--require`オプションの定義。`[MODULE*]`の`*`は複数指定可能を示す

**読解のコツ**: optparse.coffeeの`OPTIONAL`正規表現（`/\[(\w+(\*?))\]/`）で、`*`がリスト形式を示すことを理解する

#### Step 2: オプションパーサーを理解する

オプションがどのようにパースされるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | optparse.coffee | `src/optparse.coffee` | OptionParserクラスの動作、isList属性の処理方法 |

**主要処理フロー**:
- **43-48行目**: isListがtrueの場合、同名オプションを配列に追加していく処理
- **111-112行目**: buildRule関数でisListフラグを設定（`*`があればtrue）

#### Step 3: プレリュード生成処理を理解する

モジュール読み込みコードがどのように生成されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | command.coffee | `src/command.coffee` | makePrelude関数の実装詳細 |

**主要処理フロー**:
- **94行目**: opts.requireが存在する場合にmakePreludeを呼び出し
- **141-146行目**: makePrelude関数本体
  - **143行目**: `name=module`形式の解析（正規表現`/^(.*)=(.*)$/`）
  - **144行目**: エイリアスがない場合はbaseFileNameでモジュール名を抽出
  - **145行目**: `global['#{name}'] = require('#{module}')`形式のコード生成
  - **146行目**: セミコロンで結合

#### Step 4: プレリュード実行処理を理解する

生成されたプレリュードコードがどこで実行されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | command.coffee | `src/command.coffee` | run関数でのREPL起動処理 |
| 4-2 | repl.coffee | `src/repl.coffee` | start関数でのプレリュード実行 |

**主要処理フロー**:
- command.coffee **94-96行目**: replCliOptsにpreludeとtranspileを設定
- command.coffee **100行目**: REPL起動時にreplCliOptsを渡す
- repl.coffee **214行目**: `runInContext opts.prelude, repl.context, 'prelude'`でプレリュード実行

#### Step 5: eval実行時のプレリュード処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | command.coffee | `src/command.coffee` | compileScript関数でのeval処理 |

**主要処理フロー**:
- **214-217行目**: opts.runがtrueの場合
  - **216行目**: `CoffeeScript.eval opts.prelude, task.options`でプレリュードを評価

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

```
exports.run (command.coffee:72)
    │
    ├─ parseOptions (command.coffee:445)
    │      └─ optionParser.parse → opts.require に配列格納
    │
    ├─ makePrelude (command.coffee:141)
    │      ├─ module.match(/^(.*)=(.*)$/)
    │      ├─ helpers.baseFileName
    │      └─ "global['#{name}'] = require('#{module}')" 生成
    │
    ├─ require('./repl').start (command.coffee:100)
    │      └─ runInContext opts.prelude (repl.coffee:214)
    │
    └─ compileScript (command.coffee:202) ※eval時
           └─ CoffeeScript.eval opts.prelude (command.coffee:216)
```

### データフロー図

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

process.argv          parseOptions                    opts.require (配列)
  -r lodash    ───▶   optionParser.parse    ───▶    ['lodash']
  -r _=underscore                                   ['lodash', '_=underscore']

opts.require          makePrelude                    opts.prelude (文字列)
['lodash',     ───▶   モジュール名解析・      ───▶  "global['lodash']=require('lodash');
'_=underscore']       コード生成                      global['_']=require('underscore')"

opts.prelude          REPL/eval実行                  global オブジェクト
"global[...]"  ───▶   runInContext /        ───▶    global.lodash = {...}
                      CoffeeScript.eval              global._ = {...}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| command.coffee | `src/command.coffee` | ソース | CLI本体、makePrelude関数、run関数 |
| optparse.coffee | `src/optparse.coffee` | ソース | オプションパーサー、isListフラグ処理 |
| repl.coffee | `src/repl.coffee` | ソース | REPL実装、プレリュード実行（runInContext） |
| helpers.coffee | `src/helpers.coffee` | ソース | baseFileName関数（モジュール名抽出） |
