Files
2026-10-08 11:09:37 +08:00

72 lines
1.9 KiB
TypeScript

import "server-only";
import { compileMDX } from "next-mdx-remote/rsc";
import rehypePrettyCode, {
type Options as PrettyCodeOptions,
} from "rehype-pretty-code";
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import remarkGfm from "remark-gfm";
import { mdxComponents } from "@/components/mdx/mdx-components";
/**
* MDX 编译管线。
*
* 插件顺序(rehype 在 remark 之后执行):
* 1. remark-gfm → 表格、任务列表、删除线
* 2. rehype-slug → 为标题生成 id(TOC 锚点依赖它)
* 3. rehype-autolink-headings → 标题旁生成可点击的 # 锚点
* 4. rehype-pretty-code → 基于 Shiki 的语法高亮 + 行号/高亮行
*
* Shiki 在构建期完成高亮,输出静态 HTML,运行时零 JS 开销。
*/
/** 代码高亮主题:深色 surface-code (#1c1c1e) 上对比度良好 */
const CODE_THEME = "github-dark-default";
const prettyCodeOptions: PrettyCodeOptions = {
theme: CODE_THEME,
// 保留默认的 pre/code 结构,样式由 globals.css 的 .prose-blog 控制
keepBackground: true,
defaultLang: "plaintext",
};
export interface CompiledMdx {
content: React.ReactElement;
}
/** 编译 MDX 正文为可渲染的 React 元素树 */
export async function compilePostMdx(source: string): Promise<CompiledMdx> {
const { content } = await compileMDX({
source,
components: mdxComponents,
options: {
parseFrontmatter: false,
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: "append",
properties: {
className: "heading-anchor",
ariaLabel: "锚点链接",
tabIndex: -1,
},
content: {
type: "text",
value: "#",
},
},
],
[rehypePrettyCode, prettyCodeOptions],
],
},
},
});
return { content };
}