mirror of
https://github.com/litruv/lit.ruv.wtf.git
synced 2026-07-24 02:36:02 +10:00
feat: add extractDescription method and enhance renderPageTemplate with Open Graph metadata
This commit is contained in:
@@ -84,6 +84,23 @@ class StaticBlogGenerator {
|
|||||||
return this.renderPageTemplate('Blog', content, '/blog.html');
|
return this.renderPageTemplate('Blog', content, '/blog.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a plain-text description from the first paragraph of markdown content.
|
||||||
|
*
|
||||||
|
* @param {string} content
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
extractDescription(content) {
|
||||||
|
const lines = (content || '').split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!') || trimmed.startsWith('>') || /^[-*_]{3,}$/.test(trimmed) || trimmed.startsWith('```')) continue;
|
||||||
|
const plain = trimmed.replace(/[*_`\[\]]/g, '').replace(/!?\[.*?\]\(.*?\)/g, '').trim();
|
||||||
|
if (plain.length > 20) return plain.slice(0, 160);
|
||||||
|
}
|
||||||
|
return 'Read this post on lit.ruv.wtf';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{slug: string, title: string, date: string | null, author: string | null, tags?: string[], content: string}} post
|
* @param {{slug: string, title: string, date: string | null, author: string | null, tags?: string[], content: string}} post
|
||||||
* @param {{slug: string, title: string, date: string | null} | null} prev - Older post (lower index)
|
* @param {{slug: string, title: string, date: string | null} | null} prev - Older post (lower index)
|
||||||
@@ -161,7 +178,16 @@ class StaticBlogGenerator {
|
|||||||
'</main>'
|
'</main>'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
return this.renderPageTemplate(`${safeTitle} - Blog`, content, `/blog/${encodeURIComponent(post.slug)}/`);
|
const canonicalUrl = `https://lit.ruv.wtf/blog/${encodeURIComponent(post.slug)}/`;
|
||||||
|
return this.renderPageTemplate(`${safeTitle} - Blog`, content, `/blog/${encodeURIComponent(post.slug)}/`, {
|
||||||
|
description: this.extractDescription(post.content),
|
||||||
|
ogTitle: safeTitle,
|
||||||
|
ogType: 'article',
|
||||||
|
ogUrl: canonicalUrl,
|
||||||
|
articlePublishedTime: post.date || null,
|
||||||
|
articleAuthor: post.author || null,
|
||||||
|
articleTags: tags,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,8 +196,27 @@ class StaticBlogGenerator {
|
|||||||
* @param {string} canonicalPath
|
* @param {string} canonicalPath
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
renderPageTemplate(title, bodyHtml, canonicalPath) {
|
renderPageTemplate(title, bodyHtml, canonicalPath, og = {}) {
|
||||||
const escapedTitle = this.escapeHtml(title);
|
const escapedTitle = this.escapeHtml(title);
|
||||||
|
const canonicalUrl = `https://lit.ruv.wtf${canonicalPath}`;
|
||||||
|
const description = this.escapeHtml(og.description || 'lit.ruv.wtf blog posts.');
|
||||||
|
const ogTitle = this.escapeHtml(og.ogTitle || title);
|
||||||
|
const ogType = og.ogType || 'website';
|
||||||
|
const ogUrl = og.ogUrl || canonicalUrl;
|
||||||
|
|
||||||
|
const ogMeta = [
|
||||||
|
` <meta property="og:title" content="${ogTitle}">`,
|
||||||
|
` <meta property="og:type" content="${ogType}">`,
|
||||||
|
` <meta property="og:url" content="${ogUrl}">`,
|
||||||
|
` <meta property="og:description" content="${description}">`,
|
||||||
|
` <meta property="og:site_name" content="lit.ruv.wtf">`,
|
||||||
|
` <meta name="twitter:card" content="summary">`,
|
||||||
|
` <meta name="twitter:title" content="${ogTitle}">`,
|
||||||
|
` <meta name="twitter:description" content="${description}">`,
|
||||||
|
og.articlePublishedTime ? ` <meta property="article:published_time" content="${og.articlePublishedTime}">` : '',
|
||||||
|
og.articleAuthor ? ` <meta property="article:author" content="${this.escapeHtml(og.articleAuthor)}">` : '',
|
||||||
|
...(og.articleTags || []).map(t => ` <meta property="article:tag" content="${this.escapeHtml(t)}">`,),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'<!DOCTYPE html>',
|
'<!DOCTYPE html>',
|
||||||
@@ -181,8 +226,9 @@ class StaticBlogGenerator {
|
|||||||
' <meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
' <meta name="viewport" content="width=device-width, initial-scale=1.0">',
|
||||||
' <base href="/">',
|
' <base href="/">',
|
||||||
` <title>${escapedTitle}</title>`,
|
` <title>${escapedTitle}</title>`,
|
||||||
' <meta name="description" content="Lit.ruv.wtf blog posts.">',
|
` <meta name="description" content="${description}">`,
|
||||||
` <link rel="canonical" href="https://lit.ruv.wtf${canonicalPath}">`,
|
` <link rel="canonical" href="${canonicalUrl}">`,
|
||||||
|
...ogMeta,
|
||||||
' <link rel="icon" type="image/png" sizes="32x32" href="/logos/32px.png">',
|
' <link rel="icon" type="image/png" sizes="32x32" href="/logos/32px.png">',
|
||||||
' <link rel="icon" type="image/png" sizes="64x64" href="/logos/64px.png">',
|
' <link rel="icon" type="image/png" sizes="64x64" href="/logos/64px.png">',
|
||||||
' <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">',
|
' <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">',
|
||||||
|
|||||||
Reference in New Issue
Block a user