# 機能設計書 42-Cakeタスクランナー

## 概要

本ドキュメントは、CoffeeScriptのビルドツールであるCakeタスクランナーの設計を記載する。

### 本機能の処理概要

CakeはMake/Rake/Jake風のタスクランナーであり、CoffeeScriptで記述されたCakefileを読み込み、定義されたタスクを実行するビルドツールである。プロジェクトのビルド、テスト、デプロイなどの定型作業を自動化するために使用される。

**業務上の目的・背景**：ソフトウェア開発において、ビルド、テスト、デプロイなどの繰り返し行われる作業を自動化することは生産性向上に不可欠である。CakeはCoffeeScriptプロジェクト専用のタスクランナーとして、CoffeeScript構文でタスクを記述でき、Node.jsエコシステムとシームレスに統合できる。これにより、CoffeeScript開発者は馴染みのある構文でビルドスクリプトを記述できる。

**機能の利用シーン**：
- プロジェクトのソースコードをコンパイルするとき
- テストスイートを実行するとき
- 複数のビルドステップを順次実行するとき
- プロジェクト固有のタスク（ドキュメント生成、リリースなど）を実行するとき

**主要な処理内容**：
1. カレントディレクトリまたは親ディレクトリからCakefileを検索
2. CakefileをCoffeeScriptとしてコンパイル・実行
3. 定義されたタスクとオプションを収集
4. コマンドライン引数をパースしてオプションとタスク名を抽出
5. 指定されたタスクを順番に実行

**関連システム・外部連携**：
- Node.jsのファイルシステムモジュール（`fs`）
- Node.jsのパスモジュール（`path`）
- CoffeeScriptコンパイラ・ランタイム
- optparseモジュール（オプション解析）

**権限による制御**：特になし。Cakeコマンドを実行できるユーザーは全てのタスクを実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 23 | Annotated Source - Cake | 主機能 | ビルドツールcake.coffeeのソースコード解説表示 |
| 38 | Annotated Source - Cake | 主機能 | ビルドツールcake.coffeeのソースコード解説表示（v2） |

## 機能種別

タスク実行 / ビルド自動化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| タスク名 | string[] | No | 実行するタスクの名前（複数指定可） | Cakefileに定義されていること |
| オプション | object | No | task関数に渡されるオプション | Cakefileでoptionとして定義されていること |

### 入力データソース

- コマンドライン引数（`process.argv[2..]`）
- Cakefile（プロジェクトルートまたは親ディレクトリ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| タスク出力 | any | 各タスク実行時の標準出力 |
| ヘルプ | string | 引数なしの場合、定義されたタスク一覧を表示 |
| エラー | string | タスク実行失敗時のエラーメッセージ |

### 出力先

標準出力（stdout）/ 標準エラー出力（stderr）

## 処理フロー

### 処理シーケンス

```
1. Cakefileの検索
   └─ カレントディレクトリから親へ向かって探索

2. ディレクトリ移動
   └─ Cakefileが存在するディレクトリへchdir

3. Cakefileの読み込みと実行
   └─ CoffeeScript.runでCakefileを実行し、task/option定義を収集

4. オプションパーサーの初期化
   └─ switches配列からOptionParserを生成

5. 引数のパース
   └─ コマンドライン引数をパースしてオプションとタスク名を分離

6. タスク実行
   └─ 指定されたタスクを順番にinvoke
```

### フローチャート

```mermaid
flowchart TD
    A[cake コマンド実行] --> B[__originalDirnameを保存]
    B --> C[cakefileDirectory: Cakefile検索]
    C --> D{Cakefile存在?}
    D -->|No| E[親ディレクトリで再検索]
    E --> F{ルートに到達?}
    F -->|Yes| G[エラー: Cakefile not found]
    F -->|No| C
    D -->|Yes| H[そのディレクトリへchdir]
    H --> I[CoffeeScript.run: Cakefile実行]
    I --> J[OptionParser初期化]
    J --> K{引数あり?}
    K -->|No| L[printTasks: タスク一覧表示]
    K -->|Yes| M[oparse.parse: 引数パース]
    M --> N{パース成功?}
    N -->|No| O[fatalError表示]
    N -->|Yes| P[各タスクをinvoke]
    P --> Q[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Cakefile検索 | カレントディレクトリから親に向かって再帰的にCakefileを検索 | cakeコマンド実行時 |
| BR-02 | タスク一覧表示 | 引数なしでcakeを実行した場合、定義されたタスク一覧を表示 | 引数が空の場合 |
| BR-03 | 非同期実行 | Node.jsの非同期性により、タスクは予想と異なる順序で実行される可能性がある | 複数タスク指定時 |
| BR-04 | オリジナルディレクトリ保持 | サブディレクトリからCakeを実行した場合でも、元のディレクトリを__originalDirnameで参照可能 | 常時 |

### 計算ロジック

タスク一覧表示のフォーマット：
```coffeescript
for name, task of tasks
  spaces = 20 - name.length
  spaces = if spaces > 0 then Array(spaces + 1).join(' ') else ''
  desc   = if task.description then "# #{task.description}" else ''
  console.log "cake #{name}#{spaces} #{desc}"
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Cakefile not found | 検索エラー | カレントおよび親ディレクトリにCakefileが存在しない | Cakefileを作成する |
| No such task | タスクエラー | 指定されたタスク名がCakefileに定義されていない | タスク名を確認、または`cake`でタスク一覧を表示 |
| OptionParserError | オプションエラー | 不正なオプションが指定された | `cake`でヘルプを表示 |

