Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 71 additions & 66 deletions scripts/mintlify-post-processing/copy-to-local-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,85 +107,69 @@ function updateDocsJson(repoDir, sdkFiles) {
const docsContent = fs.readFileSync(docsJsonPath, "utf8");
const docs = JSON.parse(docsContent);

// Build the new SDK Reference groups using the new path structure
// Build the SDK Reference groups. `prefix` is "" for the default (English)
// language and "<locale>/" for every other locale. The generated reference
// mdx is English-only, but each locale gets its OWN prefixed page paths
// (e.g. es/developers/references/sdk/docs/...) pointing at English-content
// copies written into that locale's directory. UNIQUE per-locale paths are
// essential: pointing multiple locales at the same un-prefixed English path
// creates duplicate page records and makes Mintlify emit each SDK page N times
// in llms-full.txt. They also make the language switcher resolve to the
// in-locale SDK page instead of falling back to the Documentation tab.
const basePath = SDK_DOCS_TARGET_PATH;
const groupMap = new Map(); // group name -> pages array

const addToGroup = (groupName, pages) => {
if (!groupName || pages.length === 0) return;
if (!groupMap.has(groupName)) {
groupMap.set(groupName, []);
}
groupMap.get(groupName).push(...pages);
};

if (sdkFiles.functions?.length > 0 && categoryMap.functions) {
addToGroup(
categoryMap.functions,
sdkFiles.functions.map((file) => `${basePath}/functions/${file}`)
);
}

if (sdkFiles.interfaces?.length > 0 && categoryMap.interfaces) {
addToGroup(
categoryMap.interfaces,
sdkFiles.interfaces.map((file) => `${basePath}/interfaces/${file}`)
);
}

if (sdkFiles.classes?.length > 0 && categoryMap.classes) {
addToGroup(
categoryMap.classes,
sdkFiles.classes.map((file) => `${basePath}/classes/${file}`)
);
}

if (sdkFiles["type-aliases"]?.length > 0 && categoryMap["type-aliases"]) {
addToGroup(
categoryMap["type-aliases"],
sdkFiles["type-aliases"].map((file) => `${basePath}/type-aliases/${file}`)
);
}

// Convert map to array of nested groups for SDK Reference
const sdkReferencePages = Array.from(groupMap.entries()).map(
([groupName, pages]) => ({
group: groupName,
const buildSdkReferencePages = (prefix) => {
const groupMap = new Map(); // group name -> pages array
const addToGroup = (groupName, pages) => {
if (!groupName || pages.length === 0) return;
if (!groupMap.has(groupName)) groupMap.set(groupName, []);
groupMap.get(groupName).push(...pages);
};
const p = (kind, file) => `${prefix}${basePath}/${kind}/${file}`;

if (sdkFiles.functions?.length > 0 && categoryMap.functions)
addToGroup(categoryMap.functions, sdkFiles.functions.map((f) => p("functions", f)));
if (sdkFiles.interfaces?.length > 0 && categoryMap.interfaces)
addToGroup(categoryMap.interfaces, sdkFiles.interfaces.map((f) => p("interfaces", f)));
if (sdkFiles.classes?.length > 0 && categoryMap.classes)
addToGroup(categoryMap.classes, sdkFiles.classes.map((f) => p("classes", f)));
if (sdkFiles["type-aliases"]?.length > 0 && categoryMap["type-aliases"])
addToGroup(categoryMap["type-aliases"], sdkFiles["type-aliases"].map((f) => p("type-aliases", f)));

return Array.from(groupMap.entries()).map(([group, pages]) => ({
group,
expanded: true,
pages: pages.sort(), // Sort pages alphabetically within each group
})
);

console.debug(
`SDK Reference pages: ${JSON.stringify(sdkReferencePages, null, 2)}`
);
pages: pages.sort(),
}));
};

// docs.json supports three navigation shapes we've seen in the wild:
// 1. top-level tabs (legacy) navigation.tabs
// 2. top-level tabs with dropdowns/anchors navigation.tabs[].dropdowns | .anchors
// 3. i18n layout (current) navigation.languages[].tabs[]...
// The SDK reference mdx files are English-only, so for every locale we point its
// SDK Reference group at the same English paths. This is what the docs site
// effectively shows today anyway.
//
// We locate the target group by content (any group whose pages reference
// `/sdk/docs/`) rather than by tab/group name, because the surrounding labels
// are translated per locale while the "SDK" dropdown id and the page paths
// stay stable. Preserves the existing translated group label.
const tabsContainers =
docs.navigation.languages?.map((l) => l.tabs).filter(Boolean) ??
[docs.navigation.tabs].filter(Boolean);

if (tabsContainers.length === 0) {
// We iterate per language so we know each one's locale prefix, and only touch
// the SDK Reference group WITHIN that language (matched by its own prefixed
// sdk/docs paths). The translated group/subgroup labels are preserved.
const languageEntries = docs.navigation.languages
? docs.navigation.languages.map((l) => ({
prefix: l.default ? "" : `${l.language}/`,
tabs: l.tabs,
}))
: [{ prefix: "", tabs: docs.navigation.tabs }];

if (languageEntries.every((e) => !e.tabs)) {
console.error("Could not find navigation.tabs or navigation.languages in docs.json");
process.exit(1);
}

const groupReferencesSdkDocs = (group) =>
JSON.stringify(group).includes(`${basePath}/`);

let updatedCount = 0;
for (const tabs of tabsContainers) {
for (const { prefix, tabs } of languageEntries) {
if (!Array.isArray(tabs)) continue;
const localePathPrefix = `${prefix}${basePath}/`;
const groupReferencesSdkDocs = (group) =>
JSON.stringify(group).includes(localePathPrefix);
const sdkReferencePages = buildSdkReferencePages(prefix);

for (const tab of tabs) {
const sdkAnchor =
tab.dropdowns?.find((d) => d.dropdown === "SDK") ??
Expand Down Expand Up @@ -282,6 +266,27 @@ function main() {
fs.rmSync(readmePath, { force: true });
}

// Mirror the English reference into every non-default locale directory as
// English-content copies (see reference-i18n.json, mode "english-copy").
// The switcher needs a real page at each locale-prefixed path; the content
// stays English so there is no translation drift on regeneration. Mintlify
// translation must be disabled for these paths (dashboard exclusion).
const docsForLocales = JSON.parse(fs.readFileSync(docsJsonPath, "utf8"));
const locales = (docsForLocales.navigation?.languages ?? [])
.filter((l) => !l.default)
.map((l) => l.language);
for (const locale of locales) {
const localeTarget = path.join(target, locale, SDK_DOCS_TARGET_PATH);
fs.rmSync(localeTarget, { recursive: true, force: true });
fs.mkdirSync(localeTarget, { recursive: true });
fs.cpSync(DOCS_SOURCE_PATH, localeTarget, { recursive: true });
const localeReadme = path.join(localeTarget, "README.mdx");
if (fs.existsSync(localeReadme)) fs.rmSync(localeReadme, { force: true });
}
if (locales.length > 0) {
console.log(`Mirrored SDK reference into ${locales.length} locale(s): ${locales.join(", ")}`);
}

// Scan the sdk-docs directory
const sdkFiles = scanSdkDocs(sdkDocsTarget);
console.debug(`SDK files: ${JSON.stringify(sdkFiles, null, 2)}`);
Expand Down
Loading