#!/usr/bin/env node /** * Theme Build Script * * This script generates CSS variables from TOML theme configurations. * It can be run manually or integrated into the build pipeline. */ const fs = require('fs'); const path = require('path'); // Simple TOML parser for basic key-value pairs function parseSimpleToml(content) { const result = {}; let currentSection = null; const lines = content.split('\n'); for (const line of lines) { const trimmed = line.trim(); // Skip empty lines and comments if (!trimmed || trimmed.startsWith('#')) continue; // Section headers [section] if (trimmed.startsWith('[') && trimmed.endsWith(']')) { currentSection = trimmed.slice(1, -1); if (!result[currentSection]) { result[currentSection] = {}; } continue; } // Key-value pairs if (trimmed.includes('=')) { const [key, ...valueParts] = trimmed.split('='); let value = valueParts.join('=').trim(); // Handle quoted values vs unquoted values if (value.startsWith('"') && value.includes('"', 1)) { // Extract value between first and last quotes, ignoring comments after closing quote const firstQuote = value.indexOf('"'); const lastQuote = value.indexOf('"', firstQuote + 1); if (lastQuote !== -1) { value = value.substring(firstQuote + 1, lastQuote); } } else { // For unquoted values, remove inline comments if (value.includes('#')) { value = value.split('#')[0].trim(); } } if (currentSection) { result[currentSection][key.trim()] = value; } else { result[key.trim()] = value; } } } return result; } // Generate CSS variables from theme config function generateCssVariables(themeConfig) { let css = `:root {\n`; // Colors if (themeConfig.colors) { css += ` /* Colors */\n`; for (const [key, value] of Object.entries(themeConfig.colors)) { const cssVar = key.replace(/_/g, '-'); css += ` --color-${cssVar}: ${value};\n`; } css += `\n`; } // Typography if (themeConfig.typography) { css += ` /* Typography */\n`; for (const [key, value] of Object.entries(themeConfig.typography)) { const cssVar = key.replace(/_/g, '-'); css += ` --${cssVar}: ${value};\n`; } css += `\n`; } // Spacing if (themeConfig.spacing) { css += ` /* Spacing */\n`; for (const [key, value] of Object.entries(themeConfig.spacing)) { const cssVar = key.replace(/_/g, '-'); css += ` --space-${cssVar}: ${value};\n`; } css += `\n`; } // Border Radius if (themeConfig.radius) { css += ` /* Border Radius */\n`; for (const [key, value] of Object.entries(themeConfig.radius)) { const cssVar = key.replace(/_/g, '-'); css += ` --radius-${cssVar}: ${value};\n`; } css += `\n`; } // Component specific if (themeConfig.components) { css += ` /* Component Tokens */\n`; if (themeConfig.components.button) { css += ` --btn-border-radius: ${themeConfig.components.button.border_radius};\n`; } if (themeConfig.components.card) { css += ` --card-border-radius: ${themeConfig.components.card.border_radius};\n`; } if (themeConfig.components.input) { css += ` --input-border-radius: ${themeConfig.components.input.border_radius};\n`; } css += `\n`; } // Animations if (themeConfig.animations) { css += ` /* Animations */\n`; for (const [key, value] of Object.entries(themeConfig.animations)) { const cssVar = key.replace(/_/g, '-'); css += ` --${cssVar}: ${value};\n`; } } css += `}\n`; return css; } // Main function function buildTheme(themeName = 'default') { try { console.log(`Building theme: ${themeName}`); // Read theme TOML file from new assets location const themePath = path.join(__dirname, '..', 'assets', 'styles', 'themes', `${themeName}.toml`); if (!fs.existsSync(themePath)) { console.error(`Theme file not found: ${themePath}`); process.exit(1); } const themeContent = fs.readFileSync(themePath, 'utf8'); const themeConfig = parseSimpleToml(themeContent); // Generate CSS const css = generateCssVariables(themeConfig); // Write CSS file const outputPath = path.join(__dirname, '..', 'public', 'styles', `theme-${themeName}.css`); // Ensure directory exists const outputDir = path.dirname(outputPath); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // Write file with header const header = `/* Theme Variables - ${themeName} */\n/* Generated from ${path.basename(themePath)} */\n/* Do not edit manually */\n\n`; fs.writeFileSync(outputPath, header + css); console.log(`✅ Theme built successfully: ${outputPath}`); // Also update the main theme variables file if this is the default theme if (themeName === 'default') { const mainThemePath = path.join(__dirname, '..', 'public', 'styles', 'theme-variables.css'); fs.writeFileSync(mainThemePath, header + css); console.log(`✅ Updated main theme variables: ${mainThemePath}`); } } catch (error) { console.error('Error building theme:', error.message); process.exit(1); } } // CLI handling if (require.main === module) { const themeName = process.argv[2] || 'default'; buildTheme(themeName); } module.exports = { buildTheme, generateCssVariables, parseSimpleToml };