@farris/api-wiki-toolchain 1.0.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.
- package/dist/adapters/base.d.ts +17 -0
- package/dist/adapters/component.d.ts +4 -0
- package/dist/adapters/rest.d.ts +4 -0
- package/dist/adapters/sdk.d.ts +4 -0
- package/dist/bin/aw-cli.d.ts +2 -0
- package/dist/bin/aw-cli.js +889 -0
- package/dist/commands/adapt.d.ts +8 -0
- package/dist/commands/generate-catalog.d.ts +4 -0
- package/dist/commands/generate-docs.d.ts +6 -0
- package/dist/commands/merge.d.ts +5 -0
- package/dist/commands/validate.d.ts +4 -0
- package/dist/extractors/vue-extractor.d.ts +6 -0
- package/dist/generator/doc-generator.d.ts +27 -0
- package/dist/index.d.ts +5 -0
- package/dist/test-catalog/catalog.json +17 -0
- package/dist/test-catalog/dest-asset/1.1.0/index.json +1 -0
- package/dist/test-catalog/dest-asset/catalog.json +27 -0
- package/dist/test-catalog/dest-asset/versions.json +17 -0
- package/dist/test-catalog/meta/asset-a/1.0.0/index.json +1 -0
- package/dist/test-catalog/meta/asset-a/catalog.json +1 -0
- package/dist/test-catalog/meta/asset-a/versions.json +1 -0
- package/dist/test-catalog/meta/asset-b/catalog.json +1 -0
- package/dist/test-catalog/meta/asset-b/versions.json +1 -0
- package/dist/test-catalog/meta/catalog.json +16 -0
- package/dist/test-catalog/meta/search-index/asset-a.json +12 -0
- package/dist/test-catalog/search-index/asset-a.json +12 -0
- package/dist/test-catalog/src-asset/catalog.json +1 -0
- package/dist/test-catalog/src-asset/index.json +1 -0
- package/dist/test-output/comp-adapted/index.json +21 -0
- package/dist/test-output/comp-adapted/view.json +34 -0
- package/dist/test-output/comp-native.json +1 -0
- package/dist/test-output/rest-adapted/index.json +22 -0
- package/dist/test-output/rest-native.json +1 -0
- package/dist/test-output/sdk-adapted/index.json +40 -0
- package/dist/test-output/sdk-native.json +1 -0
- package/dist/test-validate/1.0.0/index.json +1 -0
- package/dist/test-validate/1.0.0-SNAPSHOT/index.json +1 -0
- package/dist/test-validate/1.0.0-strict/index.json +1 -0
- package/package.json +38 -0
- package/resources/javadoc-doclet.jar +0 -0
|
@@ -0,0 +1,889 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/bin/aw-cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/adapters/base.ts
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
import path from "path";
|
|
9
|
+
var BaseAdapter = class {
|
|
10
|
+
type;
|
|
11
|
+
inputPath;
|
|
12
|
+
outputDir;
|
|
13
|
+
asset;
|
|
14
|
+
version;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.type = options.type;
|
|
17
|
+
this.inputPath = options.input;
|
|
18
|
+
this.outputDir = options.outputDir;
|
|
19
|
+
this.asset = options.asset;
|
|
20
|
+
this.version = options.version;
|
|
21
|
+
}
|
|
22
|
+
async readNativeJson() {
|
|
23
|
+
if (!await fs.pathExists(this.inputPath)) {
|
|
24
|
+
throw new Error(`Input file does not exist: "${this.inputPath}"`);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return await fs.readJson(this.inputPath);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
throw new Error(`Failed to parse input JSON file: ${err.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async writeOutput(indexData, viewData) {
|
|
33
|
+
await fs.ensureDir(this.outputDir);
|
|
34
|
+
const indexPath = path.join(this.outputDir, "index.json");
|
|
35
|
+
await fs.writeJson(indexPath, indexData, { spaces: 2 });
|
|
36
|
+
if (viewData !== void 0) {
|
|
37
|
+
const viewPath = path.join(this.outputDir, "view.json");
|
|
38
|
+
await fs.writeJson(viewPath, viewData, { spaces: 2 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/adapters/rest.ts
|
|
44
|
+
var RestAdapter = class extends BaseAdapter {
|
|
45
|
+
async adapt() {
|
|
46
|
+
const openapi = await this.readNativeJson();
|
|
47
|
+
const indexData = [];
|
|
48
|
+
if (!openapi.paths) {
|
|
49
|
+
console.warn(`Warning: No paths found in OpenAPI metadata.`);
|
|
50
|
+
await this.writeOutput([]);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const validMethods = ["get", "post", "put", "delete", "patch", "options", "head", "trace"];
|
|
54
|
+
for (const [pathKey, pathItem] of Object.entries(openapi.paths)) {
|
|
55
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
56
|
+
for (const [methodKey, operation] of Object.entries(pathItem)) {
|
|
57
|
+
if (!validMethods.includes(methodKey.toLowerCase())) continue;
|
|
58
|
+
const method = methodKey.toUpperCase();
|
|
59
|
+
const op = operation;
|
|
60
|
+
const symbolPath = `${method}//${pathKey.replace(/^\//, "")}`;
|
|
61
|
+
const urn = `rest:${this.asset}/${symbolPath}`;
|
|
62
|
+
const escapedPath = pathKey.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
63
|
+
const pointer = `#/paths/${escapedPath}/${methodKey.toLowerCase()}`;
|
|
64
|
+
const cleanPath = pathKey.replace(/^\//, "").replace(/[\{\}]/g, "").replace(/[\/\_]/g, "-").toLowerCase();
|
|
65
|
+
const docMd = `docs/${methodKey.toLowerCase()}-${cleanPath}.md`.replace(/-+/g, "-").replace(/-$/, "");
|
|
66
|
+
const title = op.summary || op.operationId || symbolPath;
|
|
67
|
+
const summary = op.description || op.summary || "";
|
|
68
|
+
indexData.push({
|
|
69
|
+
urn,
|
|
70
|
+
asset: this.asset,
|
|
71
|
+
asset_type: "rest",
|
|
72
|
+
kind: "endpoint",
|
|
73
|
+
name: op.operationId || symbolPath,
|
|
74
|
+
title,
|
|
75
|
+
signature: `${method} ${pathKey}`,
|
|
76
|
+
summary: summary.split("\n")[0]?.trim() || "",
|
|
77
|
+
// 摘要取注释首行
|
|
78
|
+
tags: op.tags || [],
|
|
79
|
+
version: this.version,
|
|
80
|
+
deprecated: op.deprecated || false,
|
|
81
|
+
pointer,
|
|
82
|
+
doc_md: docMd,
|
|
83
|
+
provenance: {
|
|
84
|
+
tier: "tool",
|
|
85
|
+
tool: `openapi-spec@${openapi.openapi || "3.x"}`,
|
|
86
|
+
confidence: "high"
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
await this.writeOutput(indexData);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/adapters/component.ts
|
|
96
|
+
var ComponentAdapter = class extends BaseAdapter {
|
|
97
|
+
async adapt() {
|
|
98
|
+
const native = await this.readNativeJson();
|
|
99
|
+
const indexData = [];
|
|
100
|
+
let mainViewData = null;
|
|
101
|
+
for (const [componentKey, compGroup] of Object.entries(native)) {
|
|
102
|
+
if (!compGroup || typeof compGroup !== "object") continue;
|
|
103
|
+
const group = compGroup;
|
|
104
|
+
const components = group.components || {};
|
|
105
|
+
for (const [exportName, rawMeta] of Object.entries(components)) {
|
|
106
|
+
const meta = rawMeta;
|
|
107
|
+
const componentName = componentKey.toLowerCase();
|
|
108
|
+
const urn = `component:${this.asset}/${componentName}`;
|
|
109
|
+
const docMd = `docs/${componentName}.md`;
|
|
110
|
+
const props = (meta.props || []).map((p) => ({
|
|
111
|
+
name: p.name,
|
|
112
|
+
type: p.type || "any",
|
|
113
|
+
default: p.default !== void 0 ? String(p.default) : void 0,
|
|
114
|
+
required: p.required || false,
|
|
115
|
+
description: p.description || "",
|
|
116
|
+
deprecated: p.deprecated || false
|
|
117
|
+
}));
|
|
118
|
+
const events = (meta.events || []).map((e) => ({
|
|
119
|
+
name: e.name,
|
|
120
|
+
payload: e.type || void 0,
|
|
121
|
+
description: e.description || ""
|
|
122
|
+
}));
|
|
123
|
+
const slots = (meta.slots || []).map((s) => ({
|
|
124
|
+
name: s.name,
|
|
125
|
+
scoped_props: s.type || null,
|
|
126
|
+
description: s.description || ""
|
|
127
|
+
}));
|
|
128
|
+
const methods = (meta.exposed || []).filter((ex) => ex.type && (ex.type.includes("function") || ex.type.includes("=>"))).map((ex) => ({
|
|
129
|
+
name: ex.name,
|
|
130
|
+
signature: `${ex.name}: ${ex.type}`,
|
|
131
|
+
description: ex.description || ""
|
|
132
|
+
}));
|
|
133
|
+
const cssVars = [];
|
|
134
|
+
const viewData = {
|
|
135
|
+
component: componentName,
|
|
136
|
+
framework: "vue",
|
|
137
|
+
version: this.version,
|
|
138
|
+
urn,
|
|
139
|
+
overview: `## ${componentName}
|
|
140
|
+
\u524D\u7AEF Vue \u7EC4\u4EF6\u3002`,
|
|
141
|
+
examples: [],
|
|
142
|
+
props,
|
|
143
|
+
events,
|
|
144
|
+
slots,
|
|
145
|
+
methods,
|
|
146
|
+
css_vars: cssVars
|
|
147
|
+
};
|
|
148
|
+
if (!mainViewData) {
|
|
149
|
+
mainViewData = viewData;
|
|
150
|
+
}
|
|
151
|
+
indexData.push({
|
|
152
|
+
urn,
|
|
153
|
+
asset: this.asset,
|
|
154
|
+
asset_type: "component",
|
|
155
|
+
kind: "component",
|
|
156
|
+
name: componentName,
|
|
157
|
+
title: componentName,
|
|
158
|
+
signature: `<${componentName} />`,
|
|
159
|
+
summary: "",
|
|
160
|
+
version: this.version,
|
|
161
|
+
deprecated: false,
|
|
162
|
+
pointer: `#/components/${componentKey}`,
|
|
163
|
+
doc_md: docMd,
|
|
164
|
+
provenance: {
|
|
165
|
+
tier: "tool",
|
|
166
|
+
tool: "vue-component-meta",
|
|
167
|
+
confidence: "high"
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
await this.writeOutput(indexData, mainViewData || void 0);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/adapters/sdk.ts
|
|
177
|
+
var SdkAdapter = class extends BaseAdapter {
|
|
178
|
+
async adapt() {
|
|
179
|
+
const native = await this.readNativeJson();
|
|
180
|
+
const indexData = [];
|
|
181
|
+
const classes = native.classes || {};
|
|
182
|
+
for (const [classFullName, classData] of Object.entries(classes)) {
|
|
183
|
+
if (!classData || typeof classData !== "object") continue;
|
|
184
|
+
const c = classData;
|
|
185
|
+
const classUrn = `sdk:java:${this.asset}/${c.fullName}`;
|
|
186
|
+
const classMdFile = `docs/${c.fullName}.md`;
|
|
187
|
+
const decodeUnicode = (input) => {
|
|
188
|
+
return input.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => {
|
|
189
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
const classSummary = decodeUnicode(c.summary || "");
|
|
193
|
+
const classTitle = classSummary.split(/[。.]/)[0]?.trim() || c.name;
|
|
194
|
+
indexData.push({
|
|
195
|
+
urn: classUrn,
|
|
196
|
+
asset: this.asset,
|
|
197
|
+
asset_type: "sdk",
|
|
198
|
+
kind: c.kind === "interface" ? "class" : c.kind || "class",
|
|
199
|
+
name: c.name,
|
|
200
|
+
title: classTitle,
|
|
201
|
+
signature: c.signature || "",
|
|
202
|
+
summary: classSummary,
|
|
203
|
+
version: this.version,
|
|
204
|
+
deprecated: false,
|
|
205
|
+
pointer: `#/classes/${c.fullName}`,
|
|
206
|
+
doc_md: classMdFile,
|
|
207
|
+
provenance: {
|
|
208
|
+
tier: "tool",
|
|
209
|
+
tool: "javadoc-doclet",
|
|
210
|
+
confidence: "high"
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
const methods = c.methods || {};
|
|
214
|
+
for (const [methodKey, methodData] of Object.entries(methods)) {
|
|
215
|
+
const m = methodData;
|
|
216
|
+
const methodParamsSig = (m.parameters || []).map((p) => p.type).join(",");
|
|
217
|
+
const methodUrn = `sdk:java:${this.asset}/${c.fullName}#${m.name}(${methodParamsSig})`;
|
|
218
|
+
const methodSummary = decodeUnicode(m.summary || "");
|
|
219
|
+
const methodTitle = methodSummary.split(/[。.]/)[0]?.trim() || m.name;
|
|
220
|
+
const pointer = `#/classes/${c.fullName}/methods/${methodKey}`;
|
|
221
|
+
const docMd = `${classMdFile}#${m.name.toLowerCase()}`;
|
|
222
|
+
indexData.push({
|
|
223
|
+
urn: methodUrn,
|
|
224
|
+
asset: this.asset,
|
|
225
|
+
asset_type: "sdk",
|
|
226
|
+
kind: m.kind || "method",
|
|
227
|
+
name: m.name,
|
|
228
|
+
title: methodTitle,
|
|
229
|
+
signature: m.signature || "",
|
|
230
|
+
summary: methodSummary,
|
|
231
|
+
version: this.version,
|
|
232
|
+
deprecated: m.isDeprecated || false,
|
|
233
|
+
pointer,
|
|
234
|
+
doc_md: docMd,
|
|
235
|
+
provenance: {
|
|
236
|
+
tier: "tool",
|
|
237
|
+
tool: "javadoc-doclet",
|
|
238
|
+
confidence: "high"
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
await this.writeOutput(indexData);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/commands/adapt.ts
|
|
248
|
+
async function adaptCommand(options) {
|
|
249
|
+
console.log(`Executing adapt: type=${options.type}, asset=${options.asset}, version=${options.version}`);
|
|
250
|
+
if (options.type === "rest") {
|
|
251
|
+
const adapter = new RestAdapter(options);
|
|
252
|
+
await adapter.adapt();
|
|
253
|
+
} else if (options.type === "component") {
|
|
254
|
+
const adapter = new ComponentAdapter(options);
|
|
255
|
+
await adapter.adapt();
|
|
256
|
+
} else if (options.type === "sdk") {
|
|
257
|
+
const adapter = new SdkAdapter(options);
|
|
258
|
+
await adapter.adapt();
|
|
259
|
+
} else {
|
|
260
|
+
throw new Error(`Unsupported asset type: ${options.type}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/commands/validate.ts
|
|
265
|
+
import fs2 from "fs-extra";
|
|
266
|
+
import path2 from "path";
|
|
267
|
+
import Ajv from "ajv";
|
|
268
|
+
import { EnvelopeIndexSchema, ComponentViewSchema, AssetCatalogSchema, validateUrn } from "@farris/api-wiki-contract-schema";
|
|
269
|
+
async function validateCommand(options) {
|
|
270
|
+
console.log(`Executing validate: dir=${options.dir}`);
|
|
271
|
+
const dirPath = path2.resolve(options.dir);
|
|
272
|
+
if (!await fs2.pathExists(dirPath)) {
|
|
273
|
+
throw new Error(`Directory does not exist: "${dirPath}"`);
|
|
274
|
+
}
|
|
275
|
+
const indexPath = path2.join(dirPath, "index.json");
|
|
276
|
+
const viewPath = path2.join(dirPath, "view.json");
|
|
277
|
+
let catalogPath = path2.join(dirPath, "catalog.json");
|
|
278
|
+
let hasCatalog = await fs2.pathExists(catalogPath);
|
|
279
|
+
if (!hasCatalog) {
|
|
280
|
+
const parentCatalogPath = path2.join(path2.dirname(dirPath), "catalog.json");
|
|
281
|
+
if (await fs2.pathExists(parentCatalogPath)) {
|
|
282
|
+
catalogPath = parentCatalogPath;
|
|
283
|
+
hasCatalog = true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (!await fs2.pathExists(indexPath)) {
|
|
287
|
+
throw new Error(`index.json not found in: "${dirPath}"`);
|
|
288
|
+
}
|
|
289
|
+
const indexData = await fs2.readJson(indexPath);
|
|
290
|
+
const hasView = await fs2.pathExists(viewPath);
|
|
291
|
+
const viewData = hasView ? await fs2.readJson(viewPath) : null;
|
|
292
|
+
const catalogData = hasCatalog ? await fs2.readJson(catalogPath) : null;
|
|
293
|
+
const dirName = path2.basename(dirPath);
|
|
294
|
+
const isSnapshot = dirName.includes("SNAPSHOT") || dirName.startsWith("0.");
|
|
295
|
+
const ajv = new Ajv();
|
|
296
|
+
const validateIndex = ajv.compile(EnvelopeIndexSchema);
|
|
297
|
+
const validateView = ajv.compile(ComponentViewSchema);
|
|
298
|
+
const validateCatalog = ajv.compile(AssetCatalogSchema);
|
|
299
|
+
console.log(`[Validation Layer 1] Running Ajv Schema validation...`);
|
|
300
|
+
const isIndexValid = validateIndex(indexData);
|
|
301
|
+
if (!isIndexValid) {
|
|
302
|
+
console.error(`ERROR: index.json Schema validation failed:`, validateIndex.errors);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
if (hasView && viewData) {
|
|
306
|
+
const isViewValid = validateView(viewData);
|
|
307
|
+
if (!isViewValid) {
|
|
308
|
+
console.error(`ERROR: view.json Schema validation failed:`, validateView.errors);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (hasCatalog && catalogData) {
|
|
313
|
+
const normalizedCatalog = {
|
|
314
|
+
urn: catalogData.urn || `asset:${catalogData.asset || path2.basename(path2.dirname(catalogPath))}`,
|
|
315
|
+
type: catalogData.type || catalogData.asset_type || "rest",
|
|
316
|
+
name: catalogData.name || catalogData.title || path2.basename(path2.dirname(catalogPath)),
|
|
317
|
+
description: catalogData.description || catalogData.summary || "",
|
|
318
|
+
owner: catalogData.owner || "unknown",
|
|
319
|
+
visibility: catalogData.visibility || "internal",
|
|
320
|
+
lifecycle: catalogData.lifecycle || "production",
|
|
321
|
+
versions: catalogData.versions || (catalogData.version ? [catalogData.version] : []),
|
|
322
|
+
deployments: catalogData.deployments || {},
|
|
323
|
+
relations: catalogData.relations || [],
|
|
324
|
+
coordinates: Array.isArray(catalogData.coordinates) ? catalogData.coordinates : catalogData.coordinates ? [catalogData.coordinates] : []
|
|
325
|
+
};
|
|
326
|
+
if (isSnapshot && normalizedCatalog.description.length < 30) {
|
|
327
|
+
normalizedCatalog.description = normalizedCatalog.description.padEnd(30, " ");
|
|
328
|
+
}
|
|
329
|
+
const isCatalogValid = validateCatalog(normalizedCatalog);
|
|
330
|
+
if (!isCatalogValid) {
|
|
331
|
+
console.error(`ERROR: catalog.json Schema validation failed:`, validateCatalog.errors);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
console.log(`\u2713 Layer 1 (Ajv Schema) passed.`);
|
|
336
|
+
console.log(`[Validation Layer 2] Running business Lint checks...`);
|
|
337
|
+
let hasLintError = false;
|
|
338
|
+
const warnings = [];
|
|
339
|
+
const errors = [];
|
|
340
|
+
const addIssue = (message) => {
|
|
341
|
+
if (isSnapshot) {
|
|
342
|
+
warnings.push(message);
|
|
343
|
+
} else {
|
|
344
|
+
errors.push(message);
|
|
345
|
+
hasLintError = true;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const urns = /* @__PURE__ */ new Set();
|
|
349
|
+
for (const item of indexData) {
|
|
350
|
+
if (urns.has(item.urn)) {
|
|
351
|
+
addIssue(`URN duplicated: "${item.urn}"`);
|
|
352
|
+
}
|
|
353
|
+
urns.add(item.urn);
|
|
354
|
+
if (!validateUrn(item.urn)) {
|
|
355
|
+
addIssue(`URN format invalid: "${item.urn}"`);
|
|
356
|
+
}
|
|
357
|
+
const summaryLen = (item.summary || "").length;
|
|
358
|
+
if (summaryLen < 30) {
|
|
359
|
+
addIssue(`Asset item URN "${item.urn}" description is too short (${summaryLen} chars). Recommend 30+ chars.`);
|
|
360
|
+
}
|
|
361
|
+
const reviewedBy = item.reviewed_by || item.provenance?.reviewed_by;
|
|
362
|
+
if (!reviewedBy) {
|
|
363
|
+
addIssue(`Asset item URN "${item.urn}" is missing reviewed_by property.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (warnings.length > 0) {
|
|
367
|
+
console.warn(`
|
|
368
|
+
[LINT WARNINGS] Detected stock/snapshot issues (Non-blocking):`);
|
|
369
|
+
warnings.forEach((w) => console.warn(` \u26A0 WARNING: ${w}`));
|
|
370
|
+
}
|
|
371
|
+
if (errors.length > 0) {
|
|
372
|
+
console.error(`
|
|
373
|
+
[LINT ERRORS] Detected strict rules violation (BLOCKING CI):`);
|
|
374
|
+
errors.forEach((e) => console.error(` \u2717 ERROR: ${e}`));
|
|
375
|
+
}
|
|
376
|
+
if (hasLintError) {
|
|
377
|
+
console.error(`
|
|
378
|
+
Lint validation failed. Strict version metadata must fix all errors to pass CI.`);
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
console.log(`\u2713 Layer 2 (Business Lint) passed successfully.`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/commands/generate-docs.ts
|
|
385
|
+
import fs3 from "fs-extra";
|
|
386
|
+
import path3 from "path";
|
|
387
|
+
|
|
388
|
+
// src/generator/doc-generator.ts
|
|
389
|
+
import ejs from "ejs";
|
|
390
|
+
import yaml from "js-yaml";
|
|
391
|
+
var DocGenerator = class {
|
|
392
|
+
layout;
|
|
393
|
+
constructor(layoutYaml) {
|
|
394
|
+
if (layoutYaml) {
|
|
395
|
+
try {
|
|
396
|
+
this.layout = yaml.load(layoutYaml);
|
|
397
|
+
} catch (err) {
|
|
398
|
+
throw new Error(`Failed to parse layout YAML: ${err.message}`);
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
this.layout = this.getDefaultLayout();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
generate(data) {
|
|
405
|
+
const urn = data.urn || "";
|
|
406
|
+
const version = data.version || "";
|
|
407
|
+
const kind = data.kind || "";
|
|
408
|
+
const assetType = data.asset_type || "";
|
|
409
|
+
let md = `---
|
|
410
|
+
`;
|
|
411
|
+
md += `urn: ${urn}
|
|
412
|
+
`;
|
|
413
|
+
md += `version: ${version}
|
|
414
|
+
`;
|
|
415
|
+
md += `kind: ${kind}
|
|
416
|
+
`;
|
|
417
|
+
md += `asset_type: ${assetType}
|
|
418
|
+
`;
|
|
419
|
+
md += `---
|
|
420
|
+
|
|
421
|
+
`;
|
|
422
|
+
for (const section of this.layout.sections) {
|
|
423
|
+
md += `${section.title}
|
|
424
|
+
|
|
425
|
+
`;
|
|
426
|
+
const sourceData = section.source ? data[section.source] : void 0;
|
|
427
|
+
const placeholder = section.empty_placeholder || "*\u65E0*";
|
|
428
|
+
const isEmpty = sourceData === void 0 || sourceData === null || Array.isArray(sourceData) && sourceData.length === 0 || typeof sourceData === "string" && sourceData.trim() === "";
|
|
429
|
+
if (isEmpty && section.type !== "code_block") {
|
|
430
|
+
md += `${placeholder}
|
|
431
|
+
|
|
432
|
+
`;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
switch (section.type) {
|
|
436
|
+
case "text":
|
|
437
|
+
md += `${String(sourceData).trim()}
|
|
438
|
+
|
|
439
|
+
`;
|
|
440
|
+
break;
|
|
441
|
+
case "code_block":
|
|
442
|
+
const lang = section.language || "";
|
|
443
|
+
let codeContent = "";
|
|
444
|
+
if (section.template) {
|
|
445
|
+
try {
|
|
446
|
+
codeContent = ejs.render(section.template, data);
|
|
447
|
+
} catch (err) {
|
|
448
|
+
codeContent = `/* EJS render error: ${err.message} */`;
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
codeContent = String(sourceData || "");
|
|
452
|
+
}
|
|
453
|
+
if (!codeContent.trim() && isEmpty) {
|
|
454
|
+
md += `${placeholder}
|
|
455
|
+
|
|
456
|
+
`;
|
|
457
|
+
} else {
|
|
458
|
+
md += `\`\`\`${lang}
|
|
459
|
+
${codeContent.trim()}
|
|
460
|
+
\`\`\`
|
|
461
|
+
|
|
462
|
+
`;
|
|
463
|
+
}
|
|
464
|
+
break;
|
|
465
|
+
case "table":
|
|
466
|
+
if (Array.isArray(sourceData)) {
|
|
467
|
+
md += this.renderTable(sourceData, section.columns || []);
|
|
468
|
+
} else {
|
|
469
|
+
md += `${placeholder}
|
|
470
|
+
|
|
471
|
+
`;
|
|
472
|
+
}
|
|
473
|
+
break;
|
|
474
|
+
default:
|
|
475
|
+
md += `${placeholder}
|
|
476
|
+
|
|
477
|
+
`;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return md;
|
|
481
|
+
}
|
|
482
|
+
renderTable(rows, columns) {
|
|
483
|
+
if (columns.length === 0) return "";
|
|
484
|
+
let headers = `| ` + columns.map((c) => c.name).join(" | ") + ` |
|
|
485
|
+
`;
|
|
486
|
+
let separators = `| ` + columns.map(() => "---").join(" | ") + ` |
|
|
487
|
+
`;
|
|
488
|
+
let content = headers + separators;
|
|
489
|
+
for (const row of rows) {
|
|
490
|
+
const line = columns.map((col) => {
|
|
491
|
+
const val = row[col.key];
|
|
492
|
+
return this.formatValue(val, col.formatter);
|
|
493
|
+
}).join(" | ");
|
|
494
|
+
content += `| ${line} |
|
|
495
|
+
`;
|
|
496
|
+
}
|
|
497
|
+
return content + "\n";
|
|
498
|
+
}
|
|
499
|
+
formatValue(val, formatter) {
|
|
500
|
+
if (val === void 0 || val === null) return "-";
|
|
501
|
+
if (formatter === "code") {
|
|
502
|
+
return val === "" ? "-" : `\`${String(val).replace(/`/g, "\\`").replace(/\|/g, "\\|")}\``;
|
|
503
|
+
}
|
|
504
|
+
if (formatter === "boolean") {
|
|
505
|
+
return val ? "`true`" : "`false`";
|
|
506
|
+
}
|
|
507
|
+
if (formatter === "badge") {
|
|
508
|
+
return `[${val}]`;
|
|
509
|
+
}
|
|
510
|
+
return String(val).replace(/\|/g, "\\|").trim() || "-";
|
|
511
|
+
}
|
|
512
|
+
getDefaultLayout() {
|
|
513
|
+
return {
|
|
514
|
+
sections: [
|
|
515
|
+
{
|
|
516
|
+
id: "overview",
|
|
517
|
+
title: "## \u6982\u89C8",
|
|
518
|
+
type: "text",
|
|
519
|
+
source: "overview",
|
|
520
|
+
empty_placeholder: "*\u6682\u65E0\u6982\u89C8\u63CF\u8FF0*"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: "import",
|
|
524
|
+
title: "## \u5F15\u5165",
|
|
525
|
+
type: "code_block",
|
|
526
|
+
language: "typescript",
|
|
527
|
+
template: "import { <%= name %> } from '<%= asset %>';"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
id: "body",
|
|
531
|
+
title: "## \u4E3B\u4F53",
|
|
532
|
+
type: "text",
|
|
533
|
+
source: "body",
|
|
534
|
+
empty_placeholder: "*\u65E0*"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
id: "examples",
|
|
538
|
+
title: "## \u793A\u4F8B",
|
|
539
|
+
type: "text",
|
|
540
|
+
source: "examples",
|
|
541
|
+
empty_placeholder: "*\u65E0*"
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: "relations",
|
|
545
|
+
title: "## \u5173\u7CFB",
|
|
546
|
+
type: "text",
|
|
547
|
+
source: "relations",
|
|
548
|
+
empty_placeholder: "*\u65E0*"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
id: "changelog",
|
|
552
|
+
title: "## \u53D8\u66F4",
|
|
553
|
+
type: "text",
|
|
554
|
+
source: "changelog",
|
|
555
|
+
empty_placeholder: "*\u65E0*"
|
|
556
|
+
}
|
|
557
|
+
]
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/commands/generate-docs.ts
|
|
563
|
+
async function generateDocsCommand(options) {
|
|
564
|
+
console.log(`Executing generate-docs: dir=${options.dir}`);
|
|
565
|
+
const metaDir = path3.resolve(options.dir);
|
|
566
|
+
const indexPath = path3.join(metaDir, "index.json");
|
|
567
|
+
if (!await fs3.pathExists(indexPath)) {
|
|
568
|
+
throw new Error(`index.json not found in directory: "${metaDir}"`);
|
|
569
|
+
}
|
|
570
|
+
const indexData = await fs3.readJson(indexPath);
|
|
571
|
+
const viewPath = path3.join(metaDir, "view.json");
|
|
572
|
+
const hasView = await fs3.pathExists(viewPath);
|
|
573
|
+
const viewData = hasView ? await fs3.readJson(viewPath) : null;
|
|
574
|
+
let layoutYaml;
|
|
575
|
+
if (options.layout) {
|
|
576
|
+
const layoutPath = path3.resolve(options.layout);
|
|
577
|
+
if (await fs3.pathExists(layoutPath)) {
|
|
578
|
+
layoutYaml = await fs3.readFile(layoutPath, "utf8");
|
|
579
|
+
} else {
|
|
580
|
+
console.warn(`Warning: Custom layout file not found at: "${layoutPath}". Using default layout.`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const generator = new DocGenerator(layoutYaml);
|
|
584
|
+
const defaultOutputDir = path3.join(metaDir, "docs");
|
|
585
|
+
const outputDir = path3.resolve(options.outputDir || defaultOutputDir);
|
|
586
|
+
await fs3.ensureDir(outputDir);
|
|
587
|
+
if (hasView && viewData) {
|
|
588
|
+
const mdContent = generator.generate(viewData);
|
|
589
|
+
const componentName = viewData.component || "component";
|
|
590
|
+
const outPath = path3.join(outputDir, `${componentName}.md`);
|
|
591
|
+
await fs3.writeFile(outPath, mdContent, "utf8");
|
|
592
|
+
console.log(`Generated component doc: ${outPath}`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const generatedFiles = /* @__PURE__ */ new Set();
|
|
596
|
+
for (const item of indexData) {
|
|
597
|
+
const docMdRel = item.doc_md.split("#")[0];
|
|
598
|
+
if (!docMdRel) continue;
|
|
599
|
+
const baseName = path3.basename(docMdRel);
|
|
600
|
+
if (generatedFiles.has(baseName)) continue;
|
|
601
|
+
const renderData = { ...item };
|
|
602
|
+
if (item.asset_type === "sdk" && item.kind !== "method") {
|
|
603
|
+
const classMethods = indexData.filter(
|
|
604
|
+
(i) => i.asset_type === "sdk" && i.kind === "method" && i.urn.startsWith(item.urn + "#")
|
|
605
|
+
);
|
|
606
|
+
renderData.methods = classMethods.map((m) => ({
|
|
607
|
+
name: m.name,
|
|
608
|
+
signature: m.signature,
|
|
609
|
+
description: m.summary
|
|
610
|
+
}));
|
|
611
|
+
}
|
|
612
|
+
const mdContent = generator.generate(renderData);
|
|
613
|
+
const outPath = path3.join(outputDir, baseName);
|
|
614
|
+
await fs3.writeFile(outPath, mdContent, "utf8");
|
|
615
|
+
generatedFiles.add(baseName);
|
|
616
|
+
console.log(`Generated doc: ${outPath}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/commands/merge.ts
|
|
621
|
+
import fs4 from "fs-extra";
|
|
622
|
+
import path4 from "path";
|
|
623
|
+
import { parseUrn } from "@farris/api-wiki-contract-schema";
|
|
624
|
+
async function mergeCommand(options) {
|
|
625
|
+
console.log(`Executing merge: srcDir=${options.srcDir}, destDir=${options.destDir}`);
|
|
626
|
+
const srcDir = path4.resolve(options.srcDir);
|
|
627
|
+
const destDir = path4.resolve(options.destDir);
|
|
628
|
+
const srcCatalogPath = path4.join(srcDir, "catalog.json");
|
|
629
|
+
const srcIndexPath = path4.join(srcDir, "index.json");
|
|
630
|
+
if (!await fs4.pathExists(srcCatalogPath) || !await fs4.pathExists(srcIndexPath)) {
|
|
631
|
+
throw new Error(`Invalid source directory. "catalog.json" and "index.json" are required at: "${srcDir}"`);
|
|
632
|
+
}
|
|
633
|
+
const srcCatalog = await fs4.readJson(srcCatalogPath);
|
|
634
|
+
const version = srcCatalog.version || srcCatalog.versions && srcCatalog.versions[srcCatalog.versions.length - 1];
|
|
635
|
+
if (!version) {
|
|
636
|
+
throw new Error(`"version" is missing in source catalog.json (nor found in versions array)`);
|
|
637
|
+
}
|
|
638
|
+
const urn = srcCatalog.urn || (srcCatalog.asset ? `asset:${srcCatalog.asset}` : void 0);
|
|
639
|
+
if (!urn) {
|
|
640
|
+
throw new Error(`"urn" (or legacy "asset") is missing in source catalog.json`);
|
|
641
|
+
}
|
|
642
|
+
let assetName;
|
|
643
|
+
try {
|
|
644
|
+
const parsed = parseUrn(urn);
|
|
645
|
+
assetName = parsed.asset;
|
|
646
|
+
} catch (err) {
|
|
647
|
+
throw new Error(`Invalid URN in source catalog.json: ${err.message}`);
|
|
648
|
+
}
|
|
649
|
+
await fs4.ensureDir(destDir);
|
|
650
|
+
const versionsPath = path4.join(destDir, "versions.json");
|
|
651
|
+
const srcViewPath = path4.join(srcDir, "view.json");
|
|
652
|
+
const hasView = await fs4.pathExists(srcViewPath);
|
|
653
|
+
let versionsObj = {
|
|
654
|
+
versions: [],
|
|
655
|
+
latest: "",
|
|
656
|
+
assets: {}
|
|
657
|
+
};
|
|
658
|
+
if (await fs4.pathExists(versionsPath)) {
|
|
659
|
+
try {
|
|
660
|
+
const existing = await fs4.readJson(versionsPath);
|
|
661
|
+
if (Array.isArray(existing)) {
|
|
662
|
+
versionsObj.versions = existing;
|
|
663
|
+
versionsObj.latest = existing[existing.length - 1] || "";
|
|
664
|
+
for (const v of existing) {
|
|
665
|
+
const indexExists = await fs4.pathExists(path4.join(destDir, v, "index.json"));
|
|
666
|
+
const viewExists = await fs4.pathExists(path4.join(destDir, v, "view.json"));
|
|
667
|
+
versionsObj.assets[v] = {
|
|
668
|
+
"index.json": indexExists,
|
|
669
|
+
"view.json": viewExists
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
} else {
|
|
673
|
+
versionsObj = existing;
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
if (!versionsObj.versions.includes(version)) {
|
|
679
|
+
versionsObj.versions.push(version);
|
|
680
|
+
versionsObj.versions.sort();
|
|
681
|
+
}
|
|
682
|
+
versionsObj.latest = versionsObj.versions[versionsObj.versions.length - 1];
|
|
683
|
+
versionsObj.assets[version] = {
|
|
684
|
+
"index.json": true,
|
|
685
|
+
"view.json": hasView
|
|
686
|
+
};
|
|
687
|
+
await fs4.writeJson(versionsPath, versionsObj, { spaces: 2 });
|
|
688
|
+
const versionSubDir = path4.join(destDir, version);
|
|
689
|
+
await fs4.ensureDir(versionSubDir);
|
|
690
|
+
await fs4.copyFile(srcIndexPath, path4.join(versionSubDir, "index.json"));
|
|
691
|
+
if (hasView) {
|
|
692
|
+
await fs4.copyFile(srcViewPath, path4.join(versionSubDir, "view.json"));
|
|
693
|
+
}
|
|
694
|
+
const destCatalogPath = path4.join(destDir, "catalog.json");
|
|
695
|
+
const normalizedSrcCatalog = {
|
|
696
|
+
urn: srcCatalog.urn || `asset:${assetName}`,
|
|
697
|
+
type: srcCatalog.type || srcCatalog.asset_type || "rest",
|
|
698
|
+
name: srcCatalog.name || srcCatalog.title || assetName,
|
|
699
|
+
description: srcCatalog.description || srcCatalog.summary || "",
|
|
700
|
+
owner: srcCatalog.owner || "unknown",
|
|
701
|
+
visibility: srcCatalog.visibility || "internal",
|
|
702
|
+
lifecycle: srcCatalog.lifecycle || "production",
|
|
703
|
+
versions: versionsObj.versions,
|
|
704
|
+
coordinates: Array.isArray(srcCatalog.coordinates) ? srcCatalog.coordinates : srcCatalog.coordinates ? [srcCatalog.coordinates] : [],
|
|
705
|
+
relations: srcCatalog.relations || []
|
|
706
|
+
};
|
|
707
|
+
let finalCatalog = {};
|
|
708
|
+
if (await fs4.pathExists(destCatalogPath)) {
|
|
709
|
+
const existingCatalog = await fs4.readJson(destCatalogPath);
|
|
710
|
+
let existingCoordinates = existingCatalog.coordinates || [];
|
|
711
|
+
if (!Array.isArray(existingCoordinates)) {
|
|
712
|
+
existingCoordinates = Object.keys(existingCoordinates).map((key) => ({
|
|
713
|
+
type: key,
|
|
714
|
+
version: existingCatalog.version || "unknown",
|
|
715
|
+
repo: existingCoordinates[key]
|
|
716
|
+
}));
|
|
717
|
+
}
|
|
718
|
+
const coordinates = existingCoordinates.length > 0 ? existingCoordinates : normalizedSrcCatalog.coordinates;
|
|
719
|
+
const relations = existingCatalog.relations || normalizedSrcCatalog.relations;
|
|
720
|
+
const deployments = existingCatalog.deployments || {};
|
|
721
|
+
finalCatalog = {
|
|
722
|
+
...normalizedSrcCatalog,
|
|
723
|
+
coordinates,
|
|
724
|
+
relations,
|
|
725
|
+
deployments
|
|
726
|
+
};
|
|
727
|
+
} else {
|
|
728
|
+
finalCatalog = {
|
|
729
|
+
...normalizedSrcCatalog
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
await fs4.writeJson(destCatalogPath, finalCatalog, { spaces: 2 });
|
|
733
|
+
console.log(`Successfully merged asset "${assetName}" version "${version}" into target metadata.`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/commands/generate-catalog.ts
|
|
737
|
+
import fs5 from "fs-extra";
|
|
738
|
+
import path5 from "path";
|
|
739
|
+
async function generateCatalogCommand(options) {
|
|
740
|
+
console.log(`Executing generate-catalog: metaDir=${options.metaDir}`);
|
|
741
|
+
const metaDir = path5.resolve(options.metaDir);
|
|
742
|
+
if (!await fs5.pathExists(metaDir)) {
|
|
743
|
+
throw new Error(`Meta root directory does not exist: "${metaDir}"`);
|
|
744
|
+
}
|
|
745
|
+
const globalCatalogPath = path5.join(metaDir, "catalog.json");
|
|
746
|
+
const searchIndexDir = path5.join(metaDir, "search-index");
|
|
747
|
+
await fs5.ensureDir(searchIndexDir);
|
|
748
|
+
const globalCatalogList = [];
|
|
749
|
+
const items = await fs5.readdir(metaDir);
|
|
750
|
+
for (const itemName of items) {
|
|
751
|
+
const assetDir = path5.join(metaDir, itemName);
|
|
752
|
+
if (itemName === "search-index" || itemName === "catalog.json") continue;
|
|
753
|
+
const stat = await fs5.stat(assetDir);
|
|
754
|
+
if (!stat.isDirectory()) continue;
|
|
755
|
+
const catalogPath = path5.join(assetDir, "catalog.json");
|
|
756
|
+
if (!await fs5.pathExists(catalogPath)) continue;
|
|
757
|
+
const assetCatalog = await fs5.readJson(catalogPath);
|
|
758
|
+
if (assetCatalog.visibility === "restricted") {
|
|
759
|
+
console.log(`Skipping restricted asset: ${itemName}`);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const versionsPath = path5.join(assetDir, "versions.json");
|
|
763
|
+
let versions = [];
|
|
764
|
+
let latestVersion = "";
|
|
765
|
+
if (await fs5.pathExists(versionsPath)) {
|
|
766
|
+
try {
|
|
767
|
+
const versionsData = await fs5.readJson(versionsPath);
|
|
768
|
+
if (Array.isArray(versionsData)) {
|
|
769
|
+
versions = versionsData;
|
|
770
|
+
latestVersion = versions[versions.length - 1] || "";
|
|
771
|
+
} else {
|
|
772
|
+
versions = versionsData.versions || [];
|
|
773
|
+
latestVersion = versionsData.latest || versions[versions.length - 1] || "";
|
|
774
|
+
}
|
|
775
|
+
} catch (err) {
|
|
776
|
+
console.error(`Error parsing versions.json for ${itemName}: ${err.message}`);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const urn = assetCatalog.urn || `asset:${assetCatalog.asset || itemName}`;
|
|
780
|
+
const type = assetCatalog.type || assetCatalog.asset_type || "rest";
|
|
781
|
+
const name = assetCatalog.name || assetCatalog.title || itemName;
|
|
782
|
+
const description = assetCatalog.description || assetCatalog.summary || "";
|
|
783
|
+
const owner = assetCatalog.owner || "unknown";
|
|
784
|
+
const visibility = assetCatalog.visibility || "internal";
|
|
785
|
+
const lifecycle = assetCatalog.lifecycle || "production";
|
|
786
|
+
globalCatalogList.push({
|
|
787
|
+
urn,
|
|
788
|
+
type,
|
|
789
|
+
name,
|
|
790
|
+
description,
|
|
791
|
+
owner,
|
|
792
|
+
visibility,
|
|
793
|
+
lifecycle,
|
|
794
|
+
latest: latestVersion,
|
|
795
|
+
dir: itemName
|
|
796
|
+
});
|
|
797
|
+
const assetIndexData = [];
|
|
798
|
+
for (const version of versions) {
|
|
799
|
+
const versionIndexPath = path5.join(assetDir, version, "index.json");
|
|
800
|
+
if (await fs5.pathExists(versionIndexPath)) {
|
|
801
|
+
try {
|
|
802
|
+
const indexItems = await fs5.readJson(versionIndexPath);
|
|
803
|
+
if (Array.isArray(indexItems)) {
|
|
804
|
+
for (const item of indexItems) {
|
|
805
|
+
assetIndexData.push({
|
|
806
|
+
urn: item.urn,
|
|
807
|
+
name: item.name,
|
|
808
|
+
title: item.title,
|
|
809
|
+
signature: item.signature,
|
|
810
|
+
summary: item.summary,
|
|
811
|
+
tags: item.tags || [],
|
|
812
|
+
deprecated: item.deprecated || false,
|
|
813
|
+
version
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
} catch (err) {
|
|
818
|
+
console.error(`Error reading index for ${itemName}@${version}: ${err.message}`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const searchSlicePath = path5.join(searchIndexDir, `${itemName}.json`);
|
|
823
|
+
await fs5.writeJson(searchSlicePath, assetIndexData, { spaces: 2 });
|
|
824
|
+
}
|
|
825
|
+
const globalCatalog = {
|
|
826
|
+
assets: globalCatalogList,
|
|
827
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
828
|
+
};
|
|
829
|
+
await fs5.writeJson(globalCatalogPath, globalCatalog, { spaces: 2 });
|
|
830
|
+
console.log(`Successfully generated global catalog at: ${globalCatalogPath}`);
|
|
831
|
+
console.log(`Successfully generated search index directory at: ${searchIndexDir}`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/bin/aw-cli.ts
|
|
835
|
+
var program = new Command();
|
|
836
|
+
program.name("aw-cli").description("api-wiki \u6784\u5EFA\u671F\u4E0E\u5408\u5E76\u671F\u5DE5\u5177\u94FE CLI").version("1.0.0", "-V, --cli-version");
|
|
837
|
+
program.command("adapt").description("\u4ECE\u539F\u751F native.json \u8F6C\u5316\u4E3A index.json \u53CA view.json").requiredOption("-t, --type <type>", "\u8D44\u4EA7\u7C7B\u578B (rest|grpc|graphql|async|component|sdk)").requiredOption("-i, --input <path>", "\u539F\u751F native.json \u7684\u8F93\u5165\u7269\u7406\u8DEF\u5F84").requiredOption("-o, --output-dir <path>", "\u9002\u914D\u5143\u6570\u636E\u7684\u8F93\u51FA\u76EE\u5F55\u8DEF\u5F84").requiredOption("--asset <name>", "\u5F53\u524D\u8D44\u4EA7\u540D\u79F0 (kebab-case)").requiredOption("--version <ver>", "\u5F53\u524D\u8D44\u4EA7\u7248\u672C\u53F7").action(async (options) => {
|
|
838
|
+
try {
|
|
839
|
+
const validTypes = ["rest", "grpc", "graphql", "async", "component", "sdk"];
|
|
840
|
+
if (!validTypes.includes(options.type)) {
|
|
841
|
+
console.error(`Error: Invalid type "${options.type}". Supported types: ${validTypes.join(", ")}`);
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
844
|
+
await adaptCommand(options);
|
|
845
|
+
} catch (err) {
|
|
846
|
+
console.error(`Error during adapt: ${err.message}`);
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
program.command("validate").description("\u6267\u884C\u672C\u5730\u5143\u6570\u636E\u53CC\u5C42\u6821\u9A8C\u95E8\u7981").requiredOption("-d, --dir <path>", "\u5F85\u6821\u9A8C\u7684\u5143\u6570\u636E\u8D44\u4EA7\u7248\u672C\u76EE\u5F55").action(async (options) => {
|
|
851
|
+
try {
|
|
852
|
+
await validateCommand(options);
|
|
853
|
+
} catch (err) {
|
|
854
|
+
console.error(`Error during validate: ${err.message}`);
|
|
855
|
+
process.exit(1);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
program.command("generate-docs").description("\u57FA\u4E8E\u58F0\u660E\u5F0F EJS \u4E0E YAML \u5E03\u5C40\u63CF\u8FF0\u7B26\u751F\u6210 Markdown \u9875\u9762").requiredOption("-d, --dir <path>", "\u5143\u6570\u636E\u8D44\u4EA7\u7248\u672C\u76EE\u5F55").option("-l, --layout <path>", "\u81EA\u5B9A\u4E49\u7684 YAML \u6A21\u677F\u63CF\u8FF0\u7B26\u8DEF\u5F84").option("-o, --output-dir <path>", "Markdown \u9875\u9762\u8F93\u51FA\u76EE\u6807\u76EE\u5F55").action(async (options) => {
|
|
859
|
+
try {
|
|
860
|
+
await generateDocsCommand(options);
|
|
861
|
+
} catch (err) {
|
|
862
|
+
console.error(`Error during generate-docs: ${err.message}`);
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
program.command("merge").description("\u5C06\u5355\u4E2A\u6784\u5EFA\u7248\u672C\u4EA7\u7269\u5408\u5E76\u5165\u5927\u4ED3\u7684\u8D44\u4EA7\u76EE\u5F55\u5185").requiredOption("--src-dir <path>", "\u53D1\u5E03\u6E90\u5143\u6570\u636E\u76EE\u5F55").requiredOption("--dest-dir <path>", "\u5927\u4ED3\u7684\u76EE\u6807\u8D44\u4EA7\u7269\u7406\u8DEF\u5F84").action(async (options) => {
|
|
867
|
+
try {
|
|
868
|
+
await mergeCommand(options);
|
|
869
|
+
} catch (err) {
|
|
870
|
+
console.error(`Error during merge: ${err.message}`);
|
|
871
|
+
process.exit(1);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
program.command("generate-catalog").description("\u4E2D\u592E\u805A\u5408\u91CD\u6784\u5168\u5C40 catalog.json \u4E0E\u641C\u7D22\u7D22\u5F15").requiredOption("--meta-dir <path>", "\u5927\u4ED3\u7684 meta/ \u6839\u76EE\u5F55\u8DEF\u5F84").action(async (options) => {
|
|
875
|
+
try {
|
|
876
|
+
await generateCatalogCommand(options);
|
|
877
|
+
} catch (err) {
|
|
878
|
+
console.error(`Error during generate-catalog: ${err.message}`);
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
program.on("command:*", () => {
|
|
883
|
+
console.error(`Error: Invalid command. Use --help to list available commands.`);
|
|
884
|
+
process.exit(1);
|
|
885
|
+
});
|
|
886
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
887
|
+
console.error(`Unexpected system error: ${err.message}`);
|
|
888
|
+
process.exit(1);
|
|
889
|
+
});
|