| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- // 交叉校验:注册表 ↔ manifest ↔ 组件引用 ↔ 面板映射,四处必须一致
- import { readFileSync, readdirSync } from 'node:fs'
- import { join } from 'node:path'
- const read = (p) => readFileSync(p, 'utf8')
- const cockpit = 'src/modules/cockpit'
- const manifest = JSON.parse(read('public/cockpit/assets/manifest.json'))
- const manifestKeys = new Set(manifest.entries.map((e) => e.key))
- const iconsSrc = read(join(cockpit, 'domain/icons.js'))
- // 注册表条目形如 ` name: 'icon.key',`
- const registry = new Map()
- for (const m of iconsSrc.matchAll(/^\s{4}([a-zA-Z]+):\s*'([^']+)'/gm)) registry.set(m[1], m[2])
- console.log('注册表图标数:', registry.size)
- console.log('manifest 条目数:', manifestKeys.size)
- // ① 注册表引用的 key 必须在 manifest 中
- const missingInManifest = [...registry.entries()].filter(([, k]) => !manifestKeys.has(k))
- console.log('① 注册表引用但 manifest 缺失:', missingInManifest.length ? missingInManifest.map(([n, k]) => `${n}->${k}`).join(', ') : '无')
- // ② manifest 中未被注册表引用的 key(冗余)
- // 票 18(批次 5)修正既有盲区:`marker.*` 六键由位置符号合成层(services/markerAssets.js
- // 经 resolveAsset('marker.master.*'/'marker.inner.*'))直连消费,不经过 icons 注册表——
- // 此前每次跑都会把批次 3 的正式条目误报为「冗余」。判定依据:markerAssets.js 源码里
- // 真实出现这些 key 前缀;若该接缝改名,此处会重新报冗余(不掩盖真实漂移)。
- const usedKeys = new Set(registry.values())
- const markerAssetsSrc = read(join(cockpit, 'services/markerAssets.js'))
- const consumedElsewhere = (k) =>
- (k.startsWith('marker.master.') || k.startsWith('marker.inner.')) && /marker\.(master|inner)\./.test(markerAssetsSrc)
- const unused = [...manifestKeys].filter((k) => !usedKeys.has(k) && !consumedElsewhere(k))
- const coveredBySeams = [...manifestKeys].filter((k) => !usedKeys.has(k) && consumedElsewhere(k))
- console.log('② manifest 中冗余 key:', unused.length ? unused.join(', ') : '无')
- if (coveredBySeams.length) console.log(` (另有 ${coveredBySeams.length} 个 key 经位置符号合成层消费,不在注册表:${coveredBySeams.join(', ')})`)
- // ③ 组件里 <CockpitIcon name="x"> 的 x 必须已注册
- const vueFiles = []
- ;(function walk(dir) {
- for (const e of readdirSync(dir, { withFileTypes: true })) {
- const p = join(dir, e.name)
- if (e.isDirectory()) walk(p)
- else if (e.name.endsWith('.vue')) vueFiles.push(p)
- }
- })(cockpit)
- let badRefs = 0
- for (const f of vueFiles) {
- const s = read(f)
- for (const m of s.matchAll(/<CockpitIcon[^>]*\sname="([a-zA-Z-]+)"/g)) {
- if (!registry.has(m[1])) {
- console.log(` ✗ 未注册引用: ${m[1]} in ${f}`)
- badRefs++
- }
- }
- }
- console.log('③ 组件内未注册图标引用:', badRefs)
- // ④ PANEL_ICONS 的每个值必须已注册
- const panelSrc = read(join(cockpit, 'domain/panelCatalog.js'))
- const panelBlock = panelSrc.split('PANEL_ICONS = {')[1].split('\n}')[0]
- let badPanels = 0
- let panelCount = 0
- for (const m of panelBlock.matchAll(/(\w+):\s*'([a-zA-Z]+)'/g)) {
- panelCount++
- if (!registry.has(m[2])) {
- console.log(` ✗ 面板映射缺图标: ${m[1]} -> ${m[2]}`)
- badPanels++
- }
- }
- console.log(`④ 面板图标映射: ${panelCount} 项,缺失 ${badPanels}`)
- // ⑤ INDUSTRY_ICONS 同理
- const filterSrc = read(join(cockpit, 'domain/filterOptions.js'))
- const indBlock = filterSrc.split('INDUSTRY_ICONS = {')[1].split('\n}')[0]
- let badInd = 0
- let indCount = 0
- for (const m of indBlock.matchAll(/(\w+):\s*'([a-zA-Z]+)'/g)) {
- indCount++
- if (!registry.has(m[2])) {
- console.log(` ✗ 行业映射缺图标: ${m[1]} -> ${m[2]}`)
- badInd++
- }
- }
- console.log(`⑤ 行业图标映射: ${indCount} 项,缺失 ${badInd}`)
- const ok = !missingInManifest.length && !unused.length && !badRefs && !badPanels && !badInd
- console.log(ok ? '\n✅ 四处交叉引用完全一致' : '\n❌ 存在不一致,见上')
- if (!ok) process.exitCode = 1
|