Skip to content

Commit

Permalink
feat(site/blog): use next-mdx-remote create mdx
Browse files Browse the repository at this point in the history
  • Loading branch information
qhanw committed Oct 11, 2023
1 parent 413d546 commit e1cef6d
Show file tree
Hide file tree
Showing 12 changed files with 1,007 additions and 203 deletions.
36 changes: 36 additions & 0 deletions site/blog/app/posts/[slug]/MDXContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { MDXRemote, MDXRemoteProps } from "next-mdx-remote/rsc";

import remarkToc from "remark-toc";
import remarkGfm from "remark-gfm";

import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypePrettyCode from "rehype-pretty-code";

// mdx components
import Button from "@/posts-assets/mdx/next-mdx/button";

const options: MDXRemoteProps["options"] = {
mdxOptions: {
remarkPlugins: [[remarkToc, { maxDepth: 4 }], remarkGfm],
rehypePlugins: [
rehypeSlug,
rehypeAutolinkHeadings,

// @ts-ignore
[rehypePrettyCode, { theme: "nord" }],
],
},
};

export default function MDXContent(props: Pick<MDXRemoteProps, "source">) {
return (
<article className="fade-in-up-content prose prose-gray">
<MDXRemote
source={props.source}
components={{ Button }}
options={options}
/>
</article>
);
}
74 changes: 34 additions & 40 deletions site/blog/app/posts/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,41 @@
import { Metadata, ResolvingMetadata } from "next";
import Link from "next/link";

import { remark } from "remark";
// import { remark } from "remark";
// import remarkToc from "remark-toc";
// import remarkGfm from "remark-gfm";
// import remarkRehype from "remark-rehype";

import remarkToc from "remark-toc";
import remarkGfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import remarkReadTime from "remark-reading-time";

import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeStringify from "rehype-stringify";
// import rehypeSlug from "rehype-slug";
// import rehypeAutolinkHeadings from "rehype-autolink-headings";
// import rehypePrettyCode from "rehype-pretty-code";
// import rehypeStringify from "rehype-stringify";

import DateFormat from "@/app/components/DateFormat";

import seo from "@/utils/seo";
import { getPostBySlug, getAllPosts } from "@/lib/posts";

import MDXContent from "./MDXContent";

import "./styles.scss";

type Props = {
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
};

type ReadingTime = {
text: string;
minutes: number;
time: number;
words: number;
};

type Meta = { readingTime: ReadingTime; [propName: string]: any };

