iter(g134): 新增 ua sync-counts 自动同步组件计数
This commit is contained in:
@@ -1428,6 +1428,14 @@
|
||||
"acceptance": "treeSelect 点击开弹层、支持搜索过滤、单选/多选、父子联动、清空,弹层沿用 overlay 的 Esc 栈与外部点击收起;样式只消费 --ua-*;文档与演示同步;npm run check 全绿",
|
||||
"status": "done",
|
||||
"notes": "自补:组件补充方向"
|
||||
},
|
||||
{
|
||||
"id": "g134",
|
||||
"title": "组件计数自动同步 ua sync-counts",
|
||||
"motivation": "新增组件要手改 README 标题 / README 包说明 / AGENTS / ARCHITECTURE 四处数字,本轮四次新增每次都靠人工替换;且 README 标题「共 N 个」此前不在 verify 正则里,漏改不报错",
|
||||
"acceptance": "新增 ua sync-counts(幂等,无变化不写盘)把三份文档的「N 个组件 / N 个 DOM 组件 / 共 N 个」统一改写为清单实际数量;verify 的计数正则覆盖 README 标题形态并提示修法;npm run check 全绿",
|
||||
"notes": "自补:构建/门禁工具链改进(实测把 README 标题改成 61 后 verify 能报行号,sync-counts 一键修回)",
|
||||
"status": "open"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@
|
||||
| 5 | `packages/skin/ui/ua.manifest.json` → `api.exports` | 一行 `xxx(props):一句话说明` |
|
||||
| 6 | 同上 → `docs.components` | `{ id, title, demo, props[], example, steps[] }`:props 里每个字段名必须在实现源码里真实出现,且 `<组件>Props` 的字段一个都不能漏(verify 双向校验) |
|
||||
| 7 | `apps/demo/main.ts` | 新增 `section('<demo 锚点>', ...)` 并**真实调用**该组件(verify 会查) |
|
||||
| 8 | `README.md` | `<!-- ua:components -->` 区块补 id、「分组:」行补归组、标题「共 N 个」与包说明里的数字 |
|
||||
| 9 | `AGENTS.md` / `ARCHITECTURE.md` | 两处的「N 个组件」计数同步 |
|
||||
| 8 | `README.md` | `<!-- ua:components -->` 区块补 id、「分组:」行补归组(数字交给下一步自动同步) |
|
||||
| 9 | `npm run sync-counts` | 自动把三份文档里的「N 个组件 / 共 N 个」改成清单实际数量(幂等;漏改时 verify 报行号) |
|
||||
| 10 | `npm run docs` | 重新生成 `llms-full.txt` 与 `apps/docs/index.html`(不跑 verify 会报 llms-full 漂移) |
|
||||
|
||||
改**既有**组件的入参同样要走 5、6、10 三步;只加一个可选参数也要补文档行,否则双向校验失败。
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@
|
||||
"verify": "node packages/tools/cli/bin/ua.js verify",
|
||||
"lint-iterations": "node packages/tools/cli/bin/ua.js lint-iterations",
|
||||
"changelog": "node packages/tools/cli/bin/ua.js changelog",
|
||||
"changelog:check": "node packages/tools/cli/bin/ua.js changelog --check"
|
||||
"changelog:check": "node packages/tools/cli/bin/ua.js changelog --check",
|
||||
"sync-counts": "node packages/tools/cli/bin/ua.js sync-counts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.0"
|
||||
|
||||
@@ -627,7 +627,10 @@ function styleCheckFile(file, src) {
|
||||
let j = i - 1;
|
||||
while (j >= 0 && lines[j].trim() === '') j--; // 找上一个非空行
|
||||
if (j < 0 || !lines[j].includes('/***')) {
|
||||
issues.push(`${file}:${i + 1} 导出 ${decl[1]} 缺少 /*** 作用 ***/ 注释`);
|
||||
// 上一行是注释续行(以 ***/ 结尾却没有 /***)说明写成了多行,给出可直接照做的修法
|
||||
const cont = j >= 0 && lines[j].includes('***/') && !lines[j].includes('/***');
|
||||
const hint = cont ? '(注释被拆成多行:压成单行 /*** 作用 ***/ 并紧贴导出上一行)' : '';
|
||||
issues.push(`${file}:${i + 1} 导出 ${decl[1]} 缺少 /*** 作用 ***/ 注释${hint}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,22 +752,46 @@ function checkReadme(pkgs) {
|
||||
}
|
||||
|
||||
/*** 组件数漂移校验:AGENTS.md / ARCHITECTURE.md 中的「N 个组件」必须与 skin-dom 清单实际总数一致 ***/
|
||||
// 组件计数的三种写法:「N 个组件」「N 个 DOM 组件」「共 N 个」
|
||||
const COUNT_RE = /(\d+)\s*个\s*(?:DOM\s*)?组件|共\s*(\d+)\s*个/g;
|
||||
|
||||
/*** 逐行扫文档里的组件计数,返回 { line, value, text } 便于校验与改写共用 ***/
|
||||
function scanCountLines(text) {
|
||||
const hits = [];
|
||||
text.split(/\r?\n/).forEach((line, i) => {
|
||||
for (const m of line.matchAll(COUNT_RE)) hits.push({ line: i + 1, value: Number(m[1] ?? m[2]), text: line });
|
||||
});
|
||||
return hits;
|
||||
}
|
||||
|
||||
function checkCountDocs(actual) {
|
||||
const issues = [];
|
||||
const re = /(\d+)\s*个(?:\s*DOM\s*)?组件/;
|
||||
for (const name of ['AGENTS.md', 'ARCHITECTURE.md', 'README.md']) {
|
||||
const file = path.join(ROOT, name);
|
||||
if (!fs.existsSync(file)) continue; // 卫语句:文档缺失不校验
|
||||
fs.readFileSync(file, 'utf8').split(/\r?\n/).forEach((line, i) => {
|
||||
const m = line.match(re);
|
||||
if (m && Number(m[1]) !== actual) {
|
||||
issues.push(`${name}:${i + 1} 组件数「${m[1]}」与清单实际 ${actual} 不一致,请同步`);
|
||||
for (const hit of scanCountLines(fs.readFileSync(file, 'utf8'))) {
|
||||
if (hit.value !== actual) issues.push(`${name}:${hit.line} 组件数「${hit.value}」与清单实际 ${actual} 不一致,跑 ua sync-counts 自动同步`);
|
||||
}
|
||||
});
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
/*** 组件计数同步:把三份文档里的「N 个组件 / 共 N 个」一律改成清单实际数量(幂等,无变化不写盘) ***/
|
||||
function cmdSyncCounts() {
|
||||
const actual = collectComponents(collectManifests()).length;
|
||||
let changed = 0;
|
||||
for (const name of ['AGENTS.md', 'ARCHITECTURE.md', 'README.md']) {
|
||||
const file = path.join(ROOT, name);
|
||||
if (!fs.existsSync(file)) continue; // 卫语句:文档缺失跳过
|
||||
const before = fs.readFileSync(file, 'utf8');
|
||||
const after = before.replace(COUNT_RE, (m, a, b) => (a ? m.replace(a, String(actual)) : m.replace(b, String(actual))));
|
||||
if (after === before) continue;
|
||||
fs.writeFileSync(file, after, 'utf8');
|
||||
changed += 1;
|
||||
}
|
||||
console.log(`sync-counts: 清单实际 ${actual} 个组件,改写 ${changed} 个文件`);
|
||||
}
|
||||
|
||||
/*** llms-full.txt 同步校验:内联重渲染 Agent 文档(时间戳归一化)与落盘产物比对,防 manifest 改后忘跑 docs ***/
|
||||
function checkLlmsFull(pkgs, issues) {
|
||||
if (!fs.existsSync(LLMS_FULL)) return; // 卫语句:产物缺失不算漂移(可能是新仓库)
|
||||
@@ -1101,6 +1128,9 @@ switch (cmd) {
|
||||
case 'style':
|
||||
cmdStyle();
|
||||
break;
|
||||
case 'sync-counts':
|
||||
cmdSyncCounts();
|
||||
break;
|
||||
case 'verify':
|
||||
cmdVerify();
|
||||
break;
|
||||
@@ -1120,6 +1150,6 @@ switch (cmd) {
|
||||
cmdMcp();
|
||||
break;
|
||||
default:
|
||||
console.log('usage: ua <index|list|info <name>|docs|tokens [--json]|style|verify|commit-lint|lint-iterations|changelog|mcp>');
|
||||
console.log('usage: ua <index|list|info <name>|docs|tokens [--json]|style|sync-counts|verify|commit-lint|lint-iterations|changelog|mcp>');
|
||||
process.exit(cmd ? 1 : 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user