From c9136eef716aa8d32e0301189fbf5a511ff62d5d Mon Sep 17 00:00:00 2001 From: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> Date: Sun, 11 Oct 2026 17:11:21 +0800 Subject: [PATCH] fix(docs): validate translated README website destinations Use the website language inventory when comparing README links. Accept the matching localized page while retaining checks for wrong locales, changed destinations, fragments and query parameters. --- scripts/lib/docs-facts.mjs | 25 ++++++++++-- tests/boundary/cli/productCatalog.test.ts | 47 ++++++++++++++++------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/scripts/lib/docs-facts.mjs b/scripts/lib/docs-facts.mjs index c9c46d594..26d9a4ac3 100644 --- a/scripts/lib/docs-facts.mjs +++ b/scripts/lib/docs-facts.mjs @@ -1,7 +1,7 @@ import { readFile, readdir, stat } from "node:fs/promises"; import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; -const readmeLinkTargets = (content) => +const readmeLinkTargets = (content, language = "en") => [...content.matchAll(/\]\(([^)\s]+)\)/gu)] .map((match) => match[1] ?? "") .filter( @@ -9,6 +9,18 @@ const readmeLinkTargets = (content) => !target.startsWith("#") && !/^README(?:_[A-Za-z0-9-]+)?\.md$/u.test(target), ) + .map((target) => { + if (language === "en" || !URL.canParse(target)) return target; + const url = new URL(target); + const prefix = `/${language}/`; + if ( + url.origin !== "https://rea.tools" || + !url.pathname.startsWith(prefix) + ) + return target; + url.pathname = url.pathname.slice(prefix.length - 1); + return url.href; + }) .sort(); const requireText = (issues, path, content, expected) => { @@ -83,10 +95,17 @@ export const documentationFactIssues = async (root, catalog) => { } } const canonicalLinks = readmeLinkTargets(readmes.get("README.md") ?? ""); + const languageInventory = JSON.parse( + await readFile(join(root, "website/locales/languages.json"), "utf8"), + ); + const readmeLanguages = new Map( + languageInventory.map(({ readme, code }) => [readme, code]), + ); for (const path of readmePaths.filter((path) => path !== "README.md")) if ( - JSON.stringify(readmeLinkTargets(readmes.get(path) ?? "")) !== - JSON.stringify(canonicalLinks) + JSON.stringify( + readmeLinkTargets(readmes.get(path) ?? "", readmeLanguages.get(path)), + ) !== JSON.stringify(canonicalLinks) ) issues.push(`${path}: documentation links differ from README.md`); diff --git a/tests/boundary/cli/productCatalog.test.ts b/tests/boundary/cli/productCatalog.test.ts index 3f35fc830..cbdabd45c 100644 --- a/tests/boundary/cli/productCatalog.test.ts +++ b/tests/boundary/cli/productCatalog.test.ts @@ -173,6 +173,7 @@ describe("canonical product catalog drift", () => { "README_ja.md", "README_ko.md", "README_ar.md", + "website/locales/languages.json", "docs/installation.md", "AGENTS.md", ".github/pull_request_template.md", @@ -187,22 +188,40 @@ describe("canonical product catalog drift", () => { }); const translatedPath = join(directory, "README_zh.md"); const translated = await readFile(translatedPath, "utf8"); - await writeFile( - translatedPath, - translated.replace( + const catalog = await createProductCatalog(root); + await expect(documentationFactIssues(directory, catalog)).resolves.toEqual( + [], + ); + for (const [original, changed] of [ + [ "(docs/installation.md#supported-agents)", "(docs/installation.md#missing-agents)", - ), - "utf8", - ); - - const issues = await documentationFactIssues( - directory, - await createProductCatalog(root), - ); - expect(issues).toContain( - "README_zh.md: documentation links differ from README.md", - ); + ], + [ + "https://rea.tools/zh-CN/guides/native/", + "https://rea.tools/zh-CN/missing/", + ], + [ + "https://rea.tools/zh-CN/guides/native/", + "https://rea.tools/zh-TW/guides/native/", + ], + [ + "https://rea.tools/zh-CN/guides/native/", + "https://rea.tools/zh-CN/guides/native/?other=1", + ], + ]) { + expect(translated).toContain(original); + await writeFile( + translatedPath, + translated.replace(original, changed), + "utf8", + ); + await expect( + documentationFactIssues(directory, catalog), + ).resolves.toEqual([ + "README_zh.md: documentation links differ from README.md", + ]); + } }); it("fails check mode without rewriting a stale generated artifact", async () => {