feat: add content loading utilities for updates
This commit is contained in:
+107
@@ -0,0 +1,107 @@
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import matter from "gray-matter"
|
||||
import { remark } from "remark"
|
||||
import remarkHtml from "remark-html"
|
||||
import rehypeSlug from "rehype-slug"
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||
|
||||
export interface UpdateMeta {
|
||||
slug: string
|
||||
title: string
|
||||
date: string
|
||||
version: string
|
||||
summary: string
|
||||
}
|
||||
|
||||
export interface UpdateHeading {
|
||||
id: string
|
||||
text: string
|
||||
level: number
|
||||
}
|
||||
|
||||
export interface UpdateContent {
|
||||
meta: UpdateMeta
|
||||
html: string
|
||||
headings: UpdateHeading[]
|
||||
}
|
||||
|
||||
const UPDATES_DIR = path.join(process.cwd(), "content", "updates")
|
||||
|
||||
function getSlugs(): string[] {
|
||||
if (!fs.existsSync(UPDATES_DIR)) return []
|
||||
return fs
|
||||
.readdirSync(UPDATES_DIR)
|
||||
.filter((file) => file.endsWith(".md"))
|
||||
.map((file) => file.replace(/\.md$/, ""))
|
||||
}
|
||||
|
||||
export function getAllUpdates(): UpdateMeta[] {
|
||||
const slugs = getSlugs()
|
||||
const updates = slugs.map((slug) => {
|
||||
const { meta } = getUpdateMeta(slug)
|
||||
return meta
|
||||
})
|
||||
// Sort by date descending (newest first)
|
||||
return updates.sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
|
||||
)
|
||||
}
|
||||
|
||||
function getUpdateMeta(slug: string): { meta: UpdateMeta } {
|
||||
const filePath = path.join(UPDATES_DIR, `${slug}.md`)
|
||||
const fileContents = fs.readFileSync(filePath, "utf8")
|
||||
const { data } = matter(fileContents)
|
||||
|
||||
return {
|
||||
meta: {
|
||||
slug,
|
||||
title: data.title ?? "",
|
||||
date: data.date ?? "",
|
||||
version: data.version ?? "",
|
||||
summary: data.summary ?? "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUpdateBySlug(slug: string): Promise<UpdateContent> {
|
||||
const filePath = path.join(UPDATES_DIR, `${slug}.md`)
|
||||
const fileContents = fs.readFileSync(filePath, "utf8")
|
||||
const { data, content } = matter(fileContents)
|
||||
|
||||
const processedContent = await remark()
|
||||
.use(remarkHtml, { sanitize: false })
|
||||
.use(rehypeSlug)
|
||||
.use(rehypeAutolinkHeadings)
|
||||
.process(content)
|
||||
|
||||
const html = processedContent.toString()
|
||||
|
||||
// Extract headings from the rendered HTML
|
||||
const headingRegex = /<h([2-3])[^>]*id=["']([^"']+)["'][^>]*>(.*?)<\/h[2-3]>/g
|
||||
const headings: UpdateHeading[] = []
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = headingRegex.exec(html)) !== null) {
|
||||
headings.push({
|
||||
level: parseInt(match[1], 10),
|
||||
text: match[3].replace(/<[^>]*>/g, ""), // Strip any inner HTML tags
|
||||
id: match[2],
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
meta: {
|
||||
slug,
|
||||
title: data.title ?? "",
|
||||
date: data.date ?? "",
|
||||
version: data.version ?? "",
|
||||
summary: data.summary ?? "",
|
||||
},
|
||||
html,
|
||||
headings,
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllUpdateSlugs(): string[] {
|
||||
return getSlugs()
|
||||
}
|
||||
Reference in New Issue
Block a user