@zeus-js/component-analyzer 0.1.0-beta.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,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* component-analyzer v0.1.0-beta.0
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
//#region \0rolldown/runtime.js
|
|
11
|
+
var __create = Object.create;
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
19
|
+
key = keys[i];
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
+
get: ((k) => from[k]).bind(null, key),
|
|
22
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
28
|
+
value: mod,
|
|
29
|
+
enumerable: true
|
|
30
|
+
}) : target, mod));
|
|
31
|
+
//#endregion
|
|
32
|
+
let _babel_parser = require("@babel/parser");
|
|
33
|
+
let _babel_types = require("@babel/types");
|
|
34
|
+
_babel_types = __toESM(_babel_types, 1);
|
|
35
|
+
let node_fs_promises = require("node:fs/promises");
|
|
36
|
+
node_fs_promises = __toESM(node_fs_promises, 1);
|
|
37
|
+
let node_path = require("node:path");
|
|
38
|
+
node_path = __toESM(node_path, 1);
|
|
39
|
+
let fast_glob = require("fast-glob");
|
|
40
|
+
fast_glob = __toESM(fast_glob, 1);
|
|
41
|
+
//#region packages/web-c/component-analyzer/src/ast.ts
|
|
42
|
+
function parseSource(code, file) {
|
|
43
|
+
return (0, _babel_parser.parse)(code, {
|
|
44
|
+
sourceType: "module",
|
|
45
|
+
sourceFilename: file,
|
|
46
|
+
plugins: [
|
|
47
|
+
"typescript",
|
|
48
|
+
"jsx",
|
|
49
|
+
"decorators-legacy",
|
|
50
|
+
"classProperties",
|
|
51
|
+
"objectRestSpread"
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function walk(node, visitor, parent = null) {
|
|
56
|
+
var _t$VISITOR_KEYS$node$;
|
|
57
|
+
if (!node) return;
|
|
58
|
+
visitor(node, parent);
|
|
59
|
+
const keys = (_t$VISITOR_KEYS$node$ = _babel_types.VISITOR_KEYS[node.type]) !== null && _t$VISITOR_KEYS$node$ !== void 0 ? _t$VISITOR_KEYS$node$ : [];
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
const value = node[key];
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
for (const child of value) if (child && typeof child === "object" && "type" in child) walk(child, visitor, node);
|
|
64
|
+
} else if (value && typeof value === "object" && "type" in value) walk(value, visitor, node);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region packages/web-c/component-analyzer/src/extractDefineElement.ts
|
|
69
|
+
function extractDefineElementCalls(ast) {
|
|
70
|
+
const defineElementLocalNames = collectDefineElementLocalNames(ast);
|
|
71
|
+
const exportedNames = collectExportedNames(ast);
|
|
72
|
+
const records = [];
|
|
73
|
+
walk(ast.program, (node) => {
|
|
74
|
+
if (!_babel_types.isVariableDeclarator(node)) return;
|
|
75
|
+
if (!_babel_types.isIdentifier(node.id)) return;
|
|
76
|
+
if (!_babel_types.isCallExpression(node.init)) return;
|
|
77
|
+
const call = node.init;
|
|
78
|
+
if (!isDefineElementCall(call, defineElementLocalNames)) return;
|
|
79
|
+
const tag = extractTag(call);
|
|
80
|
+
if (!tag) return;
|
|
81
|
+
const name = node.id.name;
|
|
82
|
+
const options = extractOptions(call);
|
|
83
|
+
const setup = call.arguments[2];
|
|
84
|
+
const propsTypeName = extractPropsTypeName(call);
|
|
85
|
+
if (!exportedNames.has(name)) return;
|
|
86
|
+
records.push({
|
|
87
|
+
name,
|
|
88
|
+
exportName: name,
|
|
89
|
+
tag,
|
|
90
|
+
propsTypeName,
|
|
91
|
+
options,
|
|
92
|
+
setup,
|
|
93
|
+
call
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return records;
|
|
97
|
+
}
|
|
98
|
+
function collectDefineElementLocalNames(ast) {
|
|
99
|
+
const names = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const node of ast.program.body) {
|
|
101
|
+
if (!_babel_types.isImportDeclaration(node)) continue;
|
|
102
|
+
const source = node.source.value;
|
|
103
|
+
if (source !== "@zeus-js/zeus" && source !== "@zeus-js/runtime-dom") continue;
|
|
104
|
+
for (const spec of node.specifiers) {
|
|
105
|
+
if (!_babel_types.isImportSpecifier(spec)) continue;
|
|
106
|
+
const imported = spec.imported;
|
|
107
|
+
if (_babel_types.isIdentifier(imported) && imported.name === "defineElement" || _babel_types.isStringLiteral(imported) && imported.value === "defineElement") names.add(spec.local.name);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return names;
|
|
111
|
+
}
|
|
112
|
+
function collectExportedNames(ast) {
|
|
113
|
+
const names = /* @__PURE__ */ new Set();
|
|
114
|
+
for (const node of ast.program.body) if (_babel_types.isExportNamedDeclaration(node)) {
|
|
115
|
+
if (_babel_types.isVariableDeclaration(node.declaration)) {
|
|
116
|
+
for (const declarator of node.declaration.declarations) if (_babel_types.isIdentifier(declarator.id)) names.add(declarator.id.name);
|
|
117
|
+
}
|
|
118
|
+
for (const spec of node.specifiers) if (_babel_types.isExportSpecifier(spec) && _babel_types.isIdentifier(spec.local)) names.add(spec.local.name);
|
|
119
|
+
}
|
|
120
|
+
return names;
|
|
121
|
+
}
|
|
122
|
+
function isDefineElementCall(call, localNames) {
|
|
123
|
+
return _babel_types.isIdentifier(call.callee) && localNames.has(call.callee.name);
|
|
124
|
+
}
|
|
125
|
+
function extractTag(call) {
|
|
126
|
+
const first = call.arguments[0];
|
|
127
|
+
if (_babel_types.isStringLiteral(first)) return first.value;
|
|
128
|
+
}
|
|
129
|
+
function extractOptions(call) {
|
|
130
|
+
const second = call.arguments[1];
|
|
131
|
+
if (_babel_types.isObjectExpression(second)) return second;
|
|
132
|
+
}
|
|
133
|
+
function extractPropsTypeName(call) {
|
|
134
|
+
var _call$typeParameters;
|
|
135
|
+
const first = (_call$typeParameters = call.typeParameters) === null || _call$typeParameters === void 0 ? void 0 : _call$typeParameters.params[0];
|
|
136
|
+
if (!first) return void 0;
|
|
137
|
+
if (_babel_types.isTSTypeReference(first) && _babel_types.isIdentifier(first.typeName)) return first.typeName.name;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region packages/web-c/component-analyzer/src/utils.ts
|
|
141
|
+
function getObjectKey(key) {
|
|
142
|
+
if (_babel_types.isIdentifier(key)) return key.name;
|
|
143
|
+
if (_babel_types.isStringLiteral(key)) return key.value;
|
|
144
|
+
if (_babel_types.isNumericLiteral(key)) return String(key.value);
|
|
145
|
+
}
|
|
146
|
+
function getObjectProperty(object, name) {
|
|
147
|
+
for (const prop of object.properties) {
|
|
148
|
+
if (!_babel_types.isObjectProperty(prop)) continue;
|
|
149
|
+
if (getObjectKey(prop.key) === name) return prop.value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function staticValue(node) {
|
|
153
|
+
if (!node) return void 0;
|
|
154
|
+
if (_babel_types.isStringLiteral(node)) return node.value;
|
|
155
|
+
if (_babel_types.isNumericLiteral(node)) return node.value;
|
|
156
|
+
if (_babel_types.isBooleanLiteral(node)) return node.value;
|
|
157
|
+
if (_babel_types.isNullLiteral(node)) return null;
|
|
158
|
+
if (_babel_types.isIdentifier(node) && node.name === "undefined") return void 0;
|
|
159
|
+
if (_babel_types.isArrayExpression(node)) return node.elements.map((element) => {
|
|
160
|
+
if (!element) return null;
|
|
161
|
+
if (_babel_types.isSpreadElement(element)) return void 0;
|
|
162
|
+
return staticValue(element);
|
|
163
|
+
});
|
|
164
|
+
if (_babel_types.isObjectExpression(node)) {
|
|
165
|
+
const result = {};
|
|
166
|
+
for (const prop of node.properties) {
|
|
167
|
+
if (!_babel_types.isObjectProperty(prop)) continue;
|
|
168
|
+
const key = getObjectKey(prop.key);
|
|
169
|
+
if (!key) continue;
|
|
170
|
+
result[key] = staticValue(prop.value);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function getLeadingDescription(node) {
|
|
176
|
+
const comments = node.leadingComments;
|
|
177
|
+
if (!(comments === null || comments === void 0 ? void 0 : comments.length)) return void 0;
|
|
178
|
+
return comments.map((comment) => comment.value).join("\n").replace(/\*/g, "").split("\n").map((line) => line.trim()).filter(Boolean).join("\n") || void 0;
|
|
179
|
+
}
|
|
180
|
+
function uniqueSorted(values) {
|
|
181
|
+
return Array.from(new Set(values)).sort();
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region packages/web-c/component-analyzer/src/extractMeta.ts
|
|
185
|
+
function extractInlineMeta(options) {
|
|
186
|
+
if (!options) return {};
|
|
187
|
+
const metaNode = getObjectProperty(options, "meta");
|
|
188
|
+
if (!_babel_types.isObjectExpression(metaNode)) return {};
|
|
189
|
+
const value = staticValue(metaNode);
|
|
190
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region packages/web-c/component-analyzer/src/extractProps.ts
|
|
195
|
+
function extractRuntimeProps(options) {
|
|
196
|
+
if (!options) return {};
|
|
197
|
+
const propsNode = getObjectProperty(options, "props");
|
|
198
|
+
if (!_babel_types.isObjectExpression(propsNode)) return {};
|
|
199
|
+
const props = {};
|
|
200
|
+
for (const prop of propsNode.properties) {
|
|
201
|
+
if (!_babel_types.isObjectProperty(prop)) continue;
|
|
202
|
+
const key = getObjectKey(prop.key);
|
|
203
|
+
if (!key) continue;
|
|
204
|
+
props[key] = extractRuntimeProp(prop.value);
|
|
205
|
+
}
|
|
206
|
+
return props;
|
|
207
|
+
}
|
|
208
|
+
function extractRuntimeProp(node) {
|
|
209
|
+
if (_babel_types.isIdentifier(node)) return { type: typeFromConstructorName(node.name) };
|
|
210
|
+
if (_babel_types.isObjectExpression(node)) {
|
|
211
|
+
const typeNode = getObjectProperty(node, "type");
|
|
212
|
+
const attrNode = getObjectProperty(node, "attr");
|
|
213
|
+
const reflectNode = getObjectProperty(node, "reflect");
|
|
214
|
+
const defaultNode = getObjectProperty(node, "default");
|
|
215
|
+
const prop = { type: _babel_types.isIdentifier(typeNode) ? typeFromConstructorName(typeNode.name) : "unknown" };
|
|
216
|
+
if (attrNode) {
|
|
217
|
+
const attr = staticValue(attrNode);
|
|
218
|
+
if (attr === false || typeof attr === "string") prop.attr = attr;
|
|
219
|
+
}
|
|
220
|
+
if (reflectNode) {
|
|
221
|
+
const reflect = staticValue(reflectNode);
|
|
222
|
+
if (typeof reflect === "boolean") prop.reflect = reflect;
|
|
223
|
+
}
|
|
224
|
+
if (defaultNode) {
|
|
225
|
+
if (!_babel_types.isFunctionExpression(defaultNode) && !_babel_types.isArrowFunctionExpression(defaultNode)) prop.default = staticValue(defaultNode);
|
|
226
|
+
}
|
|
227
|
+
return prop;
|
|
228
|
+
}
|
|
229
|
+
return { type: "unknown" };
|
|
230
|
+
}
|
|
231
|
+
function typeFromConstructorName(name) {
|
|
232
|
+
switch (name) {
|
|
233
|
+
case "String": return "string";
|
|
234
|
+
case "Number": return "number";
|
|
235
|
+
case "Boolean": return "boolean";
|
|
236
|
+
case "Object": return "object";
|
|
237
|
+
case "Array": return "array";
|
|
238
|
+
default: return "unknown";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region packages/web-c/component-analyzer/src/extractSetup.ts
|
|
243
|
+
function extractSetupMeta(setup) {
|
|
244
|
+
const events = {};
|
|
245
|
+
const slots = {};
|
|
246
|
+
const hostAttributes = [];
|
|
247
|
+
const cssParts = [];
|
|
248
|
+
if (!setup || _babel_types.isSpreadElement(setup) || _babel_types.isArgumentPlaceholder(setup)) return {
|
|
249
|
+
events,
|
|
250
|
+
slots,
|
|
251
|
+
hostAttributes,
|
|
252
|
+
cssParts
|
|
253
|
+
};
|
|
254
|
+
walk(setup, (node) => {
|
|
255
|
+
extractEmit(node, events);
|
|
256
|
+
extractSlot(node, slots);
|
|
257
|
+
extractHostAttributes(node, hostAttributes);
|
|
258
|
+
extractCssParts(node, cssParts);
|
|
259
|
+
});
|
|
260
|
+
return {
|
|
261
|
+
events,
|
|
262
|
+
slots,
|
|
263
|
+
hostAttributes: uniqueSorted(hostAttributes),
|
|
264
|
+
cssParts: uniqueSorted(cssParts)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function extractEmit(node, events) {
|
|
268
|
+
if (!_babel_types.isCallExpression(node)) return;
|
|
269
|
+
if (!_babel_types.isIdentifier(node.callee, { name: "emit" })) return;
|
|
270
|
+
const first = node.arguments[0];
|
|
271
|
+
if (!_babel_types.isStringLiteral(first)) return;
|
|
272
|
+
const eventName = first.value;
|
|
273
|
+
events[eventName] || (events[eventName] = {});
|
|
274
|
+
const detailNode = node.arguments[1];
|
|
275
|
+
if (_babel_types.isObjectExpression(detailNode)) events[eventName].detail = inferDetail(detailNode);
|
|
276
|
+
}
|
|
277
|
+
function inferDetail(node) {
|
|
278
|
+
const result = {};
|
|
279
|
+
for (const prop of node.properties) {
|
|
280
|
+
if (!_babel_types.isObjectProperty(prop)) continue;
|
|
281
|
+
const key = getObjectKey(prop.key);
|
|
282
|
+
if (!key) continue;
|
|
283
|
+
result[key] = inferExpressionType(prop.value);
|
|
284
|
+
}
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
function inferExpressionType(node) {
|
|
288
|
+
if (_babel_types.isStringLiteral(node)) return "string";
|
|
289
|
+
if (_babel_types.isNumericLiteral(node)) return "number";
|
|
290
|
+
if (_babel_types.isBooleanLiteral(node)) return "boolean";
|
|
291
|
+
if (_babel_types.isObjectExpression(node)) return "object";
|
|
292
|
+
if (_babel_types.isArrayExpression(node)) return "array";
|
|
293
|
+
if (_babel_types.isIdentifier(node)) return "unknown";
|
|
294
|
+
return "unknown";
|
|
295
|
+
}
|
|
296
|
+
function extractSlot(node, slots) {
|
|
297
|
+
var _getJSXStringAttribut;
|
|
298
|
+
if (!_babel_types.isJSXElement(node)) return;
|
|
299
|
+
const name = node.openingElement.name;
|
|
300
|
+
if (!_babel_types.isJSXIdentifier(name, { name: "Slot" })) return;
|
|
301
|
+
const slotName = (_getJSXStringAttribut = getJSXStringAttribute(node, "name")) !== null && _getJSXStringAttribut !== void 0 ? _getJSXStringAttribut : "default";
|
|
302
|
+
slots[slotName] || (slots[slotName] = {});
|
|
303
|
+
}
|
|
304
|
+
function extractHostAttributes(node, hostAttributes) {
|
|
305
|
+
if (!_babel_types.isJSXElement(node)) return;
|
|
306
|
+
const name = node.openingElement.name;
|
|
307
|
+
if (!_babel_types.isJSXIdentifier(name, { name: "Host" })) return;
|
|
308
|
+
for (const attr of node.openingElement.attributes) {
|
|
309
|
+
if (!_babel_types.isJSXAttribute(attr)) continue;
|
|
310
|
+
if (!_babel_types.isJSXIdentifier(attr.name)) continue;
|
|
311
|
+
const attrName = normalizeJsxAttrName(attr.name.name);
|
|
312
|
+
if (attrName.startsWith("data-") || attrName.startsWith("aria-") || attrName === "role" || attrName === "part" || attrName === "class" || attrName === "style" || attrName === "id" || attrName === "tabindex") hostAttributes.push(attrName);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function extractCssParts(node, cssParts) {
|
|
316
|
+
if (!_babel_types.isJSXElement(node)) return;
|
|
317
|
+
const value = getJSXStringAttribute(node, "part");
|
|
318
|
+
if (!value) return;
|
|
319
|
+
for (const part of value.split(/\s+/)) if (part) cssParts.push(part);
|
|
320
|
+
}
|
|
321
|
+
function getJSXStringAttribute(node, attrName) {
|
|
322
|
+
for (const attr of node.openingElement.attributes) {
|
|
323
|
+
if (!_babel_types.isJSXAttribute(attr)) continue;
|
|
324
|
+
if (!_babel_types.isJSXIdentifier(attr.name, { name: attrName })) continue;
|
|
325
|
+
if (!attr.value) return "";
|
|
326
|
+
if (_babel_types.isStringLiteral(attr.value)) return attr.value.value;
|
|
327
|
+
if (_babel_types.isJSXExpressionContainer(attr.value) && _babel_types.isExpression(attr.value.expression)) {
|
|
328
|
+
const value = staticValue(attr.value.expression);
|
|
329
|
+
if (typeof value === "string") return value;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function normalizeJsxAttrName(name) {
|
|
334
|
+
switch (name) {
|
|
335
|
+
case "className": return "class";
|
|
336
|
+
case "tabIndex": return "tabindex";
|
|
337
|
+
default: return name;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region packages/web-c/component-analyzer/src/extractTypeProps.ts
|
|
342
|
+
function collectLocalPropTypes(ast) {
|
|
343
|
+
const map = /* @__PURE__ */ new Map();
|
|
344
|
+
for (const node of ast.program.body) {
|
|
345
|
+
if (_babel_types.isExportNamedDeclaration(node)) {
|
|
346
|
+
const declaration = node.declaration;
|
|
347
|
+
if (_babel_types.isTSInterfaceDeclaration(declaration)) map.set(declaration.id.name, extractInterfaceProps(declaration));
|
|
348
|
+
if (_babel_types.isTSTypeAliasDeclaration(declaration)) {
|
|
349
|
+
const props = extractTypeAliasProps(declaration);
|
|
350
|
+
if (props) map.set(declaration.id.name, props);
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (_babel_types.isTSInterfaceDeclaration(node)) map.set(node.id.name, extractInterfaceProps(node));
|
|
355
|
+
if (_babel_types.isTSTypeAliasDeclaration(node)) {
|
|
356
|
+
const props = extractTypeAliasProps(node);
|
|
357
|
+
if (props) map.set(node.id.name, props);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return map;
|
|
361
|
+
}
|
|
362
|
+
function extractInterfaceProps(node) {
|
|
363
|
+
const result = {};
|
|
364
|
+
for (const member of node.body.body) {
|
|
365
|
+
if (!_babel_types.isTSPropertySignature(member)) continue;
|
|
366
|
+
const key = getObjectKey(member.key);
|
|
367
|
+
if (!key) continue;
|
|
368
|
+
result[key] = extractTsProperty(member);
|
|
369
|
+
}
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
function extractTypeAliasProps(node) {
|
|
373
|
+
if (!_babel_types.isTSTypeLiteral(node.typeAnnotation)) return void 0;
|
|
374
|
+
const result = {};
|
|
375
|
+
for (const member of node.typeAnnotation.members) {
|
|
376
|
+
if (!_babel_types.isTSPropertySignature(member)) continue;
|
|
377
|
+
const key = getObjectKey(member.key);
|
|
378
|
+
if (!key) continue;
|
|
379
|
+
result[key] = extractTsProperty(member);
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
function extractTsProperty(node) {
|
|
384
|
+
var _node$typeAnnotation;
|
|
385
|
+
const prop = { required: !node.optional };
|
|
386
|
+
const annotation = (_node$typeAnnotation = node.typeAnnotation) === null || _node$typeAnnotation === void 0 ? void 0 : _node$typeAnnotation.typeAnnotation;
|
|
387
|
+
if (annotation) {
|
|
388
|
+
const inferred = inferType(annotation);
|
|
389
|
+
Object.assign(prop, inferred);
|
|
390
|
+
}
|
|
391
|
+
const description = getLeadingDescription(node);
|
|
392
|
+
if (description) prop.description = description;
|
|
393
|
+
return prop;
|
|
394
|
+
}
|
|
395
|
+
function inferType(node) {
|
|
396
|
+
if (_babel_types.isTSStringKeyword(node)) return { type: "string" };
|
|
397
|
+
if (_babel_types.isTSNumberKeyword(node)) return { type: "number" };
|
|
398
|
+
if (_babel_types.isTSBooleanKeyword(node)) return { type: "boolean" };
|
|
399
|
+
if (_babel_types.isTSArrayType(node)) return { type: "array" };
|
|
400
|
+
if (_babel_types.isTSTypeLiteral(node)) return { type: "object" };
|
|
401
|
+
if (_babel_types.isTSUnionType(node)) {
|
|
402
|
+
const values = [];
|
|
403
|
+
let allStringLiteral = true;
|
|
404
|
+
for (const type of node.types) if (_babel_types.isTSLiteralType(type) && _babel_types.isStringLiteral(type.literal)) values.push(type.literal.value);
|
|
405
|
+
else allStringLiteral = false;
|
|
406
|
+
if (allStringLiteral && values.length > 0) return {
|
|
407
|
+
type: "string",
|
|
408
|
+
values
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return { type: "unknown" };
|
|
412
|
+
}
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region packages/web-c/component-analyzer/src/merge.ts
|
|
415
|
+
function buildComponentRecord(options) {
|
|
416
|
+
const { file, call, runtimeProps, typeProps, setupMeta, inlineMeta } = options;
|
|
417
|
+
const props = mergeProps(runtimeProps, typeProps, inlineMeta.props);
|
|
418
|
+
const events = mergeEvents(setupMeta.events, inlineMeta.events);
|
|
419
|
+
const slots = mergeSlots(setupMeta.slots, inlineMeta.slots);
|
|
420
|
+
const cssParts = unique([...setupMeta.cssParts, ...toStringArray(inlineMeta.cssParts)]);
|
|
421
|
+
const cssVars = unique(toStringArray(inlineMeta.cssVars));
|
|
422
|
+
const hostAttributes = unique(setupMeta.hostAttributes);
|
|
423
|
+
return {
|
|
424
|
+
tag: call.tag,
|
|
425
|
+
name: call.name,
|
|
426
|
+
exportName: call.exportName,
|
|
427
|
+
source: file,
|
|
428
|
+
props,
|
|
429
|
+
events,
|
|
430
|
+
slots,
|
|
431
|
+
hostAttributes,
|
|
432
|
+
cssParts,
|
|
433
|
+
cssVars,
|
|
434
|
+
description: typeof inlineMeta.description === "string" ? inlineMeta.description : void 0,
|
|
435
|
+
meta: stripKnownMetaFields(inlineMeta)
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function mergeProps(runtimeProps, typeProps, metaProps) {
|
|
439
|
+
const names = unique([
|
|
440
|
+
...Object.keys(runtimeProps),
|
|
441
|
+
...Object.keys(typeProps),
|
|
442
|
+
...Object.keys(metaProps !== null && metaProps !== void 0 ? metaProps : {})
|
|
443
|
+
]);
|
|
444
|
+
const result = {};
|
|
445
|
+
for (const name of names) {
|
|
446
|
+
var _typeProps$name, _runtimeProps$name, _metaProps$name, _ref, _rp$type, _tp$description, _rp$default, _rp$reflect, _rp$attr;
|
|
447
|
+
const tp = (_typeProps$name = typeProps[name]) !== null && _typeProps$name !== void 0 ? _typeProps$name : {};
|
|
448
|
+
const rp = (_runtimeProps$name = runtimeProps[name]) !== null && _runtimeProps$name !== void 0 ? _runtimeProps$name : {};
|
|
449
|
+
const mp = (_metaProps$name = metaProps === null || metaProps === void 0 ? void 0 : metaProps[name]) !== null && _metaProps$name !== void 0 ? _metaProps$name : {};
|
|
450
|
+
result[name] = {
|
|
451
|
+
type: (_ref = (_rp$type = rp.type) !== null && _rp$type !== void 0 ? _rp$type : tp.type) !== null && _ref !== void 0 ? _ref : "unknown",
|
|
452
|
+
required: tp.required,
|
|
453
|
+
values: tp.values,
|
|
454
|
+
description: (_tp$description = tp.description) !== null && _tp$description !== void 0 ? _tp$description : mp.description,
|
|
455
|
+
default: (_rp$default = rp.default) !== null && _rp$default !== void 0 ? _rp$default : mp.default,
|
|
456
|
+
reflect: (_rp$reflect = rp.reflect) !== null && _rp$reflect !== void 0 ? _rp$reflect : mp.reflect,
|
|
457
|
+
attr: (_rp$attr = rp.attr) !== null && _rp$attr !== void 0 ? _rp$attr : mp.attr
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return result;
|
|
461
|
+
}
|
|
462
|
+
function mergeEvents(inferred, explicit) {
|
|
463
|
+
return {
|
|
464
|
+
...inferred,
|
|
465
|
+
...explicit !== null && explicit !== void 0 ? explicit : {}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function mergeSlots(inferred, explicit) {
|
|
469
|
+
return {
|
|
470
|
+
...inferred,
|
|
471
|
+
...explicit !== null && explicit !== void 0 ? explicit : {}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function toStringArray(value) {
|
|
475
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
476
|
+
}
|
|
477
|
+
function unique(values) {
|
|
478
|
+
return Array.from(new Set(values)).sort();
|
|
479
|
+
}
|
|
480
|
+
function stripKnownMetaFields(meta) {
|
|
481
|
+
const rest = { ...meta };
|
|
482
|
+
delete rest.description;
|
|
483
|
+
delete rest.props;
|
|
484
|
+
delete rest.events;
|
|
485
|
+
delete rest.slots;
|
|
486
|
+
delete rest.cssVars;
|
|
487
|
+
delete rest.cssParts;
|
|
488
|
+
return Object.keys(rest).length ? rest : void 0;
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region packages/web-c/component-analyzer/src/analyzeFile.ts
|
|
492
|
+
function analyzeFile(options) {
|
|
493
|
+
const { file, code } = options;
|
|
494
|
+
const diagnostics = [];
|
|
495
|
+
const components = [];
|
|
496
|
+
try {
|
|
497
|
+
const ast = parseSource(code, file);
|
|
498
|
+
const calls = extractDefineElementCalls(ast);
|
|
499
|
+
const localPropTypes = collectLocalPropTypes(ast);
|
|
500
|
+
for (const call of calls) {
|
|
501
|
+
var _localPropTypes$get;
|
|
502
|
+
const runtimeProps = extractRuntimeProps(call.options);
|
|
503
|
+
const typeProps = call.propsTypeName ? (_localPropTypes$get = localPropTypes.get(call.propsTypeName)) !== null && _localPropTypes$get !== void 0 ? _localPropTypes$get : {} : {};
|
|
504
|
+
if (call.propsTypeName && !localPropTypes.has(call.propsTypeName) && !isGlobalUtilityType(call.propsTypeName)) diagnostics.push({
|
|
505
|
+
level: "warning",
|
|
506
|
+
file,
|
|
507
|
+
message: `Cannot resolve local props type "${call.propsTypeName}".`
|
|
508
|
+
});
|
|
509
|
+
const setupMeta = extractSetupMeta(call.setup);
|
|
510
|
+
const inlineMeta = extractInlineMeta(call.options);
|
|
511
|
+
components.push(buildComponentRecord({
|
|
512
|
+
file,
|
|
513
|
+
call,
|
|
514
|
+
runtimeProps,
|
|
515
|
+
typeProps,
|
|
516
|
+
setupMeta,
|
|
517
|
+
inlineMeta
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
diagnostics.push({
|
|
522
|
+
level: "error",
|
|
523
|
+
file,
|
|
524
|
+
message: error instanceof Error ? error.message : String(error)
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
file,
|
|
529
|
+
components,
|
|
530
|
+
diagnostics
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function isGlobalUtilityType(name) {
|
|
534
|
+
return GLOBAL_UTILITY_TYPES.has(name);
|
|
535
|
+
}
|
|
536
|
+
const GLOBAL_UTILITY_TYPES = new Set([
|
|
537
|
+
"Awaited",
|
|
538
|
+
"ConstructorParameters",
|
|
539
|
+
"Exclude",
|
|
540
|
+
"Extract",
|
|
541
|
+
"InstanceType",
|
|
542
|
+
"NonNullable",
|
|
543
|
+
"Omit",
|
|
544
|
+
"Parameters",
|
|
545
|
+
"Partial",
|
|
546
|
+
"Pick",
|
|
547
|
+
"Readonly",
|
|
548
|
+
"Record",
|
|
549
|
+
"Required",
|
|
550
|
+
"ReturnType",
|
|
551
|
+
"ThisParameterType",
|
|
552
|
+
"ThisType",
|
|
553
|
+
"Uppercase",
|
|
554
|
+
"Lowercase",
|
|
555
|
+
"Capitalize",
|
|
556
|
+
"Uncapitalize"
|
|
557
|
+
]);
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region packages/web-c/component-analyzer/src/analyzeComponents.ts
|
|
560
|
+
async function analyzeComponents(options) {
|
|
561
|
+
var _options$root, _options$exclude;
|
|
562
|
+
const root = (_options$root = options.root) !== null && _options$root !== void 0 ? _options$root : process.cwd();
|
|
563
|
+
const files = await (0, fast_glob.default)(options.include, {
|
|
564
|
+
cwd: root,
|
|
565
|
+
absolute: true,
|
|
566
|
+
ignore: (_options$exclude = options.exclude) !== null && _options$exclude !== void 0 ? _options$exclude : ["node_modules/**", "**/dist/**"]
|
|
567
|
+
});
|
|
568
|
+
const components = [];
|
|
569
|
+
const diagnostics = [];
|
|
570
|
+
for (const file of files) {
|
|
571
|
+
const code = await node_fs_promises.default.readFile(file, "utf-8");
|
|
572
|
+
const result = analyzeFile({
|
|
573
|
+
file: normalizePath(node_path.default.relative(root, file)),
|
|
574
|
+
code
|
|
575
|
+
});
|
|
576
|
+
components.push(...result.components);
|
|
577
|
+
diagnostics.push(...result.diagnostics);
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
manifest: {
|
|
581
|
+
version: 1,
|
|
582
|
+
components
|
|
583
|
+
},
|
|
584
|
+
diagnostics
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function normalizePath(value) {
|
|
588
|
+
return value.split(node_path.default.sep).join("/");
|
|
589
|
+
}
|
|
590
|
+
//#endregion
|
|
591
|
+
exports.analyzeComponents = analyzeComponents;
|
|
592
|
+
exports.analyzeFile = analyzeFile;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type ComponentPropType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'unknown';
|
|
2
|
+
export interface ComponentManifest {
|
|
3
|
+
version: 1;
|
|
4
|
+
components: ComponentRecord[];
|
|
5
|
+
}
|
|
6
|
+
export interface ComponentRecord {
|
|
7
|
+
tag: string;
|
|
8
|
+
name: string;
|
|
9
|
+
exportName: string;
|
|
10
|
+
source: string;
|
|
11
|
+
props: Record<string, ComponentProp>;
|
|
12
|
+
events: Record<string, ComponentEvent>;
|
|
13
|
+
slots: Record<string, ComponentSlot>;
|
|
14
|
+
hostAttributes: string[];
|
|
15
|
+
cssParts: string[];
|
|
16
|
+
cssVars: string[];
|
|
17
|
+
description?: string;
|
|
18
|
+
meta?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface ComponentProp {
|
|
21
|
+
type: ComponentPropType;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
values?: string[];
|
|
24
|
+
default?: unknown;
|
|
25
|
+
reflect?: boolean;
|
|
26
|
+
attr?: string | false;
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ComponentEvent {
|
|
30
|
+
detail?: Record<string, string>;
|
|
31
|
+
description?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ComponentSlot {
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface AnalyzeFileResult {
|
|
37
|
+
file: string;
|
|
38
|
+
components: ComponentRecord[];
|
|
39
|
+
diagnostics: AnalyzerDiagnostic[];
|
|
40
|
+
}
|
|
41
|
+
export interface AnalyzeComponentsResult {
|
|
42
|
+
manifest: ComponentManifest;
|
|
43
|
+
diagnostics: AnalyzerDiagnostic[];
|
|
44
|
+
}
|
|
45
|
+
export interface AnalyzerDiagnostic {
|
|
46
|
+
level: 'warning' | 'error';
|
|
47
|
+
file: string;
|
|
48
|
+
message: string;
|
|
49
|
+
}
|
|
50
|
+
export interface AnalyzeFileOptions {
|
|
51
|
+
file: string;
|
|
52
|
+
code: string;
|
|
53
|
+
}
|
|
54
|
+
export interface AnalyzeComponentsOptions {
|
|
55
|
+
root?: string;
|
|
56
|
+
include: string[];
|
|
57
|
+
exclude?: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export declare function analyzeFile(options: AnalyzeFileOptions): AnalyzeFileResult;
|
|
61
|
+
|
|
62
|
+
export declare function analyzeComponents(options: AnalyzeComponentsOptions): Promise<AnalyzeComponentsResult>;
|
|
63
|
+
|
|
64
|
+
|