perf: convert all images to webp, update references
- 77 jpg/png/gif → webp (kept 7 where webp larger) - public/ assets: 23.3 MB → ~12 MB (~50% smaller) - 110 image references updated across data files + components - scripts/convert-to-webp.mjs + scripts/fix-image-refs.mjs added Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
de25d774af
commit
dda358b59d
@@ -0,0 +1,70 @@
|
||||
import { promises as fs } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// Build map: relative path that ORIGINALLY existed but now only has .webp
|
||||
// Walk /public for .webp files, compute their candidate original ext (.jpg/.png/.gif)
|
||||
// If the candidate no longer exists on disk, that's a swap target.
|
||||
|
||||
const PUBLIC_ROOT = path.resolve('public');
|
||||
const SRC_ROOT = path.resolve('src');
|
||||
|
||||
async function walk(dir, out) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) await walk(p, out);
|
||||
else out.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Find .webp files and detect which original ext is missing
|
||||
const all = [];
|
||||
await walk(PUBLIC_ROOT, all);
|
||||
const webps = all.filter((p) => p.toLowerCase().endsWith('.webp'));
|
||||
const existing = new Set(all.map((p) => p.toLowerCase()));
|
||||
|
||||
const swaps = []; // { from: /images/foo.jpg, to: /images/foo.webp }
|
||||
for (const wp of webps) {
|
||||
const base = wp.slice(0, -5); // strip .webp
|
||||
for (const ext of ['.jpg', '.jpeg', '.png', '.gif']) {
|
||||
const candidate = base + ext;
|
||||
if (!existing.has(candidate.toLowerCase())) {
|
||||
// Candidate gone => it was converted
|
||||
const rel = '/' + path.relative(PUBLIC_ROOT, candidate).split(path.sep).join('/');
|
||||
const relWebp = '/' + path.relative(PUBLIC_ROOT, wp).split(path.sep).join('/');
|
||||
swaps.push({ from: rel, to: relWebp });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`detected ${swaps.length} swap pairs`);
|
||||
|
||||
// 2. Walk /src for ts/tsx/js/jsx/json/md/css and apply replacements
|
||||
const srcExts = new Set(['.ts', '.tsx', '.js', '.jsx', '.json', '.md', '.css', '.html', '.mjs']);
|
||||
const srcFiles = [];
|
||||
await walk(SRC_ROOT, srcFiles);
|
||||
const targets = srcFiles.filter((p) => srcExts.has(path.extname(p).toLowerCase()));
|
||||
|
||||
let totalReplacements = 0;
|
||||
let touchedFiles = 0;
|
||||
for (const file of targets) {
|
||||
let text = await fs.readFile(file, 'utf8');
|
||||
let count = 0;
|
||||
for (const s of swaps) {
|
||||
if (text.includes(s.from)) {
|
||||
const before = text;
|
||||
text = text.split(s.from).join(s.to);
|
||||
const occurrences = (before.length - text.length) / (s.from.length - s.to.length);
|
||||
count += occurrences;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
await fs.writeFile(file, text, 'utf8');
|
||||
totalReplacements += count;
|
||||
touchedFiles++;
|
||||
const rel = path.relative(process.cwd(), file).split(path.sep).join('/');
|
||||
console.log(`UPDATE ${rel} (${count} swaps)`);
|
||||
}
|
||||
}
|
||||
console.log('---');
|
||||
console.log(`replaced ${totalReplacements} references across ${touchedFiles} files`);
|
||||
Reference in New Issue
Block a user