diff options
| author | schererleander <leander@schererleander.de> | 2025-06-25 01:28:12 +0200 |
|---|---|---|
| committer | schererleander <leander@schererleander.de> | 2025-06-25 01:28:12 +0200 |
| commit | 3a7cb097dfcf0672204fff2a3409a1817a87e5fa (patch) | |
| tree | 494cb0323d973842c4ce903f526facef9c091e43 /src | |
| parent | e6c8d4ea15ea77b08a2349ec24b11c95b634dba5 (diff) | |
swap gray-matter for vite-plugin-markdown
Diffstat (limited to 'src')
| -rw-r--r-- | src/pages/Blog.tsx | 38 | ||||
| -rw-r--r-- | src/pages/Post.tsx | 74 | ||||
| -rw-r--r-- | src/vite-env.d.ts | 12 |
3 files changed, 56 insertions, 68 deletions
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx index 5db87c8..fa9b609 100644 --- a/src/pages/Blog.tsx +++ b/src/pages/Blog.tsx @@ -1,5 +1,3 @@ -import { useMemo } from "react"; -import matter from "gray-matter"; import { Link } from "react-router-dom"; import CardLink from "../components/CardLink"; @@ -11,40 +9,36 @@ interface PostMeta { cover?: string; } -export default function Blog() { - const posts = useMemo<PostMeta[]>(() => { - const files = import.meta.glob("../blog/*.md", { - eager: true, - query: "?raw", - import: "default", - }) as Record<string, string>; +const postFiles = import.meta.glob("../blog/*.md", { eager: true }) as Record< + string, + { attributes: Omit<PostMeta, "slug"> } +>; - return Object.entries(files) - .map(([path, raw]) => { - const { data } = matter(raw); - const slug = path.split("/").pop()!.replace(".md", ""); - return { slug, ...data } as PostMeta; - }) - .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - }, []); +const posts: PostMeta[] = Object.entries(postFiles) + .map(([path, mod]) => ({ + slug: path.split("/").pop()!.replace(".md", ""), + ...(mod.attributes as Omit<PostMeta, "slug">), + })) + .sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ); +export default function Blog() { return ( <section className="container mx-auto px-4 py-10"> <h1 className="text-4xl font-bold mb-8">Blog</h1> <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> {posts.map((post) => ( - /* 1) Link für internes Routing */ - /* 2) CardLink bekommt genau deine Prop-Namen */ <Link key={post.slug} to={`/blog/${post.slug}`} className="block"> <CardLink title={post.title} - body={post.excerpt} // <— body statt description - imgSrc={post.cover} // <— imgSrc statt image + body={post.excerpt} + imgSrc={post.cover} /> </Link> ))} </div> </section> ); -} +}
\ No newline at end of file diff --git a/src/pages/Post.tsx b/src/pages/Post.tsx index c17873c..5c0f0ca 100644 --- a/src/pages/Post.tsx +++ b/src/pages/Post.tsx @@ -1,40 +1,36 @@ -import { useEffect, useState } from "react"; import { useParams, Link } from "react-router-dom"; -import matter from "gray-matter"; -import ReactMarkdown from "react-markdown"; import CodeSnippet from "../components/CodeSnippet"; import LinkWithIcon from "../components/LinkWithIcon"; import NotFoundPage from "./404Page"; -interface PostMeta { - title: string; - date: string; - cover?: string; +interface PostFile { + attributes: { + title: string; + date: string; + cover?: string; + }; + markdown: string; + ReactComponent: React.FC<any>; } +const posts = import.meta.glob("../blog/*.md", { + eager: true, +}) as Record<string, PostFile>; + export default function Post() { const { slug } = useParams<{ slug: string }>(); - const [meta, setMeta] = useState<PostMeta | null>(null); - const [content, setContent] = useState(""); - const [notFound, setNotFound] = useState(false); - useEffect(() => { - import(`../blog/${slug}.md?raw`) - .then((m) => { - const { data, content } = matter(m.default); - setMeta(data as PostMeta); - setContent(content); - }) - .catch(() => setNotFound(true)); - }, [slug]); + const match = Object.entries(posts).find(([path]) => + path.endsWith(`${slug}.md`) + ); - if (!meta) return null; - if (notFound) return <NotFoundPage />; + if (!match) return <NotFoundPage />; + const { attributes: meta, ReactComponent: Content } = match[1]; return ( <article className="prose prose-zinc dark:prose-invert mx-auto px-4 py-10"> <Link to="/blog" className="no-underline"> - ← Back + ← Back </Link> {meta.cover && ( @@ -49,27 +45,21 @@ export default function Post() { <p className="text-sm text-zinc-500 mb-8"> {new Date(meta.date).toLocaleDateString("de-DE")} </p> - - <article className="markdown-body"> - <ReactMarkdown - components={{ - code({ children }) { - const text = String(children).replace(/\n$/, ""); - return <CodeSnippet code={text} initialLines={5} />; - }, - a({ href = "", children, ...props }) { - return ( - <LinkWithIcon href={href} {...props}> - {children} - </LinkWithIcon> - ); - }, - }} - > - {content} - </ReactMarkdown> - </article> + {/* The Markdown, already a React component */} + <Content + components={{ + code({ children }: any) { + return ( + <CodeSnippet + code={String(children).replace(/\n$/, "")} + initialLines={5} + /> + ); + }, + a: LinkWithIcon, + }} + /> </article> ); }
\ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 913581e..0eb4024 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,5 +1,9 @@ /// <reference types="vite/client" /> -declare module '*.md?raw' { - const content: string; - export default content; -} +declare module "*.md" { + import { FC } from "react"; + const attributes: Record<string, any>; + const markdown: string; + const ReactComponent: FC; + export { attributes, markdown, ReactComponent }; + export default ReactComponent; +}
\ No newline at end of file |
