# 画面設計書 19-why

## 概要

本ドキュメントは、Bun CLIの `bun why` コマンド（`bun pm why`のエイリアス）の画面設計書です。指定したパッケージがなぜインストールされているのか、依存関係ツリーを表示する機能の仕様を定義します。

### 本画面の処理概要

`bun why` コマンドは、指定したパッケージがどのパッケージから依存されているかを逆引きして、依存関係の理由をツリー形式で表示します。

**業務上の目的・背景**：大規模プロジェクトでは多数の依存関係が存在し、特定のパッケージがなぜインストールされているか分かりにくくなります。セキュリティ脆弱性のあるパッケージの削除方法を調査したり、不要なパッケージを特定する際に、依存元を確認するために使用します。

**画面へのアクセス方法**：`bun why <package-name>` の形式で実行します。または `bun pm why <package-name>` でも同じ機能にアクセスできます。

**主要な操作・処理内容**：
1. 位置引数からパッケージ名（パターン）を取得
2. ロックファイルを読み込み
3. 指定パッケージにマッチするすべてのバージョンを特定
4. 各バージョンについて、依存元パッケージを逆引き
5. ツリー形式で依存関係を表示

**画面遷移**：単独で実行されるCLIコマンドです。依存関係ツリー表示後、ターミナルに戻ります。

**権限による表示制御**：特別な権限制御はありません。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | bun why | 主機能 | パッケージ依存理由の表示 |

## 画面種別

ツリー表示

## URL/ルーティング

```
bun why <package-pattern> [options]
bun pm why <package-pattern> [options]
```

## 入出力項目

### 入力項目（コマンドライン引数）

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| package-pattern | string | はい | 検索するパッケージ名（glob/semverパターン対応） |
| --top | boolean | いいえ | トップレベルの依存関係のみ表示 |
| --depth | number | いいえ | ツリーの最大深度 |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| パッケージ名@バージョン | string | 検索対象パッケージ |
| 依存元パッケージ | string | 依存しているパッケージ名と要求バージョン |
| 依存タイプ | string | dev/peer/optional等の依存タイプ |

## 表示項目

### 出力形式

```
{package-name}@{version}
  └─ {dependent-package}@{version} (requires {spec})
     └─ {parent-package}@{version} (requires {spec})
        └─ ... (deeper dependencies hidden)
```

### 依存タイプ表示

```
  └─ dev {package}@{version} (requires {spec})
  └─ peer {package}@{version} (requires {spec})
  └─ optional {package}@{version} (requires {spec})
  └─ optional peer {package}@{version} (requires {spec})
```

### ワークスペース表示

```
  └─ {workspace-package}@workspace
```

## イベント仕様

### 1-依存関係逆引き

コマンド実行時の処理フロー：

1. `WhyCommand.exec()` がエントリーポイントとして呼び出される
2. コマンドライン引数を解析し、PackageManagerを初期化
3. 位置引数からパッケージパターンを取得
4. `execWithManager()` でロックファイルを読み込み
5. `GlobPattern.init()` でパターンをパース
6. 全パッケージをスキャンし、パターンにマッチするものを抽出
7. 各パッケージについて依存元を逆引き（`all_dependents` ハッシュマップ構築）
8. `printDependencyTree()` で再帰的にツリーを出力

## データベース更新仕様

### 操作別データベース影響一覧

本コマンドは読み取り専用です。

| 操作（イベント） | 対象 | 操作種別 | 概要 |
|----------------|------|---------|------|
| 依存関係逆引き | ロックファイル | SELECT | 依存関係情報の読み取りのみ |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| WHY_001 | 情報 | bun why v{version} | コマンド開始時（--top使用時） |
| WHY_002 | エラー | No packages matching '{pattern}' found in lockfile | マッチするパッケージなし |
| WHY_003 | 情報 | No dependents found | 依存元がない場合 |
| WHY_004 | 情報 | (deeper dependencies hidden) | 深度制限到達時 |
| WHY_005 | 情報 | *circular | 循環依存検出時 |

## 例外処理

| 例外条件 | 処理内容 | エラーコード |
|---------|---------|------------|
| パッケージパターン未指定 | 使用方法を表示して終了 | 1 |
| ロックファイル未検出 | エラーメッセージを表示して終了 | 1 |
| マッチするパッケージなし | エラーメッセージを表示して終了 | 1 |

## 備考

