# 機能設計書 84-スラッグ生成

## 概要

本ドキュメントは、Decap CMSのエントリスラッグ自動生成機能について記述する。`slugFormatter`関数により、コレクション設定のテンプレートとエントリデータを基にスラッグを生成する。Unicodeエンコーディング、アクセント除去、サニタイズ置換等の設定が可能。

### 本機能の処理概要

**業務上の目的・背景**：Decap CMSではエントリはファイルシステム上のファイルとして保存されるため、各エントリに一意のスラッグ（ファイル名の一部）が必要となる。スラッグはURLにも使用されるため、URLセーフな文字列に変換する必要がある。本機能はコレクション設定のテンプレート（例: `{{slug}}`、`{{year}}-{{month}}-{{slug}}`）を基にスラッグを自動生成し、正規化処理を適用する。

**機能の利用シーン**：新規エントリの作成時、エントリのスラッグがテンプレートに基づいて自動生成される。サマリー表示、プレビューURL生成、メディアフォルダのパス解決にも使用される。

**主要な処理内容**：
1. `prepareSlug` - スラッグの前処理（トリム、小文字化、シングルクォート除去、ピリオドのダッシュ置換）
2. `getProcessSegment` - slugConfigに基づくサニタイズ処理関数の生成
3. `slugFormatter` - コレクションのslugテンプレートからスラッグを生成
4. `previewUrlFormatter` - プレビューURLのテンプレートベース生成
5. `summaryFormatter` - サマリーテンプレートの展開
6. `folderFormatter` - メディア/コンテンツフォルダのテンプレートベースパス解決

**関連システム・外部連携**：sanitizeSlug（urlHelper）、stringTemplate（decap-cms-lib-widgets）

**権限による制御**：権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | エントリ新規作成画面 | 主画面 | 新規エントリのスラッグ自動生成 |
| 6 | エントリ編集画面 | 参照画面 | プレビューURL生成、サマリー表示 |
| 3 | コレクション一覧画面 | 参照画面 | エントリサマリーの表示 |

## 機能種別

テンプレート処理 / 文字列フォーマット / スラッグ生成

## 入力仕様

### 入力パラメータ

#### slugFormatter

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| collection | Collection | Yes | コレクション設定（slugテンプレートを含む） | - |
| entryData | Map<string, unknown> | Yes | エントリのフィールドデータ | identifier_field必須 |
| slugConfig | CmsSlug | No | スラッグ設定（encoding、clean_accents、sanitize_replacement） | - |

#### previewUrlFormatter

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| baseUrl | string | Yes | ベースURL | 空の場合undefinedを返す |
| collection | Collection | Yes | コレクション設定 | - |
| slug | string | Yes | エントリスラッグ | - |
| entry | EntryMap | Yes | エントリデータ | - |
| slugConfig | CmsSlug | No | スラッグ設定 | - |

### 入力データソース

コレクション設定（config.yml）のslugフィールド、エントリデータ、CMS全体のslug設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| slug | string | 生成されたスラッグ文字列 |
| previewUrl | string/undefined | プレビューURL（baseUrlが空の場合undefined） |
| summary | string | フォーマットされたサマリー文字列 |
| mediaFolder | string | フォーマットされたメディアフォルダパス |

### 出力先

エントリ保存処理、プレビューURL表示、コレクション一覧のサマリー表示

## 処理フロー

### 処理シーケンス

```
1. スラッグ生成（slugFormatter）
   ├─ collection.slugテンプレートを取得（デフォルト: '{{slug}}'）
   ├─ identifier_fieldからidentifierを取得
   ├─ processSegment関数を生成（prepareSlug + sanitizeSlug）
   ├─ エントリデータから日付をパース
   ├─ compileStringTemplateでテンプレートをコンパイル
   └─ collection.pathがある場合はパステンプレートも適用

2. prepareSlug
   ├─ trim()で前後の空白除去
   ├─ toLocaleLowerCase()で小文字化
   ├─ シングルクォート除去
   └─ ピリオドをダッシュに置換

3. プレビューURL生成（previewUrlFormatter）
   ├─ baseUrl末尾のスラッシュ除去
   ├─ collection/fileのpreview_pathテンプレートを取得
   ├─ addFileTemplateFieldsでdirname/filename/extensionを追加
   ├─ compileStringTemplateでパスをコンパイル
   └─ basePath + '/' + compiledPathで結合
```

### フローチャート

