iter(g219): verify 增加重复关键帧检测
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
- `apps/demo`(@ui-arch/demo):交互演示,产物 apps/demo/dist/demo.html
|
||||
- `apps/docs`:文档站,纯产物(ua docs 生成)
|
||||
|
||||
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify + lint-iterations + audit:visual,提交前必过。其中 `smoke` 是两段内核断言:`@ui-arch/frame` 14 条(信号读写与同值短路、effect 注销与条件分支重追踪、computed 链式派生、多订阅者扇出)+ `@ui-arch/table-core` 21 条(排序 / 筛选 / 分页 / 选择的派生管线);改内核语义前先跑它,别靠组件 demo 肉眼验。`ua audit:visual` 统计皮肤包里五类硬编码视觉字面量(字号 / 动效时长 / 阴影 / 圆角 / 焦点环)并与 `.ua/visual-baseline.json` 做棘轮:任一类超过基线即退出码 1 并指出疑似引入的提交;迁移脚本 `npm run visual:migrate` 可把字面量幂等改写成令牌。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错)、核对 `tokens.json` 的 `color` 键与 tokens.css 的 `--ua-color-*` 同名同值(名字按 camelCase → kebab 折算,亮色值与暗色值都比,双向查漏——只改一处时文档色板会凭空多/少一个令牌,过去只能靠人眼发现),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应(另外查 backlog id 不重复、g 编号严格递增、同一 id 只有一条 done 留痕,且留痕 ts 带 +08:00、时间线不回退、不落在未来——都是过去手工插条目/手写时间戳踩过的坑);配方还要求编号连续、每条都有代码块与要点,且代码块里至少认出一个已注册组件 id(拼错组件名时 `ua recipes` 会把它列成空组件表)。
|
||||
**门禁一条龙**:`npm run check` = build + demo + style + typecheck + smoke + index + verify + lint-iterations + audit:visual,提交前必过。其中 `smoke` 是两段内核断言:`@ui-arch/frame` 14 条(信号读写与同值短路、effect 注销与条件分支重追踪、computed 链式派生、多订阅者扇出)+ `@ui-arch/table-core` 21 条(排序 / 筛选 / 分页 / 选择的派生管线);改内核语义前先跑它,别靠组件 demo 肉眼验。`ua audit:visual` 统计皮肤包里五类硬编码视觉字面量(字号 / 动效时长 / 阴影 / 圆角 / 焦点环)并与 `.ua/visual-baseline.json` 做棘轮:任一类超过基线即退出码 1 并指出疑似引入的提交;迁移脚本 `npm run visual:migrate` 可把字面量幂等改写成令牌。`ua verify` 校验清单 ↔ 文档 ↔ 演示一致(docs.components 的 id 必须在 api.exports 有对应条目、props 必须带 n/t/d、每个文档字段名必须在组件实现源码里真实出现(改名后忘改文档会直接报错)、反向也校验 `src/types.ts` 里 `<组件>Props`(可用清单的 `propsType` 覆盖接口名,`false` 表示无入参)每个字段都在文档 props 表登记(新加字段忘了写文档会报错)、demo 锚点必须在 `apps/demo/main.ts` 中存在,且该组件必须被 demo 真实调用 `id(` / `id.`),检查皮肤包样式表无硬编码颜色(`#hex` 一律禁止,必须走 `--ua-*` 令牌)与令牌引用有效(`var(--ua-*)` 必须是 tokens.css 已定义或组件自设的变量)、核对每个 `--ua-color-*` 都在 `[data-theme='dark']` 里有覆盖值且暗色块没有凭空多出的变量名(新增颜色令牌漏写暗色值直接报错)、核对 `tokens.json` 的 `color` 键与 tokens.css 的 `--ua-color-*` 同名同值(名字按 camelCase → kebab 折算,亮色值与暗色值都比,双向查漏——只改一处时文档色板会凭空多/少一个令牌,过去只能靠人眼发现)、核对 `@keyframes` 没有「帧体完全相同但名字不同」的复制粘贴(同一段动效只该有一份声明,改名复制会让方向 / 时长各写各的,翻转态就出现过「朝反方向入场」),核对 README 的 `<!-- ua:components -->` 区块与「分组:」行都覆盖清单组件集合(漏写/多写/重复都报错),并交叉校验 backlog 的 done 条目与 `.ua/iterations.log` 的 done 留痕一一对应(另外查 backlog id 不重复、g 编号严格递增、同一 id 只有一条 done 留痕,且留痕 ts 带 +08:00、时间线不回退、不落在未来——都是过去手工插条目/手写时间戳踩过的坑);配方还要求编号连续、每条都有代码块与要点,且代码块里至少认出一个已注册组件 id(拼错组件名时 `ua recipes` 会把它列成空组件表)。
|
||||
|
||||
`ua verify` 另外校验 `ITERATION.md` 的《视觉回归抽查清单》:button / textField / select / modal / dataTable / toast 六段各 ≥3 条可勾选要点、每条维度标签必须落在边框 / 阴影 / 过渡 / 焦点环 / 暗色五类内、合计 ≥18 条且五维度全覆盖——视觉类条目收尾前照单过一遍,改清单时别把条目删漏。
|
||||
|
||||
|
||||
@@ -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); /* 给 × 让出位置:文案不会被压在按钮下面 */
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ***/
|
||||
|
||||
Reference in New Issue
Block a user