- glob パターン（@scope/*、*-lodash等）に対応
- semver パターン（react@^17.0.0等）に対応
- --topオプションで直接の依存元のみ表示
- --depthオプションでツリー深度を制限
- 循環依存は検出して*circularと表示
- 依存タイプ（dev/peer/optional）はカラー表示
- ワークスペースパッケージは@workspaceとして表示
- 依存元が複数ある場合は優先度順（specificity、依存タイプ優先度）でソート

---

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

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

### 推奨読解順序

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

whyコマンドで使用される主要なデータ構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | why_command.zig | `src/cli/why_command.zig` | VersionInfo構造体（L8-11）：パッケージバージョン情報 |
| 1-2 | why_command.zig | `src/cli/why_command.zig` | DependentInfo構造体（L13-20）：依存元パッケージ情報 |
| 1-3 | why_command.zig | `src/cli/why_command.zig` | DependencyType enum（L22-28）：依存タイプの分類 |
| 1-4 | why_command.zig | `src/cli/why_command.zig` | GlobPattern構造体（L72-188）：パッケージ名/バージョンのマッチング |
| 1-5 | why_command.zig | `src/cli/why_command.zig` | TreeContext構造体（L407-427）：ツリー出力のコンテキスト |

**読解のコツ**: 依存関係の逆引きハッシュマップ（all_dependents）を構築し、再帰的にツリーを辿る構造です。

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

処理の起点となるexec関数を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | why_command.zig | `src/cli/why_command.zig` | exec関数（L213-231）：PackageManager初期化とexecWithManager呼び出し |
| 2-2 | why_command.zig | `src/cli/why_command.zig` | execFromPm関数（L233-239）：pm why経由のエントリー |

**主要処理フロー**:
1. **L214**: コマンドライン引数を.whyモードで解析
2. **L215**: PackageManagerを初期化
3. **L217-226**: 位置引数の取得と検証
4. **L230**: execWithManagerを呼び出し

#### Step 3: メイン処理を理解する

execWithManager関数でメイン処理を読み解きます。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | why_command.zig | `src/cli/why_command.zig` | execWithManager関数（L242-375）：依存関係逆引きとツリー出力 |

**主要処理フロー**:
- **L243-244**: ロックファイルを読み込み
- **L246-252**: --topと--depthオプションの処理
- **L254-258**: ロックファイルからパッケージ情報を取得
- **L272**: all_dependentsハッシュマップを初期化
- **L274**: GlobPatternを初期化
- **L276-335**: 全パッケージをスキャンして依存元を収集
- **L337-340**: マッチするパッケージがなければエラー
- **L342-374**: マッチしたパッケージごとにツリーを出力

#### Step 4: ツリー出力処理を理解する

再帰的なツリー出力処理を読み解きます。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | why_command.zig | `src/cli/why_command.zig` | printPackageWithType関数（L377-405）：依存タイプ付きパッケージ出力 |
| 4-2 | why_command.zig | `src/cli/why_command.zig` | printDependencyTree関数（L429-477）：再帰的ツリー出力 |

**主要処理フロー**:
- **L377-405**: 依存タイプ（dev/peer/optional）をカラー出力
- **L429-477**: 循環依存チェック、深度制限、再帰呼び出し

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

```
WhyCommand.exec()
    │
    ├─ PackageManager.CommandLineArguments.parse(.why)
    │
    ├─ PackageManager.init()
    │
    └─ execWithManager()
           │
           ├─ lockfile.loadFromCwd()
           │      └─ ロックファイルからパッケージ情報取得
           │
           ├─ GlobPattern.init()
           │      └─ パッケージパターンをパース
           │
           ├─ [全パッケージスキャン]
           │      │
           │      ├─ GlobPattern.matchesName()
           │      │      └─ パッケージ名がパターンにマッチするか
           │      │
           │      ├─ GlobPattern.matchesVersion()
           │      │      └─ バージョンがパターンにマッチするか
           │      │
           │      └─ all_dependents.put()
           │             └─ 依存元情報をハッシュマップに登録
           │
           ├─ compareDependents() でソート
           │
           └─ [マッチしたパッケージごと]
                  │
                  ├─ printPackageWithType()
                  │      └─ 依存タイプ付きで出力
                  │
                  └─ printDependencyTree() [再帰]
                         │
                         ├─ 循環依存チェック (path_tracker)
                         │
                         ├─ 深度制限チェック (max_depth)
                         │
                         └─ 子ノードを再帰出力
```

### データフロー図

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

package-pattern ──────────▶ GlobPattern.init() ─────────────────┐
                                  │                             │
                                  ▼                             │
bun.lockb/bun.lock ──────▶ lockfile.loadFromCwd() ─────────────▶│
                                  │                             │
                                  ▼                             │
                         ┌─────────────────────┐                │
                         │ 全パッケージスキャン  │                │
                         └─────────────────────┘                │
                                  │                             │
                     ┌────────────┴────────────┐                │
                     ▼                          ▼               │
               パターンマッチ              依存元収集             │
               matchesName()           all_dependents.put()     │
               matchesVersion()                                 │
                     │                          │               │
                     └────────────┬─────────────┘               │
                                  ▼                             │
                         target_versions                        │
                              │                                 │
                              ▼                                 │
                    ┌─────────────────────┐                     │
                    │ ツリー出力           │                     │
                    │ printDependencyTree │                     │
                    └─────────────────────┘                     │
                              │                                 │
                              ▼                                 ▼
                    ┌───────────────────────────────────────────┐
                    │ react@18.2.0                              │
                    │   └─ my-app@1.0.0 (requires ^18.0.0)      │
                    │      └─ @myorg/utils@workspace            │
                    └───────────────────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| why_command.zig | `src/cli/why_command.zig` | ソース | whyコマンドのメイン実装 |
| pm_why_command.zig | `src/cli/pm_why_command.zig` | ソース | pm why経由のラッパー |
| package_manager_command.zig | `src/cli/package_manager_command.zig` | ソース | pm whyからの呼び出し元 |
| lockfile.zig | `src/install/lockfile.zig` | ソース | ロックファイル読み込み |
| semver.zig | `src/semver.zig` | ソース | バージョンパターンマッチング |
