UNPKG

userface

Version:

Universal Data-Driven UI Engine with live data, validation, and multi-platform support

419 lines (418 loc) 18.5 kB
"use strict"; 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();