iter(g219): verify 增加重复关键帧检测

This commit is contained in:
HIsheR
2026-10-26 20:00:29 +08:00
parent 5172abda5d
commit f042c668da
3 changed files with 29 additions and 6 deletions
+2 -4
View File
@@ -337,10 +337,7 @@ body.ua-col-resizing {
background: transparent;
border: 2px solid color-mix(in srgb, var(--ua-color-bg) 40%, transparent);
border-top-color: var(--ua-color-bg);
animation: ua-switch-spin var(--ua-duration-loop-mid) linear infinite;
}
@keyframes ua-switch-spin {
to { transform: rotate(360deg); }
animation: ua-spin var(--ua-duration-loop-mid) linear infinite; /* 复用全局转圈:同一帧体不再各写一份 */
}
@media (prefers-reduced-motion: reduce) {
.ua-switch.is-loading .ua-switch-thumb {
@@ -5173,3 +5170,4 @@ h5.ua-type-title { font-size: var(--ua-font-size-md); }
.ua-timepicker.has-clear .ua-timepicker-trigger {
padding-right: var(--ua-space-6); /* 给 × 让出位置:文案不会被压在按钮下面 */
}
+26 -1
View File
@@ -923,6 +923,30 @@ function checkSkinCss(pkg) {
return issues;
}
/*** 重复关键帧校验:两份 @keyframes 的帧体完全一致 = 复制粘贴的动效,语义上只应存在一份(名字不同也算) ***/
function checkDuplicateKeyframes(pkg) {
const issues = [];
const dir = path.join(ROOT, pkg.dir);
if (!fs.existsSync(dir)) return issues;
for (const name of fs.readdirSync(dir)) {
if (!name.endsWith('.css')) continue; // 只查样式表
const file = path.join(dir, name);
const src = fs.readFileSync(file, 'utf8').replace(/\r\n/g, '\n');
const seen = new Map(); // 帧体(去空白归一)-> 首个声明它的动画名
for (const hit of src.matchAll(/@keyframes\s+([\w-]+)\s*\{([\s\S]*?)\n\}/g)) {
const [, anim, body] = hit;
const norm = body.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, ' ').trim(); // 注释与空白不参与比对
const first = seen.get(norm);
if (first) {
issues.push(`${pkg.dir}/${name}: @keyframes ${anim} 与 ${first} 帧体完全相同(复制粘贴的动效,合并成一个再复用)`);
continue;
}
seen.set(norm, anim);
}
}
return issues;
}
/*** 皮肤包令牌引用校验:var(--ua-*) 必须是 tokens.css 令牌、同文件自定义变量,或组件脚本里 setProperty 的动态变量(防拼错名字静默失效) ***/
function checkTokenRefs(pkg, allowed) {
const issues = [];
@@ -1354,6 +1378,7 @@ function cmdVerify() {
const exports = pkg.api?.exports ?? [];
if (pkg.type === 'skin-dom') {
issues.push(...checkSkinCss(pkg));
issues.push(...checkDuplicateKeyframes(pkg));
issues.push(...checkTokenRefs(pkg, tokenVars));
issues.push(...checkPropsImpl(pkg, comps));
issues.push(...checkPropsDoc(pkg, comps));
@@ -1399,7 +1424,7 @@ function cmdVerify() {
console.error(`${issues.length} violation(s)`);
process.exit(1);
}
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致,视觉回归抽查清单六组件 × 五维度齐全)');
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致、@keyframes 无复制粘贴的重复帧,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致,视觉回归抽查清单六组件 × 五维度齐全)');
}
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/