aurelie-portfolio/scripts/optimize-images.mjs
2026-04-05 18:06:36 +02:00

75 lines
2.2 KiB
JavaScript

import sharp from 'sharp';
import { readdir, stat, rename } from 'fs/promises';
import { join, extname, basename, dirname } from 'path';
const IMG_DIR = 'public/images';
const MAX_WIDTH = 1920;
const QUALITY = 80;
const EXTENSIONS = new Set(['.jpg', '.jpeg', '.png']);
async function getImages(dir) {
const entries = await readdir(dir, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...await getImages(fullPath));
} else if (EXTENSIONS.has(extname(entry.name).toLowerCase())) {
files.push(fullPath);
}
}
return files;
}
async function optimizeImage(filePath) {
const ext = extname(filePath).toLowerCase();
const dir = dirname(filePath);
const name = basename(filePath, ext);
const webpPath = join(dir, `${name}.webp`);
const before = (await stat(filePath)).size;
try {
const image = sharp(filePath);
const metadata = await image.metadata();
// Resize if wider than MAX_WIDTH
const resizeOpts = metadata.width > MAX_WIDTH ? { width: MAX_WIDTH } : {};
// Create WebP version
await image
.resize(resizeOpts)
.webp({ quality: QUALITY })
.toFile(webpPath);
// Also optimize the original JPEG in-place (for fallback)
const optimizedJpeg = await sharp(filePath)
.resize(resizeOpts)
.jpeg({ quality: QUALITY, mozjpeg: true })
.toBuffer();
// Write optimized JPEG back
const { writeFile } = await import('fs/promises');
await writeFile(filePath, optimizedJpeg);
const afterJpeg = (await stat(filePath)).size;
const afterWebp = (await stat(webpPath)).size;
const saved = ((before - afterWebp) / before * 100).toFixed(0);
console.log(`${filePath}${(before/1024).toFixed(0)}KB → ${(afterWebp/1024).toFixed(0)}KB WebP (${saved}% saved)`);
} catch (err) {
console.error(`${filePath}: ${err.message}`);
}
}
const images = await getImages(IMG_DIR);
console.log(`Found ${images.length} images to optimize...\n`);
// Process in batches of 8 for memory
const BATCH = 8;
for (let i = 0; i < images.length; i += BATCH) {
await Promise.all(images.slice(i, i + BATCH).map(optimizeImage));
}
console.log('\nDone!');