import { defineConfig } from "vite"; import fs from "fs"; import path from "path"; import { marked, Token, Tokens } from "marked"; import xml from "xml"; import matter from "gray-matter"; import highlight from "highlight.js"; const blogDir = path.resolve(__dirname, "src/blog"); const outputDir = path.resolve(__dirname, "dist/blog"); const rssFeedPath = path.resolve(__dirname, "dist/feed.xml"); const hostname = "https://augustkline.com"; const blogUrl = `${hostname}/blog`; interface Articles { title: string; link: string; description: string; pubDate: string; } const renderer = new marked.Renderer(); renderer.code = ({ text, lang }: Tokens.Code) => { const validLang = highlight.getLanguage(lang!) ? lang! : "plaintext"; const highlightedCode = highlight.highlight(text!, { language: validLang }); return `
${highlightedCode.value}
`; }; marked.setOptions({ renderer, gfm: true, breaks: true, }); function generateBlogHtml() { fs.mkdirSync(outputDir, { recursive: true }); const articles: Articles[] = []; let blurbs: { date: string; content: string }[] = []; fs.readdirSync(blogDir).forEach((file) => { if (file.endsWith(".md")) { const filePath = path.join(blogDir, file); const str = fs.readFileSync(filePath, "utf-8"); let { data, content } = matter(str); const slug = `${data.date!}-${data.title!.replace(/ /g, "-")}`; const html_content = marked.parse(content); const blogPostsPath = path.join(outputDir, `${slug}.html`); const postHtml = ` ${data.title!}
${html_content} Back to Home
`; const link = `/blog/${slug}.html`; const blurbHtml = `

${data.title!}

${new Date(data.date!).toDateString()}

${data.desc!}

`; blurbs.push({ date: data.date!, content: blurbHtml }); fs.writeFileSync(blogPostsPath, postHtml); // Store article data for RSS articles.push({ title: data.title!, link: link, description: data.desc!, pubDate: data.date!, }); } }); const blogIndexPath = path.join(outputDir, "index.html"); blurbs.sort((a, b) => { return new Date(b.date).getTime() - new Date(a.date).getTime(); }); let blurbsHtml: string = ""; for (let blurb in blurbs) { blurbsHtml = blurbsHtml.concat(blurbs[blurb].content); } const blogIndexHtml = ` august kline's blog

ʕ·ᴥ·ʔ-☆ august kline's blog ☆

${blurbsHtml}
`; fs.writeFileSync(blogIndexPath, blogIndexHtml); return articles; } // Function to generate RSS feed function generateRssFeed(articles: Articles[]) { const feedItems = articles.map((article) => ({ item: [ { title: article.title }, { link: article.link }, { description: article.description }, { pubDate: article.pubDate }, ], })); const rss = { rss: [ { channel: [ { title: "ʕ·ᴥ·ʔ-☆ august kline's blog ☆" }, { link: `${blogUrl}` }, // Update with your domain { description: "whatever's on my mind, short essays, tech stuff, etc :)", }, ...feedItems, ], }, ], }; const rssXml = xml(rss, { declaration: true }); fs.writeFileSync(rssFeedPath, rssXml); } // Export Vite config export default defineConfig({ build: { rollupOptions: { output: { entryFileNames: `main.js`, }, plugins: [ { name: "generate-blog-html-and-rss", writeBundle() { // parses blog markdown, writes the blog endpoints and returns array of article data const articles = generateBlogHtml(); generateRssFeed(articles); }, }, ], }, }, });