gt 2.14.43 → 2.14.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.14.44
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1523](https://github.com/generaltranslation/gt/pull/1523) [`bda984e`](https://github.com/generaltranslation/gt/commit/bda984e3351520e49314d1b3601a362ea9eb7396) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Update Mintlify `$ref` handling
8
+
3
9
  ## 2.14.43
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.14.43";
1
+ export declare const PACKAGE_VERSION = "2.14.44";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.14.43";
2
+ const PACKAGE_VERSION = "2.14.44";
3
3
  //#endregion
4
4
  export { PACKAGE_VERSION };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.14.43';\n"],"mappings":";AACA,MAAa,kBAAkB"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.14.44';\n"],"mappings":";AACA,MAAa,kBAAkB"}
@@ -1,5 +1,6 @@
1
1
  import { createFileMapping } from "../formats/files/fileMapping.js";
2
2
  import * as fs$1 from "fs";
3
+ import { parse } from "@babel/parser";
3
4
  import micromatch from "micromatch";
4
5
  import { unified } from "unified";
5
6
  import remarkParse from "remark-parse";
@@ -11,6 +12,89 @@ import { escapeHtmlInTextNodes, normalizeCJKCharacters } from "gt-remark";
11
12
  //#region src/utils/localizeStaticUrls.ts
12
13
  const { isMatch } = micromatch;
13
14
  /**
15
+ * URL-bearing JSX attributes that we localize. Intentionally limited to link
16
+ * attributes (`href`) — NOT asset attributes like `src`, which usually point at
17
+ * shared, locale-agnostic assets (e.g. `/docs/images/...`) that would 404 if a
18
+ * locale prefix were added. Extend deliberately.
19
+ */
20
+ const LOCALIZABLE_URL_ATTRIBUTES = new Set(["href"]);
21
+ /**
22
+ * Localize URL string literals that live inside an MDX expression's raw source.
23
+ *
24
+ * URLs can be nested arbitrarily deep inside `{...}` expressions — e.g. an
25
+ * `<a href>` buried inside a component prop:
26
+ * <ParamField type={<span><a href="/docs/x">Error</a></span>} />
27
+ * Such hrefs never appear as mdast nodes (they live in the expression's embedded
28
+ * JS), so the mdast visitors can't reach them. We re-parse the expression source
29
+ * with Babel — positions are local to `source`, which avoids any document-offset
30
+ * mapping — find JSX url-attribute string literals at any depth, and surgically
31
+ * rewrite them in place.
32
+ *
33
+ * `rootStringIsUrl` covers `href={"/docs/x"}`, where the expression itself is a
34
+ * bare string literal that a url-named attribute should treat as a URL.
35
+ *
36
+ * Only static string literals are localized; dynamically-computed URLs
37
+ * (template literals, identifiers, concatenation) are left untouched — a
38
+ * fundamental limit of static localization, not a parser shortcoming.
39
+ */
40
+ function localizeUrlsInExpressionSource(source, transformUrl, rootStringIsUrl = false) {
41
+ if (!source.trim()) return source;
42
+ let ast;
43
+ try {
44
+ ast = parse(source, {
45
+ sourceType: "module",
46
+ errorRecovery: true,
47
+ plugins: ["jsx", "typescript"]
48
+ });
49
+ } catch {
50
+ return source;
51
+ }
52
+ const replacements = [];
53
+ const pushLiteralReplacement = (literal) => {
54
+ if (!literal || literal.type !== "StringLiteral" && literal.type !== "DirectiveLiteral" || typeof literal.value !== "string" || typeof literal.start !== "number" || typeof literal.end !== "number") return;
55
+ const newUrl = transformUrl(literal.value, "href");
56
+ if (!newUrl) return;
57
+ const quote = source[literal.start] === "'" ? "'" : "\"";
58
+ replacements.push({
59
+ start: literal.start,
60
+ end: literal.end,
61
+ text: `${quote}${newUrl}${quote}`
62
+ });
63
+ };
64
+ if (rootStringIsUrl) {
65
+ const program = ast.program;
66
+ const body = program?.body ?? [];
67
+ if (body.length === 1 && body[0]?.type === "ExpressionStatement") pushLiteralReplacement(body[0].expression);
68
+ else if (body.length === 0 && program?.directives?.length === 1) pushLiteralReplacement(program.directives[0]?.value);
69
+ }
70
+ const seen = /* @__PURE__ */ new Set();
71
+ const walk = (node) => {
72
+ if (!node || typeof node !== "object") return;
73
+ if (Array.isArray(node)) {
74
+ for (const item of node) walk(item);
75
+ return;
76
+ }
77
+ if (seen.has(node)) return;
78
+ seen.add(node);
79
+ if (node.type === "JSXAttribute" && node.name?.type === "JSXIdentifier" && LOCALIZABLE_URL_ATTRIBUTES.has(node.name.name) && node.value) {
80
+ if (node.value.type === "StringLiteral") pushLiteralReplacement(node.value);
81
+ else if (node.value.type === "JSXExpressionContainer" && node.value.expression?.type === "StringLiteral") pushLiteralReplacement(node.value.expression);
82
+ }
83
+ for (const key in node) {
84
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
85
+ const child = node[key];
86
+ if (child && typeof child === "object") walk(child);
87
+ }
88
+ };
89
+ walk(ast.program ?? ast);
90
+ if (replacements.length === 0) return source;
91
+ let result = source;
92
+ replacements.sort((a, b) => b.start - a.start).forEach(({ start, end, text }) => {
93
+ result = result.slice(0, start) + text + result.slice(end);
94
+ });
95
+ return result;
96
+ }
97
+ /**
14
98
  * Localizes static urls in content files.
15
99
  * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
16
100
  * @param settings - The settings object containing the project configuration.
@@ -181,15 +265,27 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
181
265
  });
182
266
  visit(processedAst, ["mdxJsxFlowElement", "mdxJsxTextElement"], (node) => {
183
267
  const jsxNode = node;
184
- if (jsxNode.attributes) {
185
- for (const attr of jsxNode.attributes) if (attr.type === "mdxJsxAttribute" && attr.name === "href" && attr.value) {
186
- const hrefValue = typeof attr.value === "string" ? attr.value : attr.value.value;
187
- if (typeof hrefValue === "string") {
188
- const newUrl = transformUrl(hrefValue, "href");
189
- if (newUrl) if (typeof attr.value === "string") attr.value = newUrl;
190
- else attr.value.value = newUrl;
268
+ if (!jsxNode.attributes) return;
269
+ for (const attr of jsxNode.attributes) {
270
+ if (attr.type !== "mdxJsxAttribute" || !attr.value) continue;
271
+ if (typeof attr.value === "string") {
272
+ if (LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)) {
273
+ const newUrl = transformUrl(attr.value, "href");
274
+ if (newUrl) attr.value = newUrl;
191
275
  }
276
+ continue;
192
277
  }
278
+ if (typeof attr.value === "object" && attr.value.type === "mdxJsxAttributeValueExpression" && typeof attr.value.value === "string") {
279
+ const newValue = localizeUrlsInExpressionSource(attr.value.value, transformUrl, LOCALIZABLE_URL_ATTRIBUTES.has(attr.name));
280
+ if (newValue !== attr.value.value) attr.value.value = newValue;
281
+ }
282
+ }
283
+ });
284
+ visit(processedAst, ["mdxFlowExpression", "mdxTextExpression"], (node) => {
285
+ const exprNode = node;
286
+ if (typeof exprNode.value === "string" && exprNode.value) {
287
+ const newValue = localizeUrlsInExpressionSource(exprNode.value, transformUrl);
288
+ if (newValue !== exprNode.value) exprNode.value = newValue;
193
289
  }
194
290
  });
195
291
  visit(processedAst, "jsx", (node) => {
@@ -1 +1 @@
1
- {"version":3,"file":"localizeStaticUrls.js","names":["fs"],"sources":["../../src/utils/localizeStaticUrls.ts"],"sourcesContent":["import * as fs from 'fs';\nimport type { StaticLocalizationSettings } from '../types/index.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport micromatch from 'micromatch';\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkMdx from 'remark-mdx';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport remarkStringify from 'remark-stringify';\nimport { visit } from 'unist-util-visit';\nimport type { Root, Link, Literal } from 'mdast';\nimport type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';\nimport { escapeHtmlInTextNodes, normalizeCJKCharacters } from 'gt-remark';\n\nconst { isMatch } = micromatch;\n\nexport type StaticUrlSettings = StaticLocalizationSettings;\n\n/**\n * Localizes static urls in content files.\n * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)\n * @param settings - The settings object containing the project configuration.\n * @returns void\n *\n * @TODO This is an experimental feature, and only works in very specific cases. This needs to be improved before\n * it can be enabled by default.\n *\n * Before this becomes a non-experimental feature, we need to:\n * - Support more file types\n * - Support more complex paths\n */\nexport default async function localizeStaticUrls(\n settings: StaticUrlSettings,\n targetLocales?: string[],\n includeFiles?: Set<string>\n) {\n if (\n !settings.files ||\n (Object.keys(settings.files.placeholderPaths).length === 1 &&\n settings.files.placeholderPaths.gt)\n ) {\n return;\n }\n const { resolvedPaths: sourceFiles } = settings.files;\n\n // Use filtered locales if provided, otherwise use all locales\n const locales = targetLocales || settings.locales;\n\n const fileMapping = createFileMapping(\n sourceFiles,\n settings.files.placeholderPaths,\n settings.files.transformPaths ?? {},\n settings.files.transformFormats ?? {},\n settings.locales, // Always use all locales for mapping, filter later\n settings.defaultLocale\n );\n\n // Process all file types at once with a single call\n const processPromises = [];\n\n // First, process default locale files (from source files)\n // This is needed because they might not be in the fileMapping if they're not being translated\n // Only process default locale if it's in the target locales filter\n if (\n !fileMapping[settings.defaultLocale] &&\n locales.includes(settings.defaultLocale) &&\n !includeFiles // when filtering, skip default-locale pass\n ) {\n const defaultLocaleFiles: string[] = [];\n\n // Collect all .md and .mdx files from sourceFiles\n if (sourceFiles.md) {\n defaultLocaleFiles.push(...sourceFiles.md);\n }\n if (sourceFiles.mdx) {\n defaultLocaleFiles.push(...sourceFiles.mdx);\n }\n\n if (defaultLocaleFiles.length > 0) {\n const defaultPromise = Promise.all(\n defaultLocaleFiles.map(async (filePath: string) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file using default locale\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n settings.defaultLocale, // Process as default locale\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n processPromises.push(defaultPromise);\n }\n }\n\n // Then process all other locales from fileMapping\n const mappingPromises = Object.entries(fileMapping)\n .filter(([locale]) => locales.includes(locale)) // Filter by target locales\n .map(async ([locale, filesMap]) => {\n // Get all files that are md or mdx\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n (p.endsWith('.md') || p.endsWith('.mdx')) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n // Replace the placeholder path with the target path\n await Promise.all(\n targetFiles.map(async (filePath) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file (handles both URLs and hrefs in single AST pass)\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n locale,\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n });\n processPromises.push(...mappingPromises);\n\n await Promise.all(processPromises);\n}\n\ninterface UrlTransformResult {\n content: string;\n hasChanges: boolean;\n transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }>;\n}\n\n/**\n * Determines if a URL should be processed based on pattern matching\n */\nfunction shouldProcessUrl(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n baseDomain?: string\n): boolean {\n // Check fragment-only URLs like \"#id-name\"\n if (/^\\s*#/.test(originalUrl)) {\n return false;\n }\n\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n\n // Handle absolute URLs with baseDomain\n let urlToCheck = originalUrl;\n if (baseDomain && originalUrl.startsWith(baseDomain)) {\n urlToCheck = originalUrl.substring(baseDomain.length);\n }\n\n if (targetLocale === defaultLocale) {\n // For default locale processing, check if URL contains the pattern\n return urlToCheck.includes(patternWithoutSlash);\n } else {\n // For non-default locales, check if URL starts with pattern\n return urlToCheck.startsWith(patternWithoutSlash);\n }\n}\n\n/**\n * Determines if a URL should be processed based on the base domain\n */\nfunction shouldProcessAbsoluteUrl(\n originalUrl: string,\n baseDomain: string\n): boolean {\n return originalUrl.startsWith(baseDomain);\n}\n\n/**\n * Checks if a URL should be excluded based on exclusion patterns\n */\nfunction isUrlExcluded(\n originalUrl: string,\n exclude: string[],\n defaultLocale: string\n): boolean {\n const excludePatterns = exclude.map((p) =>\n p.replace(/\\[locale\\]/g, defaultLocale)\n );\n return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));\n}\n\n/**\n * Main URL transformation function that delegates to specific scenarios\n */\nexport function transformUrlPath(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n hideDefaultLocale: boolean\n): string | null {\n const originalPathArray = originalUrl\n .split('/')\n .filter((path) => path !== '');\n const patternHeadArray = patternHead.split('/').filter((path) => path !== '');\n\n // check if the pattern head matches the original path\n if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {\n return null;\n }\n\n if (patternHeadArray.length > originalPathArray.length) {\n return null; // Pattern is longer than the URL path\n }\n\n let result = null;\n if (targetLocale === defaultLocale) {\n if (hideDefaultLocale) {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // remove default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n ...originalPathArray.slice(patternHeadArray.length + 1),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {\n return null;\n }\n\n // insert default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n defaultLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n }\n } else if (hideDefaultLocale) {\n // Avoid duplicating target locale if already present\n if (originalPathArray?.[patternHeadArray.length] === targetLocale) {\n return null;\n }\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n targetLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check default locale\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // replace default locale with target locale\n const newPathArray = [...originalPathArray];\n newPathArray[patternHeadArray.length] = targetLocale;\n\n result = newPathArray.join('/');\n }\n\n // check for leading and trailing slashes\n if (originalUrl.startsWith('/')) {\n result = '/' + result;\n }\n if (originalUrl.endsWith('/')) {\n result = result + '/';\n }\n\n return result;\n}\n\n/**\n * AST-based transformation for MDX files using remark-mdx\n */\nfunction transformMdxUrls(\n mdxContent: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]',\n exclude: string[] = [],\n baseDomain?: string\n): UrlTransformResult {\n const transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }> = [];\n\n if (!pattern.startsWith('/')) {\n pattern = '/' + pattern;\n }\n\n const patternHead = pattern.split('[locale]')[0];\n\n // Quick check: if the file doesn't contain the pattern, skip expensive AST parsing\n // For default locale processing, we also need to check if content might need adjustment\n if (targetLocale === defaultLocale) {\n // For default locale files, we always need to check as we're looking for either:\n // - paths without locale (when hideDefaultLocale=false)\n // - paths with default locale (when hideDefaultLocale=true)\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n if (!mdxContent.includes(patternWithoutSlash)) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n } else {\n // For non-default locales, use the original logic\n if (!mdxContent.includes(patternHead.replace(/\\/$/, ''))) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n }\n\n // Parse the MDX content into an AST\n let processedAst: Root;\n try {\n const parseProcessor = unified()\n .use(remarkParse)\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx);\n\n const ast = parseProcessor.parse(mdxContent);\n processedAst = parseProcessor.runSync(ast) as Root;\n } catch {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls,\n };\n }\n\n // Helper function to transform URL based on pattern\n const transformUrl = (\n originalUrl: string,\n linkType: 'markdown' | 'href'\n ): string | null => {\n // For Markdown links [text](path), only process absolute-root paths starting with '/'\n // Relative markdown links should remain relative to the current page and not be localized.\n if (linkType === 'markdown') {\n const isFragment = /^\\s*#/.test(originalUrl);\n const isAbsoluteRoot = originalUrl.startsWith('/');\n const looksAbsoluteWithDomain = baseDomain\n ? shouldProcessAbsoluteUrl(originalUrl, baseDomain)\n : false;\n if (!isAbsoluteRoot && !looksAbsoluteWithDomain && !isFragment) {\n return null;\n }\n }\n // Check if URL should be processed\n if (\n !shouldProcessUrl(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n baseDomain\n )\n ) {\n return null;\n }\n\n // Skip absolute URLs (http://, https://, //, etc.)\n if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {\n // Get everything after the base domain\n const afterDomain = originalUrl.substring(baseDomain.length);\n\n const transformedPath = transformUrlPath(\n afterDomain,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n if (!transformedPath) {\n return null;\n }\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: transformedPath,\n type: linkType,\n });\n return transformedPath ? baseDomain + transformedPath : null;\n }\n\n // Exclude colon-prefixed URLs (http://, https://, //, etc.)\n if (originalUrl.split('?')[0].includes(':')) {\n return null;\n }\n\n // Transform the URL based on locale and configuration\n const newUrl = transformUrlPath(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n\n if (!newUrl) {\n return null;\n }\n\n // Check exclusions\n if (isUrlExcluded(originalUrl, exclude, defaultLocale)) {\n return null;\n }\n\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: newUrl,\n type: linkType,\n });\n return newUrl;\n };\n\n // Visit markdown link nodes: [text](url)\n visit(processedAst, 'link', (node: Link) => {\n if (node.url) {\n const newUrl = transformUrl(node.url, 'markdown');\n if (newUrl) {\n node.url = newUrl;\n }\n }\n });\n\n // Visit JSX/HTML elements for href attributes: <a href=\"url\"> or <Card href=\"url\">\n visit(processedAst, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {\n const jsxNode = node as MdxJsxFlowElement | MdxJsxTextElement;\n if (jsxNode.attributes) {\n for (const attr of jsxNode.attributes) {\n if (\n attr.type === 'mdxJsxAttribute' &&\n attr.name === 'href' &&\n attr.value\n ) {\n // Handle MdxJsxAttribute with string or MdxJsxAttributeValueExpression\n const hrefValue =\n typeof attr.value === 'string' ? attr.value : attr.value.value;\n if (typeof hrefValue === 'string') {\n const newUrl = transformUrl(hrefValue, 'href');\n if (newUrl) {\n if (typeof attr.value === 'string') {\n attr.value = newUrl;\n } else {\n attr.value.value = newUrl;\n }\n }\n }\n }\n }\n }\n });\n\n // Visit raw JSX nodes for href attributes in JSX strings\n visit(processedAst, 'jsx', (node: Literal) => {\n if (node.value && typeof node.value === 'string') {\n const jsxContent = node.value;\n\n // Use regex to find href attributes in the JSX string\n const hrefRegex = /href\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let match;\n const replacements: Array<{\n start: number;\n end: number;\n oldHrefAttr: string;\n newHrefAttr: string;\n }> = [];\n\n // Reset regex lastIndex to avoid issues with global flag\n hrefRegex.lastIndex = 0;\n\n while ((match = hrefRegex.exec(jsxContent)) !== null) {\n const originalHref = match[1];\n const newUrl = transformUrl(originalHref, 'href');\n\n if (newUrl) {\n // Store replacement info\n const oldHrefAttr = match[0]; // The full match like 'href=\"/quickstart\"'\n const quote = oldHrefAttr.includes('\"') ? '\"' : \"'\";\n const newHrefAttr = `href=${quote}${newUrl}${quote}`;\n\n replacements.push({\n start: match.index!,\n end: match.index! + oldHrefAttr.length,\n oldHrefAttr,\n newHrefAttr,\n });\n }\n }\n\n // Apply replacements in reverse order (from end to start) to avoid position shifts\n if (replacements.length > 0) {\n let newJsxContent = jsxContent;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, newHrefAttr }) => {\n newJsxContent =\n newJsxContent.slice(0, start) +\n newHrefAttr +\n newJsxContent.slice(end);\n });\n\n node.value = newJsxContent;\n }\n }\n });\n\n // Convert the modified AST back to MDX string\n let content: string;\n try {\n const stringifyProcessor = unified()\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx)\n .use(normalizeCJKCharacters)\n .use(escapeHtmlInTextNodes)\n .use(remarkStringify, {\n handlers: {\n // Handler to prevent escaping (avoids '&lt;' -> '\\&lt;')\n text(node: Literal) {\n return node.value;\n },\n },\n });\n\n const outTree = stringifyProcessor.runSync(processedAst) as Root;\n content = stringifyProcessor.stringify(outTree);\n } catch (error) {\n console.warn(\n `Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`\n );\n console.warn(\n 'Returning original content unchanged due to stringify error.'\n );\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n\n // Handle newline formatting to match original input\n if (content.endsWith('\\n') && !mdxContent.endsWith('\\n')) {\n content = content.slice(0, -1);\n }\n\n // Preserve leading newlines from original content\n if (mdxContent.startsWith('\\n') && !content.startsWith('\\n')) {\n content = '\\n' + content;\n }\n\n return {\n content,\n hasChanges: transformedUrls.length > 0,\n transformedUrls,\n };\n}\n\n// AST-based transformation for MDX files using remark\nfunction localizeStaticUrlsForFile(\n file: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]', // eg /docs/[locale] or /[locale]\n exclude: string[] = [],\n baseDomain?: string\n): UrlTransformResult {\n // Use AST-based transformation for MDX files\n return transformMdxUrls(\n file,\n defaultLocale,\n targetLocale,\n hideDefaultLocale,\n pattern,\n exclude,\n baseDomain || ''\n );\n}\n\nfunction checkIfPathMatchesPattern(\n originalUrlArray: string[],\n patternHeadArray: string[]\n): boolean {\n // check if the pattern head matches the original path\n for (let i = 0; i < patternHeadArray.length; i++) {\n if (patternHeadArray[i] !== originalUrlArray?.[i]) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"localizeStaticUrls.js","names":["parseBabel","fs"],"sources":["../../src/utils/localizeStaticUrls.ts"],"sourcesContent":["import * as fs from 'fs';\nimport type { StaticLocalizationSettings } from '../types/index.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport micromatch from 'micromatch';\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkMdx from 'remark-mdx';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport remarkStringify from 'remark-stringify';\nimport { visit } from 'unist-util-visit';\nimport type { Root, Link, Literal } from 'mdast';\nimport type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx-jsx';\nimport { escapeHtmlInTextNodes, normalizeCJKCharacters } from 'gt-remark';\nimport { parse as parseBabel } from '@babel/parser';\n\nconst { isMatch } = micromatch;\n\n/**\n * URL-bearing JSX attributes that we localize. Intentionally limited to link\n * attributes (`href`) — NOT asset attributes like `src`, which usually point at\n * shared, locale-agnostic assets (e.g. `/docs/images/...`) that would 404 if a\n * locale prefix were added. Extend deliberately.\n */\nconst LOCALIZABLE_URL_ATTRIBUTES = new Set(['href']);\n\n/**\n * Localize URL string literals that live inside an MDX expression's raw source.\n *\n * URLs can be nested arbitrarily deep inside `{...}` expressions — e.g. an\n * `<a href>` buried inside a component prop:\n * <ParamField type={<span><a href=\"/docs/x\">Error</a></span>} />\n * Such hrefs never appear as mdast nodes (they live in the expression's embedded\n * JS), so the mdast visitors can't reach them. We re-parse the expression source\n * with Babel — positions are local to `source`, which avoids any document-offset\n * mapping — find JSX url-attribute string literals at any depth, and surgically\n * rewrite them in place.\n *\n * `rootStringIsUrl` covers `href={\"/docs/x\"}`, where the expression itself is a\n * bare string literal that a url-named attribute should treat as a URL.\n *\n * Only static string literals are localized; dynamically-computed URLs\n * (template literals, identifiers, concatenation) are left untouched — a\n * fundamental limit of static localization, not a parser shortcoming.\n */\nfunction localizeUrlsInExpressionSource(\n source: string,\n transformUrl: (url: string, linkType: 'markdown' | 'href') => string | null,\n rootStringIsUrl: boolean = false\n): string {\n if (!source.trim()) return source;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let ast: any;\n try {\n ast = parseBabel(source, {\n sourceType: 'module',\n errorRecovery: true,\n plugins: ['jsx', 'typescript'],\n });\n } catch {\n // Dynamic / unparseable expression — leave it untouched.\n return source;\n }\n\n const replacements: Array<{ start: number; end: number; text: string }> = [];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pushLiteralReplacement = (literal: any) => {\n if (\n !literal ||\n // A bare string at statement position is parsed by Babel as a\n // DirectiveLiteral (cf. \"use strict\") rather than a StringLiteral.\n (literal.type !== 'StringLiteral' &&\n literal.type !== 'DirectiveLiteral') ||\n typeof literal.value !== 'string' ||\n typeof literal.start !== 'number' ||\n typeof literal.end !== 'number'\n ) {\n return;\n }\n const newUrl = transformUrl(literal.value, 'href');\n if (!newUrl) return;\n // Preserve the original quote style; URLs never contain quote chars.\n const quote = source[literal.start] === \"'\" ? \"'\" : '\"';\n replacements.push({\n start: literal.start,\n end: literal.end,\n text: `${quote}${newUrl}${quote}`,\n });\n };\n\n // `href={\"/docs/x\"}`: the entire expression is the URL string.\n if (rootStringIsUrl) {\n const program = ast.program;\n const body = (program?.body ?? []) as Array<{\n type: string;\n expression?: unknown;\n }>;\n if (body.length === 1 && body[0]?.type === 'ExpressionStatement') {\n pushLiteralReplacement(body[0].expression);\n } else if (body.length === 0 && program?.directives?.length === 1) {\n // Babel parses a lone string literal as a directive, not a statement.\n pushLiteralReplacement(program.directives[0]?.value);\n }\n }\n\n // Walk the AST for JSX url-attributes anywhere in the expression.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const seen = new Set<any>();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const walk = (node: any) => {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n for (const item of node) walk(item);\n return;\n }\n if (seen.has(node)) return;\n seen.add(node);\n\n if (\n node.type === 'JSXAttribute' &&\n node.name?.type === 'JSXIdentifier' &&\n LOCALIZABLE_URL_ATTRIBUTES.has(node.name.name) &&\n node.value\n ) {\n if (node.value.type === 'StringLiteral') {\n pushLiteralReplacement(node.value);\n } else if (\n node.value.type === 'JSXExpressionContainer' &&\n node.value.expression?.type === 'StringLiteral'\n ) {\n pushLiteralReplacement(node.value.expression);\n }\n }\n\n for (const key in node) {\n if (\n key === 'loc' ||\n key === 'start' ||\n key === 'end' ||\n key === 'range'\n ) {\n continue;\n }\n const child = node[key];\n if (child && typeof child === 'object') walk(child);\n }\n };\n walk(ast.program ?? ast);\n\n if (replacements.length === 0) return source;\n\n // Apply end-to-start so earlier offsets remain valid.\n let result = source;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, text }) => {\n result = result.slice(0, start) + text + result.slice(end);\n });\n return result;\n}\n\nexport type StaticUrlSettings = StaticLocalizationSettings;\n\n/**\n * Localizes static urls in content files.\n * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)\n * @param settings - The settings object containing the project configuration.\n * @returns void\n *\n * @TODO This is an experimental feature, and only works in very specific cases. This needs to be improved before\n * it can be enabled by default.\n *\n * Before this becomes a non-experimental feature, we need to:\n * - Support more file types\n * - Support more complex paths\n */\nexport default async function localizeStaticUrls(\n settings: StaticUrlSettings,\n targetLocales?: string[],\n includeFiles?: Set<string>\n) {\n if (\n !settings.files ||\n (Object.keys(settings.files.placeholderPaths).length === 1 &&\n settings.files.placeholderPaths.gt)\n ) {\n return;\n }\n const { resolvedPaths: sourceFiles } = settings.files;\n\n // Use filtered locales if provided, otherwise use all locales\n const locales = targetLocales || settings.locales;\n\n const fileMapping = createFileMapping(\n sourceFiles,\n settings.files.placeholderPaths,\n settings.files.transformPaths ?? {},\n settings.files.transformFormats ?? {},\n settings.locales, // Always use all locales for mapping, filter later\n settings.defaultLocale\n );\n\n // Process all file types at once with a single call\n const processPromises = [];\n\n // First, process default locale files (from source files)\n // This is needed because they might not be in the fileMapping if they're not being translated\n // Only process default locale if it's in the target locales filter\n if (\n !fileMapping[settings.defaultLocale] &&\n locales.includes(settings.defaultLocale) &&\n !includeFiles // when filtering, skip default-locale pass\n ) {\n const defaultLocaleFiles: string[] = [];\n\n // Collect all .md and .mdx files from sourceFiles\n if (sourceFiles.md) {\n defaultLocaleFiles.push(...sourceFiles.md);\n }\n if (sourceFiles.mdx) {\n defaultLocaleFiles.push(...sourceFiles.mdx);\n }\n\n if (defaultLocaleFiles.length > 0) {\n const defaultPromise = Promise.all(\n defaultLocaleFiles.map(async (filePath: string) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file using default locale\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n settings.defaultLocale, // Process as default locale\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n processPromises.push(defaultPromise);\n }\n }\n\n // Then process all other locales from fileMapping\n const mappingPromises = Object.entries(fileMapping)\n .filter(([locale]) => locales.includes(locale)) // Filter by target locales\n .map(async ([locale, filesMap]) => {\n // Get all files that are md or mdx\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n (p.endsWith('.md') || p.endsWith('.mdx')) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n // Replace the placeholder path with the target path\n await Promise.all(\n targetFiles.map(async (filePath) => {\n // Check if file exists before processing\n if (!fs.existsSync(filePath)) {\n return;\n }\n // Get file content\n const fileContent = await fs.promises.readFile(filePath, 'utf8');\n // Localize the file (handles both URLs and hrefs in single AST pass)\n const result = localizeStaticUrlsForFile(\n fileContent,\n settings.defaultLocale,\n locale,\n settings.options?.experimentalHideDefaultLocale || false,\n settings.options?.docsUrlPattern,\n settings.options?.excludeStaticUrls,\n settings.options?.baseDomain\n );\n // Only write the file if there were changes\n if (result.hasChanges) {\n await fs.promises.writeFile(filePath, result.content);\n }\n })\n );\n });\n processPromises.push(...mappingPromises);\n\n await Promise.all(processPromises);\n}\n\ninterface UrlTransformResult {\n content: string;\n hasChanges: boolean;\n transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }>;\n}\n\n/**\n * Determines if a URL should be processed based on pattern matching\n */\nfunction shouldProcessUrl(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n baseDomain?: string\n): boolean {\n // Check fragment-only URLs like \"#id-name\"\n if (/^\\s*#/.test(originalUrl)) {\n return false;\n }\n\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n\n // Handle absolute URLs with baseDomain\n let urlToCheck = originalUrl;\n if (baseDomain && originalUrl.startsWith(baseDomain)) {\n urlToCheck = originalUrl.substring(baseDomain.length);\n }\n\n if (targetLocale === defaultLocale) {\n // For default locale processing, check if URL contains the pattern\n return urlToCheck.includes(patternWithoutSlash);\n } else {\n // For non-default locales, check if URL starts with pattern\n return urlToCheck.startsWith(patternWithoutSlash);\n }\n}\n\n/**\n * Determines if a URL should be processed based on the base domain\n */\nfunction shouldProcessAbsoluteUrl(\n originalUrl: string,\n baseDomain: string\n): boolean {\n return originalUrl.startsWith(baseDomain);\n}\n\n/**\n * Checks if a URL should be excluded based on exclusion patterns\n */\nfunction isUrlExcluded(\n originalUrl: string,\n exclude: string[],\n defaultLocale: string\n): boolean {\n const excludePatterns = exclude.map((p) =>\n p.replace(/\\[locale\\]/g, defaultLocale)\n );\n return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));\n}\n\n/**\n * Main URL transformation function that delegates to specific scenarios\n */\nexport function transformUrlPath(\n originalUrl: string,\n patternHead: string,\n targetLocale: string,\n defaultLocale: string,\n hideDefaultLocale: boolean\n): string | null {\n const originalPathArray = originalUrl\n .split('/')\n .filter((path) => path !== '');\n const patternHeadArray = patternHead.split('/').filter((path) => path !== '');\n\n // check if the pattern head matches the original path\n if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {\n return null;\n }\n\n if (patternHeadArray.length > originalPathArray.length) {\n return null; // Pattern is longer than the URL path\n }\n\n let result = null;\n if (targetLocale === defaultLocale) {\n if (hideDefaultLocale) {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // remove default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n ...originalPathArray.slice(patternHeadArray.length + 1),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check if default locale is already present\n if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {\n return null;\n }\n\n // insert default locale\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n defaultLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n }\n } else if (hideDefaultLocale) {\n // Avoid duplicating target locale if already present\n if (originalPathArray?.[patternHeadArray.length] === targetLocale) {\n return null;\n }\n const newPathArray = [\n ...originalPathArray.slice(0, patternHeadArray.length),\n targetLocale,\n ...originalPathArray.slice(patternHeadArray.length),\n ];\n\n result = newPathArray.join('/');\n } else {\n // check default locale\n if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {\n return null;\n }\n\n // replace default locale with target locale\n const newPathArray = [...originalPathArray];\n newPathArray[patternHeadArray.length] = targetLocale;\n\n result = newPathArray.join('/');\n }\n\n // check for leading and trailing slashes\n if (originalUrl.startsWith('/')) {\n result = '/' + result;\n }\n if (originalUrl.endsWith('/')) {\n result = result + '/';\n }\n\n return result;\n}\n\n/**\n * AST-based transformation for MDX files using remark-mdx\n */\nfunction transformMdxUrls(\n mdxContent: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]',\n exclude: string[] = [],\n baseDomain?: string\n): UrlTransformResult {\n const transformedUrls: Array<{\n originalPath: string;\n newPath: string;\n type: 'markdown' | 'href';\n }> = [];\n\n if (!pattern.startsWith('/')) {\n pattern = '/' + pattern;\n }\n\n const patternHead = pattern.split('[locale]')[0];\n\n // Quick check: if the file doesn't contain the pattern, skip expensive AST parsing\n // For default locale processing, we also need to check if content might need adjustment\n if (targetLocale === defaultLocale) {\n // For default locale files, we always need to check as we're looking for either:\n // - paths without locale (when hideDefaultLocale=false)\n // - paths with default locale (when hideDefaultLocale=true)\n const patternWithoutSlash = patternHead.replace(/\\/$/, '');\n if (!mdxContent.includes(patternWithoutSlash)) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n } else {\n // For non-default locales, use the original logic\n if (!mdxContent.includes(patternHead.replace(/\\/$/, ''))) {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n }\n\n // Parse the MDX content into an AST\n let processedAst: Root;\n try {\n const parseProcessor = unified()\n .use(remarkParse)\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx);\n\n const ast = parseProcessor.parse(mdxContent);\n processedAst = parseProcessor.runSync(ast) as Root;\n } catch {\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls,\n };\n }\n\n // Helper function to transform URL based on pattern\n const transformUrl = (\n originalUrl: string,\n linkType: 'markdown' | 'href'\n ): string | null => {\n // For Markdown links [text](path), only process absolute-root paths starting with '/'\n // Relative markdown links should remain relative to the current page and not be localized.\n if (linkType === 'markdown') {\n const isFragment = /^\\s*#/.test(originalUrl);\n const isAbsoluteRoot = originalUrl.startsWith('/');\n const looksAbsoluteWithDomain = baseDomain\n ? shouldProcessAbsoluteUrl(originalUrl, baseDomain)\n : false;\n if (!isAbsoluteRoot && !looksAbsoluteWithDomain && !isFragment) {\n return null;\n }\n }\n // Check if URL should be processed\n if (\n !shouldProcessUrl(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n baseDomain\n )\n ) {\n return null;\n }\n\n // Skip absolute URLs (http://, https://, //, etc.)\n if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {\n // Get everything after the base domain\n const afterDomain = originalUrl.substring(baseDomain.length);\n\n const transformedPath = transformUrlPath(\n afterDomain,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n if (!transformedPath) {\n return null;\n }\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: transformedPath,\n type: linkType,\n });\n return transformedPath ? baseDomain + transformedPath : null;\n }\n\n // Exclude colon-prefixed URLs (http://, https://, //, etc.)\n if (originalUrl.split('?')[0].includes(':')) {\n return null;\n }\n\n // Transform the URL based on locale and configuration\n const newUrl = transformUrlPath(\n originalUrl,\n patternHead,\n targetLocale,\n defaultLocale,\n hideDefaultLocale\n );\n\n if (!newUrl) {\n return null;\n }\n\n // Check exclusions\n if (isUrlExcluded(originalUrl, exclude, defaultLocale)) {\n return null;\n }\n\n transformedUrls.push({\n originalPath: originalUrl,\n newPath: newUrl,\n type: linkType,\n });\n return newUrl;\n };\n\n // Visit markdown link nodes: [text](url)\n visit(processedAst, 'link', (node: Link) => {\n if (node.url) {\n const newUrl = transformUrl(node.url, 'markdown');\n if (newUrl) {\n node.url = newUrl;\n }\n }\n });\n\n // Visit JSX/HTML elements for href attributes: <a href=\"url\"> or <Card href=\"url\">\n visit(processedAst, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {\n const jsxNode = node as MdxJsxFlowElement | MdxJsxTextElement;\n if (!jsxNode.attributes) return;\n for (const attr of jsxNode.attributes) {\n if (attr.type !== 'mdxJsxAttribute' || !attr.value) continue;\n\n // Plain string attribute value, e.g. <a href=\"/docs/x\">\n if (typeof attr.value === 'string') {\n if (LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)) {\n const newUrl = transformUrl(attr.value, 'href');\n if (newUrl) {\n attr.value = newUrl;\n }\n }\n continue;\n }\n\n // Expression attribute value. Two shapes are handled:\n // href={\"/docs/x\"} (url attr, bare string)\n // type={<span><a href=\"/docs/x\">…</a></span>} (url attr nested deep\n // inside a non-url prop)\n // The nested case never surfaces as an mdast node — it lives in the\n // expression's embedded JS — so it's localized via the source-level walk.\n if (\n typeof attr.value === 'object' &&\n attr.value.type === 'mdxJsxAttributeValueExpression' &&\n typeof attr.value.value === 'string'\n ) {\n const newValue = localizeUrlsInExpressionSource(\n attr.value.value,\n transformUrl,\n LOCALIZABLE_URL_ATTRIBUTES.has(attr.name)\n );\n if (newValue !== attr.value.value) {\n attr.value.value = newValue;\n }\n }\n }\n });\n\n // Visit standalone MDX expressions for URLs inside embedded JSX, e.g.\n // {isBeta && <a href=\"/docs/x\">Beta</a>}\n visit(processedAst, ['mdxFlowExpression', 'mdxTextExpression'], (node) => {\n const exprNode = node as unknown as Literal;\n if (typeof exprNode.value === 'string' && exprNode.value) {\n const newValue = localizeUrlsInExpressionSource(\n exprNode.value,\n transformUrl\n );\n if (newValue !== exprNode.value) {\n exprNode.value = newValue;\n }\n }\n });\n\n // Visit raw JSX nodes for href attributes in JSX strings\n visit(processedAst, 'jsx', (node: Literal) => {\n if (node.value && typeof node.value === 'string') {\n const jsxContent = node.value;\n\n // Use regex to find href attributes in the JSX string\n const hrefRegex = /href\\s*=\\s*[\"']([^\"']+)[\"']/g;\n let match;\n const replacements: Array<{\n start: number;\n end: number;\n oldHrefAttr: string;\n newHrefAttr: string;\n }> = [];\n\n // Reset regex lastIndex to avoid issues with global flag\n hrefRegex.lastIndex = 0;\n\n while ((match = hrefRegex.exec(jsxContent)) !== null) {\n const originalHref = match[1];\n const newUrl = transformUrl(originalHref, 'href');\n\n if (newUrl) {\n // Store replacement info\n const oldHrefAttr = match[0]; // The full match like 'href=\"/quickstart\"'\n const quote = oldHrefAttr.includes('\"') ? '\"' : \"'\";\n const newHrefAttr = `href=${quote}${newUrl}${quote}`;\n\n replacements.push({\n start: match.index!,\n end: match.index! + oldHrefAttr.length,\n oldHrefAttr,\n newHrefAttr,\n });\n }\n }\n\n // Apply replacements in reverse order (from end to start) to avoid position shifts\n if (replacements.length > 0) {\n let newJsxContent = jsxContent;\n replacements\n .sort((a, b) => b.start - a.start)\n .forEach(({ start, end, newHrefAttr }) => {\n newJsxContent =\n newJsxContent.slice(0, start) +\n newHrefAttr +\n newJsxContent.slice(end);\n });\n\n node.value = newJsxContent;\n }\n }\n });\n\n // Convert the modified AST back to MDX string\n let content: string;\n try {\n const stringifyProcessor = unified()\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .use(remarkMdx)\n .use(normalizeCJKCharacters)\n .use(escapeHtmlInTextNodes)\n .use(remarkStringify, {\n handlers: {\n // Handler to prevent escaping (avoids '&lt;' -> '\\&lt;')\n text(node: Literal) {\n return node.value;\n },\n },\n });\n\n const outTree = stringifyProcessor.runSync(processedAst) as Root;\n content = stringifyProcessor.stringify(outTree);\n } catch (error) {\n console.warn(\n `Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`\n );\n console.warn(\n 'Returning original content unchanged due to stringify error.'\n );\n return {\n content: mdxContent,\n hasChanges: false,\n transformedUrls: [],\n };\n }\n\n // Handle newline formatting to match original input\n if (content.endsWith('\\n') && !mdxContent.endsWith('\\n')) {\n content = content.slice(0, -1);\n }\n\n // Preserve leading newlines from original content\n if (mdxContent.startsWith('\\n') && !content.startsWith('\\n')) {\n content = '\\n' + content;\n }\n\n return {\n content,\n hasChanges: transformedUrls.length > 0,\n transformedUrls,\n };\n}\n\n// AST-based transformation for MDX files using remark\nfunction localizeStaticUrlsForFile(\n file: string,\n defaultLocale: string,\n targetLocale: string,\n hideDefaultLocale: boolean,\n pattern: string = '/[locale]', // eg /docs/[locale] or /[locale]\n exclude: string[] = [],\n baseDomain?: string\n): UrlTransformResult {\n // Use AST-based transformation for MDX files\n return transformMdxUrls(\n file,\n defaultLocale,\n targetLocale,\n hideDefaultLocale,\n pattern,\n exclude,\n baseDomain || ''\n );\n}\n\nfunction checkIfPathMatchesPattern(\n originalUrlArray: string[],\n patternHeadArray: string[]\n): boolean {\n // check if the pattern head matches the original path\n for (let i = 0; i < patternHeadArray.length; i++) {\n if (patternHeadArray[i] !== originalUrlArray?.[i]) {\n return false;\n }\n }\n\n return true;\n}\n"],"mappings":"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"}
@@ -435,7 +435,7 @@ function parseSchemaValue(value) {
435
435
  if (!tokens.length) return null;
436
436
  let cursor = 0;
437
437
  let specPath;
438
- if (hasOpenApiSpecExtension(tokens[0])) {
438
+ if (hasOpenApiSpecExtension(tokens[0]) || tokens.length > 1) {
439
439
  specPath = tokens[0];
440
440
  cursor = 1;
441
441
  }
@@ -1 +1 @@
1
- {"version":3,"file":"processOpenApi.js","names":[],"sources":["../../src/utils/processOpenApi.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport YAML, { isMap, isScalar } from 'yaml';\nimport type { Root, Content, Yaml } from 'mdast';\nimport { logger } from '../console/logger.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport { Settings } from '../types/index.js';\n\ntype SpecAnalysis = {\n absPath: string;\n configPath: string; // as provided in config (for formatting)\n operations: Set<string>;\n schemas: Set<string>;\n webhooks: Set<string>;\n};\n\ntype ParsedOpenApiValue =\n | {\n kind: 'operation';\n specPath?: string;\n method: string;\n operationPath: string;\n }\n | {\n kind: 'webhook';\n specPath?: string;\n name: string;\n };\n\ntype ParsedSchemaValue = {\n specPath?: string;\n schemaName: string;\n};\n\nconst HTTP_METHODS = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n 'TRACE',\n]);\nconst OPENAPI_SPEC_EXTENSIONS = new Set(['.json', '.yaml', '.yml']);\n\n/**\n * Postprocess Mintlify OpenAPI references to point to locale-specific spec files.\n * - Uses openapi.files (ordered) to resolve ambiguities (first match wins).\n * - Relies on the user's json transform rules for locale paths.\n * - Warns on missing/ambiguous references but keeps behavior deterministic.\n */\nexport default async function processOpenApi(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const openapiConfig = settings.options?.mintlify?.openapi;\n if (!openapiConfig || !openapiConfig.files?.length) return;\n if (!settings.files) return;\n\n const configDir = path.dirname(settings.config);\n const specAnalyses = buildSpecAnalyses(openapiConfig.files, configDir);\n if (!specAnalyses.length) return;\n\n const warnings = new Set<string>();\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n const fileMappingAbs: Record<string, Record<string, string>> = {};\n for (const [locale, mapping] of Object.entries(fileMapping)) {\n fileMappingAbs[locale] = {};\n for (const [src, dest] of Object.entries(mapping)) {\n const absSrc = path.resolve(configDir, src);\n const absDest = path.resolve(configDir, dest);\n fileMappingAbs[locale][absSrc] = absDest;\n }\n }\n\n // Also rewrite default-locale source files so they use the deterministic spec selection\n const defaultFiles = [\n ...(resolvedPaths.mdx || []),\n ...(resolvedPaths.md || []),\n ];\n for (const filePath of defaultFiles) {\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf8');\n const updated = rewriteFrontmatter(\n content,\n filePath,\n settings.defaultLocale,\n specAnalyses,\n fileMappingAbs,\n warnings,\n configDir\n );\n if (updated?.changed) {\n await fs.promises.writeFile(filePath, updated.content, 'utf8');\n }\n }\n\n for (const [locale, filesMap] of Object.entries(fileMapping)) {\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n (p.endsWith('.md') || p.endsWith('.mdx')) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n for (const filePath of targetFiles) {\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf8');\n const updated = rewriteFrontmatter(\n content,\n filePath,\n locale,\n specAnalyses,\n fileMappingAbs,\n warnings,\n configDir\n );\n\n if (updated?.changed) {\n await fs.promises.writeFile(filePath, updated.content, 'utf8');\n }\n }\n }\n\n const docsJsonTargets = collectDocsJsonTargets(\n settings,\n fileMapping,\n includeFiles\n );\n for (const target of docsJsonTargets) {\n if (!fs.existsSync(target.path)) continue;\n const content = fs.readFileSync(target.path, 'utf8');\n const updated = rewriteDocsJsonOpenApi(\n content,\n target.path,\n target.localeHint,\n specAnalyses,\n fileMappingAbs,\n fileMapping,\n warnings,\n configDir,\n settings.defaultLocale\n );\n if (updated?.changed) {\n await fs.promises.writeFile(target.path, updated.content, 'utf8');\n }\n }\n\n for (const message of warnings) {\n logger.warn(message);\n }\n}\n\nfunction collectDocsJsonTargets(\n settings: Settings,\n fileMapping: Record<string, Record<string, string>>,\n includeFiles?: Set<string>\n): Array<{ path: string; localeHint?: string }> {\n const targets: Array<{ path: string; localeHint?: string }> = [];\n const seen = new Map<string, Set<string>>();\n\n const addTarget = (filePath: string, locale?: string) => {\n const canonicalPath = path.resolve(filePath);\n if (\n includeFiles &&\n !includeFiles.has(filePath) &&\n !includeFiles.has(canonicalPath)\n ) {\n return;\n }\n const locales = seen.get(canonicalPath) ?? new Set<string>();\n if (locale) locales.add(locale);\n seen.set(canonicalPath, locales);\n };\n\n if (!includeFiles && settings.files?.resolvedPaths.json) {\n for (const filePath of settings.files.resolvedPaths.json) {\n addTarget(filePath, settings.defaultLocale);\n }\n }\n\n for (const [locale, filesMap] of Object.entries(fileMapping)) {\n for (const filePath of Object.values(filesMap)) {\n if (!filePath.endsWith('.json')) continue;\n addTarget(filePath, locale);\n }\n }\n\n for (const [filePath, locales] of seen.entries()) {\n const localeHint = locales.size === 1 ? Array.from(locales)[0] : undefined;\n targets.push({ path: filePath, localeHint });\n }\n\n return targets;\n}\n\nfunction isMintlifyDocsJson(filePath: string, json: unknown): boolean {\n if (!isRecord(json)) return false;\n const schema = json.$schema;\n if (typeof schema === 'string') {\n return (\n schema.includes('mintlify.com/docs.json') ||\n schema.includes('mintlify.com/mint.json')\n );\n }\n const base = path.basename(filePath);\n return base === 'docs.json' || base === 'mint.json';\n}\n\nfunction rewriteDocsJsonOpenApi(\n content: string,\n filePath: string,\n localeHint: string | undefined,\n specs: SpecAnalysis[],\n fileMappingAbs: Record<string, Record<string, string>>,\n fileMappingRel: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string,\n defaultLocale: string\n): { changed: boolean; content: string } | null {\n let json: unknown;\n try {\n json = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (!isMintlifyDocsJson(filePath, json)) return null;\n\n let changed = false;\n\n const visitNode = (node: unknown, activeLocale?: string) => {\n if (Array.isArray(node)) {\n node.forEach((item) => visitNode(item, activeLocale));\n return;\n }\n if (!isRecord(node)) return;\n\n let nextLocale = activeLocale;\n if (typeof node.language === 'string') {\n nextLocale = node.language;\n }\n\n const locale = nextLocale || localeHint || defaultLocale;\n\n if (typeof node.openapi === 'string') {\n const sourceValue = node.openapi;\n const localizedSource = localizeDocsJsonSpecPath(\n sourceValue,\n locale,\n filePath,\n specs,\n fileMappingAbs,\n fileMappingRel,\n warnings,\n configDir\n );\n if (localizedSource && localizedSource !== sourceValue) {\n node.openapi = localizedSource;\n changed = true;\n }\n } else if (isRecord(node.openapi)) {\n const openapiConfig = node.openapi as Record<string, unknown>;\n const sourceValue = openapiConfig.source;\n if (typeof sourceValue === 'string') {\n const localizedSource = localizeDocsJsonSpecPath(\n sourceValue,\n locale,\n filePath,\n specs,\n fileMappingAbs,\n fileMappingRel,\n warnings,\n configDir\n );\n if (localizedSource && localizedSource !== sourceValue) {\n openapiConfig.source = localizedSource;\n changed = true;\n }\n }\n\n const directoryValue = openapiConfig.directory;\n if (typeof directoryValue === 'string') {\n const localizedDirectory = localizeDocsJsonDirectory(\n directoryValue,\n locale,\n defaultLocale,\n new Set([defaultLocale, ...Object.keys(fileMappingRel)])\n );\n if (localizedDirectory && localizedDirectory !== directoryValue) {\n openapiConfig.directory = localizedDirectory;\n changed = true;\n }\n }\n }\n\n if (Array.isArray(node.pages)) {\n const pages = node.pages;\n for (let i = 0; i < pages.length; i += 1) {\n const page = pages[i];\n if (typeof page !== 'string') continue;\n const updated = stripLocaleFromOpenApiPage(page, locale);\n if (updated !== page) {\n pages[i] = updated;\n changed = true;\n }\n }\n }\n\n for (const value of Object.values(node)) {\n visitNode(value, nextLocale);\n }\n };\n\n visitNode(json, undefined);\n\n if (!changed) return null;\n return { changed, content: JSON.stringify(json, null, 2) };\n}\n\nfunction stripLocaleFromOpenApiPage(value: string, locale: string): string {\n const trimmed = value.trim();\n const prefix = `${locale}/`;\n if (!trimmed.startsWith(prefix)) return value;\n const candidate = trimmed.slice(prefix.length);\n const parsed = parseOpenApiValue(candidate);\n if (!parsed) return value;\n return candidate;\n}\n\nfunction localizeDocsJsonDirectory(\n directory: string,\n locale: string,\n defaultLocale: string,\n knownLocales: Set<string>\n): string | null {\n if (locale === defaultLocale) return null;\n\n const trimmed = directory.trim();\n if (!trimmed || /^(?:[a-z][a-z0-9+.-]*:|\\/\\/|#|\\.\\/|\\.\\.\\/)/i.test(trimmed)) {\n return null;\n }\n\n const leadingWhitespace = directory.match(/^\\s*/)?.[0] ?? '';\n const trailingWhitespace = directory.match(/\\s*$/)?.[0] ?? '';\n const leadingSlash = trimmed.startsWith('/') ? '/' : '';\n const pathBody = trimmed.replace(/^\\/+/, '');\n const [firstSegment, ...restSegments] = pathBody.split('/');\n\n if (firstSegment === locale) {\n const normalized = `${leadingWhitespace}${leadingSlash}${pathBody}${trailingWhitespace}`;\n return normalized === directory ? null : normalized;\n }\n\n const unprefixedPath = knownLocales.has(firstSegment)\n ? restSegments.join('/')\n : pathBody;\n const localizedPath = unprefixedPath ? `${locale}/${unprefixedPath}` : locale;\n const normalized = `${leadingWhitespace}${leadingSlash}${localizedPath}${trailingWhitespace}`;\n\n return normalized === directory ? null : normalized;\n}\n\nfunction localizeDocsJsonSpecPath(\n source: string,\n locale: string,\n filePath: string,\n specs: SpecAnalysis[],\n fileMappingAbs: Record<string, Record<string, string>>,\n _fileMappingRel: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string\n): string | null {\n const resolvedAbs = resolveDocsJsonSpecPath(source, filePath, configDir);\n const matched = matchSpecBySource(source, resolvedAbs, specs, warnings);\n if (!matched) return source;\n\n const localizedSpecPath = resolveLocalizedSpecPath(\n matched,\n locale,\n fileMappingAbs,\n configDir,\n source\n );\n const localizedAbs = resolveDocsJsonSpecPath(\n localizedSpecPath,\n filePath,\n configDir\n );\n if (!fs.existsSync(localizedAbs)) {\n warnings.add(\n `OpenAPI source \"${source}\" localized for locale \"${locale}\" points to a missing file (${localizedAbs}). Keeping original source.`\n );\n return source;\n }\n\n const rel = normalizeSlashes(path.relative(configDir, localizedAbs));\n return formatSpecPathForDocsJson(rel, source);\n}\n\nfunction resolveDocsJsonSpecPath(\n source: string,\n filePath: string,\n configDir: string\n): string {\n if (source.startsWith('/')) {\n return path.resolve(configDir, source.replace(/^\\/+/, ''));\n }\n if (source.startsWith('./') || source.startsWith('../')) {\n return path.resolve(path.dirname(filePath), source);\n }\n return path.resolve(configDir, source);\n}\n\nfunction matchSpecBySource(\n source: string,\n resolvedAbs: string,\n specs: SpecAnalysis[],\n warnings: Set<string>\n): SpecAnalysis | null {\n const exact = specs.find((spec) => samePath(resolvedAbs, spec.absPath));\n if (exact) return exact;\n\n const normalizedExplicit = normalizeSlashes(source).replace(/^\\.?\\/+/, '');\n const explicitWithoutExt = stripExtension(normalizedExplicit);\n const explicitBase = path.basename(normalizedExplicit);\n const explicitBaseWithoutExt = stripExtension(explicitBase);\n const matches = specs.filter((spec) => {\n const configPath = normalizeSlashes(spec.configPath).replace(/^\\.?\\/+/, '');\n const configBase = path.basename(configPath);\n const configPathNoExt = stripExtension(configPath);\n const configBaseNoExt = stripExtension(configBase);\n return (\n configPath === normalizedExplicit ||\n configPathNoExt === explicitWithoutExt ||\n configBase === explicitBase ||\n configBaseNoExt === explicitBaseWithoutExt\n );\n });\n\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI source \"${source}\" matches multiple specs (${matches\n .map((m) => m.configPath)\n .join(\n ', '\n )}). Using the first configured match (${matches[0].configPath}).`\n );\n return matches[0];\n }\n\n return null;\n}\n\nfunction formatSpecPathForDocsJson(\n relativePath: string,\n originalPathText: string\n): string {\n const normalized = normalizeSlashes(relativePath);\n const base = normalized.replace(/^\\.\\//, '').replace(/\\/+/g, '/');\n\n if (originalPathText.startsWith('/')) {\n return `/${base.replace(/^\\/+/, '')}`;\n }\n if (originalPathText.startsWith('../')) {\n return normalized;\n }\n if (originalPathText.startsWith('./')) {\n return `./${base.replace(/^\\/+/, '')}`;\n }\n return base.replace(/^\\/+/, '');\n}\n\n/**\n * Resolve configured OpenAPI files to absolute paths and collect the operations,\n * schemas, and webhooks they expose. Warns and skips when files are missing,\n * unsupported (non-JSON/YAML), or fail to parse so later steps can continue gracefully.\n */\nfunction buildSpecAnalyses(\n openapiFiles: string[],\n configDir: string\n): SpecAnalysis[] {\n const analyses: SpecAnalysis[] = [];\n\n for (const configEntry of openapiFiles) {\n const absPath = path.resolve(configDir, configEntry);\n if (!fs.existsSync(absPath)) {\n logger.warn(`OpenAPI file not found: ${configEntry}`);\n continue;\n }\n const ext = path.extname(absPath).toLowerCase();\n if (!OPENAPI_SPEC_EXTENSIONS.has(ext)) {\n logger.warn(\n `Skipping OpenAPI file (only .json/.yml/.yaml supported): ${configEntry}`\n );\n continue;\n }\n\n let spec: unknown;\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n spec = ext === '.json' ? JSON.parse(raw) : YAML.parse(raw);\n } catch {\n const format = ext === '.json' ? 'JSON' : 'YAML';\n logger.warn(`Failed to parse OpenAPI ${format}: ${configEntry}`);\n continue;\n }\n\n analyses.push({\n absPath,\n configPath: configEntry,\n operations: extractOperations(spec),\n schemas: extractSchemas(spec),\n webhooks: extractWebhooks(spec),\n });\n }\n\n return analyses;\n}\n\nfunction isRecord(val: unknown): val is Record<string, unknown> {\n return typeof val === 'object' && val !== null;\n}\n\n/**\n * Collect path+method identifiers (e.g., \"POST /foo\") from an OpenAPI spec.\n * Safely no-ops when paths is missing or malformed.\n */\nfunction extractOperations(spec: unknown): Set<string> {\n const ops = new Set<string>();\n if (!isRecord(spec) || !isRecord(spec.paths)) return ops;\n const paths = spec.paths as Record<string, unknown>;\n\n for (const [route, methods] of Object.entries(paths)) {\n if (!isRecord(methods)) continue;\n for (const [method, operation] of Object.entries(methods)) {\n if (!isRecord(operation)) continue;\n const upper = method.toUpperCase();\n if (!HTTP_METHODS.has(upper)) continue;\n ops.add(`${upper} ${route}`);\n }\n }\n return ops;\n}\n\n/**\n * Collect schema names from components.schemas.\n * Returns empty set if components/schemas are missing or malformed.\n */\nfunction extractSchemas(spec: unknown): Set<string> {\n if (!isRecord(spec) || !isRecord(spec.components)) return new Set();\n const components = spec.components as Record<string, unknown>;\n if (!isRecord(components.schemas)) return new Set();\n return new Set(Object.keys(components.schemas as Record<string, unknown>));\n}\n\n/**\n * Collect webhook names from webhooks (OpenAPI 3.1+).\n * Returns empty set if webhooks is missing or malformed.\n */\nfunction extractWebhooks(spec: unknown): Set<string> {\n if (!isRecord(spec) || !isRecord(spec.webhooks)) return new Set();\n return new Set(Object.keys(spec.webhooks as Record<string, unknown>));\n}\n\n/**\n * Parse MDX/MD frontmatter, rewrite openapi/openapi-schema entries to the\n * resolved (possibly localized) spec path, and return updated content.\n * Uses remark to find the YAML node so the rest of the document remains\n * untouched. When parsing fails or no relevant keys exist, it returns null.\n */\nfunction rewriteFrontmatter(\n content: string,\n filePath: string,\n locale: string,\n specs: SpecAnalysis[],\n fileMapping: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string\n): { changed: boolean; content: string } | null {\n let tree: Root;\n try {\n tree = unified()\n .use(remarkParse)\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .parse(content) as Root;\n } catch {\n return null;\n }\n\n const yamlNode = (tree.children as Content[]).find(\n (node): node is Yaml => (node as Yaml).type === 'yaml'\n );\n if (\n !yamlNode ||\n !yamlNode.position ||\n yamlNode.position.start?.offset === undefined\n ) {\n return null;\n }\n\n const start = yamlNode.position.start.offset as number;\n const end = yamlNode.position.end.offset as number;\n const frontmatterRaw: string = yamlNode.value || '';\n\n const doc = YAML.parseDocument(frontmatterRaw, {\n prettyErrors: false,\n keepSourceTokens: true,\n });\n if (doc.errors?.length) return null;\n if (!isMap(doc.contents)) return null;\n\n let changed = false;\n\n const openapiNode = doc.get('openapi', true);\n if (isScalar(openapiNode) && typeof openapiNode.value === 'string') {\n const parsedValue = parseOpenApiValue(openapiNode.value);\n if (parsedValue) {\n const matchKey =\n parsedValue.kind === 'operation'\n ? {\n type: 'operation' as const,\n key: `${parsedValue.method.toUpperCase()} ${parsedValue.operationPath}`,\n }\n : { type: 'webhook' as const, key: parsedValue.name };\n\n const spec = resolveSpec(\n parsedValue.specPath,\n specs,\n filePath,\n configDir,\n warnings,\n describeOpenApiRef(parsedValue),\n matchKey\n );\n if (spec) {\n const descriptor = formatOpenApiDescriptor(parsedValue);\n const localizedSpecPath = resolveLocalizedSpecPath(\n spec,\n locale,\n fileMapping,\n configDir,\n parsedValue.specPath || spec.configPath\n );\n const newValue = `${localizedSpecPath} ${descriptor}`.trim();\n if (newValue !== openapiNode.value) {\n doc.set('openapi', newValue);\n changed = true;\n }\n }\n }\n }\n\n const schemaNode = doc.get('openapi-schema', true);\n if (isScalar(schemaNode) && typeof schemaNode.value === 'string') {\n const parsedValue = parseSchemaValue(schemaNode.value);\n if (parsedValue) {\n const spec = resolveSpec(\n parsedValue.specPath,\n specs,\n filePath,\n configDir,\n warnings,\n `schema \"${parsedValue.schemaName}\"`,\n { type: 'schema', key: parsedValue.schemaName }\n );\n if (spec) {\n const localizedSpecPath = resolveLocalizedSpecPath(\n spec,\n locale,\n fileMapping,\n configDir,\n parsedValue.specPath || spec.configPath\n );\n const newValue =\n `${localizedSpecPath} ${parsedValue.schemaName}`.trim();\n if (newValue !== schemaNode.value) {\n doc.set('openapi-schema', newValue);\n changed = true;\n }\n }\n }\n }\n\n if (!changed) return null;\n\n const fmString = doc.toString().trimEnd();\n const rebuilt = `${content.slice(0, start)}---\\n${fmString}\\n---${content.slice(end)}`;\n return { changed, content: rebuilt };\n}\n\nfunction stripWrappingQuotes(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '');\n}\n\n/**\n * Parse frontmatter openapi string into spec/method/path or webhook.\n * Supports optional leading spec file, the webhook keyword, quoted values,\n * and forgiving whitespace. Returns null when the structure is unrecognized.\n */\nfunction parseOpenApiValue(value: string): ParsedOpenApiValue | null {\n const stripped = stripWrappingQuotes(value);\n const tokens = stripped.split(/\\s+/).filter(Boolean);\n if (!tokens.length) return null;\n\n let cursor = 0;\n let specPath: string | undefined;\n const first = tokens[0];\n const second = tokens[1];\n const methodCandidate = second?.toUpperCase();\n const firstLooksLikeSpec =\n hasOpenApiSpecExtension(first) ||\n (second &&\n (second.toLowerCase() === 'webhook' ||\n (methodCandidate && HTTP_METHODS.has(methodCandidate))));\n\n if (firstLooksLikeSpec) {\n specPath = tokens[0];\n cursor = 1;\n }\n if (cursor >= tokens.length) return null;\n\n const keyword = tokens[cursor];\n if (keyword.toLowerCase() === 'webhook') {\n const name = tokens.slice(cursor + 1).join(' ');\n if (!name) return null;\n return { kind: 'webhook', specPath, name };\n }\n\n const method = keyword.toUpperCase();\n if (!HTTP_METHODS.has(method)) return null;\n const operationPath = tokens.slice(cursor + 1).join(' ');\n if (!operationPath) return null;\n return { kind: 'operation', specPath, method, operationPath };\n}\n\n/**\n * Parse frontmatter openapi-schema string into spec/schemaName.\n * Accepts optional leading spec file and quoted values; returns null on invalid\n * shapes so callers can skip rewrites gracefully.\n */\nfunction parseSchemaValue(value: string): ParsedSchemaValue | null {\n const stripped = stripWrappingQuotes(value);\n const tokens = stripped.split(/\\s+/).filter(Boolean);\n if (!tokens.length) return null;\n let cursor = 0;\n let specPath: string | undefined;\n if (hasOpenApiSpecExtension(tokens[0])) {\n specPath = tokens[0];\n cursor = 1;\n }\n const schemaName = tokens.slice(cursor).join(' ');\n if (!schemaName) return null;\n return { specPath, schemaName };\n}\n\n/**\n * Choose which configured spec a reference should use.\n * - If an explicit spec path is provided, resolve it relative to the config\n * and the referencing file, warn when unknown, and bail.\n * - Otherwise, try to match by operation/webhook/schema name; resolve\n * ambiguity using config order and warn when ambiguous or missing.\n */\nfunction resolveSpec(\n explicitPath: string | undefined,\n specs: SpecAnalysis[],\n filePath: string,\n configDir: string,\n warnings: Set<string>,\n refDescription: string,\n match: { type: 'operation' | 'webhook' | 'schema'; key: string }\n): SpecAnalysis | null {\n if (!specs.length) return null;\n\n if (explicitPath) {\n const normalizedExplicit = normalizeSlashes(\n explicitPath.replace(/^\\.?\\/+/, '')\n );\n const candidates = [\n path.resolve(configDir, normalizedExplicit),\n path.resolve(path.dirname(filePath), normalizedExplicit),\n ];\n const foundSpec = specs.find((spec) => {\n const normalizedSpecPath = normalizeSlashes(spec.absPath);\n return candidates.some((candidate) =>\n samePath(candidate, normalizedSpecPath)\n );\n });\n if (foundSpec) {\n if (specHasMatch(foundSpec, match)) {\n return foundSpec;\n }\n\n const alternatives = specs.filter(\n (spec) => spec !== foundSpec && specHasMatch(spec, match)\n );\n if (alternatives.length === 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found there. Using ${alternatives[0].configPath} instead.`\n );\n return alternatives[0];\n }\n if (alternatives.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found there and matches multiple specs (${alternatives\n .map((spec) => spec.configPath)\n .join(', ')}). Skipping localization for this reference.`\n );\n return null;\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found in any configured spec. Skipping localization for this reference.`\n );\n return null;\n }\n\n const explicitWithoutExt = stripExtension(normalizedExplicit);\n const explicitBase = path.basename(normalizedExplicit);\n const explicitBaseWithoutExt = stripExtension(explicitBase);\n const matches = specs.filter((spec) => {\n const configPath = normalizeSlashes(spec.configPath).replace(\n /^\\.?\\/+/,\n ''\n );\n const configBase = path.basename(configPath);\n const configPathNoExt = stripExtension(configPath);\n const configBaseNoExt = stripExtension(configBase);\n return (\n configPath === normalizedExplicit ||\n configPathNoExt === explicitWithoutExt ||\n configBase === explicitBase ||\n configBaseNoExt === explicitBaseWithoutExt\n );\n });\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} matches multiple specs (${matches\n .map((m) => m.configPath)\n .join(\n ', '\n )}). Using the first configured match (${matches[0].configPath}).`\n );\n return matches[0];\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to an unconfigured spec (${explicitPath}). Skipping localization for this reference.`\n );\n return null;\n }\n\n // No explicit spec: try to find by contents\n const matches = specs.filter((spec) => {\n if (match.type === 'schema') {\n return spec.schemas.has(match.key);\n }\n if (match.type === 'webhook') {\n return spec.webhooks.has(match.key);\n }\n // operation\n return spec.operations.has(match.key);\n });\n\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} is available in multiple specs. Skipping localization for this reference.`\n );\n return null;\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} was not found in any configured spec. Skipping localization for this reference.`\n );\n return null;\n}\n\nfunction specHasMatch(\n spec: SpecAnalysis,\n match: { type: 'operation' | 'webhook' | 'schema'; key: string }\n): boolean {\n if (match.type === 'schema') {\n return spec.schemas.has(match.key);\n }\n if (match.type === 'webhook') {\n return spec.webhooks.has(match.key);\n }\n return spec.operations.has(match.key);\n}\n\n/**\n * Map a spec to the locale-specific file path when available and normalize it\n * for frontmatter. Falls back to the source spec when the locale copy does\n * not exist to preserve deterministic behavior.\n */\nfunction resolveLocalizedSpecPath(\n spec: SpecAnalysis,\n locale: string,\n fileMapping: Record<string, Record<string, string>>,\n configDir: string,\n originalPathText?: string\n): string {\n const mapping = fileMapping[locale]?.[spec.absPath];\n const chosenAbs = mapping || spec.absPath;\n const rel = normalizeSlashes(path.relative(configDir, chosenAbs));\n const rooted = `/${rel.replace(/^\\/+/, '')}`;\n return formatSpecPathForFrontmatter(\n rooted,\n originalPathText || spec.configPath\n );\n}\n\n/**\n * Format the path that will be written back to frontmatter:\n * - Preserve the user's absolute style when they used a leading slash.\n * - Preserve upward relative references (../) exactly.\n * - Otherwise return a repo-root-relative path with a leading slash so Mintlify\n * resolves consistently regardless of the MDX file location.\n */\nfunction formatSpecPathForFrontmatter(\n relativePath: string,\n originalPathText: string\n): string {\n const normalized = normalizeSlashes(relativePath);\n const base = normalized.replace(/^\\.\\//, '').replace(/\\/+/g, '/');\n\n if (originalPathText.startsWith('/')) {\n // Force repo-root absolute style\n return `/${base.replace(/^\\/+/, '')}`;\n }\n\n if (originalPathText.startsWith('../')) {\n // Preserve explicit relative upward references\n return normalized;\n }\n\n // Default to repo-root relative with leading slash to avoid resolving relative to the MDX directory\n return `/${base.replace(/^\\/+/, '')}`;\n}\n\n/** Normalize the descriptive portion after the spec path for frontmatter. */\nfunction formatOpenApiDescriptor(value: ParsedOpenApiValue): string {\n if (value.kind === 'webhook') return `webhook ${value.name}`;\n return `${value.method.toUpperCase()} ${value.operationPath}`;\n}\n\n/** Human-readable description a specific OpenAPI reference. */\nfunction describeOpenApiRef(value: ParsedOpenApiValue): string {\n if (value.kind === 'webhook') return `webhook ${value.name}`;\n return `${value.method.toUpperCase()} ${value.operationPath}`;\n}\n\n/** Remove a single trailing file extension while preserving directory segments. */\nfunction stripExtension(p: string): string {\n const parsed = path.parse(p);\n return normalizeSlashes(path.join(parsed.dir, parsed.name));\n}\n\nfunction hasOpenApiSpecExtension(value: string): boolean {\n return OPENAPI_SPEC_EXTENSIONS.has(path.extname(value).toLowerCase());\n}\n\n/** Normalize separators for stable comparisons and output. */\nfunction normalizeSlashes(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/** Compare paths after resolution to avoid casing/separator mismatches. */\nfunction samePath(a: string, b: string): boolean {\n return path.resolve(a) === path.resolve(b);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"processOpenApi.js","names":[],"sources":["../../src/utils/processOpenApi.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkFrontmatter from 'remark-frontmatter';\nimport YAML, { isMap, isScalar } from 'yaml';\nimport type { Root, Content, Yaml } from 'mdast';\nimport { logger } from '../console/logger.js';\nimport { createFileMapping } from '../formats/files/fileMapping.js';\nimport { Settings } from '../types/index.js';\n\ntype SpecAnalysis = {\n absPath: string;\n configPath: string; // as provided in config (for formatting)\n operations: Set<string>;\n schemas: Set<string>;\n webhooks: Set<string>;\n};\n\ntype ParsedOpenApiValue =\n | {\n kind: 'operation';\n specPath?: string;\n method: string;\n operationPath: string;\n }\n | {\n kind: 'webhook';\n specPath?: string;\n name: string;\n };\n\ntype ParsedSchemaValue = {\n specPath?: string;\n schemaName: string;\n};\n\nconst HTTP_METHODS = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'OPTIONS',\n 'HEAD',\n 'TRACE',\n]);\nconst OPENAPI_SPEC_EXTENSIONS = new Set(['.json', '.yaml', '.yml']);\n\n/**\n * Postprocess Mintlify OpenAPI references to point to locale-specific spec files.\n * - Uses openapi.files (ordered) to resolve ambiguities (first match wins).\n * - Relies on the user's json transform rules for locale paths.\n * - Warns on missing/ambiguous references but keeps behavior deterministic.\n */\nexport default async function processOpenApi(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const openapiConfig = settings.options?.mintlify?.openapi;\n if (!openapiConfig || !openapiConfig.files?.length) return;\n if (!settings.files) return;\n\n const configDir = path.dirname(settings.config);\n const specAnalyses = buildSpecAnalyses(openapiConfig.files, configDir);\n if (!specAnalyses.length) return;\n\n const warnings = new Set<string>();\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n const fileMappingAbs: Record<string, Record<string, string>> = {};\n for (const [locale, mapping] of Object.entries(fileMapping)) {\n fileMappingAbs[locale] = {};\n for (const [src, dest] of Object.entries(mapping)) {\n const absSrc = path.resolve(configDir, src);\n const absDest = path.resolve(configDir, dest);\n fileMappingAbs[locale][absSrc] = absDest;\n }\n }\n\n // Also rewrite default-locale source files so they use the deterministic spec selection\n const defaultFiles = [\n ...(resolvedPaths.mdx || []),\n ...(resolvedPaths.md || []),\n ];\n for (const filePath of defaultFiles) {\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf8');\n const updated = rewriteFrontmatter(\n content,\n filePath,\n settings.defaultLocale,\n specAnalyses,\n fileMappingAbs,\n warnings,\n configDir\n );\n if (updated?.changed) {\n await fs.promises.writeFile(filePath, updated.content, 'utf8');\n }\n }\n\n for (const [locale, filesMap] of Object.entries(fileMapping)) {\n const targetFiles = Object.values(filesMap).filter(\n (p) =>\n (p.endsWith('.md') || p.endsWith('.mdx')) &&\n (!includeFiles || includeFiles.has(p))\n );\n\n for (const filePath of targetFiles) {\n if (!fs.existsSync(filePath)) continue;\n const content = fs.readFileSync(filePath, 'utf8');\n const updated = rewriteFrontmatter(\n content,\n filePath,\n locale,\n specAnalyses,\n fileMappingAbs,\n warnings,\n configDir\n );\n\n if (updated?.changed) {\n await fs.promises.writeFile(filePath, updated.content, 'utf8');\n }\n }\n }\n\n const docsJsonTargets = collectDocsJsonTargets(\n settings,\n fileMapping,\n includeFiles\n );\n for (const target of docsJsonTargets) {\n if (!fs.existsSync(target.path)) continue;\n const content = fs.readFileSync(target.path, 'utf8');\n const updated = rewriteDocsJsonOpenApi(\n content,\n target.path,\n target.localeHint,\n specAnalyses,\n fileMappingAbs,\n fileMapping,\n warnings,\n configDir,\n settings.defaultLocale\n );\n if (updated?.changed) {\n await fs.promises.writeFile(target.path, updated.content, 'utf8');\n }\n }\n\n for (const message of warnings) {\n logger.warn(message);\n }\n}\n\nfunction collectDocsJsonTargets(\n settings: Settings,\n fileMapping: Record<string, Record<string, string>>,\n includeFiles?: Set<string>\n): Array<{ path: string; localeHint?: string }> {\n const targets: Array<{ path: string; localeHint?: string }> = [];\n const seen = new Map<string, Set<string>>();\n\n const addTarget = (filePath: string, locale?: string) => {\n const canonicalPath = path.resolve(filePath);\n if (\n includeFiles &&\n !includeFiles.has(filePath) &&\n !includeFiles.has(canonicalPath)\n ) {\n return;\n }\n const locales = seen.get(canonicalPath) ?? new Set<string>();\n if (locale) locales.add(locale);\n seen.set(canonicalPath, locales);\n };\n\n if (!includeFiles && settings.files?.resolvedPaths.json) {\n for (const filePath of settings.files.resolvedPaths.json) {\n addTarget(filePath, settings.defaultLocale);\n }\n }\n\n for (const [locale, filesMap] of Object.entries(fileMapping)) {\n for (const filePath of Object.values(filesMap)) {\n if (!filePath.endsWith('.json')) continue;\n addTarget(filePath, locale);\n }\n }\n\n for (const [filePath, locales] of seen.entries()) {\n const localeHint = locales.size === 1 ? Array.from(locales)[0] : undefined;\n targets.push({ path: filePath, localeHint });\n }\n\n return targets;\n}\n\nfunction isMintlifyDocsJson(filePath: string, json: unknown): boolean {\n if (!isRecord(json)) return false;\n const schema = json.$schema;\n if (typeof schema === 'string') {\n return (\n schema.includes('mintlify.com/docs.json') ||\n schema.includes('mintlify.com/mint.json')\n );\n }\n const base = path.basename(filePath);\n return base === 'docs.json' || base === 'mint.json';\n}\n\nfunction rewriteDocsJsonOpenApi(\n content: string,\n filePath: string,\n localeHint: string | undefined,\n specs: SpecAnalysis[],\n fileMappingAbs: Record<string, Record<string, string>>,\n fileMappingRel: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string,\n defaultLocale: string\n): { changed: boolean; content: string } | null {\n let json: unknown;\n try {\n json = JSON.parse(content);\n } catch {\n return null;\n }\n\n if (!isMintlifyDocsJson(filePath, json)) return null;\n\n let changed = false;\n\n const visitNode = (node: unknown, activeLocale?: string) => {\n if (Array.isArray(node)) {\n node.forEach((item) => visitNode(item, activeLocale));\n return;\n }\n if (!isRecord(node)) return;\n\n let nextLocale = activeLocale;\n if (typeof node.language === 'string') {\n nextLocale = node.language;\n }\n\n const locale = nextLocale || localeHint || defaultLocale;\n\n if (typeof node.openapi === 'string') {\n const sourceValue = node.openapi;\n const localizedSource = localizeDocsJsonSpecPath(\n sourceValue,\n locale,\n filePath,\n specs,\n fileMappingAbs,\n fileMappingRel,\n warnings,\n configDir\n );\n if (localizedSource && localizedSource !== sourceValue) {\n node.openapi = localizedSource;\n changed = true;\n }\n } else if (isRecord(node.openapi)) {\n const openapiConfig = node.openapi as Record<string, unknown>;\n const sourceValue = openapiConfig.source;\n if (typeof sourceValue === 'string') {\n const localizedSource = localizeDocsJsonSpecPath(\n sourceValue,\n locale,\n filePath,\n specs,\n fileMappingAbs,\n fileMappingRel,\n warnings,\n configDir\n );\n if (localizedSource && localizedSource !== sourceValue) {\n openapiConfig.source = localizedSource;\n changed = true;\n }\n }\n\n const directoryValue = openapiConfig.directory;\n if (typeof directoryValue === 'string') {\n const localizedDirectory = localizeDocsJsonDirectory(\n directoryValue,\n locale,\n defaultLocale,\n new Set([defaultLocale, ...Object.keys(fileMappingRel)])\n );\n if (localizedDirectory && localizedDirectory !== directoryValue) {\n openapiConfig.directory = localizedDirectory;\n changed = true;\n }\n }\n }\n\n if (Array.isArray(node.pages)) {\n const pages = node.pages;\n for (let i = 0; i < pages.length; i += 1) {\n const page = pages[i];\n if (typeof page !== 'string') continue;\n const updated = stripLocaleFromOpenApiPage(page, locale);\n if (updated !== page) {\n pages[i] = updated;\n changed = true;\n }\n }\n }\n\n for (const value of Object.values(node)) {\n visitNode(value, nextLocale);\n }\n };\n\n visitNode(json, undefined);\n\n if (!changed) return null;\n return { changed, content: JSON.stringify(json, null, 2) };\n}\n\nfunction stripLocaleFromOpenApiPage(value: string, locale: string): string {\n const trimmed = value.trim();\n const prefix = `${locale}/`;\n if (!trimmed.startsWith(prefix)) return value;\n const candidate = trimmed.slice(prefix.length);\n const parsed = parseOpenApiValue(candidate);\n if (!parsed) return value;\n return candidate;\n}\n\nfunction localizeDocsJsonDirectory(\n directory: string,\n locale: string,\n defaultLocale: string,\n knownLocales: Set<string>\n): string | null {\n if (locale === defaultLocale) return null;\n\n const trimmed = directory.trim();\n if (!trimmed || /^(?:[a-z][a-z0-9+.-]*:|\\/\\/|#|\\.\\/|\\.\\.\\/)/i.test(trimmed)) {\n return null;\n }\n\n const leadingWhitespace = directory.match(/^\\s*/)?.[0] ?? '';\n const trailingWhitespace = directory.match(/\\s*$/)?.[0] ?? '';\n const leadingSlash = trimmed.startsWith('/') ? '/' : '';\n const pathBody = trimmed.replace(/^\\/+/, '');\n const [firstSegment, ...restSegments] = pathBody.split('/');\n\n if (firstSegment === locale) {\n const normalized = `${leadingWhitespace}${leadingSlash}${pathBody}${trailingWhitespace}`;\n return normalized === directory ? null : normalized;\n }\n\n const unprefixedPath = knownLocales.has(firstSegment)\n ? restSegments.join('/')\n : pathBody;\n const localizedPath = unprefixedPath ? `${locale}/${unprefixedPath}` : locale;\n const normalized = `${leadingWhitespace}${leadingSlash}${localizedPath}${trailingWhitespace}`;\n\n return normalized === directory ? null : normalized;\n}\n\nfunction localizeDocsJsonSpecPath(\n source: string,\n locale: string,\n filePath: string,\n specs: SpecAnalysis[],\n fileMappingAbs: Record<string, Record<string, string>>,\n _fileMappingRel: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string\n): string | null {\n const resolvedAbs = resolveDocsJsonSpecPath(source, filePath, configDir);\n const matched = matchSpecBySource(source, resolvedAbs, specs, warnings);\n if (!matched) return source;\n\n const localizedSpecPath = resolveLocalizedSpecPath(\n matched,\n locale,\n fileMappingAbs,\n configDir,\n source\n );\n const localizedAbs = resolveDocsJsonSpecPath(\n localizedSpecPath,\n filePath,\n configDir\n );\n if (!fs.existsSync(localizedAbs)) {\n warnings.add(\n `OpenAPI source \"${source}\" localized for locale \"${locale}\" points to a missing file (${localizedAbs}). Keeping original source.`\n );\n return source;\n }\n\n const rel = normalizeSlashes(path.relative(configDir, localizedAbs));\n return formatSpecPathForDocsJson(rel, source);\n}\n\nfunction resolveDocsJsonSpecPath(\n source: string,\n filePath: string,\n configDir: string\n): string {\n if (source.startsWith('/')) {\n return path.resolve(configDir, source.replace(/^\\/+/, ''));\n }\n if (source.startsWith('./') || source.startsWith('../')) {\n return path.resolve(path.dirname(filePath), source);\n }\n return path.resolve(configDir, source);\n}\n\nfunction matchSpecBySource(\n source: string,\n resolvedAbs: string,\n specs: SpecAnalysis[],\n warnings: Set<string>\n): SpecAnalysis | null {\n const exact = specs.find((spec) => samePath(resolvedAbs, spec.absPath));\n if (exact) return exact;\n\n const normalizedExplicit = normalizeSlashes(source).replace(/^\\.?\\/+/, '');\n const explicitWithoutExt = stripExtension(normalizedExplicit);\n const explicitBase = path.basename(normalizedExplicit);\n const explicitBaseWithoutExt = stripExtension(explicitBase);\n const matches = specs.filter((spec) => {\n const configPath = normalizeSlashes(spec.configPath).replace(/^\\.?\\/+/, '');\n const configBase = path.basename(configPath);\n const configPathNoExt = stripExtension(configPath);\n const configBaseNoExt = stripExtension(configBase);\n return (\n configPath === normalizedExplicit ||\n configPathNoExt === explicitWithoutExt ||\n configBase === explicitBase ||\n configBaseNoExt === explicitBaseWithoutExt\n );\n });\n\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI source \"${source}\" matches multiple specs (${matches\n .map((m) => m.configPath)\n .join(\n ', '\n )}). Using the first configured match (${matches[0].configPath}).`\n );\n return matches[0];\n }\n\n return null;\n}\n\nfunction formatSpecPathForDocsJson(\n relativePath: string,\n originalPathText: string\n): string {\n const normalized = normalizeSlashes(relativePath);\n const base = normalized.replace(/^\\.\\//, '').replace(/\\/+/g, '/');\n\n if (originalPathText.startsWith('/')) {\n return `/${base.replace(/^\\/+/, '')}`;\n }\n if (originalPathText.startsWith('../')) {\n return normalized;\n }\n if (originalPathText.startsWith('./')) {\n return `./${base.replace(/^\\/+/, '')}`;\n }\n return base.replace(/^\\/+/, '');\n}\n\n/**\n * Resolve configured OpenAPI files to absolute paths and collect the operations,\n * schemas, and webhooks they expose. Warns and skips when files are missing,\n * unsupported (non-JSON/YAML), or fail to parse so later steps can continue gracefully.\n */\nfunction buildSpecAnalyses(\n openapiFiles: string[],\n configDir: string\n): SpecAnalysis[] {\n const analyses: SpecAnalysis[] = [];\n\n for (const configEntry of openapiFiles) {\n const absPath = path.resolve(configDir, configEntry);\n if (!fs.existsSync(absPath)) {\n logger.warn(`OpenAPI file not found: ${configEntry}`);\n continue;\n }\n const ext = path.extname(absPath).toLowerCase();\n if (!OPENAPI_SPEC_EXTENSIONS.has(ext)) {\n logger.warn(\n `Skipping OpenAPI file (only .json/.yml/.yaml supported): ${configEntry}`\n );\n continue;\n }\n\n let spec: unknown;\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n spec = ext === '.json' ? JSON.parse(raw) : YAML.parse(raw);\n } catch {\n const format = ext === '.json' ? 'JSON' : 'YAML';\n logger.warn(`Failed to parse OpenAPI ${format}: ${configEntry}`);\n continue;\n }\n\n analyses.push({\n absPath,\n configPath: configEntry,\n operations: extractOperations(spec),\n schemas: extractSchemas(spec),\n webhooks: extractWebhooks(spec),\n });\n }\n\n return analyses;\n}\n\nfunction isRecord(val: unknown): val is Record<string, unknown> {\n return typeof val === 'object' && val !== null;\n}\n\n/**\n * Collect path+method identifiers (e.g., \"POST /foo\") from an OpenAPI spec.\n * Safely no-ops when paths is missing or malformed.\n */\nfunction extractOperations(spec: unknown): Set<string> {\n const ops = new Set<string>();\n if (!isRecord(spec) || !isRecord(spec.paths)) return ops;\n const paths = spec.paths as Record<string, unknown>;\n\n for (const [route, methods] of Object.entries(paths)) {\n if (!isRecord(methods)) continue;\n for (const [method, operation] of Object.entries(methods)) {\n if (!isRecord(operation)) continue;\n const upper = method.toUpperCase();\n if (!HTTP_METHODS.has(upper)) continue;\n ops.add(`${upper} ${route}`);\n }\n }\n return ops;\n}\n\n/**\n * Collect schema names from components.schemas.\n * Returns empty set if components/schemas are missing or malformed.\n */\nfunction extractSchemas(spec: unknown): Set<string> {\n if (!isRecord(spec) || !isRecord(spec.components)) return new Set();\n const components = spec.components as Record<string, unknown>;\n if (!isRecord(components.schemas)) return new Set();\n return new Set(Object.keys(components.schemas as Record<string, unknown>));\n}\n\n/**\n * Collect webhook names from webhooks (OpenAPI 3.1+).\n * Returns empty set if webhooks is missing or malformed.\n */\nfunction extractWebhooks(spec: unknown): Set<string> {\n if (!isRecord(spec) || !isRecord(spec.webhooks)) return new Set();\n return new Set(Object.keys(spec.webhooks as Record<string, unknown>));\n}\n\n/**\n * Parse MDX/MD frontmatter, rewrite openapi/openapi-schema entries to the\n * resolved (possibly localized) spec path, and return updated content.\n * Uses remark to find the YAML node so the rest of the document remains\n * untouched. When parsing fails or no relevant keys exist, it returns null.\n */\nfunction rewriteFrontmatter(\n content: string,\n filePath: string,\n locale: string,\n specs: SpecAnalysis[],\n fileMapping: Record<string, Record<string, string>>,\n warnings: Set<string>,\n configDir: string\n): { changed: boolean; content: string } | null {\n let tree: Root;\n try {\n tree = unified()\n .use(remarkParse)\n .use(remarkFrontmatter, ['yaml', 'toml'])\n .parse(content) as Root;\n } catch {\n return null;\n }\n\n const yamlNode = (tree.children as Content[]).find(\n (node): node is Yaml => (node as Yaml).type === 'yaml'\n );\n if (\n !yamlNode ||\n !yamlNode.position ||\n yamlNode.position.start?.offset === undefined\n ) {\n return null;\n }\n\n const start = yamlNode.position.start.offset as number;\n const end = yamlNode.position.end.offset as number;\n const frontmatterRaw: string = yamlNode.value || '';\n\n const doc = YAML.parseDocument(frontmatterRaw, {\n prettyErrors: false,\n keepSourceTokens: true,\n });\n if (doc.errors?.length) return null;\n if (!isMap(doc.contents)) return null;\n\n let changed = false;\n\n const openapiNode = doc.get('openapi', true);\n if (isScalar(openapiNode) && typeof openapiNode.value === 'string') {\n const parsedValue = parseOpenApiValue(openapiNode.value);\n if (parsedValue) {\n const matchKey =\n parsedValue.kind === 'operation'\n ? {\n type: 'operation' as const,\n key: `${parsedValue.method.toUpperCase()} ${parsedValue.operationPath}`,\n }\n : { type: 'webhook' as const, key: parsedValue.name };\n\n const spec = resolveSpec(\n parsedValue.specPath,\n specs,\n filePath,\n configDir,\n warnings,\n describeOpenApiRef(parsedValue),\n matchKey\n );\n if (spec) {\n const descriptor = formatOpenApiDescriptor(parsedValue);\n const localizedSpecPath = resolveLocalizedSpecPath(\n spec,\n locale,\n fileMapping,\n configDir,\n parsedValue.specPath || spec.configPath\n );\n const newValue = `${localizedSpecPath} ${descriptor}`.trim();\n if (newValue !== openapiNode.value) {\n doc.set('openapi', newValue);\n changed = true;\n }\n }\n }\n }\n\n const schemaNode = doc.get('openapi-schema', true);\n if (isScalar(schemaNode) && typeof schemaNode.value === 'string') {\n const parsedValue = parseSchemaValue(schemaNode.value);\n if (parsedValue) {\n const spec = resolveSpec(\n parsedValue.specPath,\n specs,\n filePath,\n configDir,\n warnings,\n `schema \"${parsedValue.schemaName}\"`,\n { type: 'schema', key: parsedValue.schemaName }\n );\n if (spec) {\n const localizedSpecPath = resolveLocalizedSpecPath(\n spec,\n locale,\n fileMapping,\n configDir,\n parsedValue.specPath || spec.configPath\n );\n const newValue =\n `${localizedSpecPath} ${parsedValue.schemaName}`.trim();\n if (newValue !== schemaNode.value) {\n doc.set('openapi-schema', newValue);\n changed = true;\n }\n }\n }\n }\n\n if (!changed) return null;\n\n const fmString = doc.toString().trimEnd();\n const rebuilt = `${content.slice(0, start)}---\\n${fmString}\\n---${content.slice(end)}`;\n return { changed, content: rebuilt };\n}\n\nfunction stripWrappingQuotes(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '');\n}\n\n/**\n * Parse frontmatter openapi string into spec/method/path or webhook.\n * Supports optional leading spec file, the webhook keyword, quoted values,\n * and forgiving whitespace. Returns null when the structure is unrecognized.\n */\nfunction parseOpenApiValue(value: string): ParsedOpenApiValue | null {\n const stripped = stripWrappingQuotes(value);\n const tokens = stripped.split(/\\s+/).filter(Boolean);\n if (!tokens.length) return null;\n\n let cursor = 0;\n let specPath: string | undefined;\n const first = tokens[0];\n const second = tokens[1];\n const methodCandidate = second?.toUpperCase();\n const firstLooksLikeSpec =\n hasOpenApiSpecExtension(first) ||\n (second &&\n (second.toLowerCase() === 'webhook' ||\n (methodCandidate && HTTP_METHODS.has(methodCandidate))));\n\n if (firstLooksLikeSpec) {\n specPath = tokens[0];\n cursor = 1;\n }\n if (cursor >= tokens.length) return null;\n\n const keyword = tokens[cursor];\n if (keyword.toLowerCase() === 'webhook') {\n const name = tokens.slice(cursor + 1).join(' ');\n if (!name) return null;\n return { kind: 'webhook', specPath, name };\n }\n\n const method = keyword.toUpperCase();\n if (!HTTP_METHODS.has(method)) return null;\n const operationPath = tokens.slice(cursor + 1).join(' ');\n if (!operationPath) return null;\n return { kind: 'operation', specPath, method, operationPath };\n}\n\n/**\n * Parse frontmatter openapi-schema string into spec/schemaName.\n * Accepts optional leading spec file and quoted values; returns null on invalid\n * shapes so callers can skip rewrites gracefully.\n */\nfunction parseSchemaValue(value: string): ParsedSchemaValue | null {\n const stripped = stripWrappingQuotes(value);\n const tokens = stripped.split(/\\s+/).filter(Boolean);\n if (!tokens.length) return null;\n let cursor = 0;\n let specPath: string | undefined;\n // Canonical form is \"<spec> <SchemaName>\". Treat the first token as the spec\n // when it has a spec file extension, or when more tokens follow — schema names\n // are single identifiers, so any leading token must be the spec. This covers\n // extensionless spec names (Mintlify auto-discovers specs by file stem, e.g.\n // `events-schema group.created`), mirroring parseOpenApiValue's heuristic.\n if (hasOpenApiSpecExtension(tokens[0]) || tokens.length > 1) {\n specPath = tokens[0];\n cursor = 1;\n }\n const schemaName = tokens.slice(cursor).join(' ');\n if (!schemaName) return null;\n return { specPath, schemaName };\n}\n\n/**\n * Choose which configured spec a reference should use.\n * - If an explicit spec path is provided, resolve it relative to the config\n * and the referencing file, warn when unknown, and bail.\n * - Otherwise, try to match by operation/webhook/schema name; resolve\n * ambiguity using config order and warn when ambiguous or missing.\n */\nfunction resolveSpec(\n explicitPath: string | undefined,\n specs: SpecAnalysis[],\n filePath: string,\n configDir: string,\n warnings: Set<string>,\n refDescription: string,\n match: { type: 'operation' | 'webhook' | 'schema'; key: string }\n): SpecAnalysis | null {\n if (!specs.length) return null;\n\n if (explicitPath) {\n const normalizedExplicit = normalizeSlashes(\n explicitPath.replace(/^\\.?\\/+/, '')\n );\n const candidates = [\n path.resolve(configDir, normalizedExplicit),\n path.resolve(path.dirname(filePath), normalizedExplicit),\n ];\n const foundSpec = specs.find((spec) => {\n const normalizedSpecPath = normalizeSlashes(spec.absPath);\n return candidates.some((candidate) =>\n samePath(candidate, normalizedSpecPath)\n );\n });\n if (foundSpec) {\n if (specHasMatch(foundSpec, match)) {\n return foundSpec;\n }\n\n const alternatives = specs.filter(\n (spec) => spec !== foundSpec && specHasMatch(spec, match)\n );\n if (alternatives.length === 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found there. Using ${alternatives[0].configPath} instead.`\n );\n return alternatives[0];\n }\n if (alternatives.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found there and matches multiple specs (${alternatives\n .map((spec) => spec.configPath)\n .join(', ')}). Skipping localization for this reference.`\n );\n return null;\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to ${foundSpec.configPath}, but the entry was not found in any configured spec. Skipping localization for this reference.`\n );\n return null;\n }\n\n const explicitWithoutExt = stripExtension(normalizedExplicit);\n const explicitBase = path.basename(normalizedExplicit);\n const explicitBaseWithoutExt = stripExtension(explicitBase);\n const matches = specs.filter((spec) => {\n const configPath = normalizeSlashes(spec.configPath).replace(\n /^\\.?\\/+/,\n ''\n );\n const configBase = path.basename(configPath);\n const configPathNoExt = stripExtension(configPath);\n const configBaseNoExt = stripExtension(configBase);\n return (\n configPath === normalizedExplicit ||\n configPathNoExt === explicitWithoutExt ||\n configBase === explicitBase ||\n configBaseNoExt === explicitBaseWithoutExt\n );\n });\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} matches multiple specs (${matches\n .map((m) => m.configPath)\n .join(\n ', '\n )}). Using the first configured match (${matches[0].configPath}).`\n );\n return matches[0];\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} points to an unconfigured spec (${explicitPath}). Skipping localization for this reference.`\n );\n return null;\n }\n\n // No explicit spec: try to find by contents\n const matches = specs.filter((spec) => {\n if (match.type === 'schema') {\n return spec.schemas.has(match.key);\n }\n if (match.type === 'webhook') {\n return spec.webhooks.has(match.key);\n }\n // operation\n return spec.operations.has(match.key);\n });\n\n if (matches.length === 1) return matches[0];\n if (matches.length > 1) {\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} is available in multiple specs. Skipping localization for this reference.`\n );\n return null;\n }\n\n warnings.add(\n `OpenAPI reference ${refDescription} in ${filePath} was not found in any configured spec. Skipping localization for this reference.`\n );\n return null;\n}\n\nfunction specHasMatch(\n spec: SpecAnalysis,\n match: { type: 'operation' | 'webhook' | 'schema'; key: string }\n): boolean {\n if (match.type === 'schema') {\n return spec.schemas.has(match.key);\n }\n if (match.type === 'webhook') {\n return spec.webhooks.has(match.key);\n }\n return spec.operations.has(match.key);\n}\n\n/**\n * Map a spec to the locale-specific file path when available and normalize it\n * for frontmatter. Falls back to the source spec when the locale copy does\n * not exist to preserve deterministic behavior.\n */\nfunction resolveLocalizedSpecPath(\n spec: SpecAnalysis,\n locale: string,\n fileMapping: Record<string, Record<string, string>>,\n configDir: string,\n originalPathText?: string\n): string {\n const mapping = fileMapping[locale]?.[spec.absPath];\n const chosenAbs = mapping || spec.absPath;\n const rel = normalizeSlashes(path.relative(configDir, chosenAbs));\n const rooted = `/${rel.replace(/^\\/+/, '')}`;\n return formatSpecPathForFrontmatter(\n rooted,\n originalPathText || spec.configPath\n );\n}\n\n/**\n * Format the path that will be written back to frontmatter:\n * - Preserve the user's absolute style when they used a leading slash.\n * - Preserve upward relative references (../) exactly.\n * - Otherwise return a repo-root-relative path with a leading slash so Mintlify\n * resolves consistently regardless of the MDX file location.\n */\nfunction formatSpecPathForFrontmatter(\n relativePath: string,\n originalPathText: string\n): string {\n const normalized = normalizeSlashes(relativePath);\n const base = normalized.replace(/^\\.\\//, '').replace(/\\/+/g, '/');\n\n if (originalPathText.startsWith('/')) {\n // Force repo-root absolute style\n return `/${base.replace(/^\\/+/, '')}`;\n }\n\n if (originalPathText.startsWith('../')) {\n // Preserve explicit relative upward references\n return normalized;\n }\n\n // Default to repo-root relative with leading slash to avoid resolving relative to the MDX directory\n return `/${base.replace(/^\\/+/, '')}`;\n}\n\n/** Normalize the descriptive portion after the spec path for frontmatter. */\nfunction formatOpenApiDescriptor(value: ParsedOpenApiValue): string {\n if (value.kind === 'webhook') return `webhook ${value.name}`;\n return `${value.method.toUpperCase()} ${value.operationPath}`;\n}\n\n/** Human-readable description a specific OpenAPI reference. */\nfunction describeOpenApiRef(value: ParsedOpenApiValue): string {\n if (value.kind === 'webhook') return `webhook ${value.name}`;\n return `${value.method.toUpperCase()} ${value.operationPath}`;\n}\n\n/** Remove a single trailing file extension while preserving directory segments. */\nfunction stripExtension(p: string): string {\n const parsed = path.parse(p);\n return normalizeSlashes(path.join(parsed.dir, parsed.name));\n}\n\nfunction hasOpenApiSpecExtension(value: string): boolean {\n return OPENAPI_SPEC_EXTENSIONS.has(path.extname(value).toLowerCase());\n}\n\n/** Normalize separators for stable comparisons and output. */\nfunction normalizeSlashes(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/** Compare paths after resolution to avoid casing/separator mismatches. */\nfunction samePath(a: string, b: string): boolean {\n return path.resolve(a) === path.resolve(b);\n}\n"],"mappings":"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"}
@@ -99,13 +99,21 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
99
99
  })?.[0] === defaultKeyValue;
100
100
  });
101
101
  if (defaultIndex < 0) return;
102
- const navDir = navRefEntry ? path.dirname(navRefEntry.sourceFile) : docsDir;
102
+ const defaultEntryPointer = `${jsonPointer}/${defaultIndex}`;
103
+ const defaultEntryRef = refMap?.get(defaultEntryPointer);
104
+ const arrayHostDir = navRefEntry ? path.dirname(navRefEntry.sourceFile) : docsDir;
105
+ const entryBaseDir = navRefEntry ? path.dirname(navRefEntry.sourceFile) : defaultEntryRef ? path.dirname(defaultEntryRef.sourceFile) : docsDir;
106
+ const navFileName = navRefEntry ? path.basename(navRefEntry.sourceFile) : defaultEntryRef ? path.basename(defaultEntryRef.sourceFile) : path.basename(compositeFilePath);
103
107
  if (refMap && refMap.size > 0) {
104
- const internalRefs = collectInternalRefs(refMap, `${jsonPointer}/${defaultIndex}`);
108
+ const internalRefs = collectInternalRefs(refMap, defaultEntryPointer);
105
109
  if (internalRefs.length > 0) {
106
- const defaultEntry = entries[defaultIndex];
107
- for (const ref of internalRefs) setAtPointer(defaultEntry, ref.relativePointer, { $ref: ref.refPath });
108
- for (const entry of entries) {
110
+ if (!defaultEntryRef) {
111
+ const defaultEntry = entries[defaultIndex];
112
+ for (const ref of internalRefs) setAtPointer(defaultEntry, ref.relativePointer, { $ref: ref.refPath });
113
+ }
114
+ for (let i = 0; i < entries.length; i++) {
115
+ if (i === defaultIndex) continue;
116
+ const entry = entries[i];
109
117
  const entryKeyValues = JSONPath({
110
118
  json: entry,
111
119
  path: keyJsonPath,
@@ -114,23 +122,23 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
114
122
  wrap: true
115
123
  });
116
124
  if (entryKeyValues?.[0] === defaultKeyValue) continue;
125
+ const keyValue = typeof entryKeyValues?.[0] === "string" ? entryKeyValues[0] : "unknown";
117
126
  for (const ref of internalRefs) {
118
127
  const subtree = getAtPointer(entry, ref.relativePointer);
119
128
  if (subtree === void 0) continue;
120
129
  const originalAbsPath = path.resolve(ref.resolvedDir, ref.refPath);
121
- const relToNavDir = path.relative(navDir, originalAbsPath);
122
- const keyValue = typeof entryKeyValues?.[0] === "string" ? entryKeyValues[0] : "unknown";
123
- const localeRelPath = path.join(keyValue, relToNavDir);
124
- writeJsonFile(path.resolve(navDir, localeRelPath), subtree);
130
+ const relToBaseDir = path.relative(entryBaseDir, originalAbsPath);
131
+ const localeRelPath = path.join(keyValue, relToBaseDir);
132
+ writeJsonFile(path.resolve(entryBaseDir, localeRelPath), subtree);
125
133
  setAtPointer(entry, ref.relativePointer, { $ref: ref.refPath });
126
134
  }
127
135
  }
128
136
  logger.info(`Restored $ref structure for default entry`);
129
137
  }
130
138
  }
131
- const navFileName = navRefEntry ? path.basename(navRefEntry.sourceFile) : path.basename(compositeFilePath);
132
139
  const keyPropertyName = keyJsonPath.replace(/^\$\.?/, "");
133
140
  for (let i = 0; i < entries.length; i++) {
141
+ if (i === defaultIndex) continue;
134
142
  const entry = entries[i];
135
143
  if (!entry || typeof entry !== "object") continue;
136
144
  const keyValue = JSONPath({
@@ -143,13 +151,14 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
143
151
  if (typeof keyValue !== "string" || keyValue === defaultKeyValue) continue;
144
152
  if (!isJsonContainer(entry) || Array.isArray(entry)) continue;
145
153
  const { [keyPropertyName]: _, ...contentWithoutKey } = entry;
146
- const entryFileName = `${keyValue}/${navFileName}`;
147
- writeJsonFile(path.resolve(navDir, entryFileName), contentWithoutKey);
154
+ const entryFilePath = path.resolve(entryBaseDir, path.join(keyValue, navFileName));
155
+ writeJsonFile(entryFilePath, contentWithoutKey);
148
156
  entries[i] = {
149
157
  [keyPropertyName]: keyValue,
150
- $ref: `./${entryFileName}`
158
+ $ref: toRelativeRefPath(arrayHostDir, entryFilePath)
151
159
  };
152
160
  }
161
+ if (defaultEntryRef) entries[defaultIndex] = { $ref: defaultEntryRef.refPath };
153
162
  logger.info(`Split keyed entries into ref files`);
154
163
  }
155
164
  /**
@@ -176,6 +185,7 @@ function restoreTopLevelRefs(fileJson, refMap, splitConfig) {
176
185
  for (const [pointer, entry] of entries) {
177
186
  const subtree = getAtPointer(fileJson, pointer);
178
187
  if (subtree === void 0) continue;
188
+ if (isJsonContainer(subtree) && !Array.isArray(subtree) && typeof subtree.$ref === "string") continue;
179
189
  writeJsonFile(entry.sourceFile, subtree);
180
190
  setAtPointer(fileJson, pointer, { $ref: entry.refPath });
181
191
  }
@@ -202,6 +212,15 @@ function writeJsonFile(filePath, data) {
202
212
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
203
213
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
204
214
  }
215
+ /**
216
+ * Build a relative $ref path (POSIX separators, "./"-prefixed when it isn't
217
+ * already a relative or absolute path) from the directory that hosts the
218
+ * languages array to a written entry file.
219
+ */
220
+ function toRelativeRefPath(fromDir, toPath) {
221
+ const rel = path.relative(fromDir, toPath).split(path.sep).join("/");
222
+ return rel.startsWith(".") || rel.startsWith("/") ? rel : `./${rel}`;
223
+ }
205
224
  function getAtPointer(obj, pointer) {
206
225
  if (!pointer || pointer === "/") return obj;
207
226
  const parts = pointer.split("/").filter(Boolean);
@@ -1 +1 @@
1
- {"version":3,"file":"splitMintlifyLanguageRefs.js","names":[],"sources":["../../src/utils/splitMintlifyLanguageRefs.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\nimport { Settings, JsonSchema, SourceObjectOptions } from '../types/index.js';\nimport type { RefMap } from './resolveMintlifyRefs.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { getStoredRefMap, clearStoredRefMap } from '../state/mintlifyRefMap.js';\nimport { JSONPath } from 'jsonpath-plus';\nimport { getLocaleProperties } from '@generaltranslation/format';\n\ntype JsonContainer = Record<string, unknown> | unknown[];\n\nfunction isJsonContainer(value: unknown): value is JsonContainer {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Post-processing step for composite JSON files with splitEntries enabled.\n *\n * After mergeJson writes a fully-inlined composite file, this function:\n * 1. Restores the original $ref structure (if the source used $ref / resolveRefs)\n * 2. Extracts non-default keyed entries into their own ref files\n * to keep the source file compact\n *\n * Driven entirely by the jsonSchema config — reads the composite path,\n * key field, and splitEntries flag from the schema.\n */\nexport async function splitMintlifyLanguageRefs(\n settings: Settings\n): Promise<void> {\n const refMap = getStoredRefMap();\n\n try {\n const resolvedJsonPaths = settings.files?.resolvedPaths?.json;\n if (!resolvedJsonPaths) return;\n\n // Find a JSON file that has splitEntries enabled or resolveRefs\n const targetFile = findTargetFile(resolvedJsonPaths, settings);\n if (!targetFile) return;\n\n const { filePath: compositeFilePath, splitConfig } = targetFile;\n if (!fs.existsSync(compositeFilePath)) return;\n\n let fileJson: unknown;\n try {\n fileJson = JSON.parse(fs.readFileSync(compositeFilePath, 'utf-8'));\n } catch {\n return;\n }\n\n const docsDir = path.dirname(compositeFilePath);\n\n // If splitEntries is configured, process it\n if (splitConfig) {\n processSplitEntries(\n fileJson,\n compositeFilePath,\n docsDir,\n splitConfig,\n settings,\n refMap\n );\n }\n\n // Restore top-level refs if any exist\n if (refMap && refMap.size > 0) {\n restoreTopLevelRefs(fileJson, refMap, splitConfig);\n }\n\n // Always write the composite file back — splitEntries modified the\n // languages array, and restoreTopLevelRefs may not have written it\n // (e.g., when all refs are inside language entries, not top-level)\n fs.writeFileSync(\n compositeFilePath,\n JSON.stringify(fileJson, null, 2),\n 'utf-8'\n );\n } finally {\n clearStoredRefMap();\n }\n}\n\ntype SplitConfig = {\n compositePath: string;\n jsonPointer: string;\n keyField: string;\n keyJsonPath: string;\n sourceObjectOptions: SourceObjectOptions;\n};\n\n/**\n * Find the target file and extract split configuration from the schema.\n */\nfunction findTargetFile(\n resolvedPaths: string[],\n settings: Settings\n): {\n filePath: string;\n schema: JsonSchema;\n splitConfig: SplitConfig | null;\n} | null {\n if (!settings.options?.jsonSchema) return null;\n\n for (const filePath of resolvedPaths) {\n const schema = validateJsonSchema(settings.options, filePath);\n if (!schema) continue;\n\n const hasSplitEntries = schema.composite\n ? Object.entries(schema.composite).some(([, opts]) => opts.splitEntries)\n : false;\n\n const hasResolveRefs = schema.resolveRefs;\n\n if (!hasSplitEntries && !hasResolveRefs) continue;\n\n // Extract split config if available\n let splitConfig: SplitConfig | null = null;\n if (schema.composite) {\n for (const [compositePath, opts] of Object.entries(schema.composite)) {\n if (opts.splitEntries && opts.type === 'array' && opts.key) {\n splitConfig = {\n compositePath,\n jsonPointer: jsonPathToPointer(compositePath),\n keyField: opts.key,\n keyJsonPath: opts.key,\n sourceObjectOptions: opts,\n };\n break;\n }\n }\n }\n\n return { filePath, schema, splitConfig };\n }\n\n return null;\n}\n\n/**\n * Process splitEntries: extract non-default keyed entries into ref files.\n */\nfunction processSplitEntries(\n fileJson: unknown,\n compositeFilePath: string,\n docsDir: string,\n splitConfig: SplitConfig,\n settings: Settings,\n refMap: RefMap | null\n): void {\n const { jsonPointer, keyJsonPath } = splitConfig;\n\n // Find the composite array — may be behind a $ref\n const parentPointer = jsonPointer.split('/').slice(0, -1).join('/') || '';\n const arrayKey = jsonPointer.split('/').pop() || '';\n const navRefEntry = parentPointer ? refMap?.get(parentPointer) : undefined;\n\n // Get the array from the file\n const arrayContainer = parentPointer\n ? getAtPointer(fileJson, parentPointer)\n : fileJson;\n if (!isJsonContainer(arrayContainer)) return;\n\n const entries = Array.isArray(arrayContainer)\n ? arrayContainer[Number(arrayKey)]\n : arrayContainer[arrayKey];\n if (!Array.isArray(entries) || entries.length <= 1) return;\n\n // Determine the default key value (the source entry)\n const defaultKeyValue = getDefaultKeyValue(\n settings.defaultLocale,\n splitConfig.sourceObjectOptions\n );\n\n const defaultIndex = entries.findIndex((e: unknown) => {\n if (!e || typeof e !== 'object') return false;\n const values = JSONPath({\n json: e,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n return values?.[0] === defaultKeyValue;\n });\n if (defaultIndex < 0) return;\n\n // Determine where the composite array actually lives on disk\n const navDir = navRefEntry ? path.dirname(navRefEntry.sourceFile) : docsDir;\n\n // Restore $ref structure if the source used $ref\n if (refMap && refMap.size > 0) {\n const defaultPointerPrefix = `${jsonPointer}/${defaultIndex}`;\n const internalRefs = collectInternalRefs(refMap, defaultPointerPrefix);\n\n if (internalRefs.length > 0) {\n const defaultEntry = entries[defaultIndex];\n for (const ref of internalRefs) {\n setAtPointer(defaultEntry, ref.relativePointer, {\n $ref: ref.refPath,\n });\n }\n\n for (const entry of entries) {\n const entryKeyValues = JSONPath({\n json: entry,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n if (entryKeyValues?.[0] === defaultKeyValue) continue;\n\n for (const ref of internalRefs) {\n const subtree = getAtPointer(entry, ref.relativePointer);\n if (subtree === undefined) continue;\n\n const originalAbsPath = path.resolve(ref.resolvedDir, ref.refPath);\n const relToNavDir = path.relative(navDir, originalAbsPath);\n const keyValue =\n typeof entryKeyValues?.[0] === 'string'\n ? entryKeyValues[0]\n : 'unknown';\n const localeRelPath = path.join(keyValue, relToNavDir);\n const outputPath = path.resolve(navDir, localeRelPath);\n writeJsonFile(outputPath, subtree);\n\n setAtPointer(entry, ref.relativePointer, { $ref: ref.refPath });\n }\n }\n\n logger.info(`Restored $ref structure for default entry`);\n }\n }\n\n // Extract each non-default entry into its own ref file\n const navFileName = navRefEntry\n ? path.basename(navRefEntry.sourceFile)\n : path.basename(compositeFilePath);\n\n // Get the actual property name from the key JSONPath (e.g., \"$.language\" → \"language\")\n const keyPropertyName = keyJsonPath.replace(/^\\$\\.?/, '');\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n if (!entry || typeof entry !== 'object') continue;\n\n const keyValues = JSONPath({\n json: entry,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n const keyValue = keyValues?.[0];\n if (typeof keyValue !== 'string' || keyValue === defaultKeyValue) continue;\n\n if (!isJsonContainer(entry) || Array.isArray(entry)) continue;\n const { [keyPropertyName]: _, ...contentWithoutKey } = entry;\n const entryFileName = `${keyValue}/${navFileName}`;\n const entryFilePath = path.resolve(navDir, entryFileName);\n writeJsonFile(entryFilePath, contentWithoutKey);\n\n entries[i] = { [keyPropertyName]: keyValue, $ref: `./${entryFileName}` };\n }\n\n logger.info(`Split keyed entries into ref files`);\n}\n\n/**\n * Get the identifying key value for the default locale.\n */\nfunction getDefaultKeyValue(\n defaultLocale: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const localeProperties = getLocaleProperties(defaultLocale);\n return (\n (localeProperties as Record<string, string | undefined>)[localeProperty] ||\n localeProperties.code ||\n defaultLocale\n );\n}\n\n/**\n * Convert a JSONPath like \"$.navigation.languages\" to a JSON pointer like \"/navigation/languages\".\n */\nfunction jsonPathToPointer(jsonPath: string): string {\n return jsonPath\n .replace(/^\\$\\.?/, '')\n .split('.')\n .filter(Boolean)\n .map((segment) => `/${segment}`)\n .join('');\n}\n\n/**\n * Restore top-level $ref pointers in the composite file.\n * Sorted deepest-first so nested refs are written before parents.\n */\nfunction restoreTopLevelRefs(\n fileJson: unknown,\n refMap: RefMap,\n splitConfig: SplitConfig | null\n): void {\n // Build a regex to exclude entries inside the composite array\n const arrayPointerPattern = splitConfig\n ? new RegExp(\n `^${splitConfig.jsonPointer.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\/\\\\d+`\n )\n : null;\n\n const entries = [...refMap.entries()]\n .filter(\n ([pointer]) => !arrayPointerPattern || !arrayPointerPattern.test(pointer)\n )\n .sort(([a], [b]) => b.length - a.length);\n\n for (const [pointer, entry] of entries) {\n const subtree = getAtPointer(fileJson, pointer);\n if (subtree === undefined) continue;\n\n writeJsonFile(entry.sourceFile, subtree);\n setAtPointer(fileJson, pointer, { $ref: entry.refPath });\n }\n}\n\n/**\n * Collect refMap entries that describe an entry's internal $ref chain.\n * Sorted deepest-first so nested content is extracted before parents.\n */\nfunction collectInternalRefs(\n refMap: RefMap,\n entryPointerPrefix: string\n): { relativePointer: string; refPath: string; resolvedDir: string }[] {\n const refs: {\n relativePointer: string;\n refPath: string;\n resolvedDir: string;\n }[] = [];\n\n for (const [pointer, entry] of refMap.entries()) {\n if (!pointer.startsWith(entryPointerPrefix + '/')) continue;\n refs.push({\n relativePointer: pointer.slice(entryPointerPrefix.length),\n refPath: entry.refPath,\n resolvedDir: entry.containingDir,\n });\n }\n\n refs.sort((a, b) => b.relativePointer.length - a.relativePointer.length);\n return refs;\n}\n\nfunction writeJsonFile(filePath: string, data: unknown): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\nfunction getAtPointer(obj: unknown, pointer: string): unknown {\n if (!pointer || pointer === '/') return obj;\n const parts = pointer.split('/').filter(Boolean);\n let current = obj;\n for (const part of parts) {\n if (!isJsonContainer(current)) return undefined;\n const index = /^\\d+$/.test(part) ? parseInt(part) : part;\n current = Array.isArray(current)\n ? current[index as number]\n : current[index];\n }\n return current;\n}\n\nfunction setAtPointer(obj: unknown, pointer: string, value: unknown): void {\n if (!pointer || pointer === '/') return;\n const parts = pointer.split('/').filter(Boolean);\n let current = obj;\n for (let i = 0; i < parts.length - 1; i++) {\n if (!isJsonContainer(current)) return;\n const index = /^\\d+$/.test(parts[i]) ? parseInt(parts[i]) : parts[i];\n const next = Array.isArray(current)\n ? current[index as number]\n : current[index];\n if (next === undefined) return;\n current = next;\n }\n const lastPart = parts[parts.length - 1];\n const lastIndex = /^\\d+$/.test(lastPart) ? parseInt(lastPart) : lastPart;\n if (!isJsonContainer(current)) return;\n if (Array.isArray(current)) {\n current[lastIndex as number] = value;\n } else {\n current[lastIndex] = value;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"splitMintlifyLanguageRefs.js","names":[],"sources":["../../src/utils/splitMintlifyLanguageRefs.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\nimport { Settings, JsonSchema, SourceObjectOptions } from '../types/index.js';\nimport type { RefMap } from './resolveMintlifyRefs.js';\nimport { validateJsonSchema } from '../formats/json/utils.js';\nimport { getStoredRefMap, clearStoredRefMap } from '../state/mintlifyRefMap.js';\nimport { JSONPath } from 'jsonpath-plus';\nimport { getLocaleProperties } from '@generaltranslation/format';\n\ntype JsonContainer = Record<string, unknown> | unknown[];\n\nfunction isJsonContainer(value: unknown): value is JsonContainer {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Post-processing step for composite JSON files with splitEntries enabled.\n *\n * After mergeJson writes a fully-inlined composite file, this function:\n * 1. Restores the original $ref structure (if the source used $ref / resolveRefs)\n * 2. Extracts non-default keyed entries into their own ref files\n * to keep the source file compact\n *\n * Driven entirely by the jsonSchema config — reads the composite path,\n * key field, and splitEntries flag from the schema.\n */\nexport async function splitMintlifyLanguageRefs(\n settings: Settings\n): Promise<void> {\n const refMap = getStoredRefMap();\n\n try {\n const resolvedJsonPaths = settings.files?.resolvedPaths?.json;\n if (!resolvedJsonPaths) return;\n\n // Find a JSON file that has splitEntries enabled or resolveRefs\n const targetFile = findTargetFile(resolvedJsonPaths, settings);\n if (!targetFile) return;\n\n const { filePath: compositeFilePath, splitConfig } = targetFile;\n if (!fs.existsSync(compositeFilePath)) return;\n\n let fileJson: unknown;\n try {\n fileJson = JSON.parse(fs.readFileSync(compositeFilePath, 'utf-8'));\n } catch {\n return;\n }\n\n const docsDir = path.dirname(compositeFilePath);\n\n // If splitEntries is configured, process it\n if (splitConfig) {\n processSplitEntries(\n fileJson,\n compositeFilePath,\n docsDir,\n splitConfig,\n settings,\n refMap\n );\n }\n\n // Restore top-level refs if any exist\n if (refMap && refMap.size > 0) {\n restoreTopLevelRefs(fileJson, refMap, splitConfig);\n }\n\n // Always write the composite file back — splitEntries modified the\n // languages array, and restoreTopLevelRefs may not have written it\n // (e.g., when all refs are inside language entries, not top-level)\n fs.writeFileSync(\n compositeFilePath,\n JSON.stringify(fileJson, null, 2),\n 'utf-8'\n );\n } finally {\n clearStoredRefMap();\n }\n}\n\ntype SplitConfig = {\n compositePath: string;\n jsonPointer: string;\n keyField: string;\n keyJsonPath: string;\n sourceObjectOptions: SourceObjectOptions;\n};\n\n/**\n * Find the target file and extract split configuration from the schema.\n */\nfunction findTargetFile(\n resolvedPaths: string[],\n settings: Settings\n): {\n filePath: string;\n schema: JsonSchema;\n splitConfig: SplitConfig | null;\n} | null {\n if (!settings.options?.jsonSchema) return null;\n\n for (const filePath of resolvedPaths) {\n const schema = validateJsonSchema(settings.options, filePath);\n if (!schema) continue;\n\n const hasSplitEntries = schema.composite\n ? Object.entries(schema.composite).some(([, opts]) => opts.splitEntries)\n : false;\n\n const hasResolveRefs = schema.resolveRefs;\n\n if (!hasSplitEntries && !hasResolveRefs) continue;\n\n // Extract split config if available\n let splitConfig: SplitConfig | null = null;\n if (schema.composite) {\n for (const [compositePath, opts] of Object.entries(schema.composite)) {\n if (opts.splitEntries && opts.type === 'array' && opts.key) {\n splitConfig = {\n compositePath,\n jsonPointer: jsonPathToPointer(compositePath),\n keyField: opts.key,\n keyJsonPath: opts.key,\n sourceObjectOptions: opts,\n };\n break;\n }\n }\n }\n\n return { filePath, schema, splitConfig };\n }\n\n return null;\n}\n\n/**\n * Process splitEntries: extract non-default keyed entries into ref files.\n */\nfunction processSplitEntries(\n fileJson: unknown,\n compositeFilePath: string,\n docsDir: string,\n splitConfig: SplitConfig,\n settings: Settings,\n refMap: RefMap | null\n): void {\n const { jsonPointer, keyJsonPath } = splitConfig;\n\n // Find the composite array — may be behind a $ref\n const parentPointer = jsonPointer.split('/').slice(0, -1).join('/') || '';\n const arrayKey = jsonPointer.split('/').pop() || '';\n const navRefEntry = parentPointer ? refMap?.get(parentPointer) : undefined;\n\n // Get the array from the file\n const arrayContainer = parentPointer\n ? getAtPointer(fileJson, parentPointer)\n : fileJson;\n if (!isJsonContainer(arrayContainer)) return;\n\n const entries = Array.isArray(arrayContainer)\n ? arrayContainer[Number(arrayKey)]\n : arrayContainer[arrayKey];\n if (!Array.isArray(entries) || entries.length <= 1) return;\n\n // Determine the default key value (the source entry)\n const defaultKeyValue = getDefaultKeyValue(\n settings.defaultLocale,\n splitConfig.sourceObjectOptions\n );\n\n const defaultIndex = entries.findIndex((e: unknown) => {\n if (!e || typeof e !== 'object') return false;\n const values = JSONPath({\n json: e,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n return values?.[0] === defaultKeyValue;\n });\n if (defaultIndex < 0) return;\n\n // Detect whether each language entry is itself a $ref (per-entry refs), as\n // opposed to the case where the *container* of the languages array is a $ref\n // (navRefEntry). With per-entry refs, the languages array still lives in the\n // composite file, but each entry's content lives in a separate ref file.\n const defaultEntryPointer = `${jsonPointer}/${defaultIndex}`;\n const defaultEntryRef = refMap?.get(defaultEntryPointer);\n\n // arrayHostDir: directory of the file that physically holds the languages\n // array — entry $refs in the array are written relative to this.\n // entryBaseDir: directory under which per-entry ref files (and their nested\n // refs) are written — mirrors the source entry file's location.\n // These coincide for the container-ref and fully-inline cases; they differ\n // only for per-entry refs, where the array lives in the composite file but\n // the entry files live next to the (default) entry's source ref.\n const arrayHostDir = navRefEntry\n ? path.dirname(navRefEntry.sourceFile)\n : docsDir;\n const entryBaseDir = navRefEntry\n ? path.dirname(navRefEntry.sourceFile)\n : defaultEntryRef\n ? path.dirname(defaultEntryRef.sourceFile)\n : docsDir;\n const navFileName = navRefEntry\n ? path.basename(navRefEntry.sourceFile)\n : defaultEntryRef\n ? path.basename(defaultEntryRef.sourceFile)\n : path.basename(compositeFilePath);\n\n // Restore $ref structure if the source used $ref\n if (refMap && refMap.size > 0) {\n const internalRefs = collectInternalRefs(refMap, defaultEntryPointer);\n\n if (internalRefs.length > 0) {\n // When the default entry is itself a $ref, it is restored to that single\n // $ref below and its source file is left untouched, so we must not restore\n // its nested refs in place. Otherwise (inlined default entry) we do.\n if (!defaultEntryRef) {\n const defaultEntry = entries[defaultIndex];\n for (const ref of internalRefs) {\n setAtPointer(defaultEntry, ref.relativePointer, {\n $ref: ref.refPath,\n });\n }\n }\n\n // For each non-default entry, write localized copies of the nested ref\n // files (mirroring the source topology under the locale dir) and replace\n // the inlined subtrees with their $refs.\n for (let i = 0; i < entries.length; i++) {\n if (i === defaultIndex) continue;\n const entry = entries[i];\n\n const entryKeyValues = JSONPath({\n json: entry,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n if (entryKeyValues?.[0] === defaultKeyValue) continue;\n const keyValue =\n typeof entryKeyValues?.[0] === 'string'\n ? entryKeyValues[0]\n : 'unknown';\n\n for (const ref of internalRefs) {\n const subtree = getAtPointer(entry, ref.relativePointer);\n if (subtree === undefined) continue;\n\n const originalAbsPath = path.resolve(ref.resolvedDir, ref.refPath);\n const relToBaseDir = path.relative(entryBaseDir, originalAbsPath);\n const localeRelPath = path.join(keyValue, relToBaseDir);\n const outputPath = path.resolve(entryBaseDir, localeRelPath);\n writeJsonFile(outputPath, subtree);\n\n setAtPointer(entry, ref.relativePointer, { $ref: ref.refPath });\n }\n }\n\n logger.info(`Restored $ref structure for default entry`);\n }\n }\n\n // Get the actual property name from the key JSONPath (e.g., \"$.language\" → \"language\")\n const keyPropertyName = keyJsonPath.replace(/^\\$\\.?/, '');\n\n // Extract each non-default entry into its own ref file\n for (let i = 0; i < entries.length; i++) {\n if (i === defaultIndex) continue;\n const entry = entries[i];\n if (!entry || typeof entry !== 'object') continue;\n\n const keyValues = JSONPath({\n json: entry,\n path: keyJsonPath,\n resultType: 'value',\n flatten: true,\n wrap: true,\n }) as unknown[];\n const keyValue = keyValues?.[0];\n if (typeof keyValue !== 'string' || keyValue === defaultKeyValue) continue;\n\n if (!isJsonContainer(entry) || Array.isArray(entry)) continue;\n const { [keyPropertyName]: _, ...contentWithoutKey } = entry;\n const entryFilePath = path.resolve(\n entryBaseDir,\n path.join(keyValue, navFileName)\n );\n writeJsonFile(entryFilePath, contentWithoutKey);\n\n entries[i] = {\n [keyPropertyName]: keyValue,\n $ref: toRelativeRefPath(arrayHostDir, entryFilePath),\n };\n }\n\n // When the default entry was itself a $ref, restore it to that single $ref;\n // its source file is the untouched English source already on disk.\n if (defaultEntryRef) {\n entries[defaultIndex] = { $ref: defaultEntryRef.refPath };\n }\n\n logger.info(`Split keyed entries into ref files`);\n}\n\n/**\n * Get the identifying key value for the default locale.\n */\nfunction getDefaultKeyValue(\n defaultLocale: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const localeProperties = getLocaleProperties(defaultLocale);\n return (\n (localeProperties as Record<string, string | undefined>)[localeProperty] ||\n localeProperties.code ||\n defaultLocale\n );\n}\n\n/**\n * Convert a JSONPath like \"$.navigation.languages\" to a JSON pointer like \"/navigation/languages\".\n */\nfunction jsonPathToPointer(jsonPath: string): string {\n return jsonPath\n .replace(/^\\$\\.?/, '')\n .split('.')\n .filter(Boolean)\n .map((segment) => `/${segment}`)\n .join('');\n}\n\n/**\n * Restore top-level $ref pointers in the composite file.\n * Sorted deepest-first so nested refs are written before parents.\n */\nfunction restoreTopLevelRefs(\n fileJson: unknown,\n refMap: RefMap,\n splitConfig: SplitConfig | null\n): void {\n // Build a regex to exclude entries inside the composite array\n const arrayPointerPattern = splitConfig\n ? new RegExp(\n `^${splitConfig.jsonPointer.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\/\\\\d+`\n )\n : null;\n\n const entries = [...refMap.entries()]\n .filter(\n ([pointer]) => !arrayPointerPattern || !arrayPointerPattern.test(pointer)\n )\n .sort(([a], [b]) => b.length - a.length);\n\n for (const [pointer, entry] of entries) {\n const subtree = getAtPointer(fileJson, pointer);\n if (subtree === undefined) continue;\n\n // If the value here is still an unresolved $ref placeholder, the referenced\n // file was never inlined at this pointer (e.g. mergeJson leaves non-composite\n // refs like `redirects` collapsed). Writing it back would overwrite the\n // source file with a self-referential stub and destroy its real contents —\n // the source already holds the correct data, so leave it untouched.\n if (\n isJsonContainer(subtree) &&\n !Array.isArray(subtree) &&\n typeof (subtree as Record<string, unknown>).$ref === 'string'\n ) {\n continue;\n }\n\n writeJsonFile(entry.sourceFile, subtree);\n setAtPointer(fileJson, pointer, { $ref: entry.refPath });\n }\n}\n\n/**\n * Collect refMap entries that describe an entry's internal $ref chain.\n * Sorted deepest-first so nested content is extracted before parents.\n */\nfunction collectInternalRefs(\n refMap: RefMap,\n entryPointerPrefix: string\n): { relativePointer: string; refPath: string; resolvedDir: string }[] {\n const refs: {\n relativePointer: string;\n refPath: string;\n resolvedDir: string;\n }[] = [];\n\n for (const [pointer, entry] of refMap.entries()) {\n if (!pointer.startsWith(entryPointerPrefix + '/')) continue;\n refs.push({\n relativePointer: pointer.slice(entryPointerPrefix.length),\n refPath: entry.refPath,\n resolvedDir: entry.containingDir,\n });\n }\n\n refs.sort((a, b) => b.relativePointer.length - a.relativePointer.length);\n return refs;\n}\n\nfunction writeJsonFile(filePath: string, data: unknown): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n/**\n * Build a relative $ref path (POSIX separators, \"./\"-prefixed when it isn't\n * already a relative or absolute path) from the directory that hosts the\n * languages array to a written entry file.\n */\nfunction toRelativeRefPath(fromDir: string, toPath: string): string {\n const rel = path.relative(fromDir, toPath).split(path.sep).join('/');\n return rel.startsWith('.') || rel.startsWith('/') ? rel : `./${rel}`;\n}\n\nfunction getAtPointer(obj: unknown, pointer: string): unknown {\n if (!pointer || pointer === '/') return obj;\n const parts = pointer.split('/').filter(Boolean);\n let current = obj;\n for (const part of parts) {\n if (!isJsonContainer(current)) return undefined;\n const index = /^\\d+$/.test(part) ? parseInt(part) : part;\n current = Array.isArray(current)\n ? current[index as number]\n : current[index];\n }\n return current;\n}\n\nfunction setAtPointer(obj: unknown, pointer: string, value: unknown): void {\n if (!pointer || pointer === '/') return;\n const parts = pointer.split('/').filter(Boolean);\n let current = obj;\n for (let i = 0; i < parts.length - 1; i++) {\n if (!isJsonContainer(current)) return;\n const index = /^\\d+$/.test(parts[i]) ? parseInt(parts[i]) : parts[i];\n const next = Array.isArray(current)\n ? current[index as number]\n : current[index];\n if (next === undefined) return;\n current = next;\n }\n const lastPart = parts[parts.length - 1];\n const lastIndex = /^\\d+$/.test(lastPart) ? parseInt(lastPart) : lastPart;\n if (!isJsonContainer(current)) return;\n if (Array.isArray(current)) {\n current[lastIndex as number] = value;\n } else {\n current[lastIndex] = value;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.14.43",
3
+ "version": "2.14.44",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [
@@ -115,9 +115,9 @@
115
115
  "yaml": "^2.8.0",
116
116
  "@generaltranslation/format": "0.1.1",
117
117
  "@generaltranslation/python-extractor": "0.2.22",
118
- "@generaltranslation/supported-locales": "2.1.1",
119
118
  "generaltranslation": "8.2.16",
120
- "gt-remark": "1.0.7"
119
+ "gt-remark": "1.0.7",
120
+ "@generaltranslation/supported-locales": "2.1.1"
121
121
  },
122
122
  "devDependencies": {
123
123
  "@babel/types": "^7.28.4",