# 機能設計書：型生成

## 1. 概要

### 1.1 機能の目的
型生成機能は、Payload CMSの設定（SanitizedConfig）からTypeScript型定義ファイル（payload-types.ts）を自動生成する。コレクション、グローバル、ブロック、認証操作などの型をJSON Schema経由でTypeScriptインターフェースに変換し、型安全な開発を支援する。

### 1.2 機能の範囲
- SanitizedConfigからJSON Schemaへの変換
- JSON SchemaからTypeScriptへのコンパイル
- コレクション・グローバル・ブロックの型生成
- Select型（フィールド選択用）の生成
- Join型の生成
- 認証操作型の生成
- ロケール・フォールバックロケール型の生成
- ウィジェット型の生成
- ジョブ関連型の生成

### 1.3 関連画面
- なし（CLI/ビルド時処理）

### 1.4 関連機能
| 機能ID | 機能名 | 関連内容 |
|--------|--------|----------|
| 37 | 設定サニタイズ | サニタイズ済み設定が入力 |
| 06 | フィールド基盤 | フィールド型変換 |
| 10 | 認証システム | 認証操作型生成 |
| 46 | Jobs Queue | ジョブ型生成 |

---

## 2. 機能種別

- [ ] API（REST）
- [ ] バッチ処理
- [ ] 画面機能
- [ ] 帳票出力
- [ ] 通知機能
- [x] システム内部機能

---

## 3. 入力仕様

### 3.1 generateTypes関数入力

| パラメータ | 型 | 必須 | 説明 |
|-----------|-----|------|------|
| config | SanitizedConfig | Yes | サニタイズ済み設定 |
| options.log | boolean | No | ログ出力フラグ（デフォルト: true） |

### 3.2 環境変数

| 変数名 | 説明 | デフォルト |
|--------|------|-----------|
| PAYLOAD_TS_OUTPUT_PATH | 出力ファイルパス | config.typescript.outputFile |

### 3.3 設定オプション（config.typescript）

| オプション | 型 | デフォルト | 説明 |
|-----------|-----|-----------|------|
| outputFile | string | `{cwd}/payload-types.ts` | 出力ファイルパス |
| autoGenerate | boolean | true | 自動生成有効化 |
| declare | object \| false | true | declare module生成 |
| declare.ignoreTSError | boolean | false | @ts-ignore追加 |
| schema | function[] | - | カスタムスキーマ変換 |
| strictDraftTypes | boolean | false | ドラフト厳密型 |

---

## 4. 出力仕様

### 4.1 生成ファイル構造

```typescript
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/

// 型定義（interface）
export interface Config { ... }
export interface Posts { ... }
export interface Posts_select { ... }
...

// declare module
declare module 'payload' {
  export interface GeneratedTypes extends Config {}
}
```

### 4.2 生成される主要型

| 型名 | 説明 |
|------|------|
| Config | トップレベル設定型 |
| {CollectionSlug} | 各コレクションのドキュメント型 |
| {CollectionSlug}_select | コレクションのSelect型 |
| {GlobalSlug} | 各グローバルのドキュメント型 |
| {GlobalSlug}_select | グローバルのSelect型 |
| {BlockSlug} | 各ブロックの型 |
| {BlockSlug}_select | ブロックのSelect型 |
| {AuthCollection}AuthOperations | 認証操作型 |
| supportedTimezones | タイムゾーン型 |

### 4.3 Config型のプロパティ

```typescript
interface Config {
  auth: AuthOperations
  blocks: BlockDefinitions
  collections: CollectionDefinitions
  collectionsJoins: JoinDefinitions
  collectionsSelect: SelectDefinitions
  db: { defaultIDType: 'number' | 'string' }
  fallbackLocale: LocaleType | false | null
  globals: GlobalDefinitions
  globalsSelect: SelectDefinitions
  locale: LocaleType | null
  user: AuthUser
  widgets: WidgetDefinitions
  jobs?: JobsDefinitions
  strictDraftTypes?: true  // optional
}
```

---

## 5. 処理フロー

### 5.1 型生成全体フロー

