75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import Image from "next/image";
|
||
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
||
import { Callout } from "./callout";
|
||
import { CodeBlock } from "./code-block";
|
||
|
||
/**
|
||
* MDX 组件映射。
|
||
*
|
||
* 通过替换原生元素,把 DESIGN.md 的排版规则与可访问性要求统一注入正文:
|
||
* - img → next/image(图片优化 + 保留 alt)
|
||
* - pre → 带语言标签与复制按钮的代码块
|
||
* - a → 外链自动补 rel/target
|
||
*
|
||
* 类型说明:这里不使用 `mdx/types` 的 MDXComponents,因为它依赖可选安装的
|
||
* @types/mdx。改为按组件签名手写映射类型,避免引入额外类型包。
|
||
*/
|
||
|
||
type MdxImageProps = ComponentPropsWithoutRef<"img">;
|
||
type MdxAnchorProps = ComponentPropsWithoutRef<"a"> & { children?: ReactNode };
|
||
|
||
/**
|
||
* MDX 组件映射类型。
|
||
*
|
||
* `next-mdx-remote` 内部使用 `MDXComponents`(带字符串索引签名),
|
||
* 因此这里显式声明索引签名,保证类型兼容的同时仍然约束了我们实际用到的键。
|
||
*/
|
||
export interface MdxComponentMap {
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
[key: string]: any;
|
||
img: (props: MdxImageProps) => ReactNode;
|
||
a: (props: MdxAnchorProps) => ReactNode;
|
||
pre: typeof CodeBlock;
|
||
Callout: typeof Callout;
|
||
}
|
||
|
||
export const mdxComponents: MdxComponentMap = {
|
||
// 文章内图片改用 next/image,alt 缺失时降级为空字符串(装饰性图片)
|
||
img: ({ src, alt, width, height }) => {
|
||
if (typeof src !== "string") {
|
||
return null;
|
||
}
|
||
|
||
return (
|
||
<Image
|
||
src={src}
|
||
alt={alt ?? ""}
|
||
width={typeof width === "number" ? width : 1200}
|
||
height={typeof height === "number" ? height : 630}
|
||
className="rounded-lg border border-hairline"
|
||
sizes="(max-width: 768px) 100vw, 720px"
|
||
/>
|
||
);
|
||
},
|
||
|
||
// 外链统一加上安全属性;站内锚点(# 开头)与相对路径保持原样
|
||
a: ({ href, children, ...props }) => {
|
||
const isExternal = typeof href === "string" && /^https?:\/\//.test(href);
|
||
|
||
return (
|
||
<a
|
||
href={href}
|
||
{...(isExternal
|
||
? { target: "_blank", rel: "noopener noreferrer" }
|
||
: {})}
|
||
{...props}
|
||
>
|
||
{children}
|
||
</a>
|
||
);
|
||
},
|
||
|
||
pre: CodeBlock,
|
||
Callout,
|
||
};
|