Files
what-to-eat/scripts/validate-recipes.ts
T

77 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 校验 packages/data/recipe.csv:食材/厨具词典、取值、视频号、重复
*
* pnpm validate # 有错误时退出码为 1
* pnpm validate --json # 输出 JSON,供 AI / 脚本消费
* pnpm validate --base origin/main # 同时检查 id 有没有被改给别的菜或复用
*/
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import process from 'node:process'
import { meat, staple, tools, vegetable } from '../apps/web/app/data/food'
import { recipeCsvFile } from '../packages/cook/src/utils/config'
import { parseRecipeCsv } from '../packages/cook/src/utils/csv'
import { validateRecipes } from '../packages/cook/src/utils/validate'
const csvPath = 'packages/data/recipe.csv'
// 迁移到 monorepo 之前的位置,对比旧基准分支时使用
const legacyCsvPath = 'app/data/recipe.csv'
const recipes = parseRecipeCsv(fs.readFileSync(recipeCsvFile, 'utf-8'))
function readBase() {
const index = process.argv.indexOf('--base')
const ref = index > -1 ? process.argv[index + 1] : undefined
if (!ref)
return undefined
const show = (path: string) => execFileSync('git', ['show', `${ref}:${path}`], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] })
let csv: string
try {
csv = show(csvPath)
}
catch {
csv = show(legacyCsvPath)
}
// 基准版本还没有 id 列时 parseRecipeCsv 返回空数组,等于不做对比
return parseRecipeCsv(csv)
}
const { issues, stats } = validateRecipes(recipes, {
stuff: new Set([...vegetable, ...meat, ...staple].map(item => item.name)),
tools: new Set(tools.map(item => item.name)),
}, { base: readBase() })
const errors = issues.filter(i => i.level === 'error')
const warnings = issues.filter(i => i.level === 'warning')
if (process.argv.includes('--json')) {
console.log(JSON.stringify({ stats, issues }, null, 2))
}
else {
const inCI = !!process.env.GITHUB_ACTIONS
for (const issue of issues) {
const where = issue.line ? `第 ${issue.line} 行` : ''
const text = `${where}「${issue.name}」:${issue.message}`
const location = issue.line ? `file=${csvPath},line=${issue.line}` : `file=${csvPath}`
console.log(inCI ? `::${issue.level} ${location}::${text}` : `${issue.level === 'error' ? '✗' : '!'} ${text}`)
}
const percent = (n: number) => `${n}(${Math.round(n / stats.total * 100)}%)`
const summary = [
`### 菜谱数据校验`,
'',
`| 项目 | 数量 |`,
`| --- | --- |`,
`| 菜谱总数 | ${stats.total} |`,
`| 错误 | ${errors.length} |`,
`| 警告 | ${warnings.length} |`,
`| 缺难度 | ${percent(stats.missingDifficulty)} |`,
`| 缺做法 | ${percent(stats.missingMethods)} |`,
`| 缺视频 | ${percent(stats.missingVideo)} |`,
'',
].join('\n')
console.log(`\n${summary}`)
if (process.env.GITHUB_STEP_SUMMARY)
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary)
}
process.exitCode = errors.length ? 1 : 0