106 lines
3.3 KiB
JavaScript
106 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* check-registrations.js
|
||
* 新規アプリがすべての登録ファイルに存在するかを一括検証する
|
||
* 使い方: node scripts/check-registrations.js [app-id]
|
||
* app-id を省略すると全アプリを一括チェック
|
||
*/
|
||
|
||
import { readFileSync, existsSync } from 'fs';
|
||
import { join, dirname } from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||
const ROOT = join(__dirname, '..');
|
||
|
||
// 登録が必要なファイル
|
||
const TARGETS = [
|
||
{
|
||
label: 'projects.json',
|
||
path: 'posimai-dashboard/src/data/projects.json',
|
||
check: (content, id) => {
|
||
const data = JSON.parse(content);
|
||
return data.projects.some(p => p.id === id);
|
||
},
|
||
},
|
||
{
|
||
label: 'apps/page.tsx (APP_CATEGORIES)',
|
||
path: 'posimai-dashboard/src/app/apps/page.tsx',
|
||
check: (content, id) => content.includes(`"${id}"`),
|
||
},
|
||
{
|
||
label: 'ecosystem/page.tsx (NODES)',
|
||
path: 'posimai-dashboard/src/app/ecosystem/page.tsx',
|
||
check: (content, id) => {
|
||
// id は "posimai-xxx" → node id は "xxx"
|
||
const shortId = id.replace(/^posimai-/, '');
|
||
return content.includes(`id: "${shortId}"`) || content.includes(`id: "${id}"`);
|
||
},
|
||
},
|
||
{
|
||
label: 'roadmap.json',
|
||
path: 'posimai-roadmap/roadmap.json',
|
||
check: (content, id) => {
|
||
const data = JSON.parse(content);
|
||
return (data.apps ?? []).concat(data.other ?? []).some(a => a.id === id)
|
||
|| content.includes(`"id": "${id}"`);
|
||
},
|
||
},
|
||
{
|
||
label: 'timeline/page.tsx (直近イベント)',
|
||
path: 'posimai-dashboard/src/app/timeline/page.tsx',
|
||
check: (content, id) => content.includes(`"${id}"`),
|
||
},
|
||
{
|
||
label: 'atlas.json',
|
||
path: 'posimai-atlas/atlas.json',
|
||
check: (content, id) => {
|
||
// atlas は posimai-apps ノードにまとめて記載する設計なので
|
||
// short id (guard) または full id (posimai-guard) が含まれればOK
|
||
const shortId = id.replace(/^posimai-/, '');
|
||
return content.includes(id) || content.includes(shortId);
|
||
},
|
||
},
|
||
];
|
||
|
||
// チェック対象のアプリID(引数 or 全 posimai-* ディレクトリ)
|
||
function getAppIds() {
|
||
const arg = process.argv[2];
|
||
if (arg) return [arg];
|
||
// projects.json から全IDを取得
|
||
const pjson = JSON.parse(readFileSync(join(ROOT, 'posimai-dashboard/src/data/projects.json'), 'utf8'));
|
||
return pjson.projects.map(p => p.id);
|
||
}
|
||
|
||
function run() {
|
||
const appIds = getAppIds();
|
||
let anyFail = false;
|
||
|
||
for (const id of appIds) {
|
||
const missing = [];
|
||
for (const target of TARGETS) {
|
||
const filePath = join(ROOT, target.path);
|
||
if (!existsSync(filePath)) { missing.push(`${target.label} (ファイル不在)`); continue; }
|
||
const content = readFileSync(filePath, 'utf8');
|
||
if (!target.check(content, id)) missing.push(target.label);
|
||
}
|
||
|
||
if (missing.length > 0) {
|
||
anyFail = true;
|
||
console.log(`\n[MISSING] ${id}`);
|
||
missing.forEach(m => console.log(` - ${m}`));
|
||
} else {
|
||
console.log(`[OK] ${id}`);
|
||
}
|
||
}
|
||
|
||
if (anyFail) {
|
||
console.log('\n登録が不足しているアプリがあります。CLAUDE.md の「アプリ追加・更新時」ルールを確認してください。');
|
||
process.exit(1);
|
||
} else {
|
||
console.log('\n全アプリの登録は完了しています。');
|
||
}
|
||
}
|
||
|
||
run();
|