---
title: architecture/directory-naming · ディレクトリの命名
description: ディレクトリは、その場所に宣言した記法で名付けるべきです。
---

**重大度:** info · **カテゴリ:** architecture

## チェック内容

ディレクトリの名前が、その場所について宣言した記法と一致していない箇所を検出します。features の
起点が camelCase なのに `UserProfile/` である、エンドポイントのセグメントが kebab-case なのに
`setCookie/` である、といったケースです。

このルールは**設定するまで無効**です。ディレクトリ名がどうあるべきかについて既定の考えを持ちません。
それはプロジェクトの規約であって、svelte-vitals が決めることではないからです。

## なぜ重要か

ディレクトリ名は、ツリーが持つ最も安価なシグナルです。規約が守られていれば、`parts/` や `Card/` は
何も開かなくてもそれが何であるかを読み手（人間であれエージェントであれ）に伝えます。それを破る
ディレクトリが 1 つあっても今日は何も困りませんが、その後ずっとシグナルの信頼性を損ないます。1 つの
例外に出会った読み手は、それ以降すべてのケースを確認しなければならなくなるからです。

## 修正方法

ディレクトリをリネームするか、そのディレクトリを巻き込んでいる宣言を狭めます。

## 設定

| オプション    | 型                                      | デフォルト |
| ------------- | --------------------------------------- | ---------- |
| `directories` | ディレクトリ glob → 記法の集合 のマップ | `{}`       |
| `exclude`     | ディレクトリ glob のリスト              | `[]`       |

```js svelte-vitals.config.js
export default {
  rules: {
    'architecture/directory-naming': {
      options: {
        directories: {
          'src/routes/**': 'camelCase|PascalCase',
          'src/routes/internalApi/*': 'kebab-case',
          'src/lib/features/*': 'camelCase',
          'src/lib/api/*': 'camelCase'
        }
      }
    }
  }
};
```

### 記法の名前

認識される記法は 4 つで、いずれも先頭の 1 文字ではなく名前**全体**を判定します。

| 名前         | 受け入れるパターン         | 例            |
| ------------ | -------------------------- | ------------- |
| `camelCase`  | `^[a-z][a-zA-Z0-9]*$`      | `itemList`    |
| `PascalCase` | `^[A-Z][a-zA-Z0-9]*$`      | `PageHeader`  |
| `kebab-case` | `^[a-z0-9]+(-[a-z0-9]+)*$` | `clear-cache` |
| `snake_case` | `^[a-z0-9]+(_[a-z0-9]+)*$` | `price_table` |

値は `|` で連結して複数の名前を指定できます。ある場所に複数種類のディレクトリが正当に混在する場合の
ためです。ルートの `components/` には PascalCase のコンポーネントユニットと camelCase のグルーピングが
並んで置かれます。

これらのルールは、意図的に「PascalCase」で異なることを意味しています。

- このルールは、名前全体が記法に適合しているかを見ます。
- `architecture/unit-entry-file` は、先頭 1 文字が A–Z かどうかだけを見ます。ディレクトリが
  ユニットに*見えるか*を問うているのであって、名前の適合性を問うているのではありません。
- `architecture/reserved-directory-names` は、同じ先頭 1 文字のテストに加え、同名のファイルを
  要求します。

そのため、一方のルールの PascalCase 判定を通っても、もう一方では通らないディレクトリがあり得ます。

**小文字だけの 1 語は `camelCase`、`kebab-case`、`snake_case` を同時に満たします。** `dialog` はこの
3 つすべてに一致します。名前の中に、それらと矛盾する要素が何もないからです。このルールが検出するのは、
どこかの記法に反する証拠（大文字、ハイフン、アンダースコア、先頭の数字、あるいは 4 つのどれにも
当てはまらない文字）を持つ名前だけです。

**文字を 1 つも含まない名前は検出されません。** `2024` や `404` はどの記法にも属さず、年別アーカイブの
ルートは URL を変えずにリネームできません。

### ルートディレクトリ

名前が SvelteKit のルート構文であるディレクトリは、記法のテストの前にデコードされます。そのため
`src/routes/` に届く宣言も利用できます。

| ディレクトリ       | 判定対象   |
| ------------------ | ---------- |
| `[itemId]`         | `itemId`   |
| `[itemId=integer]` | `itemId`   |
| `[...rest]`        | `rest`     |
| `[[optional]]`     | `optional` |
| `(app)`            | `app`      |

`[foo]-[bar]` のような複合セグメントは単一の識別子を持たないため、スキップされます。

結果として、`src/routes/` を対象とする宣言は、パラメータやグループの名前も静的なセグメントと同じ
記法で判定します。あるサブツリーでは URL のセグメントを kebab-case にしたいがパラメータは camelCase に
したい、というプロジェクトは、その代わりに静的セグメントだけを指す、より狭い glob を宣言してください。

