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

## 概要

本ドキュメントは、Actualアプリケーション開発で使用されるESLintプラグインの設計仕様を記載したものである。Actual固有のリントルールを提供する。

### 本機能の処理概要

**業務上の目的・背景**：Actualプロジェクトでは、コードの品質と一貫性を維持するために独自のESLintルールが必要となる。特に国際化対応（i18n）、タイポグラフィ、コーディングスタイルの統一に関するルールを提供する。これにより、レビュー工数の削減とコードベースの一貫性維持を実現する。

**機能の利用シーン**：
- コードを書いている際のリアルタイムリント
- コミット前の自動チェック（pre-commit hook）
- CI/CDパイプラインでの品質チェック
- VSCode等のエディタでのリアルタイムフィードバック

**主要な処理内容**：
1. 未翻訳文字列の検出（no-untranslated-strings）
2. Trans vs t()の推奨（prefer-trans-over-t）
3. カーリー引用符の検出（typography）
4. if文の推奨（prefer-if-statement）
5. console.logの検出（prefer-logger-over-console）
6. オブジェクトショートハンド推奨（object-shorthand-properties）
7. const推奨（prefer-const）
8. アンカータグの検出（no-anchor-tag）
9. Reactデフォルトインポートの禁止（no-react-default-import）

**関連システム・外部連携**：
- ESLint
- TypeScript ESLint
- エディタ統合（VSCode等）

**権限による制御**：特になし（開発ツール）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 開発環境 | 開発ツール | コード品質チェック |

## 機能種別

バリデーション / 開発ツール

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| AST | ESTree | Yes | ESLintから渡されるAST | - |
| context | RuleContext | Yes | ESLintルールコンテキスト | - |

### 入力データソース

- ソースコードファイル（JavaScript/TypeScript/JSX/TSX）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| report | ESLint Report | 検出された問題のレポート |
| fix | ESLint Fix | 自動修正（fixableルールの場合） |

### 出力先

- ESLintコンソール出力
- エディタのリント表示

## 処理フロー

### 処理シーケンス

```
1. ESLintがファイルを解析
   └─ ASTを生成

2. eslint-plugin-actualがロード
   └─ lib/index.jsからルールをエクスポート

3. 各ルールがASTノードを検査
   └─ create関数がビジターパターンで実装

4. 問題を検出した場合
   └─ context.report()でエラーを報告

5. 自動修正可能な場合
   └─ fixerオブジェクトを返却
```

### フローチャート