```mermaid
flowchart TD
    A[slugFormatter呼び出し] --> B[slugテンプレート取得]
    B --> C[identifier取得]
    C --> D{identifierあり?}
    D -->|No| E[エラースロー]
    D -->|Yes| F[processSegment関数生成]
    F --> G[日付パース]
    G --> H[compileStringTemplate実行]
    H --> I{collection.pathあり?}
    I -->|Yes| J[パステンプレート適用]
    I -->|No| K[スラッグ返却]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-84-01 | デフォルトスラッグテンプレート | collection.slugが未設定の場合、`{{slug}}`がデフォルト | slugテンプレート未設定時 |
| BR-84-02 | identifier必須 | identifier_field（デフォルトはtitle）の値が必須。なければエラー | slugFormatter呼び出し時 |
| BR-84-03 | 小文字変換 | スラッグは自動的に小文字に変換される | prepareSlug実行時 |
| BR-84-04 | 特殊文字置換 | シングルクォートは除去、ピリオドはダッシュに置換される | prepareSlug実行時 |
| BR-84-05 | パステンプレート | collection.pathが設定されている場合、スラッグはパステンプレートで再処理される | collection.pathが存在する場合 |
| BR-84-06 | プレビューURL省略 | baseUrlが空の場合、プレビューURLは生成されない | previewUrlFormatter呼び出し時 |
| BR-84-07 | 日付フォールバック | エントリに日付フィールドがない場合、現在日時が使用される | slugFormatter呼び出し時 |

### 計算ロジック

- prepareSlug: `slug.trim().toLocaleLowerCase().replace(/[']/g, '').replace(/[.]/g, '-')`
- getProcessSegment: `flow([String, prepareSlug, sanitizeSlug(slugConfig)])`

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

該当なし。本機能は文字列処理のみを行う。

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

なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | identifier_fieldの値がない | コレクション設定にidentifier_fieldを設定するか、titleフィールドを追加 |
| SLUG_MISSING_REQUIRED_DATE | Error | preview_pathに日付変数があるが日付が無効 | preview_path_date_fieldの設定を確認 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

文字列処理のみであり、特別なパフォーマンス要件はない。

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

- スラッグはファイルパスとして使用されるため、sanitizeSlugによるパストラバーサル防止が重要
- プレビューURLはユーザーに表示されるため、不正なURLインジェクションを防ぐ必要がある

## 備考

- sanitizeSlug関数はurlHelper.tsに定義されており、slugConfigに基づくエンコーディング（unicode/ascii）、アクセント除去、サニタイズ置換を行う
- summaryFormatterはコミット情報（author、updatedOn）もテンプレート変数として使用可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | formatters.ts | `packages/decap-cms-core/src/lib/formatters.ts` | CmsSlug型、Collection型、EntryMap型の使用箇所 |
| 1-2 | stringTemplate.ts | `packages/decap-cms-lib-widgets/src/stringTemplate.ts` | compileStringTemplate関数のインターフェース |

**読解のコツ**: slugFormatterは`compileStringTemplate`のラッパーとして機能し、スラッグ固有の前処理（prepareSlug）とサニタイズ（sanitizeSlug）を追加する。

#### Step 2: スラッグ生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | formatters.ts | `packages/decap-cms-core/src/lib/formatters.ts` | slugFormatter、prepareSlug、getProcessSegment |

**主要処理フロー**:
1. **96-109行目**: prepareSlug - トリム→小文字化→クォート除去→ピリオドのダッシュ置換
2. **111-116行目**: getProcessSegment - processSegment関数の生成（ignoreValues対応）
3. **118-148行目**: slugFormatter - スラッグテンプレートのコンパイルとパステンプレートの適用
4. **150-220行目**: previewUrlFormatter - プレビューURLの生成
5. **222-241行目**: summaryFormatter - サマリーテンプレートの展開
6. **243-275行目**: folderFormatter - フォルダテンプレートの展開

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

```
slugFormatter（formatters.ts 118行目）
    |
    +-- selectIdentifier（collections reducer）
    +-- getProcessSegment
    |      +-- prepareSlug（96行目）
    |      +-- sanitizeSlug（urlHelper）
    +-- parseDateFromEntryData（stringTemplate）
    +-- compileStringTemplate（stringTemplate）
    +-- [collection.path] compileStringTemplate（パステンプレート）

previewUrlFormatter（formatters.ts 150行目）
    |
    +-- addFileTemplateFields（stringTemplate）
    +-- parseDateFromEntry（stringTemplate）
    +-- getProcessSegment
    +-- compileStringTemplate（stringTemplate）

summaryFormatter（formatters.ts 222行目）
    |
    +-- parseDateFromEntry
    +-- selectIdentifier
    +-- addFileTemplateFields
    +-- compileStringTemplate
```

### データフロー図

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

collection.slug ─────────▶ slugFormatter ──────────────▶ スラッグ文字列
entryData ───────────────▶   |
slugConfig ──────────────▶   +-- prepareSlug（正規化）
                             +-- sanitizeSlug（サニタイズ）
                             +-- compileStringTemplate

baseUrl ─────────────────▶ previewUrlFormatter ────────▶ プレビューURL
collection.preview_path ─▶   |
entry ───────────────────▶   +-- addFileTemplateFields
                             +-- compileStringTemplate

summaryTemplate ─────────▶ summaryFormatter ───────────▶ サマリー文字列
entry ───────────────────▶   +-- compileStringTemplate
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| formatters.ts | `packages/decap-cms-core/src/lib/formatters.ts` | ソース | slugFormatter、previewUrlFormatter、summaryFormatter、folderFormatter |
| urlHelper.ts | `packages/decap-cms-core/src/lib/urlHelper.ts` | ソース | sanitizeSlug関数 |
| stringTemplate.ts | `packages/decap-cms-lib-widgets/src/stringTemplate.ts` | ソース | compileStringTemplate、addFileTemplateFields |
| collections.ts | `packages/decap-cms-core/src/reducers/collections.ts` | ソース | selectIdentifier、selectInferredField、getFileFromSlug |
