Some checks failed
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Build Docker Image (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
CI/CD Pipeline / Cleanup (push) Has been cancelled
192 lines
5.5 KiB
JavaScript
Executable File
192 lines
5.5 KiB
JavaScript
Executable File
#!/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 }; |