Skip to content

Commit 243d774

Browse files
committed
perf(og): cache rendered WebPs, skip re-render when inputs unchanged (~375x faster warm)
1 parent 5e9daa1 commit 243d774

1 file changed

Lines changed: 50 additions & 7 deletions

File tree

scripts/generate-og-images.mjs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { Resvg } from '@resvg/resvg-js';
22
import sharp from 'sharp';
3-
import { writeFile, mkdir } from 'node:fs/promises';
3+
import { writeFile, mkdir, copyFile, stat } from 'node:fs/promises';
44
import { existsSync, readFileSync, readdirSync } from 'node:fs';
5+
import { createHash } from 'node:crypto';
56
import { dirname, join, extname } from 'node:path';
67
import { buildRouteMetaMap, routeToOgPath, DIST, PUBLIC } from './lib/route-meta.mjs';
78

89
const WEBP_QUALITY = 80;
910

11+
// Bump when the rendering algorithm changes (layout, gradient logic, font, etc.)
12+
// — invalidates every cached WebP.
13+
const CACHE_VERSION = 'v1';
14+
const CACHE_DIR = join('node_modules', '.cache', 'og-images');
15+
1016
const W = 1200;
1117
const H = 630;
1218
const STRIP_H = 200;
@@ -17,6 +23,10 @@ const ART_DIR = join(PUBLIC, 'images', 'og-art');
1723
const MIME = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif' };
1824
const UNSUPPORTED_EXT = new Set(['.webp', '.avif', '.heic', '.heif', '.tiff', '.tif']);
1925

26+
function sha256Hex(input) {
27+
return createHash('sha256').update(input).digest('hex');
28+
}
29+
2030
function loadArt() {
2131
if (!existsSync(ART_DIR)) return [];
2232
const out = [];
@@ -29,7 +39,11 @@ function loadArt() {
2939
}
3040
if (!MIME[ext]) continue;
3141
const buf = readFileSync(join(ART_DIR, f));
32-
out.push({ name: f, dataUri: `data:${MIME[ext]};base64,${buf.toString('base64')}` });
42+
out.push({
43+
name: f,
44+
dataUri: `data:${MIME[ext]};base64,${buf.toString('base64')}`,
45+
hash: sha256Hex(buf),
46+
});
3347
}
3448
if (skipped.length) {
3549
console.warn(
@@ -39,6 +53,18 @@ function loadArt() {
3953
return out;
4054
}
4155

56+
function cacheKeyFor({ title, slug, art }) {
57+
return sha256Hex(
58+
JSON.stringify({
59+
v: CACHE_VERSION,
60+
title,
61+
slug,
62+
artName: art?.name ?? null,
63+
artHash: art?.hash ?? null,
64+
})
65+
);
66+
}
67+
4268
function hashSlug(s) {
4369
let h = 2166136261 >>> 0;
4470
for (let i = 0; i < s.length; i++) {
@@ -191,22 +217,39 @@ async function main() {
191217
}
192218
console.log(`generate-og-images: ${targets.length} route(s) need a fallback image`);
193219

194-
let written = 0;
220+
if (!existsSync(CACHE_DIR)) await mkdir(CACHE_DIR, { recursive: true });
221+
222+
let rendered = 0;
223+
let cached = 0;
195224
let totalBytes = 0;
196225
for (const { route, meta } of targets) {
197226
const slug = route.split('/').filter(Boolean).pop() || 'fezcodex';
198-
const svg = buildSvg({ title: meta.title, slug, art: pickArt(art, slug) });
199-
const webp = await renderWebp(svg);
227+
const chosenArt = pickArt(art, slug);
228+
const key = cacheKeyFor({ title: meta.title, slug, art: chosenArt });
229+
const cachePath = join(CACHE_DIR, `${key}.webp`);
200230
const outAbs = join(DIST, routeToOgPath(route));
201231
if (!existsSync(dirname(outAbs))) await mkdir(dirname(outAbs), { recursive: true });
232+
233+
if (existsSync(cachePath)) {
234+
await copyFile(cachePath, outAbs);
235+
const st = await stat(cachePath);
236+
cached++;
237+
totalBytes += st.size;
238+
continue;
239+
}
240+
241+
const svg = buildSvg({ title: meta.title, slug, art: chosenArt });
242+
const webp = await renderWebp(svg);
202243
await writeFile(outAbs, webp);
203-
written++;
244+
await writeFile(cachePath, webp);
245+
rendered++;
204246
totalBytes += webp.length;
205247
}
206248

249+
const written = rendered + cached;
207250
const avgKb = written ? Math.round(totalBytes / written / 1024) : 0;
208251
console.log(
209-
`generate-og-images: wrote ${written} WebP(s), avg ${avgKb} KB (${Date.now() - t0}ms)`
252+
`generate-og-images: wrote ${written} WebP(s) (${rendered} rendered, ${cached} cached), avg ${avgKb} KB (${Date.now() - t0}ms)`
210253
);
211254
}
212255

0 commit comments

Comments
 (0)