```mermaid
flowchart TD
    A[generateTypes] --> B[initI18n]
    B --> C[configToJSONSchema]
    C --> D[compile JSON Schema to TS]
    D --> E[addSelectGenericsToGeneratedTypes]
    E --> F{declare !== false?}
    F -->|Yes| G[Add declare module]
    F -->|No| H[Continue]
    G --> H
    H --> I{Same as existing?}
    I -->|Yes| J[Skip write]
    I -->|No| K[Write to file]
    K --> L[Log success]
    J --> L
```

### 5.2 configToJSONSchema処理フロー

```mermaid
flowchart TD
    A[configToJSONSchema] --> B[getCollectionIDFieldTypes]
    B --> C[For each global/collection]
    C --> D[entityToJSONSchema]
    D --> E[fieldsToJSONSchema]
    E --> F[fieldsToSelectJSONSchema]
    F --> G[Add to entityDefinitions]
    G --> H{More entities?}
    H -->|Yes| C
    H -->|No| I[generateWidgetSchemas]
    I --> J[generateAuthOperationSchemas]
    J --> K[generateJobsJSONSchemas]
    K --> L[Build blocks definitions]
    L --> M[Assemble final JSON Schema]
    M --> N{typescript.schema?}
    N -->|Yes| O[Apply custom transforms]
    N -->|No| P[Return JSON Schema]
    O --> P
```

### 5.3 フィールド型変換フロー

```mermaid
flowchart TD
    A[fieldsToJSONSchema] --> B[For each field]
    B --> C{field.type?}
    C -->|array| D[Array schema with items]
    C -->|blocks| E[OneOf block schemas]
    C -->|checkbox| F[Boolean type]
    C -->|text/email/date| G[String type]
    C -->|number| H[Number type]
    C -->|relationship/upload| I[ID or $ref schema]
    C -->|richText| J[Editor outputSchema]
    C -->|select| K[Enum string]
    C -->|group/tab| L[Nested object]
    C -->|join| M[Paginated docs schema]
    D --> N{interfaceName?}
    E --> N
    L --> N
    N -->|Yes| O[Add to interfaceNameDefinitions]
    N -->|No| P[Continue]
    O --> P
    P --> Q{More fields?}
    Q -->|Yes| B
    Q -->|No| R[Return properties + required]
```

---

## 6. ビジネスルール

### 6.1 フィールド型マッピング

| フィールドタイプ | JSON Schema型 | TypeScript型 |
|----------------|--------------|-------------|
| text | string | string |
| textarea | string | string |
| email | string | string |
| code | string | string |
| date | string | string |
| number | number | number |
| checkbox | boolean | boolean |
| select | string + enum | union literal |
| radio | string + enum | union literal |
| array | array | Type[] |
| blocks | array + oneOf | (Block1 \| Block2)[] |
| group | object | { ... } |
| tab | object | { ... } |
| relationship | oneOf | string \| number \| RelatedType |
| upload | oneOf | string \| number \| UploadType |
| richText | array | RichTextContent[] |
| point | [number, number] | [number, number] |
| json | any | unknown |
| join | object | { docs, hasNextPage, totalDocs } |

### 6.2 必須フィールドルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-01 | required判定 | field.required === trueかつconditionなしで必須 |
| BR-02 | サブフィールド必須 | ネストフィールドが1つでも必須なら親も必須 |
| BR-03 | nullable型 | 非必須フィールドはnull許容型に |

### 6.3 ID型ルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-04 | デフォルトID型 | config.db.defaultIDTypeに従う |
| BR-05 | カスタムID | コレクションにidフィールドがあればその型を使用 |
| BR-06 | timestamp必須 | timestamps !== falseならcreatedAt/updatedAt必須 |

### 6.4 interfaceName処理

| ID | ルール | 説明 |
|----|--------|------|
| BR-07 | 定義移動 | interfaceName指定時はトップレベル定義に移動 |
| BR-08 | $ref参照 | インライン定義の代わりに$refで参照 |
| BR-09 | unreachableDefinitions | 未参照でも定義を生成 |

---

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

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

---

