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();
|