File size: 6,153 Bytes
20c5151 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
#!/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(' <picture>');
console.log(' <source srcset="image.webp" type="image/webp">');
console.log(' <source srcset="image.jpg" type="image/jpeg">');
console.log(' <img src="image.jpg" alt="...">');
console.log(' </picture>');
console.log('\n6. Use lazy loading:');
console.log(' <img src="..." loading="lazy" alt="...">');
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');
|