mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-12-06 17:27:57 +00:00
167 lines
5.3 KiB
JavaScript
167 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* CSS Analysis Script
|
|
*
|
|
* Analyzes the compiled CSS bundle to identify:
|
|
* - Total size and line count
|
|
* - Bootstrap vs custom CSS breakdown
|
|
* - Most common selectors and patterns
|
|
* - Potential optimization opportunities
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const CSS_FILE = path.join(__dirname, '../static/css/devportal2024-v1.css');
|
|
|
|
function analyzeCSS() {
|
|
console.log('🔍 Analyzing CSS Bundle...\n');
|
|
|
|
if (!fs.existsSync(CSS_FILE)) {
|
|
console.error(`❌ CSS file not found: ${CSS_FILE}`);
|
|
console.log('💡 Run "npm run build-css" first to generate the CSS bundle.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const css = fs.readFileSync(CSS_FILE, 'utf-8');
|
|
const stats = fs.statSync(CSS_FILE);
|
|
|
|
// Basic stats
|
|
const lines = css.split('\n').length;
|
|
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
const selectors = css.match(/[^{}]+(?=\{)/g) || [];
|
|
const uniqueSelectors = new Set(selectors.map(s => s.trim())).size;
|
|
|
|
console.log('📊 Bundle Statistics:');
|
|
console.log('━'.repeat(50));
|
|
console.log(` Size: ${sizeKB} KB`);
|
|
console.log(` Lines: ${lines.toLocaleString()}`);
|
|
console.log(` Total Selectors: ${selectors.length.toLocaleString()}`);
|
|
console.log(` Unique Selectors: ${uniqueSelectors.toLocaleString()}`);
|
|
console.log('');
|
|
|
|
// Bootstrap component detection
|
|
const bootstrapPatterns = {
|
|
'Grid System': /\.(container|row|col-)/g,
|
|
'Buttons': /\.btn-/g,
|
|
'Forms': /\.(form-control|form-select|form-check)/g,
|
|
'Cards': /\.card-/g,
|
|
'Navbar': /\.navbar-/g,
|
|
'Dropdown': /\.dropdown-/g,
|
|
'Modal': /\.modal-/g,
|
|
'Alert': /\.alert-/g,
|
|
'Badge': /\.badge-/g,
|
|
'Breadcrumb': /\.breadcrumb/g,
|
|
'Pagination': /\.page-/g,
|
|
'Accordion': /\.accordion/g,
|
|
'Carousel': /\.carousel/g,
|
|
'Tooltip': /\.tooltip/g,
|
|
'Popover': /\.popover/g,
|
|
'Toast': /\.toast/g,
|
|
'Spinner': /\.spinner-/g,
|
|
};
|
|
|
|
console.log('🎨 Bootstrap Component Usage:');
|
|
console.log('━'.repeat(50));
|
|
|
|
const componentUsage = [];
|
|
for (const [component, pattern] of Object.entries(bootstrapPatterns)) {
|
|
const matches = css.match(pattern);
|
|
const count = matches ? matches.length : 0;
|
|
componentUsage.push({ component, count });
|
|
}
|
|
|
|
componentUsage.sort((a, b) => b.count - a.count);
|
|
componentUsage.forEach(({ component, count }) => {
|
|
const bar = '█'.repeat(Math.min(Math.floor(count / 10), 40));
|
|
console.log(` ${component.padEnd(20)} ${count.toString().padStart(4)} ${bar}`);
|
|
});
|
|
console.log('');
|
|
|
|
// Custom classes analysis
|
|
const customPatterns = [
|
|
{ name: 'Dev Tools', pattern: /\.(rpc-tool|websocket|code-tab)/g },
|
|
{ name: 'Navigation', pattern: /\.(top-nav|side-nav|breadcrumb)/g },
|
|
{ name: 'Content', pattern: /\.(content-|landing-|page-)/g },
|
|
{ name: 'Cards', pattern: /\.(card-deck|project-card)/g },
|
|
{ name: 'Video', pattern: /\.video-/g },
|
|
{ name: 'Blog', pattern: /\.blog-/g },
|
|
];
|
|
|
|
console.log('🎯 Custom Component Patterns:');
|
|
console.log('━'.repeat(50));
|
|
customPatterns.forEach(({ name, pattern }) => {
|
|
const matches = css.match(pattern);
|
|
const count = matches ? matches.length : 0;
|
|
if (count > 0) {
|
|
console.log(` ${name.padEnd(20)} ${count.toString().padStart(4)}`);
|
|
}
|
|
});
|
|
console.log('');
|
|
|
|
// Optimization recommendations
|
|
console.log('💡 Optimization Recommendations:');
|
|
console.log('━'.repeat(50));
|
|
|
|
const recommendations = [];
|
|
|
|
// Check for unused Bootstrap components
|
|
const lowUsageComponents = componentUsage.filter(c => c.count < 5 && c.count > 0);
|
|
if (lowUsageComponents.length > 0) {
|
|
recommendations.push({
|
|
priority: 'HIGH',
|
|
message: `${lowUsageComponents.length} Bootstrap components with <5 usages detected`,
|
|
action: 'Consider manually importing only needed Bootstrap modules'
|
|
});
|
|
}
|
|
|
|
const noUsageComponents = componentUsage.filter(c => c.count === 0);
|
|
if (noUsageComponents.length > 0) {
|
|
recommendations.push({
|
|
priority: 'HIGH',
|
|
message: `${noUsageComponents.length} Bootstrap components with 0 usages detected`,
|
|
action: 'Remove unused components from Bootstrap import'
|
|
});
|
|
}
|
|
|
|
if (sizeKB > 200) {
|
|
recommendations.push({
|
|
priority: 'CRITICAL',
|
|
message: 'Bundle size exceeds 200KB',
|
|
action: 'Implement PurgeCSS to remove unused styles'
|
|
});
|
|
}
|
|
|
|
recommendations.push({
|
|
priority: 'MEDIUM',
|
|
message: 'No code splitting detected',
|
|
action: 'Consider splitting vendor CSS from custom styles'
|
|
});
|
|
|
|
recommendations.forEach(({ priority, message, action }) => {
|
|
const emoji = priority === 'CRITICAL' ? '🔴' : priority === 'HIGH' ? '🟡' : '🔵';
|
|
console.log(` ${emoji} [${priority}] ${message}`);
|
|
console.log(` → ${action}`);
|
|
console.log('');
|
|
});
|
|
|
|
// Estimate potential savings
|
|
const estimatedReduction = Math.round(parseFloat(sizeKB) * 0.75);
|
|
const estimatedFinal = Math.round(parseFloat(sizeKB) * 0.25);
|
|
|
|
console.log('📈 Estimated Optimization Potential:');
|
|
console.log('━'.repeat(50));
|
|
console.log(` Current Size: ${sizeKB} KB`);
|
|
console.log(` Potential Savings: ~${estimatedReduction} KB (75%)`);
|
|
console.log(` Expected Size: ~${estimatedFinal} KB`);
|
|
console.log('');
|
|
}
|
|
|
|
analyzeCSS();
|
|
|