refactor(docs): 统一 /docs、CommunityShare、Leetcode 三处索引为 <SectionIndex>(取代 #288 #290)#292
Conversation
合并 #288 + #290 + app/docs/CommunityShare/Leetcode/index.mdx 里原先三份 各自实现的"列目录子节点"逻辑,改成一个 server component。 ## 为什么要合并 原本三处各自实现: - /docs 根路由(PR #290 draft)—— 读 pageTree.children - CommunityShare/index.mdx(PR #288 draft)—— 读 getPages() 过滤 path - CommunityShare/Leetcode/index.mdx —— 内联 MDX 里 source.getPages().filter().map() drift 问题:排序、英文过滤、fallback URL 三份逻辑各走各的;更严重的是 PR #288 里对"没 index.mdx 的子目录"硬拼 /docs/CommunityShare/<dir> 会 404 (Copilot CR 指出),和 PR #290 修 /docs 404 是同一个根因:Next [...slug] 不匹配空 slug,folder 没 index 就意味着 /docs/X 没 route。 ## <SectionIndex root?> - 走 source.pageTree(不是 getPages),fumadocs 已经把 folder+index 关系建好了,不用自己从扁平 page 列表反推 - root 接 "CommunityShare" / "CommunityShare/Leetcode" 这种相对路径, 不传就是从 pageTree 根开始(给 /docs landing 用) - URL 永不硬拼:folder 有 index 走 index.url;没 index 递归找子树第一个 page 的 url 作为 fallback(直接修掉 CR 那个 404 bug) - 英文翻译版(URL 末段 .en)过滤不进列表;语言切换仍由 [...slug] 的 cookie fallback 负责 - 统一 fumadocs <Cards>/<Card> 视觉 ## 本地验证 - /docs → 5 张卡片,全部 200 - /docs/CommunityShare → 8 张卡片,全部 200(包括原先会 404 的 Language/ Life/Personal-Study-Notes/RAG 四个没 index 的分类,现在点进去是子目录里 第一篇 page,不再死链) - /docs/CommunityShare/Leetcode → 49 张卡片,0 个 .en 泄漏 ## 取代关系 - 关闭 PR #288(CommunityShareIndex 专用实现,有 404 bug) - 关闭 PR #290(/docs landing 单独实现) - 本 PR 一并覆盖,继续承担解决 #110 的责任 Co-authored-by: LynPtl <194795025+LynPtl@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
本 PR 将 /docs landing、CommunityShare 分区首页、CommunityShare/Leetcode 分区首页三处“列出子节点目录/页面”的索引逻辑,统一收敛为一个可复用的 Server Component:<SectionIndex root?>,避免三份实现产生 drift,并修复“无 index 的目录被硬拼 URL 导致 404”的同源问题。
Changes:
- 新增通用索引组件
SectionIndex:基于source.pageTree生成 Cards 列表,并为无 index 的 folder 递归兜底到子树第一个可用 page URL - 新增
/docs根路由app/docs/page.tsx,使用<SectionIndex />生成 landing(解决/docs404) - 将
CommunityShare与Leetcode的 index.mdx 改为直接使用<SectionIndex root="...">,移除内联/硬编码索引实现
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| app/docs/page.tsx | 新增 /docs landing 页,读取 locale cookie 并渲染 <SectionIndex /> |
| app/docs/CommunityShare/index.mdx | 用 <SectionIndex root="CommunityShare" /> 替换手写分类列表 |
| app/docs/CommunityShare/Leetcode/index.mdx | 用 <SectionIndex root="CommunityShare/Leetcode" /> 替换内联 getPages().filter().map() |
| app/components/docs/SectionIndex.tsx | 新增通用索引组件:从 pageTree 构建 Cards、处理 folder 无 index 的 fallback |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** 判定页面是英文翻译版(不应出现在索引里) */ | ||
| function isEnglishVariant(page: PageNode): boolean { | ||
| // PageTree 节点 name 可能是 string | ReactNode;英文变体的 frontmatter.lang === "en" | ||
| // 但 pageTree 级别看不到 frontmatter,只能靠 URL 末段后缀兜底 | ||
| const urlSlug = page.url.split("/").pop() ?? ""; | ||
| return urlSlug.endsWith(".en"); | ||
| } |
There was a problem hiding this comment.
isEnglishVariant 只过滤了 URL 末段以 .en 结尾的翻译页,但当前 docs 的语言变体规则在 [...slug]/page.tsx 里是 ${slug}.${locale}(支持 zh / en)。在 Leetcode 目录里也能看到不少 *.zh.md 文件名;如果这些页被纳入 pageTree,会导致 .zh 翻译页仍然出现在索引卡片中、产生重复/非 canonical 链接。建议把这里的过滤改成“过滤所有语言后缀页”(例如同时排除 .en 和 .zh),并相应调整函数命名/注释。
| } | ||
| // folder | ||
| const folder = node as FolderNode; | ||
| const idxUrl = folder.index?.url; |
There was a problem hiding this comment.
这里对 folder 的链接优先直接使用 folder.index?.url,但没有对 folder.index 做语言后缀过滤;如果某个目录只有 index.en/ index.zh 这类变体页(或 index 被解析为带后缀的 URL),就会把翻译页作为卡片链接暴露出来。建议在选择 idxUrl 时也复用同一套“过滤语言后缀页”的判断(不合规时退回 findFirstPageUrl)。
| const idxUrl = folder.index?.url; | |
| const idxUrl = | |
| folder.index && !isEnglishVariant(folder.index) ? folder.index.url : null; |
|
|
||
| /** | ||
| * fumadocs 的 FolderNode.name 是 ReactNode(可能是字符串,也可能是 JSX), | ||
| * 单靠 name 匹配不稳定。这里优先用 index 页的 slug 倒数第二段反推目录名, |
There was a problem hiding this comment.
注释里写“用 index 页的 slug 倒数第二段反推目录名”,但下面实现实际取的是 folder.index.url 的最后一段(parts[parts.length - 1])。建议修正注释描述,避免后续维护者按注释误解实现。
| * 单靠 name 匹配不稳定。这里优先用 index 页的 slug 倒数第二段反推目录名, | |
| * 单靠 name 匹配不稳定。这里优先用 index 页的 slug 最后一段反推目录名, |
| async function getLocaleFromCookie(): Promise<"zh" | "en"> { | ||
| const cookieStore = await cookies(); | ||
| return cookieStore.get("locale")?.value === "en" ? "en" : "zh"; | ||
| } |
There was a problem hiding this comment.
getLocaleFromCookie 的 cookie 读取逻辑在 app/layout.tsx、i18n/request.ts、app/docs/[...slug]/page.tsx 等处已有重复实现;这里又新增一份,后续如果 locale cookie 规则变化会更容易 drift。建议抽一个共享的 server-side helper(例如 lib/locale.ts),并在这些入口统一复用。
Copilot 在 #292 提了 3 条要修的: 1) isEnglishVariant 只过滤 .en,没管 .zh —— 站点实际有 .zh.md(原文是 en 时的中文翻译), 重复链接会在索引里暴露。改成 isHideableLocaleVariant(url, canonicals):只有对应 canonical 存在时才隐藏,孤儿(只有 .en 或 .zh 单一形态的文档,共 35 + 7 篇)保留。 2) folder.index 如果本身是翻译版(理论上会有 index.en.mdx / index.zh.mdx),不能直接 当卡片 href,会暴露非 canonical URL。nodeToCard 里给 idxUrl 加同样的过滤,不合规时 退回 findFirstPageUrl。 3) folderSegmentName 注释写的"倒数第二段"但代码取的是最后一段,改掉注释。 另外按用户反馈清掉注释里的 markdown(**bold**、反引号等),代码注释又不会被渲染。
合并 #288 + #290 + Leetcode/index.mdx 三处各自实现的"列目录子节点"逻辑为一个组件。
背景
三处独立实现:
drift 风险具体:排序规则 / 英文版过滤 / 没 index 时的 fallback URL 三份逻辑各走各的。Copilot 在 #288 里还指出了一个真 bug:没 `index.mdx` 的子目录(`Language` / `Life` / `Personal-Study-Notes` / `RAG`)硬拼 `/docs/CommunityShare/
` 链接,点进去全部 404——和 PR #290 要修的 `/docs` 根路由 404 是同一个根因:Next `[...slug]` 不匹配空 slug,folder 没 index.mdx 就意味着 `/docs/X` 没任何 route。方案
一个组件 `<SectionIndex root?>`:
接入的三处
```tsx
// /docs landing
// CommunityShare 分区首页
// Leetcode 分区首页
```
本地验证
```
/docs → 200 (5 张卡片)
/docs/CommunityShare → 200 (8 张卡片)
/docs/CommunityShare/Leetcode → 200 (49 张卡片,0 个 .en 泄漏)
```
重点验证 CR 提到的 4 个没 index.mdx 的子目录链接——现在全部落到子目录里第一篇 page,200 可达:
```
/docs/CommunityShare/Language/pte-intro → 200
/docs/CommunityShare/Life/unsw-student-benefit → 200
/docs/CommunityShare/Personal-Study-Notes/Reinforcement-Learning/ppo → 200
/docs/CommunityShare/RAG/context_engineering_intro → 200
```
取代关系
Test plan