gt 2.20.2 → 2.20.4

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,17 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.20.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2262](https://github.com/generaltranslation/gt/pull/2262) [`c2c9048`](https://github.com/generaltranslation/gt/commit/c2c9048ffbe98b7aa77b0dee034e5e324bbb6395) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - fix(cli): with `experimentalAddHeaderAnchorIds: 'mintlify'`, write Mintlify's native `{#id}` on every translated heading, using the source heading's ID so anchors match across locales and the heading hover link points at the same target. Mintlify only reads `{#id}` on headings indented up to three spaces, and the MDX serializer indents JSX children two spaces per level, so headings nested in JSX are moved back to the margin. Default mode is unchanged.
8
+
9
+ ## 2.20.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [#2260](https://github.com/generaltranslation/gt/pull/2260) [`8a3c49c`](https://github.com/generaltranslation/gt/commit/8a3c49cbf48b09d1687eb3e5202bd12c4221d3ad) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - `gt translate` no longer saves local edits to translated output by default. Saving local edits is now opt-in via `--save-local` or `options.saveLocal: true` in `gt.config.json`, so running the CLI locally does not overwrite production translations.
14
+
3
15
  ## 2.20.2
4
16
 
5
17
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","names":[],"sources":["../../../src/cli/commands/translate.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport { TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport {\n FileTranslationData,\n runDownloadWorkflow,\n} from '../../workflows/download.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport copyFile from '../../fs/copyFile.js';\nimport flattenJsonFiles from '../../utils/flattenJsonFiles.js';\nimport localizeStaticUrls from '../../utils/localizeStaticUrls.js';\nimport localizeRelativeAssets from '../../utils/localizeRelativeAssets.js';\nimport processAnchorIds from '../../utils/processAnchorIds.js';\nimport localizeStaticImports from '../../utils/localizeStaticImports.js';\nimport { postprocessMintlify } from '../../formats/files/postprocess/mintlify.js';\nimport { BranchData } from '../../types/branch.js';\nimport { getDownloadedMeta } from '../../state/recentDownloads.js';\nimport { persistPostProcessHashes } from '../../utils/persistPostprocessHashes.js';\nimport { runPublishWorkflow } from '../../workflows/publish.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../../formats/files/transformFormat.js';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport type { InlineLibrary } from '../../types/libraries.js';\n\n// Downloads translations that were completed\nexport async function handleTranslate(\n options: TranslateFlags,\n settings: Settings,\n fileVersionData: FileTranslationData | undefined,\n jobData: EnqueueFilesResult | undefined,\n branchData: BranchData | undefined,\n publishMap?: Map<string, boolean>,\n inlineLibrary?: InlineLibrary\n) {\n if (fileVersionData) {\n const {\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n } = settings.files;\n\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: jobData,\n branchData: branchData,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale]?.[sourcePath] ?? null,\n options: settings,\n inlineLibrary,\n forceRetranslation: options.force,\n forceDownload: options.forceDownload || options.force, // if force is true should also force download\n });\n\n // Publish/unpublish files after translations are downloaded\n if (publishMap && branchData?.currentBranch.id) {\n const files = Object.entries(fileVersionData).map(([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n }));\n await runPublishWorkflow(\n files,\n publishMap,\n branchData.currentBranch.id,\n settings\n );\n }\n }\n}\n\nexport async function postProcessTranslations(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const postProcessIncludes = filterPostProcessIncludesForFormatTransforms(\n settings,\n includeFiles\n );\n if (includeFiles && postProcessIncludes?.size === 0) return;\n\n await postprocessMintlify(settings, postProcessIncludes);\n\n // Localize static urls (/docs -> /[locale]/docs) and preserve anchor IDs for non-default locales\n // Default locale is processed earlier in the flow in base.ts\n if (settings.options?.experimentalLocalizeStaticUrls) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeStaticUrls(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Rewrite relative asset URLs in translated md/mdx files\n if (settings.options?.experimentalLocalizeRelativeAssets) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeRelativeAssets(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Localize static imports (import Snippet from /snippets/file.mdx -> import Snippet from /snippets/[locale]/file.mdx)\n if (settings.options?.experimentalLocalizeStaticImports) {\n await localizeStaticImports(settings, postProcessIncludes);\n }\n\n const shouldProcessAnchorIds =\n settings.options?.experimentalLocalizeStaticUrls ||\n settings.options?.experimentalAddHeaderAnchorIds;\n\n // Add explicit anchor IDs to translated MDX/MD files to preserve navigation.\n // Uses inline {#id} format by default, or div wrapping if experimentalAddHeaderAnchorIds is 'mintlify'.\n //\n // Runs last of the md/mdx passes: the others re-stringify the document, which\n // re-indents headings nested in JSX and would otherwise move them out from\n // under the anchors placed here.\n if (shouldProcessAnchorIds) {\n await processAnchorIds(settings, postProcessIncludes);\n }\n\n // Flatten json files into a single file\n if (settings.options?.experimentalFlattenJsonFiles) {\n await flattenJsonFiles(settings, postProcessIncludes);\n }\n\n // Copy files to the target locale\n if (settings.options?.copyFiles) {\n await copyFile(settings);\n }\n\n // Record postprocessed content hashes for newly downloaded files\n persistPostProcessHashes(settings, postProcessIncludes, getDownloadedMeta());\n}\n\n/**\n * Exclude only outputs whose source file was translated into a different format.\n * @param settings - The settings for the project\n * @param includeFiles - The files to include in the post-processing\n * @returns The files to exclude in the post-processing\n */\nfunction filterPostProcessIncludesForFormatTransforms(\n settings: Settings,\n includeFiles?: Set<string>\n): Set<string> | undefined {\n if (!includeFiles) return includeFiles;\n\n const transformedSourcePaths = new Set<string>();\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(settings, fileType)) continue;\n\n for (const sourcePath of settings.files.resolvedPaths[fileType] || []) {\n transformedSourcePaths.add(getRelative(sourcePath));\n }\n }\n if (transformedSourcePaths.size === 0) return includeFiles;\n\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n const transformedOutputPaths = new Set<string>();\n for (const localeMapping of Object.values(fileMapping)) {\n for (const [sourcePath, outputPath] of Object.entries(localeMapping)) {\n if (transformedSourcePaths.has(sourcePath)) {\n transformedOutputPaths.add(outputPath);\n }\n }\n }\n\n return new Set(\n [...includeFiles].filter(\n (filePath) => !transformedOutputPaths.has(filePath)\n )\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,eAAsB,gBACpB,SACA,UACA,iBACA,SACA,YACA,YACA,eACA;AACA,KAAI,iBAAiB;EACnB,MAAM,EACJ,eACA,kBACA,gBACA,qBACE,SAAS;EAEb,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;AAED,QAAM,oBAAoB;GACP;GACR;GACG;GACZ,SAAS,SAAS;GAClB,iBAAiB,QAAQ;GACzB,oBAAoB,YAAY,WAC9B,YAAY,UAAU,eAAe;GACvC,SAAS;GACT;GACA,oBAAoB,QAAQ;GAC5B,eAAe,QAAQ,iBAAiB,QAAQ;GACjD,CAAC;AAGF,MAAI,cAAc,YAAY,cAAc,GAM1C,OAAM,mBALQ,OAAO,QAAQ,gBAAgB,CAAC,KAAK,CAAC,QAAQ,WAAW;GACrE;GACA,WAAW,KAAK;GAChB,UAAU,KAAK;GAChB,EAEM,EACL,YACA,WAAW,cAAc,IACzB,SACD;;;AAKP,eAAsB,wBACpB,UACA,cACA;CACA,MAAM,sBAAsB,6CAC1B,UACA,aACD;AACD,KAAI,gBAAgB,qBAAqB,SAAS,EAAG;AAErD,OAAM,oBAAoB,UAAU,oBAAoB;AAIxD,KAAI,SAAS,SAAS,gCAAgC;EACpD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,mBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,oCAAoC;EACxD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,uBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,kCACpB,OAAM,sBAAsB,UAAU,oBAAoB;AAa5D,KATE,SAAS,SAAS,kCAClB,SAAS,SAAS,+BASlB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,6BACpB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,UACpB,OAAM,SAAS,SAAS;AAI1B,0BAAyB,UAAU,qBAAqB,mBAAmB,CAAC;;;;;;;;AAS9E,SAAS,6CACP,UACA,cACyB;AACzB,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,YAAY,2BAA2B;AAChD,MAAI,CAAC,yCAAyC,UAAU,SAAS,CAAE;AAEnE,OAAK,MAAM,cAAc,SAAS,MAAM,cAAc,aAAa,EAAE,CACnE,wBAAuB,IAAI,YAAY,WAAW,CAAC;;AAGvD,KAAI,uBAAuB,SAAS,EAAG,QAAO;CAE9C,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAED,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,iBAAiB,OAAO,OAAO,YAAY,CACpD,MAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,cAAc,CAClE,KAAI,uBAAuB,IAAI,WAAW,CACxC,wBAAuB,IAAI,WAAW;AAK5C,QAAO,IAAI,IACT,CAAC,GAAG,aAAa,CAAC,QACf,aAAa,CAAC,uBAAuB,IAAI,SAAS,CACpD,CACF"}
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../src/cli/commands/translate.ts"],"sourcesContent":["import { EnqueueFilesResult } from 'generaltranslation/types';\nimport { TranslateFlags } from '../../types/index.js';\nimport { Settings } from '../../types/index.js';\nimport {\n FileTranslationData,\n runDownloadWorkflow,\n} from '../../workflows/download.js';\nimport { createFileMapping } from '../../formats/files/fileMapping.js';\nimport copyFile from '../../fs/copyFile.js';\nimport flattenJsonFiles from '../../utils/flattenJsonFiles.js';\nimport localizeStaticUrls from '../../utils/localizeStaticUrls.js';\nimport localizeRelativeAssets from '../../utils/localizeRelativeAssets.js';\nimport processAnchorIds from '../../utils/processAnchorIds.js';\nimport localizeStaticImports from '../../utils/localizeStaticImports.js';\nimport { postprocessMintlify } from '../../formats/files/postprocess/mintlify.js';\nimport { BranchData } from '../../types/branch.js';\nimport { getDownloadedMeta } from '../../state/recentDownloads.js';\nimport { persistPostProcessHashes } from '../../utils/persistPostprocessHashes.js';\nimport { runPublishWorkflow } from '../../workflows/publish.js';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { hasNonIdentityFileFormatTransformForType } from '../../formats/files/transformFormat.js';\nimport { getRelative } from '../../fs/findFilepath.js';\nimport type { InlineLibrary } from '../../types/libraries.js';\n\n// Downloads translations that were completed\nexport async function handleTranslate(\n options: TranslateFlags,\n settings: Settings,\n fileVersionData: FileTranslationData | undefined,\n jobData: EnqueueFilesResult | undefined,\n branchData: BranchData | undefined,\n publishMap?: Map<string, boolean>,\n inlineLibrary?: InlineLibrary\n) {\n if (fileVersionData) {\n const {\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n } = settings.files;\n\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n // Check for remaining translations\n await runDownloadWorkflow({\n fileVersionData: fileVersionData,\n jobData: jobData,\n branchData: branchData,\n locales: settings.locales,\n timeoutDuration: options.timeout,\n resolveOutputPath: (sourcePath, locale) =>\n fileMapping[locale]?.[sourcePath] ?? null,\n options: settings,\n inlineLibrary,\n forceRetranslation: options.force,\n forceDownload: options.forceDownload || options.force, // if force is true should also force download\n });\n\n // Publish/unpublish files after translations are downloaded\n if (publishMap && branchData?.currentBranch.id) {\n const files = Object.entries(fileVersionData).map(([fileId, data]) => ({\n fileId,\n versionId: data.versionId,\n fileName: data.fileName,\n }));\n await runPublishWorkflow(\n files,\n publishMap,\n branchData.currentBranch.id,\n settings\n );\n }\n }\n}\n\nexport async function postProcessTranslations(\n settings: Settings,\n includeFiles?: Set<string>\n) {\n const postProcessIncludes = filterPostProcessIncludesForFormatTransforms(\n settings,\n includeFiles\n );\n if (includeFiles && postProcessIncludes?.size === 0) return;\n\n await postprocessMintlify(settings, postProcessIncludes);\n\n // Localize static urls (/docs -> /[locale]/docs) and preserve anchor IDs for non-default locales\n // Default locale is processed earlier in the flow in base.ts\n if (settings.options?.experimentalLocalizeStaticUrls) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeStaticUrls(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Rewrite relative asset URLs in translated md/mdx files\n if (settings.options?.experimentalLocalizeRelativeAssets) {\n const nonDefaultLocales = settings.locales.filter(\n (locale) => locale !== settings.defaultLocale\n );\n if (nonDefaultLocales.length > 0) {\n await localizeRelativeAssets(\n settings,\n nonDefaultLocales,\n postProcessIncludes\n );\n }\n }\n\n // Localize static imports (import Snippet from /snippets/file.mdx -> import Snippet from /snippets/[locale]/file.mdx)\n if (settings.options?.experimentalLocalizeStaticImports) {\n await localizeStaticImports(settings, postProcessIncludes);\n }\n\n const shouldProcessAnchorIds =\n settings.options?.experimentalLocalizeStaticUrls ||\n settings.options?.experimentalAddHeaderAnchorIds;\n\n // Add explicit anchor IDs to translated MDX/MD files to preserve navigation.\n // Uses escaped inline \\{#id\\} by default, or Mintlify's native {#id} if\n // experimentalAddHeaderAnchorIds is 'mintlify'.\n //\n // Runs last of the md/mdx passes: the others re-stringify the document, which\n // re-indents headings nested in JSX and would otherwise move them out from\n // under the anchors placed here.\n if (shouldProcessAnchorIds) {\n await processAnchorIds(settings, postProcessIncludes);\n }\n\n // Flatten json files into a single file\n if (settings.options?.experimentalFlattenJsonFiles) {\n await flattenJsonFiles(settings, postProcessIncludes);\n }\n\n // Copy files to the target locale\n if (settings.options?.copyFiles) {\n await copyFile(settings);\n }\n\n // Record postprocessed content hashes for newly downloaded files\n persistPostProcessHashes(settings, postProcessIncludes, getDownloadedMeta());\n}\n\n/**\n * Exclude only outputs whose source file was translated into a different format.\n * @param settings - The settings for the project\n * @param includeFiles - The files to include in the post-processing\n * @returns The files to exclude in the post-processing\n */\nfunction filterPostProcessIncludesForFormatTransforms(\n settings: Settings,\n includeFiles?: Set<string>\n): Set<string> | undefined {\n if (!includeFiles) return includeFiles;\n\n const transformedSourcePaths = new Set<string>();\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (!hasNonIdentityFileFormatTransformForType(settings, fileType)) continue;\n\n for (const sourcePath of settings.files.resolvedPaths[fileType] || []) {\n transformedSourcePaths.add(getRelative(sourcePath));\n }\n }\n if (transformedSourcePaths.size === 0) return includeFiles;\n\n const { resolvedPaths, placeholderPaths, transformPaths, transformFormats } =\n settings.files;\n const fileMapping = createFileMapping(\n resolvedPaths,\n placeholderPaths,\n transformPaths,\n transformFormats,\n settings.locales,\n settings.defaultLocale\n );\n\n const transformedOutputPaths = new Set<string>();\n for (const localeMapping of Object.values(fileMapping)) {\n for (const [sourcePath, outputPath] of Object.entries(localeMapping)) {\n if (transformedSourcePaths.has(sourcePath)) {\n transformedOutputPaths.add(outputPath);\n }\n }\n }\n\n return new Set(\n [...includeFiles].filter(\n (filePath) => !transformedOutputPaths.has(filePath)\n )\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,eAAsB,gBACpB,SACA,UACA,iBACA,SACA,YACA,YACA,eACA;AACA,KAAI,iBAAiB;EACnB,MAAM,EACJ,eACA,kBACA,gBACA,qBACE,SAAS;EAEb,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;AAED,QAAM,oBAAoB;GACP;GACR;GACG;GACZ,SAAS,SAAS;GAClB,iBAAiB,QAAQ;GACzB,oBAAoB,YAAY,WAC9B,YAAY,UAAU,eAAe;GACvC,SAAS;GACT;GACA,oBAAoB,QAAQ;GAC5B,eAAe,QAAQ,iBAAiB,QAAQ;GACjD,CAAC;AAGF,MAAI,cAAc,YAAY,cAAc,GAM1C,OAAM,mBALQ,OAAO,QAAQ,gBAAgB,CAAC,KAAK,CAAC,QAAQ,WAAW;GACrE;GACA,WAAW,KAAK;GAChB,UAAU,KAAK;GAChB,EAEM,EACL,YACA,WAAW,cAAc,IACzB,SACD;;;AAKP,eAAsB,wBACpB,UACA,cACA;CACA,MAAM,sBAAsB,6CAC1B,UACA,aACD;AACD,KAAI,gBAAgB,qBAAqB,SAAS,EAAG;AAErD,OAAM,oBAAoB,UAAU,oBAAoB;AAIxD,KAAI,SAAS,SAAS,gCAAgC;EACpD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,mBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,oCAAoC;EACxD,MAAM,oBAAoB,SAAS,QAAQ,QACxC,WAAW,WAAW,SAAS,cACjC;AACD,MAAI,kBAAkB,SAAS,EAC7B,OAAM,uBACJ,UACA,mBACA,oBACD;;AAKL,KAAI,SAAS,SAAS,kCACpB,OAAM,sBAAsB,UAAU,oBAAoB;AAc5D,KAVE,SAAS,SAAS,kCAClB,SAAS,SAAS,+BAUlB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,6BACpB,OAAM,iBAAiB,UAAU,oBAAoB;AAIvD,KAAI,SAAS,SAAS,UACpB,OAAM,SAAS,SAAS;AAI1B,0BAAyB,UAAU,qBAAqB,mBAAmB,CAAC;;;;;;;;AAS9E,SAAS,6CACP,UACA,cACyB;AACzB,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,YAAY,2BAA2B;AAChD,MAAI,CAAC,yCAAyC,UAAU,SAAS,CAAE;AAEnE,OAAK,MAAM,cAAc,SAAS,MAAM,cAAc,aAAa,EAAE,CACnE,wBAAuB,IAAI,YAAY,WAAW,CAAC;;AAGvD,KAAI,uBAAuB,SAAS,EAAG,QAAO;CAE9C,MAAM,EAAE,eAAe,kBAAkB,gBAAgB,qBACvD,SAAS;CACX,MAAM,cAAc,kBAClB,eACA,kBACA,gBACA,kBACA,SAAS,SACT,SAAS,cACV;CAED,MAAM,yCAAyB,IAAI,KAAa;AAChD,MAAK,MAAM,iBAAiB,OAAO,OAAO,YAAY,CACpD,MAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,cAAc,CAClE,KAAI,uBAAuB,IAAI,WAAW,CACxC,wBAAuB,IAAI,WAAW;AAK5C,QAAO,IAAI,IACT,CAAC,GAAG,aAAa,CAAC,QACf,aAAa,CAAC,uBAAuB,IAAI,SAAS,CACpD,CACF"}
package/dist/cli/flags.js CHANGED
@@ -12,7 +12,7 @@ function attachTranslateFlags(command) {
12
12
  if (isNaN(parsedValue)) throw new Error("Invalid timeout: not a number.");
13
13
  if (parsedValue < 0) throw new Error("Invalid timeout: must be a positive number.");
14
14
  return parsedValue;
15
- }, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations (default: true; configurable via options.saveLocal in gt.config.json)").option("--no-save-local", "Skip detecting and saving local edits before enqueuing translations").option("--publish", "Publish translations to the CDN", false).option("--experimental-localize-static-urls", "Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.", false).option("--experimental-hide-default-locale", "When localizing static locales, hide the default locale from the path", false).option("--experimental-flatten-json-files", "Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.", false).option("--experimental-localize-static-imports", "Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.", false).option("--experimental-localize-relative-assets", "Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.", false).option("--force", "Force a retranslation, invalidating all existing cached translations if they exist.", false).option("--force-download", "Force download and overwrite local files, bypassing gt-lock.json checks.", false).option("--omit-config-ids", "Do not write _versionId or _branchId to gt.config.json").option("--experimental-clear-locale-dirs", "Clear locale directories before downloading new translations", false).option("--branch <branch>", "Specify a custom branch to use for translations").option("--disable-branch-detection", "Disable additional branch detection and optimizations and use the manually specified branch", false).option("--enable-branching", "Enable branching for the project").option("--remote-name <name>", "Specify a custom remote name to use for branch detection", DEFAULT_GIT_REMOTE_NAME).option("--tag [value]", "Tag this translation run (auto-resolves from git if no value provided)").option("-m, --message <message>", "Message to attach to the translation tag");
15
+ }, DEFAULT_TIMEOUT).option("--save-local", "Detect and save local edits before enqueuing translations (default: false; configurable via options.saveLocal in gt.config.json)").option("--no-save-local", "Skip detecting and saving local edits before enqueuing translations").option("--publish", "Publish translations to the CDN", false).option("--experimental-localize-static-urls", "Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.", false).option("--experimental-hide-default-locale", "When localizing static locales, hide the default locale from the path", false).option("--experimental-flatten-json-files", "Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.", false).option("--experimental-localize-static-imports", "Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.", false).option("--experimental-localize-relative-assets", "Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.", false).option("--force", "Force a retranslation, invalidating all existing cached translations if they exist.", false).option("--force-download", "Force download and overwrite local files, bypassing gt-lock.json checks.", false).option("--omit-config-ids", "Do not write _versionId or _branchId to gt.config.json").option("--experimental-clear-locale-dirs", "Clear locale directories before downloading new translations", false).option("--branch <branch>", "Specify a custom branch to use for translations").option("--disable-branch-detection", "Disable additional branch detection and optimizations and use the manually specified branch", false).option("--enable-branching", "Enable branching for the project").option("--remote-name <name>", "Specify a custom remote name to use for branch detection", DEFAULT_GIT_REMOTE_NAME).option("--tag [value]", "Tag this translation run (auto-resolves from git if no value provided)").option("-m, --message <message>", "Message to attach to the translation tag");
16
16
  return command;