### どの宣言が優先されるか

複数の glob が 1 つのディレクトリにマッチした場合は、より特異なものが優先されます。パスのセグメント数が
多いほうが先、同数なら `**` セグメントが少ないほう、それも同じならキーが長いほう、最後に辞書順です。
これによって `'src/routes/internalApi/*'` が `'src/routes/**'` を狭めます。

セグメント数はワイルドカードも数えるため、実在のディレクトリ名を含むキーより、ワイルドカードだけで
深いキーのほうが勝つことがあります。意図した深さを書いてください。

**末尾**の `/**` は「このディレクトリ配下すべて」を意味し、ディレクトリ自身は対象にしません。これは
重要です。というのも、これらのキーが名指す入れ物である `src/routes`、`src/lib`、`src` の名前は
SvelteKit が決めるものであって、あなたが決めるものではないからです。

### `exclude`

**`exclude` はそのディレクトリと配下すべてを対象外にします。** 名前を自分でコントロールできない
サブツリー（生成されたコードやベンダーツリーなど）に使ってください。

```js
options: {
  directories: { 'src/lib/**': 'camelCase' },
  exclude: ['src/lib/generated']
}
```

広い宣言が、あなたが**コントロールしている**サブツリーまで巻き込んでいる場合は、除外するのではなく
glob を狭めてください。除外は、その配下すべてをチェック対象から外してしまいます。

## 制限

対象は `src/` 配下のディレクトリだけです。その外は検査されないので、除外する必要もありません。
ファイル名は一切検査されません。

違反の `route` はその名前が指すディレクトリですが、`location`、つまり `--diff` が絞り込みに使う
フィールドです。git はファイルが変更されたことしか教えてくれず、ディレクトリについては教えてくれない
ほうは、そのディレクトリの中にあるファイルを指します。この 2 つは意図的に分けられています。
違反しているディレクトリの中に入れ子になった、別の違反ディレクトリは、親とは別に報告され、それぞれを
個別に抑制できます。

宣言が書いてある内容を実際には検査していない場合、それは報告されるので、書き間違いによってルールが
黙って何もしない状態にはなりません。次の 5 つのケースがこの finding に該当し、それぞれメッセージに
名前が出ます。

| 宣言の状態                                             | 表示される内容                                                 |
| ------------------------------------------------------ | -------------------------------------------------------------- |
| 1 つのディレクトリにもマッチしなかった                 | `matched no directory`                                         |
| マッチはしたがすべて `exclude` で除外された            | `matched only excluded directories`                            |
| 値がそもそも記法を何も指定していない(例: `''`、`'\|'`) | `the value names no casing at all, so it checks nothing`       |
| このルールが知らない記法の名前を指定した               | `unknown casing name '…', so it checks nothing`                |
| このルールが知っている記法も含んでいた                 | `unknown casing name '…'; the rest of the value still applies` |

注意すべきは最後のケースです。宣言は有効な名前の分だけ動き続けますが、書いたよりも弱いチェックに
なっていることに気づきにくいからです。

**1 つも**既知の記法を含まない宣言はマッチングの前に取り除かれるため、同じディレクトリを本来支配する
はずの、より広い有効な宣言を覆い隠すことがありません。

意図的にまったく報告されないものが 2 つあります。

- `overrides` エントリの**中だけ**で宣言したキー。何にマッチしたかが、そのオーバーライドの適用
  範囲に依存するためです。
- 何にもマッチしない `exclude` の glob。何も除外しない指定はレポートに影響しません。そのため、
  除外するつもりだったサブツリーがそもそも何も検出していなかった場合、書き間違えた `exclude` は
  気づかれないまま残ります。

記法を誤っているディレクトリが、同時にエントリファイルを欠いた宣言済みユニットでもある場合、
`architecture/unit-entry-file` からも finding が出ます。どちらもいずれかを抑制するわけではありません。
それぞれ別の主張であり、両方とも真だからです。

## モードによる違い

ありません。このルールが読むのは、ファイル内容ではなくプロジェクトのソースファイル一覧（`src/**` のパス）です。この一覧は CLI、Vite プラグインのビルド、ライブダッシュボードの静的ベースラインのいずれでも同じように作られるため結果は同一で、レンダリング済み HTML の解析で再評価されることもありません。`--route` で実行範囲を絞ると、このルールは動きません。一覧が作られず、ファイルの検出には紐づけるルートも無いためです。

## 無効化

個別に抑制するには、対象行の直前に `<!-- svelte-vitals-disable-next-line architecture/directory-naming -->` を置きます。ルールごと無効化するには、次のように設定します。

```js svelte-vitals.config.js
export default {
  rules: {
    'architecture/directory-naming': 'off'
  }
};
```
