# 機能設計書 47-run関数

## 概要

本ドキュメントは、CoffeeScriptコードをNode.js環境で実行するrun関数の設計を記載する。

### 本機能の処理概要

run関数は、CoffeeScriptのソースコード文字列をコンパイルし、Node.js環境で直接実行する機能である。コンパイル後のJavaScriptをメインモジュールのコンテキストで実行し、`__filename`、`__dirname`、相対`require()`を正しく設定する。coffeeコマンドでスクリプトを実行する際の主要な実行パスとなる。

**業務上の目的・背景**：CoffeeScriptで書かれたスクリプトを直接実行したいケースは多い。run関数を使用することで、事前コンパイル不要でCoffeeScriptコードをNode.jsアプリケーションとして実行でき、開発・テスト・自動化スクリプトの作成が容易になる。

**機能の利用シーン**：
- `coffee script.coffee`コマンドでスクリプトを実行するとき
- CoffeeScriptコードを動的に実行するとき
- Cakefileの実行時
- Try CoffeeScriptでの「Run」ボタン押下時

**主要な処理内容**：
1. メインモジュール（require.main）のファイル名とprocess.argv[1]を設定
2. モジュールキャッシュをクリア
3. node_modules解決用のパスを設定
4. 子モジュールコンパイル用のオプションを保存
5. CoffeeScriptをコンパイルしてJavaScriptを生成
6. mainModule._compileでJavaScriptを実行

**関連システム・外部連携**：
- Node.jsのモジュールシステム（require.main、Module）
- CoffeeScriptコンパイラ
- ファイルシステム（ファイル名からのパス解決）

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Try CoffeeScript | 遷移先機能 | 実行ボタン押下時にコンパイル済みJavaScriptをeval実行 |

## 機能種別

コード実行 / スクリプトランナー

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| code | string | Yes | 実行するCoffeeScriptソースコード | 文字列であること |
| options.filename | string | No | ソースファイル名（__filename設定に使用） | ファイルパスとして有効であること |
| options.transpile | object | No | Babelトランスパイルのオプション | オブジェクトであること |

### 入力データソース

- ファイルシステムから読み込まれたCoffeeScriptコード
- コマンドライン引数（--evalオプション）
- 標準入力（--stdioオプション）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | void | run関数は値を返さない |

### 出力先

- 標準出力（スクリプト内のconsole.logなど）
- グローバル状態（require.main、process.argvなど）

## 処理フロー

### 処理シーケンス

```
1. メインモジュールの参照取得
   └─ mainModule = require.main

2. ファイル名の設定
   └─ filename指定があればfs.realpathSync、なければ匿名ファイル名生成

3. process.argv[1]の設定
   └─ mainModule.filename を process.argv[1] に設定

4. モジュールキャッシュのクリア
   └─ mainModule.moduleCache = {}（存在する場合）

5. node_modulesパスの設定
   └─ Module._nodeModulePaths(dir) でパスリストを生成

6. オプションの保存
   └─ mainModule.options = options（子モジュールコンパイル用）

7. インラインソースマップの有効化
   └─ options.inlineMap = true

8. CoffeeScriptコンパイル
   └─ CoffeeScript.compile(code, options)

9. JavaScript実行
   └─ mainModule._compile(js, mainModule.filename)
```

### フローチャート

```mermaid
flowchart TD
    A["CoffeeScript.run(code, options)"] --> B[mainModule = require.main]
    B --> C{filename指定?}
    C -->|Yes| D["fs.realpathSync(filename)"]
    C -->|No| E["helpers.anonymousFileName()"]
    D --> F["mainModule.filename = filename"]
    E --> F
    F --> G["process.argv[1] = filename"]
    G --> H["mainModule.moduleCache = {}"]
    H --> I["mainModule.paths = Module._nodeModulePaths(dir)"]
    I --> J["mainModule.options = options"]
    J --> K["options.inlineMap = true"]
    K --> L["CoffeeScript.compile(code, options)"]
    L --> M["answer.js ? answer でJS取得"]
    M --> N["mainModule._compile(js, filename)"]
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ファイル名解決 | filename指定時はfs.realpathSyncで絶対パスに変換 | options.filenameが指定されている場合 |
| BR-02 | 匿名ファイル名 | filename未指定時はhelpers.anonymousFileName()で一意の名前を生成 | options.filenameが未指定の場合 |
| BR-03 | インラインソースマップ | デバッグ用にインラインソースマップを常に有効化 | 常時 |
| BR-04 | オプション継承 | mainModule.optionsに保存したオプションは子モジュールのコンパイルで使用 | 子モジュールのrequire時 |
| BR-05 | process.argv設定 | process.argv[1]をスクリプトのファイル名に設定 | 常時 |

### 計算ロジック

ディレクトリパスの計算（index.coffee 46-49行目）:
```coffeescript
dir = if options.filename?
  path.dirname fs.realpathSync options.filename
