gt 2.16.2 → 2.16.3

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.16.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2040](https://github.com/generaltranslation/gt/pull/2040) [`79e6836`](https://github.com/generaltranslation/gt/commit/79e6836349191220ee8f5848b5e6ff287246f162) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Use the locale exactly as configured when substituting `{locale}` in file and JSON transforms, instead of canonicalizing it. Projects that configure a non-canonical tag such as `fr-ca` or `ja-jp` were getting content written to `docs/fr-CA/` while `[locale]` substitution and localized URLs used `docs/fr-ca/`, so every internal link in the translated output pointed at a directory that did not exist.
8
+
3
9
  ## 2.16.2
4
10
 
5
11
  ### Patch Changes
@@ -3,9 +3,8 @@ import { getRelative } from "../../fs/findFilepath.js";
3
3
  import { SUPPORTED_FILE_EXTENSIONS } from "./supportedFiles.js";
4
4
  import { replaceFileExtensionForFormat } from "./transformFormat.js";
5
5
  import { resolveLocaleFiles } from "../../fs/config/parseFilesConfig.js";
6
- import { replaceLocalePlaceholders } from "../utils.js";
6
+ import { getConfiguredLocaleProperties, replaceLocalePlaceholders } from "../utils.js";
7
7
  import path from "node:path";
8
- import { getLocaleProperties } from "@generaltranslation/format";
9
8
  //#region src/formats/files/fileMapping.ts
10
9
  /**
11
10
  * Creates a mapping between source files and their translated counterparts for each locale
@@ -39,8 +38,8 @@ function createFileMapping(filePaths, placeholderPaths, transformPaths, transfor
39
38
  return path.join(directory, transformedFileName);
40
39
  });
41
40
  else if (Array.isArray(transformPath)) {
42
- const targetLocaleProperties = getLocaleProperties(locale);
43
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
41
+ const targetLocaleProperties = getConfiguredLocaleProperties(locale);
42
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
44
43
  translatedFiles = translatedFiles.map((filePath) => {
45
44
  const relativePath = getRelative(filePath);
46
45
  for (const transform of transformPath) {
@@ -58,8 +57,8 @@ function createFileMapping(filePaths, placeholderPaths, transformPaths, transfor
58
57
  return filePath;
59
58
  });
60
59
  } else {
61
- const targetLocaleProperties = getLocaleProperties(locale);
62
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
60
+ const targetLocaleProperties = getConfiguredLocaleProperties(locale);
61
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
63
62
  if (!transformPath.replace || typeof transformPath.replace !== "string") continue;
64
63
  const replaceString = replaceLocalePlaceholders(transformPath.replace, targetLocaleProperties);
65
64
  translatedFiles = translatedFiles.map((filePath) => {
@@ -1 +1 @@
1
- {"version":3,"file":"fileMapping.js","names":[],"sources":["../../../src/formats/files/fileMapping.ts"],"sourcesContent":["import {\n ResolvedFiles,\n TransformFiles,\n TransformFormats,\n} from '../../types/index.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../files/supportedFiles.js';\nimport { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';\nimport path from 'node:path';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport { getLocaleProperties } from '@generaltranslation/format';\nimport { replaceLocalePlaceholders } from '../utils.js';\nimport { FileMapping } from '../../types/files.js';\nimport { TEMPLATE_FILE_NAME } from '../../utils/constants.js';\nimport { replaceFileExtensionForFormat } from './transformFormat.js';\n\n/**\n * Creates a mapping between source files and their translated counterparts for each locale\n * @param filePaths - Resolved file paths for different file types\n * @param placeholderPaths - Placeholder paths for translated files\n * @param transformPaths - Transform paths for file naming\n * @param transformFormats - Output file format transforms for translated files\n * @param locales - List of locales to create a mapping for\n * @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths\n */\nexport function createFileMapping(\n filePaths: ResolvedFiles,\n placeholderPaths: ResolvedFiles,\n transformPaths: TransformFiles,\n transformFormats: TransformFormats,\n targetLocales: string[],\n defaultLocale: string\n): FileMapping {\n const fileMapping: FileMapping = {};\n\n for (const locale of targetLocales) {\n const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);\n const localeMapping: FileMapping[string] = {};\n\n // Process each file type\n\n // Start with GTJSON Template files\n if (translatedPaths.gt) {\n const filepath = translatedPaths.gt;\n localeMapping[TEMPLATE_FILE_NAME] = getRelative(filepath);\n }\n\n for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {\n if (!filePaths[typeIndex] || !translatedPaths[typeIndex]) continue;\n\n const sourcePaths = filePaths[typeIndex];\n let translatedFiles = translatedPaths[typeIndex];\n if (!translatedFiles) continue;\n\n const transformPath = transformPaths[typeIndex];\n const transformFormat = transformFormats?.[typeIndex];\n\n if (transformPath) {\n if (typeof transformPath === 'string') {\n translatedFiles = translatedFiles.map((filePath) => {\n const directory = path.dirname(filePath);\n const fileName = path.basename(filePath);\n const baseName = fileName.split('.')[0];\n const transformedFileName = transformPath\n .replace('*', baseName)\n .replace('[locale]', locale);\n return path.join(directory, transformedFileName);\n });\n } else if (Array.isArray(transformPath)) {\n // transformPath is an array of TransformOption objects\n const targetLocaleProperties = getLocaleProperties(locale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n\n translatedFiles = translatedFiles.map((filePath) => {\n const relativePath = getRelative(filePath);\n\n // Try each transform in order until one matches\n for (const transform of transformPath) {\n if (!transform.replace || typeof transform.replace !== 'string') {\n continue;\n }\n\n // Replace all locale property placeholders in the replace string\n const replaceString = replaceLocalePlaceholders(\n transform.replace,\n targetLocaleProperties\n );\n\n if (transform.match && typeof transform.match === 'string') {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transform.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n const regex = new RegExp(matchString);\n if (regex.test(relativePath)) {\n // This transform matches, apply it and break\n const transformedPath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n return path.resolve(transformedPath);\n }\n } else {\n // No match provided: treat as a direct replacement (override)\n return path.resolve(replaceString);\n }\n }\n\n // If no transforms matched, return the original path\n return filePath;\n });\n } else {\n // transformPath is an object\n const targetLocaleProperties = getLocaleProperties(locale);\n const defaultLocaleProperties = getLocaleProperties(defaultLocale);\n if (\n !transformPath.replace ||\n typeof transformPath.replace !== 'string'\n ) {\n continue;\n }\n // Replace all locale property placeholders\n const replaceString = replaceLocalePlaceholders(\n transformPath.replace,\n targetLocaleProperties\n );\n translatedFiles = translatedFiles.map((filePath) => {\n let relativePath = getRelative(filePath);\n if (\n transformPath.match &&\n typeof transformPath.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformPath.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n relativePath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n relativePath = replaceString;\n }\n return path.resolve(relativePath);\n });\n }\n }\n\n for (let i = 0; i < sourcePaths.length; i++) {\n const sourceFile = getRelative(sourcePaths[i]);\n // Format transforms keep the mapped path but rewrite the output suffix.\n const translatedFile = getRelative(\n transformFormat\n ? replaceFileExtensionForFormat(translatedFiles[i], transformFormat)\n : translatedFiles[i]\n );\n localeMapping[sourceFile] = translatedFile;\n }\n }\n\n fileMapping[locale] = localeMapping;\n }\n\n return fileMapping;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,SAAgB,kBACd,WACA,kBACA,gBACA,kBACA,eACA,eACa;CACb,MAAM,cAA2B,EAAE;AAEnC,MAAK,MAAM,UAAU,eAAe;EAClC,MAAM,kBAAkB,mBAAmB,kBAAkB,OAAO;EACpE,MAAM,gBAAqC,EAAE;AAK7C,MAAI,gBAAgB,IAAI;GACtB,MAAM,WAAW,gBAAgB;AACjC,iBAAc,sBAAsB,YAAY,SAAS;;AAG3D,OAAK,MAAM,aAAa,2BAA2B;AACjD,OAAI,CAAC,UAAU,cAAc,CAAC,gBAAgB,WAAY;GAE1D,MAAM,cAAc,UAAU;GAC9B,IAAI,kBAAkB,gBAAgB;AACtC,OAAI,CAAC,gBAAiB;GAEtB,MAAM,gBAAgB,eAAe;GACrC,MAAM,kBAAkB,mBAAmB;AAE3C,OAAI,cACF,KAAI,OAAO,kBAAkB,SAC3B,mBAAkB,gBAAgB,KAAK,aAAa;IAClD,MAAM,YAAY,KAAK,QAAQ,SAAS;IAExC,MAAM,WADW,KAAK,SAAS,SACN,CAAC,MAAM,IAAI,CAAC;IACrC,MAAM,sBAAsB,cACzB,QAAQ,KAAK,SAAS,CACtB,QAAQ,YAAY,OAAO;AAC9B,WAAO,KAAK,KAAK,WAAW,oBAAoB;KAChD;YACO,MAAM,QAAQ,cAAc,EAAE;IAEvC,MAAM,yBAAyB,oBAAoB,OAAO;IAC1D,MAAM,0BAA0B,oBAAoB,cAAc;AAElE,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,MAAM,eAAe,YAAY,SAAS;AAG1C,UAAK,MAAM,aAAa,eAAe;AACrC,UAAI,CAAC,UAAU,WAAW,OAAO,UAAU,YAAY,SACrD;MAIF,MAAM,gBAAgB,0BACpB,UAAU,SACV,uBACD;AAED,UAAI,UAAU,SAAS,OAAO,UAAU,UAAU,UAAU;OAE1D,IAAI,cAAc,UAAU;AAC5B,qBAAc,0BACZ,aACA,wBACD;AAGD,WAAI,IADc,OAAO,YAChB,CAAC,KAAK,aAAa,EAAE;QAE5B,MAAM,kBAAkB,aAAa,QACnC,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;AACD,eAAO,KAAK,QAAQ,gBAAgB;;YAItC,QAAO,KAAK,QAAQ,cAAc;;AAKtC,YAAO;MACP;UACG;IAEL,MAAM,yBAAyB,oBAAoB,OAAO;IAC1D,MAAM,0BAA0B,oBAAoB,cAAc;AAClE,QACE,CAAC,cAAc,WACf,OAAO,cAAc,YAAY,SAEjC;IAGF,MAAM,gBAAgB,0BACpB,cAAc,SACd,uBACD;AACD,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,IAAI,eAAe,YAAY,SAAS;AACxC,SACE,cAAc,SACd,OAAO,cAAc,UAAU,UAC/B;MAEA,IAAI,cAAc,cAAc;AAChC,oBAAc,0BACZ,aACA,wBACD;AAED,qBAAe,aAAa,QAC1B,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;WAED,gBAAe;AAEjB,YAAO,KAAK,QAAQ,aAAa;MACjC;;AAIN,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;IAC3C,MAAM,aAAa,YAAY,YAAY,GAAG;AAO9C,kBAAc,cALS,YACrB,kBACI,8BAA8B,gBAAgB,IAAI,gBAAgB,GAClE,gBAAgB,GAEoB;;;AAI9C,cAAY,UAAU;;AAGxB,QAAO"}
1
+ {"version":3,"file":"fileMapping.js","names":[],"sources":["../../../src/formats/files/fileMapping.ts"],"sourcesContent":["import {\n ResolvedFiles,\n TransformFiles,\n TransformFormats,\n} from '../../types/index.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../files/supportedFiles.js';\nimport { resolveLocaleFiles } from '../../fs/config/parseFilesConfig.js';\nimport path from 'node:path';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} from '../utils.js';\nimport { FileMapping } from '../../types/files.js';\nimport { TEMPLATE_FILE_NAME } from '../../utils/constants.js';\nimport { replaceFileExtensionForFormat } from './transformFormat.js';\n\n/**\n * Creates a mapping between source files and their translated counterparts for each locale\n * @param filePaths - Resolved file paths for different file types\n * @param placeholderPaths - Placeholder paths for translated files\n * @param transformPaths - Transform paths for file naming\n * @param transformFormats - Output file format transforms for translated files\n * @param locales - List of locales to create a mapping for\n * @returns A mapping between source files and their translated counterparts for each locale, in the form of relative paths\n */\nexport function createFileMapping(\n filePaths: ResolvedFiles,\n placeholderPaths: ResolvedFiles,\n transformPaths: TransformFiles,\n transformFormats: TransformFormats,\n targetLocales: string[],\n defaultLocale: string\n): FileMapping {\n const fileMapping: FileMapping = {};\n\n for (const locale of targetLocales) {\n const translatedPaths = resolveLocaleFiles(placeholderPaths, locale);\n const localeMapping: FileMapping[string] = {};\n\n // Process each file type\n\n // Start with GTJSON Template files\n if (translatedPaths.gt) {\n const filepath = translatedPaths.gt;\n localeMapping[TEMPLATE_FILE_NAME] = getRelative(filepath);\n }\n\n for (const typeIndex of SUPPORTED_FILE_EXTENSIONS) {\n if (!filePaths[typeIndex] || !translatedPaths[typeIndex]) continue;\n\n const sourcePaths = filePaths[typeIndex];\n let translatedFiles = translatedPaths[typeIndex];\n if (!translatedFiles) continue;\n\n const transformPath = transformPaths[typeIndex];\n const transformFormat = transformFormats?.[typeIndex];\n\n if (transformPath) {\n if (typeof transformPath === 'string') {\n translatedFiles = translatedFiles.map((filePath) => {\n const directory = path.dirname(filePath);\n const fileName = path.basename(filePath);\n const baseName = fileName.split('.')[0];\n const transformedFileName = transformPath\n .replace('*', baseName)\n .replace('[locale]', locale);\n return path.join(directory, transformedFileName);\n });\n } else if (Array.isArray(transformPath)) {\n // transformPath is an array of TransformOption objects\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties =\n getConfiguredLocaleProperties(defaultLocale);\n\n translatedFiles = translatedFiles.map((filePath) => {\n const relativePath = getRelative(filePath);\n\n // Try each transform in order until one matches\n for (const transform of transformPath) {\n if (!transform.replace || typeof transform.replace !== 'string') {\n continue;\n }\n\n // Replace all locale property placeholders in the replace string\n const replaceString = replaceLocalePlaceholders(\n transform.replace,\n targetLocaleProperties\n );\n\n if (transform.match && typeof transform.match === 'string') {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transform.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n const regex = new RegExp(matchString);\n if (regex.test(relativePath)) {\n // This transform matches, apply it and break\n const transformedPath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n return path.resolve(transformedPath);\n }\n } else {\n // No match provided: treat as a direct replacement (override)\n return path.resolve(replaceString);\n }\n }\n\n // If no transforms matched, return the original path\n return filePath;\n });\n } else {\n // transformPath is an object\n const targetLocaleProperties = getConfiguredLocaleProperties(locale);\n const defaultLocaleProperties =\n getConfiguredLocaleProperties(defaultLocale);\n if (\n !transformPath.replace ||\n typeof transformPath.replace !== 'string'\n ) {\n continue;\n }\n // Replace all locale property placeholders\n const replaceString = replaceLocalePlaceholders(\n transformPath.replace,\n targetLocaleProperties\n );\n translatedFiles = translatedFiles.map((filePath) => {\n let relativePath = getRelative(filePath);\n if (\n transformPath.match &&\n typeof transformPath.match === 'string'\n ) {\n // Replace locale placeholders in the match string using defaultLocale properties\n let matchString = transformPath.match;\n matchString = replaceLocalePlaceholders(\n matchString,\n defaultLocaleProperties\n );\n\n relativePath = relativePath.replace(\n new RegExp(matchString, 'g'),\n replaceString\n );\n } else {\n relativePath = replaceString;\n }\n return path.resolve(relativePath);\n });\n }\n }\n\n for (let i = 0; i < sourcePaths.length; i++) {\n const sourceFile = getRelative(sourcePaths[i]);\n // Format transforms keep the mapped path but rewrite the output suffix.\n const translatedFile = getRelative(\n transformFormat\n ? replaceFileExtensionForFormat(translatedFiles[i], transformFormat)\n : translatedFiles[i]\n );\n localeMapping[sourceFile] = translatedFile;\n }\n }\n\n fileMapping[locale] = localeMapping;\n }\n\n return fileMapping;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,SAAgB,kBACd,WACA,kBACA,gBACA,kBACA,eACA,eACa;CACb,MAAM,cAA2B,EAAE;AAEnC,MAAK,MAAM,UAAU,eAAe;EAClC,MAAM,kBAAkB,mBAAmB,kBAAkB,OAAO;EACpE,MAAM,gBAAqC,EAAE;AAK7C,MAAI,gBAAgB,IAAI;GACtB,MAAM,WAAW,gBAAgB;AACjC,iBAAc,sBAAsB,YAAY,SAAS;;AAG3D,OAAK,MAAM,aAAa,2BAA2B;AACjD,OAAI,CAAC,UAAU,cAAc,CAAC,gBAAgB,WAAY;GAE1D,MAAM,cAAc,UAAU;GAC9B,IAAI,kBAAkB,gBAAgB;AACtC,OAAI,CAAC,gBAAiB;GAEtB,MAAM,gBAAgB,eAAe;GACrC,MAAM,kBAAkB,mBAAmB;AAE3C,OAAI,cACF,KAAI,OAAO,kBAAkB,SAC3B,mBAAkB,gBAAgB,KAAK,aAAa;IAClD,MAAM,YAAY,KAAK,QAAQ,SAAS;IAExC,MAAM,WADW,KAAK,SAAS,SACN,CAAC,MAAM,IAAI,CAAC;IACrC,MAAM,sBAAsB,cACzB,QAAQ,KAAK,SAAS,CACtB,QAAQ,YAAY,OAAO;AAC9B,WAAO,KAAK,KAAK,WAAW,oBAAoB;KAChD;YACO,MAAM,QAAQ,cAAc,EAAE;IAEvC,MAAM,yBAAyB,8BAA8B,OAAO;IACpE,MAAM,0BACJ,8BAA8B,cAAc;AAE9C,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,MAAM,eAAe,YAAY,SAAS;AAG1C,UAAK,MAAM,aAAa,eAAe;AACrC,UAAI,CAAC,UAAU,WAAW,OAAO,UAAU,YAAY,SACrD;MAIF,MAAM,gBAAgB,0BACpB,UAAU,SACV,uBACD;AAED,UAAI,UAAU,SAAS,OAAO,UAAU,UAAU,UAAU;OAE1D,IAAI,cAAc,UAAU;AAC5B,qBAAc,0BACZ,aACA,wBACD;AAGD,WAAI,IADc,OAAO,YAChB,CAAC,KAAK,aAAa,EAAE;QAE5B,MAAM,kBAAkB,aAAa,QACnC,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;AACD,eAAO,KAAK,QAAQ,gBAAgB;;YAItC,QAAO,KAAK,QAAQ,cAAc;;AAKtC,YAAO;MACP;UACG;IAEL,MAAM,yBAAyB,8BAA8B,OAAO;IACpE,MAAM,0BACJ,8BAA8B,cAAc;AAC9C,QACE,CAAC,cAAc,WACf,OAAO,cAAc,YAAY,SAEjC;IAGF,MAAM,gBAAgB,0BACpB,cAAc,SACd,uBACD;AACD,sBAAkB,gBAAgB,KAAK,aAAa;KAClD,IAAI,eAAe,YAAY,SAAS;AACxC,SACE,cAAc,SACd,OAAO,cAAc,UAAU,UAC/B;MAEA,IAAI,cAAc,cAAc;AAChC,oBAAc,0BACZ,aACA,wBACD;AAED,qBAAe,aAAa,QAC1B,IAAI,OAAO,aAAa,IAAI,EAC5B,cACD;WAED,gBAAe;AAEjB,YAAO,KAAK,QAAQ,aAAa;MACjC;;AAIN,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;IAC3C,MAAM,aAAa,YAAY,YAAY,GAAG;AAO9C,kBAAc,cALS,YACrB,kBACI,8BAA8B,gBAAgB,IAAI,gBAAgB,GAClE,gBAAgB,GAEoB;;;AAI9C,cAAY,UAAU;;AAGxB,QAAO"}
@@ -1,12 +1,11 @@
1
1
  import { logger } from "../../console/logger.js";
2
2
  import { gt } from "../../utils/gt.js";
3
3
  import { exitSync } from "../../console/logging.js";
4
+ import { getConfiguredLocaleProperties, replaceLocalePlaceholders } from "../utils.js";
4
5
  import { getJSONPathMatches, getJSONPathValues } from "./jsonPath.js";
5
6
  import { findMatchingItemArray, findMatchingItemObject, generateSourceObjectPointers, getIdentifyingLocaleProperty, getSourceObjectOptionsArray, validateJsonSchema } from "./utils.js";
6
7
  import { getJSONPointerValue, setJSONPointerValue } from "./jsonPointer.js";
7
8
  import { applyStructuralTransforms, unapplyStructuralTransforms } from "./transformJson.js";
8
- import { replaceLocalePlaceholders } from "../utils.js";
9
- import { getLocaleProperties } from "@generaltranslation/format";
10
9
  //#region src/formats/json/mergeJson.ts
11
10
  function mergeJson(originalContent, inputPath, options, targets, defaultLocale, localeOrder = []) {
12
11
  const jsonSchema = validateJsonSchema(options, inputPath);
@@ -221,8 +220,8 @@ function omitProperties(item, properties) {
221
220
  */
222
221
  function applyTransformations(sourceItem, transform, targetLocale, defaultLocale) {
223
222
  if (!transform) return;
224
- const targetLocaleProperties = getLocaleProperties(targetLocale);
225
- const defaultLocaleProperties = getLocaleProperties(defaultLocale);
223
+ const targetLocaleProperties = getConfiguredLocaleProperties(targetLocale);
224
+ const defaultLocaleProperties = getConfiguredLocaleProperties(defaultLocale);
226
225
  for (const [transformPath, transformOptions] of Object.entries(transform)) {
227
226
  if (!transformOptions.replace || typeof transformOptions.replace !== "string") continue;
228
227
  const results = getJSONPathMatches(sourceItem, transformPath);
@@ -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 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
+ {"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 {\n getConfiguredLocaleProperties,\n replaceLocalePlaceholders,\n} 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 = getConfiguredLocaleProperties(targetLocale);\n const defaultLocaleProperties = getConfiguredLocaleProperties(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,11 +1,11 @@
1
1
  import { logger } from "../../console/logger.js";
2
2
  import { exitSync } from "../../console/logging.js";
3
+ import { getConfiguredLocaleProperties } from "../utils.js";
3
4
  import { getJSONPathMatches } from "./jsonPath.js";
4
5
  import { flattenJson } from "./flattenJson.js";
5
6
  import chalk from "chalk";
6
7
  import path from "node:path";
7
8
  import micromatch from "micromatch";
8
- import { getLocaleProperties } from "@generaltranslation/format";
9
9
  //#region src/formats/json/utils.ts
10
10
  const { isMatch } = micromatch;
11
11
  function findMatchingItemArray(locale, sourceObjectOptions, sourceObjectPointer, sourceObjectValue) {
@@ -55,7 +55,7 @@ function findMatchingItemObject(locale, sourceObjectPointer, sourceObjectOptions
55
55
  */
56
56
  function getIdentifyingLocaleProperty(locale, sourceObjectPointer, sourceObjectOptions) {
57
57
  const localeProperty = sourceObjectOptions.localeProperty || "code";
58
- const identifyingLocaleProperty = getLocaleProperties(locale)[localeProperty];
58
+ const identifyingLocaleProperty = getConfiguredLocaleProperties(locale)[localeProperty];
59
59
  if (!identifyingLocaleProperty) {
60
60
  logger.error(`Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`);
61
61
  return exitSync(1);
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../../src/formats/json/utils.ts"],"sourcesContent":["import { getLocaleProperties } from '@generaltranslation/format';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\nimport {\n AdditionalOptions,\n JsonSchema,\n SourceObjectOptions,\n} from '../../types/index.js';\nimport { flattenJson } from './flattenJson.js';\nimport chalk from 'chalk';\nimport path from 'node:path';\nimport micromatch from 'micromatch';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches } from './jsonPath.js';\nconst { isMatch } = micromatch;\n\ntype MatchingArrayItem = {\n sourceItem: JSONValue;\n keyParentProperty: string | number;\n keyPointer: string;\n index: number;\n};\n\ntype SourceObjectPointerMap = Record<\n string,\n { sourceObjectValue: JSONValue; sourceObjectOptions: SourceObjectOptions }\n>;\n\n// Find the matching source item in an array\n// where the key matches the identifying locale property\n// If no matching item is found, exit with an error\nexport function findMatchingItemArray(\n locale: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectPointer: string,\n sourceObjectValue: JSONValue[]\n): Record<string, MatchingArrayItem> {\n const { identifyingLocaleProperty, localeKeyJsonPath } =\n getSourceObjectOptionsArray(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n // Use the json pointer key to locate the source item\n const matchingItems: Record<string, MatchingArrayItem> = {};\n for (const [index, item] of sourceObjectValue.entries()) {\n // Get the key candidates\n const keyCandidates = getJSONPathMatches(item, localeKeyJsonPath);\n if (!keyCandidates) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} does not have a key value at path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (keyCandidates.length === 0) {\n // If no key candidates, skip the item\n continue;\n } else if (keyCandidates.length > 1) {\n // If multiple key candidates, exit with an error\n logger.error(\n `Source item at path: ${sourceObjectPointer} has multiple matching keys with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (identifyingLocaleProperty !== keyCandidates[0].value) {\n // Validate the key is the identifying locale property\n continue;\n }\n const keyParentProperty = keyCandidates[0].parentProperty;\n if (keyParentProperty === null) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} has a root-level key match with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n }\n // Map the index to the source item\n matchingItems[`/${index}`] = {\n sourceItem: item,\n keyParentProperty,\n keyPointer: keyCandidates[0].pointer,\n index,\n };\n }\n return matchingItems;\n}\n\nexport function findMatchingItemObject(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectValue: JSONObject\n): { sourceItem: JSONValue | undefined; keyParentProperty: string } {\n const { identifyingLocaleProperty } = getSourceObjectOptionsObject(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n // Locate the source item\n if (sourceObjectValue[identifyingLocaleProperty]) {\n return {\n sourceItem: sourceObjectValue[identifyingLocaleProperty],\n keyParentProperty: identifyingLocaleProperty,\n };\n }\n return {\n sourceItem: undefined,\n keyParentProperty: identifyingLocaleProperty,\n };\n}\n\n/**\n * Get the identifying locale property for an object\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property\n */\nexport function getIdentifyingLocaleProperty(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n // Validate localeProperty\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const identifyingLocaleProperty =\n getLocaleProperties(locale)[localeProperty as keyof LocaleProperties];\n if (!identifyingLocaleProperty) {\n logger.error(\n `Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return identifyingLocaleProperty;\n}\n\n/**\n * Get the identifying locale property and the json path to the key for an array\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property and the json path to the key\n */\nexport function getSourceObjectOptionsArray(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string; localeKeyJsonPath: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const localeKeyJsonPath = sourceObjectOptions.key;\n if (!localeKeyJsonPath) {\n logger.error(\n `Source object options key is required for array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty, localeKeyJsonPath };\n}\n\nexport function getSourceObjectOptionsObject(\n defaultLocale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const jsonPathKey = sourceObjectOptions.key;\n if (jsonPathKey) {\n logger.error(\n `Source object options key is not allowed for object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty };\n}\n\n/**\n * Generate a mapping of sourceObjectPointer to SourceObjectOptions\n * where the sourceObjectPointer is a jsonpointer to the array or object containing\n * @param jsonSchema - The json schema to generate the mapping from\n * @param originalJson - The original json to generate the mapping from\n * @returns A mapping of sourceObjectPointer to SourceObjectOptions\n */\nexport function generateSourceObjectPointers(\n jsonSchema: {\n [sourceObjectPath: string]: SourceObjectOptions;\n },\n originalJson: JSONValue\n): SourceObjectPointerMap {\n const sourceObjectPointers = Object.entries(jsonSchema).reduce(\n (acc: SourceObjectPointerMap, [sourceObjectPath, sourceObjectOptions]) => {\n const sourceObjects = flattenJson(originalJson, [sourceObjectPath]);\n Object.entries(sourceObjects).forEach(([pointer, value]) => {\n acc[pointer as string] = {\n sourceObjectValue: value,\n sourceObjectOptions,\n };\n });\n return acc;\n },\n {}\n );\n return sourceObjectPointers;\n}\n\n/**\n * Validate the json schema for composite or include schemas\n * @param options - Additional options containing jsonSchema config\n * @param filePath - The path to the file (used for matching jsonSchema)\n * @returns The json schema, or null if no schema is found\n * @returns exitSync(1) if the json schema is invalid\n */\nexport function validateJsonSchema(\n options: AdditionalOptions,\n filePath: string\n): JsonSchema | null {\n if (!options.jsonSchema) {\n return null;\n }\n\n const fileGlobs = Object.keys(options.jsonSchema);\n const matchingGlob = fileGlobs.find((fileGlob) =>\n isMatch(path.relative(process.cwd(), filePath), fileGlob)\n );\n if (!matchingGlob || !options.jsonSchema[matchingGlob]) {\n return null;\n }\n // Validate includes or composite\n const jsonSchema = options.jsonSchema[matchingGlob];\n if (jsonSchema.include && jsonSchema.composite) {\n logger.error(\n 'include and composite cannot be used together in the same JSON schema'\n );\n return exitSync(1);\n }\n\n if (!jsonSchema.include && !jsonSchema.composite) {\n logger.error('No include or composite property found in JSON schema');\n return exitSync(1);\n }\n\n if (jsonSchema.structuralTransform && !jsonSchema.composite) {\n logger.error(\n 'structuralTransform requires composite to be defined in the JSON schema'\n );\n return exitSync(1);\n }\n return jsonSchema;\n}\n\nconst UNSUPPORTED_MINTLIFY_FIELDS = ['$ref'];\n\n/**\n * Recursively traverse a JSON value and collect all objects whose key\n * matches one of the unsupported field names.\n */\nfunction findMintlifyUnsupportedFields(\n value: JSONValue,\n fieldNames: string[],\n pointer: string = ''\n): { pointer: string; field: string; fieldValue: string }[] {\n if (value === null || typeof value !== 'object') return [];\n if (Array.isArray(value)) {\n const results: { pointer: string; field: string; fieldValue: string }[] =\n [];\n for (let i = 0; i < value.length; i++) {\n results.push(\n ...findMintlifyUnsupportedFields(\n value[i],\n fieldNames,\n `${pointer}/${i}`\n )\n );\n }\n return results;\n }\n // Check if this object contains an unsupported field\n const objectValue = value as JSONObject;\n for (const field of fieldNames) {\n if (typeof objectValue[field] === 'string') {\n return [{ pointer, field, fieldValue: objectValue[field] }];\n }\n }\n // Recurse into child properties\n const results: { pointer: string; field: string; fieldValue: string }[] = [];\n for (const key of Object.keys(objectValue)) {\n results.push(\n ...findMintlifyUnsupportedFields(\n objectValue[key],\n fieldNames,\n `${pointer}/${key}`\n )\n );\n }\n return results;\n}\n\n/**\n * Detect unsupported fields (e.g. $ref) in Mintlify docs.json files.\n * Logs a warning listing the fields found.\n */\nexport function detectMintlifyUnsupportedFields(\n json: JSONValue,\n filePath: string\n): void {\n const unsupported = findMintlifyUnsupportedFields(\n json,\n UNSUPPORTED_MINTLIFY_FIELDS\n );\n\n if (unsupported.length > 0) {\n const fileName = path.basename(filePath);\n const lines = unsupported\n .map(\n (u) =>\n chalk.yellow('• ') +\n chalk.white(\n `${u.pointer.replace(/\\//g, '.').replace(/^\\./, '')}.${u.field}`\n )\n )\n .join('\\n');\n logger.warn(\n chalk.yellow(\n `Mintlify config splitting is not yet supported. The following \\`$ref\\` fields were detected in \\`${fileName}\\` and will not be resolved:\\n`\n ) + lines\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/formats/json/utils.ts"],"sourcesContent":["import { getConfiguredLocaleProperties } from '../utils.js';\nimport { exitSync } from '../../console/logging.js';\nimport { logger } from '../../console/logger.js';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\nimport {\n AdditionalOptions,\n JsonSchema,\n SourceObjectOptions,\n} from '../../types/index.js';\nimport { flattenJson } from './flattenJson.js';\nimport chalk from 'chalk';\nimport path from 'node:path';\nimport micromatch from 'micromatch';\nimport type { JSONObject, JSONValue } from '../../types/data/json.js';\nimport { getJSONPathMatches } from './jsonPath.js';\nconst { isMatch } = micromatch;\n\ntype MatchingArrayItem = {\n sourceItem: JSONValue;\n keyParentProperty: string | number;\n keyPointer: string;\n index: number;\n};\n\ntype SourceObjectPointerMap = Record<\n string,\n { sourceObjectValue: JSONValue; sourceObjectOptions: SourceObjectOptions }\n>;\n\n// Find the matching source item in an array\n// where the key matches the identifying locale property\n// If no matching item is found, exit with an error\nexport function findMatchingItemArray(\n locale: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectPointer: string,\n sourceObjectValue: JSONValue[]\n): Record<string, MatchingArrayItem> {\n const { identifyingLocaleProperty, localeKeyJsonPath } =\n getSourceObjectOptionsArray(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n // Use the json pointer key to locate the source item\n const matchingItems: Record<string, MatchingArrayItem> = {};\n for (const [index, item] of sourceObjectValue.entries()) {\n // Get the key candidates\n const keyCandidates = getJSONPathMatches(item, localeKeyJsonPath);\n if (!keyCandidates) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} does not have a key value at path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (keyCandidates.length === 0) {\n // If no key candidates, skip the item\n continue;\n } else if (keyCandidates.length > 1) {\n // If multiple key candidates, exit with an error\n logger.error(\n `Source item at path: ${sourceObjectPointer} has multiple matching keys with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n } else if (identifyingLocaleProperty !== keyCandidates[0].value) {\n // Validate the key is the identifying locale property\n continue;\n }\n const keyParentProperty = keyCandidates[0].parentProperty;\n if (keyParentProperty === null) {\n logger.error(\n `Source item at path: ${sourceObjectPointer} has a root-level key match with path: ${localeKeyJsonPath}`\n );\n return exitSync(1);\n }\n // Map the index to the source item\n matchingItems[`/${index}`] = {\n sourceItem: item,\n keyParentProperty,\n keyPointer: keyCandidates[0].pointer,\n index,\n };\n }\n return matchingItems;\n}\n\nexport function findMatchingItemObject(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions,\n sourceObjectValue: JSONObject\n): { sourceItem: JSONValue | undefined; keyParentProperty: string } {\n const { identifyingLocaleProperty } = getSourceObjectOptionsObject(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n\n // Locate the source item\n if (sourceObjectValue[identifyingLocaleProperty]) {\n return {\n sourceItem: sourceObjectValue[identifyingLocaleProperty],\n keyParentProperty: identifyingLocaleProperty,\n };\n }\n return {\n sourceItem: undefined,\n keyParentProperty: identifyingLocaleProperty,\n };\n}\n\n/**\n * Get the identifying locale property for an object\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property\n */\nexport function getIdentifyingLocaleProperty(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): string {\n // Validate localeProperty\n const localeProperty = sourceObjectOptions.localeProperty || 'code';\n const identifyingLocaleProperty =\n getConfiguredLocaleProperties(locale)[\n localeProperty as keyof LocaleProperties\n ];\n if (!identifyingLocaleProperty) {\n logger.error(\n `Source object options localeProperty is not a valid locale property at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return identifyingLocaleProperty;\n}\n\n/**\n * Get the identifying locale property and the json path to the key for an array\n * @param locale - The locale to get the identifying locale property for\n * @param sourceObjectPointer - The path to the source object\n * @param sourceObjectOptions - The source object options\n * @returns The identifying locale property and the json path to the key\n */\nexport function getSourceObjectOptionsArray(\n locale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string; localeKeyJsonPath: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n locale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const localeKeyJsonPath = sourceObjectOptions.key;\n if (!localeKeyJsonPath) {\n logger.error(\n `Source object options key is required for array at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty, localeKeyJsonPath };\n}\n\nexport function getSourceObjectOptionsObject(\n defaultLocale: string,\n sourceObjectPointer: string,\n sourceObjectOptions: SourceObjectOptions\n): { identifyingLocaleProperty: string } {\n const identifyingLocaleProperty = getIdentifyingLocaleProperty(\n defaultLocale,\n sourceObjectPointer,\n sourceObjectOptions\n );\n const jsonPathKey = sourceObjectOptions.key;\n if (jsonPathKey) {\n logger.error(\n `Source object options key is not allowed for object at path: ${sourceObjectPointer}`\n );\n return exitSync(1);\n }\n return { identifyingLocaleProperty };\n}\n\n/**\n * Generate a mapping of sourceObjectPointer to SourceObjectOptions\n * where the sourceObjectPointer is a jsonpointer to the array or object containing\n * @param jsonSchema - The json schema to generate the mapping from\n * @param originalJson - The original json to generate the mapping from\n * @returns A mapping of sourceObjectPointer to SourceObjectOptions\n */\nexport function generateSourceObjectPointers(\n jsonSchema: {\n [sourceObjectPath: string]: SourceObjectOptions;\n },\n originalJson: JSONValue\n): SourceObjectPointerMap {\n const sourceObjectPointers = Object.entries(jsonSchema).reduce(\n (acc: SourceObjectPointerMap, [sourceObjectPath, sourceObjectOptions]) => {\n const sourceObjects = flattenJson(originalJson, [sourceObjectPath]);\n Object.entries(sourceObjects).forEach(([pointer, value]) => {\n acc[pointer as string] = {\n sourceObjectValue: value,\n sourceObjectOptions,\n };\n });\n return acc;\n },\n {}\n );\n return sourceObjectPointers;\n}\n\n/**\n * Validate the json schema for composite or include schemas\n * @param options - Additional options containing jsonSchema config\n * @param filePath - The path to the file (used for matching jsonSchema)\n * @returns The json schema, or null if no schema is found\n * @returns exitSync(1) if the json schema is invalid\n */\nexport function validateJsonSchema(\n options: AdditionalOptions,\n filePath: string\n): JsonSchema | null {\n if (!options.jsonSchema) {\n return null;\n }\n\n const fileGlobs = Object.keys(options.jsonSchema);\n const matchingGlob = fileGlobs.find((fileGlob) =>\n isMatch(path.relative(process.cwd(), filePath), fileGlob)\n );\n if (!matchingGlob || !options.jsonSchema[matchingGlob]) {\n return null;\n }\n // Validate includes or composite\n const jsonSchema = options.jsonSchema[matchingGlob];\n if (jsonSchema.include && jsonSchema.composite) {\n logger.error(\n 'include and composite cannot be used together in the same JSON schema'\n );\n return exitSync(1);\n }\n\n if (!jsonSchema.include && !jsonSchema.composite) {\n logger.error('No include or composite property found in JSON schema');\n return exitSync(1);\n }\n\n if (jsonSchema.structuralTransform && !jsonSchema.composite) {\n logger.error(\n 'structuralTransform requires composite to be defined in the JSON schema'\n );\n return exitSync(1);\n }\n return jsonSchema;\n}\n\nconst UNSUPPORTED_MINTLIFY_FIELDS = ['$ref'];\n\n/**\n * Recursively traverse a JSON value and collect all objects whose key\n * matches one of the unsupported field names.\n */\nfunction findMintlifyUnsupportedFields(\n value: JSONValue,\n fieldNames: string[],\n pointer: string = ''\n): { pointer: string; field: string; fieldValue: string }[] {\n if (value === null || typeof value !== 'object') return [];\n if (Array.isArray(value)) {\n const results: { pointer: string; field: string; fieldValue: string }[] =\n [];\n for (let i = 0; i < value.length; i++) {\n results.push(\n ...findMintlifyUnsupportedFields(\n value[i],\n fieldNames,\n `${pointer}/${i}`\n )\n );\n }\n return results;\n }\n // Check if this object contains an unsupported field\n const objectValue = value as JSONObject;\n for (const field of fieldNames) {\n if (typeof objectValue[field] === 'string') {\n return [{ pointer, field, fieldValue: objectValue[field] }];\n }\n }\n // Recurse into child properties\n const results: { pointer: string; field: string; fieldValue: string }[] = [];\n for (const key of Object.keys(objectValue)) {\n results.push(\n ...findMintlifyUnsupportedFields(\n objectValue[key],\n fieldNames,\n `${pointer}/${key}`\n )\n );\n }\n return results;\n}\n\n/**\n * Detect unsupported fields (e.g. $ref) in Mintlify docs.json files.\n * Logs a warning listing the fields found.\n */\nexport function detectMintlifyUnsupportedFields(\n json: JSONValue,\n filePath: string\n): void {\n const unsupported = findMintlifyUnsupportedFields(\n json,\n UNSUPPORTED_MINTLIFY_FIELDS\n );\n\n if (unsupported.length > 0) {\n const fileName = path.basename(filePath);\n const lines = unsupported\n .map(\n (u) =>\n chalk.yellow('• ') +\n chalk.white(\n `${u.pointer.replace(/\\//g, '.').replace(/^\\./, '')}.${u.field}`\n )\n )\n .join('\\n');\n logger.warn(\n chalk.yellow(\n `Mintlify config splitting is not yet supported. The following \\`$ref\\` fields were detected in \\`${fileName}\\` and will not be resolved:\\n`\n ) + lines\n );\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,25 @@
1
1
  import type { LocaleProperties } from '@generaltranslation/format/types';
2
+ /**
3
+ * Locale properties for a locale as it is written in the user's `locales` config.
4
+ *
5
+ * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a
6
+ * tag, so "fr-ca" becomes "fr-CA" and "ja-jp" becomes "ja-JP". That is correct
7
+ * when talking to the API, but it is wrong for anything that names a file, a
8
+ * directory, a URL segment, or a locale key on disk: those must use the locale
9
+ * exactly as the user configured it, because that is the spelling every other
10
+ * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,
11
+ * the string form of `transform` substitutes `[locale]` verbatim, and
12
+ * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder
13
+ * substitution canonicalizes while those do not, a project that configures a
14
+ * non-canonical tag gets content written to one directory and links pointing
15
+ * at another.
16
+ *
17
+ * Locales that are already canonical (the common case) are unaffected: for
18
+ * those, `code` and the configured string are identical.
19
+ *
20
+ * Callers that genuinely want the canonical tag should use
21
+ * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such
22
+ * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.
23
+ */
24
+ export declare function getConfiguredLocaleProperties(locale: string): LocaleProperties;
2
25
  export declare function replaceLocalePlaceholders(string: string, localeProperties: LocaleProperties): string;
@@ -1,4 +1,33 @@
1
+ import { getLocaleProperties } from "@generaltranslation/format";
1
2
  //#region src/formats/utils.ts
3
+ /**
4
+ * Locale properties for a locale as it is written in the user's `locales` config.
5
+ *
6
+ * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a
7
+ * tag, so "fr-ca" becomes "fr-CA" and "ja-jp" becomes "ja-JP". That is correct
8
+ * when talking to the API, but it is wrong for anything that names a file, a
9
+ * directory, a URL segment, or a locale key on disk: those must use the locale
10
+ * exactly as the user configured it, because that is the spelling every other
11
+ * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,
12
+ * the string form of `transform` substitutes `[locale]` verbatim, and
13
+ * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder
14
+ * substitution canonicalizes while those do not, a project that configures a
15
+ * non-canonical tag gets content written to one directory and links pointing
16
+ * at another.
17
+ *
18
+ * Locales that are already canonical (the common case) are unaffected: for
19
+ * those, `code` and the configured string are identical.
20
+ *
21
+ * Callers that genuinely want the canonical tag should use
22
+ * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such
23
+ * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.
24
+ */
25
+ function getConfiguredLocaleProperties(locale) {
26
+ return {
27
+ ...getLocaleProperties(locale),
28
+ code: locale
29
+ };
30
+ }
2
31
  function replaceLocalePlaceholders(string, localeProperties) {
3
32
  return string.replace(/\{(\w+)\}/g, (match, property) => {
4
33
  if (property === "locale" || property === "localeCode") return localeProperties.code;
@@ -9,6 +38,6 @@ function replaceLocalePlaceholders(string, localeProperties) {
9
38
  });
10
39
  }
11
40
  //#endregion
12
- export { replaceLocalePlaceholders };
41
+ export { getConfiguredLocaleProperties, replaceLocalePlaceholders };
13
42
 
14
43
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/formats/utils.ts"],"sourcesContent":["import type { LocaleProperties } from '@generaltranslation/format/types';\n\n// helper function to replace locale placeholders in a string\n// with the corresponding locale properties\n// ex: {locale} -> will be replaced with the locale code\n// ex: {localeName} -> will be replaced with the locale name\nexport function replaceLocalePlaceholders(\n string: string,\n localeProperties: LocaleProperties\n): string {\n return string.replace(/\\{(\\w+)\\}/g, (match, property) => {\n // Handle common aliases\n if (property === 'locale' || property === 'localeCode') {\n return localeProperties.code;\n }\n if (property === 'localeName') {\n return localeProperties.name;\n }\n if (property === 'localeNativeName') {\n return localeProperties.nativeName;\n }\n // Check if the property exists in localeProperties\n if (property in localeProperties) {\n return localeProperties[property as keyof typeof localeProperties];\n }\n // Return the original placeholder if property not found\n return match;\n });\n}\n"],"mappings":";AAMA,SAAgB,0BACd,QACA,kBACQ;AACR,QAAO,OAAO,QAAQ,eAAe,OAAO,aAAa;AAEvD,MAAI,aAAa,YAAY,aAAa,aACxC,QAAO,iBAAiB;AAE1B,MAAI,aAAa,aACf,QAAO,iBAAiB;AAE1B,MAAI,aAAa,mBACf,QAAO,iBAAiB;AAG1B,MAAI,YAAY,iBACd,QAAO,iBAAiB;AAG1B,SAAO;GACP"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/formats/utils.ts"],"sourcesContent":["import { getLocaleProperties } from '@generaltranslation/format';\nimport type { LocaleProperties } from '@generaltranslation/format/types';\n\n/**\n * Locale properties for a locale as it is written in the user's `locales` config.\n *\n * `getLocaleProperties(locale).code` returns the *canonical* BCP-47 form of a\n * tag, so \"fr-ca\" becomes \"fr-CA\" and \"ja-jp\" becomes \"ja-JP\". That is correct\n * when talking to the API, but it is wrong for anything that names a file, a\n * directory, a URL segment, or a locale key on disk: those must use the locale\n * exactly as the user configured it, because that is the spelling every other\n * part of the CLI uses. `resolveLocaleFiles` substitutes `[locale]` verbatim,\n * the string form of `transform` substitutes `[locale]` verbatim, and\n * `localizeStaticUrls` splices the raw locale into URL paths. If placeholder\n * substitution canonicalizes while those do not, a project that configures a\n * non-canonical tag gets content written to one directory and links pointing\n * at another.\n *\n * Locales that are already canonical (the common case) are unaffected: for\n * those, `code` and the configured string are identical.\n *\n * Callers that genuinely want the canonical tag should use\n * `gt.resolveCanonicalLocale`, or one of the explicitly-named properties such\n * as `{minimizedCode}` / `{maximizedCode}` / `{regionCode}`.\n */\nexport function getConfiguredLocaleProperties(\n locale: string\n): LocaleProperties {\n return { ...getLocaleProperties(locale), code: locale };\n}\n\n// helper function to replace locale placeholders in a string\n// with the corresponding locale properties\n// ex: {locale} -> will be replaced with the locale code\n// ex: {localeName} -> will be replaced with the locale name\nexport function replaceLocalePlaceholders(\n string: string,\n localeProperties: LocaleProperties\n): string {\n return string.replace(/\\{(\\w+)\\}/g, (match, property) => {\n // Handle common aliases\n if (property === 'locale' || property === 'localeCode') {\n return localeProperties.code;\n }\n if (property === 'localeName') {\n return localeProperties.name;\n }\n if (property === 'localeNativeName') {\n return localeProperties.nativeName;\n }\n // Check if the property exists in localeProperties\n if (property in localeProperties) {\n return localeProperties[property as keyof typeof localeProperties];\n }\n // Return the original placeholder if property not found\n return match;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,8BACd,QACkB;AAClB,QAAO;EAAE,GAAG,oBAAoB,OAAO;EAAE,MAAM;EAAQ;;AAOzD,SAAgB,0BACd,QACA,kBACQ;AACR,QAAO,OAAO,QAAQ,eAAe,OAAO,aAAa;AAEvD,MAAI,aAAa,YAAY,aAAa,aACxC,QAAO,iBAAiB;AAE1B,MAAI,aAAa,aACf,QAAO,iBAAiB;AAE1B,MAAI,aAAa,mBACf,QAAO,iBAAiB;AAG1B,MAAI,YAAY,iBACd,QAAO,iBAAiB;AAG1B,SAAO;GACP"}
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.16.2";
1
+ export declare const PACKAGE_VERSION = "2.16.3";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.16.2";
2
+ const PACKAGE_VERSION = "2.16.3";
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.16.2';\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.16.3';\n"],"mappings":";AACA,MAAa,kBAAkB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.16.2",
3
+ "version": "2.16.3",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [
@@ -117,10 +117,10 @@
117
117
  "yaml": "^2.8.0",
118
118
  "@generaltranslation/icu": "0.1.1",
119
119
  "@generaltranslation/format": "0.1.4",
120
+ "@generaltranslation/supported-locales": "2.1.14",
120
121
  "@generaltranslation/python-extractor": "0.2.34",
121
122
  "generaltranslation": "9.1.1",
122
- "gt-remark": "1.0.11",
123
- "@generaltranslation/supported-locales": "2.1.14"
123
+ "gt-remark": "1.0.11"
124
124
  },
125
125
  "devDependencies": {
126
126
  "@types/babel__generator": "^7.27.0",