# 機能設計書 54-ESLintプラグイン

## 概要

本ドキュメントは、Payload CMS専用のESLintプラグイン（@payloadcms/eslint-plugin）の機能設計を定義する。このプラグインは、Payload開発における固有のベストプラクティスを強制するカスタムESLintルールを提供する。

### 本機能の処理概要

ESLintプラグインは、Payloadプロジェクト固有のコード品質ルールを提供し、一般的なESLintルールでは検出できない問題パターンを検出・修正する。

**業務上の目的・背景**：Payload CMSの開発において、モノレポ構造特有のインポート規約、ロガー使用方法、E2Eテストの安定性など、一般的なESLintルールではカバーできない固有の課題が存在する。このプラグインは、これらの課題を自動的に検出し、開発者がベストプラクティスに従ったコードを記述できるようにする。特にテストの不安定性（flaky tests）を防止するルールは、CI/CDの信頼性向上に寄与する。

**機能の利用シーン**：
- Payloadモノレポでの開発時にコード品質チェック
- CI/CDパイプラインでのE2Eテスト品質検証
- ロガー使用方法の統一
- モノレポ内パッケージ間のインポート規約遵守

**主要な処理内容**：
1. JSXインポート禁止（no-jsx-import-statements）: .jsx拡張子のインポートを禁止し.jsに自動修正
2. 相対モノレポインポート禁止（no-relative-monorepo-imports）: packages/*/srcへの相対インポート禁止
3. exportsディレクトリインポート禁止（no-imports-from-exports-dir）: exports/への相対インポート禁止
4. 自己インポート禁止（no-imports-from-self）: 自パッケージからのインポート禁止
5. Payloadロガー使用方法検証（proper-payload-logger-usage）: payload.logger.errorの正しい使用法を強制
6. Playwrightテスト安定性ルール（no-flaky-assertions、no-non-retryable-assertions）
7. wait関数使用禁止（no-wait-function）: テストでのwait関数使用禁止
8. コードブロック言語検証（valid-code-block-languages）: ドキュメントのコードブロック言語検証

**関連システム・外部連携**：
- ESLint 9.x
- @payloadcms/eslint-config（統合元）
- Playwright E2Eテスト

**権限による制御**：開発ツールのため、権限制御は存在しない。

## 関連画面

本機能は開発支援ツールであり、Payload CMSの管理画面とは直接関連しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 管理画面との直接の関連なし |

## 機能種別

開発支援ツール / コード品質管理 / 静的解析プラグイン

## 入力仕様

### 入力パラメータ

各ルールの設定は標準的なESLintルール形式で指定。

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| payload/no-jsx-import-statements | 'off' \| 'warn' \| 'error' | No | JSXインポート禁止 | - |
| payload/no-relative-monorepo-imports | 'off' \| 'warn' \| 'error' | No | 相対モノレポインポート禁止 | - |
| payload/no-imports-from-exports-dir | 'off' \| 'warn' \| 'error' | No | exportsディレクトリインポート禁止 | - |
| payload/no-imports-from-self | 'off' \| 'warn' \| 'error' | No | 自己インポート禁止 | - |
| payload/proper-payload-logger-usage | 'off' \| 'warn' \| 'error' | No | ロガー使用検証 | - |
| payload/no-flaky-assertions | 'off' \| 'warn' \| 'error' | No | 不安定アサーション禁止 | - |
| payload/no-non-retryable-assertions | 'off' \| 'warn' \| 'error' | No | リトライ不可アサーション警告 | - |
| payload/no-wait-function | 'off' \| 'warn' \| 'error' | No | wait関数使用禁止 | - |
| payload/valid-code-block-languages | 'off' \| 'warn' \| 'error' | No | コードブロック言語検証 | - |

### 入力データソース

- ソースコード: .ts、.tsx、.js、.mjs、.cjsファイル
- ESLint設定: eslint.config.mjsまたは.eslintrc.js

## 出力仕様

### 出力データ

#### ESLint診断結果

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ruleId | string | payload/ルール名 |
| severity | number | 重要度（1=warn、2=error） |
| message | string | 診断メッセージ |
| fix | object | 自動修正情報（対応ルールのみ） |

### 出力先

- コンソール: ESLint実行結果
- IDE: エディタ上のリアルタイムフィードバック

## 処理フロー

### 処理シーケンス

```
1. ESLint実行
   └─ プラグインルールの読み込み
2. ルール別処理
   ├─ ImportDeclaration処理（インポート関連ルール）
   ├─ CallExpression処理（関数呼び出し関連ルール）
   └─ AST走査
3. 問題検出
   └─ context.report()で報告
4. 自動修正（対応ルールのみ）
   └─ fixer.removeRange()等で修正
```

### フローチャート

```mermaid
flowchart TD
    A[ESLint実行] --> B[プラグインルール適用]
    B --> C{ルールタイプ}
    C -->|インポート| D[ImportDeclaration検査]
    C -->|関数呼び出し| E[CallExpression検査]
    D --> F{パターンマッチ}
    E --> G{使用法検証}
    F -->|違反| H[エラー報告]
    F -->|正常| I[次へ]
    G -->|違反| H
    G -->|正常| I
    H --> J{自動修正可能}
    J -->|Yes| K[fixer適用]
    J -->|No| L[メッセージのみ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-54-01 | JSXインポート禁止 | .jsx拡張子インポートを禁止し.jsに自動修正 | ImportDeclarationで.jsxパス |
| BR-54-02 | 相対モノレポインポート禁止 | ../packages/*/src/へのインポート禁止 | 正規表現マッチ |
| BR-54-03 | exportsディレクトリ禁止 | exports/への相対インポート禁止 | 正規表現マッチ |
| BR-54-04 | ロガー使用法 | payload.logger.errorは{ msg, err }形式を使用 | CallExpressionで検出 |
| BR-54-05 | リトライ不可アサーション | expect.poll()またはtoPass()でラップ | Playwrightテスト内 |
| BR-54-06 | wait関数禁止 | wait()関数の使用を禁止 | テストファイル内 |

