iter(g221): verify 校验滚动容器覆盖
This commit is contained in:
@@ -923,6 +923,58 @@ function checkSkinCss(pkg) {
|
|||||||
return issues;
|
return issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 滚动条覆盖校验:凡是 overflow: auto|scroll 的 .ua-* 容器,都必须出现在「scrollbar-width」那条统一规则里 ***/
|
||||||
|
function checkScrollCoverage(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 css = fs.readFileSync(path.join(dir, name), 'utf8').replace(/\r\n/g, '\n').replace(/\/\*[\s\S]*?\*\//g, '');
|
||||||
|
const rules = []; // 括号配平扫出的「选择器 + 规则体」;正则切不了嵌套(@media 里有花括号)
|
||||||
|
let i = 0;
|
||||||
|
let sel = '';
|
||||||
|
while (i < css.length) {
|
||||||
|
if (css[i] === '{') {
|
||||||
|
const start = i + 1;
|
||||||
|
let depth = 1;
|
||||||
|
i += 1;
|
||||||
|
while (i < css.length && depth > 0) {
|
||||||
|
if (css[i] === '{') depth += 1;
|
||||||
|
else if (css[i] === '}') depth -= 1;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
rules.push({ sel: sel.trim(), body: css.slice(start, i - 1) });
|
||||||
|
sel = '';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (css[i] === '}') { sel = ''; i += 1; continue; } // 顶层多余的右括号:重置选择器
|
||||||
|
sel += css[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
const containers = new Set(); // 滚得动的单类容器
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (!/overflow(-[xy])?:\s*(auto|scroll)/.test(rule.body)) continue;
|
||||||
|
for (const part of rule.sel.split(',')) {
|
||||||
|
const hit = part.trim().match(/^\.(ua-[a-z0-9-]+)$/);
|
||||||
|
if (hit) containers.add(hit[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!containers.size) continue;
|
||||||
|
const group = rules.find((rule) => rule.body.includes('scrollbar-width')); // 细条 + 令牌拇指那条统一规则
|
||||||
|
if (!group) {
|
||||||
|
issues.push(`${pkg.dir}/${name}: 未找到滚动条统一规则(scrollbar-width),新增滚动容器会退回系统原生粗条`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const covered = new Set([...group.sel.matchAll(/\.(ua-[a-z0-9-]+)/g)].map((m) => m[1]));
|
||||||
|
for (const cls of containers) {
|
||||||
|
if (covered.has(cls)) continue;
|
||||||
|
issues.push(`${pkg.dir}/${name}: 滚动容器 .${cls} 未纳入滚动条统一规则(暗色下会露出原生亮条)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return issues;
|
||||||
|
}
|
||||||
|
|
||||||
/*** 重复关键帧校验:两份 @keyframes 的帧体完全一致 = 复制粘贴的动效,语义上只应存在一份(名字不同也算) ***/
|
/*** 重复关键帧校验:两份 @keyframes 的帧体完全一致 = 复制粘贴的动效,语义上只应存在一份(名字不同也算) ***/
|
||||||
function checkDuplicateKeyframes(pkg) {
|
function checkDuplicateKeyframes(pkg) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
@@ -1379,6 +1431,7 @@ function cmdVerify() {
|
|||||||
if (pkg.type === 'skin-dom') {
|
if (pkg.type === 'skin-dom') {
|
||||||
issues.push(...checkSkinCss(pkg));
|
issues.push(...checkSkinCss(pkg));
|
||||||
issues.push(...checkDuplicateKeyframes(pkg));
|
issues.push(...checkDuplicateKeyframes(pkg));
|
||||||
|
issues.push(...checkScrollCoverage(pkg));
|
||||||
issues.push(...checkTokenRefs(pkg, tokenVars));
|
issues.push(...checkTokenRefs(pkg, tokenVars));
|
||||||
issues.push(...checkPropsImpl(pkg, comps));
|
issues.push(...checkPropsImpl(pkg, comps));
|
||||||
issues.push(...checkPropsDoc(pkg, comps));
|
issues.push(...checkPropsDoc(pkg, comps));
|
||||||
@@ -1424,7 +1477,7 @@ function cmdVerify() {
|
|||||||
console.error(`${issues.length} violation(s)`);
|
console.error(`${issues.length} violation(s)`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致、@keyframes 无复制粘贴的重复帧,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 ***/
|
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
||||||
|
|||||||
Reference in New Issue
Block a user