Files
Portfolio/scripts/optimize-images.js
T
damjan_savicandClaude Opus 4.5 588d6e1f17 Performance: PageSpeed Optimierungen implementiert
- LCP-Bild (Portrait) mit fetchpriority="high" und Preload optimiert
- Logo SVG mit expliziten width/height Attributen versehen (CLS)
- Touch-Targets in Experience Navigation von 5px auf 24px vergrößert
- Kontrast für Firmenname/Zeitraum verbessert (zinc-300/zinc-400)
- PWA Service Worker mit script-defer für nicht-blockierendes Laden
- Responsive Bilder: srcset mit 400w/800w/1200w + WebP-Format
- Neues Build-Script für Bildoptimierung (scripts/optimize-images.js)
- Original-Bilder in source-images/ verschoben (gitignored)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 21:14:29 +01:00

116 lines
3.3 KiB
JavaScript

import sharp from 'sharp';
import { readdir, mkdir } from 'fs/promises';
import { join, dirname, basename, extname } from 'path';
import { fileURLToPath } from 'url';
import { existsSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const SIZES = [400, 800, 1200];
const INPUT_DIR = join(__dirname, '../source-images/projects');
const OUTPUT_DIR = join(__dirname, '../public/images/projects');
async function processImage(inputPath, projectSlug) {
const filename = basename(inputPath, extname(inputPath));
const ext = extname(inputPath).toLowerCase();
// Skip non-image files
if (!['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
return;
}
const outputDir = join(OUTPUT_DIR, projectSlug);
// Create output directory if it doesn't exist
await mkdir(outputDir, { recursive: true });
console.log(`Processing: ${projectSlug}/${filename}${ext}`);
// Create optimized original size (cover.jpg)
const originalOutputPath = join(outputDir, `${filename}.jpg`);
try {
await sharp(inputPath)
.jpeg({ quality: 85, progressive: true })
.toFile(originalOutputPath);
console.log(` Created: ${filename}.jpg (optimized original)`);
} catch (error) {
console.error(` Error creating optimized original:`, error.message);
}
// Create different sizes
for (const width of SIZES) {
const outputFilename = `${filename}-${width}w.jpg`;
const outputPath = join(outputDir, outputFilename);
try {
await sharp(inputPath)
.resize(width, null, {
withoutEnlargement: true,
fit: 'inside'
})
.jpeg({ quality: 80, progressive: true })
.toFile(outputPath);
console.log(` Created: ${outputFilename}`);
} catch (error) {
console.error(` Error creating ${outputFilename}:`, error.message);
}
}
// Create WebP version of original size
const webpFilename = `${filename}.webp`;
const webpPath = join(outputDir, webpFilename);
try {
await sharp(inputPath)
.webp({ quality: 80 })
.toFile(webpPath);
console.log(` Created: ${webpFilename}`);
} catch (error) {
console.error(` Error creating WebP:`, error.message);
}
}
async function processDirectory(dirPath) {
if (!existsSync(dirPath)) {
console.error(`Error: Source directory not found: ${dirPath}`);
console.log('\nPlease ensure original images are placed in:');
console.log(' source-images/projects/<project-slug>/cover.jpg');
process.exit(1);
}
const entries = await readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dirPath, entry.name);
if (entry.isDirectory()) {
// Check for cover.jpg in this project directory
const coverPath = join(fullPath, 'cover.jpg');
if (existsSync(coverPath)) {
await processImage(coverPath, entry.name);
} else {
console.log(`Skipping ${entry.name}: no cover.jpg found`);
}
}
}
}
async function main() {
console.log('Starting image optimization...');
console.log(`Source: ${INPUT_DIR}`);
console.log(`Output: ${OUTPUT_DIR}\n`);
try {
await processDirectory(INPUT_DIR);
console.log('\nImage optimization complete!');
} catch (error) {
console.error('Error during optimization:', error);
process.exit(1);
}
}
main();