feat: See Also includes in-text links to other blog posts

Adds seeAlsoLinks filter that extracts all internal blog.giersig.eu
links from the raw markdown source and merges them with the explicit
`related` frontmatter field, deduplicated. The section now surfaces
automatically whenever an article links to another post inline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-23 18:45:56 +01:00
parent 7e8da755ef
commit 85beed8ef3
2 changed files with 24 additions and 3 deletions

View File

@@ -128,12 +128,13 @@ withBlogSidebar: true
</details>
{% endif %}
{# See Also — related posts from frontmatter (resolved wikilinks → URLs) #}
{% if related and related.length > 0 %}
{# See Also — explicit related: frontmatter + in-text links to other blog posts #}
{% set _seeAlso = page.inputPath | seeAlsoLinks(related) %}
{% if _seeAlso.length > 0 %}
<section class="post-related mt-6 pt-5 border-t border-surface-200 dark:border-surface-700">
<h2 class="text-xs font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-widest mb-3">See Also</h2>
<ul class="space-y-1.5 list-none p-0 m-0">
{% for relUrl in related %}
{% for relUrl in _seeAlso %}
{% set _relPost = collections.posts | postByUrl(relUrl) %}
<li>
<a href="{{ relUrl }}" class="text-sm text-accent-700 dark:text-accent-300 hover:underline">

View File

@@ -1119,6 +1119,26 @@ export default function (eleventyConfig) {
return (posts || []).find((p) => p.url === path || p.url === `${path}/`);
});
// Collect all internal blog links from a post's raw markdown and merge with
// the explicit `related` frontmatter URLs. Returns a deduplicated list.
eleventyConfig.addFilter("seeAlsoLinks", function (inputPath, relatedUrls) {
const seen = new Set();
const result = [];
const add = (url) => {
const key = String(url).replace(/\/$/, "");
if (!seen.has(key)) { seen.add(key); result.push(String(url)); }
};
for (const url of (relatedUrls || [])) add(url);
try {
const content = readFileSync(inputPath, "utf-8");
const escaped = siteUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const re = new RegExp(`\\]\\((${escaped}/[^)\\s]+)\\)`, "g");
let m;
while ((m = re.exec(content)) !== null) add(m[1]);
} catch { /* ignore */ }
return result;
});
// Strip garden/* tags from a category list so they don't render as
// plain category pills alongside the garden badge.
eleventyConfig.addFilter("withoutGardenTags", (categories) => {