Skip to content

Commit 17aa48c

Browse files
authored
Merge pull request #323 from InvolutionHell/fix-pagedata-type-refactor-1321556333293577153
Centralize PageData type and refactor page.data casts
2 parents 1bf07ca + 0221ac9 commit 17aa48c

4 files changed

Lines changed: 54 additions & 44 deletions

File tree

app/docs/[...slug]/page.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { PageFeedback } from "@/app/components/PageFeedback";
1818
import { DocHistoryPanel } from "@/app/components/DocHistoryPanel";
1919
import { DocShareButton } from "@/app/components/DocShareButton";
2020
import { cookies } from "next/headers";
21+
import { type PageData } from "@/app/types/doc";
2122
// Extract clean text content from MDX - no longer used on client/page side
2223
// content fetching moved to API route for performance
2324

@@ -51,7 +52,7 @@ function getPageWithLocale(
5152
return { page: originalPage, isFallback: false };
5253

5354
const originalLang =
54-
(originalPage?.data as { lang?: string } | undefined)?.lang ?? null;
55+
(originalPage?.data as PageData | undefined)?.lang ?? null;
5556

5657
// 已经是目标语言,直接返回
5758
if (originalLang === locale) return { page: originalPage, isFallback: false };
@@ -83,11 +84,8 @@ export default async function DocPage({ params }: Param) {
8384

8485
// 统一通过工具函数生成 Edit 链接,内部已处理中文目录编码
8586
const editUrl = buildDocsEditUrl(page.path);
86-
const docIdFromPage =
87-
(page.data as { docId?: string; frontmatter?: { docId?: string } })
88-
?.docId ??
89-
(page.data as { docId?: string; frontmatter?: { docId?: string } })
90-
?.frontmatter?.docId;
87+
const data = page.data as PageData;
88+
const docIdFromPage = data.docId ?? data.frontmatter?.docId;
9189

9290
const contributorsEntry =
9391
getDocContributorsByPath(page.file.path) ||

app/sitemap.ts

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,12 @@ import leaderboard from "@/generated/site-leaderboard.json";
2424
// SITE_URL 由 lib/site-url.ts 统一提供(从 NEXT_PUBLIC_SITE_URL 读 + 归一化),
2525
// 这里和 app/robots.ts 共用一份,避免两边 drift。
2626
import { SITE_URL } from "@/lib/site-url";
27+
import { type PageData, type DateLike } from "@/app/types/doc";
2728

2829
/** * 定义 `source.getPages()` 返回的单个页面对象的类型别名
2930
*/
3031
type SourcePage = ReturnType<typeof source.getPages>[number];
3132

32-
/** * 定义可以被解析为日期的宽松类型
33-
*/
34-
type DateLike = string | number | Date | undefined | null;
35-
36-
/**
37-
* (FIX) 定义一个用于 page.data 的基础类型,
38-
* 以避免在 isDraftOrHidden 和 extractDateFromPage 中使用 'any'。
39-
*/
40-
type PageData = {
41-
date?: DateLike;
42-
updated?: DateLike;
43-
updatedAt?: DateLike;
44-
lastUpdated?: DateLike;
45-
draft?: boolean;
46-
hidden?: boolean;
47-
frontmatter?: {
48-
date?: DateLike;
49-
updated?: DateLike;
50-
updatedAt?: DateLike;
51-
lastUpdated?: DateLike;
52-
draft?: boolean;
53-
hidden?: boolean;
54-
};
55-
};
56-
5733
/**
5834
* Next.js 会调用的默认导出函数,用于生成整个站点的 Sitemap。
5935
* * @returns {MetadataRoute.Sitemap} 一个包含所有站点地图条目的数组。

app/types/doc.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { StructuredData } from "fumadocs-core/mdx-plugins";
2+
3+
/**
4+
* 定义可以被解析为日期的宽松类型
5+
*/
6+
export type DateLike = string | number | Date | undefined | null;
7+
8+
/**
9+
* 定义用于 page.data 的基础类型,
10+
* 包含 Fumadocs 自动生成的字段以及常见的前置元数据 (frontmatter)。
11+
*/
12+
export interface PageData {
13+
title?: string;
14+
description?: string;
15+
date?: DateLike;
16+
updated?: DateLike;
17+
updatedAt?: DateLike;
18+
lastUpdated?: DateLike;
19+
draft?: boolean;
20+
hidden?: boolean;
21+
docId?: string;
22+
lang?: string;
23+
structuredData?: StructuredData;
24+
load?: () => Promise<{ structuredData: StructuredData }>;
25+
/**
26+
* 允许访问 frontmatter 原始对象(Fumadocs 默认会将字段打平到 data 根部,
27+
* 但部分逻辑可能仍显式访问 .frontmatter)。
28+
*/
29+
frontmatter?: {
30+
title?: string;
31+
description?: string;
32+
date?: DateLike;
33+
updated?: DateLike;
34+
updatedAt?: DateLike;
35+
lastUpdated?: DateLike;
36+
draft?: boolean;
37+
hidden?: boolean;
38+
docId?: string;
39+
lang?: string;
40+
[key: string]: unknown;
41+
};
42+
// 故意不挂顶层 [key: string]: unknown 索引签名 —— Fumadocs 的 page.data 由
43+
// zod DocOut 推出,没有 index signature;如果在 PageData 上挂一个,as PageData
44+
// 会触发 TS2352 "neither type sufficiently overlaps"。所有需要的字段都已
45+
// 在上面显式声明;真要拓展加新字段,往这里加显式 ? 字段,别走 escape hatch。
46+
}

lib/search-index.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,16 @@ import type { AdvancedIndex } from "fumadocs-core/search/server";
44
import type { StructuredData } from "fumadocs-core/mdx-plugins";
55
import { source } from "@/lib/source";
66
import { basename, extname } from "path";
7+
import { type PageData } from "@/app/types/doc";
78

89
type Page = ReturnType<typeof source.getPages>[number];
910

10-
/**
11-
* fumadocs page.data 在构建产物里的 runtime shape。
12-
* 老路径:structuredData 直接 inline;新路径:通过 load() 异步拉。
13-
*/
14-
interface PageDataShape {
15-
structuredData?: StructuredData;
16-
load?: () => Promise<{ structuredData: StructuredData }>;
17-
title?: string;
18-
description?: string;
19-
}
20-
2111
/**
2212
* 把一个 fumadocs 页面转成 Orama 索引项(复用 fumadocs-core 默认实现逻辑),
2313
* 单独抽出来是因为我们需要分片(zh / en),用 createSearchAPI 手动传 indexes。
2414
*/
2515
export async function pageToIndex(page: Page): Promise<AdvancedIndex> {
26-
const data = page.data as PageDataShape;
16+
const data = page.data as PageData;
2717

2818
let structuredData: StructuredData | undefined;
2919
if (data.structuredData) {
@@ -52,6 +42,6 @@ export async function pageToIndex(page: Page): Promise<AdvancedIndex> {
5242
* 翻译版 frontmatter 会声明 `lang: "en"` 且通常 `translatedFrom: "zh"`。
5343
*/
5444
export function isEnglishPage(page: Page): boolean {
55-
const lang = (page.data as { lang?: string }).lang;
45+
const lang = (page.data as PageData).lang;
5646
return lang === "en";
5747
}

0 commit comments

Comments
 (0)