# 機能設計書 30 --output オプション

## 概要

本ドキュメントは、CoffeeScript CLIツールの `--output` (`-o`) オプションの機能設計を記述する。このオプションは、コンパイル結果のJavaScriptファイルの出力先パスまたはファイル名を指定する機能を提供する。

### 本機能の処理概要

`--output` オプションは、CoffeeScriptからJavaScriptへのコンパイル結果の出力先を指定する機能を提供する。デフォルトでは、ソースファイルと同じディレクトリに`.js`ファイルが生成されるが、このオプションを使用することで、出力先ディレクトリを変更したり、出力ファイル名を直接指定したりできる。

**業務上の目的・背景**：プロジェクト構成として、ソースコードと生成されたファイルを分離したい場合が多い。例えば、`src/`ディレクトリにCoffeeScriptファイルを配置し、`dist/`や`lib/`ディレクトリにコンパイル結果を出力するような構成である。`--output` オプションにより、このような柔軟なプロジェクト構成が可能になる。

**機能の利用シーン**：
- ソースコードとビルド成果物を別ディレクトリに分離したい場合
- 特定のファイル名でJavaScriptを出力したい場合
- ビルドツールやタスクランナーと連携する場合
- 複数のソースを1つのファイルに出力したい場合（単一ソースの場合）

**主要な処理内容**：
1. --output引数の値を解析してパスかファイル名かを判定
2. ファイル名が含まれる場合はoutputFilename、ディレクトリのみの場合はoutputPathを設定
3. 出力先ディレクトリが存在しない場合は再帰的に作成
4. 指定されたパス/ファイル名でJavaScriptファイルを出力

**関連システム・外部連携**：ファイルシステム

**権限による制御**：出力先ディレクトリへの書き込み権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIオプションのため関連画面なし |

## 機能種別

出力制御 / ファイル管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --output / -o | string | No | 出力先パスまたはファイル名 | 有効なファイルシステムパス |
| source | string | Yes | コンパイル対象のCoffeeScriptファイル | ファイルが存在すること |

### 入力データソース

- コマンドライン引数（`--output [PATH]`）
- ファイルシステム（CoffeeScriptソースファイル）

### パス指定の形式

| 指定形式 | 例 | 動作 |
|---------|-----|------|
| ディレクトリ | `--output dist/` | dist/配下にソースファイル名.jsを出力 |
| ファイル名 | `--output bundle.js` | 指定されたファイル名で出力（単一ソースのみ） |
| パス+ファイル名 | `--output dist/app.js` | 指定されたパスとファイル名で出力 |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| JavaScript file | file | コンパイルされたJavaScriptファイル |
| Source Map file | file | ソースマップファイル（--map併用時） |

### 出力先

- 指定されたパス/ファイル名
- 出力先ディレクトリが存在しない場合は自動作成

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数をパース
   └─ --output オプションの値を取得

2. 出力先の解析
   ├─ 値にファイル拡張子が含まれるか判定
   ├─ .や..で終わらないか判定
   └─ パス区切り文字で終わらないか判定

3. outputFilename と outputPath の設定
   ├─ ファイル名指定: outputFilename = basename, outputPath = dirname
   └─ ディレクトリ指定: outputFilename = null, outputPath = 指定パス

4. コンパイル処理

5. 出力先ディレクトリの作成
   └─ mkdirp で再帰的にディレクトリ作成

6. ファイル出力
   └─ writeJs関数でJavaScriptファイルを書き込み
```

### フローチャート

```mermaid
flowchart TD
    A[開始: coffee -o dist/ source.coffee] --> B[引数パース]
    B --> C{--output 指定?}
    C -->|No| D[ソースと同じディレクトリに出力]
    C -->|Yes| E[出力パス解析]
    E --> F{ファイル名含む?}
    F -->|Yes| G[outputFilename = basename]
    F -->|No| H[outputFilename = null]
    G --> I[outputPath = dirname]
    H --> J[outputPath = 指定パス]
    I --> K[path.resolve で絶対パス化]
    J --> K
    K --> L[CoffeeScript.compile]
    L --> M{出力先ディレクトリ存在?}
    M -->|Yes| N[writeJs: ファイル出力]
    M -->|No| O[mkdirp: ディレクトリ作成]
    O --> N
    N --> P[終了]
    D --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | 暗黙のcompile | --output を指定すると暗黙的に --compile が有効になる | --output指定時 |
| BR-30-02 | ファイル名判定 | パスに`.`が含まれ、`.`や`..`で終わらず、パス区切りで終わらない場合はファイル名指定と判定 | パス解析時 |
| BR-30-03 | 単一ソース制限 | outputFilename指定は単一ソースファイルの場合のみ有効 | 複数ソースの場合は無視 |
| BR-30-04 | ディレクトリ自動作成 | 出力先ディレクトリが存在しない場合、再帰的に作成する | ファイル出力時 |
| BR-30-05 | 絶対パス変換 | 指定されたパスはpath.resolveで絶対パスに変換される | パス設定時 |

