userface
Version:
Universal Data-Driven UI Engine with live data, validation, and multi-platform support
255 lines (254 loc) • 10.6 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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnifiedEngine = void 0;
const ts = __importStar(require("typescript"));
const standalone_1 = require("@babel/standalone");
const parser = __importStar(require("@babel/parser"));
const traverse_1 = __importDefault(require("@babel/traverse"));
const generator_1 = __importDefault(require("@babel/generator"));
/**
* Унифицированный движок для обработки компонентов
* Объединяет AST анализ, Babel компиляцию, регистрацию
*/
class UnifiedEngine {
constructor() {
Object.defineProperty(this, "componentRegistry", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "styleRegistry", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}
/**
* Основной метод: обрабатывает компонент от кода до готовой схемы
*/
async processComponent(input) {
console.log(`[UnifiedEngine] Processing component: ${input.name}`);
// 1. AST Анализ (извлекаем пропсы и метаданные)
const schema = await this.analyzeComponent(input);
console.log(`[UnifiedEngine] Schema generated:`, schema);
// 2. Babel Компиляция (TSX -> JS + обработка импортов)
const { compiledCode, cssImports } = await this.compileComponent(input);
console.log(`[UnifiedEngine] Compilation complete, CSS imports:`, cssImports);
// 3. Генерация дефолтных пропсов
const props = this.generateDefaultProps(schema);
console.log(`[UnifiedEngine] Default props:`, props);
// 4. Создаем финальный объект
const processed = {
name: input.name,
compiledCode,
schema,
cssImports,
props
};
// 5. Регистрируем в движке
this.componentRegistry.set(input.name, processed);
console.log(`[UnifiedEngine] Component registered: ${input.name}`);
return processed;
}
/**
* AST анализ компонента
*/
async analyzeComponent(input) {
console.log(`[UnifiedEngine] Starting AST analysis for: ${input.name}`);
// Создаем TypeScript программу
const sourceFile = ts.createSourceFile(`${input.name}.tsx`, input.code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
const props = [];
let componentFound = false;
// Обходим AST
const visit = (node) => {
// Ищем интерфейс пропсов
if (ts.isInterfaceDeclaration(node) && node.name.text.includes('Props')) {
console.log(`[UnifiedEngine] Found props interface: ${node.name.text}`);
node.members.forEach(member => {
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
const propName = member.name.text;
const isRequired = !member.questionToken;
const typeText = member.type ? sourceFile.text.substring(member.type.pos, member.type.end).trim() : 'any';
// Определяем тип пропа
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: isRequired,
description: `${propName}: ${typeText};`
});
console.log(`[UnifiedEngine] Found prop: ${propName} (${propType}, required: ${isRequired})`);
}
});
}
// Ищем компонент
if (ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node)) {
const name = node.name?.getText(sourceFile);
if (name === input.name) {
componentFound = true;
console.log(`[UnifiedEngine] Found component declaration: ${name}`);
}
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
if (!componentFound) {
console.warn(`[UnifiedEngine] Component ${input.name} not found in code`);
}
const schema = {
name: input.name,
detectedPlatform: 'react',
props,
events: [],
children: true,
description: `Auto-generated schema for ${input.name}`,
supportsChildren: true
};
console.log(`[UnifiedEngine] AST analysis complete for ${input.name}:`, schema);
return schema;
}
/**
* Babel компиляция компонента
*/
async compileComponent(input) {
console.log(`[UnifiedEngine] Starting Babel compilation for: ${input.name}`);
// Парсим AST для обработки импортов
const ast = parser.parse(input.code, {
sourceType: 'module',
plugins: ['typescript', 'jsx'],
});
const cssImports = [];
// Обрабатываем импорты
(0, traverse_1.default)(ast, {
ImportDeclaration(path) {
const source = path.node.source.value;
if (source.endsWith('.css')) {
// Сохраняем CSS импорты и удаляем из кода
cssImports.push(source);
path.remove();
console.log(`[UnifiedEngine] Found CSS import: ${source}`);
}
else if (source === 'react' || source === 'react-dom') {
// Заменяем на CDN импорты для клиента
path.node.source.value = `https://esm.sh/${source}`;
console.log(`[UnifiedEngine] Replaced import: ${source} -> https://esm.sh/${source}`);
}
},
});
// Генерируем обновленный код
const { code: transformedCode } = (0, generator_1.default)(ast);
// Компилируем через Babel
const compiled = (0, standalone_1.transform)(transformedCode, {
presets: ['react', 'typescript'],
filename: `${input.name}.tsx`,
});
if (!compiled.code) {
throw new Error(`Babel compilation failed for ${input.name}`);
}
console.log(`[UnifiedEngine] Babel compilation complete for ${input.name}`);
return {
compiledCode: compiled.code,
cssImports
};
}
/**
* Генерация дефолтных значений пропсов
*/
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;
}
/**
* Получение обработанного компонента
*/
getProcessedComponent(name) {
return this.componentRegistry.get(name);
}
/**
* Получение всех зарегистрированных компонентов
*/
getAllComponents() {
return Array.from(this.componentRegistry.keys());
}
/**
* Регистрация CSS стилей
*/
registerStyles(name, css) {
this.styleRegistry.set(name, css);
console.log(`[UnifiedEngine] Styles registered for: ${name}`);
}
/**
* Получение CSS стилей
*/
getStyles(name) {
return this.styleRegistry.get(name);
}
/**
* Очистка реестра
*/
clear() {
this.componentRegistry.clear();
this.styleRegistry.clear();
console.log(`[UnifiedEngine] Registry cleared`);
}
}
exports.UnifiedEngine = UnifiedEngine;