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.
This commit is contained in:
N0zoM1z0 2026-10-11 17:11:21 +08:00 committed by Ysopwnable
parent d981a58d7f
commit c9136eef71
2 changed files with 55 additions and 17 deletions

View File

@ -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`);

View File

@ -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 () => {