UNPKG

@ordojs/core

Version:

Core compiler and runtime for OrdoJS framework

533 lines 19.8 kB
/** * @fileoverview OrdoJS Code Generator - Refactored modular implementation * @author OrdoJS Framework Team */ /** * Source map builder utility */ export class SourceMapBuilder { segments = []; sources = []; names = []; addMapping(generatedLine, generatedColumn, originalLine, originalColumn, source, name) { if (!this.sources.includes(source)) { this.sources.push(source); } if (name && !this.names.includes(name)) { this.names.push(name); } this.segments.push({ generatedLine, generatedColumn, originalLine, originalColumn, source, name }); } build() { return { version: 3, sources: this.sources, names: this.names, mappings: this.encodeMappings(), sourcesContent: [] }; } encodeMappings() { // Simplified VLQ encoding - in production, use proper source map library return this.segments .map(seg => `${seg.generatedLine},${seg.generatedColumn},${seg.originalLine},${seg.originalColumn}`) .join(';'); } } /** * Enhanced OrdoJS Code Generator with modular architecture */ export class OrdoJSCodeGenerator { options; transformers; constructor(options = {}) { this.options = { target: 'development', minify: false, sourceMaps: true, esTarget: 'es2022', treeShaking: true, transformers: [], format: 'esm', hmr: false, splitting: 'none', ...options }; this.transformers = new Map([ ['pre', []], ['post', []], ['optimize', []] ]); this.registerDefaultTransformers(); this.registerCustomTransformers(); } /** * Generate code for all targets */ generate(ast) { const metadata = this.extractMetadata(ast); const context = this.createTransformContext(ast, metadata); const results = {}; // Generate client code if (metadata.hasClientCode || ast.component.markupBlock) { results.client = this.generateClientCode(ast, context); } // Generate server code if (metadata.hasServerCode) { results.server = this.generateServerCode(ast, context); } // Generate HTML results.html = this.generateHTML(ast, context); // Generate CSS if (metadata.hasStyles) { results.css = this.generateCSS(ast, context); } // Generate source map if (this.options.sourceMaps) { results.sourceMap = context.sourceMapBuilder?.build() || this.createEmptySourceMap(); } return results; } /** * Generate client-side code */ generateClientCode(ast, context) { const genContext = this.createCodeGenContext(); this.generateClientImports(ast, genContext); this.generateClientComponent(ast.component, genContext); this.generateClientExports(ast, genContext); let code = genContext.lines.join('\n'); // Apply transformers code = this.applyTransformers(code, { ...context, target: 'client' }); return code; } /** * Generate server-side code */ generateServerCode(ast, context) { if (!ast.component.serverBlock) { return ''; } const genContext = this.createCodeGenContext(); this.generateServerImports(ast, genContext); this.generateServerFunctions(ast.component.serverBlock, genContext); this.generateServerExports(ast, genContext); let code = genContext.lines.join('\n'); // Apply transformers code = this.applyTransformers(code, { ...context, target: 'server' }); return code; } /** * Generate HTML template */ generateHTML(ast, context, props = {}) { const genContext = this.createCodeGenContext(); this.generateHTMLStructure(ast.component.markupBlock, genContext, props); let code = genContext.lines.join('\n'); // Apply transformers code = this.applyTransformers(code, { ...context, target: 'static' }); return code; } /** * Generate CSS styles */ generateCSS(ast, context) { // CSS generation would be implemented here return ''; } /** * Add custom transformer */ addTransformer(transformer) { const phaseTransformers = this.transformers.get(transformer.phase) || []; phaseTransformers.push(transformer); this.transformers.set(transformer.phase, phaseTransformers); } /** * Remove transformer by name */ removeTransformer(name) { for (const [phase, transformers] of this.transformers) { const index = transformers.findIndex(t => t.name === name); if (index >= 0) { transformers.splice(index, 1); return true; } } return false; } registerDefaultTransformers() { // Minification transformer this.addTransformer({ name: 'minifier', phase: 'optimize', targets: ['client', 'server'], transform: (code, context) => { if (!context.options.minify) { return { code }; } // Simple minification - in production, use proper minifier const minified = code .replace(/\/\*[\s\S]*?\*\//g, '') // Remove comments .replace(/\/\/.*$/gm, '') // Remove single-line comments .replace(/\s+/g, ' ') // Collapse whitespace .replace(/;\s*}/g, '}') // Remove unnecessary semicolons .trim(); return { code: minified }; } }); // ES target transformer this.addTransformer({ name: 'es-target', phase: 'post', targets: ['client', 'server'], transform: (code, context) => { if (context.options.esTarget === 'es2022') { return { code }; } // Transform modern JS to older versions let transformed = code; if (context.options.esTarget === 'es2018' || context.options.esTarget === 'es5') { // Transform arrow functions to regular functions transformed = transformed.replace(/(\w+)\s*=>\s*{([^}]*)}/g, 'function($1) { $2 }'); // Transform const/let to var for ES5 if (context.options.esTarget === 'es5') { transformed = transformed.replace(/\b(const|let)\b/g, 'var'); } } return { code: transformed }; } }); // HMR transformer this.addTransformer({ name: 'hmr', phase: 'post', targets: ['client'], transform: (code, context) => { if (!context.options.hmr || context.options.target === 'production') { return { code }; } const hmrCode = ` // Hot Module Replacement if (module.hot) { module.hot.accept(); module.hot.dispose(() => { // Cleanup code }); } `; return { code: code + '\n' + hmrCode }; } }); } registerCustomTransformers() { // Register any custom transformers from options for (const transformer of this.options.transformers) { this.addTransformer(transformer); } } extractMetadata(ast) { const component = ast.component; return { name: component.name, hasClientCode: !!component.clientBlock, hasServerCode: !!component.serverBlock, hasStyles: false, // Would check for style blocks dependencies: ast.dependencies, exports: ast.exports, props: component.props.map(p => p.name), state: component.clientBlock?.reactiveVariables.map(v => v.name) || [], functions: [ ...(component.clientBlock?.functions.map(f => f.name) || []), ...(component.serverBlock?.functions.map(f => f.name) || []) ], lifecycle: component.clientBlock?.lifecycle.map(l => l.hookType.toString()) || [] }; } createTransformContext(ast, metadata) { return { ast, options: this.options, target: 'client', metadata, sourceMapBuilder: this.options.sourceMaps ? new SourceMapBuilder() : undefined }; } createCodeGenContext() { return { indent: 0, lines: [], sourceMap: this.options.sourceMaps ? new SourceMapBuilder() : undefined, scopes: new Map(), scopeDepth: 0, imports: new Map(), exports: new Set() }; } generateClientImports(ast, context) { // Generate import statements if (this.options.format === 'esm') { context.lines.push("import { createSignal, createEffect } from '@ordojs/runtime';"); } else { context.lines.push("const { createSignal, createEffect } = require('@ordojs/runtime');"); } context.lines.push(''); } generateClientComponent(component, context) { const componentName = component.name; // Function declaration context.lines.push(`function ${componentName}(props = {}) {`); context.indent++; // Generate reactive variables if (component.clientBlock?.reactiveVariables) { for (const variable of component.clientBlock.reactiveVariables) { this.generateReactiveVariable(variable, context); } context.lines.push(''); } // Generate functions if (component.clientBlock?.functions) { for (const func of component.clientBlock.functions) { this.generateFunction(func, context); } context.lines.push(''); } // Generate lifecycle hooks if (component.clientBlock?.lifecycle) { for (const hook of component.clientBlock.lifecycle) { this.generateLifecycleHook(hook, context); } context.lines.push(''); } // Generate render function this.generateRenderFunction(component.markupBlock, context); // Return component API context.lines.push(' return {'); context.lines.push(' mount,'); context.lines.push(' unmount,'); context.lines.push(' render,'); context.lines.push(' getState: () => ({ ...state }),'); context.lines.push(' setState: (key, value) => { state[key] = value; }'); context.lines.push(' };'); context.indent--; context.lines.push('}'); context.lines.push(''); } generateReactiveVariable(variable, context) { const indent = ' '.repeat(context.indent); const initialValue = this.generateExpression(variable.initialValue); if (variable.isConst) { context.lines.push(`${indent}const ${variable.name} = ${initialValue};`); } else { context.lines.push(`${indent}const [${variable.name}, set${this.capitalize(variable.name)}] = createSignal(${initialValue});`); } } generateFunction(func, context) { const indent = ' '.repeat(context.indent); const params = func.parameters.map((p) => p.name).join(', '); context.lines.push(`${indent}function ${func.name}(${params}) {`); for (const statement of func.body) { this.generateStatement(statement, context); } context.lines.push(`${indent}}`); } generateLifecycleHook(hook, context) { const indent = ' '.repeat(context.indent); context.lines.push(`${indent}createEffect(() => {`); for (const statement of hook.handler) { this.generateStatement(statement, context); } context.lines.push(`${indent}});`); } generateRenderFunction(markupBlock, context) { const indent = ' '.repeat(context.indent); context.lines.push(`${indent}function render() {`); context.lines.push(`${indent} const element = document.createElement('div');`); context.lines.push(`${indent} element.className = 'ordojs-component';`); // Generate markup rendering logic for (const element of markupBlock.elements) { this.generateHTMLElement(element, context); } context.lines.push(`${indent} return element;`); context.lines.push(`${indent}}`); context.lines.push(''); context.lines.push(`${indent}function mount(target) {`); context.lines.push(`${indent} const element = render();`); context.lines.push(`${indent} target.appendChild(element);`); context.lines.push(`${indent}}`); context.lines.push(''); context.lines.push(`${indent}function unmount() {`); context.lines.push(`${indent} // Cleanup logic`); context.lines.push(`${indent}}`); } generateHTMLElement(element, context) { const indent = ' '.repeat(context.indent + 1); context.lines.push(`${indent}// HTML element: ${element.tagName || 'div'}`); } generateStatement(statement, context) { const indent = ' '.repeat(context.indent + 1); if (statement.expression) { const expr = this.generateExpression(statement.expression); context.lines.push(`${indent}${expr};`); } } generateExpression(expression) { switch (expression.expressionType) { case 'LITERAL': return JSON.stringify(expression.value); case 'IDENTIFIER': return expression.identifier || 'undefined'; case 'BINARY': const left = this.generateExpression(expression.left); const right = this.generateExpression(expression.right); return `${left} ${expression.operator} ${right}`; case 'CALL': const callee = this.generateExpression(expression.callee); const args = expression.arguments?.map(arg => this.generateExpression(arg)).join(', ') || ''; return `${callee}(${args})`; default: return 'undefined'; } } generateServerImports(ast, context) { context.lines.push("const { createServerFunction } = require('@ordojs/runtime');"); context.lines.push(''); } generateServerFunctions(serverBlock, context) { for (const func of serverBlock.functions) { this.generateServerFunction(func, context); } } generateServerFunction(func, context) { const params = func.parameters.map((p) => p.name).join(', '); const visibility = func.isPublic ? 'public' : 'private'; context.lines.push(`// ${visibility} server function`); context.lines.push(`async function ${func.name}(${params}) {`); for (const statement of func.body) { this.generateStatement(statement, context); } context.lines.push('}'); context.lines.push(''); if (func.isPublic) { context.exports.add(func.name); } } generateServerExports(ast, context) { if (context.exports.size > 0) { const exports = Array.from(context.exports).join(', '); if (this.options.format === 'esm') { context.lines.push(`export { ${exports} };`); } else { context.lines.push(`module.exports = { ${exports} };`); } } } generateClientExports(ast, context) { const componentName = ast.component.name; if (this.options.format === 'esm') { context.lines.push(`export default ${componentName};`); context.lines.push(`export { ${componentName} };`); } else if (this.options.format === 'cjs') { context.lines.push(`module.exports = ${componentName};`); context.lines.push(`module.exports.${componentName} = ${componentName};`); } else if (this.options.format === 'umd') { context.lines.push(` (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.OrdoJS = {})); }(this, (function (exports) { exports.${componentName} = ${componentName}; })));`); } else if (this.options.format === 'iife') { context.lines.push(`window.${componentName} = ${componentName};`); } } generateHTMLStructure(markupBlock, context, props) { context.lines.push('<div class="ordojs-component">'); // Generate HTML from markup block for (const element of markupBlock.elements) { context.lines.push(` <!-- ${element.tagName || 'element'} -->`); context.lines.push(' <div>Generated HTML content</div>'); } context.lines.push('</div>'); } applyTransformers(code, context) { const phases = ['pre', 'post', 'optimize']; for (const phase of phases) { const transformers = this.transformers.get(phase) || []; for (const transformer of transformers) { if (transformer.targets.includes(context.target)) { const result = transformer.transform(code, context); code = result.code; } } } return code; } createEmptySourceMap() { return { version: 3, sources: [], names: [], mappings: '', sourcesContent: [] }; } capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } } /** * Default code transformers */ export const defaultTransformers = [ { name: 'import-optimizer', phase: 'optimize', targets: ['client', 'server'], transform: (code, context) => { // Remove unused imports const lines = code.split('\n'); const optimizedLines = lines.filter(line => { if (line.trim().startsWith('import') || line.trim().startsWith('const')) { // Simple check - in production, use proper AST analysis return true; } return true; }); return { code: optimizedLines.join('\n') }; } }, { name: 'dead-code-eliminator', phase: 'optimize', targets: ['client', 'server'], transform: (code, context) => { if (!context.options.treeShaking) { return { code }; } // Simple dead code elimination - in production, use proper analysis const lines = code.split('\n'); const optimizedLines = lines.filter(line => { const trimmed = line.trim(); return trimmed !== '' && !trimmed.startsWith('//'); }); return { code: optimizedLines.join('\n') }; } } ]; //# sourceMappingURL=code-generator-refactored.js.map