import { PurgeCSS } from 'purgecss'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Create reports directory if it doesn't exist const reportsDir = './reports'; if (!fs.existsSync(reportsDir)) { fs.mkdirSync(reportsDir); } console.log('š Starting CSS Analysis...\n'); // 1. Run PurgeCSS to find unused CSS console.log('š Running PurgeCSS analysis...'); async function runPurgeCSS() { const purgeCSSResults = await new PurgeCSS().purge({ content: [ './src/**/*.ts', './wwwroot/**/*.html' ], css: [ './wwwroot/css/*.css' ], rejected: true, rejectedCss: true, safelist: { standard: [ /^swp-/, /^cols-[1-4]$/, /^stack-level-[0-4]$/, 'dragging', 'hover', 'highlight', 'transitioning', 'filter-active', 'swp--resizing', 'max-event-indicator', 'max-event-overflow-hide', 'max-event-overflow-show', 'allday-chevron', 'collapsed', 'expanded', /^month-/, /^week-/, 'today', 'weekend', 'other-month', 'hidden', 'invisible', 'transparent', 'calendar-wrapper' ] } }); // Calculate statistics let totalOriginalSize = 0; let totalPurgedSize = 0; let totalRejected = 0; const rejectedByFile = {}; purgeCSSResults.forEach(result => { const fileName = path.basename(result.file); const originalSize = result.css.length + (result.rejected ? result.rejected.join('').length : 0); const purgedSize = result.css.length; const rejectedSize = result.rejected ? result.rejected.length : 0; totalOriginalSize += originalSize; totalPurgedSize += purgedSize; totalRejected += rejectedSize; rejectedByFile[fileName] = { originalSize, purgedSize, rejectedCount: rejectedSize, rejected: result.rejected || [] }; }); const report = { summary: { totalFiles: purgeCSSResults.length, totalOriginalSize, totalPurgedSize, totalRejected, percentageRemoved: ((totalRejected / (totalOriginalSize || 1)) * 100).toFixed(2) + '%', potentialSavings: totalOriginalSize - totalPurgedSize }, fileDetails: rejectedByFile }; fs.writeFileSync( path.join(reportsDir, 'purgecss-report.json'), JSON.stringify(report, null, 2) ); console.log('ā PurgeCSS analysis complete'); console.log(` - Total CSS rules analyzed: ${totalOriginalSize}`); console.log(` - Unused CSS rules found: ${totalRejected}`); console.log(` - Potential removal: ${report.summary.percentageRemoved}`); return report; } // 2. Analyze CSS with basic stats console.log('\nš Running CSS Stats analysis...'); function runCSSStats() { const cssFiles = [ './wwwroot/css/calendar-base-css.css', './wwwroot/css/calendar-components-css.css', './wwwroot/css/calendar-events-css.css', './wwwroot/css/calendar-layout-css.css', './wwwroot/css/calendar-month-css.css', './wwwroot/css/calendar-popup-css.css', './wwwroot/css/calendar-sliding-animation.css' ]; const stats = {}; cssFiles.forEach(file => { if (fs.existsSync(file)) { const fileName = path.basename(file); const content = fs.readFileSync(file, 'utf8'); // Basic statistics const lines = content.split('\n').length; const size = Buffer.byteLength(content, 'utf8'); const rules = (content.match(/\{[^}]*\}/g) || []).length; const selectors = (content.match(/[^{]+(?=\{)/g) || []).length; const properties = (content.match(/[^:]+:[^;]+;/g) || []).length; const colors = [...new Set(content.match(/#[0-9a-fA-F]{3,6}|rgba?\([^)]+\)|hsla?\([^)]+\)/g) || [])]; const mediaQueries = (content.match(/@media[^{]+/g) || []).length; stats[fileName] = { lines, size: `${(size / 1024).toFixed(2)} KB`, sizeBytes: size, rules, selectors, properties, uniqueColors: colors.length, colors: colors.slice(0, 10), // First 10 colors mediaQueries }; } }); fs.writeFileSync( path.join(reportsDir, 'css-stats.json'), JSON.stringify(stats, null, 2) ); console.log('ā CSS Stats analysis complete'); console.log(` - Files analyzed: ${Object.keys(stats).length}`); return stats; } // 3. Generate HTML report function generateHTMLReport(purgeReport, statsReport) { const totalSize = Object.values(statsReport).reduce((sum, stat) => sum + stat.sizeBytes, 0); const totalSizeKB = (totalSize / 1024).toFixed(2); const html = `
Calendar Plantempus - Production CSS Analysis
| File | Size | Lines | Rules | Selectors | Properties | Colors |
|---|---|---|---|---|---|---|
| ${file} | ${stats.size} | ${stats.lines} | ${stats.rules} | ${stats.selectors} | ${stats.properties} | ${stats.uniqueColors} |
${details.rejectedCount} unused rules Original: ${details.originalSize} | After purge: ${details.purgedSize}
${details.rejectedCount > 0 ? `ā No unused CSS found!
'}