import { readFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' function walk(d, acc = []) { for (const e of readdirSync(d, { withFileTypes: true })) { const p = join(d, e.name) if (e.isDirectory()) walk(p, acc) else if (/\.(vue|ts|md)$/.test(e.name)) acc.push(p) } return acc } const checks = [ ['src/components/quote/ChartPanel.vue', ['分时', '五日', '日K', '周K', '月K', '成交量', '均价']], ['src/components/ai/AiAdviceDrawer.vue', ['置信度', '一键获取建议', '收起', '展开', 'DeepSeek']], ['src/components/position/PositionPanel.vue', ['总持仓', '成交量', '净持仓', '多单持仓前20名', '空单持仓前20名']], ['src/components/header/SettingsDialog.vue', ['设置', '额外关键词', '保存', '取消']], ['src/components/quote/OrderBook.vue', ['买', '卖']], ['src/components/quote/QuoteStats.vue', ['开盘', '涨跌', '持仓量', '外盘', '内盘']], ['src/components/position/PositionTable.vue', ['名次', '会员简称', '增减', '数量']], ['src/components/position/PositionDonut.vue', ['其他', '数量', '增减', '占比']], ['src/components/quote/TradeTape.vue', ['分时成交', '时间', '价格', '现手']], ['src/components/news/NewsList.vue', ['相关新闻']], ['src/components/news/NewsItem.vue', ['查看原文']], ['src/components/header/AppHeader.vue', ['更新', '设置']], ['src/mocks/quote.ts', ['玻璃', '郑商所', '休市']], ['src/mocks/aiAdvice.ts', ['观望']], ] let failed = 0 for (const [f, words] of checks) { const t = readFileSync(f, 'utf8') const miss = words.filter((w) => !t.includes(w)) if (miss.length) { failed++ console.log('FAIL', f, miss.join(',')) } else { console.log('OK ', f) } } // detect literal ??? garbled (3+ question marks in a row outside of ??) for (const f of walk('src')) { const t = readFileSync(f, 'utf8') if (/\?{3,}/.test(t)) { console.log('GARBLED?', f) failed++ } } process.exit(failed ? 1 : 0)