/** * This script optimizes images in the public directory, * converting them to WebP format and resizing as needed. */ const fs = require('fs'); const path = require('path'); const { promisify } = require('util'); const glob = promisify(require('glob').glob); const sharp = require('sharp'); const { exec } = require('child_process'); // Configuration const PUBLIC_DIR = path.join(__dirname, '../public'); const SIZES = [400, 800, 1200, 1600]; // Responsive sizes const QUALITY = 80; // WebP quality async function optimizeImages() { try { console.log('🔍 Finding images to optimize...'); // Find all image files const imageFiles = await glob('**/*.{jpg,jpeg,png,gif}', { cwd: PUBLIC_DIR, ignore: [ 'optimized-images/**', // Skip already optimized images 'scripts/**', '*.webp', // Skip webp files 'node_modules/**', '.next/**' ], nodir: true }); console.log(`📷 Found ${imageFiles.length} images to process`); if (imageFiles.length === 0) { console.log('✅ No images to optimize!'); return; } // Create optimized-images directory if it doesn't exist const optimizedDir = path.join(PUBLIC_DIR, 'optimized-images'); if (!fs.existsSync(optimizedDir)) { fs.mkdirSync(optimizedDir, { recursive: true }); } // Process each image for (const file of imageFiles) { const inputPath = path.join(PUBLIC_DIR, file); const fileInfo = path.parse(file); // Create optimized versions const webpPath = path.join(PUBLIC_DIR, fileInfo.dir, `${fileInfo.name}.webp`); console.log(`🔄 Processing: ${file} -> WebP`); // Get image dimensions const metadata = await sharp(inputPath).metadata(); const { width } = metadata; // Convert to WebP with maximum quality await sharp(inputPath) .webp({ quality: QUALITY, effort: 6 }) .toFile(webpPath); // Create responsive sizes if original is large enough if (width > 800) { for (const size of SIZES) { if (width > size) { const resizedWebpPath = path.join( PUBLIC_DIR, fileInfo.dir, `${fileInfo.name}-${size}.webp` ); await sharp(inputPath) .resize(size) .webp({ quality: QUALITY, effort: 6 }) .toFile(resizedWebpPath); console.log(`✅ Created: ${fileInfo.name}-${size}.webp`); } } } // Save a backup to optimized-images const backupPath = path.join(optimizedDir, path.basename(file)); fs.copyFileSync(inputPath, backupPath); console.log(`✅ Optimized: ${file}`); } console.log('📊 Optimization complete!'); // Print size comparison const originalSize = await calculateDirectorySize(PUBLIC_DIR, ['**/*.{jpg,jpeg,png,gif}']); const webpSize = await calculateDirectorySize(PUBLIC_DIR, ['**/*.webp']); const savings = originalSize - webpSize; console.log(` 📊 Optimization Results: ----------------------- Original images: ${formatBytes(originalSize)} WebP images: ${formatBytes(webpSize)} Savings: ${formatBytes(savings)} (${Math.round((savings / originalSize) * 100)}%) `); } catch (error) { console.error('❌ Error optimizing images:', error); process.exit(1); } } async function calculateDirectorySize(dir, patterns) { let totalSize = 0; for (const pattern of patterns) { const files = await glob(pattern, { cwd: dir, nodir: true }); for (const file of files) { const stats = fs.statSync(path.join(dir, file)); totalSize += stats.size; } } return totalSize; } function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } // Run the script optimizeImages();