50 lines
1.9 KiB
JavaScript
50 lines
1.9 KiB
JavaScript
#!/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 列表,正则抽取即可,不引入编译步骤
|
||
const registrySrc = readdirSync(dataDir)
|
||
.filter((f) => f.endsWith('.ts'))
|
||
.map((f) => readFileSync(join(dataDir, f), 'utf-8'))
|
||
.join('\n')
|
||
|
||
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(', ')})`)
|