fix: derive OG slug from page.url to avoid Nunjucks race condition

page.fileSlug suffers from a race condition in Eleventy 3.x parallel
rendering where Nunjucks shares state across template compilations,
causing slugs to get mixed up between pages. page.url is always
correct, so derive the OG slug from it instead.
This commit is contained in:
Ricardo
2026-02-18 16:48:11 +01:00
parent cd8a218afb
commit 8d800e2c28
3 changed files with 22 additions and 11 deletions

View File

@@ -12,10 +12,11 @@
{% set _bookmarkedUrl = _prevPost.data.bookmarkOf or _prevPost.data.bookmark_of %}
{% set _repostedUrl = _prevPost.data.repostOf or _prevPost.data.repost_of %}
{% set _replyToUrl = _prevPost.data.inReplyTo or _prevPost.data.in_reply_to %}
{% set _prevHasOg = _prevPost.fileSlug | hasOgImage %}
{% set _prevOgSlug = _prevPost.url | ogSlug %}
{% set _prevHasOg = _prevOgSlug | hasOgImage %}
<a href="{{ _prevPost.url }}" class="group flex items-start gap-3 text-sm text-primary-600 dark:text-primary-400 hover:underline">
{% if _prevHasOg %}
<img src="/og/{{ _prevPost.fileSlug }}.png" alt="" class="w-20 h-[42px] object-cover rounded flex-shrink-0 opacity-80 group-hover:opacity-100 transition-opacity" loading="lazy" eleventy:ignore>
<img src="/og/{{ _prevOgSlug }}.png" alt="" class="w-20 h-[42px] object-cover rounded flex-shrink-0 opacity-80 group-hover:opacity-100 transition-opacity" loading="lazy" eleventy:ignore>
{% endif %}
<span class="line-clamp-2 flex items-center gap-1.5">
{% if _likedUrl %}
@@ -46,7 +47,8 @@
{% set _bookmarkedUrl = _nextPost.data.bookmarkOf or _nextPost.data.bookmark_of %}
{% set _repostedUrl = _nextPost.data.repostOf or _nextPost.data.repost_of %}
{% set _replyToUrl = _nextPost.data.inReplyTo or _nextPost.data.in_reply_to %}
{% set _nextHasOg = _nextPost.fileSlug | hasOgImage %}
{% set _nextOgSlug = _nextPost.url | ogSlug %}
{% set _nextHasOg = _nextOgSlug | hasOgImage %}
<a href="{{ _nextPost.url }}" class="group flex items-start gap-3 text-sm text-primary-600 dark:text-primary-400 hover:underline {% if _prevPost %}justify-end{% endif %}">
<span class="line-clamp-2 flex items-center gap-1.5 {% if _prevPost %}justify-end{% endif %}">
{% if _likedUrl %}
@@ -66,7 +68,7 @@
{% endif %}
</span>
{% if _nextHasOg %}
<img src="/og/{{ _nextPost.fileSlug }}.png" alt="" class="w-20 h-[42px] object-cover rounded flex-shrink-0 opacity-80 group-hover:opacity-100 transition-opacity" loading="lazy" eleventy:ignore>
<img src="/og/{{ _nextOgSlug }}.png" alt="" class="w-20 h-[42px] object-cover rounded flex-shrink-0 opacity-80 group-hover:opacity-100 transition-opacity" loading="lazy" eleventy:ignore>
{% endif %}
</a>
</div>

View File

@@ -23,7 +23,8 @@
<meta property="og:type" content="{% if page.url == '/' %}website{% else %}article{% endif %}">
<meta property="og:description" content="{{ ogDesc }}">
<meta name="description" content="{{ ogDesc }}">
{% set hasGeneratedOg = page.fileSlug | hasOgImage %}
{% set ogFileSlug = page.url | ogSlug %}
{% set hasGeneratedOg = ogFileSlug | hasOgImage %}
{% if ogPhoto and ogPhoto != "" and (ogPhoto | length) > 10 %}
<meta property="og:image" content="{% if 'http' in ogPhoto %}{{ ogPhoto }}{% else %}{{ site.url }}{% if ogPhoto[0] != '/' %}/{% endif %}{{ ogPhoto }}{% endif %}">
{% elif image and image != "" and (image | length) > 10 %}
@@ -31,7 +32,7 @@
{% elif contentImage and contentImage != "" and (contentImage | length) > 10 %}
<meta property="og:image" content="{% if 'http' in contentImage %}{{ contentImage }}{% else %}{{ site.url }}{% if contentImage[0] != '/' %}/{% endif %}{{ contentImage }}{% endif %}">
{% elif hasGeneratedOg %}
<meta property="og:image" content="{{ site.url }}/og/{{ page.fileSlug }}.png">
<meta property="og:image" content="{{ site.url }}/og/{{ ogFileSlug }}.png">
{% else %}
<meta property="og:image" content="{{ site.url }}/images/og-default.png">
{% endif %}
@@ -51,7 +52,7 @@
{% elif contentImage and contentImage != "" and (contentImage | length) > 10 %}
<meta name="twitter:image" content="{% if 'http' in contentImage %}{{ contentImage }}{% else %}{{ site.url }}/{{ contentImage }}{% endif %}">
{% elif hasGeneratedOg %}
<meta name="twitter:image" content="{{ site.url }}/og/{{ page.fileSlug }}.png">
<meta name="twitter:image" content="{{ site.url }}/og/{{ ogFileSlug }}.png">
{% endif %}
{# Favicon #}

View File

@@ -362,10 +362,18 @@ export default function (eleventyConfig) {
}
});
// Check if a generated OG image exists for this page slug
eleventyConfig.addFilter("hasOgImage", (fileSlug) => {
if (!fileSlug) return false;
const ogPath = resolve(__dirname, ".cache", "og", `${fileSlug}.png`);
// Derive OG slug from page.url (reliable) instead of page.fileSlug
// (which suffers from Nunjucks race conditions in Eleventy 3.x parallel rendering)
eleventyConfig.addFilter("ogSlug", (url) => {
if (!url) return "";
const last = url.replace(/\/$/, "").split("/").pop();
return last.replace(/^\d{4}-\d{2}-\d{2}-/, "");
});
// Check if a generated OG image exists for this slug
eleventyConfig.addFilter("hasOgImage", (slug) => {
if (!slug) return false;
const ogPath = resolve(__dirname, ".cache", "og", `${slug}.png`);
return existsSync(ogPath);
});