17
17
  }
18
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"flags.js","names":[],"sources":["../../src/cli/flags.ts"],"sourcesContent":["import { Command } from 'commander';\nimport findFilepath from '../fs/findFilepath.js';\nimport { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';\n\nconst DEFAULT_TIMEOUT = 900;\n\nexport function attachSharedFlags(command: Command) {\n command\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--api-key <key>', 'API key for General Translation cloud service')\n .option('--project-id <id>', 'General Translation project ID');\n return command;\n}\n\nexport function attachTranslateFlags(command: Command) {\n attachSharedFlags(command)\n .option('--version-id <id>', 'General Translation version ID')\n .option(\n '--default-language, --default-locale <locale>',\n 'Default locale (e.g., en)'\n )\n .option(\n '--new, --locales <locales...>',\n 'Space-separated list of locales (e.g., en fr es)'\n )\n .option(\n '--dry-run',\n 'Dry run, do not send updates to the General Translation API',\n false\n )\n .option(\n '--timeout <seconds>',\n 'Translation wait timeout in seconds',\n (value) => {\n const parsedValue = parseInt(value, 10);\n if (isNaN(parsedValue)) {\n throw new Error('Invalid timeout: not a number.');\n }\n if (parsedValue < 0) {\n throw new Error('Invalid timeout: must be a positive number.');\n }\n return parsedValue;\n },\n DEFAULT_TIMEOUT\n )\n .option(\n '--save-local',\n 'Detect and save local edits before enqueuing translations (default: true; configurable via options.saveLocal in gt.config.json)'\n )\n .option(\n '--no-save-local',\n 'Skip detecting and saving local edits before enqueuing translations'\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .option(\n '--experimental-localize-static-urls',\n 'Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-hide-default-locale',\n 'When localizing static locales, hide the default locale from the path',\n false\n )\n .option(\n '--experimental-flatten-json-files',\n 'Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.',\n false\n )\n .option(\n '--experimental-localize-static-imports',\n 'Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-localize-relative-assets',\n 'Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.',\n false\n )\n .option(\n '--force',\n 'Force a retranslation, invalidating all existing cached translations if they exist.',\n false\n )\n .option(\n '--force-download',\n 'Force download and overwrite local files, bypassing gt-lock.json checks.',\n false\n )\n .option(\n '--omit-config-ids',\n 'Do not write _versionId or _branchId to gt.config.json'\n )\n .option(\n '--experimental-clear-locale-dirs',\n 'Clear locale directories before downloading new translations',\n false\n )\n .option(\n '--branch <branch>',\n 'Specify a custom branch to use for translations'\n )\n .option(\n '--disable-branch-detection',\n 'Disable additional branch detection and optimizations and use the manually specified branch',\n false\n )\n .option('--enable-branching', 'Enable branching for the project')\n .option(\n '--remote-name <name>',\n 'Specify a custom remote name to use for branch detection',\n DEFAULT_GIT_REMOTE_NAME\n )\n .option(\n '--tag [value]',\n 'Tag this translation run (auto-resolves from git if no value provided)'\n )\n .option(\n '-m, --message <message>',\n 'Message to attach to the translation tag'\n );\n return command;\n}\n\n/**\n * Attaches flags necessary for parsing inline content\n * @param command - The command to attach the flags to\n * @returns The command with the inline content parsing flags attached\n */\nfunction attachInlineContentParsingFlags(\n command: Command,\n sourceHelp?: string\n) {\n return command\n .option(\n '--tsconfig, --jsconfig <path>',\n 'Path to custom jsconfig or tsconfig file',\n findFilepath(['./tsconfig.json', './jsconfig.json'])\n )\n .option('--dictionary <path>', 'Path to dictionary file')\n .option(\n '--src <paths...>',\n sourceHelp ??\n \"Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'\"\n )\n .option(\n '--inline',\n 'Include inline content in translations (e.g., inline jsx translations, inline string translations, etc.)',\n true\n );\n}\n\n/**\n * Attaches flags necessary for validating a project\n * @param command\n * @returns The command with the validate flags attached\n */\nexport function attachValidateFlags(command: Command, sourceHelp?: string) {\n return attachInlineContentParsingFlags(\n command.option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n ),\n sourceHelp\n );\n}\n\n/**\n * Attaches flags necessary for translating a project\n * @param command\n * @returns The command with the translate flags attached\n */\nexport function attachInlineTranslateFlags(\n command: Command,\n sourceHelp?: string\n) {\n return attachInlineContentParsingFlags(\n command.option(\n '--ignore-errors',\n 'Ignore errors encountered while scanning for inline content',\n false\n ),\n sourceHelp\n );\n}\n"],"mappings":";;;AAIA,MAAM,kBAAkB;AAExB,SAAgB,kBAAkB,SAAkB;AAClD,SACG,OACC,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,CACA,OAAO,mBAAmB,gDAAgD,CAC1E,OAAO,qBAAqB,iCAAiC;AAChE,QAAO;;AAGT,SAAgB,qBAAqB,SAAkB;AACrD,mBAAkB,QAAQ,CACvB,OAAO,qBAAqB,iCAAiC,CAC7D,OACC,iDACA,4BACD,CACA,OACC,iCACA,mDACD,CACA,OACC,aACA,+DACA,MACD,CACA,OACC,uBACA,wCACC,UAAU;EACT,MAAM,cAAc,SAAS,OAAO,GAAG;AACvC,MAAI,MAAM,YAAY,CACpB,OAAM,IAAI,MAAM,iCAAiC;AAEnD,MAAI,cAAc,EAChB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,SAAO;IAET,gBACD,CACA,OACC,gBACA,kIACD,CACA,OACC,mBACA,sEACD,CACA,OAAO,aAAa,mCAAmC,MAAM,CAC7D,OACC,uCACA,iJACA,MACD,CACA,OACC,sCACA,yEACA,MACD,CACA,OACC,qCACA,8HACA,MACD,CACA,OACC,0CACA,2JACA,MACD,CACA,OACC,2CACA,qGACA,MACD,CACA,OACC,WACA,uFACA,MACD,CACA,OACC,oBACA,4EACA,MACD,CACA,OACC,qBACA,yDACD,CACA,OACC,oCACA,gEACA,MACD,CACA,OACC,qBACA,kDACD,CACA,OACC,8BACA,+FACA,MACD,CACA,OAAO,sBAAsB,mCAAmC,CAChE,OACC,wBACA,4DACA,wBACD,CACA,OACC,iBACA,yEACD,CACA,OACC,2BACA,2CACD;AACH,QAAO;;;;;;;AAQT,SAAS,gCACP,SACA,YACA;AACA,QAAO,QACJ,OACC,iCACA,4CACA,aAAa,CAAC,mBAAmB,kBAAkB,CAAC,CACrD,CACA,OAAO,uBAAuB,0BAA0B,CACxD,OACC,oBACA,cACE,0MACH,CACA,OACC,YACA,4GACA,KACD;;;;;;;AAQL,SAAgB,oBAAoB,SAAkB,YAAqB;AACzE,QAAO,gCACL,QAAQ,OACN,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,EACD,WACD;;;;;;;AAQH,SAAgB,2BACd,SACA,YACA;AACA,QAAO,gCACL,QAAQ,OACN,mBACA,+DACA,MACD,EACD,WACD"}
1
+ {"version":3,"file":"flags.js","names":[],"sources":["../../src/cli/flags.ts"],"sourcesContent":["import { Command } from 'commander';\nimport findFilepath from '../fs/findFilepath.js';\nimport { DEFAULT_GIT_REMOTE_NAME } from '../utils/constants.js';\n\nconst DEFAULT_TIMEOUT = 900;\n\nexport function attachSharedFlags(command: Command) {\n command\n .option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n )\n .option('--api-key <key>', 'API key for General Translation cloud service')\n .option('--project-id <id>', 'General Translation project ID');\n return command;\n}\n\nexport function attachTranslateFlags(command: Command) {\n attachSharedFlags(command)\n .option('--version-id <id>', 'General Translation version ID')\n .option(\n '--default-language, --default-locale <locale>',\n 'Default locale (e.g., en)'\n )\n .option(\n '--new, --locales <locales...>',\n 'Space-separated list of locales (e.g., en fr es)'\n )\n .option(\n '--dry-run',\n 'Dry run, do not send updates to the General Translation API',\n false\n )\n .option(\n '--timeout <seconds>',\n 'Translation wait timeout in seconds',\n (value) => {\n const parsedValue = parseInt(value, 10);\n if (isNaN(parsedValue)) {\n throw new Error('Invalid timeout: not a number.');\n }\n if (parsedValue < 0) {\n throw new Error('Invalid timeout: must be a positive number.');\n }\n return parsedValue;\n },\n DEFAULT_TIMEOUT\n )\n .option(\n '--save-local',\n 'Detect and save local edits before enqueuing translations (default: false; configurable via options.saveLocal in gt.config.json)'\n )\n .option(\n '--no-save-local',\n 'Skip detecting and saving local edits before enqueuing translations'\n )\n .option('--publish', 'Publish translations to the CDN', false)\n .option(\n '--experimental-localize-static-urls',\n 'Triggering this will run a script after the cli tool that localizes all urls in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-hide-default-locale',\n 'When localizing static locales, hide the default locale from the path',\n false\n )\n .option(\n '--experimental-flatten-json-files',\n 'Triggering this will flatten the json files into a single file. This is useful for projects that have a lot of json files.',\n false\n )\n .option(\n '--experimental-localize-static-imports',\n 'Triggering this will run a script after the cli tool that localizes all static imports in content files. Currently only supported for md and mdx files.',\n false\n )\n .option(\n '--experimental-localize-relative-assets',\n 'Triggering this will rewrite relative image asset URLs in translated md/mdx files to valid paths.',\n false\n )\n .option(\n '--force',\n 'Force a retranslation, invalidating all existing cached translations if they exist.',\n false\n )\n .option(\n '--force-download',\n 'Force download and overwrite local files, bypassing gt-lock.json checks.',\n false\n )\n .option(\n '--omit-config-ids',\n 'Do not write _versionId or _branchId to gt.config.json'\n )\n .option(\n '--experimental-clear-locale-dirs',\n 'Clear locale directories before downloading new translations',\n false\n )\n .option(\n '--branch <branch>',\n 'Specify a custom branch to use for translations'\n )\n .option(\n '--disable-branch-detection',\n 'Disable additional branch detection and optimizations and use the manually specified branch',\n false\n )\n .option('--enable-branching', 'Enable branching for the project')\n .option(\n '--remote-name <name>',\n 'Specify a custom remote name to use for branch detection',\n DEFAULT_GIT_REMOTE_NAME\n )\n .option(\n '--tag [value]',\n 'Tag this translation run (auto-resolves from git if no value provided)'\n )\n .option(\n '-m, --message <message>',\n 'Message to attach to the translation tag'\n );\n return command;\n}\n\n/**\n * Attaches flags necessary for parsing inline content\n * @param command - The command to attach the flags to\n * @returns The command with the inline content parsing flags attached\n */\nfunction attachInlineContentParsingFlags(\n command: Command,\n sourceHelp?: string\n) {\n return command\n .option(\n '--tsconfig, --jsconfig <path>',\n 'Path to custom jsconfig or tsconfig file',\n findFilepath(['./tsconfig.json', './jsconfig.json'])\n )\n .option('--dictionary <path>', 'Path to dictionary file')\n .option(\n '--src <paths...>',\n sourceHelp ??\n \"Space-separated list of glob patterns containing the app's source code, by default 'src/**/*.{js,jsx,ts,tsx}' 'app/**/*.{js,jsx,ts,tsx}' 'pages/**/*.{js,jsx,ts,tsx}' 'components/**/*.{js,jsx,ts,tsx}'\"\n )\n .option(\n '--inline',\n 'Include inline content in translations (e.g., inline jsx translations, inline string translations, etc.)',\n true\n );\n}\n\n/**\n * Attaches flags necessary for validating a project\n * @param command\n * @returns The command with the validate flags attached\n */\nexport function attachValidateFlags(command: Command, sourceHelp?: string) {\n return attachInlineContentParsingFlags(\n command.option(\n '-c, --config <path>',\n 'Filepath to config file, by default gt.config.json',\n findFilepath(['gt.config.json'])\n ),\n sourceHelp\n );\n}\n\n/**\n * Attaches flags necessary for translating a project\n * @param command\n * @returns The command with the translate flags attached\n */\nexport function attachInlineTranslateFlags(\n command: Command,\n sourceHelp?: string\n) {\n return attachInlineContentParsingFlags(\n command.option(\n '--ignore-errors',\n 'Ignore errors encountered while scanning for inline content',\n false\n ),\n sourceHelp\n );\n}\n"],"mappings":";;;AAIA,MAAM,kBAAkB;AAExB,SAAgB,kBAAkB,SAAkB;AAClD,SACG,OACC,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,CACA,OAAO,mBAAmB,gDAAgD,CAC1E,OAAO,qBAAqB,iCAAiC;AAChE,QAAO;;AAGT,SAAgB,qBAAqB,SAAkB;AACrD,mBAAkB,QAAQ,CACvB,OAAO,qBAAqB,iCAAiC,CAC7D,OACC,iDACA,4BACD,CACA,OACC,iCACA,mDACD,CACA,OACC,aACA,+DACA,MACD,CACA,OACC,uBACA,wCACC,UAAU;EACT,MAAM,cAAc,SAAS,OAAO,GAAG;AACvC,MAAI,MAAM,YAAY,CACpB,OAAM,IAAI,MAAM,iCAAiC;AAEnD,MAAI,cAAc,EAChB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,SAAO;IAET,gBACD,CACA,OACC,gBACA,mIACD,CACA,OACC,mBACA,sEACD,CACA,OAAO,aAAa,mCAAmC,MAAM,CAC7D,OACC,uCACA,iJACA,MACD,CACA,OACC,sCACA,yEACA,MACD,CACA,OACC,qCACA,8HACA,MACD,CACA,OACC,0CACA,2JACA,MACD,CACA,OACC,2CACA,qGACA,MACD,CACA,OACC,WACA,uFACA,MACD,CACA,OACC,oBACA,4EACA,MACD,CACA,OACC,qBACA,yDACD,CACA,OACC,oCACA,gEACA,MACD,CACA,OACC,qBACA,kDACD,CACA,OACC,8BACA,+FACA,MACD,CACA,OAAO,sBAAsB,mCAAmC,CAChE,OACC,wBACA,4DACA,wBACD,CACA,OACC,iBACA,yEACD,CACA,OACC,2BACA,2CACD;AACH,QAAO;;;;;;;AAQT,SAAS,gCACP,SACA,YACA;AACA,QAAO,QACJ,OACC,iCACA,4CACA,aAAa,CAAC,mBAAmB,kBAAkB,CAAC,CACrD,CACA,OAAO,uBAAuB,0BAA0B,CACxD,OACC,oBACA,cACE,0MACH,CACA,OACC,YACA,4GACA,KACD;;;;;;;AAQL,SAAgB,oBAAoB,SAAkB,YAAqB;AACzE,QAAO,gCACL,QAAQ,OACN,uBACA,sDACA,aAAa,CAAC,iBAAiB,CAAC,CACjC,EACD,WACD;;;;;;;AAQH,SAAgB,2BACd,SACA,YACA;AACA,QAAO,gCACL,QAAQ,OACN,mBACA,+DACA,MACD,EACD,WACD"}
@@ -120,7 +120,7 @@ async function generateSettings(flags, cwd = process.cwd(), options) {
120
120
  experimentalFlattenJsonFiles: gtConfig.options?.experimentalFlattenJsonFiles || flags.experimentalFlattenJsonFiles,
121
121
  experimentalClearLocaleDirs: gtConfig.options?.experimentalClearLocaleDirs || flags.experimentalClearLocaleDirs,
122
122
  clearLocaleDirsExclude: gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,
123
- saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? true
123
+ saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false
124
124
  };
