187 lines
5.0 KiB
TypeScript
187 lines
5.0 KiB
TypeScript
|
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 `<pre><code class="language-${validLang}">${highlightedCode.value}</code></pre>`;
|
|||
|
};
|
|||
|
|
|||
|
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 = `
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>${data.title!}</title>
|
|||
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
|||
|
<link rel="stylesheet" type="text/css" href="/github.css">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<main>
|
|||
|
${html_content}
|
|||
|
<a href="/">Back to Home</a>
|
|||
|
</main>
|
|||
|
<div id="background">
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
<script type="module" src="/main.js"></script>
|
|||
|
</html>
|
|||
|
`;
|
|||
|
|
|||
|
const link = `/blog/${slug}.html`;
|
|||
|
const blurbHtml = `
|
|||
|
<div>
|
|||
|
<a href="${link}">
|
|||
|
<h2>${data.title!}</h2>
|
|||
|
<p>${new Date(data.date!).toDateString()}</p>
|
|||
|
<p>${data.desc!}</p>
|
|||
|
</a>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
|
|||
|
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 = `
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>august kline's blog</title>
|
|||
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
|||
|
<link rel="stylesheet" type="text/css" href="/github.css">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<main>
|
|||
|
<h1>ʕ·ᴥ·ʔ-☆ august kline's blog ☆</h1>
|
|||
|
${blurbsHtml}
|
|||
|
</main>
|
|||
|
<div id="background">
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
<script type="module" src="/main.js"></script>
|
|||
|
</html>
|
|||
|
`;
|
|||
|
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);
|
|||
|
},
|
|||
|
},
|
|||
|
],
|
|||
|
},
|
|||
|
},
|
|||
|
});
|