#!/usr/bin/env node /** * Image Optimization Script * * Provides recommendations for image optimization * Note: Requires external tools for actual optimization */ const fs = require('fs'); const path = require('path'); function getFileSize(filePath) { try { const stats = fs.statSync(filePath); return stats.size; } catch { return 0; } } function formatSize(bytes) { return (bytes / 1024).toFixed(2) + ' KB'; } function analyzeImages(dir, extensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg']) { let images = []; try { const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { images = images.concat(analyzeImages(fullPath, extensions)); } else if (stat.isFile()) { const ext = path.extname(item).toLowerCase(); if (extensions.includes(ext)) { images.push({ path: fullPath.replace(process.cwd(), ''), name: item, size: stat.size, ext: ext }); } } } } catch (error) { console.error(`Error analyzing directory ${dir}:`, error.message); } return images; } console.log('šŸ–¼ļø Image Optimization Analysis\n'); console.log('='.repeat(60)); const publicDir = path.join(__dirname, '..', 'public'); const iconsDir = path.join(publicDir, 'icons'); // Analyze images const images = analyzeImages(publicDir); if (images.length === 0) { console.log('\nNo images found to optimize.'); process.exit(0); } console.log(`\nFound ${images.length} images\n`); // Group by type const byType = {}; images.forEach(img => { if (!byType[img.ext]) { byType[img.ext] = []; } byType[img.ext].push(img); }); // Report by type let totalSize = 0; let potentialSavings = 0; for (const [ext, imgs] of Object.entries(byType)) { console.log(`\n${ext.toUpperCase()} Images:`); console.log('-'.repeat(60)); const typeSize = imgs.reduce((sum, img) => sum + img.size, 0); totalSize += typeSize; // Calculate potential savings based on type let savings = 0; let recommendation = ''; switch (ext) { case '.png': savings = typeSize * 0.4; // ~40% savings with compression recommendation = 'Use pngquant or TinyPNG. Consider WebP format.'; break; case '.jpg': case '.jpeg': savings = typeSize * 0.3; // ~30% savings recommendation = 'Use mozjpeg or jpegoptim. Consider WebP format.'; break; case '.svg': savings = typeSize * 0.2; // ~20% savings recommendation = 'Use SVGO to optimize.'; break; case '.gif': savings = typeSize * 0.5; // ~50% savings recommendation = 'Convert to WebP or use gifsicle.'; break; default: savings = 0; recommendation = 'Already optimized format.'; } potentialSavings += savings; // Show largest images imgs.sort((a, b) => b.size - a.size); imgs.slice(0, 5).forEach(img => { console.log(` ${img.name.padEnd(30)} ${formatSize(img.size).padStart(12)}`); }); if (imgs.length > 5) { console.log(` ... and ${imgs.length - 5} more`); } console.log(` Total: ${formatSize(typeSize)}`); console.log(` šŸ’” ${recommendation}`); console.log(` Potential savings: ${formatSize(savings)}`); } console.log('\n\nšŸ“Š Summary'); console.log('='.repeat(60)); console.log(` Total images: ${images.length}`); console.log(` Total size: ${formatSize(totalSize)}`); console.log(` Potential savings: ${formatSize(potentialSavings)} (${((potentialSavings/totalSize)*100).toFixed(1)}%)`); console.log('\n\nšŸ’” Optimization Recommendations'); console.log('='.repeat(60)); console.log('\n1. Install optimization tools:'); console.log(' npm install -g svgo'); console.log(' # For PNG: brew install pngquant (macOS) or apt-get install pngquant (Linux)'); console.log(' # For JPEG: brew install mozjpeg (macOS) or apt-get install mozjpeg (Linux)'); console.log('\n2. Optimize SVG icons:'); console.log(' svgo -f public/icons -o public/icons-optimized'); console.log('\n3. Convert to WebP (best compression):'); console.log(' # For each image:'); console.log(' # cwebp input.png -q 80 -o output.webp'); console.log('\n4. Use modern image formats:'); console.log(' • WebP: Best compression, wide support'); console.log(' • AVIF: Better than WebP, limited support'); console.log(' • Provide fallbacks for older browsers'); console.log('\n5. Implement responsive images:'); console.log(' '); console.log(' '); console.log(' '); console.log(' ...'); console.log(' '); console.log('\n6. Use lazy loading:'); console.log(' ...'); console.log('\n\nšŸŽÆ Priority Actions:'); console.log('='.repeat(60)); // Find large images that need optimization const largeImages = images.filter(img => img.size > 100 * 1024).sort((a, b) => b.size - a.size); if (largeImages.length > 0) { console.log('\nāš ļø Large images that need optimization (>100KB):'); largeImages.slice(0, 10).forEach((img, i) => { console.log(` ${i + 1}. ${img.name} (${formatSize(img.size)})`); }); } else { console.log('\nāœ… No large images found! All images are optimized.'); } // Check for SVG optimization opportunities const svgImages = images.filter(img => img.ext === '.svg'); if (svgImages.length > 0) { console.log(`\nšŸ“ ${svgImages.length} SVG files can be optimized with SVGO`); } console.log('\n' + '='.repeat(60)); console.log('āœ… Analysis complete!\n');