UNPKG

@ordojs/core

Version:

Core compiler and runtime for OrdoJS framework

267 lines 9.41 kB
/** * @fileoverview CSS-in-JS Compiler for OrdoJS Framework * Handles compilation of CSS-in-JS expressions to optimized CSS */ import {} from '../types/index.js'; /** * Default CSS-in-JS options */ const DEFAULT_CSS_IN_JS_OPTIONS = { scoped: true, classPrefix: 'ordojs-css', optimize: true, generateSourceMaps: true }; /** * CSS-in-JS Compiler for OrdoJS components */ export class OrdoJSCSSInJSCompiler { options; classCounter = 0; constructor(options = {}) { this.options = { ...DEFAULT_CSS_IN_JS_OPTIONS, ...options }; } /** * Compile CSS-in-JS expression to CSS */ compile(expression, componentName) { const className = this.generateClassName(componentName); const declarations = this.compileExpression(expression); const rule = { type: 'CSSRule', selector: `.${className}`, declarations, range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }; const styleBlock = { type: 'StyleBlock', rules: [rule], scoped: this.options.scoped || false, range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }; const css = this.generateCSS(styleBlock); const dependencies = this.extractDependencies(expression); return { css, className, styleBlock, dependencies }; } /** * Compile CSS-in-JS expression to CSS declarations */ compileExpression(expression) { switch (expression.type) { case 'CSSObject': return this.compileCSSObject(expression); case 'CSSTemplate': return this.compileCSSTemplate(expression); case 'CSSFunction': return this.compileCSSFunction(expression); default: throw new Error(`Unsupported CSS-in-JS expression type: ${expression.type}`); } } /** * Compile CSS object expression */ compileCSSObject(expression) { const declarations = []; for (const property of expression.properties) { if (typeof property.value === 'object' && property.value.type === 'CSSObject') { // Nested object - handle pseudo-classes, media queries, etc. const nestedDeclarations = this.compileCSSObject(property.value); // For now, we'll flatten nested objects - in a real implementation, // we'd need to handle nesting properly declarations.push(...nestedDeclarations); } else { const cssProperty = this.camelCaseToKebabCase(property.key); const cssValue = this.normalizeCSSValue(property.value); declarations.push({ type: 'CSSDeclaration', property: cssProperty, value: cssValue, important: false, range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }); } } return declarations; } /** * Compile CSS template expression */ compileCSSTemplate(expression) { // Parse the template string as CSS let cssText = expression.template; // Replace template expressions with their values // This is a simplified implementation - in reality, we'd need proper template parsing expression.expressions.forEach((expr, index) => { const placeholder = `\${${index}}`; if (cssText.includes(placeholder)) { // For now, we'll assume expressions evaluate to strings // In a real implementation, we'd need to evaluate the expressions cssText = cssText.replace(placeholder, `/* expression ${index} */`); } }); return this.parseCSSDeclarations(cssText); } /** * Compile CSS function expression */ compileCSSFunction(expression) { const declarations = []; for (const arg of expression.arguments) { if (typeof arg === 'string') { declarations.push(...this.parseCSSDeclarations(arg)); } else { declarations.push(...this.compileExpression(arg)); } } return declarations; } /** * Parse CSS declarations from a string */ parseCSSDeclarations(cssText) { const declarations = []; // Split by semicolons and parse each declaration const declarationStrings = cssText.split(';').map(s => s.trim()).filter(Boolean); for (const declStr of declarationStrings) { const colonIndex = declStr.indexOf(':'); if (colonIndex > 0) { const property = declStr.substring(0, colonIndex).trim(); const value = declStr.substring(colonIndex + 1).trim(); if (property && value) { declarations.push({ type: 'CSSDeclaration', property, value, important: value.includes('!important'), range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }); } } } return declarations; } /** * Convert camelCase to kebab-case for CSS properties */ camelCaseToKebabCase(str) { return str.replace(/([A-Z])/g, '-$1').toLowerCase(); } /** * Normalize CSS value (handle numbers, add units, etc.) */ normalizeCSSValue(value) { if (typeof value === 'number') { // Add 'px' unit to numeric values for properties that need units return `${value}px`; } if (typeof value === 'string') { return value; } // For nested objects, we'd need to handle them differently return 'inherit'; } /** * Generate unique class name */ generateClassName(componentName) { const prefix = this.options.classPrefix || 'ordojs-css'; const suffix = componentName ? `${componentName.toLowerCase()}-${this.classCounter++}` : `${this.classCounter++}`; return `${prefix}-${suffix}`; } /** * Extract dependencies from CSS-in-JS expression */ extractDependencies(expression) { const dependencies = []; // This is a simplified implementation // In a real implementation, we'd analyze the expression for variable references return dependencies; } /** * Generate CSS string from StyleBlockNode */ generateCSS(styleBlock) { return styleBlock.rules.map(rule => { const declarations = rule.declarations.map(decl => ` ${decl.property}: ${decl.value};`).join('\n'); return `${rule.selector} {\n${declarations}\n}`; }).join('\n\n'); } /** * Compile multiple CSS-in-JS expressions and merge them */ compileMultiple(expressions, componentName) { const allDeclarations = []; const allDependencies = []; for (const expression of expressions) { const declarations = this.compileExpression(expression); const dependencies = this.extractDependencies(expression); allDeclarations.push(...declarations); allDependencies.push(...dependencies); } const className = this.generateClassName(componentName); const rule = { type: 'CSSRule', selector: `.${className}`, declarations: allDeclarations, range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }; const styleBlock = { type: 'StyleBlock', rules: [rule], scoped: this.options.scoped || false, range: { start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 0, offset: 0 } } }; const css = this.generateCSS(styleBlock); return { css, className, styleBlock, dependencies: [...new Set(allDependencies)] }; } /** * Create CSS-in-JS expression from JavaScript object */ static createCSSObject(obj) { const properties = Object.entries(obj).map(([key, value]) => ({ key, value: typeof value === 'object' && value !== null ? OrdoJSCSSInJSCompiler.createCSSObject(value) : value, computed: false })); return { type: 'CSSObject', properties }; } /** * Create CSS template expression */ static createCSSTemplate(template, expressions = []) { return { type: 'CSSTemplate', template, expressions }; } /** * Create CSS function expression */ static createCSSFunction(functionName, args) { return { type: 'CSSFunction', functionName, arguments: args }; } } //# sourceMappingURL=css-in-js-compiler.js.map