@qilitt-mickey/vue3-temp-skill 1.1.34 → 1.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +16 -14
  2. package/SKILL.md +4 -2
  3. package/bin/cli.js +18 -4
  4. package/package.json +2 -1
  5. package/references/addon-enable.md +1 -1
  6. package/references/api-check.md +1 -1
  7. package/references/auth-pages.md +1 -1
  8. package/references/base-code-dict.md +1 -1
  9. package/references/chart-echarts.md +1 -1
  10. package/references/code-quality.md +1 -1
  11. package/references/core-kernel.md +1 -1
  12. package/references/crud-pages.md +244 -244
  13. package/references/data-compare.md +1 -1
  14. package/references/data-mapping.md +1 -1
  15. package/references/data-screen.md +1 -1
  16. package/references/data-writeback.md +1 -1
  17. package/references/design-handoff.md +25 -35
  18. package/references/design-system.md +1 -1
  19. package/references/design-verify.md +27 -2
  20. package/references/detail-page.md +1 -1
  21. package/references/directives-advanced.md +7 -7
  22. package/references/download-export.md +1 -1
  23. package/references/feedback-loading.md +1 -1
  24. package/references/feedback-ui.md +1 -1
  25. package/references/file-management.md +7 -7
  26. package/references/flowchart-g6.md +1 -1
  27. package/references/form-advanced.md +1 -1
  28. package/references/graph-relation.md +7 -7
  29. package/references/http-api.md +1 -1
  30. package/references/i18n-localization.md +1 -1
  31. package/references/icons.md +1 -1
  32. package/references/layout-theme.md +1 -1
  33. package/references/mobile-h5.md +1 -1
  34. package/references/particles.md +7 -7
  35. package/references/permission-auth.md +1 -1
  36. package/references/project-inventory.md +1 -1
  37. package/references/qrcode-barcode.md +1 -1
  38. package/references/rich-text.md +1 -1
  39. package/references/seamless-scroll.md +1 -1
  40. package/references/table-vxe.md +1 -1
  41. package/references/tree-table.md +7 -7
  42. package/references/ui-components.md +1 -1
  43. package/references/verify-captcha.md +1 -1
  44. package/references/websocket-realtime.md +7 -7
  45. package/references/wechat-js.md +1 -1
  46. package/references/workflow-bpmn.md +1 -1
  47. package/scripts/check.mjs +6 -0
  48. package/scripts/field-schema-audit.mjs +246 -0
  49. package/scripts/handoff.mjs +388 -22
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * field-schema-audit — 协议字段对账闸门
4
+ *
5
+ * 目的:项目技能(design-handoff.md)里每一条「JSON 来源」引用必须与
6
+ * 设计协议(handoff-output.md)schema 中真实存在的字段一一对应。
7
+ * 引用了协议未定义的字段 = 幽灵字段(ghost),会让校验闸门对照空气、
8
+ * 逼 AI 瞎猜,是还原度上不去的底层硬伤。
9
+ *
10
+ * 原理(确定性、可复现,不以独立 manifest 为真值源,避免漂移):
11
+ * 1. 以「设计协议文档本身」为唯一真值源:解析文档内 ```json 实例块
12
+ * (§2 总览 + §6 完整示例),递归收集所有真实点分路径 → definedPaths。
13
+ * 2. 从项目技能文档提取所有反引号包裹的 `xxx.yyy` 引用路径,并过滤掉
14
+ * 非 JSON 路径(文件名如 dark.scss / 选择器如 html.dark / CSS 段名),
15
+ * 只保留「首段 ∈ 协议顶层 key ∪ 已知简写根」的引用 → referencedPaths。
16
+ * 3. 双向比对:
17
+ * - ghost = 被引用但 definedPaths 中不存在 → 硬失败(exit 1)
18
+ * - unused = 协议定义但项目技能未引用(多为可选/推荐字段)→ 仅告警
19
+ *
20
+ * 匹配规则(容忍项目技能里的简写 / 父级引用):
21
+ * 引用 R 被视为「已覆盖」当且仅当存在定义路径 D 满足:
22
+ * D === R (精确匹配)
23
+ * D.startsWith(R + ".") (R 是父级,如 interactionSpecs.menuActive → .fullWidth)
24
+ * D.endsWith("." + R) (R 是简写,如 frameworkShell.header → structureRequirements.frameworkShell.header)
25
+ *
26
+ * 用法:
27
+ * node scripts/field-schema-audit.mjs
28
+ * node scripts/field-schema-audit.mjs <schema.md> <project.md>
29
+ * node scripts/field-schema-audit.mjs --json # 机器可读摘要
30
+ *
31
+ * 退出码:发现 ghost → 1(可接入发布闸门);否则 0。
32
+ */
33
+
34
+ import fs from "node:fs";
35
+ import path from "node:path";
36
+ import { fileURLToPath } from "node:url";
37
+
38
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
+ const SKILL_ROOT = path.resolve(__dirname, "..");
40
+
41
+ // 默认值:设计协议在兄弟技能 project-ui-design 下;项目技能在本技能 references 下
42
+ const DEFAULT_SCHEMA = path.resolve(SKILL_ROOT, "..", "project-ui-design", "references", "handoff-output.md");
43
+ const DEFAULT_PROJECT = path.resolve(SKILL_ROOT, "references", "design-handoff.md");
44
+
45
+ // 项目技能中使用的「简写根」→ 协议中的完整前缀
46
+ const SHORTHANDS = {
47
+ frameworkShell: "structureRequirements.frameworkShell",
48
+ };
49
+
50
+ function parseArgs(argv) {
51
+ const opts = { json: false, positional: [] };
52
+ for (const a of argv) {
53
+ if (a === "--json") opts.json = true;
54
+ else if (a === "--help" || a === "-h") opts.help = true;
55
+ else opts.positional.push(a);
56
+ }
57
+ const schema = opts.positional[0] || DEFAULT_SCHEMA;
58
+ const project = opts.positional[1] || DEFAULT_PROJECT;
59
+ return { schema, project, json: opts.json, help: opts.help };
60
+ }
61
+
62
+ /** 递归收集对象的所有点分路径(含中间节点)。数组取首个元素代表。 */
63
+ function walk(prefix, val, out) {
64
+ if (val && typeof val === "object" && !Array.isArray(val)) {
65
+ for (const k of Object.keys(val)) {
66
+ const p = prefix ? `${prefix}.${k}` : k;
67
+ out.add(p);
68
+ walk(p, val[k], out);
69
+ }
70
+ } else if (Array.isArray(val)) {
71
+ if (prefix) out.add(prefix); // 数组本身也是一条路径(如 pageStructure.regions)
72
+ if (val.length) walk(prefix, val[0], out);
73
+ } else if (prefix) {
74
+ out.add(prefix);
75
+ }
76
+ }
77
+
78
+ /** 从协议文档解析所有 ```json 实例块,汇总真实字段路径集合 + 顶层根集合。 */
79
+ function extractDefinedPaths(md) {
80
+ const paths = new Set();
81
+ const re = /```json\s*([\s\S]*?)```/g;
82
+ let m;
83
+ let blocks = 0;
84
+ while ((m = re.exec(md)) !== null) {
85
+ blocks++;
86
+ const text = m[1];
87
+ try {
88
+ const obj = JSON.parse(text);
89
+ walk("", obj, paths);
90
+ } catch {
91
+ // 单块解析失败时退化为逐行提取裸 key(容错,不阻塞整体)
92
+ const keyRe = /"([A-Za-z_][\w]*)\s*"/g;
93
+ let km;
94
+ while ((km = keyRe.exec(text)) !== null) paths.add(km[1]);
95
+ }
96
+ }
97
+ const roots = new Set();
98
+ for (const p of paths) roots.add(p.split(".")[0]);
99
+ return { paths, roots, blocks };
100
+ }
101
+
102
+ /** 从项目技能文档提取所有反引号包裹的点分路径,过滤非 JSON 路径(文件名/CSS 选择器等)。 */
103
+ function extractReferencedPaths(md, validRoots) {
104
+ const raw = new Set();
105
+ const re = /`([A-Za-z][\w]*(?:\.[\w]+)+)`/g;
106
+ let m;
107
+ while ((m = re.exec(md)) !== null) raw.add(m[1]);
108
+
109
+ const out = new Set();
110
+ for (const r of raw) {
111
+ const root = r.split(".")[0];
112
+ // 只保留「首段是协议顶层 key 或已知简写根」的引用,剔除 dark.scss / html.dark 等
113
+ if (validRoots.has(root) || Object.prototype.hasOwnProperty.call(SHORTHANDS, root)) {
114
+ out.add(r);
115
+ }
116
+ }
117
+ return out;
118
+ }
119
+
120
+ /** 将简写根展开为协议完整前缀(frameworkShell → structureRequirements.frameworkShell)。 */
121
+ function normalize(ref) {
122
+ const segs = ref.split(".");
123
+ if (Object.prototype.hasOwnProperty.call(SHORTHANDS, segs[0])) {
124
+ return SHORTHANDS[segs[0]] + "." + segs.slice(1).join(".");
125
+ }
126
+ return ref;
127
+ }
128
+
129
+ /** R 是否被定义路径集合覆盖(精确 / 父级 / 简写后缀)。入参均已 normalize。 */
130
+ function isCovered(ref, defined) {
131
+ for (const d of defined) {
132
+ if (d === ref) return true;
133
+ if (d.startsWith(ref + ".")) return true;
134
+ if (d.endsWith("." + ref)) return true;
135
+ }
136
+ return false;
137
+ }
138
+
139
+ /** 为 ghost 字段提供「可能想写的真实字段」建议(末段 / 次段 / 子串命中)。 */
140
+ function suggest(ref, defined) {
141
+ const segs = ref.split(".");
142
+ const leaf = segs[segs.length - 1];
143
+ const parent = segs.length > 1 ? segs[segs.length - 2] : "";
144
+ const hits = new Set();
145
+ for (const d of defined) {
146
+ const ds = d.split(".");
147
+ if (ds[ds.length - 1] === leaf) hits.add(d);
148
+ else if (parent && ds.includes(parent)) hits.add(d);
149
+ else if (ds[ds.length - 1].includes(leaf) && leaf.length >= 4) hits.add(d); // 子串兜底(leaf 足够长避免误命中)
150
+ }
151
+ return [...hits].slice(0, 4);
152
+ }
153
+
154
+ function main() {
155
+ const { schema, project, json, help } = parseArgs(process.argv.slice(2));
156
+
157
+ if (help) {
158
+ console.log("field-schema-audit — 协议字段对账");
159
+ console.log("用法: node scripts/field-schema-audit.mjs [schema.md] [project.md] [--json]");
160
+ process.exit(0);
161
+ }
162
+
163
+ if (!fs.existsSync(schema)) {
164
+ console.error(`x 设计协议文件不存在: ${schema}`);
165
+ process.exit(1);
166
+ }
167
+ if (!fs.existsSync(project)) {
168
+ console.error(`x 项目技能文件不存在: ${project}`);
169
+ process.exit(1);
170
+ }
171
+
172
+ const schemaMd = fs.readFileSync(schema, "utf-8");
173
+ const projectMd = fs.readFileSync(project, "utf-8");
174
+
175
+ const { paths: defined, roots, blocks } = extractDefinedPaths(schemaMd);
176
+ const validRoots = new Set([...roots, ...Object.keys(SHORTHANDS)]);
177
+ const referencedRaw = extractReferencedPaths(projectMd, validRoots);
178
+ const referenced = new Set([...referencedRaw].map(normalize));
179
+
180
+ const ghosts = [];
181
+ const covered = [];
182
+ for (const r of [...referenced].sort()) {
183
+ if (isCovered(r, defined)) covered.push(r);
184
+ else ghosts.push(r);
185
+ }
186
+
187
+ const unused = [...defined].filter((d) => {
188
+ for (const r of referenced) {
189
+ if (d === r || d.startsWith(r + ".") || d.endsWith("." + r)) return false;
190
+ }
191
+ return true;
192
+ });
193
+
194
+ if (json) {
195
+ console.log(
196
+ JSON.stringify(
197
+ {
198
+ schemaFile: schema,
199
+ projectFile: project,
200
+ jsonBlocksParsed: blocks,
201
+ definedCount: defined.size,
202
+ referencedCount: referenced.size,
203
+ ghosts,
204
+ ghostCount: ghosts.length,
205
+ unusedCount: unused.length,
206
+ ok: ghosts.length === 0,
207
+ },
208
+ null,
209
+ 2
210
+ )
211
+ );
212
+ } else {
213
+ console.log("=".repeat(60));
214
+ console.log("协议字段对账 — field-schema-audit");
215
+ console.log("=".repeat(60));
216
+ console.log(`协议文档 : ${schema}`);
217
+ console.log(`项目技能 : ${project}`);
218
+ console.log(`JSON 实例块: ${blocks} 个已解析`);
219
+ console.log(`协议字段数 : ${defined.size}`);
220
+ console.log(`引用字段数 : ${referenced.size}`);
221
+ console.log("");
222
+
223
+ if (ghosts.length === 0) {
224
+ console.log(`OK 幽灵字段: 0 — 项目技能所有 xxx.yyy 引用均在协议中定义`);
225
+ } else {
226
+ console.log(`FAIL 幽灵字段: ${ghosts.length} 个(被引用但协议未定义,硬失败)`);
227
+ console.log("-".repeat(60));
228
+ for (const g of ghosts) {
229
+ const sug = suggest(g, defined);
230
+ const hint = sug.length ? ` -> 可能应为: ${sug.join(" / ")}` : "";
231
+ console.log(` x ${g}${hint}`);
232
+ }
233
+ }
234
+
235
+ console.log("");
236
+ console.log(`~ 未引用字段(协议定义但项目技能未消费,多为可选/推荐,仅提示): ${unused.length} 个`);
237
+ if (unused.length) {
238
+ console.log(" " + unused.sort().join("\n "));
239
+ }
240
+ console.log("");
241
+ }
242
+
243
+ process.exit(ghosts.length === 0 ? 0 : 1);
244
+ }
245
+
246
+ main();