wucaishi-generative-react-skill 0.1.0

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.
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ function fail(message, code = 1) {
7
+ console.error(`错误:${message}`);
8
+ process.exit(code);
9
+ }
10
+
11
+ function parseArgs(argv) {
12
+ const args = {
13
+ contract: null,
14
+ module: null,
15
+ manifest: null,
16
+ schema: null,
17
+ };
18
+
19
+ for (let i = 0; i < argv.length; i += 1) {
20
+ const arg = argv[i];
21
+ const nextValue = () => {
22
+ const value = argv[i + 1];
23
+ if (!value || value.startsWith("--")) {
24
+ fail(`${arg} 缺少参数值`);
25
+ }
26
+ i += 1;
27
+ return value;
28
+ };
29
+
30
+ if (arg === "-h" || arg === "--help") {
31
+ printHelp();
32
+ process.exit(0);
33
+ } else if (arg === "--contract") {
34
+ args.contract = nextValue();
35
+ } else if (arg === "--module") {
36
+ args.module = nextValue();
37
+ } else if (arg === "--manifest") {
38
+ args.manifest = nextValue();
39
+ } else if (arg === "--schema") {
40
+ args.schema = nextValue();
41
+ } else {
42
+ fail(`未知参数:${arg}`);
43
+ }
44
+ }
45
+
46
+ for (const key of ["contract", "module", "manifest", "schema"]) {
47
+ if (!args[key]) {
48
+ fail(`必须传入 --${key}`);
49
+ }
50
+ }
51
+
52
+ return args;
53
+ }
54
+
55
+ function printHelp() {
56
+ console.log(`usage: validate_component_contract.mjs --contract CONTRACT_JSON --module HTML_ID_OR_NAME --manifest MANIFEST_JSON --schema SCHEMA_JSON
57
+
58
+ 校验组件 manifest/schema 是否遵守模板说明解析出的字段契约。`);
59
+ }
60
+
61
+ function propsArray(manifest) {
62
+ return Array.isArray(manifest?.props) ? manifest.props : [];
63
+ }
64
+
65
+ function schemaField(schema, propName) {
66
+ if (!schema || typeof schema !== "object") {
67
+ return undefined;
68
+ }
69
+ if (schema[propName]) {
70
+ return schema[propName];
71
+ }
72
+ if (schema.properties?.[propName]) {
73
+ return schema.properties[propName];
74
+ }
75
+ if (schema.fields?.[propName]) {
76
+ return schema.fields[propName];
77
+ }
78
+ return undefined;
79
+ }
80
+
81
+ function propByName(manifest, propName) {
82
+ return propsArray(manifest).find((prop) => prop?.name === propName);
83
+ }
84
+
85
+ function hasExpectedFixedValue(value, expected) {
86
+ if (value == null) {
87
+ return false;
88
+ }
89
+ if (value.default === expected || value.const === expected || value.fixed === expected) {
90
+ return true;
91
+ }
92
+ return Array.isArray(value.enum) && value.enum.length === 1 && value.enum[0] === expected;
93
+ }
94
+
95
+ function hasExpectedNumber(value, key, expected) {
96
+ return value && Number(value[key]) === expected;
97
+ }
98
+
99
+ function includesEnum(value, expectedValues) {
100
+ if (!Array.isArray(value?.enum)) {
101
+ return false;
102
+ }
103
+ return expectedValues.every((item) => value.enum.includes(item));
104
+ }
105
+
106
+ export function validateComponentContract({ contract, manifest, schema }) {
107
+ const errors = [];
108
+ const warnings = [];
109
+
110
+ if (!contract || typeof contract !== "object") {
111
+ return { errors: ["缺少 contract"], warnings };
112
+ }
113
+
114
+ if (manifest?.name !== contract.componentDisplayName) {
115
+ errors.push(
116
+ `manifest.name 必须等于场景模块名称:${contract.componentDisplayName}`,
117
+ );
118
+ }
119
+
120
+ if (manifest?.description !== contract.componentDescription) {
121
+ errors.push(
122
+ `manifest.description 必须等于用户要解决的问题:${contract.componentDescription}`,
123
+ );
124
+ }
125
+
126
+ for (const field of contract.fields || []) {
127
+ const propName = field.propName;
128
+ const manifestProp = propByName(manifest, propName);
129
+ const schemaProp = schemaField(schema, propName);
130
+
131
+ if (!manifestProp || !schemaProp) {
132
+ errors.push(`缺少字段:${propName}(来源字段:${field.name})`);
133
+ continue;
134
+ }
135
+
136
+ const constraints = contract.constraints?.[propName] || {};
137
+ if (constraints.fixed) {
138
+ if (!hasExpectedFixedValue(manifestProp, constraints.fixed)) {
139
+ errors.push(`manifest.props.${propName} 必须固定为:${constraints.fixed}`);
140
+ }
141
+ if (!hasExpectedFixedValue(schemaProp, constraints.fixed)) {
142
+ errors.push(`schema.${propName} 必须固定为:${constraints.fixed}`);
143
+ }
144
+ }
145
+
146
+ if (constraints.maxLength) {
147
+ if (!hasExpectedNumber(manifestProp, "maxLength", constraints.maxLength)) {
148
+ errors.push(`manifest.props.${propName}.maxLength 必须为 ${constraints.maxLength}`);
149
+ }
150
+ if (!hasExpectedNumber(schemaProp, "maxLength", constraints.maxLength)) {
151
+ errors.push(`schema.${propName}.maxLength 必须为 ${constraints.maxLength}`);
152
+ }
153
+ }
154
+
155
+ if (constraints.maxItems) {
156
+ if (!hasExpectedNumber(manifestProp, "maxItems", constraints.maxItems)) {
157
+ errors.push(`manifest.props.${propName}.maxItems 必须为 ${constraints.maxItems}`);
158
+ }
159
+ if (!hasExpectedNumber(schemaProp, "maxItems", constraints.maxItems)) {
160
+ errors.push(`schema.${propName}.maxItems 必须为 ${constraints.maxItems}`);
161
+ }
162
+ }
163
+
164
+ if (constraints.enum) {
165
+ if (!includesEnum(manifestProp, constraints.enum)) {
166
+ errors.push(`manifest.props.${propName}.enum 必须包含:${constraints.enum.join("/")}`);
167
+ }
168
+ if (!includesEnum(schemaProp, constraints.enum)) {
169
+ errors.push(`schema.${propName}.enum 必须包含:${constraints.enum.join("/")}`);
170
+ }
171
+ }
172
+ }
173
+
174
+ return { errors, warnings };
175
+ }
176
+
177
+ async function readJson(path) {
178
+ return JSON.parse(await readFile(path, "utf8"));
179
+ }
180
+
181
+ function findContractModule(contractDoc, moduleKey) {
182
+ const modules = Array.isArray(contractDoc?.modules) ? contractDoc.modules : [];
183
+ return modules.find(
184
+ (item) =>
185
+ item.htmlId === moduleKey ||
186
+ item.sceneModuleName === moduleKey ||
187
+ item.componentDisplayName === moduleKey,
188
+ );
189
+ }
190
+
191
+ async function main() {
192
+ const args = parseArgs(process.argv.slice(2));
193
+ const [contractDoc, manifest, schema] = await Promise.all([
194
+ readJson(args.contract),
195
+ readJson(args.manifest),
196
+ readJson(args.schema),
197
+ ]);
198
+ const contract = findContractModule(contractDoc, args.module);
199
+ if (!contract) {
200
+ fail(`契约文件中找不到模块:${args.module}`);
201
+ }
202
+
203
+ const result = validateComponentContract({ contract, manifest, schema });
204
+ if (result.errors.length > 0) {
205
+ console.error("组件契约校验失败:");
206
+ for (const error of result.errors) {
207
+ console.error(`- ${error}`);
208
+ }
209
+ process.exit(1);
210
+ }
211
+
212
+ console.log("组件契约校验通过。");
213
+ }
214
+
215
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
216
+ main().catch((error) => fail(error.stack || error.message || String(error)));
217
+ }