設定不要のレジストリ:ゲーム追加はディレクトリ追加と同じ
一覧・詳細ルート・トップの注目枠・sitemap はすべて import.meta.glob が自動検出します。代償は Vite の規則、リテラルの相対パスだけという一点です。
このサイトにはゲームが 12 本、ツールが 20 個あります。33 個目を足したとき、設定ファイルは一行も触っていません。
契約は三行
各アプリは自己完結したディレクトリです。
src/games/<slug>/
meta.ts AppMeta をデフォルト export(三言語のタイトル/説明、order、featured)
App.vue Vue コンポーネントをデフォルト export
logic/ DOM に触れない純粋ロジック
ディレクトリ名が slug、つまり URL のセグメントです。
レジストリの発見方法
const gameMeta = import.meta.glob('../games/*/meta.ts', { eager: true });
function collect(kind, modules) {
return Object.entries(modules)
.map(([path, mod]) => {
const slug = path.split('/').slice(-2)[0];
if (!mod.default) throw new Error('デフォルト export がありません: ' + path);
return { kind, slug, ...mod.default };
})
.filter((entry) => !entry.draft)
.sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
}
三点が重要です。slug はパスから取り、ファイル名以外の情報に頼りません。デフォルト export が無ければ例外を投げます。ビルドで落ちるほうが、カードが一枚欠けた状態で公開されるより良いからです。並び順は order だけで決まり、ファイルシステムの列挙順に依存しません。
一箇所を三箇所が使う
一覧ページ、詳細ルート、トップの注目枠は、同じ listApps(kind) から派生します。ルートは appDetailPaths(kind) が三言語分まとめて生成し、一覧は同じ配列からカードを作り、トップは featured の先頭三件を取ります。
出典が一つなら、「アプリを追加するときどこを直すか」の答えは「どこも」になります。
避けられない規則
Vite はビルド時にリテラルのパスしか静的に解析できません。したがって glob パターンはエイリアスにできず、変数にもできません。このプロジェクトで相対パスが必須になるのはちょうど二箇所で、もう一つは mount.ts にある App.vue のレジストリです。
代償
「設定不要」は契約が安定していることが前提です。あるアプリだけ追加のビルド時情報を必要としたら、それを meta.ts に積むのではなく、そもそも全アプリが持つべきフィールドなのかを考えるべきです。

コメント
…