125
125
  if (mergedOptions.omitConfigIds && (mergedOptions.publish === true || mergedOptions.files.gtJson.publish === true)) logger.warn(chalk.yellow("Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version."));
126
126
  if (mergedOptions.options) {
@@ -1 +1 @@
1
- {"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? true,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Keep both clients on the same resolved credentials while consumers migrate.\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"generateSettings.js","names":[],"sources":["../../src/config/generateSettings.ts"],"sourcesContent":["import {\n displayProjectId,\n exitSync,\n logErrorAndExit,\n warnApiKeyInConfig,\n warnDeprecatedField,\n} from '../console/logging.js';\nimport { loadConfig } from '../fs/config/loadConfig.js';\nimport { FilesOptions, Settings } from '../types/index.js';\nimport {\n defaultBaseUrl,\n libraryDefaultLocale,\n} from 'generaltranslation/internal';\nimport { resolveFiles } from '../fs/config/parseFilesConfig.js';\nimport { validateSettings } from './validateSettings.js';\nimport {\n DEFAULT_GIT_REMOTE_NAME,\n GT_DASHBOARD_URL,\n} from '../utils/constants.js';\nimport { resolveProjectId } from '../fs/utils.js';\nimport crypto from 'node:crypto';\nimport { execSync } from 'node:child_process';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { resolveConfig } from './resolveConfig.js';\nimport { configureApiClient } from '../utils/api.js';\nimport { gt } from '../utils/gt.js';\nimport { generatePreset } from './optionPresets.js';\nimport { GT_PARSING_FLAGS_DEFAULT } from './defaults.js';\nimport { normalizeFilesOptions } from '../formats/files/transformFormat.js';\nimport { determineLibrary } from '../fs/determineFramework/index.js';\nimport { logger } from '../console/logger.js';\n\nexport const DEFAULT_SRC_PATTERNS = [\n 'src/**/*.{js,jsx,ts,tsx}',\n 'app/**/*.{js,jsx,ts,tsx}',\n 'pages/**/*.{js,jsx,ts,tsx}',\n 'components/**/*.{js,jsx,ts,tsx}',\n];\n\nexport const DEFAULT_PYTHON_SRC_PATTERNS = ['**/*.py'];\nexport const DEFAULT_PYTHON_SRC_EXCLUDES = [\n 'venv/**',\n '.venv/**',\n '__pycache__/**',\n '**/migrations/**',\n '**/tests/**',\n '**/test_*.py',\n '**/*_test.py',\n];\n\ntype GenerateSettingsInput = Partial<Omit<Settings, 'tag'>> & {\n config?: string;\n projectId?: string;\n locales?: string[];\n options?: Settings['options'];\n files?: unknown;\n publish?: boolean;\n omitConfigIds?: boolean;\n saveLocal?: boolean;\n tag?: string | boolean;\n message?: string;\n branch?: string;\n enableBranching?: boolean;\n disableBranchDetection?: boolean;\n remoteName?: string;\n experimentalLocalizeStaticImports?: boolean;\n experimentalLocalizeStaticUrls?: boolean;\n experimentalLocalizeRelativeAssets?: boolean;\n experimentalHideDefaultLocale?: boolean;\n experimentalFlattenJsonFiles?: boolean;\n experimentalClearLocaleDirs?: boolean;\n clearLocaleDirsExclude?: string[];\n [key: string]: unknown;\n};\n\nfunction hasConfiguredTranslationFiles(files: unknown): boolean {\n if (!files || typeof files !== 'object' || Array.isArray(files)) {\n return false;\n }\n return Object.keys(files).some((key) => key !== 'gt');\n}\n\n/**\n * Generates settings from any\n * @param flags - The CLI flags to generate settings from\n * @param cwd - The current working directory\n * @param options - Additional options\n * @param options.requireConfig - If true, exit with an error when no config file is found\n * @returns The generated settings\n */\nexport async function generateSettings(\n flags: GenerateSettingsInput,\n cwd: string = process.cwd(),\n options?: { requireConfig?: boolean }\n): Promise<Settings> {\n // Load config file\n let gtConfig: GenerateSettingsInput = {};\n\n if (flags.config && !flags.config.endsWith('.json')) {\n flags.config = `${flags.config}.json`;\n }\n if (flags.config) {\n gtConfig = loadConfig(flags.config);\n } else {\n const config = resolveConfig(cwd);\n if (config) {\n gtConfig = config.config as GenerateSettingsInput;\n flags.config = config.path;\n } else {\n if (options?.requireConfig) {\n return logErrorAndExit(\n 'No gt.config.json file was found. Run `npx gt init` to create one, pass --config, or run this command from your project root.'\n );\n }\n gtConfig = {};\n }\n }\n\n // Warn if apiKey is present in gt.config.json\n if (gtConfig.apiKey) {\n warnApiKeyInConfig(flags.config ?? 'gt.config.json');\n exitSync(1);\n }\n const projectIdEnv = resolveProjectId();\n // Resolve mismatched projectIds\n if (\n gtConfig.projectId &&\n flags.projectId &&\n gtConfig.projectId !== flags.projectId\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but the CLI flag uses ${chalk.green(flags.projectId)}. Use the same projectId in all configs.`\n );\n } else if (\n gtConfig.projectId &&\n projectIdEnv &&\n gtConfig.projectId !== projectIdEnv\n ) {\n logErrorAndExit(\n `Project ID mismatch: gt.config.json uses ${chalk.green(gtConfig.projectId)}, but GT_PROJECT_ID uses ${chalk.green(projectIdEnv)}. Use the same projectId in all configs.`\n );\n }\n\n if (\n flags.options?.docsUrlPattern &&\n !flags.options?.docsUrlPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static URLs could not be localized because the URL pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated URL.'\n );\n }\n\n if (\n flags.options?.docsImportPattern &&\n !flags.options?.docsImportPattern.includes('[locale]')\n ) {\n logErrorAndExit(\n 'Static imports could not be localized because the import pattern is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the generated import path.'\n );\n }\n\n if (flags.options?.copyFiles) {\n for (const file of flags.options.copyFiles) {\n if (!file.includes('[locale]')) {\n logErrorAndExit(\n 'Files could not be copied because the file path is missing \"[locale]\". Add \"[locale]\" where the locale should appear in the copied path.'\n );\n }\n }\n }\n\n // Warn on deprecated includeSourceCodeContext\n const configuredFiles = gtConfig.files as FilesOptions | undefined;\n if (configuredFiles?.gt?.includeSourceCodeContext != null) {\n warnDeprecatedField(\n 'files.gt.includeSourceCodeContext',\n 'files.gt.parsingFlags.includeSourceCodeContext'\n );\n }\n\n // merge options\n const mergedOptions: Settings = { ...gtConfig, ...flags } as Settings;\n\n if (\n determineLibrary().library === 'base' &&\n !hasConfiguredTranslationFiles(mergedOptions.files)\n ) {\n logger.warn(\n chalk.yellow(\n 'No package.json or Python project file found in the current directory. Run this command from the root of your project.'\n )\n );\n }\n\n // Add defaultLocale if not provided\n mergedOptions.defaultLocale =\n mergedOptions.defaultLocale || libraryDefaultLocale;\n\n // merge locales\n mergedOptions.locales = Array.from(\n new Set([...(gtConfig.locales || []), ...(flags.locales || [])])\n );\n // Separate defaultLocale from locales\n mergedOptions.locales = mergedOptions.locales.filter(\n (locale) => locale !== mergedOptions.defaultLocale\n );\n\n // Add apiKey if not provided\n mergedOptions.apiKey = mergedOptions.apiKey || process.env.GT_API_KEY;\n\n // Add projectId if not provided\n mergedOptions.projectId = mergedOptions.projectId || resolveProjectId();\n\n // Add baseUrl if not provided\n mergedOptions.baseUrl = mergedOptions.baseUrl || defaultBaseUrl;\n\n // Add dashboardUrl if not provided\n mergedOptions.dashboardUrl = mergedOptions.dashboardUrl || GT_DASHBOARD_URL;\n\n // Add locales if not provided\n mergedOptions.locales = mergedOptions.locales || [];\n\n // Only set config path if one was actually found or explicitly provided.\n // Do not default to a phantom path — that would cause downstream writes\n // (e.g. updateConfig) to silently create a config file.\n if (!mergedOptions.config) {\n mergedOptions.config = '';\n }\n\n // Display projectId if present\n if (mergedOptions.projectId) {\n displayProjectId(mergedOptions.projectId);\n }\n\n // Add stageTranslations if not provided\n // For human review, always stage the project\n mergedOptions.stageTranslations = mergedOptions.stageTranslations ?? false;\n\n // Top-level default for whether translated files require approval before use.\n // Effective policy is hash-changing, so reject anything but a real boolean.\n if (\n mergedOptions.requiresReview !== undefined &&\n typeof mergedOptions.requiresReview !== 'boolean'\n ) {\n logErrorAndExit(\n 'requiresReview in gt.config.json must be a boolean. Use files.<type>.requiresReview for glob-scoped overrides.'\n );\n }\n mergedOptions.requiresReview = mergedOptions.requiresReview ?? false;\n\n // Add publish — only set if explicitly configured or passed via flag.\n // When neither is set, leave undefined so the publish step knows\n // there is no global publish intent.\n if (flags.publish) {\n mergedOptions.publish = true;\n } else if (gtConfig.publish !== undefined) {\n mergedOptions.publish = gtConfig.publish;\n } else {\n mergedOptions.publish = undefined;\n }\n\n mergedOptions.omitConfigIds =\n flags.omitConfigIds === true || gtConfig.omitConfigIds === true;\n\n // Don't default src here — each pipeline (JS/Python) has its own defaults.\n // Only set src if the user explicitly provided it via flags or config.\n\n // Resolve all glob patterns in the files object\n const compositePatterns = Object.entries(\n mergedOptions.options?.jsonSchema || {}\n )\n .filter(([, schema]) => schema.composite)\n .map(([key]) => key);\n mergedOptions.files = mergedOptions.files\n ? resolveFiles(\n normalizeFilesOptions(mergedOptions.files as FilesOptions),\n mergedOptions.defaultLocale,\n mergedOptions.locales,\n cwd,\n compositePatterns,\n mergedOptions.requiresReview\n )\n : {\n resolvedPaths: {},\n placeholderPaths: {},\n transformPaths: {},\n transformFormats: {},\n publishPaths: new Set<string>(),\n unpublishPaths: new Set<string>(),\n requiresReviewPaths: new Set<string>(),\n parsingFlags: {},\n gtJson: {\n parsingFlags: GT_PARSING_FLAGS_DEFAULT,\n },\n };\n\n mergedOptions.options = {\n ...mergedOptions.options,\n mintlify: {\n ...mergedOptions.options?.mintlify,\n inferTitleFromFilename:\n gtConfig.options?.mintlify?.inferTitleFromFilename ||\n mergedOptions.options?.mintlify?.inferTitleFromFilename,\n },\n experimentalLocalizeStaticImports:\n gtConfig.options?.experimentalLocalizeStaticImports ||\n flags.experimentalLocalizeStaticImports,\n experimentalLocalizeStaticUrls:\n gtConfig.options?.experimentalLocalizeStaticUrls ||\n flags.experimentalLocalizeStaticUrls,\n experimentalLocalizeRelativeAssets:\n gtConfig.options?.experimentalLocalizeRelativeAssets ||\n flags.experimentalLocalizeRelativeAssets,\n experimentalHideDefaultLocale:\n gtConfig.options?.experimentalHideDefaultLocale ||\n flags.experimentalHideDefaultLocale,\n experimentalFlattenJsonFiles:\n gtConfig.options?.experimentalFlattenJsonFiles ||\n flags.experimentalFlattenJsonFiles,\n experimentalClearLocaleDirs:\n gtConfig.options?.experimentalClearLocaleDirs ||\n flags.experimentalClearLocaleDirs,\n clearLocaleDirsExclude:\n gtConfig.options?.clearLocaleDirsExclude || flags.clearLocaleDirsExclude,\n saveLocal: flags.saveLocal ?? gtConfig.options?.saveLocal ?? false,\n };\n\n if (\n mergedOptions.omitConfigIds &&\n (mergedOptions.publish === true ||\n mergedOptions.files.gtJson.publish === true)\n ) {\n logger.warn(\n chalk.yellow(\n 'Config IDs will be omitted even though CDN publishing is enabled. Remote cache/CDN consumers may load the latest available translations instead of a pinned version.'\n )\n );\n }\n\n // Add additional options if provided\n if (mergedOptions.options) {\n if (mergedOptions.options.jsonSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.jsonSchema)) {\n const jsonSchema = mergedOptions.options.jsonSchema[fileGlob];\n if (jsonSchema.preset) {\n mergedOptions.options.jsonSchema[fileGlob] = {\n ...generatePreset(jsonSchema.preset, 'json'),\n ...jsonSchema,\n };\n }\n }\n }\n if (mergedOptions.options.yamlSchema) {\n for (const fileGlob of Object.keys(mergedOptions.options.yamlSchema)) {\n const yamlSchema = mergedOptions.options.yamlSchema[fileGlob];\n if (yamlSchema.preset) {\n mergedOptions.options.yamlSchema[fileGlob] = {\n ...generatePreset(yamlSchema.preset, 'yaml'),\n ...yamlSchema,\n };\n }\n }\n }\n }\n\n // Add parsing options if not provided\n mergedOptions.parsingOptions = mergedOptions.parsingOptions || {};\n mergedOptions.parsingOptions.conditionNames = mergedOptions.parsingOptions\n .conditionNames || [\n 'development',\n 'browser',\n 'module',\n 'import',\n 'require',\n 'default',\n ];\n\n // Add branch options if not provided\n const branchOptions = mergedOptions.branchOptions || {};\n // If --branch is set, enable branching\n branchOptions.enabled =\n flags.enableBranching ??\n gtConfig.branchOptions?.enabled ??\n (flags.branch ? true : false);\n branchOptions.currentBranch =\n flags.branch ?? gtConfig.branchOptions?.currentBranch ?? undefined;\n branchOptions.autoDetectBranches = flags.disableBranchDetection\n ? false\n : (gtConfig.branchOptions?.autoDetectBranches ?? true);\n branchOptions.remoteName =\n flags.remoteName ??\n gtConfig.branchOptions?.remoteName ??\n DEFAULT_GIT_REMOTE_NAME;\n mergedOptions.branchOptions = branchOptions;\n\n // Map -m/--message flag to tagMessage\n if (flags.message) {\n mergedOptions.tagMessage = flags.message;\n }\n\n // Resolve tag:\n // --tag (bare) or -m without --tag: try git SHA, fall back to random hex\n // --tag <value>: use as-is\n // No flags: no tag\n if (flags.tag === true || (!flags.tag && mergedOptions.tagMessage)) {\n try {\n mergedOptions.tag = execSync('git rev-parse --short HEAD', {\n encoding: 'utf-8',\n }).trim();\n // If no message provided, use git commit message\n if (!mergedOptions.tagMessage) {\n mergedOptions.tagMessage = execSync('git log -1 --format=%s', {\n encoding: 'utf-8',\n }).trim();\n }\n } catch {\n // Not in a git repo or git unavailable — fall back to random hex\n mergedOptions.tag = crypto.randomBytes(4).toString('hex');\n }\n }\n\n mergedOptions.configDirectory = path.join(cwd, '.gt');\n\n validateSettings(mergedOptions);\n\n // Keep both clients on the same resolved credentials while consumers migrate.\n configureApiClient({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n customMapping: mergedOptions.customMapping,\n });\n gt.setConfig({\n projectId: mergedOptions.projectId,\n apiKey: mergedOptions.apiKey,\n baseUrl: mergedOptions.baseUrl,\n sourceLocale: mergedOptions.defaultLocale,\n customMapping: mergedOptions.customMapping,\n });\n\n return mergedOptions;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.20.2";
1
+ export declare const PACKAGE_VERSION = "2.20.4";
@@ -1,5 +1,5 @@
1
1
  //#region src/generated/version.ts
2
- const PACKAGE_VERSION = "2.20.2";
2
+ const PACKAGE_VERSION = "2.20.4";
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.20.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.20.4';\n"],"mappings":";AACA,MAAa,kBAAkB"}
@@ -2,12 +2,6 @@ import type { AdditionalOptions } from '../types/index.js';
2
2
  type AnchorIdSettings = {
3
3
  options?: Pick<AdditionalOptions, 'experimentalAddHeaderAnchorIds'>;
4
4
  };
5
- /** A source range on a single line, as 1-based inclusive/exclusive columns. */
6
- interface ColumnRange {
7
- line: number;
8
- startColumn: number;
9
- endColumn: number;
10
- }
11
5
  /**
12
6
  * Represents a heading with its position and metadata
13
7
  */
@@ -24,8 +18,6 @@ export interface HeadingInfo {
24
18
  startColumn: number;
25
19
  /** 1-based column just past the text, before any closing `##`; -1 if unknown. */
26
20
  textEndColumn: number;
27
- /** `id` attribute of a wrapper element already anchoring this heading. */
28
- wrapperId: ColumnRange | null;
29
21
  /** Whether the author wrote an explicit `{#id}`. */
30
22
  explicit: boolean;
31
23
  }
@@ -7,6 +7,12 @@ const ATX_HEADING = /^([ \t]*)(#{1,6}[ \t]+)(.*)$/;
7
7
  /** A trailing custom anchor ID, in either the plain or MDX-escaped form. */
8
8
  const TRAILING_ANCHOR = /\s*(?:\\\{#[^}]+\\\}|\{#[^}]+\})\s*$/;
9
9
  /**
10
+ * Deepest indentation at which Mintlify still reads `## Heading {#id}`. It is
11
+ * the CommonMark limit for a heading; MDX itself accepts deeper headings, so
12
+ * past it the `{#id}` reaches the expression parser and fails to compile.
13
+ */
14
+ const MAX_MINTLIFY_HEADING_INDENT = 3;
15
+ /**
10
16
  * Generates a slug from heading text
11
17
  */
12
18
  function generateSlug(text) {
@@ -43,7 +49,6 @@ function extractHeadingsWithFallback(mdxContent) {
43
49
  endLine: index + 1,
44
50
  startColumn: indent.length + 1,
45
51
  textEndColumn: line.length + 1,
46
- wrapperId: null,
47
52
  explicit: explicitId !== void 0
48
53
  });
49
54
  });
@@ -79,22 +84,6 @@ function assignUniqueSlugs(headings) {
79
84
  }
80
85
  }
81
86
  /**
82
- * Finds the `id` of a wrapper element already anchoring this heading. Requiring
83
- * the heading to be its only child rules out containers like `<Tab>`.
84
- */
85
- function findWrapperId(heading, parent) {
86
- if (!parent || parent.type !== "mdxJsxFlowElement") return null;
87
- const element = parent;
88
- if (element.children.length !== 1 || element.children[0] !== heading) return null;
89
- const position = element.attributes.find((attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "id")?.position;
90
- if (!position || position.start.line !== position.end.line) return null;
91
- return {
92
- line: position.start.line,
93
- startColumn: position.start.column,
94
- endColumn: position.end.column
95
- };
96
- }
97
- /**
98
87
  * Extracts heading information from content (read-only, no modifications).
99
88
  * Source and translation are matched by position, so both must parse the same
100
89
  * way — the fallback extractor misses headings nested in JSX.
@@ -108,7 +97,7 @@ function extractHeadingInfo(mdxContent) {
108
97
  }
109
98
  const headings = [];
110
99
  let position = 0;
111
- visit(ast, "heading", (heading, _index, parent) => {
100
+ visit(ast, "heading", (heading) => {
112
101
  const { cleanedText, explicitId } = parseHeadingContent(extractHeadingText(heading));
113
102
  if (!cleanedText && !explicitId) return;
114
103
  const lastChild = heading.children[heading.children.length - 1];
@@ -121,7 +110,6 @@ function extractHeadingInfo(mdxContent) {
121
110
  endLine: heading.position?.end.line ?? -1,
122
111
  startColumn: heading.position?.start.column ?? 1,
123
112
  textEndColumn: lastChild?.position?.end.column ?? heading.position?.end.column ?? -1,
124
- wrapperId: findWrapperId(heading, parent),
125
113
  explicit: explicitId !== void 0
126
114
  });
127
115
  });
@@ -133,7 +121,7 @@ function extractHeadingInfo(mdxContent) {
133
121
  */
134
122
  function addExplicitAnchorIds(translatedContent, sourceHeadingMap, settings, sourcePath, translatedPath, fileTypeHint) {
135
123
  const addedIds = [];
136
- const useDivWrapping = settings?.options?.experimentalAddHeaderAnchorIds === "mintlify";
124
+ const mintlifyMode = settings?.options?.experimentalAddHeaderAnchorIds === "mintlify";
137
125
  const translatedHeadings = extractHeadingInfo(translatedContent);
138
126
  if (sourceHeadingMap.length !== translatedHeadings.length) {
139
127
  const sourceFile = sourcePath ? `Source file: ${sourcePath}` : "Source file";
@@ -155,17 +143,16 @@ function addExplicitAnchorIds(translatedContent, sourceHeadingMap, settings, sou
155
143
  }
156
144
  });
157
145
  const translatedIsMdx = translatedPath ? translatedPath.toLowerCase().endsWith(".mdx") : true;
158
- const shouldEscapeAnchors = fileTypeHint === "mdx" ? true : fileTypeHint === "md" ? false : translatedIsMdx;
146
+ const shouldEscapeAnchors = mintlifyMode ? false : fileTypeHint === "mdx" ? true : fileTypeHint === "md" ? false : translatedIsMdx;
159
147
  if (idMappings.size === 0) {
160
- const content = useDivWrapping ? translatedContent : normalizeInlineAnchors(translatedContent, shouldEscapeAnchors);
148
+ const content = normalizeInlineAnchors(translatedContent, shouldEscapeAnchors);
161
149
  return {
162
150
  content,
163
151
  hasChanges: content !== translatedContent,
164
152
  addedIds: []
165
153
  };
166
154
  }
167
- let content = applyAnchorIds(translatedContent, translatedHeadings, idMappings, useDivWrapping, shouldEscapeAnchors);
168
- if (!useDivWrapping) content = normalizeInlineAnchors(content, shouldEscapeAnchors);
155
+ const content = normalizeInlineAnchors(applyAnchorIds(translatedContent, translatedHeadings, idMappings, mintlifyMode, shouldEscapeAnchors), shouldEscapeAnchors);
169
156
  return {
170
157
  content,
171
158
  hasChanges: content !== translatedContent,
@@ -176,7 +163,7 @@ function addExplicitAnchorIds(translatedContent, sourceHeadingMap, settings, sou
176
163
  * Writes anchor IDs onto the translated document, locating headings by parser
177
164
  * line position rather than by text. Edits run bottom-up to keep lines valid.
178
165
  */
179
- function applyAnchorIds(translatedContent, translatedHeadings, idMappings, useDivWrapping, escapeAnchors) {
166
+ function applyAnchorIds(translatedContent, translatedHeadings, idMappings, mintlifyMode, escapeAnchors) {
180
167
  const lines = translatedContent.split("\n");
181
168
  const ordered = [...translatedHeadings].sort((a, b) => b.startLine - a.startLine);
182
169
  for (const heading of ordered) {
@@ -184,26 +171,13 @@ function applyAnchorIds(translatedContent, translatedHeadings, idMappings, useDi
184
171
  if (!mapping) continue;
185
172
  if (heading.startLine < 1 || heading.endLine > lines.length) continue;
186
173
  const index = heading.startLine - 1;
187
- if (!useDivWrapping) {
188
- if (heading.textEndColumn < 1) continue;
189
- const anchor = escapeAnchors && !mapping.explicit ? `\\{#${mapping.id}\\}` : `{#${mapping.id}}`;
190
- const { text, trailer } = splitHeadingLine(lines[index], heading);
191
- lines[index] = `${text} ${anchor}${trailer}`;
192
- continue;
193
- }
194
- if (heading.textEndColumn >= 1) {
195
- const { text, trailer } = splitHeadingLine(lines[index], heading);
196
- lines[index] = `${text}${trailer}`;
197
- }
198
- if (heading.wrapperId) {
199
- const { line, startColumn, endColumn } = heading.wrapperId;
200
- const wrapper = lines[line - 1];
201
- lines[line - 1] = wrapper.slice(0, startColumn - 1) + `id="${mapping.id}"` + wrapper.slice(endColumn - 1);
202
- continue;
203
- }
204
- const indent = lines[index].slice(0, Math.max(0, heading.startColumn - 1));
205
- const body = lines.slice(index, heading.endLine).map((line) => ` ${line}`);
206
- lines.splice(index, heading.endLine - heading.startLine + 1, `${indent}<div id="${mapping.id}">`, ...body, `${indent}</div>`);
174
+ if (heading.textEndColumn < 1) continue;
175
+ if (mintlifyMode && heading.endLine > heading.startLine) continue;
176
+ const anchor = escapeAnchors && !mapping.explicit ? `\\{#${mapping.id}\\}` : `{#${mapping.id}}`;
177
+ const { text, trailer } = splitHeadingLine(lines[index], heading);
178
+ const line = `${text} ${anchor}${trailer}`;
179
+ const indent = line.match(/^[ \t]*/)?.[0].length ?? 0;
180
+ lines[index] = mintlifyMode && indent > MAX_MINTLIFY_HEADING_INDENT ? line.trimStart() : line;
207
181
  }
208
182
  return lines.join("\n");
209
183
  }
@@ -1 +1 @@
1
- {"version":3,"file":"addExplicitAnchorIds.js","names":[],"sources":["../../src/utils/addExplicitAnchorIds.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\nimport type { Heading, Node } from 'mdast';\nimport type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';\nimport { logger } from '../console/logger.js';\nimport type { AdditionalOptions } from '../types/index.js';\nimport {\n forEachLineOutsideCodeFences,\n mapLinesOutsideCodeFences,\n parseMdxTolerantly,\n} from './mdxAnchorSyntax.js';\n\ntype AnchorIdSettings = {\n options?: Pick<AdditionalOptions, 'experimentalAddHeaderAnchorIds'>;\n};\n\n/** An ATX heading line, split into indentation, marker and text. */\nconst ATX_HEADING = /^([ \\t]*)(#{1,6}[ \\t]+)(.*)$/;\n\n/** A trailing custom anchor ID, in either the plain or MDX-escaped form. */\nconst TRAILING_ANCHOR = /\\s*(?:\\\\\\{#[^}]+\\\\\\}|\\{#[^}]+\\})\\s*$/;\n\n/**\n * Generates a slug from heading text\n */\nfunction generateSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '') // Remove special chars except spaces and hyphens\n .trim()\n .replace(/\\s+/g, '-') // Replace spaces with hyphens\n .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen\n .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens\n}\n\n/**\n * Extracts text content from heading nodes\n */\nfunction extractHeadingText(heading: Heading): string {\n let text = '';\n\n visit(heading, ['text', 'inlineCode'], (node: Node) => {\n if ('value' in node && typeof node.value === 'string') {\n text += node.value;\n }\n });\n\n return text;\n}\n\n/**\n * Line-by-line heading extractor used when MDX parsing fails outright.\n */\nfunction extractHeadingsWithFallback(mdxContent: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n forEachLineOutsideCodeFences(mdxContent, (line, index) => {\n const headingMatch = line.match(ATX_HEADING);\n if (!headingMatch) return;\n\n const [, indent, marker, rawText] = headingMatch;\n const { cleanedText, explicitId } = parseHeadingContent(rawText);\n if (!cleanedText && !explicitId) return;\n\n headings.push({\n text: cleanedText,\n level: marker.trim().length,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: index + 1,\n endLine: index + 1,\n startColumn: indent.length + 1,\n // Without a parser there is nothing finer to go on than end of line.\n textEndColumn: line.length + 1,\n wrapperId: null,\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\nfunction parseHeadingContent(text: string): {\n cleanedText: string;\n explicitId?: string;\n} {\n // Support both {#id} and escaped \\{#id\\} forms\n const anchorMatch = text.match(/(\\\\\\{#([^}]+)\\\\\\}|\\{#([^}]+)\\})\\s*$/);\n\n if (!anchorMatch) {\n return { cleanedText: text };\n }\n\n const explicitId = anchorMatch[2] || anchorMatch[3];\n const cleanedText = text.replace(anchorMatch[0], '').trimEnd();\n\n return { cleanedText, explicitId };\n}\n\n/**\n * Suffixes repeated slugs `-2`, `-3`, ... as Mintlify does. Author-written IDs\n * are never renumbered, only reserved.\n */\nfunction assignUniqueSlugs(headings: HeadingInfo[]): void {\n // Reserve every explicit ID up front, including ones later in the document,\n // so a generated slug never claims an ID an author asked for.\n const used = new Set(\n headings\n .filter((heading) => heading.explicit)\n .map((heading) => heading.slug)\n );\n\n for (const heading of headings) {\n if (heading.explicit) continue;\n\n // Headings with no slug-able characters would produce id=\"\".\n const base = heading.slug || 'section';\n let slug = base;\n let suffix = 1;\n while (used.has(slug)) {\n suffix += 1;\n slug = `${base}-${suffix}`;\n }\n\n heading.slug = slug;\n used.add(slug);\n }\n}\n\n/** A source range on a single line, as 1-based inclusive/exclusive columns. */\ninterface ColumnRange {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\n/**\n * Represents a heading with its position and metadata\n */\nexport interface HeadingInfo {\n text: string;\n level: number;\n slug: string;\n position: number;\n /** 1-based line the heading starts on. */\n startLine: number;\n /** 1-based line the heading ends on (differs from startLine for setext). */\n endLine: number;\n /** 1-based column of the heading marker; anything left of it is indentation. */\n startColumn: number;\n /** 1-based column just past the text, before any closing `##`; -1 if unknown. */\n textEndColumn: number;\n /** `id` attribute of a wrapper element already anchoring this heading. */\n wrapperId: ColumnRange | null;\n /** Whether the author wrote an explicit `{#id}`. */\n explicit: boolean;\n}\n\n/**\n * Finds the `id` of a wrapper element already anchoring this heading. Requiring\n * the heading to be its only child rules out containers like `<Tab>`.\n */\nfunction findWrapperId(\n heading: Heading,\n parent: Node | undefined\n): ColumnRange | null {\n if (!parent || parent.type !== 'mdxJsxFlowElement') return null;\n\n const element = parent as MdxJsxFlowElement;\n if (element.children.length !== 1 || element.children[0] !== heading) {\n return null;\n }\n\n const id = element.attributes.find(\n (attribute) =>\n attribute.type === 'mdxJsxAttribute' && attribute.name === 'id'\n );\n const position = id?.position;\n if (!position || position.start.line !== position.end.line) return null;\n\n return {\n line: position.start.line,\n startColumn: position.start.column,\n endColumn: position.end.column,\n };\n}\n\n/**\n * Extracts heading information from content (read-only, no modifications).\n * Source and translation are matched by position, so both must parse the same\n * way — the fallback extractor misses headings nested in JSX.\n */\nexport function extractHeadingInfo(mdxContent: string): HeadingInfo[] {\n let ast;\n try {\n ast = parseMdxTolerantly(mdxContent);\n } catch {\n // Fallback: line-by-line extraction skipping fenced code blocks\n return extractHeadingsWithFallback(mdxContent);\n }\n\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n visit(ast, 'heading', (heading: Heading, _index, parent) => {\n const headingText = extractHeadingText(heading);\n const { cleanedText, explicitId } = parseHeadingContent(headingText);\n if (!cleanedText && !explicitId) return;\n\n const lastChild = heading.children[heading.children.length - 1];\n\n headings.push({\n text: cleanedText,\n level: heading.depth,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: heading.position?.start.line ?? -1,\n endLine: heading.position?.end.line ?? -1,\n startColumn: heading.position?.start.column ?? 1,\n textEndColumn:\n lastChild?.position?.end.column ?? heading.position?.end.column ?? -1,\n wrapperId: findWrapperId(heading, parent),\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\n/**\n * Applies anchor IDs to translated content based on source heading mapping\n */\nexport function addExplicitAnchorIds(\n translatedContent: string,\n sourceHeadingMap: HeadingInfo[],\n settings?: AnchorIdSettings,\n sourcePath?: string,\n translatedPath?: string,\n fileTypeHint?: 'md' | 'mdx'\n): {\n content: string;\n hasChanges: boolean;\n addedIds: Array<{ heading: string; id: string }>;\n} {\n const addedIds: Array<{ heading: string; id: string }> = [];\n const useDivWrapping =\n settings?.options?.experimentalAddHeaderAnchorIds === 'mintlify';\n\n // Extract headings from translated content\n const translatedHeadings = extractHeadingInfo(translatedContent);\n\n // Pre-processing validation: check if header counts match\n if (sourceHeadingMap.length !== translatedHeadings.length) {\n const sourceFile = sourcePath\n ? `Source file: ${sourcePath}`\n : 'Source file';\n const translatedFile = translatedPath\n ? `translated file: ${translatedPath}`\n : 'translated file';\n\n logger.warn(\n `Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +\n `This likely means your source file was edited after translation was requested, causing a mismatch between ` +\n `the number of headers in your source file vs the translated file. Re-translate this file to resolve the issue.`\n );\n }\n\n // Create ID mapping based on positional matching\n const idMappings = new Map<number, { id: string; explicit: boolean }>();\n sourceHeadingMap.forEach((sourceHeading, index) => {\n const translatedHeading = translatedHeadings[index];\n // Match by position and level for safety\n if (translatedHeading && translatedHeading.level === sourceHeading.level) {\n idMappings.set(index, {\n id: sourceHeading.slug,\n explicit: sourceHeading.explicit,\n });\n addedIds.push({\n heading: translatedHeading.text,\n id: sourceHeading.slug,\n });\n }\n });\n\n const translatedIsMdx = translatedPath\n ? translatedPath.toLowerCase().endsWith('.mdx')\n : true; // default to mdx-style escaping when unknown\n const shouldEscapeAnchors =\n fileTypeHint === 'mdx'\n ? true\n : fileTypeHint === 'md'\n ? false\n : translatedIsMdx;\n\n if (idMappings.size === 0) {\n // Normalize anchors the translation carried over.\n const content = useDivWrapping\n ? translatedContent\n : normalizeInlineAnchors(translatedContent, shouldEscapeAnchors);\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds: [],\n };\n }\n\n let content = applyAnchorIds(\n translatedContent,\n translatedHeadings,\n idMappings,\n useDivWrapping,\n shouldEscapeAnchors\n );\n\n if (!useDivWrapping) {\n content = normalizeInlineAnchors(content, shouldEscapeAnchors);\n }\n\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds,\n };\n}\n\n/**\n * Writes anchor IDs onto the translated document, locating headings by parser\n * line position rather than by text. Edits run bottom-up to keep lines valid.\n */\nfunction applyAnchorIds(\n translatedContent: string,\n translatedHeadings: HeadingInfo[],\n idMappings: Map<number, { id: string; explicit: boolean }>,\n useDivWrapping: boolean,\n escapeAnchors: boolean\n): string {\n const lines = translatedContent.split('\\n');\n\n const ordered = [...translatedHeadings].sort(\n (a, b) => b.startLine - a.startLine\n );\n\n for (const heading of ordered) {\n const mapping = idMappings.get(heading.position);\n if (!mapping) continue;\n if (heading.startLine < 1 || heading.endLine > lines.length) continue;\n\n const index = heading.startLine - 1;\n\n if (!useDivWrapping) {\n // Setext headings have no column to append to.\n if (heading.textEndColumn < 1) continue;\n\n const escape = escapeAnchors && !mapping.explicit;\n const anchor = escape ? `\\\\{#${mapping.id}\\\\}` : `{#${mapping.id}}`;\n const { text, trailer } = splitHeadingLine(lines[index], heading);\n\n lines[index] = `${text} ${anchor}${trailer}`;\n continue;\n }\n\n // In wrapper mode every heading gets a wrapper, including ones whose ID\n // the author wrote inline. Mintlify's `{#id}` pre-pass does not recognize\n // headings indented four or more spaces, which the MDX serializer produces\n // for headings nested in JSX, so a re-attached inline ID fails to compile.\n // A wrapper anchors the heading at any indentation. Drop any inline ID the\n // translation carried over so the two forms never appear together.\n if (heading.textEndColumn >= 1) {\n const { text, trailer } = splitHeadingLine(lines[index], heading);\n lines[index] = `${text}${trailer}`;\n }\n\n if (heading.wrapperId) {\n // Already wrapped: fix the ID in place rather than nesting another.\n const { line, startColumn, endColumn } = heading.wrapperId;\n const wrapper = lines[line - 1];\n lines[line - 1] =\n wrapper.slice(0, startColumn - 1) +\n `id=\"${mapping.id}\"` +\n wrapper.slice(endColumn - 1);\n continue;\n }\n\n const indent = lines[index].slice(0, Math.max(0, heading.startColumn - 1));\n const body = lines.slice(index, heading.endLine).map((line) => ` ${line}`);\n\n lines.splice(\n index,\n heading.endLine - heading.startLine + 1,\n `${indent}<div id=\"${mapping.id}\">`,\n ...body,\n `${indent}</div>`\n );\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Splits a heading line into its text, minus any trailing inline anchor, and\n * whatever follows the text (a closing `##` sequence, for instance).\n */\nfunction splitHeadingLine(\n line: string,\n heading: HeadingInfo\n): { text: string; trailer: string } {\n return {\n text: line.slice(0, heading.textEndColumn - 1).replace(TRAILING_ANCHOR, ''),\n trailer: line.slice(heading.textEndColumn - 1),\n };\n}\n\n/**\n * Normalizes every inline anchor: escaped for MDX, bare for Markdown.\n */\nfunction normalizeInlineAnchors(\n content: string,\n escapeAnchors: boolean\n): string {\n return mapLinesOutsideCodeFences(content, (line) => {\n const atx = line.match(ATX_HEADING);\n if (!atx) return line;\n\n const escaped = atx[3].match(/\\\\\\{#([A-Za-z0-9_-]+)\\\\\\}\\s*$/);\n const bare = atx[3].match(/(?<!\\\\)\\{#([A-Za-z0-9_-]+)\\}\\s*$/);\n\n if (escapeAnchors && bare) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} \\\\{#${bare[1]}\\\\}`;\n }\n if (!escapeAnchors && escaped) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} {#${escaped[1]}}`;\n }\n return line;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"addExplicitAnchorIds.js","names":[],"sources":["../../src/utils/addExplicitAnchorIds.ts"],"sourcesContent":["import { visit } from 'unist-util-visit';\nimport type { Heading, Node } from 'mdast';\nimport { logger } from '../console/logger.js';\nimport type { AdditionalOptions } from '../types/index.js';\nimport {\n forEachLineOutsideCodeFences,\n mapLinesOutsideCodeFences,\n parseMdxTolerantly,\n} from './mdxAnchorSyntax.js';\n\ntype AnchorIdSettings = {\n options?: Pick<AdditionalOptions, 'experimentalAddHeaderAnchorIds'>;\n};\n\n/** An ATX heading line, split into indentation, marker and text. */\nconst ATX_HEADING = /^([ \\t]*)(#{1,6}[ \\t]+)(.*)$/;\n\n/** A trailing custom anchor ID, in either the plain or MDX-escaped form. */\nconst TRAILING_ANCHOR = /\\s*(?:\\\\\\{#[^}]+\\\\\\}|\\{#[^}]+\\})\\s*$/;\n\n/**\n * Deepest indentation at which Mintlify still reads `## Heading {#id}`. It is\n * the CommonMark limit for a heading; MDX itself accepts deeper headings, so\n * past it the `{#id}` reaches the expression parser and fails to compile.\n */\nconst MAX_MINTLIFY_HEADING_INDENT = 3;\n\n/**\n * Generates a slug from heading text\n */\nfunction generateSlug(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, '') // Remove special chars except spaces and hyphens\n .trim()\n .replace(/\\s+/g, '-') // Replace spaces with hyphens\n .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen\n .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens\n}\n\n/**\n * Extracts text content from heading nodes\n */\nfunction extractHeadingText(heading: Heading): string {\n let text = '';\n\n visit(heading, ['text', 'inlineCode'], (node: Node) => {\n if ('value' in node && typeof node.value === 'string') {\n text += node.value;\n }\n });\n\n return text;\n}\n\n/**\n * Line-by-line heading extractor used when MDX parsing fails outright.\n */\nfunction extractHeadingsWithFallback(mdxContent: string): HeadingInfo[] {\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n forEachLineOutsideCodeFences(mdxContent, (line, index) => {\n const headingMatch = line.match(ATX_HEADING);\n if (!headingMatch) return;\n\n const [, indent, marker, rawText] = headingMatch;\n const { cleanedText, explicitId } = parseHeadingContent(rawText);\n if (!cleanedText && !explicitId) return;\n\n headings.push({\n text: cleanedText,\n level: marker.trim().length,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: index + 1,\n endLine: index + 1,\n startColumn: indent.length + 1,\n // Without a parser there is nothing finer to go on than end of line.\n textEndColumn: line.length + 1,\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\nfunction parseHeadingContent(text: string): {\n cleanedText: string;\n explicitId?: string;\n} {\n // Support both {#id} and escaped \\{#id\\} forms\n const anchorMatch = text.match(/(\\\\\\{#([^}]+)\\\\\\}|\\{#([^}]+)\\})\\s*$/);\n\n if (!anchorMatch) {\n return { cleanedText: text };\n }\n\n const explicitId = anchorMatch[2] || anchorMatch[3];\n const cleanedText = text.replace(anchorMatch[0], '').trimEnd();\n\n return { cleanedText, explicitId };\n}\n\n/**\n * Suffixes repeated slugs `-2`, `-3`, ... as Mintlify does. Author-written IDs\n * are never renumbered, only reserved.\n */\nfunction assignUniqueSlugs(headings: HeadingInfo[]): void {\n // Reserve every explicit ID up front, including ones later in the document,\n // so a generated slug never claims an ID an author asked for.\n const used = new Set(\n headings\n .filter((heading) => heading.explicit)\n .map((heading) => heading.slug)\n );\n\n for (const heading of headings) {\n if (heading.explicit) continue;\n\n // Headings with no slug-able characters would produce id=\"\".\n const base = heading.slug || 'section';\n let slug = base;\n let suffix = 1;\n while (used.has(slug)) {\n suffix += 1;\n slug = `${base}-${suffix}`;\n }\n\n heading.slug = slug;\n used.add(slug);\n }\n}\n\n/**\n * Represents a heading with its position and metadata\n */\nexport interface HeadingInfo {\n text: string;\n level: number;\n slug: string;\n position: number;\n /** 1-based line the heading starts on. */\n startLine: number;\n /** 1-based line the heading ends on (differs from startLine for setext). */\n endLine: number;\n /** 1-based column of the heading marker; anything left of it is indentation. */\n startColumn: number;\n /** 1-based column just past the text, before any closing `##`; -1 if unknown. */\n textEndColumn: number;\n /** Whether the author wrote an explicit `{#id}`. */\n explicit: boolean;\n}\n\n/**\n * Extracts heading information from content (read-only, no modifications).\n * Source and translation are matched by position, so both must parse the same\n * way — the fallback extractor misses headings nested in JSX.\n */\nexport function extractHeadingInfo(mdxContent: string): HeadingInfo[] {\n let ast;\n try {\n ast = parseMdxTolerantly(mdxContent);\n } catch {\n // Fallback: line-by-line extraction skipping fenced code blocks\n return extractHeadingsWithFallback(mdxContent);\n }\n\n const headings: HeadingInfo[] = [];\n let position = 0;\n\n visit(ast, 'heading', (heading: Heading) => {\n const headingText = extractHeadingText(heading);\n const { cleanedText, explicitId } = parseHeadingContent(headingText);\n if (!cleanedText && !explicitId) return;\n\n const lastChild = heading.children[heading.children.length - 1];\n\n headings.push({\n text: cleanedText,\n level: heading.depth,\n slug: explicitId ?? generateSlug(cleanedText),\n position: position++,\n startLine: heading.position?.start.line ?? -1,\n endLine: heading.position?.end.line ?? -1,\n startColumn: heading.position?.start.column ?? 1,\n textEndColumn:\n lastChild?.position?.end.column ?? heading.position?.end.column ?? -1,\n explicit: explicitId !== undefined,\n });\n });\n\n assignUniqueSlugs(headings);\n return headings;\n}\n\n/**\n * Applies anchor IDs to translated content based on source heading mapping\n */\nexport function addExplicitAnchorIds(\n translatedContent: string,\n sourceHeadingMap: HeadingInfo[],\n settings?: AnchorIdSettings,\n sourcePath?: string,\n translatedPath?: string,\n fileTypeHint?: 'md' | 'mdx'\n): {\n content: string;\n hasChanges: boolean;\n addedIds: Array<{ heading: string; id: string }>;\n} {\n const addedIds: Array<{ heading: string; id: string }> = [];\n // Mintlify mode writes Mintlify's native `{#id}` on every heading.\n const mintlifyMode =\n settings?.options?.experimentalAddHeaderAnchorIds === 'mintlify';\n\n // Extract headings from translated content\n const translatedHeadings = extractHeadingInfo(translatedContent);\n\n // Pre-processing validation: check if header counts match\n if (sourceHeadingMap.length !== translatedHeadings.length) {\n const sourceFile = sourcePath\n ? `Source file: ${sourcePath}`\n : 'Source file';\n const translatedFile = translatedPath\n ? `translated file: ${translatedPath}`\n : 'translated file';\n\n logger.warn(\n `Header count mismatch detected! ${sourceFile} has ${sourceHeadingMap.length} headers but ${translatedFile} has ${translatedHeadings.length} headers. ` +\n `This likely means your source file was edited after translation was requested, causing a mismatch between ` +\n `the number of headers in your source file vs the translated file. Re-translate this file to resolve the issue.`\n );\n }\n\n // Create ID mapping based on positional matching\n const idMappings = new Map<number, { id: string; explicit: boolean }>();\n sourceHeadingMap.forEach((sourceHeading, index) => {\n const translatedHeading = translatedHeadings[index];\n // Match by position and level for safety\n if (translatedHeading && translatedHeading.level === sourceHeading.level) {\n idMappings.set(index, {\n id: sourceHeading.slug,\n explicit: sourceHeading.explicit,\n });\n addedIds.push({\n heading: translatedHeading.text,\n id: sourceHeading.slug,\n });\n }\n });\n\n const translatedIsMdx = translatedPath\n ? translatedPath.toLowerCase().endsWith('.mdx')\n : true; // default to mdx-style escaping when unknown\n const shouldEscapeAnchors = mintlifyMode\n ? false\n : fileTypeHint === 'mdx'\n ? true\n : fileTypeHint === 'md'\n ? false\n : translatedIsMdx;\n\n if (idMappings.size === 0) {\n // Normalize anchors the translation carried over.\n const content = normalizeInlineAnchors(\n translatedContent,\n shouldEscapeAnchors\n );\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds: [],\n };\n }\n\n const content = normalizeInlineAnchors(\n applyAnchorIds(\n translatedContent,\n translatedHeadings,\n idMappings,\n mintlifyMode,\n shouldEscapeAnchors\n ),\n shouldEscapeAnchors\n );\n\n return {\n content,\n hasChanges: content !== translatedContent,\n addedIds,\n };\n}\n\n/**\n * Writes anchor IDs onto the translated document, locating headings by parser\n * line position rather than by text. Edits run bottom-up to keep lines valid.\n */\nfunction applyAnchorIds(\n translatedContent: string,\n translatedHeadings: HeadingInfo[],\n idMappings: Map<number, { id: string; explicit: boolean }>,\n mintlifyMode: boolean,\n escapeAnchors: boolean\n): string {\n const lines = translatedContent.split('\\n');\n\n const ordered = [...translatedHeadings].sort(\n (a, b) => b.startLine - a.startLine\n );\n\n for (const heading of ordered) {\n const mapping = idMappings.get(heading.position);\n if (!mapping) continue;\n if (heading.startLine < 1 || heading.endLine > lines.length) continue;\n\n const index = heading.startLine - 1;\n\n if (heading.textEndColumn < 1) continue;\n\n // Mintlify reads `{#id}` on ATX headings only, so a setext heading is left\n // alone. Other modes keep the anchor on its text line as before.\n if (mintlifyMode && heading.endLine > heading.startLine) continue;\n\n const escape = escapeAnchors && !mapping.explicit;\n const anchor = escape ? `\\\\{#${mapping.id}\\\\}` : `{#${mapping.id}}`;\n const { text, trailer } = splitHeadingLine(lines[index], heading);\n const line = `${text} ${anchor}${trailer}`;\n\n // The MDX serializer indents JSX children two spaces per level, so a\n // heading nested in JSX can sit deeper than Mintlify reads `{#id}`. Move it\n // to the margin; mixed indentation inside a JSX element is valid MDX.\n const indent = line.match(/^[ \\t]*/)?.[0].length ?? 0;\n lines[index] =\n mintlifyMode && indent > MAX_MINTLIFY_HEADING_INDENT\n ? line.trimStart()\n : line;\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Splits a heading line into its text, minus any trailing inline anchor, and\n * whatever follows the text (a closing `##` sequence, for instance).\n */\nfunction splitHeadingLine(\n line: string,\n heading: HeadingInfo\n): { text: string; trailer: string } {\n return {\n text: line.slice(0, heading.textEndColumn - 1).replace(TRAILING_ANCHOR, ''),\n trailer: line.slice(heading.textEndColumn - 1),\n };\n}\n\n/**\n * Normalizes every inline anchor: escaped for MDX, bare for Markdown.\n */\nfunction normalizeInlineAnchors(\n content: string,\n escapeAnchors: boolean\n): string {\n return mapLinesOutsideCodeFences(content, (line) => {\n const atx = line.match(ATX_HEADING);\n if (!atx) return line;\n\n const escaped = atx[3].match(/\\\\\\{#([A-Za-z0-9_-]+)\\\\\\}\\s*$/);\n const bare = atx[3].match(/(?<!\\\\)\\{#([A-Za-z0-9_-]+)\\}\\s*$/);\n\n if (escapeAnchors && bare) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} \\\\{#${bare[1]}\\\\}`;\n }\n if (!escapeAnchors && escaped) {\n const text = atx[3].replace(TRAILING_ANCHOR, '');\n return `${atx[1]}${atx[2]}${text} {#${escaped[1]}}`;\n }\n return line;\n });\n}\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ async function runStageFilesWorkflow({ files, options, settings, inlineLibrary }
35
35
  files,
36
36
  branchData
37
37
  });
38
- if (settings.options?.saveLocal !== false) await userEditDiffsStep.run(uploadedFiles);
38
+ if (settings.options?.saveLocal === true) await userEditDiffsStep.run(uploadedFiles);
39
39
  if (settings.tag) try {
40
40
  await new TagStep(api, settings, !!options.tag).run(uploadedFiles);
41
41
  } catch {
@@ -1 +1 @@
1
- {"version":3,"file":"stage.js","names":[],"sources":["../../src/workflows/stage.ts"],"sourcesContent":["import { logCollectedFiles, logErrorAndExit } from '../console/logging.js';\nimport { branchResolutionError, withOriginalError } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { Settings, TranslateFlags } from '../types/index.js';\nimport { api } from '../utils/api.js';\nimport { EnqueueFilesResult, FileToUpload } from 'generaltranslation/types';\nimport { UploadSourcesStep } from './steps/UploadSourcesStep.js';\nimport { SetupStep } from './steps/SetupStep.js';\nimport { EnqueueStep } from './steps/EnqueueStep.js';\nimport { BranchStep } from './steps/BranchStep.js';\nimport { TagStep } from './steps/TagStep.js';\nimport { UserEditDiffsStep } from './steps/UserEditDiffsStep.js';\nimport { BranchData } from '../types/branch.js';\nimport { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';\nimport { filterFilesForEnqueue } from './utils/filterFilesForEnqueue.js';\nimport { syncFonts } from './utils/syncFonts.js';\nimport type { InlineLibrary } from '../types/libraries.js';\n\n/**\n * Sends multiple files for translation to the API using a workflow pattern\n * @param files - Array of file objects to translate\n * @param options - The options for the API call\n * @param settings - Settings configuration\n * @returns The translated content or version ID\n */\nexport async function runStageFilesWorkflow({\n files,\n options,\n settings,\n inlineLibrary,\n}: {\n files: FileToUpload[];\n options: TranslateFlags;\n settings: Settings;\n inlineLibrary?: InlineLibrary;\n}): Promise<{\n branchData: BranchData;\n enqueueResult: EnqueueFilesResult;\n}> {\n try {\n // Log files to be translated\n logCollectedFiles(files, undefined, inlineLibrary);\n\n // Sync fonts before enqueueing so the translation jobs (e.g. Lottie\n // layout refinement) can use them instead of fallback fonts.\n await syncFonts(settings);\n\n // Calculate timeout for setup step\n const timeoutMs = calculateTimeoutMs(options.timeout);\n\n // Create workflow with steps\n const branchStep = new BranchStep(api, settings);\n const uploadStep = new UploadSourcesStep(api, settings);\n const userEditDiffsStep = new UserEditDiffsStep(settings);\n const setupStep = new SetupStep(api, settings, timeoutMs);\n const enqueueStep = new EnqueueStep(api, settings, options.force);\n\n // first run the branch step\n const branchData = await branchStep.run();\n if (!branchData) {\n return logErrorAndExit(branchResolutionError);\n }\n\n // then run the upload step\n const uploadedFiles = await uploadStep.run({ files, branchData });\n\n // optionally run the user edit diffs step\n if (settings.options?.saveLocal !== false) {\n await userEditDiffsStep.run(uploadedFiles);\n }\n\n // then run the tag step (non-fatal — tagging failure should not block translations)\n if (settings.tag) {\n try {\n const userProvidedTag = !!options.tag;\n const tagStep = new TagStep(api, settings, userProvidedTag);\n await tagStep.run(uploadedFiles);\n } catch {\n logger.warn('Failed to create translation tag. Continuing...');\n }\n }\n\n // then run the setup step\n await setupStep.run(uploadedFiles);\n\n // then run the enqueue step\n const { filesToEnqueue, skippedFiles } = await filterFilesForEnqueue({\n gt: api,\n files: uploadedFiles,\n locales: settings.locales,\n force: options.force,\n });\n if (skippedFiles.length > 0) {\n logger.info(\n `Skipped enqueue for ${skippedFiles.length} already translated file${skippedFiles.length === 1 ? '' : 's'}`\n );\n }\n\n const enqueueResult = await enqueueStep.run(filesToEnqueue);\n\n return { branchData, enqueueResult };\n } catch (error) {\n return logErrorAndExit(\n withOriginalError(\n 'Files could not be sent for translation. Check the files, branch configuration, and API credentials, then try again.',\n error\n )\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,sBAAsB,EAC1C,OACA,SACA,UACA,iBASC;AACD,KAAI;AAEF,oBAAkB,OAAO,KAAA,GAAW,cAAc;AAIlD,QAAM,UAAU,SAAS;EAGzB,MAAM,YAAY,mBAAmB,QAAQ,QAAQ;EAGrD,MAAM,aAAa,IAAI,WAAW,KAAK,SAAS;EAChD,MAAM,aAAa,IAAI,kBAAkB,KAAK,SAAS;EACvD,MAAM,oBAAoB,IAAI,kBAAkB,SAAS;EACzD,MAAM,YAAY,IAAI,UAAU,KAAK,UAAU,UAAU;EACzD,MAAM,cAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM;EAGjE,MAAM,aAAa,MAAM,WAAW,KAAK;AACzC,MAAI,CAAC,WACH,QAAO,gBAAgB,sBAAsB;EAI/C,MAAM,gBAAgB,MAAM,WAAW,IAAI;GAAE;GAAO;GAAY,CAAC;AAGjE,MAAI,SAAS,SAAS,cAAc,MAClC,OAAM,kBAAkB,IAAI,cAAc;AAI5C,MAAI,SAAS,IACX,KAAI;AAGF,SAAM,IADc,QAAQ,KAAK,UAAU,CADlB,CAAC,QAAQ,IAErB,CAAC,IAAI,cAAc;UAC1B;AACN,UAAO,KAAK,kDAAkD;;AAKlE,QAAM,UAAU,IAAI,cAAc;EAGlC,MAAM,EAAE,gBAAgB,iBAAiB,MAAM,sBAAsB;GACnE,IAAI;GACJ,OAAO;GACP,SAAS,SAAS;GAClB,OAAO,QAAQ;GAChB,CAAC;AACF,MAAI,aAAa,SAAS,EACxB,QAAO,KACL,uBAAuB,aAAa,OAAO,0BAA0B,aAAa,WAAW,IAAI,KAAK,MACvG;AAKH,SAAO;GAAE;GAAY,eAAA,MAFO,YAAY,IAAI,eAAe;GAEvB;UAC7B,OAAO;AACd,SAAO,gBACL,kBACE,wHACA,MACD,CACF"}
1
+ {"version":3,"file":"stage.js","names":[],"sources":["../../src/workflows/stage.ts"],"sourcesContent":["import { logCollectedFiles, logErrorAndExit } from '../console/logging.js';\nimport { branchResolutionError, withOriginalError } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { Settings, TranslateFlags } from '../types/index.js';\nimport { api } from '../utils/api.js';\nimport { EnqueueFilesResult, FileToUpload } from 'generaltranslation/types';\nimport { UploadSourcesStep } from './steps/UploadSourcesStep.js';\nimport { SetupStep } from './steps/SetupStep.js';\nimport { EnqueueStep } from './steps/EnqueueStep.js';\nimport { BranchStep } from './steps/BranchStep.js';\nimport { TagStep } from './steps/TagStep.js';\nimport { UserEditDiffsStep } from './steps/UserEditDiffsStep.js';\nimport { BranchData } from '../types/branch.js';\nimport { calculateTimeoutMs } from '../utils/calculateTimeoutMs.js';\nimport { filterFilesForEnqueue } from './utils/filterFilesForEnqueue.js';\nimport { syncFonts } from './utils/syncFonts.js';\nimport type { InlineLibrary } from '../types/libraries.js';\n\n/**\n * Sends multiple files for translation to the API using a workflow pattern\n * @param files - Array of file objects to translate\n * @param options - The options for the API call\n * @param settings - Settings configuration\n * @returns The translated content or version ID\n */\nexport async function runStageFilesWorkflow({\n files,\n options,\n settings,\n inlineLibrary,\n}: {\n files: FileToUpload[];\n options: TranslateFlags;\n settings: Settings;\n inlineLibrary?: InlineLibrary;\n}): Promise<{\n branchData: BranchData;\n enqueueResult: EnqueueFilesResult;\n}> {\n try {\n // Log files to be translated\n logCollectedFiles(files, undefined, inlineLibrary);\n\n // Sync fonts before enqueueing so the translation jobs (e.g. Lottie\n // layout refinement) can use them instead of fallback fonts.\n await syncFonts(settings);\n\n // Calculate timeout for setup step\n const timeoutMs = calculateTimeoutMs(options.timeout);\n\n // Create workflow with steps\n const branchStep = new BranchStep(api, settings);\n const uploadStep = new UploadSourcesStep(api, settings);\n const userEditDiffsStep = new UserEditDiffsStep(settings);\n const setupStep = new SetupStep(api, settings, timeoutMs);\n const enqueueStep = new EnqueueStep(api, settings, options.force);\n\n // first run the branch step\n const branchData = await branchStep.run();\n if (!branchData) {\n return logErrorAndExit(branchResolutionError);\n }\n\n // then run the upload step\n const uploadedFiles = await uploadStep.run({ files, branchData });\n\n // optionally run the user edit diffs step (opt-in via --save-local or options.saveLocal)\n if (settings.options?.saveLocal === true) {\n await userEditDiffsStep.run(uploadedFiles);\n }\n\n // then run the tag step (non-fatal — tagging failure should not block translations)\n if (settings.tag) {\n try {\n const userProvidedTag = !!options.tag;\n const tagStep = new TagStep(api, settings, userProvidedTag);\n await tagStep.run(uploadedFiles);\n } catch {\n logger.warn('Failed to create translation tag. Continuing...');\n }\n }\n\n // then run the setup step\n await setupStep.run(uploadedFiles);\n\n // then run the enqueue step\n const { filesToEnqueue, skippedFiles } = await filterFilesForEnqueue({\n gt: api,\n files: uploadedFiles,\n locales: settings.locales,\n force: options.force,\n });\n if (skippedFiles.length > 0) {\n logger.info(\n `Skipped enqueue for ${skippedFiles.length} already translated file${skippedFiles.length === 1 ? '' : 's'}`\n );\n }\n\n const enqueueResult = await enqueueStep.run(filesToEnqueue);\n\n return { branchData, enqueueResult };\n } catch (error) {\n return logErrorAndExit(\n withOriginalError(\n 'Files could not be sent for translation. Check the files, branch configuration, and API credentials, then try again.',\n error\n )\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,sBAAsB,EAC1C,OACA,SACA,UACA,iBASC;AACD,KAAI;AAEF,oBAAkB,OAAO,KAAA,GAAW,cAAc;AAIlD,QAAM,UAAU,SAAS;EAGzB,MAAM,YAAY,mBAAmB,QAAQ,QAAQ;EAGrD,MAAM,aAAa,IAAI,WAAW,KAAK,SAAS;EAChD,MAAM,aAAa,IAAI,kBAAkB,KAAK,SAAS;EACvD,MAAM,oBAAoB,IAAI,kBAAkB,SAAS;EACzD,MAAM,YAAY,IAAI,UAAU,KAAK,UAAU,UAAU;EACzD,MAAM,cAAc,IAAI,YAAY,KAAK,UAAU,QAAQ,MAAM;EAGjE,MAAM,aAAa,MAAM,WAAW,KAAK;AACzC,MAAI,CAAC,WACH,QAAO,gBAAgB,sBAAsB;EAI/C,MAAM,gBAAgB,MAAM,WAAW,IAAI;GAAE;GAAO;GAAY,CAAC;AAGjE,MAAI,SAAS,SAAS,cAAc,KAClC,OAAM,kBAAkB,IAAI,cAAc;AAI5C,MAAI,SAAS,IACX,KAAI;AAGF,SAAM,IADc,QAAQ,KAAK,UAAU,CADlB,CAAC,QAAQ,IAErB,CAAC,IAAI,cAAc;UAC1B;AACN,UAAO,KAAK,kDAAkD;;AAKlE,QAAM,UAAU,IAAI,cAAc;EAGlC,MAAM,EAAE,gBAAgB,iBAAiB,MAAM,sBAAsB;GACnE,IAAI;GACJ,OAAO;GACP,SAAS,SAAS;GAClB,OAAO,QAAQ;GAChB,CAAC;AACF,MAAI,aAAa,SAAS,EACxB,QAAO,KACL,uBAAuB,aAAa,OAAO,0BAA0B,aAAa,WAAW,IAAI,KAAK,MACvG;AAKH,SAAO;GAAE;GAAY,eAAA,MAFO,YAAY,IAAI,eAAe;GAEvB;UAC7B,OAAO;AACd,SAAO,gBACL,kBACE,wHACA,MACD,CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gt",
3
- "version": "2.20.2",
3
+ "version": "2.20.4",
4
4
  "main": "dist/index.js",
5
5
  "bin": "bin/main.js",
6
6
  "files": [
@@ -115,13 +115,13 @@
115
115
  "unified": "^11.0.5",
116
116
  "unist-util-visit": "^5.0.0",
117
117
  "yaml": "^2.8.0",
118
- "@generaltranslation/icu": "0.1.2",
119
118
  "@generaltranslation/format": "0.1.8",
120
- "@generaltranslation/python-extractor": "0.2.47",
121
119
  "@generaltranslation/supported-locales": "2.1.27",
122
- "@generaltranslation/vue-extractor": "0.1.7",
123
120
  "generaltranslation": "9.2.0",
124
- "gt-remark": "1.0.12"
121
+ "@generaltranslation/vue-extractor": "0.1.7",
122
+ "gt-remark": "1.0.12",
123
+ "@generaltranslation/python-extractor": "0.2.47",
124
+ "@generaltranslation/icu": "0.1.2"
125
125
  },
126
126
  "devDependencies": {
127
127
  "@types/babel__generator": "^7.27.0",