iter(g211): verify 增加令牌双源一致性校验
This commit is contained in:
@@ -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 ***/
|
||||
|
||||
Reference in New Issue
Block a user