iter(g211): verify 增加令牌双源一致性校验

This commit is contained in:
HIsheR
2026-10-26 19:49:05 +08:00
parent 82fa8c8732
commit fd2e9517d3
2 changed files with 36 additions and 2 deletions
+35 -1
View File
@@ -1023,6 +1023,39 @@ function cmdSyncCounts() {
console.log(`sync-counts: 清单实际 ${actual} 个组件,改写 ${changed} 个文件`);
}
/*** 令牌双源一致性:tokens.json 的 color 键(camelCase)与 tokens.css 的 --ua-color-* 必须同名同值(亮/暗两套都比) ***/
function checkTokenParity(issues) {
const jsonPath = path.join(ROOT, 'packages', 'design', 'tokens', 'tokens.json');
const cssPath = path.join(ROOT, 'packages', 'design', 'tokens', 'tokens.css');
if (!fs.existsSync(jsonPath) || !fs.existsSync(cssPath)) return; // 卫语句:任一缺失交给其它校验兜底
const tokens = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
if (!tokens.color) return; // 卫语句:没有颜色分组(旧版本令牌表)不参与本校验
const src = fs.readFileSync(cssPath, 'utf8').replace(/\r\n/g, '\n');
const grab = (block) => {
const out = new Map(); // cssVar -> 值
for (const m of block.matchAll(/(--ua-[\w-]+)\s*:\s*([^;]+);/g)) out.set(m[1], m[2].trim());
return out;
};
const root = grab((src.match(/:root\s*\{([\s\S]*?)\}/) ?? [, ''])[1]);
const dark = grab((src.match(/\[data-theme='dark'\]\s*\{([\s\S]*?)\}/) ?? [, ''])[1]);
const kebab = (key) => key.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/([a-zA-Z])(\d)/g, '$1-$2').toLowerCase();
const jsonVars = new Set(); // JSON 声明的颜色变量名,用于反查 CSS 里多出来的
for (const [key, pair] of Object.entries(tokens.color)) {
const name = `--ua-color-${kebab(key)}`;
jsonVars.add(name);
if (!root.has(name)) {
issues.push(`tokens: tokens.json 的 color.${key} 在 tokens.css :root 里没有 ${name}(文档色板会列出不存在的令牌)`);
continue; // 名字都不存在,值比对没意义
}
if (root.get(name) !== pair.light) issues.push(`tokens: ${name} 亮色值不一致(tokens.json "${pair.light}" vs tokens.css "${root.get(name)}")`);
if (dark.has(name) && dark.get(name) !== pair.dark) issues.push(`tokens: ${name} 暗色值不一致(tokens.json "${pair.dark}" vs tokens.css "${dark.get(name)}")`);
}
for (const name of root.keys()) {
if (!name.startsWith('--ua-color-')) continue; // 卫语句:只对颜色分组做双向比对
if (!jsonVars.has(name)) issues.push(`tokens: tokens.css 的 ${name} 未在 tokens.json 的 color 里登记(文档色板与 ua tokens 会漏项)`);
}
}
/*** 暗色令牌齐全校验:每个 --ua-color-* 都得在 [data-theme='dark'] 里有覆盖值,反向也查暗色块里的变量先在 :root 定义过(防改名漏改) ***/
function checkDarkTokens(issues) {
const p = path.join(ROOT, 'packages', 'design', 'tokens', 'tokens.css');
@@ -1356,6 +1389,7 @@ function cmdVerify() {
issues.push(...checkCountDocs(skinTotal));
issues.push(...checkReadme(pkgs));
checkDarkTokens(issues);
checkTokenParity(issues);
checkLlmsFull(pkgs, issues);
issues.push(...checkIterations());
issues.push(...checkVisualChecklist());
@@ -1365,7 +1399,7 @@ function cmdVerify() {
console.error(`${issues.length} violation(s)`);
process.exit(1);
}
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致,视觉回归抽查清单六组件 × 五维度齐全)');
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致,README 组件清单与分组同步,backlog ↔ 留痕一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00),组合配方结构完整,MCP 工具清单三处一致,视觉回归抽查清单六组件 × 五维度齐全)');
}
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/