From 99ce2ce8b43c62612a49697c72aa8fc18352736f Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Mon, 20 Jul 2026 15:25:47 +0800 Subject: [PATCH] fix duplicate documentation homepage content --- ...026-07-13-documentation-site-projection.md | 2 ++ scripts/project-doc-site.spec.ts | 32 ++++++++++++++++++- scripts/project-doc-site.ts | 22 ++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md b/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md index 1ddd8fabde..b3bdbb0c1b 100644 --- a/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md +++ b/.agents/notes/implemented/process/2026-07-13-documentation-site-projection.md @@ -14,6 +14,8 @@ Canonical Markdown remains in the repository tier that owns it. Product-facing g `scripts/project-doc-site.ts` projects the manifest into the ignored `website/.generated/` directory before VitePress starts or builds. The generated tree follows public routes so VitePress navigation, locale detection, and local search share the same route vocabulary. Each page receives an `editSource` frontmatter field pointing to its canonical repository file; the edit-link callback reads only that page data, so public URLs remain independent of the source layout. +Locale home projections retain only the canonical YAML frontmatter. The repository-facing body can keep its H1 and bilingual source links, while the VitePress home theme owns the rendered hero and features and the site navigation owns locale switching. + The projector parses Markdown links without reserializing the document. A link to another published source becomes a site-relative route; a link to an unpublished repository file becomes a GitHub source link; a repository image becomes a raw GitHub URL. Missing relative targets fail projection. Unit tests pin these transformations, and `docs:check` runs the projector tests plus a production VitePress build as part of `doc-sync` and the parallel documentation gates. Mermaid renders the canonical diagrams. The website workspace explicitly declares the five packages that `vitepress-plugin-mermaid` asks Vite to prebundle because pnpm's strict dependency isolation otherwise makes those transitive packages unavailable to the local development server; Knip records this runtime-only use as an intentional dependency exception. diff --git a/scripts/project-doc-site.spec.ts b/scripts/project-doc-site.spec.ts index 19417d5d99..7378152ac5 100644 --- a/scripts/project-doc-site.spec.ts +++ b/scripts/project-doc-site.spec.ts @@ -5,7 +5,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { docsPages, type DocsPage } from '../website/docs.ts' -import { addProjectionFrontmatter, rewriteMarkdown } from './project-doc-site.ts' +import { addProjectionFrontmatter, projectedPageContent, rewriteMarkdown } from './project-doc-site.ts' const roots: string[] = [] @@ -175,3 +175,33 @@ describe('addProjectionFrontmatter', () => { ) }) }) + +describe('projectedPageContent', () => { + const page = (sidebar: DocsPage['sidebar']): DocsPage => ({ + locale: 'root', + contentLocale: 'zh-CN', + source: 'docs/index.zh.md', + route: 'index.md', + label: 'Home', + sidebar, + section: 'Home', + order: 0, + }) + + it('omits the source-only body from locale home pages', () => { + expect(projectedPageContent( + '---\nlayout: home\nhero:\n name: Harness\n---\n\n# Harness\n\n[English](index.md) | 中文\n', + page(null), + )).toBe('---\nlayout: home\nhero:\n name: Harness\n---\n') + }) + + it('keeps the full body for ordinary pages', () => { + const markdown = '---\ntitle: Guide\n---\n\n# Guide\n' + expect(projectedPageContent(markdown, page('zh-guide'))).toBe(markdown) + }) + + it('rejects a locale home source without frontmatter', () => { + expect(() => projectedPageContent('# Harness\n', page(null))) + .toThrow('locale home source "docs/index.zh.md" must start with YAML frontmatter') + }) +}) diff --git a/scripts/project-doc-site.ts b/scripts/project-doc-site.ts index 5c43e6f46a..8d68aac7a6 100644 --- a/scripts/project-doc-site.ts +++ b/scripts/project-doc-site.ts @@ -268,6 +268,26 @@ export function addProjectionFrontmatter(markdown: string, sourcePath: string): return `---\n${field}\n---\n\n${markdown}` } +/** + * Select the Markdown rendered for one published page. + * + * @param markdown Rewritten canonical Markdown content. + * @param page Publication manifest entry for the content. + * @returns Full Markdown for ordinary pages or frontmatter-only Markdown for a locale home page. + */ +export function projectedPageContent(markdown: string, page: DocsPage): string { + if (page.sidebar !== null) return markdown + if (!markdown.startsWith('---\n')) { + throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} must start with YAML frontmatter.`) + } + const closingDelimiter = '\n---\n' + const closing = markdown.indexOf(closingDelimiter, 4) + if (closing === -1) { + throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} has unclosed YAML frontmatter.`) + } + return markdown.slice(0, closing + closingDelimiter.length) +} + /** Canonical Markdown files watched by the local VitePress dev server. */ export function docsSourceFiles(): string[] { return [...new Set(docsPages.map(page => resolve(root, page.source)))] @@ -297,6 +317,6 @@ export function projectDocs(): void { repoRoot: root, repositoryRef, }) - writeFileSync(output, addProjectionFrontmatter(projected, page.source)) + writeFileSync(output, addProjectionFrontmatter(projectedPageContent(projected, page), page.source)) } }