import "dotenv/config"; import { defineCollection, defineConfig } from "@content-collections/core"; import { decode } from "html-entities"; import { unified } from "unified"; import { z } from "zod"; import { rehypeSanitize, rehypeStringify } from "@/lib/rehype"; import { remarkFrontmatter, remarkMdx, remarkParse, remarkRehype, remarkSmartypants, remarkStripMdxImportsExports, } from "@/lib/remark"; const POSTS_DIR = "notes" as const; const parseableDate = z.string().refine((value) => !Number.isNaN(Date.parse(value)), { message: "Invalid date string", }); const titleToHtml = async (title: string): Promise => { const parsedTitle = await unified() .use(remarkParse) .use(remarkSmartypants) .use(remarkRehype) .use(rehypeSanitize, { tagNames: ["code", "em", "strong"], }) .use(rehypeStringify) .process(title); return parsedTitle.toString().trim(); }; const contentToFeedHtml = async (content: string): Promise => { const parsedContent = await unified() .use(remarkParse) .use(remarkMdx) .use(remarkStripMdxImportsExports) .use(remarkFrontmatter) .use(remarkSmartypants) .use(remarkRehype) .use(rehypeSanitize, { tagNames: [ "p", "a", "em", "strong", "code", "pre", "blockquote", "del", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "hr", ], }) .use(rehypeStringify) .process(content); return parsedContent .toString() .replaceAll("/* prettier-ignore */", "") .replaceAll("

", "") .trim(); }; const posts = defineCollection({ name: "posts", directory: POSTS_DIR, include: "*/index.mdx", schema: z.object({ content: z.string(), date: parseableDate, title: z.string(), description: z.string().optional(), tags: z.array(z.string()).optional(), image: z.string().optional(), noComments: z.boolean().optional(), }), transform: async (post) => { const slug = post["_meta"].directory.replace(`${POSTS_DIR}/`, ""); const htmlTitle = await titleToHtml(post.title); const title = decode(htmlTitle.replace(/<[^>]*>/g, "")); return { ...post, feedHtml: await contentToFeedHtml(post.content), title, htmlTitle, slug, date: new Date(post.date).toISOString(), permalink: `${process.env.NEXT_PUBLIC_BASE_URL || ""}/${POSTS_DIR}/${slug}`, }; }, }); export default defineConfig({ content: [posts], });