```mermaid
flowchart TD
    A[ESLintがソースファイルを読み込み] --> B[AST生成]
    B --> C[eslint-plugin-actualルール適用]
    C --> D{各ルールでノード検査}
    D --> E[no-untranslated-strings]
    D --> F[typography]
    D --> G[その他のルール]
    E --> H{問題検出?}
    F --> H
    G --> H
    H -->|Yes| I[context.report]
    H -->|No| J[次のノードへ]
    I --> K{fixable?}
    K -->|Yes| L[fixer生成]
    K -->|No| M[エラーのみ報告]
    L --> N[ESLintがレポート生成]
    M --> N
    J --> D
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-69-001 | no-untranslated-strings | 英語文字列は翻訳関数でラップ必須 | JSXテキスト、属性 |
| BR-69-002 | prefer-trans-over-t | JSXテキストでは`<Trans>`を推奨 | JSXコンテキスト |
| BR-69-003 | typography | カーリー引用符をストレート引用符に変換 | 文字列リテラル |
| BR-69-004 | prefer-if-statement | 三項演算子よりif文を推奨 | 条件式 |
| BR-69-005 | prefer-logger-over-console | console.logよりloggerを推奨 | console呼び出し |
| BR-69-006 | object-shorthand-properties | オブジェクトショートハンドを推奨 | オブジェクトリテラル |
| BR-69-007 | prefer-const | 再代入なしの変数はconstを推奨 | 変数宣言 |
| BR-69-008 | no-anchor-tag | `<a>`タグより`<Link>`を推奨 | JSX要素 |
| BR-69-009 | no-react-default-import | Reactデフォルトインポートを禁止 | import文 |

### 計算ロジック

**isProbablyEnglish判定（no-untranslated-strings）**:
- トリムした文字列がホワイトリストに含まれる場合は除外
- 正規表現: `/^[A-Z][a-z].*[a-z](\p{P})?$/`（大文字開始、小文字終了）

**カーリー引用符検出（typography）**:
- U+2018, U+2019（シングルカーリー）
- U+201C, U+201D（ダブルカーリー）

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

特になし（静的解析ツール）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| useTrans | リントエラー | 未翻訳の英語文字列（JSX） | `<Trans>`でラップ |
| useT | リントエラー | 未翻訳の英語文字列（属性） | `t()`でラップ |
| quote | リントエラー | カーリー引用符使用 | ストレート引用符に変換 |

### リトライ仕様

特になし

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

特になし

## パフォーマンス要件

- リント処理: ファイルあたり100ms以内

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

特になし（開発ツール）

## 備考

- CommonJS形式（type: "commonjs"）
- vitestでテスト実行
- eslint-vitest-rule-testerでルールテスト

---

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

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

### 推奨読解順序

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

まず、ESLintプラグインの構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `packages/eslint-plugin-actual/lib/index.js` | ルールのエクスポート構造 |
| 1-2 | package.json | `packages/eslint-plugin-actual/package.json` | パッケージ設定 |

**読解のコツ**: ESLintプラグインはmodule.exportsでrulesオブジェクトをエクスポート。各ルールは個別のファイルで実装される。

#### Step 2: 代表的なルールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | no-untranslated-strings.js | `packages/eslint-plugin-actual/lib/rules/no-untranslated-strings.js` | 国際化対応ルール |

**主要処理フロー（no-untranslated-strings.js）**:
1. **4-17行目**: meta定義（type, docs, fixable, schema, messages）
2. **18-38行目**: whitelist定義（除外文字列リスト）
3. **40-48行目**: isProbablyEnglish関数
4. **50-84行目**: isInsideTrans関数（Trans内かチェック）
5. **86-109行目**: isProgrammaticString関数
6. **111-139行目**: isUserFacingJSXAttribute関数
7. **141-183行目**: isInJSXContext関数
8. **185-194行目**: isInJSXAttribute関数
9. **196-219行目**: getFix関数（自動修正生成）
10. **221-262行目**: ビジター関数（JSXText, Literal, TemplateLiteral）

#### Step 3: タイポグラフィルールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | typography.js | `packages/eslint-plugin-actual/lib/rules/typography.js` | カーリー引用符検出 |

**主要処理フロー（typography.js）**:
- **6-16行目**: meta定義
- **20-27行目**: check関数（カーリー引用符マッチ）
- **54-83行目**: 自動修正関数（ストレート引用符への変換）
- **100-119行目**: ヘルパー関数群（isMemberCall, isIdentifierCall等）
- **150-162行目**: isIgnored関数（除外パターン）
- **169-186行目**: ビジター関数（Literal, JSXText, TemplateElement）

#### Step 4: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | import-helpers.js | `packages/eslint-plugin-actual/lib/utils/import-helpers.js` | インポート追加ヘルパー |

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

```
eslint-plugin-actual (lib/index.js)
    │
    ├─ rules: {
    │      ├─ 'no-untranslated-strings' ──▶ lib/rules/no-untranslated-strings.js
    │      │      │
    │      │      ├─ isProbablyEnglish()
    │      │      ├─ isInsideTrans()
    │      │      ├─ isProgrammaticString()
    │      │      ├─ isUserFacingJSXAttribute()
    │      │      ├─ isInJSXContext()
    │      │      ├─ isInJSXAttribute()
    │      │      ├─ getFix()
    │      │      │      └─ ensureImport() ──▶ lib/utils/import-helpers.js
    │      │      │
    │      │      └─ return { JSXText, Literal, TemplateLiteral }
    │      │
    │      ├─ 'prefer-trans-over-t' ──▶ lib/rules/prefer-trans-over-t.js
    │      │
    │      ├─ 'typography' ──▶ lib/rules/typography.js
    │      │      │
    │      │      ├─ check()
    │      │      ├─ isMemberCall()
    │      │      ├─ isIdentifierCall()
    │      │      ├─ isIgnored()
    │      │      │
    │      │      └─ return { Literal, JSXText, TemplateElement }
    │      │
    │      ├─ 'prefer-if-statement' ──▶ lib/rules/prefer-if-statement.js
    │      │
    │      ├─ 'prefer-logger-over-console' ──▶ lib/rules/prefer-logger-over-console.js
    │      │
    │      ├─ 'object-shorthand-properties' ──▶ lib/rules/object-shorthand-properties.js
    │      │
    │      ├─ 'prefer-const' ──▶ lib/rules/prefer-const.js
    │      │
    │      ├─ 'no-anchor-tag' ──▶ lib/rules/no-anchor-tag.js
    │      │
    │      └─ 'no-react-default-import' ──▶ lib/rules/no-react-default-import.js
    │
    └─ meta: { name: 'eslint-plugin-actual' }
```

### データフロー図

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

ソースコード ────────┐
(.js/.jsx/.ts/.tsx) │
                    ▼
              ┌─────────────────┐
              │ ESLint Parser   │
              │ (AST生成)       │
              └────────┬────────┘
                       │
                       ▼
              ┌─────────────────┐
              │ eslint-plugin-  │
              │ actual          │
              │ (ルール適用)    │
              └────────┬────────┘
                       │
         ┌─────────────┼─────────────┐
         ▼             ▼             ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│no-untrans-  │ │typography   │ │その他ルール │
│lated-strings│ │             │ │             │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
       │               │               │
       ▼               ▼               ▼
┌─────────────────────────────────────────────┐
│ context.report() / fixer                     │
│ - messageId                                  │
│ - node                                       │
│ - fix (optional)                             │
└────────────────────┬────────────────────────┘
                     │
                     ▼
              ┌─────────────────┐
              │ ESLint Report   │
              │ - errors        │
              │ - warnings      │
              │ - fixes         │
              └────────┬────────┘
                       │
    ┌──────────────────┼──────────────────┐
    ▼                  ▼                  ▼
┌─────────┐     ┌─────────────┐    ┌─────────────┐
│ コンソール│     │ エディタ    │    │ --fix       │
│ 出力     │     │ 表示        │    │ 自動修正    │
└─────────┘     └─────────────┘    └─────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `packages/eslint-plugin-actual/lib/index.js` | ソース | プラグインエントリーポイント |
| package.json | `packages/eslint-plugin-actual/package.json` | 設定 | パッケージ設定 |
| no-untranslated-strings.js | `packages/eslint-plugin-actual/lib/rules/no-untranslated-strings.js` | ソース | 未翻訳文字列検出ルール |
| prefer-trans-over-t.js | `packages/eslint-plugin-actual/lib/rules/prefer-trans-over-t.js` | ソース | Trans推奨ルール |
| typography.js | `packages/eslint-plugin-actual/lib/rules/typography.js` | ソース | タイポグラフィルール |
| prefer-if-statement.js | `packages/eslint-plugin-actual/lib/rules/prefer-if-statement.js` | ソース | if文推奨ルール |
| prefer-logger-over-console.js | `packages/eslint-plugin-actual/lib/rules/prefer-logger-over-console.js` | ソース | logger推奨ルール |
| object-shorthand-properties.js | `packages/eslint-plugin-actual/lib/rules/object-shorthand-properties.js` | ソース | ショートハンド推奨ルール |
| prefer-const.js | `packages/eslint-plugin-actual/lib/rules/prefer-const.js` | ソース | const推奨ルール |
| no-anchor-tag.js | `packages/eslint-plugin-actual/lib/rules/no-anchor-tag.js` | ソース | アンカータグ検出ルール |
| no-react-default-import.js | `packages/eslint-plugin-actual/lib/rules/no-react-default-import.js` | ソース | Reactインポート検出ルール |
| import-helpers.js | `packages/eslint-plugin-actual/lib/utils/import-helpers.js` | ソース | インポートヘルパー |
| vitest.config.mts | `packages/eslint-plugin-actual/vitest.config.mts` | 設定 | テスト設定 |