### リトライ仕様

タスク実行時にエラーが発生した場合、自動リトライは行わない。ユーザーが再度cakeコマンドを実行する必要がある。

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

該当なし（データベーストランザクションは使用しない）

## パフォーマンス要件

- Cakefileの読み込みとコンパイルは起動時に1回のみ実行
- タスク実行時間はタスクの内容に依存
- Cakefile検索は親ディレクトリへの再帰的探索のため、深いディレクトリ構造では遅延の可能性あり

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

- CakefileはCoffeeScriptコードとして実行されるため、信頼できるCakefileのみを使用すること
- Cakefileはグローバルスコープ（`global`）を拡張するため、他のモジュールに影響を与える可能性がある
- タスク内で外部コマンドを実行する場合は、入力のサニタイズに注意

## 備考

- Cakefileはプロジェクトルートに配置することが推奨される
- CakeはCoffeeScript固有のビルドツールであり、汎用的なタスクランナー（Gulp、Gruntなど）の代替としては機能が限定的

---

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

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

### 推奨読解順序

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

Cakeが管理する主要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cake.coffee | `src/cake.coffee` | グローバル変数（tasks, options, switches, oparse）の定義 |

**読解のコツ**: CoffeeScriptでは `=` による代入で変数が定義される。ファイル先頭で定義された変数は、モジュールスコープで共有される。

**主要データ構造**:
- **tasks** (20行目): タスク名をキー、{name, description, action}を値とするオブジェクト
- **options** (21行目): パース済みオプションを格納するオブジェクト
- **switches** (22行目): [letter, flag, description]形式のオプション定義配列
- **oparse** (23行目): OptionParserインスタンス

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

`exports.run`がcakeコマンドのエントリーポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cake.coffee | `src/cake.coffee` | exports.run関数（49-60行目） |

**主要処理フロー**:
1. **50行目**: `__originalDirname`にカレントディレクトリのrealPathを保存
2. **51行目**: `cakefileDirectory`でCakefileのあるディレクトリを検索し、そこへ`process.chdir`
3. **52行目**: コマンドライン引数を`args`に格納
4. **53行目**: `CoffeeScript.run`でCakefileを読み込み・実行
5. **54行目**: `OptionParser`を`switches`で初期化
6. **55行目**: 引数がなければ`printTasks()`でタスク一覧表示
7. **57行目**: `oparse.parse(args)`で引数をパース
8. **60行目**: 各引数（タスク名）に対して`invoke`を実行

#### Step 3: グローバル関数を理解する

Cakefileで使用できるtask, option, invoke関数の実装。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cake.coffee | `src/cake.coffee` | helpers.extendによるグローバル拡張（26-43行目） |

**主要処理フロー**:
- **30-32行目**: `task`関数 - タスクを`tasks`オブジェクトに登録
- **37-38行目**: `option`関数 - オプションを`switches`配列に追加
- **41-43行目**: `invoke`関数 - 指定タスクを実行（存在しなければエラー）

#### Step 4: Cakefile検索ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cake.coffee | `src/cake.coffee` | cakefileDirectory関数（84-88行目） |

**主要処理フロー**:
- **85行目**: `fs.existsSync`でCakefileの存在確認
- **86行目**: 存在しなければ`path.normalize`で親ディレクトリを取得
- **87行目**: ルートに到達していなければ再帰呼び出し
- **88行目**: ルートに到達したらエラーをthrow

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

```
cake (bin/cake)
    │
    └─ exports.run (cake.coffee:49)
           │
           ├─ cakefileDirectory (cake.coffee:84)
           │      └─ fs.existsSync, path.normalize (再帰)
           │
           ├─ CoffeeScript.run (index.coffee:35)
           │      └─ Cakefile実行 → task/option/invoke がグローバルに登録
           │
           ├─ new optparse.OptionParser (optparse.coffee)
           │
           ├─ printTasks (cake.coffee:63) [引数なしの場合]
           │
           ├─ oparse.parse (optparse.coffee)
           │
           └─ invoke (cake.coffee:41) [各タスク]
                  └─ tasks[name].action(options)
```

### データフロー図

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

コマンドライン引数 ─▶ exports.run ─────────────────────────▶ タスク実行結果
       │                 │
       │                 ├─▶ cakefileDirectory
       │                 │        │
       │                 │        ▼
       │                 │   Cakefile検索
       │                 │
       │                 ├─▶ CoffeeScript.run
       │                 │        │
       │                 │        ▼
       │                 │   tasks, options, switches収集
       │                 │
       │                 ├─▶ OptionParser.parse
       │                 │        │
       │                 │        ▼
       │                 │   options + arguments
       │                 │
       │                 └─▶ invoke
       │                          │
       │                          ▼
       └───────────────────▶ task.action(options) ───▶ stdout/stderr
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cake.coffee | `src/cake.coffee` | ソース | Cakeタスクランナーのメイン実装 |
| optparse.coffee | `src/optparse.coffee` | ソース | コマンドラインオプションのパーサー |
| index.coffee | `src/index.coffee` | ソース | CoffeeScript.run関数の実装 |
| helpers.coffee | `src/helpers.coffee` | ソース | extend関数などのユーティリティ |
| Cakefile | プロジェクトルート | 設定 | タスク定義ファイル（ユーザー作成） |
