gt 2.14.47 → 2.14.48

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.48
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1582](https://github.com/generaltranslation/gt/pull/1582) [`1e5e748`](https://github.com/generaltranslation/gt/commit/1e5e748b1aef33eb58f536496592a6258fc441e5) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Handle Mintlify `$ref` siblings, add omit to composite JSONs
8
+
3
9
  ## 2.14.47
4
10
 
5
11
  ### Patch Changes
@@ -14,8 +14,11 @@ function generatePreset(preset, type) {
14
14
  "$..tab",
15
15
  "$..item",
16
16
  "$..anchor",
17
- "$..dropdown"
17
+ "$..dropdown",
18
+ "$..product",
19
+ "$..description"
18
20
  ],
21
+ omitProperties: ["default"],
19
22
  transform: { "$..pages[*]": {
20
23
  match: "^{locale}/(.*)$",
21
24
  replace: "{locale}/$1"
@@ -50,8 +53,11 @@ function generatePreset(preset, type) {
50
53
  "$..tab",
51
54
  "$..item",
52
55
  "$..anchor",
53
- "$..dropdown"
56
+ "$..dropdown",
57
+ "$..product",
58
+ "$..description"
54
59
  ],
60
+ omitProperties: ["default"],
55
61
  transform: {
56
62
  "$..pages[*]": {
57
63
  match: "^/?(.*)$",
@@ -1 +1 @@
1
- {"version":3,"file":"optionPresets.js","names":[],"sources":["../../src/config/optionPresets.ts"],"sourcesContent":["import { JsonSchema, YamlSchema } from '../types/index.js';\n\nexport function generatePreset(\n preset: JsonSchema['preset'],\n type: 'json'\n): JsonSchema;\nexport function generatePreset(\n preset: YamlSchema['preset'],\n type: 'yaml'\n): YamlSchema;\nexport function generatePreset(\n preset: string | undefined,\n type: 'json' | 'yaml'\n): JsonSchema | YamlSchema {\n if (type === 'json') {\n switch (preset) {\n case 'mintlify':\n // https://mintlify.com/docs/navigation\n return {\n resolveRefs: true,\n composite: {\n '$.navigation.languages': {\n type: 'array',\n key: '$.language',\n experimentalSort: 'localesAlphabetical',\n splitEntries: true,\n include: [\n '$..group',\n '$..tab',\n '$..item',\n '$..anchor',\n '$..dropdown',\n ],\n transform: {\n '$..pages[*]': {\n match: '^{locale}/(.*)$',\n replace: '{locale}/$1',\n },\n },\n },\n '$.redirects': {\n type: 'array',\n key: '$.language',\n include: [],\n transform: {\n '$.source': {\n match: '^/{locale}/(.*)$',\n replace: '/{locale}/$1',\n },\n '$.destination': {\n match: '^/{locale}/(.*)$',\n replace: '/{locale}/$1',\n },\n },\n },\n },\n };\n case 'mintlify-hide-default':\n // Mintlify with hideDefaultLocale — paths don't have locale prefix in source\n return {\n resolveRefs: true,\n composite: {\n '$.navigation.languages': {\n type: 'array',\n key: '$.language',\n experimentalSort: 'localesAlphabetical',\n splitEntries: true,\n include: [\n '$..group',\n '$..tab',\n '$..item',\n '$..anchor',\n '$..dropdown',\n ],\n transform: {\n '$..pages[*]': {\n match: '^/?(.*)$',\n replace: '{locale}/$1',\n },\n '$..root': {\n match: '^/?(.*)$',\n replace: '{locale}/$1',\n },\n },\n },\n },\n };\n case 'openapi':\n return {\n include: ['$..summary', '$..description'],\n };\n default:\n return {};\n }\n } else {\n switch (preset) {\n case 'mintlify':\n return {\n include: ['$..summary', '$..description'],\n };\n case 'openapi':\n return {\n include: ['$..summary', '$..description'],\n };\n default:\n return {};\n }\n }\n}\n"],"mappings":";AAUA,SAAgB,eACd,QACA,MACyB;AACzB,KAAI,SAAS,OACX,SAAQ,QAAR;EACE,KAAK,WAEH,QAAO;GACL,aAAa;GACb,WAAW;IACT,0BAA0B;KACxB,MAAM;KACN,KAAK;KACL,kBAAkB;KAClB,cAAc;KACd,SAAS;MACP;MACA;MACA;MACA;MACA;MACD;KACD,WAAW,EACT,eAAe;MACb,OAAO;MACP,SAAS;MACV,EACF;KACF;IACD,eAAe;KACb,MAAM;KACN,KAAK;KACL,SAAS,EAAE;KACX,WAAW;MACT,YAAY;OACV,OAAO;OACP,SAAS;OACV;MACD,iBAAiB;OACf,OAAO;OACP,SAAS;OACV;MACF;KACF;IACF;GACF;EACH,KAAK,wBAEH,QAAO;GACL,aAAa;GACb,WAAW,EACT,0BAA0B;IACxB,MAAM;IACN,KAAK;IACL,kBAAkB;IAClB,cAAc;IACd,SAAS;KACP;KACA;KACA;KACA;KACA;KACD;IACD,WAAW;KACT,eAAe;MACb,OAAO;MACP,SAAS;MACV;KACD,WAAW;MACT,OAAO;MACP,SAAS;MACV;KACF;IACF,EACF;GACF;EACH,KAAK,UACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,QACE,QAAO,EAAE;;KAGb,SAAQ,QAAR;EACE,KAAK,WACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,KAAK,UACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,QACE,QAAO,EAAE"}
1
+ {"version":3,"file":"optionPresets.js","names":[],"sources":["../../src/config/optionPresets.ts"],"sourcesContent":["import { JsonSchema, YamlSchema } from '../types/index.js';\n\nexport function generatePreset(\n preset: JsonSchema['preset'],\n type: 'json'\n): JsonSchema;\nexport function generatePreset(\n preset: YamlSchema['preset'],\n type: 'yaml'\n): YamlSchema;\nexport function generatePreset(\n preset: string | undefined,\n type: 'json' | 'yaml'\n): JsonSchema | YamlSchema {\n if (type === 'json') {\n switch (preset) {\n case 'mintlify':\n // https://mintlify.com/docs/navigation\n return {\n resolveRefs: true,\n composite: {\n '$.navigation.languages': {\n type: 'array',\n key: '$.language',\n experimentalSort: 'localesAlphabetical',\n splitEntries: true,\n include: [\n '$..group',\n '$..tab',\n '$..item',\n '$..anchor',\n '$..dropdown',\n '$..product',\n '$..description',\n ],\n omitProperties: ['default'],\n transform: {\n '$..pages[*]': {\n match: '^{locale}/(.*)$',\n replace: '{locale}/$1',\n },\n },\n },\n '$.redirects': {\n type: 'array',\n key: '$.language',\n include: [],\n transform: {\n '$.source': {\n match: '^/{locale}/(.*)$',\n replace: '/{locale}/$1',\n },\n '$.destination': {\n match: '^/{locale}/(.*)$',\n replace: '/{locale}/$1',\n },\n },\n },\n },\n };\n case 'mintlify-hide-default':\n // Mintlify with hideDefaultLocale — paths don't have locale prefix in source\n return {\n resolveRefs: true,\n composite: {\n '$.navigation.languages': {\n type: 'array',\n key: '$.language',\n experimentalSort: 'localesAlphabetical',\n splitEntries: true,\n include: [\n '$..group',\n '$..tab',\n '$..item',\n '$..anchor',\n '$..dropdown',\n '$..product',\n '$..description',\n ],\n omitProperties: ['default'],\n transform: {\n '$..pages[*]': {\n match: '^/?(.*)$',\n replace: '{locale}/$1',\n },\n '$..root': {\n match: '^/?(.*)$',\n replace: '{locale}/$1',\n },\n },\n },\n },\n };\n case 'openapi':\n return {\n include: ['$..summary', '$..description'],\n };\n default:\n return {};\n }\n } else {\n switch (preset) {\n case 'mintlify':\n return {\n include: ['$..summary', '$..description'],\n };\n case 'openapi':\n return {\n include: ['$..summary', '$..description'],\n };\n default:\n return {};\n }\n }\n}\n"],"mappings":";AAUA,SAAgB,eACd,QACA,MACyB;AACzB,KAAI,SAAS,OACX,SAAQ,QAAR;EACE,KAAK,WAEH,QAAO;GACL,aAAa;GACb,WAAW;IACT,0BAA0B;KACxB,MAAM;KACN,KAAK;KACL,kBAAkB;KAClB,cAAc;KACd,SAAS;MACP;MACA;MACA;MACA;MACA;MACA;MACA;MACD;KACD,gBAAgB,CAAC,UAAU;KAC3B,WAAW,EACT,eAAe;MACb,OAAO;MACP,SAAS;MACV,EACF;KACF;IACD,eAAe;KACb,MAAM;KACN,KAAK;KACL,SAAS,EAAE;KACX,WAAW;MACT,YAAY;OACV,OAAO;OACP,SAAS;OACV;MACD,iBAAiB;OACf,OAAO;OACP,SAAS;OACV;MACF;KACF;IACF;GACF;EACH,KAAK,wBAEH,QAAO;GACL,aAAa;GACb,WAAW,EACT,0BAA0B;IACxB,MAAM;IACN,KAAK;IACL,kBAAkB;IAClB,cAAc;IACd,SAAS;KACP;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,gBAAgB,CAAC,UAAU;IAC3B,WAAW;KACT,eAAe;MACb,OAAO;MACP,SAAS;MACV;KACD,WAAW;MACT,OAAO;MACP,SAAS;MACV;KACF;IACF,EACF;GACF;EACH,KAAK,UACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,QACE,QAAO,EAAE;;KAGb,SAAQ,QAAR;EACE,KAAK,WACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,KAAK,UACH,QAAO,EACL,SAAS,CAAC,cAAc,iBAAiB,EAC1C;EACH,QACE,QAAO,EAAE"}
@@ -86,6 +86,7 @@ function mergeJson(originalContent, inputPath, options, targets, defaultLocale,
86
86
  const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);
87
87
  const { identifyingLocaleProperty: targetLocaleKeyProperty } = getSourceObjectOptionsArray(useCanonicalLocaleKeys ? gt.resolveCanonicalLocale(target.targetLocale) : target.targetLocale, sourceObjectPointer, sourceObjectOptions);
88
88
  setJSONPointerValue(mutatedSourceItem, defaultLocaleKeyPointer, targetLocaleKeyProperty);
89
+ omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);
89
90
  for (const [translatedKeyJsonPointer, translatedValue] of Object.entries(targetItem || {})) {
90
91
  const valueToSet = useCanonicalLocaleKeys && defaultLocaleKeyPointer && translatedKeyJsonPointer === defaultLocaleKeyPointer ? targetLocaleKeyProperty : translatedValue;
91
92
  try {
@@ -122,6 +123,7 @@ function mergeJson(originalContent, inputPath, options, targets, defaultLocale,
122
123
  continue;
123
124
  }
124
125
  const mutateSourceItem = structuredClone(defaultLocaleSourceItem);
126
+ omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);
125
127
  const mergedItems = {
126
128
  ...sourceObjectOptions.transform ? defaultLocaleSourceItem : {},
127
129
  ...targetItems
@@ -202,6 +204,15 @@ function sortByLocaleOrder(items, sourceObjectOptions, localeOrder, sourceObject
202
204
  return items;
203
205
  }
204
206
  /**
207
+ * Remove top-level properties from a generated non-default-locale entry
208
+ * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)
209
+ */
210
+ function omitProperties(item, properties) {
211
+ if (!properties?.length) return;
212
+ if (!item || typeof item !== "object" || Array.isArray(item)) return;
213
+ for (const property of properties) delete item[property];
214
+ }
215
+ /**
205
216
  * Apply transformations to the sourceItem in-place
206
217
  * @param sourceItem - The source item to apply transformations to
207
218
  * @param transform - The transformations to apply
@@ -1 +1 @@
1
- {"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport { getLocaleProperties } from '@generaltranslation/format';\nimport { replaceLocalePlaceholders } from '../utils.js';\nimport { gt } from '../../utils/gt.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getLocaleProperties(targetLocale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mergeJson.js","names":[],"sources":["../../../src/formats/json/mergeJson.ts"],"sourcesContent":["import { AdditionalOptions, SourceObjectOptions } from '../../types/index.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport {\n findMatchingItemArray,\n findMatchingItemObject,\n generateSourceObjectPointers,\n getIdentifyingLocaleProperty,\n getSourceObjectOptionsArray,\n validateJsonSchema,\n} from './utils.js';\nimport { getLocaleProperties } from '@generaltranslation/format';\nimport { replaceLocalePlaceholders } from '../utils.js';\nimport { gt } from '../../utils/gt.js';\nimport {\n applyStructuralTransforms,\n unapplyStructuralTransforms,\n} from './transformJson.js';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches, getJSONPathValues } from './jsonPath.js';\nimport { getJSONPointerValue, setJSONPointerValue } from './jsonPointer.js';\n\ntype ParsedTarget = {\n translatedContent: string;\n targetLocale: string;\n parsedContent: JSONObject;\n};\n\nexport function mergeJson(\n originalContent: string,\n inputPath: string,\n options: AdditionalOptions,\n targets: {\n translatedContent: string;\n targetLocale: string;\n }[],\n defaultLocale: string,\n localeOrder: string[] = []\n): string[] {\n const jsonSchema = validateJsonSchema(options, inputPath);\n if (!jsonSchema) {\n return targets.map((target) => target.translatedContent);\n }\n\n let originalJson: JSONValue;\n try {\n originalJson = JSON.parse(originalContent);\n } catch {\n logger.error(`Invalid JSON file: ${inputPath}`);\n return exitSync(1);\n }\n\n const useCanonicalLocaleKeys =\n options?.experimentalCanonicalLocaleKeys ?? false;\n const canonicalDefaultLocale = useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(defaultLocale)\n : defaultLocale;\n const canonicalLocaleOrder = useCanonicalLocaleKeys\n ? localeOrder.map((locale) => gt.resolveCanonicalLocale(locale))\n : localeOrder;\n\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n applyStructuralTransforms(\n originalJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n // Handle include\n if (jsonSchema.include) {\n const output: string[] = [];\n for (const target of targets) {\n // Must clone the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n const translatedJson = JSON.parse(target.translatedContent) as JSONObject;\n for (const [jsonPointer, translatedValue] of Object.entries(\n translatedJson\n )) {\n try {\n const value = getJSONPointerValue(mergedJson, jsonPointer);\n if (!value) continue;\n setJSONPointerValue(mergedJson, jsonPointer, translatedValue);\n } catch {\n /* empty */\n }\n }\n output.push(JSON.stringify(mergedJson, null, 2));\n }\n return output;\n }\n\n if (!jsonSchema.composite) {\n logger.error('No composite property found in JSON schema');\n return exitSync(1);\n }\n\n // Handle composite\n // Create a deep copy of the original JSON to avoid mutations\n const mergedJson = structuredClone(originalJson);\n\n // Pre-parse all target contents ONCE (avoid re-parsing per pointer)\n const parsedTargets = targets.map((target) => ({\n ...target,\n parsedContent: JSON.parse(target.translatedContent) as JSONObject,\n })) satisfies ParsedTarget[];\n\n // Create mapping of sourceObjectPointer to SourceObjectOptions\n const sourceObjectPointers = generateSourceObjectPointers(\n jsonSchema.composite,\n originalJson\n );\n\n // Find the source object\n for (const [\n sourceObjectPointer,\n { sourceObjectValue, sourceObjectOptions },\n ] of Object.entries(sourceObjectPointers)) {\n // Find the source item\n if (sourceObjectOptions.type === 'array') {\n // Validate type\n if (!Array.isArray(sourceObjectValue)) {\n logger.error(\n `Source object value is not an array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n\n // Get source item for default locale\n const matchingDefaultLocaleItems = findMatchingItemArray(\n canonicalDefaultLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n if (!Object.keys(matchingDefaultLocaleItems).length) {\n logger.warn(\n `Matching sourceItems not found at path: ${sourceObjectPointer}. Check that your JSON file includes the key field. Skipping this target`\n );\n continue;\n }\n\n const matchingDefaultLocaleItemKeys = new Set(\n Object.keys(matchingDefaultLocaleItems)\n );\n\n // For each target:\n // 1. Get the target items\n // 2. Track all array indecies to remove (will be overwritten)\n // 3. Merge matchingDefaultLocaleItems and targetItems\n // 4. Validate that the mergedItems is not empty\n // For each target item:\n // 5. Validate that all the array indecies are still present in the source json\n // 6. Override the source item with the translated values\n // 7. Apply additional mutations to the sourceItem\n // 8. Track all items to add\n // 9. Check that items to add is >= items to remove\n // 10. Remove all items for the target locale (they can be identified by the key)\n const indiciesToRemove = new Set<number>();\n const itemsToAdd: JSONValue[] = [];\n for (const target of parsedTargets) {\n let targetItems = target.parsedContent[sourceObjectPointer];\n // 1. Get the target items\n if (!targetItems) {\n // If no translation can be found, a transformation may need to happen still\n targetItems = {};\n }\n\n // 2. Track all array indecies to remove (will be overwritten)\n const targetItemsToRemove = findMatchingItemArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectOptions,\n sourceObjectPointer,\n sourceObjectValue\n );\n Object.values(targetItemsToRemove).forEach(({ index }) =>\n indiciesToRemove.add(index)\n );\n\n // Remap mismatched positional keys to current source positions\n const sourceKeys = [...matchingDefaultLocaleItemKeys];\n const remappedTargetItems: Record<string, JSONValue> = {};\n for (const [key, value] of Object.entries(targetItems as JSONObject)) {\n if (matchingDefaultLocaleItemKeys.has(key)) {\n remappedTargetItems[key] = value;\n } else if (\n sourceKeys.length === 1 &&\n !(sourceKeys[0] in remappedTargetItems)\n ) {\n remappedTargetItems[sourceKeys[0]] = value;\n } else {\n logger.warn(\n `Skipping translated item at ${key}: cannot map to source item at path ${sourceObjectPointer}`\n );\n }\n }\n\n // Merge matchingDefaultLocaleItems and remapped targetItems\n const mergedItems = {\n ...(sourceObjectOptions.transform ? matchingDefaultLocaleItems : {}),\n ...remappedTargetItems,\n };\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n for (const [sourceItemPointer, targetItem] of Object.entries(\n mergedItems\n )) {\n // 5. Validate that all the array indecies are still present in the source json\n if (!matchingDefaultLocaleItemKeys.has(sourceItemPointer)) {\n logger.warn(\n `Skipping translated item at ${sourceItemPointer}: not present in source json at path ${sourceObjectPointer}`\n );\n continue;\n }\n\n // 6. Override the source item with the translated values\n const defaultLocaleSourceItem =\n matchingDefaultLocaleItems[sourceItemPointer].sourceItem;\n const defaultLocaleKeyPointer =\n matchingDefaultLocaleItems[sourceItemPointer].keyPointer;\n const mutatedSourceItem = structuredClone(defaultLocaleSourceItem);\n const { identifyingLocaleProperty: targetLocaleKeyProperty } =\n getSourceObjectOptionsArray(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n setJSONPointerValue(\n mutatedSourceItem,\n defaultLocaleKeyPointer,\n targetLocaleKeyProperty\n );\n omitProperties(mutatedSourceItem, sourceObjectOptions.omitProperties);\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries((targetItem || {}) as JSONObject)) {\n const valueToSet =\n useCanonicalLocaleKeys &&\n defaultLocaleKeyPointer &&\n translatedKeyJsonPointer === defaultLocaleKeyPointer\n ? targetLocaleKeyProperty\n : translatedValue;\n try {\n const value = getJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutatedSourceItem,\n translatedKeyJsonPointer,\n valueToSet\n );\n } catch {\n /* empty */\n }\n }\n\n // 7. Apply additional mutations to the sourceItem\n applyTransformations(\n mutatedSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n itemsToAdd.push(mutatedSourceItem);\n }\n }\n\n // 8. Check that items to add is >= items to remove\n if (itemsToAdd.length < indiciesToRemove.size) {\n logger.warn(\n `Items to add (${itemsToAdd.length}) is less than items to remove (${indiciesToRemove.size}) at path: ${sourceObjectPointer}. Some translated items may have been skipped.`\n );\n }\n\n // 9. Remove all items for the target locale (they can be identified by the key)\n const filteredSourceObjectValue = sourceObjectValue.filter(\n (_, index: number) => !indiciesToRemove.has(index)\n );\n\n // 10. Add all items to the original JSON\n filteredSourceObjectValue.push(...itemsToAdd);\n\n setJSONPointerValue(\n mergedJson,\n sourceObjectPointer,\n sortByLocaleOrder(\n filteredSourceObjectValue,\n sourceObjectOptions,\n canonicalLocaleOrder,\n sourceObjectPointer,\n canonicalDefaultLocale\n )\n );\n } else {\n // Validate type\n if (typeof sourceObjectValue !== 'object' || sourceObjectValue === null) {\n logger.error(\n `Source object value is not an object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n const sourceObjectRecord = sourceObjectValue as JSONObject;\n // Validate localeProperty\n const matchingDefaultLocaleItem = findMatchingItemObject(\n canonicalDefaultLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n // Validate source item exists\n if (!matchingDefaultLocaleItem.sourceItem) {\n logger.error(\n `Source item not found at path: ${sourceObjectPointer}. You must specify a source item where its key matches the default locale`\n );\n return exitSync(1);\n }\n const { sourceItem: defaultLocaleSourceItem } = matchingDefaultLocaleItem;\n\n // For each target:\n // 1. Get the target items\n // 2. Find the source item for the target locale\n // 3. Merge the target items with the source item\n // 4. Validate that the mergedItems is not empty\n // 5. Override the source item with the translated values\n // 6. Apply additional mutations to the sourceItem\n // 7. Merge the source item with the original JSON (if the source item is not a new item)\n for (const target of parsedTargets) {\n // 1. Get the target items\n let targetItems = target.parsedContent[sourceObjectPointer];\n if (targetItems == null) {\n targetItems = {};\n }\n\n // 2. Find the source item for the target locale\n const matchingTargetItem = findMatchingItemObject(\n useCanonicalLocaleKeys\n ? gt.resolveCanonicalLocale(target.targetLocale)\n : target.targetLocale,\n sourceObjectPointer,\n sourceObjectOptions,\n sourceObjectRecord\n );\n const mutateSourceItemKey = matchingTargetItem.keyParentProperty;\n\n // If the source item is a string, use the translated string directly\n if (typeof defaultLocaleSourceItem === 'string') {\n if (typeof targetItems === 'string') {\n sourceObjectRecord[mutateSourceItemKey] = targetItems;\n }\n // If no translation found, leave the locale slot unchanged\n continue;\n }\n\n // If the target locale has a matching source item, use it to mutate the source item\n // Otherwise, fallback to the default locale source item\n const mutateSourceItem = structuredClone(defaultLocaleSourceItem);\n omitProperties(mutateSourceItem, sourceObjectOptions.omitProperties);\n\n // 3. Merge the target items with the source item (if there are transformations to perform)\n const mergedItems: Record<string, JSONValue> = {\n ...(sourceObjectOptions.transform\n ? (defaultLocaleSourceItem as JSONObject)\n : {}),\n ...(targetItems as JSONObject),\n };\n\n // 4. Validate that the mergedItems is not empty\n if (Object.keys(mergedItems).length === 0) {\n logger.warn(\n `Translated JSON for locale: ${target.targetLocale} does not have a valid sourceObjectPointer: ${sourceObjectPointer}. Skipping this target`\n );\n continue;\n }\n\n // 5. Override the source item with the translated values\n for (const [\n translatedKeyJsonPointer,\n translatedValue,\n ] of Object.entries(mergedItems || {})) {\n try {\n const value = getJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer\n );\n if (!value) continue;\n setJSONPointerValue(\n mutateSourceItem,\n translatedKeyJsonPointer,\n translatedValue\n );\n } catch {\n /* empty */\n }\n }\n // 6. Apply additional mutations to the sourceItem\n applyTransformations(\n mutateSourceItem,\n sourceObjectOptions.transform,\n target.targetLocale,\n defaultLocale\n );\n\n // 7. Merge the source item with the original JSON\n sourceObjectRecord[mutateSourceItemKey] = mutateSourceItem;\n }\n setJSONPointerValue(mergedJson, sourceObjectPointer, sourceObjectValue);\n }\n }\n if (jsonSchema.structuralTransform && jsonSchema.composite) {\n unapplyStructuralTransforms(\n mergedJson,\n jsonSchema.structuralTransform,\n jsonSchema.composite\n );\n }\n\n return [JSON.stringify(mergedJson, null, 2)];\n}\n\nfunction sortByLocaleOrder(\n items: JSONValue[],\n sourceObjectOptions: SourceObjectOptions,\n localeOrder: string[],\n sourceObjectPointer: string,\n defaultLocale: string\n): JSONValue[] {\n const sortMode = sourceObjectOptions.experimentalSort;\n if (!sortMode || !sourceObjectOptions.key) {\n return items;\n }\n\n const itemsWithLocale = items.map((item) => {\n let localeValue: string | undefined;\n try {\n const values = getJSONPathValues(item, sourceObjectOptions.key as string);\n const value = values?.[0];\n if (typeof value === 'string') {\n localeValue = value;\n }\n } catch {\n /* empty */\n }\n return { item, localeValue };\n });\n\n if (sortMode === 'locales') {\n if (!localeOrder.length) {\n return items;\n }\n\n const orderedLocaleList = [\n defaultLocale,\n ...localeOrder.filter((locale) => locale !== defaultLocale),\n ];\n const localeOrderValues = orderedLocaleList.map((locale) =>\n getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n )\n );\n\n const orderedItems: JSONValue[] = [];\n const remainingItems = [...itemsWithLocale];\n\n for (const localeValue of localeOrderValues) {\n for (let i = 0; i < remainingItems.length; ) {\n const entry = remainingItems[i];\n if (entry.localeValue === localeValue) {\n orderedItems.push(entry.item);\n remainingItems.splice(i, 1);\n continue;\n }\n i += 1;\n }\n }\n\n remainingItems.forEach((entry) => orderedItems.push(entry.item));\n\n return orderedItems;\n }\n\n if (sortMode === 'localesAlphabetical') {\n const defaultLocaleValue = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n const defaultItems: typeof itemsWithLocale = [];\n const sortableItems: typeof itemsWithLocale = [];\n const remainingItems: typeof itemsWithLocale = [];\n\n for (const entry of itemsWithLocale) {\n if (entry.localeValue === defaultLocaleValue) {\n defaultItems.push(entry);\n continue;\n }\n if (entry.localeValue) {\n sortableItems.push(entry);\n continue;\n }\n remainingItems.push(entry);\n }\n\n sortableItems.sort((a, b) => {\n if (!a.localeValue || !b.localeValue) {\n return 0;\n }\n return a.localeValue.localeCompare(b.localeValue);\n });\n\n return [...defaultItems, ...sortableItems, ...remainingItems].map(\n (entry) => entry.item\n );\n }\n\n return items;\n}\n\n/**\n * Remove top-level properties from a generated non-default-locale entry\n * (e.g. Mintlify's `default: true` flag, which is only valid on one entry)\n */\nfunction omitProperties(\n item: JSONValue,\n properties: string[] | undefined\n): void {\n if (!properties?.length) return;\n if (!item || typeof item !== 'object' || Array.isArray(item)) return;\n for (const property of properties) {\n delete (item as JSONObject)[property];\n }\n}\n\n/**\n * Apply transformations to the sourceItem in-place\n * @param sourceItem - The source item to apply transformations to\n * @param transform - The transformations to apply\n * @param targetLocale - The target locale\n * @param defaultLocale - The default locale\n */\nexport function applyTransformations(\n sourceItem: JSONValue,\n transform: SourceObjectOptions['transform'],\n targetLocale: string,\n defaultLocale: string\n): void {\n if (!transform) return;\n\n const targetLocaleProperties = getLocaleProperties(targetLocale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n\n for (const [transformPath, transformOptions] of Object.entries(transform)) {\n if (\n !transformOptions.replace ||\n typeof transformOptions.replace !== 'string'\n ) {\n continue;\n }\n const results = getJSONPathMatches(sourceItem, transformPath);\n if (!results || results.length === 0) {\n continue;\n }\n results.forEach((result) => {\n if (typeof result.value !== 'string') {\n return;\n }\n // Replace locale placeholders in the replace string\n let replaceString = transformOptions.replace;\n\n // Replace all locale property placeholders\n replaceString = replaceLocalePlaceholders(\n replaceString,\n targetLocaleProperties\n );\n\n if (\n transformOptions.match &&\n typeof transformOptions.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformOptions.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n result.value = result.value.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n result.value = replaceString;\n }\n\n // Update the actual sourceItem using JSONPointer\n setJSONPointerValue(sourceItem, result.pointer, result.value);\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.14.47";
1
+ export declare const PACKAGE_VERSION = "2.14.48";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.14.47";
2
+ const PACKAGE_VERSION = "2.14.48";
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.47';\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.48';\n"],"mappings":";AACA,MAAa,kBAAkB"}
@@ -281,6 +281,7 @@ export type SourceObjectOptions = {
281
281
  key?: string;
282
282
  localeProperty?: string;
283
283
  transform?: TransformOptions;
284
+ omitProperties?: string[];
284
285
  experimentalSort?: 'locales' | 'localesAlphabetical';
285
286
  splitEntries?: boolean;
286
287
  };
@@ -3,6 +3,7 @@ export type RefMapEntry = {
3
3
  refPath: string;
4
4
  containingDir: string;
5
5
  originalContent: unknown;
6
+ siblings?: Record<string, unknown>;
6
7
  };
7
8
  export type RefMap = Map<string, RefMapEntry>;
8
9
  export type ResolvedRefs = {
@@ -61,17 +61,18 @@ function resolveRef(obj, baseDir, pointer, visiting, refMap) {
61
61
  const { $ref: _, ...rest } = obj;
62
62
  return rest;
63
63
  }
64
+ const { $ref: _, ...siblings } = obj;
64
65
  refMap.set(pointer, {
65
66
  sourceFile: resolvedFilePath,
66
67
  refPath,
67
68
  containingDir: baseDir,
68
- originalContent: parsed
69
+ originalContent: parsed,
70
+ siblings
69
71
  });
70
72
  const refFileDir = path.dirname(resolvedFilePath);
71
73
  const nextVisiting = new Set(visiting);
72
74
  nextVisiting.add(resolvedFilePath);
73
75
  const resolvedContent = resolveNode(parsed, refFileDir, pointer, nextVisiting, refMap);
74
- const { $ref: _, ...siblings } = obj;
75
76
  if (resolvedContent !== null && typeof resolvedContent === "object" && !Array.isArray(resolvedContent)) return {
76
77
  ...resolvedContent,
77
78
  ...siblings
@@ -1 +1 @@
1
- {"version":3,"file":"resolveMintlifyRefs.js","names":["jsonSchema"],"sources":["../../src/utils/resolveMintlifyRefs.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\nimport chalk from 'chalk';\nimport micromatch from 'micromatch';\n\nexport type RefMapEntry = {\n sourceFile: string;\n refPath: string;\n containingDir: string;\n originalContent: unknown;\n};\n\nexport type RefMap = Map<string, RefMapEntry>;\n\nexport type ResolvedRefs = {\n resolved: unknown;\n refMap: RefMap;\n};\n\n/**\n * Resolve all Mintlify $ref references in a parsed JSON object.\n *\n * Returns the fully-expanded JSON and a refMap that tracks which subtrees\n * came from which files (used later to split translated output back into\n * the same file topology).\n */\nexport function resolveMintlifyRefs(\n json: unknown,\n filePath: string\n): ResolvedRefs {\n const refMap: RefMap = new Map();\n const resolved = resolveNode(\n json,\n path.dirname(path.resolve(filePath)),\n '',\n new Set<string>(),\n refMap\n );\n return { resolved, refMap };\n}\n\nfunction resolveNode(\n node: unknown,\n baseDir: string,\n pointer: string,\n visiting: Set<string>,\n refMap: RefMap\n): unknown {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n return node.map((item, i) =>\n resolveNode(item, baseDir, `${pointer}/${i}`, visiting, refMap)\n );\n }\n\n const obj = node as Record<string, unknown>;\n\n if (typeof obj['$ref'] === 'string') {\n return resolveRef(obj, baseDir, pointer, visiting, refMap);\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = resolveNode(\n value,\n baseDir,\n `${pointer}/${key}`,\n visiting,\n refMap\n );\n }\n return result;\n}\n\nfunction resolveRef(\n obj: Record<string, unknown>,\n baseDir: string,\n pointer: string,\n visiting: Set<string>,\n refMap: RefMap\n): unknown {\n const refPath = obj['$ref'] as string;\n\n if (!isRelativePath(refPath)) {\n logger.warn(\n chalk.yellow(\n `Skipping non-relative $ref at ${pointer || '/'}: ${refPath}`\n )\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n const resolvedFilePath = path.resolve(baseDir, refPath);\n\n if (visiting.has(resolvedFilePath)) {\n logger.warn(\n chalk.yellow(`Circular $ref detected at ${pointer || '/'}: ${refPath}`)\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n if (!fs.existsSync(resolvedFilePath)) {\n logger.warn(\n chalk.yellow(\n `$ref file not found at ${pointer || '/'}: ${refPath} (resolved to ${resolvedFilePath})`\n )\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n let fileContent: string;\n try {\n fileContent = fs.readFileSync(resolvedFilePath, 'utf-8');\n } catch {\n logger.warn(chalk.yellow(`Failed to read $ref file: ${resolvedFilePath}`));\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fileContent);\n } catch {\n logger.warn(\n chalk.yellow(`$ref file is not valid JSON: ${resolvedFilePath}`)\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n // Record provenance before recursive resolution\n refMap.set(pointer, {\n sourceFile: resolvedFilePath,\n refPath,\n containingDir: baseDir,\n originalContent: parsed,\n });\n\n // Recursively resolve nested $ref in the referenced file\n const refFileDir = path.dirname(resolvedFilePath);\n const nextVisiting = new Set(visiting);\n nextVisiting.add(resolvedFilePath);\n\n const resolvedContent = resolveNode(\n parsed,\n refFileDir,\n pointer,\n nextVisiting,\n refMap\n );\n\n // Apply Mintlify merge rules\n const { $ref: _, ...siblings } = obj;\n\n if (\n resolvedContent !== null &&\n typeof resolvedContent === 'object' &&\n !Array.isArray(resolvedContent)\n ) {\n // Object: merge siblings on top (siblings take precedence)\n return { ...(resolvedContent as Record<string, unknown>), ...siblings };\n }\n\n // Non-object (array, string, number, etc.): replace entirely, drop siblings\n return resolvedContent;\n}\n\n/**\n * Check if a file should have $ref resolution applied based on the settings.\n * Returns true if the file matches a jsonSchema entry with resolveRefs: true.\n */\nexport function shouldResolveRefs(\n filePath: string,\n options?: { jsonSchema?: Record<string, unknown> }\n): boolean {\n if (!options?.jsonSchema) return false;\n\n const relative = path.relative(process.cwd(), filePath);\n for (const [glob, schema] of Object.entries(options.jsonSchema)) {\n const jsonSchema = schema as { resolveRefs?: boolean };\n if (jsonSchema.resolveRefs && micromatch.isMatch(relative, glob)) {\n return true;\n }\n }\n return false;\n}\n\nfunction isRelativePath(refPath: string): boolean {\n if (path.isAbsolute(refPath)) return false;\n if (/^[a-z]+:\\/\\//i.test(refPath)) return false;\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolveMintlifyRefs.js","names":["jsonSchema"],"sources":["../../src/utils/resolveMintlifyRefs.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { logger } from '../console/logger.js';\nimport chalk from 'chalk';\nimport micromatch from 'micromatch';\n\nexport type RefMapEntry = {\n sourceFile: string;\n refPath: string;\n containingDir: string;\n originalContent: unknown;\n siblings?: Record<string, unknown>;\n};\n\nexport type RefMap = Map<string, RefMapEntry>;\n\nexport type ResolvedRefs = {\n resolved: unknown;\n refMap: RefMap;\n};\n\n/**\n * Resolve all Mintlify $ref references in a parsed JSON object.\n *\n * Returns the fully-expanded JSON and a refMap that tracks which subtrees\n * came from which files (used later to split translated output back into\n * the same file topology).\n */\nexport function resolveMintlifyRefs(\n json: unknown,\n filePath: string\n): ResolvedRefs {\n const refMap: RefMap = new Map();\n const resolved = resolveNode(\n json,\n path.dirname(path.resolve(filePath)),\n '',\n new Set<string>(),\n refMap\n );\n return { resolved, refMap };\n}\n\nfunction resolveNode(\n node: unknown,\n baseDir: string,\n pointer: string,\n visiting: Set<string>,\n refMap: RefMap\n): unknown {\n if (node === null || typeof node !== 'object') return node;\n\n if (Array.isArray(node)) {\n return node.map((item, i) =>\n resolveNode(item, baseDir, `${pointer}/${i}`, visiting, refMap)\n );\n }\n\n const obj = node as Record<string, unknown>;\n\n if (typeof obj['$ref'] === 'string') {\n return resolveRef(obj, baseDir, pointer, visiting, refMap);\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = resolveNode(\n value,\n baseDir,\n `${pointer}/${key}`,\n visiting,\n refMap\n );\n }\n return result;\n}\n\nfunction resolveRef(\n obj: Record<string, unknown>,\n baseDir: string,\n pointer: string,\n visiting: Set<string>,\n refMap: RefMap\n): unknown {\n const refPath = obj['$ref'] as string;\n\n if (!isRelativePath(refPath)) {\n logger.warn(\n chalk.yellow(\n `Skipping non-relative $ref at ${pointer || '/'}: ${refPath}`\n )\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n const resolvedFilePath = path.resolve(baseDir, refPath);\n\n if (visiting.has(resolvedFilePath)) {\n logger.warn(\n chalk.yellow(`Circular $ref detected at ${pointer || '/'}: ${refPath}`)\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n if (!fs.existsSync(resolvedFilePath)) {\n logger.warn(\n chalk.yellow(\n `$ref file not found at ${pointer || '/'}: ${refPath} (resolved to ${resolvedFilePath})`\n )\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n let fileContent: string;\n try {\n fileContent = fs.readFileSync(resolvedFilePath, 'utf-8');\n } catch {\n logger.warn(chalk.yellow(`Failed to read $ref file: ${resolvedFilePath}`));\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fileContent);\n } catch {\n logger.warn(\n chalk.yellow(`$ref file is not valid JSON: ${resolvedFilePath}`)\n );\n const { $ref: _, ...rest } = obj;\n return rest;\n }\n\n const { $ref: _, ...siblings } = obj;\n\n // Record provenance before recursive resolution\n refMap.set(pointer, {\n sourceFile: resolvedFilePath,\n refPath,\n containingDir: baseDir,\n originalContent: parsed,\n siblings,\n });\n\n // Recursively resolve nested $ref in the referenced file\n const refFileDir = path.dirname(resolvedFilePath);\n const nextVisiting = new Set(visiting);\n nextVisiting.add(resolvedFilePath);\n\n const resolvedContent = resolveNode(\n parsed,\n refFileDir,\n pointer,\n nextVisiting,\n refMap\n );\n\n // Apply Mintlify merge rules\n if (\n resolvedContent !== null &&\n typeof resolvedContent === 'object' &&\n !Array.isArray(resolvedContent)\n ) {\n // Object: merge siblings on top (siblings take precedence)\n return { ...(resolvedContent as Record<string, unknown>), ...siblings };\n }\n\n // Non-object (array, string, number, etc.): replace entirely, drop siblings\n return resolvedContent;\n}\n\n/**\n * Check if a file should have $ref resolution applied based on the settings.\n * Returns true if the file matches a jsonSchema entry with resolveRefs: true.\n */\nexport function shouldResolveRefs(\n filePath: string,\n options?: { jsonSchema?: Record<string, unknown> }\n): boolean {\n if (!options?.jsonSchema) return false;\n\n const relative = path.relative(process.cwd(), filePath);\n for (const [glob, schema] of Object.entries(options.jsonSchema)) {\n const jsonSchema = schema as { resolveRefs?: boolean };\n if (jsonSchema.resolveRefs && micromatch.isMatch(relative, glob)) {\n return true;\n }\n }\n return false;\n}\n\nfunction isRelativePath(refPath: string): boolean {\n if (path.isAbsolute(refPath)) return false;\n if (/^[a-z]+:\\/\\//i.test(refPath)) return false;\n return true;\n}\n"],"mappings":"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"}
@@ -109,7 +109,10 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
109
109
  if (internalRefs.length > 0) {
110
110
  if (!defaultEntryRef) {
111
111
  const defaultEntry = entries[defaultIndex];
112
- for (const ref of internalRefs) setAtPointer(defaultEntry, ref.relativePointer, { $ref: ref.refPath });
112
+ for (const ref of internalRefs) setAtPointer(defaultEntry, ref.relativePointer, {
113
+ ...ref.siblings,
114
+ $ref: ref.refPath
115
+ });
113
116
  }
114
117
  for (let i = 0; i < entries.length; i++) {
115
118
  if (i === defaultIndex) continue;
@@ -129,8 +132,13 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
129
132
  const originalAbsPath = path.resolve(ref.resolvedDir, ref.refPath);
130
133
  const relToBaseDir = path.relative(entryBaseDir, originalAbsPath);
131
134
  const localeRelPath = path.join(keyValue, relToBaseDir);
132
- writeJsonFile(path.resolve(entryBaseDir, localeRelPath), subtree);
133
- setAtPointer(entry, ref.relativePointer, { $ref: ref.refPath });
135
+ const outputPath = path.resolve(entryBaseDir, localeRelPath);
136
+ const { siblings, content } = extractRefSiblings(subtree, ref);
137
+ writeJsonFile(outputPath, content);
138
+ setAtPointer(entry, ref.relativePointer, {
139
+ ...siblings,
140
+ $ref: ref.refPath
141
+ });
134
142
  }
135
143
  }
136
144
  logger.info(`Restored $ref structure for default entry`);
@@ -158,7 +166,10 @@ function processSplitEntries(fileJson, compositeFilePath, docsDir, splitConfig,
158
166
  $ref: toRelativeRefPath(arrayHostDir, entryFilePath)
159
167
  };
160
168
  }
161
- if (defaultEntryRef) entries[defaultIndex] = { $ref: defaultEntryRef.refPath };
169
+ if (defaultEntryRef) entries[defaultIndex] = {
170
+ ...defaultEntryRef.siblings,
171
+ $ref: defaultEntryRef.refPath
172
+ };
162
173
  logger.info(`Split keyed entries into ref files`);
163
174
  }
164
175
  /**
@@ -186,9 +197,49 @@ function restoreTopLevelRefs(fileJson, refMap, splitConfig) {
186
197
  const subtree = getAtPointer(fileJson, pointer);
187
198
  if (subtree === void 0) continue;
188
199
  if (isJsonContainer(subtree) && !Array.isArray(subtree) && typeof subtree.$ref === "string") continue;
189
- writeJsonFile(entry.sourceFile, subtree);
190
- setAtPointer(fileJson, pointer, { $ref: entry.refPath });
200
+ const { siblings, content } = extractRefSiblings(subtree, entry);
201
+ writeJsonFile(entry.sourceFile, content);
202
+ setAtPointer(fileJson, pointer, {
203
+ ...siblings,
204
+ $ref: entry.refPath
205
+ });
206
+ }
207
+ }
208
+ /**
209
+ * Mintlify merges keys placed next to a `$ref` on top of the referenced file's
210
+ * content, so ref resolution folds them into the inlined subtree. When
211
+ * restoring the `$ref`, lift those keys back out: they stay next to the `$ref`
212
+ * (with their possibly translated values) and are dropped from the content
213
+ * written to the ref file — unless the referenced file also defined the key,
214
+ * in which case it stays in both, preserving the source topology and
215
+ * Mintlify's sibling-precedence semantics.
216
+ */
217
+ function extractRefSiblings(subtree, ref) {
218
+ const originalSiblings = ref.siblings ?? {};
219
+ const siblingKeys = Object.keys(originalSiblings);
220
+ if (siblingKeys.length === 0) return {
221
+ siblings: {},
222
+ content: subtree
223
+ };
224
+ if (!isJsonContainer(subtree) || Array.isArray(subtree)) return {
225
+ siblings: { ...originalSiblings },
226
+ content: subtree
227
+ };
228
+ const content = { ...subtree };
229
+ const siblings = {};
230
+ const original = ref.originalContent;
231
+ const originalContentHasKey = (key) => isJsonContainer(original) && !Array.isArray(original) && key in original;
232
+ for (const key of siblingKeys) if (key in content) {
233
+ siblings[key] = content[key];
234
+ if (!originalContentHasKey(key)) delete content[key];
235
+ } else {
236
+ logger.warn(`Sibling key "${key}" missing from translated content for $ref ${ref.refPath ?? "(unknown)"}; restoring source value`);
237
+ siblings[key] = originalSiblings[key];
191
238
  }
239
+ return {
240
+ siblings,
241
+ content
242
+ };
192
243
  }
193
244
  /**
194
245
  * Collect refMap entries that describe an entry's internal $ref chain.
@@ -201,7 +252,9 @@ function collectInternalRefs(refMap, entryPointerPrefix) {
201
252
  refs.push({
202
253
  relativePointer: pointer.slice(entryPointerPrefix.length),
203
254
  refPath: entry.refPath,
204
- resolvedDir: entry.containingDir
255
+ resolvedDir: entry.containingDir,
256
+ siblings: entry.siblings,
257
+ originalContent: entry.originalContent
205
258
  });
206
259
  }
207
260
  refs.sort((a, b) => b.relativePointer.length - a.relativePointer.length);
@@ -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 // 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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"}
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.siblings,\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\n const { siblings, content } = extractRefSiblings(subtree, ref);\n writeJsonFile(outputPath, content);\n\n setAtPointer(entry, ref.relativePointer, {\n ...siblings,\n $ref: ref.refPath,\n });\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] = {\n ...defaultEntryRef.siblings,\n $ref: defaultEntryRef.refPath,\n };\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 const { siblings, content } = extractRefSiblings(subtree, entry);\n writeJsonFile(entry.sourceFile, content);\n setAtPointer(fileJson, pointer, { ...siblings, $ref: entry.refPath });\n }\n}\n\n/**\n * Mintlify merges keys placed next to a `$ref` on top of the referenced file's\n * content, so ref resolution folds them into the inlined subtree. When\n * restoring the `$ref`, lift those keys back out: they stay next to the `$ref`\n * (with their possibly translated values) and are dropped from the content\n * written to the ref file — unless the referenced file also defined the key,\n * in which case it stays in both, preserving the source topology and\n * Mintlify's sibling-precedence semantics.\n */\nfunction extractRefSiblings(\n subtree: unknown,\n ref: {\n siblings?: Record<string, unknown>;\n originalContent?: unknown;\n refPath?: string;\n }\n): { siblings: Record<string, unknown>; content: unknown } {\n const originalSiblings = ref.siblings ?? {};\n const siblingKeys = Object.keys(originalSiblings);\n if (siblingKeys.length === 0) {\n return { siblings: {}, content: subtree };\n }\n if (!isJsonContainer(subtree) || Array.isArray(subtree)) {\n // Non-object content cannot carry merged siblings; restore the originals\n return { siblings: { ...originalSiblings }, content: subtree };\n }\n const content = { ...subtree };\n const siblings: Record<string, unknown> = {};\n const original = ref.originalContent;\n const originalContentHasKey = (key: string) =>\n isJsonContainer(original) && !Array.isArray(original) && key in original;\n for (const key of siblingKeys) {\n if (key in content) {\n siblings[key] = content[key];\n if (!originalContentHasKey(key)) {\n delete content[key];\n }\n } else {\n // Object resolutions always merge siblings into the subtree, so this\n // only fires if that invariant breaks upstream. Restoring the source\n // value keeps the output schema-valid, but it skips translation — warn\n // so the regression is visible.\n logger.warn(\n `Sibling key \"${key}\" missing from translated content for $ref ${ref.refPath ?? '(unknown)'}; restoring source value`\n );\n siblings[key] = originalSiblings[key];\n }\n }\n return { siblings, content };\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): {\n relativePointer: string;\n refPath: string;\n resolvedDir: string;\n siblings?: Record<string, unknown>;\n originalContent?: unknown;\n}[] {\n const refs: {\n relativePointer: string;\n refPath: string;\n resolvedDir: string;\n siblings?: Record<string, unknown>;\n originalContent?: unknown;\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 siblings: entry.siblings,\n originalContent: entry.originalContent,\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.47",
3
+ "version": "2.14.48",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [