@sky.ui/mcp 0.0.1
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/LICENSE.md +139 -0
- package/README.md +182 -0
- package/data/chart-api-sections.json +4185 -0
- package/data/component-tier.json +25 -0
- package/data/design-guidelines/p0-guidelines.json +13381 -0
- package/data/reactivity-readme-snapshot.json +1 -0
- package/data/theme-authoring-contract.json +598 -0
- package/data/utils-suggestion-snapshot.json +1 -0
- package/dist/cache.d.ts +3 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +15 -0
- package/dist/cache.js.map +1 -0
- package/dist/catalog.d.ts +60 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +343 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cem.d.ts +26 -0
- package/dist/cem.d.ts.map +1 -0
- package/dist/cem.js +348 -0
- package/dist/cem.js.map +1 -0
- package/dist/chart-usage-tool.d.ts +20 -0
- package/dist/chart-usage-tool.d.ts.map +1 -0
- package/dist/chart-usage-tool.js +153 -0
- package/dist/chart-usage-tool.js.map +1 -0
- package/dist/component-docs-tool.d.ts +45 -0
- package/dist/component-docs-tool.d.ts.map +1 -0
- package/dist/component-docs-tool.js +217 -0
- package/dist/component-docs-tool.js.map +1 -0
- package/dist/component-method-filter.d.ts +3 -0
- package/dist/component-method-filter.d.ts.map +1 -0
- package/dist/component-method-filter.js +32 -0
- package/dist/component-method-filter.js.map +1 -0
- package/dist/component-tier.d.ts +20 -0
- package/dist/component-tier.d.ts.map +1 -0
- package/dist/component-tier.js +59 -0
- package/dist/component-tier.js.map +1 -0
- package/dist/component-usage-tool.d.ts +20 -0
- package/dist/component-usage-tool.d.ts.map +1 -0
- package/dist/component-usage-tool.js +90 -0
- package/dist/component-usage-tool.js.map +1 -0
- package/dist/design-guidelines/a11y-engine.d.ts +22 -0
- package/dist/design-guidelines/a11y-engine.d.ts.map +1 -0
- package/dist/design-guidelines/a11y-engine.js +78 -0
- package/dist/design-guidelines/a11y-engine.js.map +1 -0
- package/dist/design-guidelines/compatibility-engine.d.ts +20 -0
- package/dist/design-guidelines/compatibility-engine.d.ts.map +1 -0
- package/dist/design-guidelines/compatibility-engine.js +57 -0
- package/dist/design-guidelines/compatibility-engine.js.map +1 -0
- package/dist/design-guidelines/component-guideline-engine.d.ts +19 -0
- package/dist/design-guidelines/component-guideline-engine.d.ts.map +1 -0
- package/dist/design-guidelines/component-guideline-engine.js +40 -0
- package/dist/design-guidelines/component-guideline-engine.js.map +1 -0
- package/dist/design-guidelines/composition-engine.d.ts +10 -0
- package/dist/design-guidelines/composition-engine.d.ts.map +1 -0
- package/dist/design-guidelines/composition-engine.js +29 -0
- package/dist/design-guidelines/composition-engine.js.map +1 -0
- package/dist/design-guidelines/pattern-engine.d.ts +20 -0
- package/dist/design-guidelines/pattern-engine.d.ts.map +1 -0
- package/dist/design-guidelines/pattern-engine.js +58 -0
- package/dist/design-guidelines/pattern-engine.js.map +1 -0
- package/dist/design-guidelines/recommendation-engine.d.ts +11 -0
- package/dist/design-guidelines/recommendation-engine.d.ts.map +1 -0
- package/dist/design-guidelines/recommendation-engine.js +88 -0
- package/dist/design-guidelines/recommendation-engine.js.map +1 -0
- package/dist/design-guidelines/repository.d.ts +19 -0
- package/dist/design-guidelines/repository.d.ts.map +1 -0
- package/dist/design-guidelines/repository.js +71 -0
- package/dist/design-guidelines/repository.js.map +1 -0
- package/dist/design-guidelines/review-engine.d.ts +20 -0
- package/dist/design-guidelines/review-engine.d.ts.map +1 -0
- package/dist/design-guidelines/review-engine.js +179 -0
- package/dist/design-guidelines/review-engine.js.map +1 -0
- package/dist/design-guidelines/schema.d.ts +256 -0
- package/dist/design-guidelines/schema.d.ts.map +1 -0
- package/dist/design-guidelines/schema.js +124 -0
- package/dist/design-guidelines/schema.js.map +1 -0
- package/dist/design-guidelines/token-engine.d.ts +23 -0
- package/dist/design-guidelines/token-engine.d.ts.map +1 -0
- package/dist/design-guidelines/token-engine.js +119 -0
- package/dist/design-guidelines/token-engine.js.map +1 -0
- package/dist/docs-read.d.ts +28 -0
- package/dist/docs-read.d.ts.map +1 -0
- package/dist/docs-read.js +102 -0
- package/dist/docs-read.js.map +1 -0
- package/dist/docs.d.ts +73 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +323 -0
- package/dist/docs.js.map +1 -0
- package/dist/example-site-doc-enrichment.d.ts +27 -0
- package/dist/example-site-doc-enrichment.d.ts.map +1 -0
- package/dist/example-site-doc-enrichment.js +171 -0
- package/dist/example-site-doc-enrichment.js.map +1 -0
- package/dist/example-site-fetch.d.ts +44 -0
- package/dist/example-site-fetch.d.ts.map +1 -0
- package/dist/example-site-fetch.js +255 -0
- package/dist/example-site-fetch.js.map +1 -0
- package/dist/framework-wrappers.d.ts +46 -0
- package/dist/framework-wrappers.d.ts.map +1 -0
- package/dist/framework-wrappers.js +131 -0
- package/dist/framework-wrappers.js.map +1 -0
- package/dist/mcp-response-truth.d.ts +18 -0
- package/dist/mcp-response-truth.d.ts.map +1 -0
- package/dist/mcp-response-truth.js +45 -0
- package/dist/mcp-response-truth.js.map +1 -0
- package/dist/mcp-runtime-status.d.ts +37 -0
- package/dist/mcp-runtime-status.d.ts.map +1 -0
- package/dist/mcp-runtime-status.js +96 -0
- package/dist/mcp-runtime-status.js.map +1 -0
- package/dist/mcp-stdio-entry.d.ts +3 -0
- package/dist/mcp-stdio-entry.d.ts.map +1 -0
- package/dist/mcp-stdio-entry.js +35 -0
- package/dist/mcp-stdio-entry.js.map +1 -0
- package/dist/mcp-tool-errors.d.ts +35 -0
- package/dist/mcp-tool-errors.d.ts.map +1 -0
- package/dist/mcp-tool-errors.js +42 -0
- package/dist/mcp-tool-errors.js.map +1 -0
- package/dist/parse-component-ast.d.ts +112 -0
- package/dist/parse-component-ast.d.ts.map +1 -0
- package/dist/parse-component-ast.js +695 -0
- package/dist/parse-component-ast.js.map +1 -0
- package/dist/post-endpoint-server.d.ts +19 -0
- package/dist/post-endpoint-server.d.ts.map +1 -0
- package/dist/post-endpoint-server.js +777 -0
- package/dist/post-endpoint-server.js.map +1 -0
- package/dist/reactivity-info.d.ts +22 -0
- package/dist/reactivity-info.d.ts.map +1 -0
- package/dist/reactivity-info.js +99 -0
- package/dist/reactivity-info.js.map +1 -0
- package/dist/reactivity-layer-tool.d.ts +18 -0
- package/dist/reactivity-layer-tool.d.ts.map +1 -0
- package/dist/reactivity-layer-tool.js +58 -0
- package/dist/reactivity-layer-tool.js.map +1 -0
- package/dist/reactivity-readme-topics.d.ts +46 -0
- package/dist/reactivity-readme-topics.d.ts.map +1 -0
- package/dist/reactivity-readme-topics.js +206 -0
- package/dist/reactivity-readme-topics.js.map +1 -0
- package/dist/read-only-tool-allowlist.d.ts +7 -0
- package/dist/read-only-tool-allowlist.d.ts.map +1 -0
- package/dist/read-only-tool-allowlist.js +17 -0
- package/dist/read-only-tool-allowlist.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +786 -0
- package/dist/server.js.map +1 -0
- package/dist/source-read.d.ts +31 -0
- package/dist/source-read.d.ts.map +1 -0
- package/dist/source-read.js +213 -0
- package/dist/source-read.js.map +1 -0
- package/dist/theme-authoring/contract-schema.d.ts +191 -0
- package/dist/theme-authoring/contract-schema.d.ts.map +1 -0
- package/dist/theme-authoring/contract-schema.js +49 -0
- package/dist/theme-authoring/contract-schema.js.map +1 -0
- package/dist/theme-authoring/repository.d.ts +18 -0
- package/dist/theme-authoring/repository.d.ts.map +1 -0
- package/dist/theme-authoring/repository.js +55 -0
- package/dist/theme-authoring/repository.js.map +1 -0
- package/dist/theme-tool.d.ts +26 -0
- package/dist/theme-tool.d.ts.map +1 -0
- package/dist/theme-tool.js +312 -0
- package/dist/theme-tool.js.map +1 -0
- package/dist/utils-guide-topics.d.ts +39 -0
- package/dist/utils-guide-topics.d.ts.map +1 -0
- package/dist/utils-guide-topics.js +202 -0
- package/dist/utils-guide-topics.js.map +1 -0
- package/dist/utils-info.d.ts +75 -0
- package/dist/utils-info.d.ts.map +1 -0
- package/dist/utils-info.js +219 -0
- package/dist/utils-info.js.map +1 -0
- package/dist/utils-suggestion-grounding.d.ts +59 -0
- package/dist/utils-suggestion-grounding.d.ts.map +1 -0
- package/dist/utils-suggestion-grounding.js +267 -0
- package/dist/utils-suggestion-grounding.js.map +1 -0
- package/docs/agent-recipe-example-site.md +91 -0
- package/docs/ai-design-mcp-blueprint.md +75 -0
- package/docs/cross-model-mcp-playbook.md +127 -0
- package/docs/example-site-and-mcp-training.md +82 -0
- package/docs/mcp-capability-status.md +51 -0
- package/docs/mcp-tooling-roadmap.md +36 -0
- package/docs/sky-chart-option-api.md +47 -0
- package/docs/starter-prompt.md +17 -0
- package/docs/theme-config-guide.md +178 -0
- package/docs/utils-usage-guide.md +110 -0
- package/docs/vue-wrapper-v-model.md +36 -0
- package/package.json +63 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import * as ts from 'typescript';
|
|
4
|
+
function kebabToPascal(kebab) {
|
|
5
|
+
return kebab
|
|
6
|
+
.split('-')
|
|
7
|
+
.map((p) => (p ? p.charAt(0).toUpperCase() + p.slice(1).toLowerCase() : ''))
|
|
8
|
+
.join('');
|
|
9
|
+
}
|
|
10
|
+
function commentToString(comment) {
|
|
11
|
+
if (comment == null) {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
if (typeof comment === 'string') {
|
|
15
|
+
return comment.trim();
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(comment)) {
|
|
18
|
+
return comment
|
|
19
|
+
.map((part) => {
|
|
20
|
+
if (part.kind === ts.SyntaxKind.JSDocText) {
|
|
21
|
+
return part.text;
|
|
22
|
+
}
|
|
23
|
+
return '';
|
|
24
|
+
})
|
|
25
|
+
.join('')
|
|
26
|
+
.trim();
|
|
27
|
+
}
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
function getCustomElementTagName(classNode, _sf) {
|
|
31
|
+
if (!ts.canHaveDecorators(classNode)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const decorators = ts.getDecorators(classNode);
|
|
35
|
+
if (!decorators?.length) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
for (const dec of decorators) {
|
|
39
|
+
const ex = dec.expression;
|
|
40
|
+
if (!ts.isCallExpression(ex)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!ts.isIdentifier(ex.expression) || ex.expression.text !== 'customElement') {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const arg0 = ex.arguments[0];
|
|
47
|
+
if (arg0 && ts.isStringLiteralLike(arg0)) {
|
|
48
|
+
return arg0.text;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function findPrimaryComponentClass(sf, componentFolderName) {
|
|
54
|
+
const expected = kebabToPascal(componentFolderName);
|
|
55
|
+
const candidates = [];
|
|
56
|
+
const visit = (node) => {
|
|
57
|
+
if (ts.isClassDeclaration(node) && getCustomElementTagName(node, sf)) {
|
|
58
|
+
candidates.push(node);
|
|
59
|
+
}
|
|
60
|
+
ts.forEachChild(node, visit);
|
|
61
|
+
};
|
|
62
|
+
visit(sf);
|
|
63
|
+
if (candidates.length === 0) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
const named = candidates.find((c) => c.name?.text === expected);
|
|
67
|
+
return named ?? candidates[0];
|
|
68
|
+
}
|
|
69
|
+
/** Parse `{Type} name - description` JSDoc lines, including nested generic braces. */
|
|
70
|
+
function balancedBracedType(text) {
|
|
71
|
+
const t = text.trimStart();
|
|
72
|
+
if (!t.startsWith('{'))
|
|
73
|
+
return null;
|
|
74
|
+
let depth = 0;
|
|
75
|
+
for (let i = 0; i < t.length; i++) {
|
|
76
|
+
const ch = t[i];
|
|
77
|
+
if (ch === '{')
|
|
78
|
+
depth++;
|
|
79
|
+
else if (ch === '}') {
|
|
80
|
+
depth--;
|
|
81
|
+
if (depth === 0) {
|
|
82
|
+
return { type: t.slice(1, i).trim(), rest: t.slice(i + 1).trimStart() };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function parseGenericPropLike(text) {
|
|
89
|
+
const t = text.trim();
|
|
90
|
+
const braced = balancedBracedType(t);
|
|
91
|
+
if (braced) {
|
|
92
|
+
const nameMatch = /^(\S+)\s*[-–—]?\s*(.*)$/s.exec(braced.rest);
|
|
93
|
+
if (nameMatch) {
|
|
94
|
+
return {
|
|
95
|
+
type: braced.type,
|
|
96
|
+
name: nameMatch[1].trim(),
|
|
97
|
+
description: nameMatch[2]?.trim() || undefined,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const loose = /^(\S+)\s*[-–—]?\s*(.*)$/s.exec(t);
|
|
102
|
+
if (loose) {
|
|
103
|
+
return { name: loose[1].trim(), description: loose[2]?.trim() || undefined };
|
|
104
|
+
}
|
|
105
|
+
return { description: t };
|
|
106
|
+
}
|
|
107
|
+
function pushMisc(misc, tagName, text) {
|
|
108
|
+
const t = text.trim();
|
|
109
|
+
if (t) {
|
|
110
|
+
misc.push({ tag: tagName, text: t });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function collectFromTags(classNode, sf, doc) {
|
|
114
|
+
const tags = ts.getJSDocTags(classNode);
|
|
115
|
+
for (const tag of tags) {
|
|
116
|
+
const rawName = tag.tagName.getText(sf).toLowerCase();
|
|
117
|
+
if (ts.isJSDocPropertyTag(tag)) {
|
|
118
|
+
const name = tag.name.getText(sf);
|
|
119
|
+
const typ = tag.typeExpression?.getText(sf);
|
|
120
|
+
const desc = commentToString(tag.comment);
|
|
121
|
+
doc.properties.push({ name, type: typ, description: desc || undefined });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const text = commentToString(tag.comment);
|
|
125
|
+
switch (rawName) {
|
|
126
|
+
case 'element':
|
|
127
|
+
doc.element = text || doc.element;
|
|
128
|
+
break;
|
|
129
|
+
case 'summary':
|
|
130
|
+
doc.summary = text || doc.summary;
|
|
131
|
+
break;
|
|
132
|
+
case 'status':
|
|
133
|
+
doc.status = text || doc.status;
|
|
134
|
+
break;
|
|
135
|
+
case 'since':
|
|
136
|
+
doc.since = text || doc.since;
|
|
137
|
+
break;
|
|
138
|
+
case 'documentation':
|
|
139
|
+
doc.documentation = text || doc.documentation;
|
|
140
|
+
break;
|
|
141
|
+
case 'slot': {
|
|
142
|
+
const t = text.trim();
|
|
143
|
+
if (t.startsWith('-')) {
|
|
144
|
+
doc.slots.push({ name: '', description: t.replace(/^\s*-\s+/, '').trim() || undefined });
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
const m = /^(.+?)\s+-\s+(.+)$/s.exec(t);
|
|
148
|
+
if (m) {
|
|
149
|
+
doc.slots.push({ name: m[1].trim(), description: m[2].trim() });
|
|
150
|
+
}
|
|
151
|
+
else if (t) {
|
|
152
|
+
doc.slots.push({ name: t, description: undefined });
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 'csspart': {
|
|
157
|
+
const m = /^\s*([^\s-][^\n]*?)\s+-\s+(.+)$/s.exec(text);
|
|
158
|
+
if (m) {
|
|
159
|
+
doc.cssParts.push({ name: m[1].trim(), description: m[2].trim() });
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const name = text.split(/\s+/)[0]?.trim();
|
|
163
|
+
if (name) {
|
|
164
|
+
doc.cssParts.push({ name, description: text.slice(name.length).trim() || undefined });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case 'event':
|
|
170
|
+
case 'fires': {
|
|
171
|
+
const parsed = parseGenericPropLike(text);
|
|
172
|
+
const nm = parsed.name ?? text.split(/\s+/)[0]?.trim() ?? 'event';
|
|
173
|
+
doc.events.push({
|
|
174
|
+
name: nm,
|
|
175
|
+
type: parsed.type,
|
|
176
|
+
description: parsed.description ?? text.trim()
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case 'property':
|
|
181
|
+
case 'prop': {
|
|
182
|
+
const parsed = parseGenericPropLike(text);
|
|
183
|
+
if (parsed.name) {
|
|
184
|
+
doc.properties.push({
|
|
185
|
+
name: parsed.name,
|
|
186
|
+
type: parsed.type,
|
|
187
|
+
description: parsed.description
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
pushMisc(doc.miscTags, rawName, text);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 'example': {
|
|
196
|
+
const ex = text.trim();
|
|
197
|
+
if (ex) {
|
|
198
|
+
doc.examples.push({ source: ex });
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
default:
|
|
203
|
+
pushMisc(doc.miscTags, rawName, text);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function trimExamples(examples, maxCharsPerExample) {
|
|
208
|
+
let truncated = false;
|
|
209
|
+
const out = examples.map((e) => {
|
|
210
|
+
if (e.source.length <= maxCharsPerExample) {
|
|
211
|
+
return e;
|
|
212
|
+
}
|
|
213
|
+
truncated = true;
|
|
214
|
+
return { source: `${e.source.slice(0, maxCharsPerExample)}\n… [truncated]` };
|
|
215
|
+
});
|
|
216
|
+
return { examples: out, truncated };
|
|
217
|
+
}
|
|
218
|
+
function collectPublicMethods(classNode, sf, doc) {
|
|
219
|
+
for (const member of classNode.members) {
|
|
220
|
+
if (!ts.isMethodDeclaration(member))
|
|
221
|
+
continue;
|
|
222
|
+
if (!member.name || !ts.isIdentifier(member.name))
|
|
223
|
+
continue;
|
|
224
|
+
const name = member.name.text;
|
|
225
|
+
if (!name || name.startsWith('_'))
|
|
226
|
+
continue;
|
|
227
|
+
const mods = member.modifiers;
|
|
228
|
+
if (mods?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword))
|
|
229
|
+
continue;
|
|
230
|
+
if (mods?.some((m) => m.kind === ts.SyntaxKind.StaticKeyword))
|
|
231
|
+
continue;
|
|
232
|
+
const params = member.parameters.map((p) => ({
|
|
233
|
+
name: p.name.getText(sf),
|
|
234
|
+
type: p.type?.getText(sf),
|
|
235
|
+
optional: !!p.questionToken || !!p.initializer
|
|
236
|
+
}));
|
|
237
|
+
const paramDescByName = new Map();
|
|
238
|
+
const tags = ts.getJSDocTags(member);
|
|
239
|
+
let returnsDescription;
|
|
240
|
+
for (const tag of tags) {
|
|
241
|
+
const raw = tag.tagName.getText(sf).toLowerCase();
|
|
242
|
+
if (raw === 'param' && ts.isJSDocParameterTag(tag)) {
|
|
243
|
+
const pName = tag.name.getText(sf);
|
|
244
|
+
const pDesc = commentToString(tag.comment);
|
|
245
|
+
if (pName && pDesc)
|
|
246
|
+
paramDescByName.set(pName, pDesc);
|
|
247
|
+
}
|
|
248
|
+
if (raw === 'returns' || raw === 'return') {
|
|
249
|
+
const d = commentToString(tag.comment);
|
|
250
|
+
if (d)
|
|
251
|
+
returnsDescription = d;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const jsDocs = member.jsDoc ?? [];
|
|
255
|
+
const description = jsDocs
|
|
256
|
+
.map((d) => commentToString(d.comment))
|
|
257
|
+
.filter(Boolean)
|
|
258
|
+
.join('\n')
|
|
259
|
+
.trim();
|
|
260
|
+
doc.methods.push({
|
|
261
|
+
name,
|
|
262
|
+
description: description || undefined,
|
|
263
|
+
params: params.map((p) => ({ ...p, description: paramDescByName.get(p.name) })),
|
|
264
|
+
returns: member.type || returnsDescription
|
|
265
|
+
? {
|
|
266
|
+
type: member.type?.getText(sf),
|
|
267
|
+
description: returnsDescription
|
|
268
|
+
}
|
|
269
|
+
: undefined
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const BUILTIN_TYPE_NAMES = new Set([
|
|
274
|
+
'string',
|
|
275
|
+
'number',
|
|
276
|
+
'boolean',
|
|
277
|
+
'null',
|
|
278
|
+
'undefined',
|
|
279
|
+
'void',
|
|
280
|
+
'unknown',
|
|
281
|
+
'never',
|
|
282
|
+
'object',
|
|
283
|
+
'array',
|
|
284
|
+
'record',
|
|
285
|
+
'partial',
|
|
286
|
+
'readonly',
|
|
287
|
+
'required',
|
|
288
|
+
'pick',
|
|
289
|
+
'omit',
|
|
290
|
+
'exclude',
|
|
291
|
+
'extract',
|
|
292
|
+
'nonnullable',
|
|
293
|
+
'promise',
|
|
294
|
+
'map',
|
|
295
|
+
'set',
|
|
296
|
+
'weakmap',
|
|
297
|
+
'weakset',
|
|
298
|
+
'date',
|
|
299
|
+
'regexp',
|
|
300
|
+
'customEvent',
|
|
301
|
+
'event',
|
|
302
|
+
'htmlelement',
|
|
303
|
+
'element',
|
|
304
|
+
'window',
|
|
305
|
+
'document',
|
|
306
|
+
'any'
|
|
307
|
+
]);
|
|
308
|
+
function collectTypeRefsFromText(typeText, out) {
|
|
309
|
+
if (!typeText)
|
|
310
|
+
return;
|
|
311
|
+
const re = /\b[A-Za-z_]\w*\b/g;
|
|
312
|
+
let m;
|
|
313
|
+
while ((m = re.exec(typeText)) !== null) {
|
|
314
|
+
const token = m[0];
|
|
315
|
+
if (!token)
|
|
316
|
+
continue;
|
|
317
|
+
const lowered = token.toLowerCase();
|
|
318
|
+
if (BUILTIN_TYPE_NAMES.has(lowered))
|
|
319
|
+
continue;
|
|
320
|
+
if (token === token.toUpperCase())
|
|
321
|
+
continue; // likely TS keywords-like enums/constants
|
|
322
|
+
out.add(token);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Prefer the better human description when both CEM and JSDoc supply text.
|
|
327
|
+
* CEM summaries are often terse; JSDoc next to `@property` / `@event` is usually richer—use length + tie-break to AST.
|
|
328
|
+
*/
|
|
329
|
+
export function pickRicherDescription(cemDesc, astDesc) {
|
|
330
|
+
const c = cemDesc?.trim() ?? '';
|
|
331
|
+
const a = astDesc?.trim() ?? '';
|
|
332
|
+
if (!c)
|
|
333
|
+
return a || undefined;
|
|
334
|
+
if (!a)
|
|
335
|
+
return c || undefined;
|
|
336
|
+
if (a.length > c.length)
|
|
337
|
+
return a;
|
|
338
|
+
if (c.length > a.length)
|
|
339
|
+
return c;
|
|
340
|
+
return a;
|
|
341
|
+
}
|
|
342
|
+
/** Prefer the more specific / documented type (e.g. `CustomEvent<Foo>` over `Event` or empty). */
|
|
343
|
+
export function pickRicherPropertyOrEventType(a, b) {
|
|
344
|
+
const x = a?.trim() ?? '';
|
|
345
|
+
const y = b?.trim() ?? '';
|
|
346
|
+
if (!x)
|
|
347
|
+
return y || undefined;
|
|
348
|
+
if (!y)
|
|
349
|
+
return x || undefined;
|
|
350
|
+
if (x === 'any' && y)
|
|
351
|
+
return y;
|
|
352
|
+
if (y === 'any' && x)
|
|
353
|
+
return x;
|
|
354
|
+
const score = (t) => {
|
|
355
|
+
let s = t.length;
|
|
356
|
+
if (t.includes('CustomEvent'))
|
|
357
|
+
s += 12;
|
|
358
|
+
if (t.includes('<') || t.includes('|') || t.includes('[]'))
|
|
359
|
+
s += 6;
|
|
360
|
+
return s;
|
|
361
|
+
};
|
|
362
|
+
return score(y) > score(x) ? y : x;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* When CEM is the primary source, it often omits or weakly types `events` / `properties`,
|
|
366
|
+
* or carries shorter descriptions than JSDoc.
|
|
367
|
+
* Merge with the AST (JSDoc) list: union by `name`, richer type via {@link pickRicherPropertyOrEventType},
|
|
368
|
+
* richer description via {@link pickRicherDescription}, keep CEM order then append AST-only.
|
|
369
|
+
*/
|
|
370
|
+
export function mergeCemWithAstEventLists(cem, ast) {
|
|
371
|
+
return mergePropOrEventLikeLists(cem, ast);
|
|
372
|
+
}
|
|
373
|
+
export function mergeCemWithAstPropertyLists(cem, ast) {
|
|
374
|
+
return mergePropOrEventLikeLists(cem, ast);
|
|
375
|
+
}
|
|
376
|
+
function mergePropOrEventLikeLists(cem, ast) {
|
|
377
|
+
if (ast.length === 0)
|
|
378
|
+
return cem;
|
|
379
|
+
if (cem.length === 0)
|
|
380
|
+
return ast;
|
|
381
|
+
const mergedByName = new Map();
|
|
382
|
+
for (const e of cem) {
|
|
383
|
+
mergedByName.set(e.name, { ...e });
|
|
384
|
+
}
|
|
385
|
+
for (const e of ast) {
|
|
386
|
+
const prev = mergedByName.get(e.name);
|
|
387
|
+
if (!prev) {
|
|
388
|
+
mergedByName.set(e.name, { ...e });
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const t = pickRicherPropertyOrEventType(prev.type, e.type);
|
|
392
|
+
const description = pickRicherDescription(prev.description, e.description);
|
|
393
|
+
mergedByName.set(e.name, { name: e.name, type: t, description });
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const cemNames = new Set(cem.map((c) => c.name));
|
|
397
|
+
const out = [];
|
|
398
|
+
for (const e of cem) {
|
|
399
|
+
out.push(mergedByName.get(e.name));
|
|
400
|
+
}
|
|
401
|
+
for (const e of ast) {
|
|
402
|
+
if (!cemNames.has(e.name)) {
|
|
403
|
+
out.push(mergedByName.get(e.name));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return out;
|
|
407
|
+
}
|
|
408
|
+
/** Merge slots or cssParts: union by `name`, enrich descriptions (same rules as props/events). */
|
|
409
|
+
export function mergeCemWithAstSlotLikeLists(cem, ast) {
|
|
410
|
+
if (ast.length === 0)
|
|
411
|
+
return cem;
|
|
412
|
+
if (cem.length === 0)
|
|
413
|
+
return ast;
|
|
414
|
+
const mergedByName = new Map();
|
|
415
|
+
for (const e of cem) {
|
|
416
|
+
mergedByName.set(e.name, { ...e });
|
|
417
|
+
}
|
|
418
|
+
for (const e of ast) {
|
|
419
|
+
const prev = mergedByName.get(e.name);
|
|
420
|
+
if (!prev) {
|
|
421
|
+
mergedByName.set(e.name, { ...e });
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
mergedByName.set(e.name, {
|
|
425
|
+
name: prev.name,
|
|
426
|
+
description: pickRicherDescription(prev.description, e.description)
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const cemNames = new Set(cem.map((c) => c.name));
|
|
431
|
+
const out = [];
|
|
432
|
+
for (const e of cem) {
|
|
433
|
+
out.push(mergedByName.get(e.name));
|
|
434
|
+
}
|
|
435
|
+
for (const e of ast) {
|
|
436
|
+
if (!cemNames.has(e.name)) {
|
|
437
|
+
out.push(mergedByName.get(e.name));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return out;
|
|
441
|
+
}
|
|
442
|
+
/** Max bytes for echarts-option.ts when printing only the root `EChartsOption` interface for MCP. */
|
|
443
|
+
const SKY_CHART_ECHARTS_OPTION_FILE_MAX_BYTES = 900_000;
|
|
444
|
+
const TYPE_DECL_FILE_MAX_BYTES = 450_000;
|
|
445
|
+
const TYPE_DECL_VISITED = new Set();
|
|
446
|
+
function resolveRelativeImport(fromDir, spec) {
|
|
447
|
+
if (!spec.startsWith('.'))
|
|
448
|
+
return undefined;
|
|
449
|
+
const raw = join(fromDir, spec);
|
|
450
|
+
const candidates = [
|
|
451
|
+
raw,
|
|
452
|
+
`${raw}.ts`,
|
|
453
|
+
`${raw}.tsx`,
|
|
454
|
+
join(raw, 'index.ts'),
|
|
455
|
+
raw.replace(/\.js$/i, '.ts'),
|
|
456
|
+
raw.replace(/\.js$/i, '.tsx')
|
|
457
|
+
];
|
|
458
|
+
for (const p of candidates) {
|
|
459
|
+
if (existsSync(p))
|
|
460
|
+
return p;
|
|
461
|
+
}
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
function readTypeSourceFile(path) {
|
|
465
|
+
if (TYPE_DECL_VISITED.has(path))
|
|
466
|
+
return undefined;
|
|
467
|
+
try {
|
|
468
|
+
const st = statSync(path);
|
|
469
|
+
if (st.size > TYPE_DECL_FILE_MAX_BYTES)
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
let raw;
|
|
476
|
+
try {
|
|
477
|
+
raw = readFileSync(path, 'utf8');
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
TYPE_DECL_VISITED.add(path);
|
|
483
|
+
return ts.createSourceFile(path, raw, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
484
|
+
}
|
|
485
|
+
function indexTopLevelTypeDecls(sf, into) {
|
|
486
|
+
for (const stmt of sf.statements) {
|
|
487
|
+
if (ts.isTypeAliasDeclaration(stmt) || ts.isInterfaceDeclaration(stmt)) {
|
|
488
|
+
const name = stmt.name?.text;
|
|
489
|
+
if (name && !into.has(name))
|
|
490
|
+
into.set(name, { decl: stmt, sf });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function loadTypeDeclsFromFile(path, into) {
|
|
495
|
+
const sf = readTypeSourceFile(path);
|
|
496
|
+
if (!sf)
|
|
497
|
+
return;
|
|
498
|
+
indexTopLevelTypeDecls(sf, into);
|
|
499
|
+
const dir = dirname(sf.fileName);
|
|
500
|
+
for (const stmt of sf.statements) {
|
|
501
|
+
if (!ts.isExportDeclaration(stmt) || !stmt.moduleSpecifier || !ts.isStringLiteralLike(stmt.moduleSpecifier)) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const resolved = resolveRelativeImport(dir, stmt.moduleSpecifier.text);
|
|
505
|
+
if (!resolved)
|
|
506
|
+
continue;
|
|
507
|
+
if (!stmt.exportClause) {
|
|
508
|
+
loadTypeDeclsFromFile(resolved, into);
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (!ts.isNamedExports(stmt.exportClause))
|
|
512
|
+
continue;
|
|
513
|
+
const remote = new Map();
|
|
514
|
+
loadTypeDeclsFromFile(resolved, remote);
|
|
515
|
+
for (const el of stmt.exportClause.elements) {
|
|
516
|
+
const exported = el.propertyName?.text ?? el.name.text;
|
|
517
|
+
const local = el.name.text;
|
|
518
|
+
const hit = remote.get(exported);
|
|
519
|
+
if (hit && !into.has(local))
|
|
520
|
+
into.set(local, hit);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
/** Host file + type-only imports + `export type { … } from` re-exports. */
|
|
525
|
+
function buildTypeDeclLookup(hostSf) {
|
|
526
|
+
TYPE_DECL_VISITED.clear();
|
|
527
|
+
const into = new Map();
|
|
528
|
+
indexTopLevelTypeDecls(hostSf, into);
|
|
529
|
+
const fromDir = dirname(hostSf.fileName);
|
|
530
|
+
for (const stmt of hostSf.statements) {
|
|
531
|
+
if (ts.isImportDeclaration(stmt) && stmt.moduleSpecifier && ts.isStringLiteralLike(stmt.moduleSpecifier)) {
|
|
532
|
+
const resolved = resolveRelativeImport(fromDir, stmt.moduleSpecifier.text);
|
|
533
|
+
if (!resolved)
|
|
534
|
+
continue;
|
|
535
|
+
const remote = new Map();
|
|
536
|
+
loadTypeDeclsFromFile(resolved, remote);
|
|
537
|
+
const bindings = stmt.importClause?.namedBindings;
|
|
538
|
+
if (!bindings || !ts.isNamedImports(bindings))
|
|
539
|
+
continue;
|
|
540
|
+
for (const el of bindings.elements) {
|
|
541
|
+
const exported = el.propertyName?.text ?? el.name.text;
|
|
542
|
+
const local = el.name.text;
|
|
543
|
+
const hit = remote.get(exported);
|
|
544
|
+
if (hit && !into.has(local))
|
|
545
|
+
into.set(local, hit);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (ts.isExportDeclaration(stmt) && stmt.exportClause && ts.isNamedExports(stmt.exportClause) && stmt.moduleSpecifier && ts.isStringLiteralLike(stmt.moduleSpecifier)) {
|
|
549
|
+
const resolved = resolveRelativeImport(fromDir, stmt.moduleSpecifier.text);
|
|
550
|
+
if (!resolved)
|
|
551
|
+
continue;
|
|
552
|
+
const remote = new Map();
|
|
553
|
+
loadTypeDeclsFromFile(resolved, remote);
|
|
554
|
+
for (const el of stmt.exportClause.elements) {
|
|
555
|
+
const exported = el.propertyName?.text ?? el.name.text;
|
|
556
|
+
const local = el.name.text;
|
|
557
|
+
const hit = remote.get(exported);
|
|
558
|
+
if (hit && !into.has(local))
|
|
559
|
+
into.set(local, hit);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return into;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* For `sky-chart`, append the **`EChartsOption`** root interface from sibling `echarts-option.ts`
|
|
567
|
+
* without expanding nested types (avoids multi‑MB type payloads while giving agents the top-level field list).
|
|
568
|
+
*/
|
|
569
|
+
function trySkyChartEChartsOptionRootOnly(doc, hostSf) {
|
|
570
|
+
if (doc.name !== 'sky-chart')
|
|
571
|
+
return undefined;
|
|
572
|
+
const optionPath = join(dirname(hostSf.fileName), 'echarts-option.ts');
|
|
573
|
+
if (!existsSync(optionPath))
|
|
574
|
+
return undefined;
|
|
575
|
+
try {
|
|
576
|
+
const st = statSync(optionPath);
|
|
577
|
+
if (st.size > SKY_CHART_ECHARTS_OPTION_FILE_MAX_BYTES)
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
let raw;
|
|
584
|
+
try {
|
|
585
|
+
raw = readFileSync(optionPath, 'utf8');
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
const optSf = ts.createSourceFile(optionPath, raw, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
591
|
+
let decl;
|
|
592
|
+
for (const stmt of optSf.statements) {
|
|
593
|
+
if (ts.isInterfaceDeclaration(stmt) && stmt.name.text === 'EChartsOption') {
|
|
594
|
+
decl = stmt;
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (!decl)
|
|
599
|
+
return undefined;
|
|
600
|
+
const printer = ts.createPrinter({ removeComments: false, newLine: ts.NewLineKind.LineFeed });
|
|
601
|
+
const definition = printer.printNode(ts.EmitHint.Unspecified, decl, optSf).trim();
|
|
602
|
+
return { name: 'EChartsOption', kind: 'interface', definition };
|
|
603
|
+
}
|
|
604
|
+
export function collectNamedTypeDefinitions(sf, doc) {
|
|
605
|
+
const echartsOptionRoot = trySkyChartEChartsOptionRootOnly(doc, sf);
|
|
606
|
+
const refs = new Set();
|
|
607
|
+
for (const p of doc.properties)
|
|
608
|
+
collectTypeRefsFromText(p.type, refs);
|
|
609
|
+
for (const e of doc.events)
|
|
610
|
+
collectTypeRefsFromText(e.type, refs);
|
|
611
|
+
for (const m of doc.methods) {
|
|
612
|
+
for (const p of m.params)
|
|
613
|
+
collectTypeRefsFromText(p.type, refs);
|
|
614
|
+
collectTypeRefsFromText(m.returns?.type, refs);
|
|
615
|
+
}
|
|
616
|
+
if (refs.size === 0 && !echartsOptionRoot)
|
|
617
|
+
return undefined;
|
|
618
|
+
const printer = ts.createPrinter({ removeComments: false, newLine: ts.NewLineKind.LineFeed });
|
|
619
|
+
const declByName = buildTypeDeclLookup(sf);
|
|
620
|
+
const out = [];
|
|
621
|
+
const visited = new Set();
|
|
622
|
+
const queue = [...refs];
|
|
623
|
+
while (queue.length) {
|
|
624
|
+
const name = queue.shift();
|
|
625
|
+
if (!name || visited.has(name))
|
|
626
|
+
continue;
|
|
627
|
+
visited.add(name);
|
|
628
|
+
const entry = declByName.get(name);
|
|
629
|
+
if (!entry)
|
|
630
|
+
continue;
|
|
631
|
+
const definition = printer.printNode(ts.EmitHint.Unspecified, entry.decl, entry.sf).trim();
|
|
632
|
+
out.push({
|
|
633
|
+
name,
|
|
634
|
+
kind: ts.isTypeAliasDeclaration(entry.decl) ? 'typeAlias' : 'interface',
|
|
635
|
+
definition
|
|
636
|
+
});
|
|
637
|
+
const nested = new Set();
|
|
638
|
+
collectTypeRefsFromText(definition, nested);
|
|
639
|
+
for (const token of nested) {
|
|
640
|
+
if (!visited.has(token) && declByName.has(token)) {
|
|
641
|
+
queue.push(token);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (echartsOptionRoot && !out.some((r) => r.name === 'EChartsOption')) {
|
|
646
|
+
out.push(echartsOptionRoot);
|
|
647
|
+
}
|
|
648
|
+
if (out.length === 0)
|
|
649
|
+
return undefined;
|
|
650
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Parse a Lit core component `.ts` file and return structured documentation from JSDoc + `@customElement`.
|
|
654
|
+
*/
|
|
655
|
+
export function parseLitComponentDocumentation(sourceFilePath, sourceText, componentFolderName, npmImport, options) {
|
|
656
|
+
const maxEx = options?.maxCharsPerExample ?? 12_000;
|
|
657
|
+
const sf = ts.createSourceFile(sourceFilePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
658
|
+
const classNode = findPrimaryComponentClass(sf, componentFolderName);
|
|
659
|
+
if (!classNode) {
|
|
660
|
+
return { ok: false, error: 'No @customElement class declaration found in file.' };
|
|
661
|
+
}
|
|
662
|
+
const tagName = getCustomElementTagName(classNode, sf);
|
|
663
|
+
if (!tagName) {
|
|
664
|
+
return { ok: false, error: 'Could not read @customElement tag string.' };
|
|
665
|
+
}
|
|
666
|
+
const rawLead = sourceText.slice(classNode.getFullStart(), classNode.getStart(sf)).trim();
|
|
667
|
+
const doc = {
|
|
668
|
+
schemaVersion: 1,
|
|
669
|
+
name: componentFolderName,
|
|
670
|
+
tagName,
|
|
671
|
+
npmImport,
|
|
672
|
+
sourceFile: sourceFilePath,
|
|
673
|
+
className: classNode.name?.text,
|
|
674
|
+
slots: [],
|
|
675
|
+
properties: [],
|
|
676
|
+
cssParts: [],
|
|
677
|
+
events: [],
|
|
678
|
+
methods: [],
|
|
679
|
+
examples: [],
|
|
680
|
+
miscTags: [],
|
|
681
|
+
rawLeadTrivia: rawLead.length > 24_000 ? `${rawLead.slice(0, 24_000)}\n… [raw lead truncated]` : rawLead || undefined
|
|
682
|
+
};
|
|
683
|
+
collectFromTags(classNode, sf, doc);
|
|
684
|
+
collectPublicMethods(classNode, sf, doc);
|
|
685
|
+
const { examples, truncated } = trimExamples(doc.examples, maxEx);
|
|
686
|
+
doc.examples = examples;
|
|
687
|
+
doc.typeDefinitions = collectNamedTypeDefinitions(sf, doc);
|
|
688
|
+
return { ok: true, doc, truncated };
|
|
689
|
+
}
|
|
690
|
+
/** Lightweight summary for search (parses full file; reuse parseLitComponentDocumentation). */
|
|
691
|
+
export function parseSummaryOnly(sourceFilePath, sourceText, componentFolderName) {
|
|
692
|
+
const r = parseLitComponentDocumentation(sourceFilePath, sourceText, componentFolderName);
|
|
693
|
+
return r.ok ? r.doc.summary ?? null : null;
|
|
694
|
+
}
|
|
695
|
+
//# sourceMappingURL=parse-component-ast.js.map
|