# 機能設計書 50-child_process.fork対応

## 概要

本ドキュメントは、CoffeeScriptファイルを直接forkできるようにするchild_process.forkパッチの設計を記載する。

### 本機能の処理概要

child_process.fork対応機能は、Node.jsの`child_process.fork()`をパッチして、CoffeeScriptファイル（.coffee、.litcoffee、.coffee.md）を直接子プロセスとして起動できるようにする機能である。通常、child_process.forkはJavaScriptファイルのみを受け付けるが、このパッチによりCoffeeScriptファイルを指定した場合、自動的にcoffeeバイナリ経由で実行されるようになる。

**業務上の目的・背景**：Node.jsでは、計算負荷の高い処理やワーカープロセスを子プロセスとしてforkすることが一般的である。CoffeeScriptで書かれたアプリケーションでも同様のパターンを使用したい場合、通常はJavaScriptにコンパイルしてからforkする必要がある。このパッチにより、事前コンパイル不要でCoffeeScriptファイルを直接forkでき、開発効率が向上する。

**機能の利用シーン**：
- ワーカープロセスをCoffeeScriptで実装しているとき
- クラスターモードでCoffeeScriptアプリケーションを実行するとき
- バックグラウンド処理をCoffeeScriptで記述してforkするとき
- テスト環境で子プロセスをCoffeeScriptで実行するとき

**主要な処理内容**：
1. child_process.forkをラッパー関数で置き換え
2. fork呼び出し時にパスがCoffeeScriptファイルかどうかを判定
3. CoffeeScriptファイルの場合、coffeeバイナリのパスを取得
4. 引数を再構成してcoffeeバイナリ経由でfork

**関連システム・外部連携**：
- Node.jsのchild_processモジュール
- coffeeバイナリ（bin/coffee）
- helpersモジュール（isCoffee関数）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | Annotated Source - Register | 補助機能 | forkパッチの解説を含む |
| 36 | Annotated Source - Register | 補助機能 | forkパッチの解説を含む（v2） |

## 機能種別

モジュールシステム拡張 / プロセス管理

## 入力仕様

### 入力パラメータ

パッチされたchild_process.forkの引数:

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string | Yes | 実行するファイルのパス | 文字列 |
| args | string[] | No | 子プロセスに渡す引数配列 | 配列またはオブジェクト |
| options | object | No | forkオプション（env, cwd等） | オブジェクト |

### 入力データソース

- アプリケーションコードからのchild_process.fork呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ChildProcess | ChildProcess | Node.jsのChildProcessインスタンス |

### 出力先

Node.jsのChildProcessオブジェクトとして呼び出し元に返却

## 処理フロー

### 処理シーケンス

```
1. fork呼び出しの受け取り
   └─ パッチされたfork関数がpath, args, optionsを受け取る

2. CoffeeScriptファイル判定
   └─ helpers.isCoffee(path)でファイル拡張子を確認

3. 引数の正規化
   └─ argsが配列でない場合はoptionsとして扱い、argsを空配列に

4. 引数の再構成（CoffeeScriptの場合）
   └─ argsの先頭にpathを追加し、pathをcoffeeバイナリに変更

5. 元のfork呼び出し
   └─ 元のchild_process.fork(path, args, options)を呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A["child_process.fork(path, args, options)"] --> B{"helpers.isCoffee(path)?"}
    B -->|No| C["元のfork(path, args, options)"]
    B -->|Yes| D{argsは配列?}
    D -->|No| E["options = args or {}"]
    E --> F["args = []"]
    D -->|Yes| G["argsをそのまま使用"]
    F --> H["args = [path].concat(args)"]
    G --> H
    H --> I["path = binary (coffeeバイナリ)"]
    I --> J["元のfork(path, args, options)"]
    C --> K[ChildProcess返却]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | CoffeeScript判定 | helpers.isCoffee()で拡張子が.coffee、.litcoffee、.coffee.mdかを判定 | fork呼び出し時 |
| BR-02 | 引数の柔軟性 | argsが配列でない場合はoptionsとして扱う（Node.jsのforkと同様） | argsが配列でない場合 |
| BR-03 | coffeeバイナリパス | require.resolveでbin/coffeeの絶対パスを取得 | CoffeeScriptファイルfork時 |
| BR-04 | JavaScriptファイル透過 | CoffeeScriptでないファイルは元のforkをそのまま使用 | isCoffeeがfalseの場合 |

### 計算ロジック

coffeeバイナリパスの取得（register.coffee 61行目）:
```coffeescript
binary = require.resolve '../../bin/coffee'
```

引数の再構成（register.coffee 62-68行目）:
```coffeescript
child_process.fork = (path, args, options) ->
  if helpers.isCoffee path
    unless Array.isArray args
      options = args or {}
      args = []
    args = [path].concat args
    path = binary
  fork path, args, options
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOENT | ファイルエラー | 指定されたファイルが存在しない | ファイルパスを確認 |
| SyntaxError | 構文エラー | CoffeeScriptファイルに構文エラーがある | 子プロセス側のエラーとして報告される |

