t08-sfc-static-check.mjs 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. // 静态自检(只读):SFC 能否编译 + 模板用到的标识符是否都在 <script setup> 里存在。
  2. // 这不是 build/dev,只是用仓库已有的 @vue/compiler-sfc 做一次语法与绑定核对。
  3. import fs from 'node:fs'
  4. import { createRequire } from 'node:module'
  5. const require = createRequire(import.meta.url)
  6. const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc')
  7. let failed = 0
  8. const ok = (name, cond) => { if (!cond) failed += 1; console.log(`${cond ? 'PASS' : 'FAIL'} ${name}`) }
  9. for (const file of [
  10. 'src/modules/cockpit/prototypes/AnchorPrototype.vue',
  11. 'src/pages/cockpit/index.vue',
  12. ]) {
  13. const src = fs.readFileSync(file, 'utf8')
  14. const { descriptor, errors } = parse(src, { filename: file })
  15. ok(`${file} 解析`, errors.length === 0)
  16. if (errors.length) { errors.forEach((e) => console.log(' -', e.message)); continue }
  17. try {
  18. const script = compileScript(descriptor, { id: 'proto' })
  19. ok(`${file} script 编译`, true)
  20. if (descriptor.template) {
  21. const tpl = compileTemplate({ source: descriptor.template.content, filename: file, id: 'proto' })
  22. ok(`${file} template 编译`, tpl.errors.length === 0)
  23. tpl.errors.forEach((e) => console.log(' -', e.message || e))
  24. }
  25. } catch (error) {
  26. ok(`${file} 编译`, false)
  27. console.log(' -', error.message)
  28. }
  29. }
  30. // 逐个核对 AnchorPrototype 模板里的顶层标识符是否真的有定义
  31. const file = 'src/modules/cockpit/prototypes/AnchorPrototype.vue'
  32. const src = fs.readFileSync(file, 'utf8')
  33. const { descriptor } = parse(src, { filename: file })
  34. const script = compileScript(descriptor, { id: 'proto' })
  35. const bindings = new Set(Object.keys(script.bindings || {}))
  36. const content = descriptor.scriptSetup.content
  37. const required = [
  38. 'ANCHOR_FORMS', 'deviceVisual', 'effectLabel', 'shapeChar', 'toneOf',
  39. 'variant', 'point', 'pointVisual', 'plan', 'layout', 'panelStyle', 'arrowStyle', 'stateText',
  40. 'deviceCount', 'pointIndex', 'barEl', 'cfg', 'statusOrder', 'selectedCode', 'selectDevice',
  41. 'closePanel', 'choose', 'cycle', 'rebuild', 'samplePoints', 'flyToPoints',
  42. ]
  43. const missing = required.filter((name) => !bindings.has(name) && !new RegExp(`(function|const|let)\\s+${name}\\b`).test(content))
  44. ok('模板/脚本引用的标识符全部有定义', missing.length === 0)
  45. if (missing.length) console.log(' 缺失:', missing.join(', '))
  46. // 关键纪律:不能残留未清理的调试语句
  47. ok('无 console.log 残留', !/console\.(log|debug)\(/.test(content))
  48. ok('无 debugger 残留', !/\bdebugger\b/.test(content))
  49. console.log(`\n${failed === 0 ? '静态自检全部通过' : `${failed} 项失败`}`)
  50. process.exit(failed === 0 ? 0 : 1)