async function getPost(params: Props["params"]) {
const post = getPostBySlug(params.slug);
const markdown = await remark()
.use(remarkToc, { maxDepth: 4 })
.use(remarkReadTime, {})
.use(remarkGfm)
.use(remarkRehype)
.use(rehypeSlug)
.use(rehypeAutolinkHeadings)
.use(rehypePrettyCode, { theme: "nord" })
.use(rehypeStringify)
.process(post.content || "");
// const markdown = await remark()
// .use(remarkToc, { maxDepth: 4 })
// .use(remarkGfm)
// .use(remarkRehype)
// .use(rehypeSlug)
// .use(rehypeAutolinkHeadings)
// .use(rehypePrettyCode, { theme: "nord" })
// .use(rehypeStringify)
// .process(post.content || "");

const posts = getAllPosts();
const idx = posts.findIndex((c) => c.slug === params.slug);
Expand All @@ -54,9 +44,10 @@ async function getPost(params: Props["params"]) {
const prev = posts[idx - 1];

return {
post: { ...post, meta: markdown.data as Meta, html: markdown.toString() },
prev: prev ? { title: prev.frontmatter.title, slug: prev.slug } : null,
next: next ? { title: next.frontmatter.title, slug: next.slug } : null,
// post: { ...post, html: markdown.toString() },
post,
prev: prev ? { title: prev.meta.title, slug: prev.slug } : null,
next: next ? { title: next.meta.title, slug: next.slug } : null,
};
}

Expand All @@ -74,8 +65,8 @@ export async function generateMetadata(
): Promise<Metadata> {
const { post } = await getPost(params);
return seo({
title: post?.frontmatter?.title || "",
description: post?.frontmatter?.description || post?.excerpt || "",
title: post?.meta?.title || "",
description: post?.meta?.description || post?.excerpt || "",
});
}

Expand All @@ -90,29 +81,32 @@ export default async ({ params }: Props) => {
return (
<>
<header className="mb-8 prose prose-gray">
<h1 className="slide-enter-50">{post.frontmatter.title}</h1>
<h1 className="slide-enter-50">{post.meta.title}</h1>

<div className="slide-enter-50 opacity-50 -mt-2 flex items-center text-sm">
<time className="inline-flex items-center">
<span className="i-heroicons:calendar mr-1 w-4 h-4 text-brand" />
<DateFormat value={post.frontmatter?.date} />
<DateFormat value={post.meta?.date} />
</time>
{/* <span className="mx-2 w-0.5 h-0.5 bg-gray-500" /> */}
<time className="inline-flex items-center ml-2">
<span className="i-heroicons:clock mr-1 w-4 h-4 text-brand" />
阅读
{Math.round(post.meta?.readingTime?.minutes!)}
{Math.ceil(post.meta?.readingTime?.minutes!)}
分钟
</time>
</div>
</header>
<article

{/* <article
className="fade-in-up-content prose prose-gray"
dangerouslySetInnerHTML={{ __html: post.html }}
/>
/> */}
<MDXContent source={post.content} />

<div className="text-sm text-right text-gray-600">
最近修改时间:
<DateFormat value={post.frontmatter.lastModified} />
<DateFormat value={post.meta.lastModified} />
</div>
<div className="flex my-12 text-sm gap-4">
<span className="w-1/2 overflow-hidden flex">
Expand Down
16 changes: 8 additions & 8 deletions site/blog/app/posts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,28 @@ export default async function Posts() {
Total {posts?.length} Posts
</div>
<div className="prose grid gap-9 m-auto">
{posts?.map((node: any) => (
{posts?.map((post: any) => (
<Link
href={`/posts/${node.slug}`}
href={`/posts/${post.slug}`}
className="group font-normal overflow-hidden cursor-pointer no-underline transition fade-in-up "
key={node.slug}
key={post.slug}
>
<div className="text-xl text-gray-600 group-hover:text-brand truncate ease-in duration-300">
{node.frontmatter?.title}
{post.meta?.title}
</div>
<div className="text-gray-400 text-sm leading-none flex items-center">
<time className="my-3 inline-flex items-center">
<span className="i-heroicons:calendar mr-1 w-4 h-4 text-brand" />
<DateFormat value={node.frontmatter?.date} short />
<DateFormat value={post.meta?.date} short />
</time>
<span className="mx-2 w-0.5 h-0.5 bg-gray-400" />
{/* {node.frontmatter.category} */}
{node.frontmatter?.tags.map((tag: string) => (
{/* {post.meta.category} */}
{post.meta?.tags.map((tag: string) => (
<PostLabel title={tag} key={tag} />
))}
</div>

<div className="text-gray-500 line-clamp-3">{node.excerpt}</div>
<div className="text-gray-500 line-clamp-3">{post.excerpt}</div>
</Link>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion site/blog/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// 转换为博客的 sitemap
const maps: MetadataRoute.Sitemap = posts.map((post) => ({
url: `https://qhan.wang/posts/${post.slug}`,
lastModified: post.frontmatter.lastModified,
lastModified: post.meta.lastModified,
changeFrequency: "weekly",
priority: 0.6,
}));
Expand Down
40 changes: 27 additions & 13 deletions site/blog/lib/posts.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,57 @@
import matter from "gray-matter";
import fs from "fs";
import { join } from "path";

import matter from "gray-matter";
import readingTime from "reading-time";

import { getGitLastUpdatedTimeStamp } from "./utils";

const postsDir = join(process.cwd(), "posts");

type Frontmatter = {
title?: string;
description?: string;
tags?: string[];
type ReadingTime = {
text: string;
minutes: number;
time: number;
words: number;
};

type MetaData = {
title: string;
date: Date;
category: string;
tags?: string[];
description?: string;
lastModified?: Date;
readingTime?: ReadingTime;
draft?: boolean;
};

export function getPostBySlug(slug: string) {
const realSlug = slug.replace(/\.md$/, "");

const fullPath = join(postsDir, `${realSlug}.md`);
const fileContents = fs.readFileSync(fullPath, "utf8");

const lastModified = getGitLastUpdatedTimeStamp(fullPath);
const fileContents = fs.readFileSync(fullPath, "utf8");

const { data, content, excerpt } = matter(fileContents, {
excerpt: true,
});

const frontmatter = { ...data, lastModified } as Frontmatter;
const lastModified = getGitLastUpdatedTimeStamp(fullPath);
const readTime = readingTime(content);

const meta = { ...data, readingTime: readTime, lastModified } as MetaData;

return { slug: realSlug, frontmatter, content, excerpt };
return { slug: realSlug, meta, content, excerpt };
}

export function getAllPosts() {
const slugs = fs.readdirSync(postsDir);

const posts = slugs
.map((slug) => getPostBySlug(slug))
// 排除草稿文件
.filter((c) => !/\.draft$/.test(c.slug));
.filter((c) => !c.meta.draft);

return posts.sort((a, b) =>
a.frontmatter.date < b.frontmatter.date ? 1 : -1
);
return posts.sort((a, b) => +b.meta.date - +a.meta.date);
}
5 changes: 3 additions & 2 deletions site/blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
"dayjs": "^1.11.10",
"gray-matter": "^4.0.3",
"next": "13.5.4",
"next-mdx-remote": "^4.4.1",
"react": "18.2.0",
"react-content-loader": "^6.2.1",
"react-dom": "18.2.0",
"reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.0.0",
"rehype-pretty-code": "^0.10.1",
"rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.0",
"remark": "^15.0.1",
"remark-gfm": "^4.0.0",
"remark-reading-time": "^2.0.1",
"remark-gfm": "^3.0.1",
"remark-rehype": "^11.0.0",
"remark-toc": "^9.0.0",
"shiki": "^0.14.4"
Expand Down
Loading

1 comment on commit e1cef6d

@vercel
Copy link

@vercel vercel bot commented on e1cef6d Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

qhan – ./

qhan.wang
qhan-git-main-qhanw.vercel.app
qhan-qhanw.vercel.app

Please sign in to comment.