### 計算ロジック

出力パス計算:
- ファイル名指定時: `path.join(opts.outputPath, opts.outputFilename)`
- ディレクトリ指定時: `path.join(opts.outputPath, path.relative(base, srcDir), basename + '.js')`

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ENOENT | ファイル不存在 | ソースファイルが存在しない | エラーメッセージを表示して終了コード1 |
| EACCES | 権限エラー | 出力先ディレクトリへの書き込み権限がない | 権限を確認して再実行 |
| Write Error | 書き込みエラー | ファイルの書き込みに失敗 | ディスク容量、権限を確認 |

### リトライ仕様

本機能はリトライ処理を行わない。

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

本機能はトランザクション処理を使用しない。

## パフォーマンス要件

特になし

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

- 出力先パスの適切な検証（パストラバーサル攻撃への対策）
- 既存ファイルの上書きに注意

## 備考

- `--watch` オプションと併用すると、ファイル変更時に自動で指定先に出力される
- `--map` オプションと併用すると、ソースマップも同じディレクトリに出力される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | command.coffee | `src/command.coffee` | SWITCHESの `--output` 定義（48行目）を確認 |

**読解のコツ**: `['-o', '--output [PATH]', 'set the output path or path/filename for compiled JavaScript']` の形式で定義されている。`[PATH]`は引数を取ることを示す。

#### Step 2: 出力パス解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | command.coffee | `src/command.coffee` | run関数内の--output処理（108-119行目） |

**主要処理フロー**:
- **108行目**: `if opts.output`
- **109行目**: `outputBasename = path.basename opts.output`
- **110-112行目**: ファイル名判定条件
- **113-115行目**: ファイル名指定の場合の処理
- **117-119行目**: ディレクトリ指定の場合の処理

#### Step 3: 暗黙のcompile有効化を理解する

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

**主要処理フロー**:
- **447行目**: `o.compile or= !!o.output` で暗黙のcompile有効化

#### Step 4: 出力パス計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | command.coffee | `src/command.coffee` | outputPath関数（376-385行目） |

**主要処理フロー**:
- **376行目**: `outputPath = (source, base, extension=".js") ->`
- **379-384行目**: opts.outputPathの有無に応じたディレクトリ計算
- **385行目**: `path.join dir, basename + extension`

#### Step 5: ディレクトリ作成と出力を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | command.coffee | `src/command.coffee` | mkdirp関数（388-399行目） |
| 5-2 | command.coffee | `src/command.coffee` | writeJs関数（407-426行目） |

**主要処理フロー**:
- **388行目**: `mkdirp = (dir, fn) ->`
- **391-399行目**: 再帰的ディレクトリ作成
- **425行目**: `fs.exists jsDir, (itExists) ->` でディレクトリ存在チェック
- **426行目**: `if itExists then compile() else mkdirp jsDir, compile`

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

```
exports.run (72行目)
    │
    ├─ parseOptions (74行目)
    │      └─ o.compile or= !!o.output (447行目)
    │
    ├─ [--output処理] (108-119行目)
    │      ├─ outputFilename設定
    │      └─ outputPath設定
    │
    └─ compileScript (202行目)
           │
           ├─ compileOptions (203行目)
           │      └─ jsPath計算 (514行目)
           │             └─ outputPath関数 (376行目)
           │
           └─ writeJs (237行目)
                  │
                  ├─ fs.exists (425行目)
                  │
                  ├─ mkdirp (426行目)
                  │      └─ 再帰的ディレクトリ作成
                  │
                  └─ fs.writeFile (414行目)
```

### データフロー図

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

--output 引数 ───▶ run関数 (108行目)
                        │
                        ├─ path.basename
                        │
                        └─ ファイル名/ディレクトリ判定
                              │
                              ▼
                    ┌─────────┴─────────┐
                    ▼                   ▼
            [ファイル名]          [ディレクトリ]
                    │                   │
                    ▼                   ▼
            outputFilename        outputFilename = null
            outputPath            outputPath
                    │                   │
                    └─────────┬─────────┘
                              ▼
                        outputPath関数 (376行目)
                              │
                              ▼
                        最終出力パス計算
                              │
                              ▼
                        mkdirp (必要な場合)
                              │
                              ▼
                        fs.writeFile ───▶ .jsファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| command.coffee | `src/command.coffee` | ソース | CLIメイン処理、--output解析、outputPath関数、writeJs関数、mkdirp関数 |
| helpers.coffee | `src/helpers.coffee` | ソース | baseFileName関数、ends関数 |
| optparse.coffee | `src/optparse.coffee` | ソース | コマンドライン引数パーサー |
