userface
Version:
Universal Data-Driven UI Engine with live data, validation, and multi-platform support
1,045 lines (886 loc) • 31 kB
JavaScript
/**
* Robust Browser Engine - Production-ready component processing
* No hardcode, no hacks, no temporary solutions
*/
export class RobustBrowserEngine {
constructor(options = {}) {
this.options = {
debug: false,
strictMode: true,
isolateComponents: true,
...options
};
this.components = new Map();
this.styles = new Map();
this.modules = new Map();
this.dependencies = new Map();
this.log('Engine initialized with options:', this.options);
}
/**
* Process component bundle with proper validation and error handling
*/
async processComponentBundle(bundle) {
this.validateBundle(bundle);
const context = this.createProcessingContext(bundle);
try {
// 1. Parse and validate all files
const parsedFiles = await this.parseFiles(bundle.files, context);
// 2. Extract component metadata
const metadata = this.extractMetadata(parsedFiles, context);
// 3. Resolve dependencies
const dependencies = this.resolveDependencies(parsedFiles, context);
// 4. Compile component
const factory = await this.compileComponent(parsedFiles, metadata, dependencies, context);
// 5. Process styles
const styles = this.processComponentStyles(parsedFiles, context);
// 6. Create component descriptor
const descriptor = this.createComponentDescriptor({
name: bundle.name,
factory,
metadata,
dependencies,
styles,
context
});
// 7. Register component
this.registerComponent(descriptor);
return descriptor;
} catch (error) {
this.handleProcessingError(error, context);
throw error;
}
}
/**
* Validate bundle structure
*/
validateBundle(bundle) {
if (!bundle || typeof bundle !== 'object') {
throw new Error('Bundle must be an object');
}
if (!bundle.name || typeof bundle.name !== 'string') {
throw new Error('Bundle must have a valid name');
}
if (!Array.isArray(bundle.files) || bundle.files.length === 0) {
throw new Error('Bundle must contain files array');
}
// Validate each file
bundle.files.forEach((file, index) => {
if (!file.name || !file.code || !file.type) {
throw new Error(`File at index ${index} is missing required properties`);
}
});
}
/**
* Create isolated processing context
*/
createProcessingContext(bundle) {
return {
bundleName: bundle.name,
timestamp: Date.now(),
scope: `component_${bundle.name}_${Date.now()}`,
errors: [],
warnings: [],
metadata: {}
};
}
/**
* Parse all files in bundle
*/
async parseFiles(files, context) {
const parsed = new Map();
for (const file of files) {
try {
const parseResult = await this.parseFile(file);
parsed.set(file.name, parseResult);
} catch (error) {
this.log(`Failed to parse file ${file.name}:`, error);
throw error;
}
}
return parsed;
}
/**
* Parse a single file and extract metadata
*/
async parseFile(file) {
console.log(`[RobustBrowserEngine] Parsing file: ${file.path} (${file.type})`);
const result = {
path: file.path,
name: file.name,
type: file.type,
originalCode: file.code,
cleanCode: null,
metadata: {}
};
if (file.type === 'tsx') {
// Clean code by removing TypeScript-specific syntax
result.cleanCode = await this.cleanTypeScriptCode(file.code, result);
return result;
} else if (file.type === 'css') {
// CSS files are used as-is
result.cleanCode = file.code;
return result;
}
return result;
}
/**
* Parse TypeScript/TSX file with proper analysis
*/
async parseTypeScriptFile(file, context) {
const result = {
type: 'typescript',
originalCode: file.code,
cleanCode: '',
exports: [],
imports: [],
interfaces: [],
components: [],
dependencies: []
};
// Extract imports
const importRegex = /import\s+(?:(?:\{[^}]*\}|\w+|\*\s+as\s+\w+)\s+from\s+)?['"]([^'"]+)['"];?/g;
let match;
while ((match = importRegex.exec(file.code)) !== null) {
result.imports.push({
statement: match[0],
module: match[1]
});
}
// Extract interfaces
const interfaceRegex = /interface\s+(\w+)\s*\{([^}]+)\}/g;
while ((match = interfaceRegex.exec(file.code)) !== null) {
result.interfaces.push({
name: match[1],
definition: match[2],
props: this.parseInterfaceProps(match[2])
});
}
// Extract component definition - AST-BASED APPROACH
console.log(`[RobustBrowserEngine] Extracting components using AST`);
if (typeof window !== 'undefined' && window.Babel) {
try {
// Parse with Babel to get AST
const ast = window.Babel.parse(file.code, {
presets: [['react', { runtime: 'classic' }]],
plugins: ['@babel/plugin-syntax-typescript']
});
// Extract components from AST
this.extractComponentsFromAST(ast, result);
} catch (error) {
console.warn(`[RobustBrowserEngine] AST parsing failed, falling back to regex:`, error);
// Fallback to regex
this.extractComponentsWithRegex(file.code, result);
}
} else {
// Fallback to regex
this.extractComponentsWithRegex(file.code, result);
}
console.log(`[RobustBrowserEngine] Extracted components:`, result.components);
// Clean code by removing TypeScript-specific syntax
result.cleanCode = await this.cleanTypeScriptCode(file.code, result);
return result;
}
/**
* Parse interface properties
*/
parseInterfaceProps(interfaceBody) {
const props = [];
const propRegex = /(\w+)\s*\??\s*:\s*([^;,]+)/g;
let match;
while ((match = propRegex.exec(interfaceBody)) !== null) {
props.push({
name: match[1],
type: match[2].trim(),
optional: interfaceBody.includes(`${match[1]}?`)
});
}
return props;
}
/**
* Clean TypeScript code for execution - PRECISE PATTERNS ONLY
*/
async cleanTypeScriptCode(code, parseResult) {
console.log(`[RobustBrowserEngine] CLEANING CODE - ORIGINAL:`, code);
let cleaned = code;
// Remove imports (will be handled by dependency resolution)
cleaned = cleaned.replace(/import\s+[^;]+;?\s*/g, '');
console.log(`[RobustBrowserEngine] After removing imports:`, cleaned);
// Remove ALL interfaces completely - including extends - PRECISE PATTERNS
cleaned = cleaned.replace(/export\s+interface\s+\w+\s+extends\s+[^{]+\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/interface\s+\w+\s+extends\s+[^{]+\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/export\s+interface\s+\w+[^{]*\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/interface\s+\w+[^{]*\{[^}]*\}\s*/g, '');
console.log(`[RobustBrowserEngine] After removing interfaces:`, cleaned);
// Remove React.FC type annotations - PRECISE PATTERN
cleaned = cleaned.replace(/:\s*React\.FC<[^>]+>/g, '');
console.log(`[RobustBrowserEngine] After removing React.FC:`, cleaned);
// Remove ONLY TypeScript function parameter type annotations - PRECISE PATTERNS
// Pattern 1: ({ param1, param2 }: { param1: Type, param2: Type })
cleaned = cleaned.replace(/\(\s*\{([^}]+)\}\s*:\s*\{[^}]+\}\s*\)/g, '({ $1 })');
// Pattern 2: ({ param1, param2 }: InterfaceName)
cleaned = cleaned.replace(/\(\s*\{([^}]+)\}\s*:\s*[A-Z][A-Za-z]+\s*\)/g, '({ $1 })');
// Pattern 3: (param: Type) - including React.Type<Generic>
cleaned = cleaned.replace(/\(\s*(\w+)\s*:\s*[A-Za-z][A-Za-z.]*<[^>]+>\s*\)/g, '($1)');
// Pattern 4: (param: SimpleType)
cleaned = cleaned.replace(/\(\s*(\w+)\s*:\s*[A-Z][A-Za-z.]+\s*\)/g, '($1)');
console.log(`[RobustBrowserEngine] After removing function parameter types:`, cleaned);
// Remove variable type annotations - PRECISE PATTERNS
cleaned = cleaned.replace(/const\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'const $1 =');
cleaned = cleaned.replace(/let\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'let $1 =');
cleaned = cleaned.replace(/var\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'var $1 =');
console.log(`[RobustBrowserEngine] After removing variable types:`, cleaned);
// Remove export statements
cleaned = cleaned.replace(/export\s+(?:default\s+)?/g, '');
console.log(`[RobustBrowserEngine] After removing exports:`, cleaned);
// Remove type assertions
cleaned = cleaned.replace(/as\s+\w+/g, '');
// Fix reserved words in object properties
cleaned = this.fixReservedWords(cleaned);
console.log(`[RobustBrowserEngine] After fixing reserved words:`, cleaned);
// Transform JSX using BABEL - the ONLY correct way
if (cleaned.includes('<') && cleaned.includes('>') && !cleaned.includes('interface')) {
console.log(`[RobustBrowserEngine] Detected JSX syntax, using Babel`);
try {
if (typeof window !== 'undefined' && window.Babel) {
const result = window.Babel.transform(cleaned, {
presets: [['react', { runtime: 'classic' }]],
plugins: []
});
cleaned = result.code;
console.log(`[RobustBrowserEngine] Babel JSX transformation completed`);
} else {
console.warn(`[RobustBrowserEngine] Babel not available, JSX will fail`);
}
} catch (error) {
console.warn(`[RobustBrowserEngine] Babel transformation failed:`, error);
}
}
console.log(`[RobustBrowserEngine] FINAL CLEANED CODE:`, cleaned);
return cleaned.trim();
}
/**
* Fix reserved words in object properties and destructuring
*/
fixReservedWords(code) {
const reservedWords = [
'class', 'interface', 'package', 'private', 'protected', 'public',
'static', 'extends', 'implements', 'import', 'export', 'default',
'enum', 'namespace', 'module', 'declare', 'abstract', 'readonly'
];
reservedWords.forEach(word => {
// Fix in object properties: { class: value } -> { 'class': value }
const objectPropRegex = new RegExp(`\\b${word}\\s*:`, 'g');
code = code.replace(objectPropRegex, `'${word}':`);
// Fix in destructuring: { class } -> { 'class': class }
const destructuringRegex = new RegExp(`\\{([^}]*\\b)${word}(\\b[^}]*)\\}`, 'g');
code = code.replace(destructuringRegex, (match, before, after) => {
return `{${before}'${word}': ${word}${after}}`;
});
// Fix property access: obj.class -> obj['class']
const propAccessRegex = new RegExp(`\\.${word}\\b`, 'g');
code = code.replace(propAccessRegex, `['${word}']`);
});
return code;
}
/**
* Transform JSX to React.createElement calls - USING BABEL
*/
async transformJSXToCreateElement(code) {
console.log(`[RobustBrowserEngine] Starting BABEL JSX transformation`);
// Don't transform if there are still TypeScript artifacts
if (code.includes('interface') || code.includes('React.FC')) {
console.log(`[RobustBrowserEngine] Skipping JSX transformation - TypeScript artifacts present`);
return code;
}
try {
// Use Babel for proper JSX transformation
if (typeof window !== 'undefined' && window.Babel) {
const result = window.Babel.transform(code, {
presets: [['react', { runtime: 'classic' }]],
plugins: []
});
console.log(`[RobustBrowserEngine] Babel JSX transformation completed`);
return result.code;
} else {
console.warn(`[RobustBrowserEngine] Babel not available, skipping JSX transformation`);
return code;
}
} catch (error) {
console.warn(`[RobustBrowserEngine] Babel JSX transformation failed:`, error);
return code; // Return original code if transformation fails
}
}
/**
* Parse CSS file
*/
parseCSSFile(file, context) {
return {
type: 'css',
originalCode: file.code,
rules: this.extractCSSRules(file.code),
variables: this.extractCSSVariables(file.code)
};
}
/**
* Extract CSS rules
*/
extractCSSRules(css) {
const rules = [];
const ruleRegex = /([^{]+)\{([^}]+)\}/g;
let match;
while ((match = ruleRegex.exec(css)) !== null) {
rules.push({
selector: match[1].trim(),
declarations: match[2].trim()
});
}
return rules;
}
/**
* Extract CSS variables
*/
extractCSSVariables(css) {
const variables = [];
const varRegex = /--([\w-]+):\s*([^;]+);/g;
let match;
while ((match = varRegex.exec(css)) !== null) {
variables.push({
name: match[1],
value: match[2].trim()
});
}
return variables;
}
/**
* Extract component metadata
*/
extractMetadata(parsedFiles, context) {
const metadata = {
name: context.bundleName,
props: [],
events: [],
slots: [],
platform: 'react'
};
// Find main component file
const mainFile = this.findMainComponentFile(parsedFiles, context.bundleName);
if (!mainFile) {
throw new Error(`Main component file not found for ${context.bundleName}`);
}
// Extract props from interface
const propsInterface = mainFile.interfaces.find(iface =>
iface.name.toLowerCase().includes('props')
);
if (propsInterface) {
metadata.props = propsInterface.props.map(prop => ({
name: prop.name,
type: this.mapTypeScriptType(prop.type),
required: !prop.optional,
description: `${prop.name}: ${prop.type}`
}));
}
return metadata;
}
/**
* Find main component file
*/
findMainComponentFile(parsedFiles, componentName) {
console.log(`[RobustBrowserEngine] Searching for component: ${componentName}`);
console.log(`[RobustBrowserEngine] Parsed files:`, parsedFiles);
for (const [fileName, parsed] of parsedFiles) {
console.log(`[RobustBrowserEngine] Checking file: ${fileName}`, {
type: parsed.type,
components: parsed.components,
hasComponents: !!parsed.components
});
if ((parsed.type === 'typescript' || parsed.type === 'tsx') &&
parsed.components && parsed.components.some(comp => comp.name === componentName)) {
console.log(`[RobustBrowserEngine] Found main component file: ${fileName}`);
return parsed;
}
}
console.log(`[RobustBrowserEngine] No main component file found for: ${componentName}`);
return null;
}
/**
* Map TypeScript types to our internal types
*/
mapTypeScriptType(tsType) {
const typeMap = {
'string': 'text',
'boolean': 'boolean',
'number': 'number',
'Function': 'function',
'() => void': 'function'
};
return typeMap[tsType] || 'text';
}
/**
* Resolve component dependencies
*/
resolveDependencies(parsedFiles, context) {
const dependencies = new Set();
for (const [fileName, parsed] of parsedFiles) {
if (parsed.imports) {
parsed.imports.forEach(imp => {
if (!imp.module.startsWith('.')) { // External dependency
dependencies.add(imp.module);
}
});
}
}
return Array.from(dependencies);
}
/**
* Compile component with proper isolation
*/
async compileComponent(parsedFiles, metadata, dependencies, context) {
const mainFile = this.findMainComponentFile(parsedFiles, context.bundleName);
if (!mainFile) {
throw new Error('Main component file not found');
}
try {
// Create isolated execution context
const executionContext = this.createExecutionContext(dependencies, context);
// Compile the component
const factory = this.executeComponentCode(mainFile.cleanCode, metadata.name, executionContext);
// Validate compiled component
this.validateCompiledComponent(factory, metadata);
return factory;
} catch (error) {
this.log('Compilation failed:', error);
return this.createFallbackComponent(metadata, error);
}
}
/**
* Create execution context with proper dependency injection
*/
createExecutionContext(dependencies, context) {
const ctx = {
React: this.getReactReference(),
console: this.createScopedConsole(context.scope),
// Add other required globals
};
// Inject dependencies
dependencies.forEach(dep => {
if (this.isKnownDependency(dep)) {
ctx[dep] = this.resolveDependency(dep);
}
});
return ctx;
}
/**
* Get React reference safely
*/
getReactReference() {
if (typeof window !== 'undefined' && window.React) {
return window.React;
}
throw new Error('React is not available in global scope');
}
/**
* Create scoped console for debugging
*/
createScopedConsole(scope) {
return {
log: (...args) => this.log(`[${scope}]`, ...args),
warn: (...args) => this.log(`[${scope}] WARN:`, ...args),
error: (...args) => this.log(`[${scope}] ERROR:`, ...args)
};
}
/**
* Execute component code in controlled environment
*/
executeComponentCode(code, componentName, context) {
const contextKeys = Object.keys(context);
const contextValues = Object.values(context);
console.log(`[RobustBrowserEngine] EXECUTING CODE FOR ${componentName}:`);
console.log('Clean code:', code);
console.log('Context keys:', contextKeys);
// Try without strict mode first
let wrappedCode = `
${code}
if (typeof ${componentName} !== 'function') {
throw new Error('Component ${componentName} is not a function');
}
return ${componentName};
`;
console.log(`[RobustBrowserEngine] WRAPPED CODE (non-strict):`, wrappedCode);
try {
const factory = new Function(...contextKeys, wrappedCode);
const result = factory(...contextValues);
console.log(`[RobustBrowserEngine] EXECUTION RESULT:`, result);
console.log(`[RobustBrowserEngine] IS FUNCTION:`, typeof result === 'function');
return result;
} catch (error) {
console.warn(`[RobustBrowserEngine] Non-strict execution failed:`, error);
// Try with more aggressive cleaning
const cleanedCode = this.aggressiveCleanCode(code);
const fallbackWrappedCode = `
${cleanedCode}
if (typeof ${componentName} !== 'function') {
throw new Error('Component ${componentName} is not a function');
}
return ${componentName};
`;
console.log(`[RobustBrowserEngine] FALLBACK WRAPPED CODE:`, fallbackWrappedCode);
try {
const fallbackFactory = new Function(...contextKeys, fallbackWrappedCode);
const fallbackResult = fallbackFactory(...contextValues);
console.log(`[RobustBrowserEngine] FALLBACK EXECUTION SUCCESS:`, fallbackResult);
return fallbackResult;
} catch (fallbackError) {
console.error(`[RobustBrowserEngine] All execution attempts failed:`, fallbackError);
throw new Error(`Compilation failed: ${fallbackError.message}`);
}
}
}
/**
* Aggressive code cleaning for problematic cases - PRECISE PATTERNS ONLY
*/
aggressiveCleanCode(code) {
console.log(`[RobustBrowserEngine] Starting aggressive cleaning`);
let cleaned = code;
// Remove all comments
cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, '');
cleaned = cleaned.replace(/\/\/.*$/gm, '');
// Remove ALL interfaces completely - PRECISE PATTERNS
cleaned = cleaned.replace(/export\s+interface\s+\w+\s+extends\s+[^{]+\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/interface\s+\w+\s+extends\s+[^{]+\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/export\s+interface\s+\w+[^{]*\{[^}]*\}\s*/g, '');
cleaned = cleaned.replace(/interface\s+\w+[^{]*\{[^}]*\}\s*/g, '');
// Remove React.FC type annotations - PRECISE PATTERNS
cleaned = cleaned.replace(/:\s*React\.FC<[^>]+>/g, '');
cleaned = cleaned.replace(/const\s+(\w+)\s*:\s*React\.FC<[^>]+>\s*=/g, 'const $1 =');
// Remove type annotations from variable declarations - PRECISE PATTERNS
cleaned = cleaned.replace(/const\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'const $1 =');
cleaned = cleaned.replace(/let\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'let $1 =');
cleaned = cleaned.replace(/var\s+(\w+)\s*:\s*[A-Z][A-Za-z<>]+\s*=/g, 'var $1 =');
// Fix common strict mode issues
cleaned = cleaned.replace(/\bwith\s*\(/g, '// with(');
cleaned = cleaned.replace(/\beval\s*\(/g, '// eval(');
// Remove export statements
cleaned = cleaned.replace(/export\s+(?:default\s+)?/g, '');
// Remove ONLY TypeScript function parameter type annotations - PRECISE PATTERNS
cleaned = cleaned.replace(/\(\s*\{([^}]+)\}\s*:\s*\{[^}]+\}\s*\)/g, '({ $1 })');
cleaned = cleaned.replace(/\(\s*\{([^}]+)\}\s*:\s*[A-Z][A-Za-z]+\s*\)/g, '({ $1 })');
cleaned = cleaned.replace(/\(\s*(\w+)\s*:\s*[A-Za-z][A-Za-z.]*<[^>]+>\s*\)/g, '($1)');
cleaned = cleaned.replace(/\(\s*(\w+)\s*:\s*[A-Z][A-Za-z.]+\s*\)/g, '($1)');
// Fix reserved words
cleaned = this.fixReservedWords(cleaned);
console.log(`[RobustBrowserEngine] Aggressive cleaning completed`);
return cleaned;
}
/**
* Validate compiled component
*/
validateCompiledComponent(factory, metadata) {
if (typeof factory !== 'function') {
throw new Error(`Component ${metadata.name} is not a function`);
}
// Additional validation can be added here
}
/**
* Create fallback component for failed compilation
*/
createFallbackComponent(metadata, error) {
return (props) => {
const React = this.getReactReference();
return React.createElement('div', {
style: {
padding: '20px',
border: '2px dashed #ff6b6b',
borderRadius: '8px',
backgroundColor: '#fff5f5',
color: '#c53030',
fontFamily: 'monospace',
textAlign: 'center'
}
}, [
React.createElement('h3', { key: 'title' }, `⚠️ ${metadata.name}`),
React.createElement('p', { key: 'error' }, `Compilation Error: ${error.message}`),
React.createElement('details', { key: 'props' }, [
React.createElement('summary', { key: 'summary' }, 'Props'),
React.createElement('pre', { key: 'data' }, JSON.stringify(props, null, 2))
])
]);
};
}
/**
* Process component styles with proper scoping
*/
processComponentStyles(parsedFiles, context) {
const styles = [];
for (const [fileName, parsed] of parsedFiles) {
if (parsed.type === 'css') {
const scopedCSS = this.scopeCSS(parsed.originalCode, context.scope);
styles.push({
fileName,
originalCSS: parsed.originalCode,
scopedCSS,
rules: parsed.rules,
variables: parsed.variables
});
}
}
return styles;
}
/**
* Scope CSS to prevent conflicts
*/
scopeCSS(css, scope) {
// Simple scoping - prepend scope to selectors
return css.replace(/([^{}]+)\{/g, (match, selector) => {
const trimmedSelector = selector.trim();
if (trimmedSelector.startsWith('@') || trimmedSelector.startsWith(':root')) {
return match; // Don't scope at-rules or :root
}
return `.${scope} ${trimmedSelector} {`;
});
}
/**
* Create component descriptor
*/
createComponentDescriptor(data) {
return {
id: `${data.name}_${data.context.timestamp}`,
name: data.name,
factory: data.factory,
metadata: data.metadata,
dependencies: data.dependencies,
styles: data.styles,
context: data.context,
createdAt: new Date().toISOString(),
version: '1.0.0'
};
}
/**
* Register component in engine
*/
registerComponent(descriptor) {
// Unregister previous version if exists
if (this.components.has(descriptor.name)) {
this.unregisterComponent(descriptor.name);
}
this.components.set(descriptor.name, descriptor);
// Apply styles
this.applyComponentStyles(descriptor);
this.log(`Component registered: ${descriptor.name}`);
}
/**
* Apply component styles to DOM
*/
applyComponentStyles(descriptor) {
descriptor.styles.forEach((style, index) => {
const styleId = `${descriptor.id}_style_${index}`;
// Remove existing style if present
const existing = document.getElementById(styleId);
if (existing) {
existing.remove();
}
// Create and inject new style
const styleElement = document.createElement('style');
styleElement.id = styleId;
styleElement.textContent = style.scopedCSS;
document.head.appendChild(styleElement);
// Store reference for cleanup
this.styles.set(styleId, styleElement);
});
}
/**
* Unregister component and cleanup
*/
unregisterComponent(name) {
const descriptor = this.components.get(name);
if (!descriptor) return;
// Remove styles
descriptor.styles.forEach((_, index) => {
const styleId = `${descriptor.id}_style_${index}`;
const styleElement = this.styles.get(styleId);
if (styleElement) {
styleElement.remove();
this.styles.delete(styleId);
}
});
// Remove component
this.components.delete(name);
this.log(`Component unregistered: ${name}`);
}
/**
* Render component
*/
renderComponent(name, props = {}) {
const descriptor = this.components.get(name);
if (!descriptor) {
this.log(`Component not found: ${name}`);
return null;
}
try {
const React = this.getReactReference();
const finalProps = { ...descriptor.metadata.props.reduce((acc, prop) => {
acc[prop.name] = this.getDefaultValue(prop.type);
return acc;
}, {}), ...props };
// Wrap in scoped container
return React.createElement('div', {
className: descriptor.context.scope
}, React.createElement(descriptor.factory, finalProps));
} catch (error) {
this.log(`Render error for ${name}:`, error);
return this.createErrorComponent(name, error);
}
}
/**
* Get default value for prop type
*/
getDefaultValue(type) {
const defaults = {
'text': 'Sample Text',
'boolean': false,
'number': 0,
'function': () => {}
};
return defaults[type] || null;
}
/**
* Create error component
*/
createErrorComponent(name, error) {
const React = this.getReactReference();
return React.createElement('div', {
style: { color: 'red', padding: '10px', border: '1px solid red' }
}, `Error rendering ${name}: ${error.message}`);
}
/**
* Handle processing errors
*/
handleProcessingError(error, context) {
context.errors.push(error.message);
this.log(`Processing error in ${context.bundleName}:`, error);
}
/**
* Check if dependency is known
*/
isKnownDependency(dep) {
const knownDeps = ['react', 'react-dom'];
return knownDeps.includes(dep);
}
/**
* Resolve dependency
*/
resolveDependency(dep) {
switch (dep) {
case 'react':
return this.getReactReference();
default:
return null;
}
}
/**
* Get component by name
*/
getComponent(name) {
return this.components.get(name);
}
/**
* Get all registered components
*/
getAllComponents() {
return Array.from(this.components.keys());
}
/**
* Clear all components and cleanup
*/
clear() {
// Cleanup all components
for (const name of this.components.keys()) {
this.unregisterComponent(name);
}
// Clear maps
this.components.clear();
this.styles.clear();
this.modules.clear();
this.dependencies.clear();
this.log('Engine cleared');
}
/**
* Logging utility
*/
log(...args) {
if (this.options.debug) {
console.log('[RobustBrowserEngine]', ...args);
}
}
/**
* Extract components from Babel AST
*/
extractComponentsFromAST(ast, result) {
if (!ast || !ast.program || !ast.program.body) return;
ast.program.body.forEach(node => {
if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
this.extractComponentFromExport(node, result);
} else if (node.type === 'VariableDeclaration') {
this.extractComponentFromVariable(node, result);
} else if (node.type === 'FunctionDeclaration') {
this.extractComponentFromFunction(node, result);
}
});
}
/**
* Extract component from export declaration
*/
extractComponentFromExport(node, result) {
if (node.declaration) {
if (node.declaration.type === 'VariableDeclaration') {
this.extractComponentFromVariable(node.declaration, result);
} else if (node.declaration.type === 'FunctionDeclaration') {
this.extractComponentFromFunction(node.declaration, result);
}
}
}
/**
* Extract component from variable declaration
*/
extractComponentFromVariable(node, result) {
node.declarations.forEach(declaration => {
if (declaration.id && declaration.id.name) {
const name = declaration.id.name;
// Check if it's a React component (starts with uppercase)
if (name[0] === name[0].toUpperCase()) {
result.components.push({
name: name,
type: 'function'
});
}
}
});
}
/**
* Extract component from function declaration
*/
extractComponentFromFunction(node, result) {
if (node.id && node.id.name) {
const name = node.id.name;
// Check if it's a React component (starts with uppercase)
if (name[0] === name[0].toUpperCase()) {
result.components.push({
name: name,
type: 'function'
});
}
}
}
/**
* Fallback regex extraction
*/
extractComponentsWithRegex(code, result) {
const componentRegex = /(?:export\s+)?(?:const|function|class)\s+(\w+)\s*(?:[=:]|:\s*React\.FC<[^>]+>)/g;
let match;
while ((match = componentRegex.exec(code)) !== null) {
result.components.push({
name: match[1],
type: 'function'
});
}
}
}