posts setup

This commit is contained in:
taskylizard
2023-12-31 06:57:10 +00:00
parent 8f457fa166
commit 5ae23bea33
14 changed files with 528 additions and 229 deletions

View File

@@ -9,7 +9,7 @@ import { renderAsync } from "@resvg/resvg-js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const __fonts = resolve(__dirname, "../fonts");
export async function generateImages(config: SiteConfig) {
export async function generateImages(config: SiteConfig): Promise<void> {
const pages = await createContentLoader("**/*.md", { excerpt: true }).load();
const template = await readFile(resolve(__dirname, "./Template.vue"), "utf-8");
@@ -57,7 +57,12 @@ interface GenerateImagesOptions {
fonts: SatoriOptions["fonts"];
}
async function generateImage({ page, template, outDir, fonts }: GenerateImagesOptions) {
async function generateImage({
page,
template,
outDir,
fonts,
}: GenerateImagesOptions): Promise<void> {
const { frontmatter, url } = page;
const options: SatoriOptions = {

39
.vitepress/rss.ts Normal file
View File

@@ -0,0 +1,39 @@
import path from "path";
import { writeFileSync } from "fs";
import { Feed } from "feed";
import { createContentLoader, type SiteConfig } from "vitepress";
import { meta } from "./constants";
export async function genFeed(config: SiteConfig) {
const feed = new Feed({
title: "FMHY • Monthy Posts",
description: meta.description,
id: meta.hostname,
link: meta.hostname,
language: "en",
image: "https://github.com/fmhy.png",
copyright: "",
});
const posts = await createContentLoader("posts/**/*.md", {
excerpt: true,
render: true,
}).load();
posts.sort(
(a, b) => +new Date(b.frontmatter.date as string) - +new Date(a.frontmatter.date as string),
);
for (const { url, excerpt, frontmatter, html } of posts) {
feed.addItem({
title: frontmatter.title,
id: `${meta.hostname}${url}`,
link: `${meta.hostname}${url.split("/posts")[1]}`,
description: excerpt,
content: html,
date: frontmatter.date,
});
}
writeFileSync(path.join(config.outDir, "feed.rss"), feed.rss2());
}

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import DefaultTheme from "vitepress/theme";
import Sidebar from "./components/SidebarCard.vue";
import Announcement from "./components/Announcement.vue";
import { useData } from "vitepress";
import { nextTick, provide } from "vue";
import Sidebar from "./components/SidebarCard.vue";
import Announcement from "./components/Announcement.vue";
const { isDark } = useData();

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
import { useData } from "vitepress";
import Authors from "./components/Authors.vue";
const props = defineProps<{
authors: string[];
}>();
const formatDate = (raw: string): string => {
const date = new Date(raw);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
});
};
const { frontmatter } = useData();
</script>
<template>
<h3>
{{ frontmatter.title }}
</h3>
<span>{{ frontmatter.description }} {{ formatDate(frontmatter.date) }}</span>
<Authors :authors="props.authors" />
</template>

View File

@@ -0,0 +1,37 @@
<!-- eslint-disable vue/require-v-for-key -->
<script setup lang="ts">
import { data as posts } from "./posts.data";
const formatDate = (raw: string): string => {
const date = new Date(raw);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
});
};
</script>
<template>
<div>
<section>
<h1 class="flex items-center gap-2">Posts</h1>
<p>Everything from Monthly Updates to fmhy updates.</p>
</section>
<template v-for="year in Object.keys(posts).reverse()" :key="year">
<h2>{{ year }}</h2>
<ul>
<li v-for="post of posts[year]" :key="post.url">
<article>
<a :href="post.url" class="border-none">{{ post.title }}</a> -
<dl class="m-0 inline">
<dt class="sr-only">Published on</dt>
<dd class="m-0 inline">
<time :datetime="post.date" class="font-bold">{{ formatDate(post.date) }}</time>
</dd>
</dl>
</article>
</li>
</ul>
</template>
</div>
</template>

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from "vue";
const props = defineProps<{
authors: string[];
}>();
interface Author {
name: string;
github: string;
}
const data = [
{
name: "nbats",
github: "https://github.com/nbats",
},
{
name: "Kai",
github: "https://github.com/Kai-FMHY",
},
{
name: "taskylizard",
github: "https://github.com/taskylizard",
},
{
name: "zinklog",
github: "https://github.com/zinklog2",
},
] satisfies Author[];
const authors = computed(() => data.filter((author) => props.authors.includes(author.name)));
</script>
<template>
<div class="flex flex-wrap gap-4 pt-2">
<div v-for="(c, index) of authors" class="flex gap-2 items-center">
<img :src="`${c.github}.png`" class="w-8 h-8 rounded-full" />
<a :href="c.github">{{ c.name }}</a>
<span v-if="index < authors.length - 1"> </span>
</div>
</div>
</template>

View File

@@ -1,6 +1,7 @@
import { type Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
import Layout from "./Layout.vue";
import Post from "./PostLayout.vue";
import { loadProgress } from "./composables/nprogress";
import "./style.css";
import "uno.css";
@@ -8,7 +9,8 @@ import "uno.css";
export default {
extends: DefaultTheme,
Layout,
enhanceApp({ router }) {
enhanceApp({ router, app }) {
app.component("Post", Post);
loadProgress(router);
},
} satisfies Theme;

View File

@@ -0,0 +1,30 @@
import { createContentLoader, type ContentData } from "vitepress";
import { groupBy } from "../utils";
interface Post {
title: string;
url: string;
date: string;
}
type Dictionary = ReturnType<typeof transformRawPosts>;
declare const data: Dictionary;
export { data };
function transformRawPosts(rawPosts: ContentData[]): Record<string, Post[]> {
const posts: Post[] = rawPosts
.map(({ url, frontmatter }) => ({
title: frontmatter.title,
url,
date: (frontmatter.date as Date).toISOString().slice(0, 10),
}))
.sort((a, b) => b.date.localeCompare(a.date));
return groupBy(posts, (post) => post.date.slice(0, 4));
}
export default createContentLoader("posts/*.md", {
includeSrc: true,
transform: (raw) => transformRawPosts(raw),
});

9
.vitepress/utils.ts Normal file
View File

@@ -0,0 +1,9 @@
export function groupBy<T, K extends keyof any>(arr: T[], key: (i: T) => K): Record<K, T[]> {
return arr.reduce(
(groups, item) => {
(groups[key(item)] ||= []).push(item);
return groups;
},
{} as Record<K, T[]>,
);
}