# 画面設計書 13-unlink

## 概要

本ドキュメントは、Bun CLIの `bun unlink` コマンドの画面設計書です。`bun link` で登録されたローカルnpmパッケージのグローバル登録を解除する機能の仕様を定義します。

### 本画面の処理概要

`bun unlink` コマンドは、以前 `bun link` で作成されたグローバルシンボリックリンクを削除し、パッケージの登録を解除する機能を提供します。

**業務上の目的・背景**：開発中のパッケージをグローバル登録したが、テスト完了後や公開後に不要になった場合、またはパッケージ名を変更したい場合などにグローバル登録を解除する必要があります。本コマンドにより、グローバル環境をクリーンに保ち、不要なリンクによる混乱を防ぎます。

**画面へのアクセス方法**：リンク解除したいパッケージのディレクトリで `bun unlink` を実行します。

**主要な操作・処理内容**：
1. 現在のディレクトリのpackage.jsonからパッケージ名を取得
2. グローバルディレクトリにリンクが存在するか確認
3. バイナリリンクがあれば削除
4. グローバルnode_modulesからシンボリックリンクを削除

**画面遷移**：単独で実行されるCLIコマンドです。リンク解除完了後、成功メッセージがターミナルに表示されます。

**権限による表示制御**：グローバルディレクトリへの書き込み権限が必要です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | bun unlink | 主機能 | リンクしたパッケージの登録解除 |

## 画面種別

コマンド実行（成功/エラーメッセージ出力）

## URL/ルーティング

```
bun unlink
```

## 入出力項目

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

| 項目名 | 型 | 必須 | 説明 |
|--------|-----|------|------|
| package-name | string | いいえ | リンク解除するパッケージ名（未実装） |
| --global-dir | string | いいえ | グローバルディレクトリのパス |
| --silent | boolean | いいえ | 出力を抑制する |

### 出力項目

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 成功メッセージ | string | リンク解除完了時のメッセージ |
| 情報メッセージ | string | リンクが存在しない場合のメッセージ |
| エラーメッセージ | string | 処理失敗時の詳細情報 |

## 表示項目

### 成功時出力

```
success: unlinked package "{package-name}"
```

### リンクなし時出力

```
success: package "{package-name}" is not globally linked, so there's nothing to do.
```

## イベント仕様

### 1-グローバル登録解除（引数なし）

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

1. `UnlinkCommand.exec()` がエントリーポイントとして呼び出される
2. コマンドライン引数を解析し、PackageManagerを初期化
3. package.jsonからパッケージ名を取得・検証
4. グローバルディレクトリでリンクの存在を `lstat` で確認
5. リンクが存在しない場合は「nothing to do」メッセージを出力して終了
6. バイナリリンクがあれば `Bin.Linker.unlink()` で削除
7. グローバルnode_modulesから `deleteTree()` でリンクを削除
8. 成功メッセージを出力

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

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

本コマンドはファイルシステム操作のみを行います。

| 操作（イベント） | 対象 | 操作種別 | 概要 |
|----------------|------|---------|------|
| バイナリリンク解除 | グローバルbinディレクトリ | DELETE | 実行可能ファイルへのリンク削除 |
| パッケージリンク解除 | グローバルnode_modules | DELETE | パッケージディレクトリへのリンク削除 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| UNLINK_001 | 情報 | bun unlink v{version} | コマンド開始時 |
| UNLINK_002 | 成功 | success: unlinked package "{name}" | リンク解除成功時 |
| UNLINK_003 | 成功 | success: package "{name}" is not globally linked, so there's nothing to do. | リンクが存在しない場合 |
| UNLINK_004 | エラー | package.json missing "name" | nameフィールドがない場合 |
| UNLINK_005 | エラー | invalid package.json name "{name}" | 名前が不正な場合 |
| UNLINK_006 | エラー | failed to read "{path}" for unlinking | package.json読み込み失敗 |
| UNLINK_007 | エラー | failed to unlink package in global dir | リンク削除失敗 |
| UNLINK_008 | エラー | bun unlink {packageName} not implemented yet | 引数ありの場合 |

## 例外処理

| 例外条件 | 処理内容 | エラーコード |
|---------|---------|------------|
| package.json未検出 | 自動作成を試行、失敗時はエラー終了 | 1 |
| nameフィールド不在 | エラーメッセージを表示して終了 | 1 |
| 無効なパッケージ名 | エラーメッセージを表示して終了 | 1 |
| リンクが存在しない | 「nothing to do」メッセージを表示して正常終了 | 0 |
| リンク削除失敗 | エラーメッセージを表示して終了 | 1 |

## 備考

