diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx index 960c092..ab699a1 100644 --- a/app/docs/[...slug]/page.tsx +++ b/app/docs/[...slug]/page.tsx @@ -18,6 +18,7 @@ import { PageFeedback } from "@/app/components/PageFeedback"; import { DocHistoryPanel } from "@/app/components/DocHistoryPanel"; import { DocShareButton } from "@/app/components/DocShareButton"; import { cookies } from "next/headers"; +import { type PageData } from "@/app/types/doc"; // Extract clean text content from MDX - no longer used on client/page side // content fetching moved to API route for performance @@ -51,7 +52,7 @@ function getPageWithLocale( return { page: originalPage, isFallback: false }; const originalLang = - (originalPage?.data as { lang?: string } | undefined)?.lang ?? null; + (originalPage?.data as PageData | undefined)?.lang ?? null; // 已经是目标语言,直接返回 if (originalLang === locale) return { page: originalPage, isFallback: false }; @@ -83,11 +84,8 @@ export default async function DocPage({ params }: Param) { // 统一通过工具函数生成 Edit 链接,内部已处理中文目录编码 const editUrl = buildDocsEditUrl(page.path); - const docIdFromPage = - (page.data as { docId?: string; frontmatter?: { docId?: string } }) - ?.docId ?? - (page.data as { docId?: string; frontmatter?: { docId?: string } }) - ?.frontmatter?.docId; + const data = page.data as PageData; + const docIdFromPage = data.docId ?? data.frontmatter?.docId; const contributorsEntry = getDocContributorsByPath(page.file.path) || diff --git a/app/sitemap.ts b/app/sitemap.ts index 85cc8db..262722b 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -24,36 +24,12 @@ import leaderboard from "@/generated/site-leaderboard.json"; // SITE_URL 由 lib/site-url.ts 统一提供(从 NEXT_PUBLIC_SITE_URL 读 + 归一化), // 这里和 app/robots.ts 共用一份,避免两边 drift。 import { SITE_URL } from "@/lib/site-url"; +import { type PageData, type DateLike } from "@/app/types/doc"; /** * 定义 `source.getPages()` 返回的单个页面对象的类型别名 */ type SourcePage = ReturnType[number]; -/** * 定义可以被解析为日期的宽松类型 - */ -type DateLike = string | number | Date | undefined | null; - -/** - * (FIX) 定义一个用于 page.data 的基础类型, - * 以避免在 isDraftOrHidden 和 extractDateFromPage 中使用 'any'。 - */ -type PageData = { - date?: DateLike; - updated?: DateLike; - updatedAt?: DateLike; - lastUpdated?: DateLike; - draft?: boolean; - hidden?: boolean; - frontmatter?: { - date?: DateLike; - updated?: DateLike; - updatedAt?: DateLike; - lastUpdated?: DateLike; - draft?: boolean; - hidden?: boolean; - }; -}; - /** * Next.js 会调用的默认导出函数,用于生成整个站点的 Sitemap。 * * @returns {MetadataRoute.Sitemap} 一个包含所有站点地图条目的数组。 diff --git a/app/types/doc.ts b/app/types/doc.ts new file mode 100644 index 0000000..557c216 --- /dev/null +++ b/app/types/doc.ts @@ -0,0 +1,46 @@ +import type { StructuredData } from "fumadocs-core/mdx-plugins"; + +/** + * 定义可以被解析为日期的宽松类型 + */ +export type DateLike = string | number | Date | undefined | null; + +/** + * 定义用于 page.data 的基础类型, + * 包含 Fumadocs 自动生成的字段以及常见的前置元数据 (frontmatter)。 + */ +export interface PageData { + title?: string; + description?: string; + date?: DateLike; + updated?: DateLike; + updatedAt?: DateLike; + lastUpdated?: DateLike; + draft?: boolean; + hidden?: boolean; + docId?: string; + lang?: string; + structuredData?: StructuredData; + load?: () => Promise<{ structuredData: StructuredData }>; + /** + * 允许访问 frontmatter 原始对象(Fumadocs 默认会将字段打平到 data 根部, + * 但部分逻辑可能仍显式访问 .frontmatter)。 + */ + frontmatter?: { + title?: string; + description?: string; + date?: DateLike; + updated?: DateLike; + updatedAt?: DateLike; + lastUpdated?: DateLike; + draft?: boolean; + hidden?: boolean; + docId?: string; + lang?: string; + [key: string]: unknown; + }; + // 故意不挂顶层 [key: string]: unknown 索引签名 —— Fumadocs 的 page.data 由 + // zod DocOut 推出,没有 index signature;如果在 PageData 上挂一个,as PageData + // 会触发 TS2352 "neither type sufficiently overlaps"。所有需要的字段都已 + // 在上面显式声明;真要拓展加新字段,往这里加显式 ? 字段,别走 escape hatch。 +} diff --git a/lib/search-index.ts b/lib/search-index.ts index f0af15f..c76198c 100644 --- a/lib/search-index.ts +++ b/lib/search-index.ts @@ -4,26 +4,16 @@ import type { AdvancedIndex } from "fumadocs-core/search/server"; import type { StructuredData } from "fumadocs-core/mdx-plugins"; import { source } from "@/lib/source"; import { basename, extname } from "path"; +import { type PageData } from "@/app/types/doc"; type Page = ReturnType[number]; -/** - * fumadocs page.data 在构建产物里的 runtime shape。 - * 老路径:structuredData 直接 inline;新路径:通过 load() 异步拉。 - */ -interface PageDataShape { - structuredData?: StructuredData; - load?: () => Promise<{ structuredData: StructuredData }>; - title?: string; - description?: string; -} - /** * 把一个 fumadocs 页面转成 Orama 索引项(复用 fumadocs-core 默认实现逻辑), * 单独抽出来是因为我们需要分片(zh / en),用 createSearchAPI 手动传 indexes。 */ export async function pageToIndex(page: Page): Promise { - const data = page.data as PageDataShape; + const data = page.data as PageData; let structuredData: StructuredData | undefined; if (data.structuredData) { @@ -52,6 +42,6 @@ export async function pageToIndex(page: Page): Promise { * 翻译版 frontmatter 会声明 `lang: "en"` 且通常 `translatedFrom: "zh"`。 */ export function isEnglishPage(page: Page): boolean { - const lang = (page.data as { lang?: string }).lang; + const lang = (page.data as PageData).lang; return lang === "en"; }