UNPKG

userface

Version:

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

227 lines (226 loc) 9.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BrowserEngine = void 0; const react_1 = __importDefault(require("react")); /** * Браузерный движок для обработки и рендеринга компонентов * Работает только в браузере, без серверных зависимостей */ class BrowserEngine { constructor() { Object.defineProperty(this, "components", { enumerable: true, configurable: true, writable: true, value: new Map() }); Object.defineProperty(this, "styleElements", { enumerable: true, configurable: true, writable: true, value: new Map() }); } /** * Обрабатывает пакет файлов компонента */ async processComponentBundle(bundle) { console.log(`[BrowserEngine] Processing bundle: ${bundle.name}`); // 1. Найдем главный TSX файл const mainFile = bundle.files.find(f => f.type === 'tsx' && f.name.includes(bundle.name)); if (!mainFile) { throw new Error(`Main TSX file not found for component ${bundle.name}`); } // 2. Найдем CSS файлы const cssFiles = bundle.files.filter(f => f.type === 'css'); // 3. Анализируем схему компонента (упрощенная версия) const schema = this.analyzeComponentSchema(mainFile); console.log(`[BrowserEngine] Schema extracted:`, schema); // 4. Компилируем TSX в исполняемую функцию (упрощенная версия) const factory = this.compileComponent(mainFile, bundle.name); console.log(`[BrowserEngine] Component compiled:`, factory); // 5. Обрабатываем стили const styles = this.processStyles(cssFiles, bundle.name); console.log(`[BrowserEngine] Styles processed:`, styles.length); // 6. Генерируем дефолтные пропсы const props = this.generateDefaultProps(schema); // 7. Создаем обработанный компонент const processed = { name: bundle.name, factory, schema, styles, props }; // 8. Регистрируем в движке this.components.set(bundle.name, processed); console.log(`[BrowserEngine] Component registered: ${bundle.name}`); return processed; } /** * Упрощенный анализ схемы компонента через regex */ analyzeComponentSchema(file) { console.log(`[BrowserEngine] Analyzing schema for: ${file.name}`); const props = []; let componentName = ''; // Ищем имя компонента const componentMatch = file.code.match(/(?:export\s+)?(?:const|function|class)\s+(\w+)/); if (componentMatch) { componentName = componentMatch[1]; } // Ищем интерфейс пропсов const interfaceMatch = file.code.match(/interface\s+(\w+Props)\s*\{([^}]+)\}/); if (interfaceMatch) { const interfaceContent = interfaceMatch[2]; const propMatches = interfaceContent.matchAll(/(\w+)\s*:\s*([^;]+);/g); for (const match of propMatches) { const propName = match[1]; const typeText = match[2].trim(); let propType = 'text'; if (typeText.includes('boolean')) propType = 'boolean'; else if (typeText.includes('number')) propType = 'number'; else if (typeText.includes('function') || typeText.includes('=>')) propType = 'function'; props.push({ name: propName, type: propType, required: !typeText.includes('?'), description: `${propName}: ${typeText};` }); } } // Если интерфейс не найден, ищем пропсы в параметрах функции if (props.length === 0) { const propsMatch = file.code.match(/\([^)]*\{[^}]*\}[^)]*\)/); if (propsMatch) { // Простая эвристика - добавляем базовые пропсы props.push({ name: 'text', type: 'text', required: false, description: 'text: string;' }, { name: 'disabled', type: 'boolean', required: false, description: 'disabled: boolean;' }); } } return { name: componentName || file.name.replace('.tsx', ''), detectedPlatform: 'react', props, events: [], children: true, description: `Generated schema for ${componentName}`, supportsChildren: true }; } /** * Упрощенная компиляция TSX компонента */ compileComponent(file, componentName) { console.log(`[BrowserEngine] Compiling component: ${componentName}`); // 1. Подготавливаем код - убираем CSS импорты и TypeScript типы let cleanCode = file.code; // Удаляем CSS импорты cleanCode = cleanCode.replace(/import\s+['"][^'"]*\.css['"];?\s*/g, ''); // Удаляем React импорты cleanCode = cleanCode.replace(/import\s+React[^;]*;?\s*/g, ''); // Удаляем TypeScript типы из параметров cleanCode = cleanCode.replace(/:\s*[A-Za-z<>\[\]{}|&]+/g, ''); // Удаляем интерфейсы cleanCode = cleanCode.replace(/interface\s+\w+\s*\{[^}]*\}/g, ''); // Добавляем React глобально cleanCode = `const React = window.React;\n${cleanCode}`; // 2. Создаем функцию-компонент const componentFunction = new Function('React', `${cleanCode}\n\nreturn ${componentName};`); // 3. Исполняем и получаем компонент const component = componentFunction(react_1.default); if (typeof component !== 'function') { throw new Error(`Component ${componentName} is not a valid React component`); } return component; } /** * Обработка CSS стилей */ processStyles(cssFiles, componentName) { const styles = []; cssFiles.forEach((file, index) => { const styleId = `${componentName}-style-${index}`; // Удаляем предыдущий стиль если есть const existingStyle = this.styleElements.get(styleId); if (existingStyle) { existingStyle.remove(); } // Создаем новый элемент стиля const styleElement = document.createElement('style'); styleElement.id = styleId; styleElement.textContent = file.code; document.head.appendChild(styleElement); // Сохраняем ссылку this.styleElements.set(styleId, styleElement); styles.push(file.code); console.log(`[BrowserEngine] Style applied: ${file.name}`); }); return styles; } /** * Генерация дефолтных пропсов */ generateDefaultProps(schema) { const props = {}; schema.props.forEach(prop => { if (prop.required && ['text', 'boolean', 'number'].includes(prop.type)) { switch (prop.type) { case 'text': props[prop.name] = 'Sample Text'; break; case 'boolean': props[prop.name] = false; break; case 'number': props[prop.name] = 0; break; } } }); return props; } /** * Рендеринг компонента */ renderComponent(name, props = {}) { const processed = this.components.get(name); if (!processed) { console.error(`[BrowserEngine] Component not found: ${name}`); return null; } const Component = processed.factory; const finalProps = { ...processed.props, ...props }; console.log(`[BrowserEngine] Rendering component: ${name}`, finalProps); return react_1.default.createElement(Component, finalProps); } /** * Получение обработанного компонента */ getComponent(name) { return this.components.get(name); } /** * Получение всех компонентов */ getAllComponents() { return Array.from(this.components.keys()); } /** * Очистка движка */ clear() { // Удаляем все стили this.styleElements.forEach(style => style.remove()); // Очищаем кеши this.components.clear(); this.styleElements.clear(); console.log(`[BrowserEngine] Engine cleared`); } } exports.BrowserEngine = BrowserEngine;