perf: review-fixes (N+1, waterfalls, sanitize-cache, lazy strommix)
- blog-utils.resolvePostOverviewBlocks: parallel getPostBySlug via Promise.all + .catch(null) instead of sequential await loop. - blog-utils.resolveCalendarBlocks: same fix for calendar items. - [...slug] page load: SearchableText/Calendar/DeadlineBanner now run parallel after tagsMap (was strict waterfall). - sanitize-blocks: LRU cache keyed on raw html input — sanitize-html is ~ms/KB, the same content repeats across pages and cache hits. - cms.getNormalizedTranslations: WeakMap-memoised the per-request Object.entries normalize loop on top of the existing 5-min bundle TTL. - cms.cached: opt-in dev cache via DEV_CACHE_CMS=1 (default still off). - rusty-image.ensureTransformedImage: was async-wrapping a sync URL builder; made sync, removed awaits at call sites — saves microtasks. - BlockRenderer: lazy-load StrommixBlock (~220 KB) via dynamic import in the template — pages without the live-strommix widget no longer ship its bundle. Also: enabled Caddy zstd+gzip on windwiderstand.de vhost (server-side edit; verified `content-encoding: zstd` on response). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+61
-61
@@ -326,26 +326,22 @@ export async function loadPostsList(opts: {
|
||||
for (const post of posts) {
|
||||
const field = getPostImageField(post.postImage);
|
||||
if (field) {
|
||||
try {
|
||||
const url = await ensureTransformedImage(field.url, {
|
||||
width: 400,
|
||||
height: 267,
|
||||
fit: "cover",
|
||||
format: "webp",
|
||||
focalX: field.focal?.x,
|
||||
focalY: field.focal?.y,
|
||||
});
|
||||
const p = post as PostEntry & {
|
||||
_resolvedImageUrl?: string;
|
||||
_rawImageUrl?: string;
|
||||
_imageFocal?: { x: number; y: number } | null;
|
||||
};
|
||||
p._resolvedImageUrl = url;
|
||||
p._rawImageUrl = field.url;
|
||||
p._imageFocal = field.focal ?? null;
|
||||
} catch {
|
||||
/* image optional */
|
||||
}
|
||||
const url = ensureTransformedImage(field.url, {
|
||||
width: 400,
|
||||
height: 267,
|
||||
fit: "cover",
|
||||
format: "webp",
|
||||
focalX: field.focal?.x,
|
||||
focalY: field.focal?.y,
|
||||
});
|
||||
const p = post as PostEntry & {
|
||||
_resolvedImageUrl?: string;
|
||||
_rawImageUrl?: string;
|
||||
_imageFocal?: { x: number; y: number } | null;
|
||||
};
|
||||
p._resolvedImageUrl = url;
|
||||
p._rawImageUrl = field.url;
|
||||
p._imageFocal = field.focal ?? null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -512,15 +508,14 @@ export async function resolvePostOverviewBlocks(
|
||||
.filter(Boolean);
|
||||
const limit =
|
||||
typeof item.numberItems === "number" ? item.numberItems : 9999;
|
||||
const resolved: PostEntry[] = [];
|
||||
for (const slug of slugs.slice(0, limit)) {
|
||||
const post = await getPostBySlug(slug, {
|
||||
locale: "de",
|
||||
resolve: ["all"],
|
||||
});
|
||||
if (post) resolved.push(post);
|
||||
}
|
||||
item.postsResolved = resolved;
|
||||
const settled = await Promise.all(
|
||||
slugs.slice(0, limit).map((slug) =>
|
||||
getPostBySlug(slug, { locale: "de", resolve: ["all"] }).catch(
|
||||
() => null,
|
||||
),
|
||||
),
|
||||
);
|
||||
item.postsResolved = settled.filter((p): p is PostEntry => !!p);
|
||||
}
|
||||
}
|
||||
if (Array.isArray(item.postsResolved)) {
|
||||
@@ -531,26 +526,22 @@ export async function resolvePostOverviewBlocks(
|
||||
for (const post of item.postsResolved as PostEntry[]) {
|
||||
const field = getPostImageField(post.postImage);
|
||||
if (field) {
|
||||
try {
|
||||
const url = await ensureTransformedImage(field.url, {
|
||||
width: 400,
|
||||
height: 267,
|
||||
fit: "cover",
|
||||
format: "webp",
|
||||
focalX: field.focal?.x,
|
||||
focalY: field.focal?.y,
|
||||
});
|
||||
const p = post as PostEntry & {
|
||||
_resolvedImageUrl?: string;
|
||||
_rawImageUrl?: string;
|
||||
_imageFocal?: { x: number; y: number } | null;
|
||||
};
|
||||
p._resolvedImageUrl = url;
|
||||
p._rawImageUrl = field.url;
|
||||
p._imageFocal = field.focal ?? null;
|
||||
} catch {
|
||||
// Bild optional
|
||||
}
|
||||
const url = ensureTransformedImage(field.url, {
|
||||
width: 400,
|
||||
height: 267,
|
||||
fit: "cover",
|
||||
format: "webp",
|
||||
focalX: field.focal?.x,
|
||||
focalY: field.focal?.y,
|
||||
});
|
||||
const p = post as PostEntry & {
|
||||
_resolvedImageUrl?: string;
|
||||
_rawImageUrl?: string;
|
||||
_imageFocal?: { x: number; y: number } | null;
|
||||
};
|
||||
p._resolvedImageUrl = url;
|
||||
p._rawImageUrl = field.url;
|
||||
p._imageFocal = field.focal ?? null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -602,19 +593,28 @@ export async function resolveCalendarBlocks(
|
||||
resolve: "items",
|
||||
});
|
||||
const raw = calendar?.items ?? rawItems;
|
||||
const resolved: CalendarItemData[] = [];
|
||||
for (const x of raw) {
|
||||
if (typeof x === "object" && x !== null && "terminZeit" in x && "title" in x) {
|
||||
resolved.push(x as CalendarItemData);
|
||||
} else {
|
||||
const islug = typeof x === "string" ? x : (x as { _slug?: string })?._slug;
|
||||
if (islug) {
|
||||
const entry = await getCalendarItemBySlug(islug, { locale: "de" });
|
||||
if (entry) resolved.push(entry);
|
||||
const settled = await Promise.all(
|
||||
raw.map(async (x): Promise<CalendarItemData | null> => {
|
||||
if (
|
||||
typeof x === "object" &&
|
||||
x !== null &&
|
||||
"terminZeit" in x &&
|
||||
"title" in x
|
||||
) {
|
||||
return x as CalendarItemData;
|
||||
}
|
||||
}
|
||||
}
|
||||
(item as { items?: CalendarItemData[] }).items = resolved;
|
||||
const islug =
|
||||
typeof x === "string" ? x : (x as { _slug?: string })?._slug;
|
||||
if (!islug) return null;
|
||||
const entry = await getCalendarItemBySlug(islug, { locale: "de" }).catch(
|
||||
() => null,
|
||||
);
|
||||
return entry ?? null;
|
||||
}),
|
||||
);
|
||||
(item as { items?: CalendarItemData[] }).items = settled.filter(
|
||||
(e): e is CalendarItemData => !!e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user