Some checks failed
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Performance Benchmarks (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (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 / Cleanup (push) Has been cancelled
200 lines
6.8 KiB
JavaScript
Executable File
200 lines
6.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* CSS Bundle Builder
|
|
*
|
|
* Combines and minifies CSS files into optimized bundles:
|
|
* - site.min.css: Essential site styles (design system, theme, layout)
|
|
* - app.min.css: Main application styles (UnoCSS, components)
|
|
* - enhancements.min.css: Progressive enhancement styles (highlighting, etc.)
|
|
*
|
|
* Usage:
|
|
* node scripts/build-css-bundles.js [theme]
|
|
*
|
|
* Examples:
|
|
* node scripts/build-css-bundles.js # default theme
|
|
* node scripts/build-css-bundles.js purple # purple theme
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Simple CSS minifier
|
|
function minifyCss(css) {
|
|
return css
|
|
// Remove comments
|
|
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
// Remove extra whitespace
|
|
.replace(/\s+/g, ' ')
|
|
// Remove whitespace around specific characters
|
|
.replace(/\s*([{}:;,>+~])\s*/g, '$1')
|
|
// Remove trailing semicolons before }
|
|
.replace(/;}/g, '}')
|
|
// Remove leading/trailing whitespace
|
|
.trim();
|
|
}
|
|
|
|
// Extract critical above-the-fold styles from website.css
|
|
function extractSiteStyles(websiteCss) {
|
|
// Extract CSS reset, root variables, and essential layout styles
|
|
// This is a simplified extraction - in a real scenario you might use a more sophisticated approach
|
|
const sitePatterns = [
|
|
// CSS reset and variables
|
|
/\/\* layer: preflights \*\/[\s\S]*?(?=\/\* layer:|$)/g,
|
|
// Root variables
|
|
/:root\s*\{[^}]*\}/g,
|
|
// Essential layout classes (simplified extraction)
|
|
/\.(?:min-h-screen|max-w-|mx-auto|py-|flex|flex-col|flex-grow)[^{]*\{[^}]*\}/g
|
|
];
|
|
|
|
let extracted = '';
|
|
sitePatterns.forEach(pattern => {
|
|
const matches = websiteCss.match(pattern);
|
|
if (matches) {
|
|
extracted += matches.join('\n') + '\n';
|
|
}
|
|
});
|
|
|
|
return extracted;
|
|
}
|
|
|
|
async function buildCssBundles() {
|
|
try {
|
|
const assetsStylesDir = path.join(__dirname, '../assets/styles');
|
|
const publicStylesDir = path.join(__dirname, '../public/styles');
|
|
|
|
// Get theme from command line argument or default
|
|
const theme = process.argv[2] || 'default';
|
|
const themeFile = `theme-${theme}.css`;
|
|
|
|
console.log(`🎨 Building CSS bundles with theme: ${theme}`);
|
|
|
|
// Read source files from assets
|
|
const files = {
|
|
designSystem: path.join(assetsStylesDir, 'design-system.css'),
|
|
theme: path.join(assetsStylesDir, themeFile),
|
|
website: path.join(assetsStylesDir, 'website.css'),
|
|
contactOverrides: path.join(assetsStylesDir, 'overrides/contact-tailwind-overrides.css'),
|
|
custom: path.join(assetsStylesDir, 'custom.css'),
|
|
highlight: path.join(publicStylesDir, 'highlight-github-dark.min.css')
|
|
};
|
|
|
|
// Check if all required files exist
|
|
const missingFiles = [];
|
|
for (const [name, filePath] of Object.entries(files)) {
|
|
if (!fs.existsSync(filePath)) {
|
|
missingFiles.push(`${name}: ${filePath}`);
|
|
}
|
|
}
|
|
|
|
if (missingFiles.length > 0) {
|
|
console.log('⚠️ Some files are missing but continuing with available files:');
|
|
missingFiles.forEach(file => console.log(` ${file}`));
|
|
console.log('');
|
|
}
|
|
|
|
// Read file contents
|
|
const contents = {};
|
|
for (const [name, filePath] of Object.entries(files)) {
|
|
if (fs.existsSync(filePath)) {
|
|
contents[name] = fs.readFileSync(filePath, 'utf8');
|
|
} else {
|
|
contents[name] = '';
|
|
}
|
|
}
|
|
|
|
console.log('📂 Source files read:');
|
|
for (const [name, content] of Object.entries(contents)) {
|
|
const size = Math.round(content.length / 1024);
|
|
console.log(` ${name}: ${size}KB`);
|
|
}
|
|
console.log('');
|
|
|
|
// 1. Build site.min.css (essential styles)
|
|
const siteExtracted = extractSiteStyles(contents.website);
|
|
const siteBundle = [
|
|
'/* Site Bundle - Essential Styles */',
|
|
`/* Generated on ${new Date().toISOString()} */`,
|
|
'/* Theme: ' + theme + ' */',
|
|
'',
|
|
'/* Design System Variables */',
|
|
contents.designSystem,
|
|
'',
|
|
'/* Theme Variables */',
|
|
contents.theme,
|
|
'',
|
|
'/* Essential Layout Styles */',
|
|
siteExtracted
|
|
].join('\n');
|
|
|
|
const siteMinified = minifyCss(siteBundle);
|
|
const sitePath = path.join(assetsStylesDir, 'site.min.css');
|
|
fs.writeFileSync(sitePath, siteMinified);
|
|
|
|
// 2. Build app.min.css (main application styles)
|
|
const appBundle = [
|
|
'/* App Bundle - Main Application Styles */',
|
|
`/* Generated on ${new Date().toISOString()} */`,
|
|
'',
|
|
'/* Main Website Styles (minus site essentials) */',
|
|
contents.website.replace(siteExtracted, ''), // Remove extracted site styles
|
|
'',
|
|
'/* Custom Styles */',
|
|
contents.custom,
|
|
'',
|
|
'/* Contact Page Overrides */',
|
|
contents.contactOverrides
|
|
].join('\n');
|
|
|
|
const appMinified = minifyCss(appBundle);
|
|
const appPath = path.join(assetsStylesDir, 'app.min.css');
|
|
fs.writeFileSync(appPath, appMinified);
|
|
|
|
// 3. Build enhancements.min.css (progressive features)
|
|
const enhancementsBundle = [
|
|
'/* Enhancements Bundle - Progressive Features */',
|
|
`/* Generated on ${new Date().toISOString()} */`,
|
|
'',
|
|
'/* Code Highlighting Styles */',
|
|
contents.highlight
|
|
].join('\n');
|
|
|
|
const enhancementsMinified = minifyCss(enhancementsBundle);
|
|
const enhancementsPath = path.join(assetsStylesDir, 'enhancements.min.css');
|
|
fs.writeFileSync(enhancementsPath, enhancementsMinified);
|
|
|
|
// Get final file sizes
|
|
const finalSizes = {
|
|
site: Math.round(fs.statSync(sitePath).size / 1024),
|
|
app: Math.round(fs.statSync(appPath).size / 1024),
|
|
enhancements: Math.round(fs.statSync(enhancementsPath).size / 1024)
|
|
};
|
|
|
|
const totalSize = finalSizes.site + finalSizes.app + finalSizes.enhancements;
|
|
const originalTotal = Math.round(Object.values(contents).reduce((sum, content) => sum + content.length, 0) / 1024);
|
|
const savings = Math.round(((originalTotal - totalSize) / originalTotal) * 100);
|
|
|
|
console.log('✅ CSS bundles created successfully!');
|
|
console.log('');
|
|
console.log('📊 Bundle Sizes:');
|
|
console.log(` 📁 site.min.css: ${finalSizes.site}KB (design system + theme + essential layout)`);
|
|
console.log(` 📁 app.min.css: ${finalSizes.app}KB (main application styles)`);
|
|
console.log(` 📁 enhancements.min.css: ${finalSizes.enhancements}KB (code highlighting + progressive features)`);
|
|
console.log('');
|
|
console.log(`📈 Total: ${totalSize}KB (${savings}% size reduction from ${originalTotal}KB)`);
|
|
console.log('');
|
|
console.log('🚀 CSS bundles generated in assets/styles/');
|
|
console.log(' 📁 assets/styles/site.min.css');
|
|
console.log(' 📁 assets/styles/app.min.css');
|
|
console.log(' 📁 assets/styles/enhancements.min.css');
|
|
console.log('');
|
|
console.log('💡 Run copy script to deploy to public/styles/');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error building CSS bundles:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
buildCssBundles();
|