### 計算ロジック

**no-relative-monorepo-imports正規表現**:
```javascript
// ../で始まり、packages/*/src/にマッチ
const regex = /^(\.\.\/)*((?!src\b)\w+\/)+src\//
```

**no-imports-from-exports-dir正規表現**:
```javascript
// ../または./で始まりexports/にマッチ
const regex = /^(\.?\.\/)*exports\//
```

**proper-payload-logger-usage検証ロジック**:
```javascript
// 有効な使用法:
// 1. payload.logger.error('message')
// 2. payload.logger.error({ msg: '...', err: error })
// 無効な使用法:
// - payload.logger.error('message', err)
// - payload.logger.error({ message: '...' }) // msgを使用すべき
// - payload.logger.error({ error: e }) // errを使用すべき
```

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

ESLintプラグインはデータベースを操作しない。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| improperUsage | ロガー使用エラー | payload.logger.errorの不正使用 | { msg, err }形式に修正 |
| wrongErrorField | ロガー使用エラー | { error }の使用 | { err }に変更 |
| wrongMessageField | ロガー使用エラー | { message }の使用 | { msg }に変更 |

### リトライ仕様

リトライは行わない。

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

トランザクション管理は不要。

## パフォーマンス要件

- 各ルールは個別のAST走査で効率的に処理
- 正規表現パターンはシンプルで高速

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

- 開発ツールのため、本番環境では使用されない
- 外部ネットワークへのアクセスは行わない

## 備考

- ESLint 9.x対応
- @payloadcms/eslint-configに統合されて使用
- テスト関連ルールはE2Eテストファイル（*.e2e.spec.ts）に適用推奨

---

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

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

### 推奨読解順序

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

ESLint RuleModuleの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | package.json | `packages/eslint-plugin/package.json` | パッケージ定義 |

**読解のコツ**:
- ESLint.Rule.RuleModule型を参照
- meta: ルールメタデータ（type、docs、fixable、schema）
- create: ルール実装（AST visitorを返す）

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

プラグインのエクスポート構造を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.mjs | `packages/eslint-plugin/index.mjs` | プラグインエクスポート |

**主要処理フロー**:
1. **1-8行目**: 各カスタムルールのインポート
2. **13-41行目**: rulesオブジェクトでルール名とルール実装をマッピング
3. **25-40行目**: no-wait-functionはインラインで定義

#### Step 3: インポート関連ルールを理解する

インポート文を検査するルールの実装を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | no-jsx-import-statements.js | `packages/eslint-plugin/customRules/no-jsx-import-statements.js` | JSXインポート禁止 |
| 3-2 | no-relative-monorepo-imports.js | `packages/eslint-plugin/customRules/no-relative-monorepo-imports.js` | 相対モノレポインポート禁止 |
| 3-3 | no-imports-from-exports-dir.js | `packages/eslint-plugin/customRules/no-imports-from-exports-dir.js` | exportsディレクトリ禁止 |

**主要処理フロー**:
- **6-14行目（no-jsx-import-statements.js）**: meta定義、fixable: 'code'で自動修正可能
- **17-30行目**: ImportDeclaration visitor、パスを検査しfixerで修正
- **24-27行目（no-relative-monorepo-imports.js）**: 正規表現でパスパターンをマッチ

