#!/usr/bin/env node /** * 校验「项目注册表」与「详情页目录」是否一一对应。 * * 门户的项目清单由 data/projects.ts 单一驱动。如果注册表加了项目却忘了写详情页, * 或者写了详情页却没登记进注册表,首页卡片与侧边栏就会和实际内容脱节。 * 这个脚本在 dev / build 之前自动跑,不一致直接退出非零。 */ import { readdirSync, readFileSync, existsSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { dirname, join } from 'node:path' const root = join(dirname(fileURLToPath(import.meta.url)), '..') const dataDir = join(root, 'data') const pagesDir = join(root, 'pages', 'projects') // 注册表是 TS 源文件,这里只需要 id 列表,正则抽取即可,不引入编译步骤。 // 只看 projects.ts:data/ 下其它文件(如首页文案 content.ts)里的 id 不是项目 id。 const registrySrc = readFileSync(join(dataDir, 'projects.ts'), 'utf-8') const registered = [...registrySrc.matchAll(/\bid:\s*'([^']+)'/g)].map((m) => m[1]) if (registered.length === 0) { console.error('[check] 注册表里没解析到任何项目,检查 data/projects.ts 的格式') process.exit(1) } const onDisk = existsSync(pagesDir) ? readdirSync(pagesDir, { withFileTypes: true }) .filter((d) => d.isDirectory()) .map((d) => d.name) : [] const missingPages = registered.filter((id) => !onDisk.includes(id)) const missingEntries = onDisk.filter((id) => !registered.includes(id)) if (missingPages.length || missingEntries.length) { if (missingPages.length) { console.error(`[check] 注册表里有项目但缺详情页:${missingPages.join(', ')}`) } if (missingEntries.length) { console.error(`[check] 有详情页但没登记进注册表:${missingEntries.join(', ')}`) } process.exit(1) } console.log(`[check] OK:注册表 ${registered.length} 个项目与详情页一一对应(${registered.join(', ')})`)