else
  fs.realpathSync '.'
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SyntaxError | 構文エラー | CoffeeScriptコードに構文エラーがある | エラー箇所を修正 |
| Error | コンパイルエラー | コンパイル時に一般的なエラー発生 | エラーメッセージを確認 |
| ENOENT | ファイルエラー | 指定されたfilenameが存在しない | ファイルパスを確認 |

### リトライ仕様

該当なし。エラー発生時は例外がスローされる。

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

該当なし

## パフォーマンス要件

- コンパイルはrun呼び出し時に1回のみ実行
- コンパイル済みJavaScriptはメインモジュールのコンテキストで即座に実行
- 実行時間はスクリプトの内容に依存

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

- run関数は渡されたコードをそのまま実行するため、信頼できないソースからのコードは実行しないこと
- mainModule（require.main）を直接変更するため、他のモジュールに影響を与える可能性がある

## 備考

- run関数はsrc/index.coffeeに定義されている（coffeescript.coffeeにはスタブのみ）
- `answer.js ? answer`の記述は、コンパイル結果がオブジェクトの場合はjsプロパティを、文字列の場合はそのまま使用する
- transpileオプションが指定されている場合、コンパイル後のJavaScriptはBabelでさらに変換される

---

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

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

### 推奨読解順序

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

run関数の実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.coffee | `src/index.coffee` | run関数の定義（35-62行目） |

**主要処理フロー**:
- **36行目**: `mainModule = require.main` でメインモジュール参照取得
- **39-40行目**: ファイル名の設定（realpathSyncまたはanonymousFileName）
- **43行目**: `mainModule.moduleCache and= {}` でキャッシュクリア
- **46-49行目**: ディレクトリパスの決定
- **50行目**: `Module._nodeModulePaths(dir)` でnode_modulesパス設定
- **53行目**: `mainModule.options = options` でオプション保存
- **55-56行目**: `options.filename`, `options.inlineMap`の設定
- **59行目**: `CoffeeScript.compile(code, options)` でコンパイル
- **60行目**: `answer.js ? answer` でJavaScript取得
- **62行目**: `mainModule._compile(js, filename)` で実行

**読解のコツ**:
- `and=` は `if a then a = b` のショートハンド
- `?` は存在演算子で、`answer.js ? answer` は `answer.js` が存在すればそれを、なければ `answer` を使用

#### Step 2: コンパイル処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.coffee | `src/index.coffee` | CoffeeScript.compile のラッパー（24-31行目） |

**主要処理フロー**:
- **29-30行目**: transpileオプションがある場合、transpile関数をバインド
- **31行目**: `universalCompile.call`で元のcompileを呼び出し

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | helpers.coffee | `src/helpers.coffee` | anonymousFileName関数（187-190行目） |

**主要処理フロー**:
- 連番でユニークな匿名ファイル名を生成 `<anonymous-N>`

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

```
coffee script.coffee
    │
    └─ command.coffee (run関数呼び出し)
           │
           └─ CoffeeScript.run(code, options) (index.coffee:35)
                  │
                  ├─ mainModule = require.main
                  │
                  ├─ filename設定
                  │       └─ fs.realpathSync / helpers.anonymousFileName
                  │
                  ├─ process.argv[1] = filename
                  │
                  ├─ mainModule.paths = Module._nodeModulePaths(dir)
                  │
                  ├─ mainModule.options = options
                  │
                  ├─ CoffeeScript.compile(code, options)
                  │       │
                  │       ├─ options.transpile? → transpile処理
                  │       │
                  │       └─ universalCompile (coffeescript.coffee:62)
                  │
                  └─ mainModule._compile(js, filename)
                         │
                         └─ スクリプト実行 → 子モジュールのrequire
                                │
                                └─ loadFile (register.coffee) → getRootModule → options継承
```

### データフロー図

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

CoffeeScriptコード ─▶ CoffeeScript.run
                         │
options.filename ────────┤
                         │
                         ├─▶ ファイル名解決
                         │       │
                         │       ▼
                         │   __filename, __dirname設定
                         │
                         ├─▶ process.argv[1]設定
                         │
                         ├─▶ node_modulesパス設定
                         │
                         ├─▶ CoffeeScript.compile
                         │       │
                         │       ▼
                         │   JavaScript + SourceMap
                         │
                         └─▶ mainModule._compile
                                 │
                                 ▼
                           スクリプト実行 ──▶ stdout/stderr
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.coffee | `src/index.coffee` | ソース | run関数のメイン実装 |
| coffeescript.coffee | `src/coffeescript.coffee` | ソース | compile関数の実装 |
| helpers.coffee | `src/helpers.coffee` | ソース | anonymousFileName関数 |
| register.coffee | `src/register.coffee` | ソース | 子モジュールのCoffeeScript読み込み |
| command.coffee | `src/command.coffee` | ソース | CLIからのrun呼び出し |
