userface
Version:
Universal Data-Driven UI Engine with live data, validation, and multi-platform support
419 lines (418 loc) • 18.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.realASTAnalyzer = exports.RealASTAnalyzer = void 0;
const ts = __importStar(require("typescript"));
const prop_generator_1 = require("./prop-generator");
class RealASTAnalyzer {
constructor() {
Object.defineProperty(this, "program", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "sourceFile", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
}
/**
* Анализирует компонент и возвращает его схему
*/
async analyzeComponent(component, componentName) {
try {
console.log(`[AST-DEBUG] Analyzing component: ${componentName}`);
// Создаем программу TypeScript
this.program = ts.createProgram([`${componentName}.tsx`], {
target: ts.ScriptTarget.Latest,
module: ts.ModuleKind.CommonJS,
jsx: ts.JsxEmit.React,
esModuleInterop: true,
allowSyntheticDefaultImports: true,
strict: false,
skipLibCheck: true
}, {
getSourceFile: (fileName) => {
if (fileName.endsWith('.tsx')) {
return ts.createSourceFile(fileName, component.code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
}
return undefined;
},
writeFile: () => { },
getCurrentDirectory: () => process.cwd(),
getDirectories: () => [],
fileExists: (fileName) => fileName.endsWith('.tsx'),
readFile: (fileName) => fileName.endsWith('.tsx') ? component.code : undefined,
getDefaultLibFileName: () => 'lib.d.ts',
getCanonicalFileName: (fileName) => fileName,
useCaseSensitiveFileNames: () => false,
getNewLine: () => '\n'
});
const sourceFile = this.program.getSourceFile(`${componentName}.tsx`);
this.sourceFile = sourceFile || null;
if (!this.sourceFile) {
throw new Error('Failed to create source file');
}
// Анализируем компонент
const schema = {
name: componentName,
detectedPlatform: this.detectPlatform(component.code),
props: [],
events: [],
children: false,
description: '',
supportsChildren: false
};
// Обходим AST
this.visitNode(this.sourceFile, schema);
// Убираем дубликаты пропсов
schema.props = this.deduplicateProps(schema.props);
console.log(`[AST-DEBUG] Analysis complete for ${componentName}:`, schema);
return {
...schema,
platform: schema.detectedPlatform // Для обратной совместимости
};
}
catch (error) {
console.error(`[AST-DEBUG] Error analyzing component ${componentName}:`, error);
throw error;
}
}
/**
* Генерирует пропсы для компонента на основе его анализа
*/
generatePropsForComponent(component, componentName) {
try {
// Сначала анализируем компонент
return this.analyzeComponent(component, componentName).then(schema => {
// Затем генерируем пропсы на основе схемы
return prop_generator_1.PropGenerator.generateProps(schema, componentName);
});
}
catch (error) {
console.error(`[AST-DEBUG] Error generating props for ${componentName}:`, error);
// Возвращаем дефолтные пропсы если анализ не удался
return prop_generator_1.PropGenerator.generateDefaultProps(componentName);
}
}
visitNode(node, schema) {
switch (node.kind) {
case ts.SyntaxKind.InterfaceDeclaration:
this.analyzeInterface(node, schema);
break;
case ts.SyntaxKind.TypeAliasDeclaration:
this.analyzeTypeAlias(node, schema);
break;
case ts.SyntaxKind.FunctionDeclaration:
this.analyzeFunction(node, schema);
break;
case ts.SyntaxKind.VariableStatement:
this.analyzeVariableStatement(node, schema);
break;
case ts.SyntaxKind.ArrowFunction:
this.analyzeArrowFunction(node, schema);
break;
case ts.SyntaxKind.FunctionExpression:
this.analyzeFunctionExpression(node, schema);
break;
case ts.SyntaxKind.JsxElement:
this.analyzeJSXElement(node, schema);
break;
case ts.SyntaxKind.JsxSelfClosingElement:
this.analyzeJSXSelfClosingElement(node, schema);
break;
}
// Рекурсивно обходим дочерние узлы
ts.forEachChild(node, (child) => this.visitNode(child, schema));
}
analyzeInterface(interfaceDecl, schema) {
const interfaceName = interfaceDecl.name.text;
// Проверяем, является ли это интерфейсом пропсов
if (interfaceName.includes('Props') || interfaceName.includes('props')) {
interfaceDecl.members.forEach(member => {
if (ts.isPropertySignature(member)) {
const propName = member.name.getText();
const propType = member.type ? member.type.getText() : 'any';
const isRequired = !member.questionToken;
schema.props.push({
name: propName,
type: this.mapTypeScriptType(propType),
required: isRequired,
description: member.getText()
});
}
else if (ts.isMethodSignature(member)) {
const methodName = member.name.getText();
if (methodName.startsWith('on')) {
schema.events.push({
name: methodName,
parameters: member.parameters.map(p => p.getText()),
description: member.getText()
});
}
}
});
}
}
analyzeTypeAlias(typeAlias, schema) {
const typeName = typeAlias.name.text;
if (typeName.includes('Props') || typeName.includes('props')) {
if (ts.isTypeLiteralNode(typeAlias.type)) {
typeAlias.type.members.forEach(member => {
if (ts.isPropertySignature(member)) {
const propName = member.name.getText();
const propType = member.type ? member.type.getText() : 'any';
const isRequired = !member.questionToken;
schema.props.push({
name: propName,
type: this.mapTypeScriptType(propType),
required: isRequired,
description: member.getText()
});
}
else if (ts.isMethodSignature(member)) {
const methodName = member.name.getText();
if (methodName.startsWith('on')) {
schema.events.push({
name: methodName,
parameters: member.parameters.map(p => p.getText()),
description: member.getText()
});
}
}
});
}
}
}
analyzeFunction(funcDecl, schema) {
if (!funcDecl.name)
return;
const funcName = funcDecl.name.text;
if (funcName.includes('Component') || funcName.includes('component')) {
// Анализируем параметры функции
funcDecl.parameters.forEach(param => {
// Проверяем тип параметра
if (param.type) {
const paramType = param.type.getText();
if (paramType.includes('Props') || paramType.includes('props')) {
// Это пропсы компонента
schema.props.push({
name: param.name.getText(),
type: this.mapTypeScriptType(paramType),
required: !param.questionToken,
description: param.getText()
});
}
}
// Анализируем деструктурированные параметры
if (ts.isObjectBindingPattern(param.name)) {
param.name.elements.forEach(element => {
if (ts.isBindingElement(element)) {
const propName = element.propertyName ? element.propertyName.getText() : element.name.getText();
const hasDefault = element.initializer !== undefined;
schema.props.push({
name: propName,
type: 'text', // По умолчанию text
required: !hasDefault,
description: element.getText()
});
}
});
}
});
}
}
analyzeVariableStatement(varStmt, schema) {
varStmt.declarationList.declarations.forEach(decl => {
if (decl.initializer && ts.isArrowFunction(decl.initializer)) {
this.analyzeArrowFunction(decl.initializer, schema);
}
else if (decl.initializer && ts.isFunctionExpression(decl.initializer)) {
this.analyzeFunctionExpression(decl.initializer, schema);
}
});
}
analyzeArrowFunction(arrowFunc, schema) {
// Анализируем параметры стрелочной функции
arrowFunc.parameters.forEach(param => {
// Проверяем тип параметра
if (param.type) {
const paramType = param.type.getText();
if (paramType.includes('Props') || paramType.includes('props')) {
schema.props.push({
name: param.name.getText(),
type: this.mapTypeScriptType(paramType),
required: !param.questionToken,
description: param.getText()
});
}
}
// Анализируем деструктурированные параметры
if (ts.isObjectBindingPattern(param.name)) {
param.name.elements.forEach(element => {
if (ts.isBindingElement(element)) {
const propName = element.propertyName ? element.propertyName.getText() : element.name.getText();
const hasDefault = element.initializer !== undefined;
schema.props.push({
name: propName,
type: 'text', // По умолчанию text, можно улучшить
required: !hasDefault,
description: element.getText()
});
}
});
}
});
}
analyzeFunctionExpression(funcExpr, schema) {
// Анализируем параметры функционального выражения
funcExpr.parameters.forEach(param => {
// Проверяем тип параметра
if (param.type) {
const paramType = param.type.getText();
if (paramType.includes('Props') || paramType.includes('props')) {
schema.props.push({
name: param.name.getText(),
type: this.mapTypeScriptType(paramType),
required: !param.questionToken,
description: param.getText()
});
}
}
// Анализируем деструктурированные параметры
if (ts.isObjectBindingPattern(param.name)) {
param.name.elements.forEach(element => {
if (ts.isBindingElement(element)) {
const propName = element.propertyName ? element.propertyName.getText() : element.name.getText();
const hasDefault = element.initializer !== undefined;
schema.props.push({
name: propName,
type: 'text', // По умолчанию text
required: !hasDefault,
description: element.getText()
});
}
});
}
});
}
analyzeJSXElement(jsxElement, schema) {
// Проверяем поддержку children
if (jsxElement.children && jsxElement.children.length > 0) {
schema.children = true;
schema.supportsChildren = true;
}
// Анализируем атрибуты
this.analyzeJSXAttributes(jsxElement.openingElement.attributes, schema);
}
analyzeJSXSelfClosingElement(jsxElement, schema) {
// Анализируем атрибуты
this.analyzeJSXAttributes(jsxElement.attributes, schema);
}
analyzeJSXAttributes(attributes, schema) {
attributes.properties.forEach(attr => {
if (ts.isJsxAttribute(attr)) {
const attrName = attr.name.getText();
// Проверяем, является ли это событием
if (attrName.startsWith('on')) {
schema.events.push({
name: attrName,
parameters: [],
description: attr.getText()
});
}
}
});
}
detectPlatform(code) {
if (code.includes('React') || code.includes('react'))
return 'react';
if (code.includes('Vue') || code.includes('vue'))
return 'vue';
if (code.includes('Angular') || code.includes('angular'))
return 'angular';
if (code.includes('Svelte') || code.includes('svelte'))
return 'svelte';
if (code.includes('vanilla') || code.includes('Vanilla'))
return 'vanilla';
return 'universal';
}
detectChildrenSupport(node) {
return ts.forEachChild(node, (child) => {
if (ts.isJsxElement(child)) {
return child.children && child.children.length > 0;
}
return false;
}) || false;
}
deduplicateProps(props) {
const seen = new Set();
return props.filter(prop => {
if (seen.has(prop.name)) {
return false;
}
seen.add(prop.name);
return true;
});
}
mapTypeScriptType(tsType) {
const typeLower = tsType.toLowerCase();
if (typeLower.includes('string'))
return 'text';
if (typeLower.includes('number'))
return 'number';
if (typeLower.includes('boolean'))
return 'boolean';
if (typeLower.includes('array') || typeLower.includes('[]'))
return 'array';
if (typeLower.includes('object') || typeLower.includes('{}'))
return 'object';
if (typeLower.includes('function') || typeLower.includes('()'))
return 'function';
if (typeLower.includes('void') || typeLower.includes('undefined'))
return 'text';
if (typeLower.includes('null'))
return 'text';
if (typeLower.includes('any') || typeLower.includes('unknown'))
return 'text';
return 'text';
}
}
exports.RealASTAnalyzer = RealASTAnalyzer;
// Экспортируем экземпляр
exports.realASTAnalyzer = new RealASTAnalyzer();