- 引数ありでの `bun unlink {packageName}` は未実装（L130-131）
- リンクの存在確認は `lstat` を使用してシンボリックリンクかどうかを判定
- シンボリックリンクでない通常のディレクトリの場合は「nothing to do」として処理
- バイナリのリンク解除は `Bin.Linker.unlink()` で実行

---

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

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

### 推奨読解順序

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

unlinkコマンドではlinkコマンドと同様の構造体を使用します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | unlink_command.zig | `src/cli/unlink_command.zig` | Lockfile.Package構造体の使用（L29）：パッケージ情報の格納 |

**読解のコツ**: unlinkコマンドはlinkコマンドの逆操作であり、構造は類似している。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | unlink_command.zig | `src/cli/unlink_command.zig` | exec関数（L2-4）：unlink関数への委譲 |
| 2-2 | unlink_command.zig | `src/cli/unlink_command.zig` | unlink関数（L7-133）：メイン処理ロジック |

**主要処理フロー**:
1. **L8**: コマンドライン引数を.unlinkモードで解析
2. **L9-16**: PackageManagerを初期化（package.jsonがなければ作成を試行）
3. **L24**: 位置引数の数で処理を分岐

#### Step 3: パッケージ名取得と検証を理解する

package.jsonからパッケージ名を取得し、検証する処理を読み解きます。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | unlink_command.zig | `src/cli/unlink_command.zig` | パッケージ名取得（L32-56）：package.json解析と名前検証 |

**主要処理フロー**:
- **L33-36**: package.jsonをファイルから読み込み
- **L37-40**: Lockfileを初期化してパッケージを解析
- **L41-55**: パッケージ名の存在と妥当性をチェック

#### Step 4: リンク存在確認と削除処理を理解する

グローバルリンクの存在確認と削除処理を読み解きます。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | unlink_command.zig | `src/cli/unlink_command.zig` | リンク存在確認（L58-69）：lstatによるシンボリックリンク判定 |
| 4-2 | unlink_command.zig | `src/cli/unlink_command.zig` | バイナリリンク解除（L90-118）：Bin.Linker.unlink呼び出し |
| 4-3 | unlink_command.zig | `src/cli/unlink_command.zig` | ディレクトリ削除（L121-125）：deleteTree呼び出し |

**主要処理フロー**:
- **L58**: joinAbsStringZでグローバルパスを構築
- **L59-69**: lstatでリンクの存在とタイプを確認
- **L90-118**: バイナリがあればBin.Linker.unlinkで削除
- **L121-125**: node_modules.deleteTreeでパッケージリンクを削除

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

```
UnlinkCommand.exec()
    │
    └─ unlink()
           │
           ├─ CommandLineArguments.parse(.unlink)
           │
           ├─ PackageManager.init()
           │      └─ attemptToCreatePackageJSON() [package.jsonがない場合]
           │
           ├─ [引数なし: グローバル登録解除]
           │      │
           │      ├─ File.toSource() - package.json読み込み
           │      │
           │      ├─ Lockfile.Package.parse() - パッケージ情報解析
           │      │
           │      ├─ Syscall.lstat() - リンク存在確認
           │      │
           │      ├─ Options.openGlobalDir() - グローバルディレクトリ取得
           │      │
           │      ├─ manager.setupGlobalDir() - ディレクトリセットアップ
           │      │
           │      ├─ Bin.Linker.unlink() - バイナリリンク解除
           │      │
           │      └─ node_modules.deleteTree() - パッケージリンク削除
           │
           └─ [引数あり: 未実装]
                  └─ エラーメッセージ出力
```

### データフロー図

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

package.json ───────────▶ Package.parse() ─────────────────┐
                                                           │
                         ┌────────────────────────────────▶│
                         │                                 │
グローバルディレクトリ ──▶ lstat() ──────────────────────▶│
                          │                               │
                          │  リンク存在チェック             │
                          │                               ▼
                          │                    [リンク存在?]
                          │                        │
                        ┌─┴─┐                     │
                        │NO │─────────────────────┼──▶ "nothing to do"
                        └───┘                     │
                                                  │YES
                                                  ▼
                                        Bin.Linker.unlink()
                                                  │
                                                  ▼
                                        deleteTree()
                                                  │
                                                  ▼
                                        [成功メッセージ]
                                        "unlinked package..."
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| unlink_command.zig | `src/cli/unlink_command.zig` | ソース | unlinkコマンドのメイン実装 |
| link_command.zig | `src/cli/link_command.zig` | ソース | 参考：linkコマンドの実装（対となる機能） |
| install.zig | `src/install/install.zig` | ソース | PackageManager、Bin.Linker定義 |
| lockfile.zig | `src/install/lockfile.zig` | ソース | Lockfile.Package定義 |
| sys.zig | `src/sys.zig` | ソース | lstat、ファイルシステム操作ユーティリティ |
