@fragments-sdk/cli 0.11.1 → 0.13.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/ai-client-I6MDWNYA.js +21 -0
- package/dist/bin.js +419 -410
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-HRFUSSZI.js → chunk-3SOAPJDX.js} +2 -2
- package/dist/{chunk-D5PYOXEI.js → chunk-4K7EAQ5L.js} +148 -13
- package/dist/{chunk-D5PYOXEI.js.map → chunk-4K7EAQ5L.js.map} +1 -1
- package/dist/chunk-DXX6HADE.js +443 -0
- package/dist/chunk-DXX6HADE.js.map +1 -0
- package/dist/chunk-EYXVAMEX.js +626 -0
- package/dist/chunk-EYXVAMEX.js.map +1 -0
- package/dist/{chunk-ZM4ZQZWZ.js → chunk-FO6EBJWP.js} +39 -37
- package/dist/chunk-FO6EBJWP.js.map +1 -0
- package/dist/{chunk-OQO55NKV.js → chunk-QM7SVOGF.js} +120 -12
- package/dist/chunk-QM7SVOGF.js.map +1 -0
- package/dist/{chunk-5G3VZH43.js → chunk-RF3C6LGA.js} +281 -351
- package/dist/chunk-RF3C6LGA.js.map +1 -0
- package/dist/{chunk-WXSR2II7.js → chunk-SM674YAS.js} +58 -6
- package/dist/chunk-SM674YAS.js.map +1 -0
- package/dist/chunk-SXTKFDCR.js +104 -0
- package/dist/chunk-SXTKFDCR.js.map +1 -0
- package/dist/{chunk-PW7QTQA6.js → chunk-UV5JQV3R.js} +2 -2
- package/dist/core/index.js +13 -1
- package/dist/{discovery-NEOY4MPN.js → discovery-VSGC76JN.js} +3 -3
- package/dist/{generate-FBHSXR3D.js → generate-QZXOXYFW.js} +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init-XK6PRUE5.js +636 -0
- package/dist/init-XK6PRUE5.js.map +1 -0
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CJF2DOQW.js → scan-CHQHXWVD.js} +6 -6
- package/dist/scan-generate-U3RFVDTX.js +1115 -0
- package/dist/scan-generate-U3RFVDTX.js.map +1 -0
- package/dist/{service-TQYWY65E.js → service-MMEKG4MZ.js} +3 -3
- package/dist/{snapshot-SV2JOFZH.js → snapshot-53TUR3HW.js} +2 -2
- package/dist/{static-viewer-NUBFPKWH.js → static-viewer-KKCR4KXR.js} +3 -3
- package/dist/static-viewer-KKCR4KXR.js.map +1 -0
- package/dist/{test-Z5LVO724.js → test-5UCKXYSC.js} +4 -4
- package/dist/{tokens-CE46OTMD.js → tokens-L46MK5AW.js} +5 -5
- package/dist/{viewer-DLLJIMCK.js → viewer-M2EQQSGE.js} +14 -14
- package/dist/viewer-M2EQQSGE.js.map +1 -0
- package/package.json +11 -9
- package/src/ai-client.ts +156 -0
- package/src/bin.ts +99 -2
- package/src/build.ts +95 -33
- package/src/commands/__tests__/drift-sync.test.ts +252 -0
- package/src/commands/__tests__/scan-generate.test.ts +497 -45
- package/src/commands/enhance.ts +11 -35
- package/src/commands/govern.ts +122 -0
- package/src/commands/init.ts +288 -260
- package/src/commands/scan-generate.ts +740 -139
- package/src/commands/scan.ts +37 -32
- package/src/commands/setup.ts +143 -52
- package/src/commands/sync.ts +357 -0
- package/src/commands/validate.ts +43 -1
- package/src/core/component-extractor.test.ts +282 -0
- package/src/core/component-extractor.ts +1030 -0
- package/src/core/discovery.ts +93 -7
- package/src/service/enhance/props-extractor.ts +235 -13
- package/src/validators.ts +236 -0
- package/src/viewer/vite-plugin.ts +1 -1
- package/dist/chunk-5G3VZH43.js.map +0 -1
- package/dist/chunk-OQO55NKV.js.map +0 -1
- package/dist/chunk-WXSR2II7.js.map +0 -1
- package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
- package/dist/init-UFGK5TCN.js +0 -867
- package/dist/init-UFGK5TCN.js.map +0 -1
- package/dist/scan-generate-SJAN5MVI.js +0 -691
- package/dist/scan-generate-SJAN5MVI.js.map +0 -1
- package/dist/viewer-DLLJIMCK.js.map +0 -1
- package/src/ai.ts +0 -266
- package/src/commands/init-framework.ts +0 -414
- package/src/mcp/bin.ts +0 -36
- package/src/migrate/bin.ts +0 -114
- package/src/theme/index.ts +0 -77
- package/src/viewer/bin.ts +0 -86
- package/src/viewer/cli/health.ts +0 -256
- package/src/viewer/cli/index.ts +0 -33
- package/src/viewer/cli/scan.ts +0 -124
- package/src/viewer/cli/utils.ts +0 -174
- /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
- /package/dist/{chunk-HRFUSSZI.js.map → chunk-3SOAPJDX.js.map} +0 -0
- /package/dist/{chunk-PW7QTQA6.js.map → chunk-UV5JQV3R.js.map} +0 -0
- /package/dist/{scan-CJF2DOQW.js.map → discovery-VSGC76JN.js.map} +0 -0
- /package/dist/{generate-FBHSXR3D.js.map → generate-QZXOXYFW.js.map} +0 -0
- /package/dist/{service-TQYWY65E.js.map → scan-CHQHXWVD.js.map} +0 -0
- /package/dist/{static-viewer-NUBFPKWH.js.map → service-MMEKG4MZ.js.map} +0 -0
- /package/dist/{snapshot-SV2JOFZH.js.map → snapshot-53TUR3HW.js.map} +0 -0
- /package/dist/{test-Z5LVO724.js.map → test-5UCKXYSC.js.map} +0 -0
- /package/dist/{tokens-CE46OTMD.js.map → tokens-L46MK5AW.js.map} +0 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/core/component-extractor.ts
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { resolve, dirname } from "path";
|
|
7
|
+
function createComponentExtractor(tsconfigPath) {
|
|
8
|
+
let projectVersion = 0;
|
|
9
|
+
const fileVersions = /* @__PURE__ */ new Map();
|
|
10
|
+
const fileSnapshots = /* @__PURE__ */ new Map();
|
|
11
|
+
const rootDir = tsconfigPath ? dirname(resolve(tsconfigPath)) : process.cwd();
|
|
12
|
+
const parsedCommandLine = tsconfigPath ? parseTsConfig(tsconfigPath) : inferCompilerOptions(rootDir);
|
|
13
|
+
const scriptFileNames = new Set(parsedCommandLine.fileNames);
|
|
14
|
+
const host = {
|
|
15
|
+
getProjectVersion: () => projectVersion.toString(),
|
|
16
|
+
getScriptVersion: (fileName) => (fileVersions.get(fileName) ?? 0).toString(),
|
|
17
|
+
getScriptSnapshot: (fileName) => {
|
|
18
|
+
const cached = fileSnapshots.get(fileName);
|
|
19
|
+
if (cached) return cached;
|
|
20
|
+
let text;
|
|
21
|
+
try {
|
|
22
|
+
text = readFileSync(fileName, "utf-8");
|
|
23
|
+
} catch {
|
|
24
|
+
return void 0;
|
|
25
|
+
}
|
|
26
|
+
const snapshot = ts.ScriptSnapshot.fromString(text);
|
|
27
|
+
fileSnapshots.set(fileName, snapshot);
|
|
28
|
+
return snapshot;
|
|
29
|
+
},
|
|
30
|
+
getScriptFileNames: () => [...scriptFileNames],
|
|
31
|
+
getCompilationSettings: () => parsedCommandLine.options,
|
|
32
|
+
getCurrentDirectory: () => rootDir,
|
|
33
|
+
getDefaultLibFileName: ts.getDefaultLibFilePath,
|
|
34
|
+
fileExists: ts.sys.fileExists,
|
|
35
|
+
readFile: ts.sys.readFile,
|
|
36
|
+
readDirectory: ts.sys.readDirectory,
|
|
37
|
+
directoryExists: ts.sys.directoryExists,
|
|
38
|
+
getDirectories: ts.sys.getDirectories
|
|
39
|
+
};
|
|
40
|
+
const languageService = ts.createLanguageService(host, ts.createDocumentRegistry());
|
|
41
|
+
function ensureFile(filePath) {
|
|
42
|
+
const resolved = resolve(filePath);
|
|
43
|
+
if (!scriptFileNames.has(resolved)) {
|
|
44
|
+
scriptFileNames.add(resolved);
|
|
45
|
+
projectVersion++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function getChecker() {
|
|
49
|
+
const program = languageService.getProgram();
|
|
50
|
+
if (!program) throw new Error("Failed to get program from LanguageService");
|
|
51
|
+
return program.getTypeChecker();
|
|
52
|
+
}
|
|
53
|
+
function getSourceFile(filePath) {
|
|
54
|
+
return languageService.getProgram()?.getSourceFile(resolve(filePath));
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
extract(filePath, exportName) {
|
|
58
|
+
const resolved = resolve(filePath);
|
|
59
|
+
ensureFile(resolved);
|
|
60
|
+
const sourceFile = getSourceFile(resolved);
|
|
61
|
+
if (!sourceFile) return null;
|
|
62
|
+
const checker = getChecker();
|
|
63
|
+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
64
|
+
if (!moduleSymbol) return null;
|
|
65
|
+
const exports = checker.getExportsOfModule(moduleSymbol);
|
|
66
|
+
const targetName = exportName ?? findPrimaryExport(exports, sourceFile);
|
|
67
|
+
if (!targetName) return null;
|
|
68
|
+
const exportSymbol = exports.find((s) => s.getName() === targetName);
|
|
69
|
+
if (!exportSymbol) return null;
|
|
70
|
+
const component = resolveExportedComponent(checker, exportSymbol, sourceFile);
|
|
71
|
+
if (!component) return null;
|
|
72
|
+
return buildComponentMeta(checker, component, resolved, sourceFile, exports);
|
|
73
|
+
},
|
|
74
|
+
extractAll(filePath) {
|
|
75
|
+
const resolved = resolve(filePath);
|
|
76
|
+
ensureFile(resolved);
|
|
77
|
+
const sourceFile = getSourceFile(resolved);
|
|
78
|
+
if (!sourceFile) return [];
|
|
79
|
+
const checker = getChecker();
|
|
80
|
+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
81
|
+
if (!moduleSymbol) return [];
|
|
82
|
+
const exports = checker.getExportsOfModule(moduleSymbol);
|
|
83
|
+
const results = [];
|
|
84
|
+
for (const exportSymbol of exports) {
|
|
85
|
+
const name = exportSymbol.getName();
|
|
86
|
+
if (!/^[A-Z]/.test(name)) continue;
|
|
87
|
+
const component = resolveExportedComponent(checker, exportSymbol, sourceFile);
|
|
88
|
+
if (!component) continue;
|
|
89
|
+
const meta = buildComponentMeta(checker, component, resolved, sourceFile, exports);
|
|
90
|
+
if (meta) results.push(meta);
|
|
91
|
+
}
|
|
92
|
+
return results;
|
|
93
|
+
},
|
|
94
|
+
invalidate(filePath) {
|
|
95
|
+
const resolved = resolve(filePath);
|
|
96
|
+
fileVersions.set(resolved, (fileVersions.get(resolved) ?? 0) + 1);
|
|
97
|
+
fileSnapshots.delete(resolved);
|
|
98
|
+
projectVersion++;
|
|
99
|
+
},
|
|
100
|
+
dispose() {
|
|
101
|
+
languageService.dispose();
|
|
102
|
+
fileSnapshots.clear();
|
|
103
|
+
fileVersions.clear();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function parseTsConfig(tsconfigPath) {
|
|
108
|
+
const resolved = resolve(tsconfigPath);
|
|
109
|
+
const configFile = ts.readConfigFile(resolved, ts.sys.readFile);
|
|
110
|
+
if (configFile.error) {
|
|
111
|
+
throw new Error(`Failed to read tsconfig: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`);
|
|
112
|
+
}
|
|
113
|
+
return ts.parseJsonConfigFileContent(
|
|
114
|
+
configFile.config,
|
|
115
|
+
ts.sys,
|
|
116
|
+
dirname(resolved),
|
|
117
|
+
void 0,
|
|
118
|
+
resolved
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
function inferCompilerOptions(rootDir) {
|
|
122
|
+
return {
|
|
123
|
+
options: {
|
|
124
|
+
target: ts.ScriptTarget.ES2022,
|
|
125
|
+
module: ts.ModuleKind.ESNext,
|
|
126
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
127
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
128
|
+
allowSyntheticDefaultImports: true,
|
|
129
|
+
esModuleInterop: true,
|
|
130
|
+
skipLibCheck: true,
|
|
131
|
+
strict: false,
|
|
132
|
+
noEmit: true
|
|
133
|
+
},
|
|
134
|
+
fileNames: [],
|
|
135
|
+
errors: []
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function findPrimaryExport(exports, sourceFile) {
|
|
139
|
+
const defaultExport = exports.find((s) => s.getName() === "default");
|
|
140
|
+
if (defaultExport) return "default";
|
|
141
|
+
for (const s of exports) {
|
|
142
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(s.getName())) {
|
|
143
|
+
return s.getName();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
function resolveExportedComponent(checker, exportSymbol, sourceFile) {
|
|
149
|
+
const name = exportSymbol.getName();
|
|
150
|
+
let symbol = exportSymbol;
|
|
151
|
+
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
152
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
153
|
+
}
|
|
154
|
+
const declarations = symbol.getDeclarations();
|
|
155
|
+
if (!declarations || declarations.length === 0) return null;
|
|
156
|
+
const declaration = declarations[0];
|
|
157
|
+
if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
158
|
+
return resolveFromExpression(checker, name, declaration.initializer, declaration, sourceFile);
|
|
159
|
+
}
|
|
160
|
+
if (ts.isFunctionDeclaration(declaration)) {
|
|
161
|
+
const propsType = extractPropsFromFunctionLike(checker, declaration);
|
|
162
|
+
return propsType ? { name, propsType, componentNode: declaration, compoundParts: null } : null;
|
|
163
|
+
}
|
|
164
|
+
if (ts.isExportAssignment(declaration) && declaration.expression) {
|
|
165
|
+
return resolveFromExpression(checker, name, declaration.expression, null, sourceFile);
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function resolveFromExpression(checker, name, expression, variableDecl, sourceFile) {
|
|
170
|
+
expression = unwrapExpression(expression);
|
|
171
|
+
if (ts.isArrowFunction(expression) || ts.isFunctionExpression(expression)) {
|
|
172
|
+
const propsType = extractPropsFromFunctionLike(checker, expression);
|
|
173
|
+
return propsType ? { name, propsType, componentNode: expression, compoundParts: null } : null;
|
|
174
|
+
}
|
|
175
|
+
if (ts.isCallExpression(expression)) {
|
|
176
|
+
return resolveCallExpression(checker, name, expression, variableDecl, sourceFile);
|
|
177
|
+
}
|
|
178
|
+
if (ts.isIdentifier(expression)) {
|
|
179
|
+
const sym = checker.getSymbolAtLocation(expression);
|
|
180
|
+
if (sym) {
|
|
181
|
+
const decls = sym.getDeclarations();
|
|
182
|
+
if (decls && decls.length > 0) {
|
|
183
|
+
const decl = decls[0];
|
|
184
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
185
|
+
return resolveFromExpression(checker, name, decl.initializer, decl, sourceFile);
|
|
186
|
+
}
|
|
187
|
+
if (ts.isFunctionDeclaration(decl)) {
|
|
188
|
+
const propsType = extractPropsFromFunctionLike(checker, decl);
|
|
189
|
+
return propsType ? { name, propsType, componentNode: decl, compoundParts: null } : null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (variableDecl?.type && ts.isTypeReferenceNode(variableDecl.type)) {
|
|
195
|
+
const typeName = variableDecl.type.typeName.getText(sourceFile);
|
|
196
|
+
if (typeName.includes("FC") || typeName.includes("FunctionComponent")) {
|
|
197
|
+
const typeArg = variableDecl.type.typeArguments?.[0];
|
|
198
|
+
if (typeArg) {
|
|
199
|
+
const propsType = checker.getTypeFromTypeNode(typeArg);
|
|
200
|
+
return { name, propsType, componentNode: expression, compoundParts: null };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function resolveCallExpression(checker, name, call, variableDecl, sourceFile) {
|
|
207
|
+
const callee = call.expression;
|
|
208
|
+
if (isObjectAssignCall(callee, sourceFile)) {
|
|
209
|
+
return resolveObjectAssign(checker, name, call, sourceFile);
|
|
210
|
+
}
|
|
211
|
+
if (isForwardRefCall(callee)) {
|
|
212
|
+
return resolveForwardRef(checker, name, call, sourceFile);
|
|
213
|
+
}
|
|
214
|
+
if (isMemoCall(callee)) {
|
|
215
|
+
const innerArg = call.arguments[0];
|
|
216
|
+
if (innerArg) {
|
|
217
|
+
return resolveFromExpression(checker, name, innerArg, variableDecl, sourceFile);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function resolveObjectAssign(checker, name, call, sourceFile) {
|
|
223
|
+
if (call.arguments.length < 2) return null;
|
|
224
|
+
const rootExpr = call.arguments[0];
|
|
225
|
+
const rootResult = resolveFromExpression(checker, name, rootExpr, null, sourceFile);
|
|
226
|
+
const subsArg = call.arguments[1];
|
|
227
|
+
const compoundParts = /* @__PURE__ */ new Map();
|
|
228
|
+
if (ts.isObjectLiteralExpression(subsArg)) {
|
|
229
|
+
for (const prop of subsArg.properties) {
|
|
230
|
+
let subName = null;
|
|
231
|
+
let subExpression = null;
|
|
232
|
+
if (ts.isShorthandPropertyAssignment(prop)) {
|
|
233
|
+
subName = prop.name.text;
|
|
234
|
+
const sym = checker.getSymbolAtLocation(prop.name);
|
|
235
|
+
if (sym) {
|
|
236
|
+
const subPropsType = extractPropsFromComponentSymbol(checker, sym);
|
|
237
|
+
if (subPropsType) {
|
|
238
|
+
compoundParts.set(subName, subPropsType);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} else if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
242
|
+
subName = prop.name.text;
|
|
243
|
+
subExpression = prop.initializer;
|
|
244
|
+
if (subExpression) {
|
|
245
|
+
const subType = extractPropsFromExpression(checker, subExpression, sourceFile);
|
|
246
|
+
if (subType) {
|
|
247
|
+
compoundParts.set(subName, subType);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
name,
|
|
255
|
+
propsType: rootResult?.propsType ?? null,
|
|
256
|
+
componentNode: rootResult?.componentNode ?? rootExpr,
|
|
257
|
+
compoundParts: compoundParts.size > 0 ? compoundParts : null
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function extractPropsFromComponentSymbol(checker, symbol) {
|
|
261
|
+
let sym = symbol;
|
|
262
|
+
if (sym.flags & ts.SymbolFlags.Alias) {
|
|
263
|
+
sym = checker.getAliasedSymbol(sym);
|
|
264
|
+
}
|
|
265
|
+
const decls = sym.getDeclarations();
|
|
266
|
+
if (!decls || decls.length === 0) return null;
|
|
267
|
+
const decl = decls[0];
|
|
268
|
+
if (ts.isFunctionDeclaration(decl)) {
|
|
269
|
+
return extractPropsFromFunctionLike(checker, decl);
|
|
270
|
+
}
|
|
271
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
272
|
+
return extractPropsFromExpressionDeep(checker, decl);
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
function extractPropsFromExpression(checker, expr, sourceFile) {
|
|
277
|
+
expr = unwrapExpression(expr);
|
|
278
|
+
if (ts.isIdentifier(expr)) {
|
|
279
|
+
const sym = checker.getSymbolAtLocation(expr);
|
|
280
|
+
if (sym) return extractPropsFromComponentSymbol(checker, sym);
|
|
281
|
+
}
|
|
282
|
+
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
283
|
+
return extractPropsFromFunctionLike(checker, expr);
|
|
284
|
+
}
|
|
285
|
+
if (ts.isCallExpression(expr)) {
|
|
286
|
+
if (isForwardRefCall(expr.expression)) {
|
|
287
|
+
const typeArg = expr.typeArguments?.[1];
|
|
288
|
+
if (typeArg) return checker.getTypeFromTypeNode(typeArg);
|
|
289
|
+
const innerArg = expr.arguments[0];
|
|
290
|
+
if (innerArg && (ts.isArrowFunction(innerArg) || ts.isFunctionExpression(innerArg))) {
|
|
291
|
+
return extractPropsFromFunctionLike(checker, innerArg);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (isMemoCall(expr.expression) && expr.arguments[0]) {
|
|
295
|
+
return extractPropsFromExpression(checker, expr.arguments[0], sourceFile);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function extractPropsFromExpressionDeep(checker, decl) {
|
|
301
|
+
if (!decl.initializer) return null;
|
|
302
|
+
const expr = unwrapExpression(decl.initializer);
|
|
303
|
+
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
304
|
+
return extractPropsFromFunctionLike(checker, expr);
|
|
305
|
+
}
|
|
306
|
+
if (ts.isCallExpression(expr)) {
|
|
307
|
+
if (isForwardRefCall(expr.expression)) {
|
|
308
|
+
const typeArg = expr.typeArguments?.[1];
|
|
309
|
+
if (typeArg) return checker.getTypeFromTypeNode(typeArg);
|
|
310
|
+
const innerArg = expr.arguments[0];
|
|
311
|
+
if (innerArg && (ts.isArrowFunction(innerArg) || ts.isFunctionExpression(innerArg))) {
|
|
312
|
+
return extractPropsFromFunctionLike(checker, innerArg);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (isMemoCall(expr.expression) && expr.arguments[0]) {
|
|
316
|
+
return extractPropsFromExpressionDeep(checker, {
|
|
317
|
+
...decl,
|
|
318
|
+
initializer: expr.arguments[0]
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
function resolveForwardRef(checker, name, call, sourceFile) {
|
|
325
|
+
const propsTypeArg = call.typeArguments?.[1];
|
|
326
|
+
if (propsTypeArg) {
|
|
327
|
+
const propsType = checker.getTypeFromTypeNode(propsTypeArg);
|
|
328
|
+
const innerArg2 = call.arguments[0];
|
|
329
|
+
const componentNode = innerArg2 && (ts.isArrowFunction(innerArg2) || ts.isFunctionExpression(innerArg2)) ? innerArg2 : null;
|
|
330
|
+
return { name, propsType, componentNode, compoundParts: null };
|
|
331
|
+
}
|
|
332
|
+
const innerArg = call.arguments[0];
|
|
333
|
+
if (innerArg) {
|
|
334
|
+
if (ts.isArrowFunction(innerArg) || ts.isFunctionExpression(innerArg)) {
|
|
335
|
+
const propsType = extractPropsFromFunctionLike(checker, innerArg);
|
|
336
|
+
return propsType ? { name, propsType, componentNode: innerArg, compoundParts: null } : null;
|
|
337
|
+
}
|
|
338
|
+
if (ts.isIdentifier(innerArg)) {
|
|
339
|
+
const sym = checker.getSymbolAtLocation(innerArg);
|
|
340
|
+
if (sym) {
|
|
341
|
+
const propsType = extractPropsFromComponentSymbol(checker, sym);
|
|
342
|
+
if (propsType) return { name, propsType, componentNode: innerArg, compoundParts: null };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
function extractPropsFromFunctionLike(checker, func) {
|
|
349
|
+
const firstParam = func.parameters[0];
|
|
350
|
+
if (!firstParam) return null;
|
|
351
|
+
if (firstParam.type) {
|
|
352
|
+
return checker.getTypeFromTypeNode(firstParam.type);
|
|
353
|
+
}
|
|
354
|
+
const paramSymbol = checker.getSymbolAtLocation(firstParam.name);
|
|
355
|
+
if (paramSymbol) {
|
|
356
|
+
return checker.getTypeOfSymbolAtLocation(paramSymbol, firstParam);
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
function buildComponentMeta(checker, component, filePath, sourceFile, moduleExports) {
|
|
361
|
+
const props = {};
|
|
362
|
+
const sourceFilePath = toPosixPath(sourceFile.fileName);
|
|
363
|
+
if (component.propsType) {
|
|
364
|
+
extractPropsFromType(checker, component.propsType, props, sourceFilePath);
|
|
365
|
+
}
|
|
366
|
+
const defaults = component.componentNode ? extractDefaultValues(component.componentNode) : {};
|
|
367
|
+
for (const [propName, defaultVal] of Object.entries(defaults)) {
|
|
368
|
+
if (props[propName]) {
|
|
369
|
+
props[propName].default = defaultVal;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
let composition = null;
|
|
373
|
+
if (component.compoundParts && component.compoundParts.size > 0) {
|
|
374
|
+
const parts = [];
|
|
375
|
+
for (const [partName, partType] of component.compoundParts) {
|
|
376
|
+
const partProps = {};
|
|
377
|
+
extractPropsFromType(checker, partType, partProps, sourceFilePath);
|
|
378
|
+
parts.push({ name: partName, props: partProps });
|
|
379
|
+
}
|
|
380
|
+
composition = {
|
|
381
|
+
pattern: "compound",
|
|
382
|
+
parts,
|
|
383
|
+
required: []
|
|
384
|
+
// Could be inferred from usage patterns
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
let description = "";
|
|
388
|
+
const componentSymbol = moduleExports.find((s) => s.getName() === component.name);
|
|
389
|
+
if (componentSymbol) {
|
|
390
|
+
description = extractJSDocDescription(checker, componentSymbol);
|
|
391
|
+
}
|
|
392
|
+
const exportNames = moduleExports.filter((s) => /^[A-Z]/.test(s.getName())).map((s) => s.getName());
|
|
393
|
+
const dependencies = extractDependencies(sourceFile);
|
|
394
|
+
return {
|
|
395
|
+
name: component.name,
|
|
396
|
+
filePath,
|
|
397
|
+
description,
|
|
398
|
+
props,
|
|
399
|
+
composition,
|
|
400
|
+
exports: exportNames,
|
|
401
|
+
dependencies
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function extractPropsFromType(checker, propsType, result, sourceFilePath) {
|
|
405
|
+
for (const symbol of checker.getPropertiesOfType(propsType)) {
|
|
406
|
+
const propName = symbol.getName();
|
|
407
|
+
if (propName.startsWith("_") || propName.startsWith("$")) continue;
|
|
408
|
+
if (propName === "key" || propName === "ref") continue;
|
|
409
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
410
|
+
const isLocal = declarations.some(
|
|
411
|
+
(d) => toPosixPath(d.getSourceFile().fileName) === sourceFilePath
|
|
412
|
+
);
|
|
413
|
+
const referenceNode = declarations[0];
|
|
414
|
+
if (!referenceNode) continue;
|
|
415
|
+
const propType = checker.getTypeOfSymbolAtLocation(symbol, referenceNode);
|
|
416
|
+
const serialized = serializePropType(checker, propType);
|
|
417
|
+
const description = ts.displayPartsToString(symbol.getDocumentationComment(checker)).trim();
|
|
418
|
+
const required = (symbol.flags & ts.SymbolFlags.Optional) === 0;
|
|
419
|
+
let jsDocDefault;
|
|
420
|
+
const jsDocTags = symbol.getJsDocTags(checker);
|
|
421
|
+
const defaultTag = jsDocTags.find((t) => t.name === "default");
|
|
422
|
+
if (defaultTag?.text) {
|
|
423
|
+
jsDocDefault = ts.displayPartsToString(defaultTag.text).trim();
|
|
424
|
+
}
|
|
425
|
+
result[propName] = {
|
|
426
|
+
name: propName,
|
|
427
|
+
type: serialized.type,
|
|
428
|
+
typeKind: serialized.typeKind,
|
|
429
|
+
values: serialized.values,
|
|
430
|
+
default: jsDocDefault,
|
|
431
|
+
description: description || void 0,
|
|
432
|
+
required,
|
|
433
|
+
source: isLocal ? "local" : "inherited"
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function serializePropType(checker, type) {
|
|
438
|
+
const aliasSymbol = type.aliasSymbol;
|
|
439
|
+
if (aliasSymbol) {
|
|
440
|
+
const aliasName = aliasSymbol.getName();
|
|
441
|
+
if (aliasName === "ReactNode") {
|
|
442
|
+
return { type: "ReactNode", typeKind: "node" };
|
|
443
|
+
}
|
|
444
|
+
if (aliasName === "ReactElement") {
|
|
445
|
+
return { type: "ReactElement", typeKind: "element" };
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (type.isUnion()) {
|
|
449
|
+
const nonNullableTypes = type.types.filter(
|
|
450
|
+
(t) => !(t.flags & ts.TypeFlags.Undefined || t.flags & ts.TypeFlags.Null || t.flags & ts.TypeFlags.Void)
|
|
451
|
+
);
|
|
452
|
+
if (nonNullableTypes.length === 1) {
|
|
453
|
+
return serializePropType(checker, nonNullableTypes[0]);
|
|
454
|
+
}
|
|
455
|
+
if (nonNullableTypes.length > 0 && nonNullableTypes.every((t) => t.isStringLiteral())) {
|
|
456
|
+
const values = nonNullableTypes.map((t) => t.value);
|
|
457
|
+
return {
|
|
458
|
+
type: values.map((v) => `"${v}"`).join(" | "),
|
|
459
|
+
typeKind: "enum",
|
|
460
|
+
values
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (nonNullableTypes.every((t) => isBooleanLike(t))) {
|
|
464
|
+
return { type: "boolean", typeKind: "boolean" };
|
|
465
|
+
}
|
|
466
|
+
const typeStr2 = checker.typeToString(type, void 0, ts.TypeFormatFlags.NoTruncation);
|
|
467
|
+
return { type: typeStr2, typeKind: "union" };
|
|
468
|
+
}
|
|
469
|
+
const typeStr = checker.typeToString(type, void 0, ts.TypeFormatFlags.NoTruncation);
|
|
470
|
+
if (typeStr.includes("ReactNode")) {
|
|
471
|
+
return { type: "ReactNode", typeKind: "node" };
|
|
472
|
+
}
|
|
473
|
+
if (typeStr.includes("ReactElement") || typeStr.includes("JSX.Element")) {
|
|
474
|
+
return { type: "ReactElement", typeKind: "element" };
|
|
475
|
+
}
|
|
476
|
+
if (type.getCallSignatures().length > 0) {
|
|
477
|
+
return { type: typeStr, typeKind: "function" };
|
|
478
|
+
}
|
|
479
|
+
if (type.flags & ts.TypeFlags.String) return { type: "string", typeKind: "string" };
|
|
480
|
+
if (type.flags & ts.TypeFlags.Number) return { type: "number", typeKind: "number" };
|
|
481
|
+
if (type.flags & ts.TypeFlags.Boolean || type.flags & ts.TypeFlags.BooleanLiteral) {
|
|
482
|
+
return { type: "boolean", typeKind: "boolean" };
|
|
483
|
+
}
|
|
484
|
+
if (type.isStringLiteral()) {
|
|
485
|
+
return { type: `"${type.value}"`, typeKind: "enum", values: [type.value] };
|
|
486
|
+
}
|
|
487
|
+
if (checker.isArrayType(type) || checker.isTupleType(type)) {
|
|
488
|
+
return { type: typeStr, typeKind: "array" };
|
|
489
|
+
}
|
|
490
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
491
|
+
return { type: typeStr, typeKind: "object" };
|
|
492
|
+
}
|
|
493
|
+
return { type: typeStr, typeKind: "custom" };
|
|
494
|
+
}
|
|
495
|
+
function extractDefaultValues(node) {
|
|
496
|
+
const defaults = {};
|
|
497
|
+
let funcNode = null;
|
|
498
|
+
if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
499
|
+
funcNode = node;
|
|
500
|
+
}
|
|
501
|
+
if (!funcNode?.parameters?.length) return defaults;
|
|
502
|
+
const firstParam = funcNode.parameters[0];
|
|
503
|
+
if (!ts.isObjectBindingPattern(firstParam.name)) return defaults;
|
|
504
|
+
for (const element of firstParam.name.elements) {
|
|
505
|
+
let propName = null;
|
|
506
|
+
if (element.propertyName) {
|
|
507
|
+
if (ts.isIdentifier(element.propertyName) || ts.isStringLiteral(element.propertyName)) {
|
|
508
|
+
propName = element.propertyName.text;
|
|
509
|
+
}
|
|
510
|
+
} else if (ts.isIdentifier(element.name)) {
|
|
511
|
+
propName = element.name.text;
|
|
512
|
+
}
|
|
513
|
+
if (!propName || !element.initializer) continue;
|
|
514
|
+
const value = readLiteralValue(element.initializer);
|
|
515
|
+
if (value !== void 0) {
|
|
516
|
+
defaults[propName] = value;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return defaults;
|
|
520
|
+
}
|
|
521
|
+
function readLiteralValue(expression) {
|
|
522
|
+
if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
|
|
523
|
+
return expression.text;
|
|
524
|
+
}
|
|
525
|
+
if (ts.isNumericLiteral(expression)) {
|
|
526
|
+
return expression.text;
|
|
527
|
+
}
|
|
528
|
+
if (expression.kind === ts.SyntaxKind.TrueKeyword) return "true";
|
|
529
|
+
if (expression.kind === ts.SyntaxKind.FalseKeyword) return "false";
|
|
530
|
+
if (expression.kind === ts.SyntaxKind.NullKeyword) return "null";
|
|
531
|
+
if (ts.isPrefixUnaryExpression(expression) && expression.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(expression.operand)) {
|
|
532
|
+
return `-${expression.operand.text}`;
|
|
533
|
+
}
|
|
534
|
+
return void 0;
|
|
535
|
+
}
|
|
536
|
+
function extractJSDocDescription(checker, symbol) {
|
|
537
|
+
let sym = symbol;
|
|
538
|
+
if (sym.flags & ts.SymbolFlags.Alias) {
|
|
539
|
+
sym = checker.getAliasedSymbol(sym);
|
|
540
|
+
}
|
|
541
|
+
const docComment = ts.displayPartsToString(sym.getDocumentationComment(checker)).trim();
|
|
542
|
+
if (docComment) return docComment;
|
|
543
|
+
const decls = sym.getDeclarations();
|
|
544
|
+
if (decls) {
|
|
545
|
+
for (const decl of decls) {
|
|
546
|
+
const sourceFile = decl.getSourceFile();
|
|
547
|
+
for (const stmt of sourceFile.statements) {
|
|
548
|
+
if ((ts.isInterfaceDeclaration(stmt) || ts.isTypeAliasDeclaration(stmt)) && stmt.name.text === `${symbol.getName()}Props`) {
|
|
549
|
+
const propsDoc = ts.displayPartsToString(
|
|
550
|
+
checker.getSymbolAtLocation(stmt.name)?.getDocumentationComment(checker) ?? []
|
|
551
|
+
).trim();
|
|
552
|
+
if (propsDoc) return propsDoc;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return "";
|
|
558
|
+
}
|
|
559
|
+
function extractDependencies(sourceFile) {
|
|
560
|
+
const deps = [];
|
|
561
|
+
for (const stmt of sourceFile.statements) {
|
|
562
|
+
if (!ts.isImportDeclaration(stmt)) continue;
|
|
563
|
+
if (!ts.isStringLiteral(stmt.moduleSpecifier)) continue;
|
|
564
|
+
const modulePath = stmt.moduleSpecifier.text;
|
|
565
|
+
if (!modulePath.startsWith(".") && !modulePath.startsWith("/")) continue;
|
|
566
|
+
const clause = stmt.importClause;
|
|
567
|
+
if (!clause) continue;
|
|
568
|
+
if (clause.name && /^[A-Z]/.test(clause.name.text)) {
|
|
569
|
+
deps.push(clause.name.text);
|
|
570
|
+
}
|
|
571
|
+
if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
572
|
+
for (const element of clause.namedBindings.elements) {
|
|
573
|
+
if (/^[A-Z]/.test(element.name.text)) {
|
|
574
|
+
deps.push(element.name.text);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return deps;
|
|
580
|
+
}
|
|
581
|
+
function unwrapExpression(expr) {
|
|
582
|
+
while (true) {
|
|
583
|
+
if (ts.isParenthesizedExpression(expr)) {
|
|
584
|
+
expr = expr.expression;
|
|
585
|
+
} else if (ts.isAsExpression(expr) || ts.isTypeAssertionExpression(expr)) {
|
|
586
|
+
expr = expr.expression;
|
|
587
|
+
} else {
|
|
588
|
+
return expr;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function isObjectAssignCall(callee, sourceFile) {
|
|
593
|
+
if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.expression) && callee.expression.text === "Object" && callee.name.text === "assign") {
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
function isForwardRefCall(callee) {
|
|
599
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "forwardRef") {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
if (ts.isIdentifier(callee) && callee.text === "forwardRef") {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
function isMemoCall(callee) {
|
|
608
|
+
if (ts.isPropertyAccessExpression(callee) && callee.name.text === "memo") {
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
if (ts.isIdentifier(callee) && callee.text === "memo") {
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
function isBooleanLike(type) {
|
|
617
|
+
return (type.flags & ts.TypeFlags.BooleanLike) !== 0 || type.flags === ts.TypeFlags.BooleanLiteral;
|
|
618
|
+
}
|
|
619
|
+
function toPosixPath(filePath) {
|
|
620
|
+
return filePath.replace(/\\/g, "/");
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export {
|
|
624
|
+
createComponentExtractor
|
|
625
|
+
};
|
|
626
|
+
//# sourceMappingURL=chunk-EYXVAMEX.js.map
|