#### Step 4: ロガー使用検証ルールを理解する

payload.logger.errorの使用法を検証するルールを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | proper-payload-logger-usage.js | `packages/eslint-plugin/customRules/proper-payload-logger-usage.js` | ロガー使用検証 |

**主要処理フロー**:
- **8-12行目**: メッセージID定義（improperUsage、wrongErrorField、wrongMessageField）
- **21-31行目**: isPayloadLoggerError関数でpayload.logger.error呼び出しを検出
- **38-44行目**: Case 1: 単一文字列引数は有効
- **47-67行目**: Case 2: オブジェクト引数の場合、error/messageキーを検出
- **71-82行目**: Case 3: 文字列+エラーの組み合わせはエラー

#### Step 5: テスト安定性ルールを理解する

Playwrightテストの安定性を向上させるルールを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | no-flaky-assertions.js | `packages/eslint-plugin/customRules/no-flaky-assertions.js` | 不安定アサーション禁止 |

**主要処理フロー**:
- **14-44行目**: nonRetryableAssertions配列でリトライ不可アサーションを定義
- **46-52行目**: isNonRetryableAssertionでアサーションを判定
- **54-70行目**: isExpectPollOrToPassでラップされているか確認
- **72-96行目**: 親チェーンを走査してラップ確認
- **99-118行目**: CallExpression visitorでルール適用

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

```
ESLintプラグイン (index.mjs)
    │
    ├─ no-jsx-import-statements
    │      └─ ImportDeclaration visitor
    │             └─ fixer.removeRange()
    │
    ├─ no-relative-monorepo-imports
    │      └─ ImportDeclaration visitor
    │             └─ 正規表現マッチ
    │
    ├─ no-imports-from-exports-dir
    │      └─ ImportDeclaration visitor
    │             └─ 正規表現マッチ
    │
    ├─ no-imports-from-self
    │      └─ ImportDeclaration visitor
    │
    ├─ proper-payload-logger-usage
    │      └─ CallExpression visitor
    │             ├─ isPayloadLoggerError()
    │             └─ 引数検証
    │
    ├─ no-flaky-assertions
    │      └─ CallExpression visitor
    │             ├─ isNonRetryableAssertion()
    │             ├─ isExpectPollOrToPass()
    │             └─ hasExpectPollOrToPassInChain()
    │
    └─ no-wait-function
           └─ CallExpression visitor
                  └─ callee.name === 'wait'
```

### データフロー図

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

ソースコード ───▶ ESLintパーサー ───▶ AST
                                        │
                                        ▼
                              プラグインルール適用
                                        │
              ┌─────────────────────────┼─────────────────────────┐
              ▼                         ▼                         ▼
      ImportDeclaration           CallExpression              その他
              │                         │                         │
              ▼                         ▼                         ▼
      パスパターン検査            関数呼び出し検査             MDX検査
              │                         │                         │
              └─────────────────────────┼─────────────────────────┘
                                        │
                                        ▼
                              context.report()
                                        │
                      ┌─────────────────┼─────────────────┐
                      ▼                                   ▼
                診断メッセージ                        自動修正
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.mjs | `packages/eslint-plugin/index.mjs` | エントリー | プラグインエクスポート |
| package.json | `packages/eslint-plugin/package.json` | 設定 | パッケージ定義 |
| no-jsx-import-statements.js | `packages/eslint-plugin/customRules/no-jsx-import-statements.js` | ルール | JSXインポート禁止 |
| no-relative-monorepo-imports.js | `packages/eslint-plugin/customRules/no-relative-monorepo-imports.js` | ルール | 相対モノレポインポート禁止 |
| no-imports-from-exports-dir.js | `packages/eslint-plugin/customRules/no-imports-from-exports-dir.js` | ルール | exportsディレクトリ禁止 |
| no-imports-from-self.js | `packages/eslint-plugin/customRules/no-imports-from-self.js` | ルール | 自己インポート禁止 |
| proper-payload-logger-usage.js | `packages/eslint-plugin/customRules/proper-payload-logger-usage.js` | ルール | ロガー使用検証 |
| no-flaky-assertions.js | `packages/eslint-plugin/customRules/no-flaky-assertions.js` | ルール | 不安定アサーション禁止 |
| no-non-retryable-assertions.js | `packages/eslint-plugin/customRules/no-non-retryable-assertions.js` | ルール | リトライ不可アサーション警告 |
| valid-code-block-languages.js | `packages/eslint-plugin/customRules/valid-code-block-languages.js` | ルール | コードブロック言語検証 |