## 8. エラー処理

### 8.1 エラー種別

| エラー | 発生条件 | 説明 |
|--------|----------|------|
| MissingEditorProp | richTextにeditor未設定 | RichTextフィールドにエディタ必須 |
| Unsanitized editor | editor関数のまま | 未サニタイズのエディタ関数 |
| File write error | 書き込み失敗 | 出力ファイル書き込みエラー |

---

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

本機能にトランザクション制御は含まれない。

---

## 10. パフォーマンス要件

| 項目 | 要件 | 備考 |
|------|------|------|
| 差分チェック | 変更なしならスキップ | 既存ファイルと比較 |
| インターフェース再利用 | $refで重複排除 | 同一構造の再定義回避 |
| 遅延コンパイル | ビルド/CLI時のみ実行 | 起動時実行しない |

---

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

| 項目 | 対応 |
|------|------|
| ファイル出力パス | 設定または環境変数で指定 |
| 生成コード | 自動生成コメントで手動編集防止を促す |

---

## 12. 備考

### 12.1 json-schema-to-typescript

JSON Schema 4形式からTypeScriptへの変換に`json-schema-to-typescript`ライブラリを使用。

### 12.2 addSelectGenericsToGeneratedTypes

生成後の型にSelect用ジェネリクスを追加する後処理。

### 12.3 カスタムスキーマ変換

`config.typescript.schema`配列で、生成されるJSON Schemaをカスタマイズ可能。

---

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

### 13.1 推奨読解順序

1. **エントリーポイント**（`bin/generateTypes.ts`）- 全体フロー
2. **JSON Schema変換**（`utilities/configToJSONSchema.ts`）- 設定→スキーマ変換
3. **フィールド変換**（`fieldsToJSONSchema`）- フィールド型マッピング
4. **Select型生成**（`fieldsToSelectJSONSchema`）- Select型構築

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

```
generateTypes (bin/generateTypes.ts)
├── initI18n() → @payloadcms/translations
├── configToJSONSchema() → utilities/configToJSONSchema.ts
│   ├── getCollectionIDFieldTypes() → utilities/getCollectionIDFieldTypes.ts
│   ├── entityToJSONSchema() [each collection/global]
│   │   ├── fieldsToJSONSchema() [recursive]
│   │   │   ├── buildOptionEnums() [select/radio]
│   │   │   ├── withNullableJSONSchemaType()
│   │   │   └── fieldsToJSONSchema() [nested fields]
│   │   └── entityOrFieldToJsDocs()
│   ├── fieldsToSelectJSONSchema() [each entity]
│   ├── generateWidgetSchemas()
│   ├── generateAuthOperationSchemas()
│   │   └── authCollectionToOperationsJSONSchema()
│   ├── generateJobsJSONSchemas() → queues/config/generateJobsJSONSchemas.ts
│   ├── timezonesToJSONSchema()
│   ├── generateLocaleEntitySchemas()
│   ├── generateFallbackLocaleEntitySchemas()
│   ├── generateEntitySchemas()
│   ├── generateEntitySelectSchemas()
│   ├── generateCollectionJoinsSchemas()
│   └── generateDbEntitySchema()
├── compile() → json-schema-to-typescript
├── addSelectGenericsToGeneratedTypes() → utilities/addSelectGenericsToGeneretedTypes.ts
└── fs.writeFile()
```

### 13.3 データフロー図

```
[SanitizedConfig]
         ↓
┌─────────────────────────────────┐
│  configToJSONSchema()           │
│  - コレクション/グローバル処理   │
│  - フィールド→JSON Schema変換   │
│  - interfaceNameDefinitions構築 │
└─────────────────────────────────┘
         ↓
[JSON Schema (JSONSchema4)]
         ↓
┌─────────────────────────────────┐
│  compile()                      │
│  - json-schema-to-typescript    │
│  - singleQuote: true            │
│  - unreachableDefinitions: true │
└─────────────────────────────────┘
         ↓
[TypeScript string]
         ↓
┌─────────────────────────────────┐
│  addSelectGenericsToGeneratedTypes│
│  - Select型ジェネリクス追加      │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  declare module追加              │
│  - GeneratedTypes extends Config│
└─────────────────────────────────┘
         ↓
[payload-types.ts]
```

