Skip to content

Commit 9c042bb

Browse files
committed
add JSON-LD markup for blogs
1 parent 6060131 commit 9c042bb

File tree

3 files changed

+270
-137
lines changed

3 files changed

+270
-137
lines changed

src/content/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const posts = defineCollection({
1010
draft: z.boolean().default(false),
1111
tags: z.array(z.string()).default([]),
1212
keyword: z.array(z.string()).default([]),
13+
image: z.string().optional(),
14+
author: z.string().default('حمد بنقالي'),
1315
// Add more fields as needed
1416
}),
1517
});

src/pages/posts/[...slug].astro

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import Layout from '@/layouts/Layout.astro';
33
import { type CollectionEntry, getCollection } from 'astro:content';
4+
import { blogPost } from '@/schema.blog';
45
56
export async function getStaticPaths() {
67
const posts = await getCollection('posts', ({ data }) => {
@@ -15,6 +16,60 @@ type Props = CollectionEntry<'posts'>;
1516
1617
const post = Astro.props;
1718
const { Content } = await post.render();
19+
20+
// Prepare data for JSON-LD
21+
const postUrl = `https://blog.x7md.net/posts/${post.slug}/`;
22+
const postContent = await post.render();
23+
24+
// Extract article body text from markdown content
25+
// Remove markdown syntax and get clean text for schema.org
26+
function extractTextFromMarkdown(markdown) {
27+
if (!markdown) return '';
28+
29+
// Remove markdown headers, links, code blocks, etc.
30+
let text = markdown
31+
.replace(/^#{1,6}\s+/gm, '') // Remove headers
32+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
33+
.replace(/```[\s\S]*?```/g, '') // Remove code blocks
34+
.replace(/`([^`]+)`/g, '$1') // Remove inline code
35+
.replace(/\*\*([^*]+)\*\*/g, '$1') // Remove bold
36+
.replace(/\*([^*]+)\*/g, '$1') // Remove italic
37+
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '') // Remove images
38+
.replace(/^\s*[-*+]\s+/gm, '') // Remove list markers
39+
.replace(/^\s*\d+\.\s+/gm, '') // Remove numbered list markers
40+
.replace(/^\s*>\s+/gm, '') // Remove blockquotes
41+
.replace(/\n{3,}/g, '\n\n') // Normalize line breaks
42+
.trim();
43+
44+
// Truncate to reasonable length for schema.org (first 500 words)
45+
const words = text.split(/\s+/).filter(word => word.length > 0);
46+
if (words.length > 500) {
47+
text = words.slice(0, 500).join(' ') + '...';
48+
}
49+
50+
return text;
51+
}
52+
53+
const articleBodyText = extractTextFromMarkdown(post.body) || post.data.description;
54+
55+
// Count words in the content (more accurate)
56+
const wordCount = post.body ? post.body.split(/\s+/).filter(word => word.trim().length > 0).length : 0;
57+
58+
// Calculate reading time (average 200 words per minute)
59+
const readingTimeMinutes = Math.ceil(wordCount / 200);
60+
61+
const jsonLdData = {
62+
url: postUrl,
63+
headline: post.data.title,
64+
description: post.data.description,
65+
articleBody: articleBodyText,
66+
datePublished: post.data.pubDate.toISOString(),
67+
dateModified: post.data.updatedDate ? post.data.updatedDate.toISOString() : post.data.pubDate.toISOString(),
68+
image: post.data.image ? `https://blog.x7md.net${post.data.image}` : undefined,
69+
keywords: [...(post.data.tags || []), ...(post.data.keyword || [])],
70+
wordCount: wordCount,
71+
timeRequired: `PT${readingTimeMinutes}M` // ISO 8601 duration format
72+
};
1873
---
1974

2075
<Layout title={post.data.title} description={post.data.description}>
@@ -45,4 +100,7 @@ const { Content } = await post.render();
45100
<Content />
46101
</article>
47102
</main>
103+
104+
<!-- JSON-LD Structured Data -->
105+
<script type="application/ld+json" set:html={blogPost(jsonLdData)}></script>
48106
</Layout>

0 commit comments

Comments
 (0)