### リトライ仕様

該当なし。エラー発生時は子プロセスが異常終了する。

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

該当なし

## パフォーマンス要件

- forkパッチのオーバーヘッドは最小（文字列判定と引数再構成のみ）
- 子プロセスの起動時間はcoffeeバイナリの初期化時間が加算される

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

- forkされる子プロセスはcoffeeバイナリ経由で実行されるため、coffeeバイナリへのアクセス権限が必要
- 子プロセスは親プロセスと同じ権限で実行される

## 備考

- このパッチはregister.coffeeで適用される
- child_process.spawnではなくforkのみがパッチされる
- cluster.forkは内部的にchild_process.forkを使用するため、このパッチが適用される

---

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

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

### 推奨読解順序

#### Step 1: パッチの全体構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | register.coffee | `src/register.coffee` | child_process.forkパッチ（57-69行目） |

**主要処理フロー**:
- **59-60行目**: `if child_process` でNode.js環境確認
- **60行目**: `{fork} = child_process` で元のfork関数を保存
- **61行目**: `binary = require.resolve '../../bin/coffee'` でcoffeeバイナリパス取得
- **62-69行目**: パッチされたfork関数の定義

**読解のコツ**:
- CoffeeScriptの分割代入 `{fork} = child_process` は `fork = child_process.fork` と同等
- `require.resolve` はファイルの絶対パスを返す

#### Step 2: CoffeeScript判定を理解する

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

**主要処理フロー**:
- **204行目**: `/\.((lit)?coffee|coffee\.md)$/.test file` で拡張子を正規表現で判定

#### Step 3: 引数の再構成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | register.coffee | `src/register.coffee` | 引数処理部分（63-68行目） |

**主要処理フロー**:
- **63行目**: `if helpers.isCoffee path` でCoffeeScriptファイルか判定
- **64-66行目**: `unless Array.isArray args` で引数形式の正規化
- **67行目**: `args = [path].concat args` で元のパスを引数の先頭に追加
- **68行目**: `path = binary` でパスをcoffeeバイナリに変更

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

```
アプリケーションコード
    │
    └─ child_process.fork('worker.coffee', [arg1, arg2])
           │
           └─ パッチされたfork関数 (register.coffee:62)
                  │
                  ├─ helpers.isCoffee('worker.coffee') → true
                  │
                  ├─ args = ['worker.coffee', arg1, arg2]
                  │
                  ├─ path = '/path/to/bin/coffee'
                  │
                  └─ 元のfork('/path/to/bin/coffee', ['worker.coffee', arg1, arg2], options)
                         │
                         └─ 子プロセス: coffee worker.coffee arg1 arg2
```

### データフロー図

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

fork('script.coffee', args)
         │
         ▼
    helpers.isCoffee()
         │
         ├─▶ false ──▶ 元のfork ──▶ ChildProcess
         │
         └─▶ true
              │
              ▼
         引数再構成
              │
              ├─▶ path = coffeeバイナリ
              │
              └─▶ args = [script.coffee, ...元args]
                       │
                       ▼
                  元のfork(binary, args, options)
                       │
                       ▼
                  ChildProcess ──▶ 呼び出し元
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| register.coffee | `src/register.coffee` | ソース | child_process.forkパッチの実装 |
| helpers.coffee | `src/helpers.coffee` | ソース | isCoffee関数（拡張子判定） |
| coffee | `bin/coffee` | バイナリ | CoffeeScriptの実行バイナリ |