### 13.4 関連ファイル一覧

| ファイル | 行数 | 概要 |
|---------|------|------|
| `packages/payload/src/bin/generateTypes.ts` | 1-76 | 型生成エントリーポイント |
| `packages/payload/src/utilities/configToJSONSchema.ts` | 1-1461 | JSON Schema変換 |
| `packages/payload/src/utilities/addSelectGenericsToGeneretedTypes.ts` | - | Select型ジェネリクス追加 |
| `packages/payload/src/utilities/getCollectionIDFieldTypes.ts` | - | ID型取得 |
| `packages/payload/src/utilities/flattenAllFields.ts` | - | フィールドフラット化 |
| `packages/payload/src/queues/config/generateJobsJSONSchemas.ts` | - | ジョブ型生成 |

### 13.5 重要な実装ポイント

#### 13.5.1 generateTypes（bin/generateTypes.ts:13-76）
```typescript
// 13-76行目: 型生成メイン関数
export async function generateTypes(config: SanitizedConfig, options?: { log: boolean }): Promise<void> {
  const outputFile = process.env.PAYLOAD_TS_OUTPUT_PATH || config.typescript.outputFile
  const jsonSchema = configToJSONSchema(config, config.db.defaultIDType, i18n)
  let compiled = await compile(jsonSchema, 'Config', {
    bannerComment: '/* tslint:disable */\n/* eslint-disable */\n/**\n* This file was automatically generated...',
    style: { singleQuote: true },
    unreachableDefinitions: true,
    cwd: process.cwd(),
  })
  // 差分チェック後に書き込み
}
```

#### 13.5.2 fieldsToJSONSchema（configToJSONSchema.ts:345-882）
```typescript
// 345-882行目: フィールド→JSON Schema変換
export function fieldsToJSONSchema(
  collectionIDFieldTypes,
  fields,
  interfaceNameDefinitions,
  config,
  i18n,
): { properties, required } {
  // 各フィールドタイプに応じたスキーマ生成
  switch (field.type) {
    case 'array': { /* ... */ }
    case 'blocks': { /* ... */ }
    case 'relationship': { /* ... */ }
    // ...
  }
}
```

#### 13.5.3 withNullableJSONSchemaType（configToJSONSchema.ts:308-318）
```typescript
// 308-318行目: nullable型生成
export function withNullableJSONSchemaType(
  fieldType: JSONSchema4TypeName,
  isRequired: boolean,
): JSONSchema4TypeName | JSONSchema4TypeName[] {
  const fieldTypes = [fieldType]
  if (isRequired) {
    return fieldType
  }
  fieldTypes.push('null')
  return fieldTypes
}
```

#### 13.5.4 configToJSONSchema（configToJSONSchema.ts:1261-1461）
```typescript
// 1261-1461行目: 設定→JSON Schema変換メイン
export function configToJSONSchema(config, defaultIDType, i18n): JSONSchema4 {
  const interfaceNameDefinitions = new Map()
  const collectionIDFieldTypes = getCollectionIDFieldTypes({ config, defaultIDType })

  // エンティティ定義生成
  const entityDefinitions = entities.reduce((acc, { type, entity }) => {
    acc[entity.slug] = entityToJSONSchema(...)
    acc[`${entity.slug}_select`] = fieldsToSelectJSONSchema(...)
    return acc
  }, {})

  // 最終スキーマ構築
  return {
    definitions: { ...entityDefinitions, ...interfaceNameDefinitions },
    properties: { collections, globals, auth, ... },
    title: 'Config',
  }
}
```

#### 13.5.5 差分チェック（bin/generateTypes.ts:62-70）
```typescript
// 62-70行目: 既存ファイルとの差分チェック
try {
  const existingTypes = await fs.readFile(outputFile, 'utf-8')
  if (compiled === existingTypes) {
    return  // 変更なしならスキップ
  }
} catch (_) {
  // ファイルなし、続行
}
```
