@intlayer/cli 9.6.0 → 9.7.1

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.
Files changed (332) hide show
  1. package/dist/cjs/IntlayerEventListener.cjs +1 -2
  2. package/dist/cjs/IntlayerEventListener.cjs.map +1 -1
  3. package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -2
  4. package/dist/cjs/_virtual/_utils_asset.cjs +2 -3
  5. package/dist/cjs/auth/login.cjs +19 -9
  6. package/dist/cjs/auth/login.cjs.map +1 -1
  7. package/dist/cjs/auth/sessionToken.cjs +1 -2
  8. package/dist/cjs/auth/setupRepository.cjs +94 -0
  9. package/dist/cjs/auth/setupRepository.cjs.map +1 -0
  10. package/dist/cjs/build.cjs +2 -3
  11. package/dist/cjs/bundle.cjs +1 -2
  12. package/dist/cjs/ci.cjs +2 -3
  13. package/dist/cjs/cli.cjs +17 -12
  14. package/dist/cjs/cli.cjs.map +1 -1
  15. package/dist/cjs/config.cjs +2 -3
  16. package/dist/cjs/editor.cjs +62 -34
  17. package/dist/cjs/editor.cjs.map +1 -1
  18. package/dist/cjs/extract.cjs +7 -8
  19. package/dist/cjs/fill/deepMergeContent.cjs +1 -2
  20. package/dist/cjs/fill/extractTranslatableContent.cjs +1 -2
  21. package/dist/cjs/fill/fill.cjs +9 -10
  22. package/dist/cjs/fill/fill.cjs.map +1 -1
  23. package/dist/cjs/fill/formatAutoFilledFilePath.cjs +2 -3
  24. package/dist/cjs/fill/formatFillData.cjs +2 -3
  25. package/dist/cjs/fill/getAvailableLocalesInDictionary.cjs +1 -2
  26. package/dist/cjs/fill/getFilterMissingContentPerLocale.cjs +1 -2
  27. package/dist/cjs/fill/index.cjs +2 -2
  28. package/dist/cjs/fill/listTranslationsTasks.cjs +4 -5
  29. package/dist/cjs/fill/translateDictionary.cjs +6 -7
  30. package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
  31. package/dist/cjs/fill/writeFill.cjs +4 -5
  32. package/dist/cjs/getTargetDictionary.cjs +2 -3
  33. package/dist/cjs/index.cjs +37 -23
  34. package/dist/cjs/init.cjs +175 -22
  35. package/dist/cjs/init.cjs.map +1 -1
  36. package/dist/cjs/initChromeExtension.cjs +5 -6
  37. package/dist/cjs/initInfra.cjs +3 -4
  38. package/dist/cjs/initMCP.cjs +6 -7
  39. package/dist/cjs/initSkills.cjs +6 -7
  40. package/dist/cjs/listContentDeclaration.cjs +3 -4
  41. package/dist/cjs/listProjects.cjs +1 -2
  42. package/dist/cjs/liveSync.cjs +4 -5
  43. package/dist/cjs/liveSync.cjs.map +1 -1
  44. package/dist/cjs/loadPrompts.cjs +1 -2
  45. package/dist/cjs/pull.cjs +9 -11
  46. package/dist/cjs/pull.cjs.map +1 -1
  47. package/dist/cjs/push/pullLog.cjs +2 -3
  48. package/dist/cjs/push/push.cjs +10 -10
  49. package/dist/cjs/push/push.cjs.map +1 -1
  50. package/dist/cjs/pushConfig.cjs +4 -5
  51. package/dist/cjs/pushLog.cjs +2 -3
  52. package/dist/cjs/reviewDoc/reviewDoc.cjs +15 -15
  53. package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
  54. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +8 -9
  55. package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
  56. package/dist/cjs/reviewDoc/reviewDocLog.cjs +2 -3
  57. package/dist/cjs/reviewDoc/reviewDocProgress.cjs +2 -3
  58. package/dist/cjs/reviewDoc/reviewDocSynthesis.cjs +2 -3
  59. package/dist/cjs/scan.cjs +2 -3
  60. package/dist/cjs/searchDoc.cjs +2 -3
  61. package/dist/cjs/test/index.cjs +2 -2
  62. package/dist/cjs/test/listMissingTranslations.cjs +2 -3
  63. package/dist/cjs/test/test.cjs +3 -4
  64. package/dist/cjs/translateDoc/index.cjs +3 -3
  65. package/dist/cjs/translateDoc/translateDoc.cjs +12 -13
  66. package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
  67. package/dist/cjs/translateDoc/translateFile.cjs +8 -9
  68. package/dist/cjs/translateDoc/validation.cjs +1 -2
  69. package/dist/cjs/upgrade.cjs +2 -3
  70. package/dist/cjs/utils/calculateChunks.cjs +1 -2
  71. package/dist/cjs/utils/checkAccess.cjs +5 -6
  72. package/dist/cjs/utils/checkAccess.cjs.map +1 -1
  73. package/dist/cjs/utils/checkConfigConsistency.cjs +1 -2
  74. package/dist/cjs/utils/checkFileModifiedRange.cjs +3 -4
  75. package/dist/cjs/utils/checkLastUpdateTime.cjs +1 -2
  76. package/dist/cjs/utils/chunkInference.cjs +2 -3
  77. package/dist/cjs/utils/detectGitRepository.cjs +86 -0
  78. package/dist/cjs/utils/detectGitRepository.cjs.map +1 -0
  79. package/dist/cjs/utils/fixChunkStartEndChars.cjs +1 -2
  80. package/dist/cjs/utils/formatLineRanges.cjs +1 -2
  81. package/dist/cjs/utils/formatTimeDiff.cjs +1 -2
  82. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +1 -2
  83. package/dist/cjs/utils/getOutputFilePath.cjs +1 -2
  84. package/dist/cjs/utils/getParentPackageJSON.cjs +2 -3
  85. package/dist/cjs/utils/isInteractiveTerminal.cjs +1 -2
  86. package/dist/cjs/utils/listSpecialChars.cjs +1 -2
  87. package/dist/cjs/utils/mapChunksBetweenFiles.cjs +2 -3
  88. package/dist/cjs/utils/openBrowser.cjs +1 -2
  89. package/dist/cjs/utils/parseChoice.cjs +1 -2
  90. package/dist/cjs/utils/reorderParagraphs.cjs +2 -3
  91. package/dist/cjs/utils/selectCmsEnvironment.cjs +2 -3
  92. package/dist/cjs/utils/setupAI.cjs +3 -4
  93. package/dist/cjs/watch.cjs +2 -3
  94. package/dist/esm/IntlayerEventListener.mjs +1 -2
  95. package/dist/esm/IntlayerEventListener.mjs.map +1 -1
  96. package/dist/esm/_virtual/_rolldown/runtime.mjs +1 -2
  97. package/dist/esm/_virtual/_utils_asset.mjs +2 -3
  98. package/dist/esm/auth/login.mjs +15 -5
  99. package/dist/esm/auth/login.mjs.map +1 -1
  100. package/dist/esm/auth/sessionToken.mjs +1 -2
  101. package/dist/esm/auth/setupRepository.mjs +89 -0
  102. package/dist/esm/auth/setupRepository.mjs.map +1 -0
  103. package/dist/esm/build.mjs +2 -3
  104. package/dist/esm/bundle.mjs +1 -2
  105. package/dist/esm/ci.mjs +2 -3
  106. package/dist/esm/cli.mjs +16 -11
  107. package/dist/esm/cli.mjs.map +1 -1
  108. package/dist/esm/config.mjs +2 -3
  109. package/dist/esm/editor.mjs +63 -36
  110. package/dist/esm/editor.mjs.map +1 -1
  111. package/dist/esm/extract.mjs +5 -6
  112. package/dist/esm/fill/deepMergeContent.mjs +1 -2
  113. package/dist/esm/fill/extractTranslatableContent.mjs +1 -2
  114. package/dist/esm/fill/fill.mjs +3 -4
  115. package/dist/esm/fill/fill.mjs.map +1 -1
  116. package/dist/esm/fill/formatAutoFilledFilePath.mjs +2 -3
  117. package/dist/esm/fill/formatFillData.mjs +1 -2
  118. package/dist/esm/fill/getAvailableLocalesInDictionary.mjs +1 -2
  119. package/dist/esm/fill/getFilterMissingContentPerLocale.mjs +1 -2
  120. package/dist/esm/fill/index.mjs +1 -1
  121. package/dist/esm/fill/listTranslationsTasks.mjs +2 -3
  122. package/dist/esm/fill/translateDictionary.mjs +2 -3
  123. package/dist/esm/fill/translateDictionary.mjs.map +1 -1
  124. package/dist/esm/fill/writeFill.mjs +2 -3
  125. package/dist/esm/getTargetDictionary.mjs +2 -3
  126. package/dist/esm/index.mjs +15 -11
  127. package/dist/esm/init.mjs +169 -20
  128. package/dist/esm/init.mjs.map +1 -1
  129. package/dist/esm/initChromeExtension.mjs +2 -3
  130. package/dist/esm/initInfra.mjs +1 -2
  131. package/dist/esm/initMCP.mjs +3 -4
  132. package/dist/esm/initSkills.mjs +2 -3
  133. package/dist/esm/listContentDeclaration.mjs +3 -4
  134. package/dist/esm/listProjects.mjs +1 -2
  135. package/dist/esm/liveSync.mjs +2 -3
  136. package/dist/esm/liveSync.mjs.map +1 -1
  137. package/dist/esm/loadPrompts.mjs +1 -2
  138. package/dist/esm/pull.mjs +6 -8
  139. package/dist/esm/pull.mjs.map +1 -1
  140. package/dist/esm/push/pullLog.mjs +1 -2
  141. package/dist/esm/push/push.mjs +6 -6
  142. package/dist/esm/push/push.mjs.map +1 -1
  143. package/dist/esm/pushConfig.mjs +2 -3
  144. package/dist/esm/pushLog.mjs +1 -2
  145. package/dist/esm/reviewDoc/reviewDoc.mjs +8 -8
  146. package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
  147. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +3 -4
  148. package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
  149. package/dist/esm/reviewDoc/reviewDocLog.mjs +2 -3
  150. package/dist/esm/reviewDoc/reviewDocProgress.mjs +1 -2
  151. package/dist/esm/reviewDoc/reviewDocSynthesis.mjs +1 -2
  152. package/dist/esm/scan.mjs +1 -2
  153. package/dist/esm/searchDoc.mjs +2 -3
  154. package/dist/esm/test/listMissingTranslations.mjs +2 -3
  155. package/dist/esm/test/test.mjs +1 -2
  156. package/dist/esm/translateDoc/index.mjs +2 -2
  157. package/dist/esm/translateDoc/translateDoc.mjs +7 -8
  158. package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
  159. package/dist/esm/translateDoc/translateFile.mjs +4 -5
  160. package/dist/esm/translateDoc/validation.mjs +1 -2
  161. package/dist/esm/upgrade.mjs +1 -2
  162. package/dist/esm/utils/calculateChunks.mjs +1 -2
  163. package/dist/esm/utils/checkAccess.mjs +2 -3
  164. package/dist/esm/utils/checkAccess.mjs.map +1 -1
  165. package/dist/esm/utils/checkConfigConsistency.mjs +1 -2
  166. package/dist/esm/utils/checkFileModifiedRange.mjs +1 -2
  167. package/dist/esm/utils/checkLastUpdateTime.mjs +1 -2
  168. package/dist/esm/utils/chunkInference.mjs +1 -2
  169. package/dist/esm/utils/detectGitRepository.mjs +84 -0
  170. package/dist/esm/utils/detectGitRepository.mjs.map +1 -0
  171. package/dist/esm/utils/fixChunkStartEndChars.mjs +1 -2
  172. package/dist/esm/utils/formatLineRanges.mjs +1 -2
  173. package/dist/esm/utils/formatTimeDiff.mjs +1 -2
  174. package/dist/esm/utils/getIsFileUpdatedRecently.mjs +1 -2
  175. package/dist/esm/utils/getOutputFilePath.mjs +1 -2
  176. package/dist/esm/utils/getParentPackageJSON.mjs +2 -3
  177. package/dist/esm/utils/isInteractiveTerminal.mjs +1 -2
  178. package/dist/esm/utils/listSpecialChars.mjs +1 -2
  179. package/dist/esm/utils/mapChunksBetweenFiles.mjs +1 -2
  180. package/dist/esm/utils/openBrowser.mjs +1 -2
  181. package/dist/esm/utils/parseChoice.mjs +1 -2
  182. package/dist/esm/utils/reorderParagraphs.mjs +1 -2
  183. package/dist/esm/utils/selectCmsEnvironment.mjs +1 -2
  184. package/dist/esm/utils/setupAI.mjs +1 -2
  185. package/dist/esm/watch.mjs +2 -3
  186. package/dist/types/IntlayerEventListener.d.ts +1 -3
  187. package/dist/types/IntlayerEventListener.d.ts.map +1 -1
  188. package/dist/types/auth/login.d.ts +6 -2
  189. package/dist/types/auth/login.d.ts.map +1 -1
  190. package/dist/types/auth/sessionToken.d.ts +0 -2
  191. package/dist/types/auth/sessionToken.d.ts.map +1 -1
  192. package/dist/types/auth/setupRepository.d.ts +24 -0
  193. package/dist/types/auth/setupRepository.d.ts.map +1 -0
  194. package/dist/types/build.d.ts +1 -2
  195. package/dist/types/build.d.ts.map +1 -1
  196. package/dist/types/bundle.d.ts +0 -2
  197. package/dist/types/bundle.d.ts.map +1 -1
  198. package/dist/types/ci.d.ts +1 -2
  199. package/dist/types/ci.d.ts.map +1 -1
  200. package/dist/types/cli.d.ts +1 -2
  201. package/dist/types/cli.d.ts.map +1 -1
  202. package/dist/types/config.d.ts +1 -2
  203. package/dist/types/config.d.ts.map +1 -1
  204. package/dist/types/editor.d.ts +11 -2
  205. package/dist/types/editor.d.ts.map +1 -1
  206. package/dist/types/extract.d.ts +1 -2
  207. package/dist/types/extract.d.ts.map +1 -1
  208. package/dist/types/fill/deepMergeContent.d.ts +0 -2
  209. package/dist/types/fill/deepMergeContent.d.ts.map +1 -1
  210. package/dist/types/fill/extractTranslatableContent.d.ts +0 -2
  211. package/dist/types/fill/extractTranslatableContent.d.ts.map +1 -1
  212. package/dist/types/fill/fill.d.ts +1 -3
  213. package/dist/types/fill/fill.d.ts.map +1 -1
  214. package/dist/types/fill/formatAutoFilledFilePath.d.ts +0 -2
  215. package/dist/types/fill/formatAutoFilledFilePath.d.ts.map +1 -1
  216. package/dist/types/fill/formatFillData.d.ts +0 -2
  217. package/dist/types/fill/formatFillData.d.ts.map +1 -1
  218. package/dist/types/fill/getAvailableLocalesInDictionary.d.ts +0 -2
  219. package/dist/types/fill/getAvailableLocalesInDictionary.d.ts.map +1 -1
  220. package/dist/types/fill/getFilterMissingContentPerLocale.d.ts +0 -2
  221. package/dist/types/fill/getFilterMissingContentPerLocale.d.ts.map +1 -1
  222. package/dist/types/fill/listTranslationsTasks.d.ts +0 -2
  223. package/dist/types/fill/listTranslationsTasks.d.ts.map +1 -1
  224. package/dist/types/fill/translateDictionary.d.ts +3 -5
  225. package/dist/types/fill/translateDictionary.d.ts.map +1 -1
  226. package/dist/types/fill/writeFill.d.ts +0 -2
  227. package/dist/types/fill/writeFill.d.ts.map +1 -1
  228. package/dist/types/getTargetDictionary.d.ts +0 -2
  229. package/dist/types/getTargetDictionary.d.ts.map +1 -1
  230. package/dist/types/index.d.ts +7 -3
  231. package/dist/types/init.d.ts +20 -4
  232. package/dist/types/init.d.ts.map +1 -1
  233. package/dist/types/initChromeExtension.d.ts +0 -2
  234. package/dist/types/initChromeExtension.d.ts.map +1 -1
  235. package/dist/types/initInfra.d.ts +0 -2
  236. package/dist/types/initInfra.d.ts.map +1 -1
  237. package/dist/types/initMCP.d.ts +0 -2
  238. package/dist/types/initMCP.d.ts.map +1 -1
  239. package/dist/types/initSkills.d.ts +0 -2
  240. package/dist/types/initSkills.d.ts.map +1 -1
  241. package/dist/types/listContentDeclaration.d.ts +1 -2
  242. package/dist/types/listContentDeclaration.d.ts.map +1 -1
  243. package/dist/types/listProjects.d.ts +0 -2
  244. package/dist/types/listProjects.d.ts.map +1 -1
  245. package/dist/types/liveSync.d.ts +1 -2
  246. package/dist/types/liveSync.d.ts.map +1 -1
  247. package/dist/types/loadPrompts.d.ts +0 -2
  248. package/dist/types/loadPrompts.d.ts.map +1 -1
  249. package/dist/types/pull.d.ts +1 -2
  250. package/dist/types/pull.d.ts.map +1 -1
  251. package/dist/types/push/pullLog.d.ts +0 -2
  252. package/dist/types/push/pullLog.d.ts.map +1 -1
  253. package/dist/types/push/push.d.ts +1 -2
  254. package/dist/types/push/push.d.ts.map +1 -1
  255. package/dist/types/pushConfig.d.ts +1 -2
  256. package/dist/types/pushConfig.d.ts.map +1 -1
  257. package/dist/types/pushLog.d.ts +0 -2
  258. package/dist/types/pushLog.d.ts.map +1 -1
  259. package/dist/types/reviewDoc/reviewDoc.d.ts +2 -3
  260. package/dist/types/reviewDoc/reviewDoc.d.ts.map +1 -1
  261. package/dist/types/reviewDoc/reviewDocBlockAware.d.ts +1 -4
  262. package/dist/types/reviewDoc/reviewDocBlockAware.d.ts.map +1 -1
  263. package/dist/types/reviewDoc/reviewDocLog.d.ts +0 -2
  264. package/dist/types/reviewDoc/reviewDocLog.d.ts.map +1 -1
  265. package/dist/types/reviewDoc/reviewDocProgress.d.ts +0 -2
  266. package/dist/types/reviewDoc/reviewDocProgress.d.ts.map +1 -1
  267. package/dist/types/reviewDoc/reviewDocSynthesis.d.ts +0 -2
  268. package/dist/types/reviewDoc/reviewDocSynthesis.d.ts.map +1 -1
  269. package/dist/types/scan.d.ts +0 -2
  270. package/dist/types/scan.d.ts.map +1 -1
  271. package/dist/types/searchDoc.d.ts +1 -2
  272. package/dist/types/searchDoc.d.ts.map +1 -1
  273. package/dist/types/test/listMissingTranslations.d.ts +0 -2
  274. package/dist/types/test/listMissingTranslations.d.ts.map +1 -1
  275. package/dist/types/test/test.d.ts +1 -2
  276. package/dist/types/test/test.d.ts.map +1 -1
  277. package/dist/types/translateDoc/index.d.ts +1 -1
  278. package/dist/types/translateDoc/translateDoc.d.ts +0 -2
  279. package/dist/types/translateDoc/translateDoc.d.ts.map +1 -1
  280. package/dist/types/translateDoc/translateFile.d.ts +0 -2
  281. package/dist/types/translateDoc/translateFile.d.ts.map +1 -1
  282. package/dist/types/translateDoc/types.d.ts +1 -3
  283. package/dist/types/translateDoc/types.d.ts.map +1 -1
  284. package/dist/types/translateDoc/validation.d.ts +0 -2
  285. package/dist/types/translateDoc/validation.d.ts.map +1 -1
  286. package/dist/types/upgrade.d.ts +0 -2
  287. package/dist/types/upgrade.d.ts.map +1 -1
  288. package/dist/types/utils/calculateChunks.d.ts +0 -2
  289. package/dist/types/utils/calculateChunks.d.ts.map +1 -1
  290. package/dist/types/utils/checkAccess.d.ts +2 -3
  291. package/dist/types/utils/checkAccess.d.ts.map +1 -1
  292. package/dist/types/utils/checkConfigConsistency.d.ts +0 -2
  293. package/dist/types/utils/checkConfigConsistency.d.ts.map +1 -1
  294. package/dist/types/utils/checkFileModifiedRange.d.ts +1 -2
  295. package/dist/types/utils/checkFileModifiedRange.d.ts.map +1 -1
  296. package/dist/types/utils/checkLastUpdateTime.d.ts +0 -2
  297. package/dist/types/utils/checkLastUpdateTime.d.ts.map +1 -1
  298. package/dist/types/utils/chunkInference.d.ts +1 -2
  299. package/dist/types/utils/chunkInference.d.ts.map +1 -1
  300. package/dist/types/utils/detectGitRepository.d.ts +32 -0
  301. package/dist/types/utils/detectGitRepository.d.ts.map +1 -0
  302. package/dist/types/utils/fixChunkStartEndChars.d.ts +0 -2
  303. package/dist/types/utils/fixChunkStartEndChars.d.ts.map +1 -1
  304. package/dist/types/utils/formatLineRanges.d.ts +0 -2
  305. package/dist/types/utils/formatLineRanges.d.ts.map +1 -1
  306. package/dist/types/utils/formatTimeDiff.d.ts +0 -2
  307. package/dist/types/utils/formatTimeDiff.d.ts.map +1 -1
  308. package/dist/types/utils/getIsFileUpdatedRecently.d.ts +0 -2
  309. package/dist/types/utils/getIsFileUpdatedRecently.d.ts.map +1 -1
  310. package/dist/types/utils/getOutputFilePath.d.ts +0 -2
  311. package/dist/types/utils/getOutputFilePath.d.ts.map +1 -1
  312. package/dist/types/utils/getParentPackageJSON.d.ts +1 -2
  313. package/dist/types/utils/getParentPackageJSON.d.ts.map +1 -1
  314. package/dist/types/utils/isInteractiveTerminal.d.ts +0 -2
  315. package/dist/types/utils/isInteractiveTerminal.d.ts.map +1 -1
  316. package/dist/types/utils/listSpecialChars.d.ts +1 -2
  317. package/dist/types/utils/listSpecialChars.d.ts.map +1 -1
  318. package/dist/types/utils/mapChunksBetweenFiles.d.ts +0 -2
  319. package/dist/types/utils/mapChunksBetweenFiles.d.ts.map +1 -1
  320. package/dist/types/utils/openBrowser.d.ts +0 -2
  321. package/dist/types/utils/openBrowser.d.ts.map +1 -1
  322. package/dist/types/utils/parseChoice.d.ts +0 -2
  323. package/dist/types/utils/parseChoice.d.ts.map +1 -1
  324. package/dist/types/utils/reorderParagraphs.d.ts +0 -2
  325. package/dist/types/utils/reorderParagraphs.d.ts.map +1 -1
  326. package/dist/types/utils/selectCmsEnvironment.d.ts +1 -3
  327. package/dist/types/utils/selectCmsEnvironment.d.ts.map +1 -1
  328. package/dist/types/utils/setupAI.d.ts +1 -2
  329. package/dist/types/utils/setupAI.d.ts.map +1 -1
  330. package/dist/types/watch.d.ts +1 -2
  331. package/dist/types/watch.d.ts.map +1 -1
  332. package/package.json +14 -14
@@ -1,52 +1,79 @@
1
1
  import { __require } from "./_virtual/_rolldown/runtime.mjs";
2
- import { dirname, resolve } from "node:path";
2
+ import { getParentPackageJSON } from "./utils/getParentPackageJSON.mjs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { readFileSync } from "node:fs";
3
6
  import { spawn } from "node:child_process";
4
7
  import { createRequire } from "node:module";
5
8
  import { isESModule } from "@intlayer/config/utils";
6
9
 
7
- //#region src/editor.ts
10
+ // src/editor.ts
11
+ /** Version of this CLI: `intlayer-editor` is released in lockstep with it. */
12
+ const getCliVersion = () => getParentPackageJSON(isESModule ? dirname(fileURLToPath(import.meta.url)) : __dirname).version;
13
+ /**
14
+ * Package runners trying `intlayer-editor` without it being installed. The
15
+ * editor is pinned to the CLI version, so both speak the same protocol.
16
+ */
17
+ const getRemoteEditorRunners = (cliVersion) => {
18
+ const editorPackage = cliVersion ? `intlayer-editor@${cliVersion}` : "intlayer-editor";
19
+ return [["bun", ["x", editorPackage]], ["npx", ["-y", editorPackage]]];
20
+ };
21
+ /**
22
+ * The installed `intlayer-editor`: resolved from the project first (the CLI
23
+ * may be hoisted elsewhere in a monorepo), then from the CLI.
24
+ */
25
+ const resolveInstalledEditor = () => {
26
+ const cliRequire = isESModule ? createRequire(import.meta.url) : __require;
27
+ const requireFunctions = [createRequire(join(process.cwd(), "package.json")), cliRequire];
28
+ for (const requireFunction of requireFunctions) try {
29
+ const packageJsonPath = requireFunction.resolve("intlayer-editor/package.json");
30
+ const { version } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
31
+ return {
32
+ binaryPath: resolve(dirname(packageJsonPath), "bin", "intlayer-editor.mjs"),
33
+ version
34
+ };
35
+ } catch {}
36
+ };
37
+ /** Exits with the editor's exit code, so callers notice a failed start. */
38
+ const forwardExit = (child) => {
39
+ child.on("exit", (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
40
+ };
41
+ /**
42
+ * Starts the visual editor: the installed `intlayer-editor`, else one of the
43
+ * CLI version run by `bun x` or `npx` (the next runner is tried when one is
44
+ * not installed).
45
+ */
8
46
  const startEditor = (options) => {
9
47
  const args = ["start"];
48
+ const cliVersion = getCliVersion();
49
+ const remoteRunners = getRemoteEditorRunners(cliVersion);
10
50
  if (options.env) args.push("--env", options.env);
11
51
  if (options.envFile) args.push("--env-file", options.envFile);
12
- const spawnNodeProcess = (binPath) => spawn(process.execPath, [binPath, ...args], {
52
+ const spawnInheriting = (command, commandArgs) => spawn(command, commandArgs, {
13
53
  stdio: "inherit",
14
54
  env: { ...process.env }
15
55
  });
16
- const spawnWith = (cmd, cmdArgs) => spawn(cmd, cmdArgs, {
17
- stdio: "inherit",
18
- env: { ...process.env }
19
- });
20
- try {
21
- const pkgJsonPath = (isESModule ? createRequire(import.meta.url) : __require).resolve("intlayer-editor/package.json");
22
- const pkgDir = dirname(pkgJsonPath);
23
- const child = spawnNodeProcess(resolve(pkgDir, "bin", "intlayer-editor.mjs"));
24
- child.on("error", () => runFallback());
25
- child.on("exit", (code) => {
26
- if (code === 255) process.exit(code ?? 0);
27
- });
28
- } catch {
29
- runFallback();
30
- }
31
- function runFallback() {
32
- spawnWith("bun", [
33
- "dlx",
34
- "intlayer-editor",
35
- ...args
36
- ]).on("error", () => {
37
- spawnWith("npx", [
38
- "-y",
39
- "intlayer-editor",
40
- ...args
41
- ]).on("error", (err) => {
42
- console.error("Unable to execute intlayer-editor via bun or npx.");
43
- console.error(String(err?.message ?? err));
44
- process.exit(1);
45
- });
46
- });
56
+ const runRemote = (runnerIndex) => {
57
+ const runner = remoteRunners[runnerIndex];
58
+ if (!runner) {
59
+ console.error("Unable to run intlayer-editor: install it, or install bun or npm.");
60
+ process.exit(1);
61
+ }
62
+ const [command, runnerArgs] = runner;
63
+ const child = spawnInheriting(command, [...runnerArgs, ...args]);
64
+ child.on("error", () => runRemote(runnerIndex + 1));
65
+ forwardExit(child);
66
+ };
67
+ const installedEditor = resolveInstalledEditor();
68
+ if (!installedEditor) {
69
+ runRemote(0);
70
+ return;
47
71
  }
72
+ if (cliVersion && installedEditor.version && installedEditor.version !== cliVersion) console.warn(`intlayer-editor ${installedEditor.version} is installed, but the Intlayer CLI is ${cliVersion}. Align their versions (\`npx intlayer upgrade\`) to avoid editor issues.`);
73
+ const child = spawnInheriting(process.execPath, [installedEditor.binaryPath, ...args]);
74
+ child.on("error", () => runRemote(0));
75
+ forwardExit(child);
48
76
  };
49
77
 
50
- //#endregion
51
- export { startEditor };
78
+ export { getRemoteEditorRunners, startEditor };
52
79
  //# sourceMappingURL=editor.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.mjs","names":["pathDirname","pathResolve"],"sources":["../../src/editor.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport { dirname as pathDirname, resolve as pathResolve } from 'node:path';\nimport { isESModule } from '@intlayer/config/utils';\n\ntype StartEditorOptions = {\n env?: string;\n envFile?: string;\n};\n\nexport const startEditor = (options: StartEditorOptions): void => {\n const args: string[] = ['start'];\n\n if (options.env) args.push('--env', options.env);\n if (options.envFile) args.push('--env-file', options.envFile);\n\n const spawnNodeProcess = (binPath: string) =>\n spawn(process.execPath, [binPath, ...args], {\n stdio: 'inherit',\n env: { ...process.env },\n });\n\n const spawnWith = (cmd: string, cmdArgs: string[]) =>\n spawn(cmd, cmdArgs, {\n stdio: 'inherit',\n env: { ...process.env },\n });\n\n try {\n const requireFunction = isESModule\n ? createRequire(import.meta.url)\n : require;\n const pkgJsonPath = requireFunction.resolve('intlayer-editor/package.json');\n const pkgDir = pathDirname(pkgJsonPath);\n const binPath = pathResolve(pkgDir, 'bin', 'intlayer-editor.mjs');\n\n const child = spawnNodeProcess(binPath);\n child.on('error', () => runFallback());\n child.on('exit', (code) => {\n if (code === 255) process.exit(code ?? 0);\n });\n } catch {\n runFallback();\n }\n\n function runFallback() {\n const bun = spawnWith('bun', ['dlx', 'intlayer-editor', ...args]);\n bun.on('error', () => {\n const npx = spawnWith('npx', ['-y', 'intlayer-editor', ...args]);\n npx.on('error', (err) => {\n console.error('Unable to execute intlayer-editor via bun or npx.');\n console.error(String(err?.message ?? err));\n process.exit(1);\n });\n });\n }\n};\n"],"mappings":";;;;;;;AAUA,MAAa,eAAe,YAAsC;CAChE,MAAM,OAAiB,CAAC,OAAO;CAE/B,IAAI,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,GAAG;CAC/C,IAAI,QAAQ,SAAS,KAAK,KAAK,cAAc,QAAQ,OAAO;CAE5D,MAAM,oBAAoB,YACxB,MAAM,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG;EAC1C,OAAO;EACP,KAAK,EAAE,GAAG,QAAQ,IAAI;CACxB,CAAC;CAEH,MAAM,aAAa,KAAa,YAC9B,MAAM,KAAK,SAAS;EAClB,OAAO;EACP,KAAK,EAAE,GAAG,QAAQ,IAAI;CACxB,CAAC;CAEH,IAAI;EAIF,MAAM,eAHkB,aACpB,cAAc,YAAY,GAAG,cAEE,CAAC,QAAQ,8BAA8B;EAC1E,MAAM,SAASA,QAAY,WAAW;EAGtC,MAAM,QAAQ,iBAFEC,QAAY,QAAQ,OAAO,qBAEN,CAAC;EACtC,MAAM,GAAG,eAAe,YAAY,CAAC;EACrC,MAAM,GAAG,SAAS,SAAS;GACzB,IAAI,SAAS,KAAK,QAAQ,KAAK,QAAQ,CAAC;EAC1C,CAAC;CACH,QAAQ;EACN,YAAY;CACd;CAEA,SAAS,cAAc;EAErB,AADY,UAAU,OAAO;GAAC;GAAO;GAAmB,GAAG;EAAI,CAC7D,CAAC,CAAC,GAAG,eAAe;GAEpB,AADY,UAAU,OAAO;IAAC;IAAM;IAAmB,GAAG;GAAI,CAC5D,CAAC,CAAC,GAAG,UAAU,QAAQ;IACvB,QAAQ,MAAM,mDAAmD;IACjE,QAAQ,MAAM,OAAO,KAAK,WAAW,GAAG,CAAC;IACzC,QAAQ,KAAK,CAAC;GAChB,CAAC;EACH,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"editor.mjs","names":["pathDirname","pathResolve"],"sources":["../../src/editor.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport {\n join,\n dirname as pathDirname,\n resolve as pathResolve,\n} from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { isESModule } from '@intlayer/config/utils';\nimport { getParentPackageJSON } from './utils/getParentPackageJSON';\n\ntype StartEditorOptions = {\n env?: string;\n envFile?: string;\n};\n\n/** Version of this CLI: `intlayer-editor` is released in lockstep with it. */\nconst getCliVersion = (): string | undefined =>\n getParentPackageJSON(\n isESModule ? pathDirname(fileURLToPath(import.meta.url)) : __dirname\n ).version;\n\n/**\n * Package runners trying `intlayer-editor` without it being installed. The\n * editor is pinned to the CLI version, so both speak the same protocol.\n */\nexport const getRemoteEditorRunners = (\n cliVersion: string | undefined\n): [command: string, args: string[]][] => {\n const editorPackage = cliVersion\n ? `intlayer-editor@${cliVersion}`\n : 'intlayer-editor';\n\n return [\n ['bun', ['x', editorPackage]],\n ['npx', ['-y', editorPackage]],\n ];\n};\n\ntype InstalledEditor = {\n /** Path of the `intlayer-editor` binary. */\n binaryPath: string;\n version: string | undefined;\n};\n\n/**\n * The installed `intlayer-editor`: resolved from the project first (the CLI\n * may be hoisted elsewhere in a monorepo), then from the CLI.\n */\nconst resolveInstalledEditor = (): InstalledEditor | undefined => {\n const cliRequire = isESModule ? createRequire(import.meta.url) : require;\n const requireFunctions = [\n createRequire(join(process.cwd(), 'package.json')),\n cliRequire,\n ];\n\n for (const requireFunction of requireFunctions) {\n try {\n const packageJsonPath = requireFunction.resolve(\n 'intlayer-editor/package.json'\n );\n\n const { version } = JSON.parse(readFileSync(packageJsonPath, 'utf8'));\n\n return {\n binaryPath: pathResolve(\n pathDirname(packageJsonPath),\n 'bin',\n 'intlayer-editor.mjs'\n ),\n version,\n };\n } catch {\n // Not resolvable from there\n }\n }\n\n return undefined;\n};\n\n/** Exits with the editor's exit code, so callers notice a failed start. */\nconst forwardExit = (child: ChildProcess): void => {\n child.on('exit', (code, signal) => process.exit(code ?? (signal ? 1 : 0)));\n};\n\n/**\n * Starts the visual editor: the installed `intlayer-editor`, else one of the\n * CLI version run by `bun x` or `npx` (the next runner is tried when one is\n * not installed).\n */\nexport const startEditor = (options: StartEditorOptions): void => {\n const args: string[] = ['start'];\n const cliVersion = getCliVersion();\n const remoteRunners = getRemoteEditorRunners(cliVersion);\n\n if (options.env) args.push('--env', options.env);\n if (options.envFile) args.push('--env-file', options.envFile);\n\n const spawnInheriting = (command: string, commandArgs: string[]) =>\n spawn(command, commandArgs, {\n stdio: 'inherit',\n env: { ...process.env },\n });\n\n const runRemote = (runnerIndex: number): void => {\n const runner = remoteRunners[runnerIndex];\n\n if (!runner) {\n console.error(\n 'Unable to run intlayer-editor: install it, or install bun or npm.'\n );\n process.exit(1);\n }\n\n const [command, runnerArgs] = runner;\n const child = spawnInheriting(command, [...runnerArgs, ...args]);\n\n // `error` only fires when the runner itself cannot be spawned\n child.on('error', () => runRemote(runnerIndex + 1));\n forwardExit(child);\n };\n\n const installedEditor = resolveInstalledEditor();\n\n if (!installedEditor) {\n runRemote(0);\n return;\n }\n\n if (\n cliVersion &&\n installedEditor.version &&\n installedEditor.version !== cliVersion\n ) {\n console.warn(\n `intlayer-editor ${installedEditor.version} is installed, but the ` +\n `Intlayer CLI is ${cliVersion}. Align their versions ` +\n '(`npx intlayer upgrade`) to avoid editor issues.'\n );\n }\n\n const child = spawnInheriting(process.execPath, [\n installedEditor.binaryPath,\n ...args,\n ]);\n\n child.on('error', () => runRemote(0));\n forwardExit(child);\n};\n"],"mappings":";;;;;;;;;;;AAkBA,MAAM,sBACJ,qBACE,aAAaA,QAAY,cAAc,YAAY,GAAG,CAAC,IAAI,SAC7D,CAAC,CAAC;;;;;AAMJ,MAAa,0BACX,eACwC;CACxC,MAAM,gBAAgB,aAClB,mBAAmB,eACnB;CAEJ,OAAO,CACL,CAAC,OAAO,CAAC,KAAK,aAAa,CAAC,GAC5B,CAAC,OAAO,CAAC,MAAM,aAAa,CAAC,CAC/B;AACF;;;;;AAYA,MAAM,+BAA4D;CAChE,MAAM,aAAa,aAAa,cAAc,YAAY,GAAG;CAC7D,MAAM,mBAAmB,CACvB,cAAc,KAAK,QAAQ,IAAI,GAAG,cAAc,CAAC,GACjD,UACF;CAEA,KAAK,MAAM,mBAAmB,kBAC5B,IAAI;EACF,MAAM,kBAAkB,gBAAgB,QACtC,8BACF;EAEA,MAAM,EAAE,YAAY,KAAK,MAAM,aAAa,iBAAiB,MAAM,CAAC;EAEpE,OAAO;GACL,YAAYC,QACVD,QAAY,eAAe,GAC3B,OACA,qBACF;GACA;EACF;CACF,QAAQ,CAER;AAIJ;;AAGA,MAAM,eAAe,UAA8B;CACjD,MAAM,GAAG,SAAS,MAAM,WAAW,QAAQ,KAAK,SAAS,SAAS,IAAI,EAAE,CAAC;AAC3E;;;;;;AAOA,MAAa,eAAe,YAAsC;CAChE,MAAM,OAAiB,CAAC,OAAO;CAC/B,MAAM,aAAa,cAAc;CACjC,MAAM,gBAAgB,uBAAuB,UAAU;CAEvD,IAAI,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,GAAG;CAC/C,IAAI,QAAQ,SAAS,KAAK,KAAK,cAAc,QAAQ,OAAO;CAE5D,MAAM,mBAAmB,SAAiB,gBACxC,MAAM,SAAS,aAAa;EAC1B,OAAO;EACP,KAAK,EAAE,GAAG,QAAQ,IAAI;CACxB,CAAC;CAEH,MAAM,aAAa,gBAA8B;EAC/C,MAAM,SAAS,cAAc;EAE7B,IAAI,CAAC,QAAQ;GACX,QAAQ,MACN,mEACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAEA,MAAM,CAAC,SAAS,cAAc;EAC9B,MAAM,QAAQ,gBAAgB,SAAS,CAAC,GAAG,YAAY,GAAG,IAAI,CAAC;EAG/D,MAAM,GAAG,eAAe,UAAU,cAAc,CAAC,CAAC;EAClD,YAAY,KAAK;CACnB;CAEA,MAAM,kBAAkB,uBAAuB;CAE/C,IAAI,CAAC,iBAAiB;EACpB,UAAU,CAAC;EACX;CACF;CAEA,IACE,cACA,gBAAgB,WAChB,gBAAgB,YAAY,YAE5B,QAAQ,KACN,mBAAmB,gBAAgB,QAAQ,yCACtB,WAAW,0EAElC;CAGF,MAAM,QAAQ,gBAAgB,QAAQ,UAAU,CAC9C,gBAAgB,YAChB,GAAG,IACL,CAAC;CAED,MAAM,GAAG,eAAe,UAAU,CAAC,CAAC;CACpC,YAAY,KAAK;AACnB"}
@@ -1,15 +1,15 @@
1
- import { logConfigDetails } from "@intlayer/engine/cli";
2
- import { existsSync } from "node:fs";
3
- import { relative, resolve } from "node:path";
4
1
  import * as ANSIColors from "@intlayer/config/colors";
5
2
  import { colorize, getAppLogger, x } from "@intlayer/config/logger";
6
3
  import { getConfiguration } from "@intlayer/config/node";
7
- import enquirer from "enquirer";
4
+ import { logConfigDetails } from "@intlayer/engine/cli";
5
+ import { relative, resolve } from "node:path";
6
+ import { existsSync } from "node:fs";
8
7
  import { buildComponentFilesList, formatPath } from "@intlayer/engine/utils";
9
8
  import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
10
9
  import { prepareIntlayer as prepareIntlayer$1 } from "@intlayer/engine/build";
10
+ import enquirer from "enquirer";
11
11
 
12
- //#region src/extract.ts
12
+ // src/extract.ts
13
13
  const extract = async (options) => {
14
14
  const configuration = getConfiguration(options.configOptions);
15
15
  logConfigDetails(options?.configOptions);
@@ -98,6 +98,5 @@ const extract = async (options) => {
98
98
  await prepareIntlayer$1(configuration);
99
99
  };
100
100
 
101
- //#endregion
102
101
  export { extract };
103
102
  //# sourceMappingURL=extract.mjs.map
@@ -1,4 +1,4 @@
1
- //#region src/fill/deepMergeContent.ts
1
+ // src/fill/deepMergeContent.ts
2
2
  /**
3
3
  * Deep merge two plain objects (for per-locale content).
4
4
  * The second object's values take precedence over the first.
@@ -21,6 +21,5 @@ const deepMergeContent = (target, source) => {
21
21
  return result;
22
22
  };
23
23
 
24
- //#endregion
25
24
  export { deepMergeContent };
26
25
  //# sourceMappingURL=deepMergeContent.mjs.map
@@ -1,4 +1,4 @@
1
- //#region src/fill/extractTranslatableContent.ts
1
+ // src/fill/extractTranslatableContent.ts
2
2
  const extractTranslatableContent = (content, currentPath = [], state = {
3
3
  currentIndex: 1,
4
4
  extractedContent: [],
@@ -50,6 +50,5 @@ const reinsertTranslatedContent = (baseContent, extractedContentProps, translate
50
50
  return result;
51
51
  };
52
52
 
53
- //#endregion
54
53
  export { extractTranslatableContent, reinsertTranslatedContent };
55
54
  //# sourceMappingURL=extractTranslatableContent.mjs.map
@@ -3,15 +3,15 @@ import { setupAI } from "../utils/setupAI.mjs";
3
3
  import { listTranslationsTasks } from "./listTranslationsTasks.mjs";
4
4
  import { translateDictionary } from "./translateDictionary.mjs";
5
5
  import { writeFill } from "./writeFill.mjs";
6
- import { logConfigDetails } from "@intlayer/engine/cli";
7
- import { basename, join, relative } from "node:path";
8
6
  import * as ANSIColors from "@intlayer/config/colors";
9
7
  import { colorize, colorizeKey, colorizePath, getAppLogger, x } from "@intlayer/config/logger";
10
8
  import { getConfiguration } from "@intlayer/config/node";
9
+ import { logConfigDetails } from "@intlayer/engine/cli";
10
+ import { basename, join, relative } from "node:path";
11
11
  import { formatPath, getGlobalLimiter, getTaskLimiter } from "@intlayer/engine/utils";
12
12
  import { loadContentDeclarations, prepareIntlayer as prepareIntlayer$1, writeContentDeclaration } from "@intlayer/engine/build";
13
13
 
14
- //#region src/fill/fill.ts
14
+ // src/fill/fill.ts
15
15
  const NB_CONCURRENT_TRANSLATIONS = 7;
16
16
  /**
17
17
  * Fill translations based on the provided options.
@@ -107,6 +107,5 @@ const fill = async (options) => {
107
107
  };
108
108
  };
109
109
 
110
- //#endregion
111
110
  export { fill };
112
111
  //# sourceMappingURL=fill.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fill.mjs","names":["prepareIntlayer"],"sources":["../../../src/fill/fill.ts"],"sourcesContent":["import { basename, join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n loadContentDeclarations,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/engine/build';\nimport {\n type ListGitFilesOptions,\n logConfigDetails,\n} from '@intlayer/engine/cli';\nimport {\n formatPath,\n getGlobalLimiter,\n getTaskLimiter,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport {\n ensureArray,\n type GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { setupAI } from '../utils/setupAI';\nimport {\n listTranslationsTasks,\n type TranslationTask,\n} from './listTranslationsTasks';\nimport { translateDictionary } from './translateDictionary';\nimport { writeFill } from './writeFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 7;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locale;\n outputLocales?: Locale | Locale[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n nbConcurrentTasks?: number; // NEW: number of tasks that may run at once\n build?: boolean;\n skipMetadata?: boolean;\n} & GetTargetDictionaryOptions;\n\n/** Outcome of a {@link fill} run. */\nexport type FillResult =\n | {\n /** No AI provider is reachable: nothing was translated. */\n status: 'no-ai-access';\n /** Why the AI provider could not be used, when known. */\n error?: string;\n }\n | {\n /** No dictionary matched the filters: nothing was translated. */\n status: 'no-dictionaries';\n }\n | {\n status: 'completed';\n /** Keys of the dictionaries processed. */\n dictionaryKeys: string[];\n /** Translation tasks (one per dictionary and locale set) run. */\n taskCount: number;\n /** Tasks whose translation was written. */\n writtenCount: number;\n };\n\n/**\n * Fill translations based on the provided options.\n *\n * @returns What was done, so callers without a terminal (editor extension,\n * MCP) can tell a completed run from one that translated nothing.\n */\nexport const fill = async (options?: FillOptions): Promise<FillResult> => {\n const configuration = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n if (options?.build === true) {\n await prepareIntlayer(configuration, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options?.mode ?? 'complete';\n const baseLocale = options?.sourceLocale ?? defaultLocale;\n\n const outputLocales = options?.outputLocales\n ? ensureArray(options.outputLocales)\n : locales;\n\n const aiResult = await setupAI(configuration, options?.aiOptions);\n\n if (!aiResult?.hasAIAccess) return { status: 'no-ai-access' };\n\n const { aiClient, aiConfig, isCustomAI } = aiResult;\n\n if (isCustomAI && aiClient && aiConfig) {\n const { hasAIAccess, error } = await aiClient.checkAISDKAccess(aiConfig);\n if (!hasAIAccess) {\n appLogger(`${x} ${error}`);\n return { status: 'no-ai-access', error };\n }\n }\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n // Load the original source content declaration files to recover function-type\n // `fill` values that are lost when dictionaries are JSON-serialised into\n // unmerged_dictionaries.cjs. Dictionary-level fill takes priority over the\n // config-level fill, but we can only know that by reading the source files.\n const uniqueSourcePaths = [\n ...new Set(\n targetUnmergedDictionaries\n .filter((unmergedDictionary) =>\n ['local', 'hybrid'].includes(unmergedDictionary.location!)\n )\n .map((unmergedDictionary) => unmergedDictionary.filePath)\n .filter(Boolean) as string[]\n ),\n ];\n const sourceDictionaries = await loadContentDeclarations(\n uniqueSourcePaths.map((sourcePath) =>\n join(configuration.system.baseDir, sourcePath)\n ),\n configuration,\n undefined,\n {\n logError: false,\n }\n );\n // Map relative filePath → original fill value from the source file\n const originalFillByPath = new Map<string, Fill | undefined>();\n\n for (const dictionary of sourceDictionaries) {\n if (dictionary.filePath) {\n originalFillByPath.set(\n dictionary.filePath,\n dictionary.fill as Fill | undefined\n );\n }\n }\n\n const affectedDictionaryKeys = new Set<string>();\n\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n const keysToProcess = Array.from(affectedDictionaryKeys);\n\n appLogger([\n 'Affected dictionary keys for processing:',\n keysToProcess.length > 0\n ? keysToProcess.map((key) => colorizeKey(key)).join(', ')\n : colorize('No keys found', ANSIColors.YELLOW),\n ]);\n\n if (keysToProcess.length === 0) return { status: 'no-dictionaries' };\n\n /**\n * List the translations tasks\n *\n * Create a list of per-locale dictionaries to translate\n *\n * In 'complete' mode, filter only the missing locales to translate\n */\n const translationTasks: TranslationTask[] = listTranslationsTasks(\n targetUnmergedDictionaries.map((dictionary) => dictionary.localId!),\n outputLocales,\n mode,\n baseLocale,\n configuration\n );\n\n // AI calls in flight at once (translateJSON + metadata audit)\n const nbConcurrentTranslations =\n options?.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS;\n const globalLimiter = getGlobalLimiter(nbConcurrentTranslations);\n\n // NEW: number of *tasks* that may run at once (start/prepare/log/write)\n const nbConcurrentTasks = Math.max(\n 1,\n Math.min(\n options?.nbConcurrentTasks ?? nbConcurrentTranslations,\n translationTasks.length\n )\n );\n\n const taskLimiter = getTaskLimiter(nbConcurrentTasks);\n\n let writtenCount = 0;\n\n const runners = translationTasks.map((task) =>\n taskLimiter(async () => {\n const relativePath = relative(\n configuration?.system?.baseDir ?? process.cwd(),\n task?.dictionaryFilePath ?? ''\n );\n\n // log AFTER acquiring a task slot\n appLogger(\n `${task.dictionaryPreset} Processing ${colorizePath(basename(relativePath))}`,\n { level: 'info' }\n );\n\n const translationTaskResult = await translateDictionary(\n task,\n configuration,\n {\n mode,\n aiOptions: options?.aiOptions,\n fillMetadata: !options?.skipMetadata,\n onHandle: globalLimiter,\n aiClient,\n aiConfig,\n }\n );\n\n if (!translationTaskResult?.dictionaryOutput) return;\n\n const { dictionaryOutput, sourceLocale } = translationTaskResult;\n\n // Determine if we should write to separate files\n // - If dictionary has explicit fill setting (string, function, or object), use it\n // - If dictionary is per-locale AND has no explicit fill=false, use global fill config\n // - If dictionary is multilingual (no locale property), always write to same file\n //\n // NOTE: function-type fill values are lost during JSON serialisation of\n // unmerged_dictionaries.cjs. We recover them by checking the original\n // source file that was loaded above (originalFillByPath). Dictionary-level\n // fill always takes priority over config-level fill.\n const originalFill = originalFillByPath.get(\n dictionaryOutput.filePath ?? ''\n );\n\n // originalFill is undefined when the source file had no fill property; use\n // the (possibly JSON-preserved) dictionaryOutput.fill as a fallback so that\n // string/boolean fill values set directly on the dict still work.\n const dictFill: Fill | undefined =\n originalFill !== undefined ? originalFill : dictionaryOutput.fill;\n\n const hasDictionaryLevelFill =\n typeof dictFill === 'string' ||\n typeof dictFill === 'function' ||\n (typeof dictFill === 'object' && dictFill !== null);\n\n const isPerLocale = typeof dictionaryOutput.locale === 'string';\n\n const effectiveFill = hasDictionaryLevelFill\n ? dictFill\n : isPerLocale\n ? (configuration.dictionary?.fill ?? true)\n : false; // Multilingual dictionaries (no locale property) always write to same file unless explicitly set on the dictionary\n\n const isFillOtherFile =\n typeof effectiveFill === 'string' ||\n typeof effectiveFill === 'function' ||\n (typeof effectiveFill === 'object' && effectiveFill !== null);\n\n if (isFillOtherFile) {\n await writeFill(\n {\n ...dictionaryOutput,\n // Ensure fill is set on the dictionary for writeFill to use\n fill: effectiveFill,\n },\n outputLocales,\n [sourceLocale],\n configuration\n );\n writtenCount++;\n } else {\n await writeContentDeclaration(dictionaryOutput, configuration);\n writtenCount++;\n\n if (dictionaryOutput.filePath) {\n appLogger(\n `${task.dictionaryPreset} Content declaration written to ${formatPath(basename(dictionaryOutput.filePath))}`,\n { level: 'info' }\n );\n }\n }\n })\n );\n\n await Promise.all(runners);\n await (globalLimiter as any).onIdle();\n\n return {\n status: 'completed',\n dictionaryKeys: keysToProcess,\n taskCount: translationTasks.length,\n writtenCount,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"fill.mjs","names":["prepareIntlayer"],"sources":["../../../src/fill/fill.ts"],"sourcesContent":["import { basename, join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/ai';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n x,\n} from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport {\n loadContentDeclarations,\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/engine/build';\nimport {\n type ListGitFilesOptions,\n logConfigDetails,\n} from '@intlayer/engine/cli';\nimport {\n formatPath,\n getGlobalLimiter,\n getTaskLimiter,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { Fill } from '@intlayer/types/dictionary';\nimport {\n ensureArray,\n type GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { setupAI } from '../utils/setupAI';\nimport {\n listTranslationsTasks,\n type TranslationTask,\n} from './listTranslationsTasks';\nimport { translateDictionary } from './translateDictionary';\nimport { writeFill } from './writeFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 7;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locale;\n outputLocales?: Locale | Locale[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n nbConcurrentTasks?: number; // NEW: number of tasks that may run at once\n build?: boolean;\n skipMetadata?: boolean;\n} & GetTargetDictionaryOptions;\n\n/** Outcome of a {@link fill} run. */\nexport type FillResult =\n | {\n /** No AI provider is reachable: nothing was translated. */\n status: 'no-ai-access';\n /** Why the AI provider could not be used, when known. */\n error?: string;\n }\n | {\n /** No dictionary matched the filters: nothing was translated. */\n status: 'no-dictionaries';\n }\n | {\n status: 'completed';\n /** Keys of the dictionaries processed. */\n dictionaryKeys: string[];\n /** Translation tasks (one per dictionary and locale set) run. */\n taskCount: number;\n /** Tasks whose translation was written. */\n writtenCount: number;\n };\n\n/**\n * Fill translations based on the provided options.\n *\n * @returns What was done, so callers without a terminal (editor extension,\n * MCP) can tell a completed run from one that translated nothing.\n */\nexport const fill = async (options?: FillOptions): Promise<FillResult> => {\n const configuration = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n if (options?.build === true) {\n await prepareIntlayer(configuration, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options?.mode ?? 'complete';\n const baseLocale = options?.sourceLocale ?? defaultLocale;\n\n const outputLocales = options?.outputLocales\n ? ensureArray(options.outputLocales)\n : locales;\n\n const aiResult = await setupAI(configuration, options?.aiOptions);\n\n if (!aiResult?.hasAIAccess) return { status: 'no-ai-access' };\n\n const { aiClient, aiConfig, isCustomAI } = aiResult;\n\n if (isCustomAI && aiClient && aiConfig) {\n const { hasAIAccess, error } = await aiClient.checkAISDKAccess(aiConfig);\n if (!hasAIAccess) {\n appLogger(`${x} ${error}`);\n return { status: 'no-ai-access', error };\n }\n }\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n // Load the original source content declaration files to recover function-type\n // `fill` values that are lost when dictionaries are JSON-serialised into\n // unmerged_dictionaries.cjs. Dictionary-level fill takes priority over the\n // config-level fill, but we can only know that by reading the source files.\n const uniqueSourcePaths = [\n ...new Set(\n targetUnmergedDictionaries\n .filter((unmergedDictionary) =>\n ['local', 'hybrid'].includes(unmergedDictionary.location!)\n )\n .map((unmergedDictionary) => unmergedDictionary.filePath)\n .filter(Boolean) as string[]\n ),\n ];\n const sourceDictionaries = await loadContentDeclarations(\n uniqueSourcePaths.map((sourcePath) =>\n join(configuration.system.baseDir, sourcePath)\n ),\n configuration,\n undefined,\n {\n logError: false,\n }\n );\n // Map relative filePath → original fill value from the source file\n const originalFillByPath = new Map<string, Fill | undefined>();\n\n for (const dictionary of sourceDictionaries) {\n if (dictionary.filePath) {\n originalFillByPath.set(\n dictionary.filePath,\n dictionary.fill as Fill | undefined\n );\n }\n }\n\n const affectedDictionaryKeys = new Set<string>();\n\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n const keysToProcess = Array.from(affectedDictionaryKeys);\n\n appLogger([\n 'Affected dictionary keys for processing:',\n keysToProcess.length > 0\n ? keysToProcess.map((key) => colorizeKey(key)).join(', ')\n : colorize('No keys found', ANSIColors.YELLOW),\n ]);\n\n if (keysToProcess.length === 0) return { status: 'no-dictionaries' };\n\n /**\n * List the translations tasks\n *\n * Create a list of per-locale dictionaries to translate\n *\n * In 'complete' mode, filter only the missing locales to translate\n */\n const translationTasks: TranslationTask[] = listTranslationsTasks(\n targetUnmergedDictionaries.map((dictionary) => dictionary.localId!),\n outputLocales,\n mode,\n baseLocale,\n configuration\n );\n\n // AI calls in flight at once (translateJSON + metadata audit)\n const nbConcurrentTranslations =\n options?.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS;\n const globalLimiter = getGlobalLimiter(nbConcurrentTranslations);\n\n // NEW: number of *tasks* that may run at once (start/prepare/log/write)\n const nbConcurrentTasks = Math.max(\n 1,\n Math.min(\n options?.nbConcurrentTasks ?? nbConcurrentTranslations,\n translationTasks.length\n )\n );\n\n const taskLimiter = getTaskLimiter(nbConcurrentTasks);\n\n let writtenCount = 0;\n\n const runners = translationTasks.map((task) =>\n taskLimiter(async () => {\n const relativePath = relative(\n configuration?.system?.baseDir ?? process.cwd(),\n task?.dictionaryFilePath ?? ''\n );\n\n // log AFTER acquiring a task slot\n appLogger(\n `${task.dictionaryPreset} Processing ${colorizePath(basename(relativePath))}`,\n { level: 'info' }\n );\n\n const translationTaskResult = await translateDictionary(\n task,\n configuration,\n {\n mode,\n aiOptions: options?.aiOptions,\n fillMetadata: !options?.skipMetadata,\n onHandle: globalLimiter,\n aiClient,\n aiConfig,\n }\n );\n\n if (!translationTaskResult?.dictionaryOutput) return;\n\n const { dictionaryOutput, sourceLocale } = translationTaskResult;\n\n // Determine if we should write to separate files\n // - If dictionary has explicit fill setting (string, function, or object), use it\n // - If dictionary is per-locale AND has no explicit fill=false, use global fill config\n // - If dictionary is multilingual (no locale property), always write to same file\n //\n // NOTE: function-type fill values are lost during JSON serialisation of\n // unmerged_dictionaries.cjs. We recover them by checking the original\n // source file that was loaded above (originalFillByPath). Dictionary-level\n // fill always takes priority over config-level fill.\n const originalFill = originalFillByPath.get(\n dictionaryOutput.filePath ?? ''\n );\n\n // originalFill is undefined when the source file had no fill property; use\n // the (possibly JSON-preserved) dictionaryOutput.fill as a fallback so that\n // string/boolean fill values set directly on the dict still work.\n const dictFill: Fill | undefined =\n originalFill !== undefined ? originalFill : dictionaryOutput.fill;\n\n const hasDictionaryLevelFill =\n typeof dictFill === 'string' ||\n typeof dictFill === 'function' ||\n (typeof dictFill === 'object' && dictFill !== null);\n\n const isPerLocale = typeof dictionaryOutput.locale === 'string';\n\n const effectiveFill = hasDictionaryLevelFill\n ? dictFill\n : isPerLocale\n ? (configuration.dictionary?.fill ?? true)\n : false; // Multilingual dictionaries (no locale property) always write to same file unless explicitly set on the dictionary\n\n const isFillOtherFile =\n typeof effectiveFill === 'string' ||\n typeof effectiveFill === 'function' ||\n (typeof effectiveFill === 'object' && effectiveFill !== null);\n\n if (isFillOtherFile) {\n await writeFill(\n {\n ...dictionaryOutput,\n // Ensure fill is set on the dictionary for writeFill to use\n fill: effectiveFill,\n },\n outputLocales,\n [sourceLocale],\n configuration\n );\n writtenCount++;\n } else {\n await writeContentDeclaration(dictionaryOutput, configuration);\n writtenCount++;\n\n if (dictionaryOutput.filePath) {\n appLogger(\n `${task.dictionaryPreset} Content declaration written to ${formatPath(basename(dictionaryOutput.filePath))}`,\n { level: 'info' }\n );\n }\n }\n })\n );\n\n await Promise.all(runners);\n await (globalLimiter as any).onIdle();\n\n return {\n status: 'completed',\n dictionaryKeys: keysToProcess,\n taskCount: translationTasks.length,\n writtenCount,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;AAwCA,MAAM,6BAA6B;;;;;;;AA4CnC,MAAa,OAAO,OAAO,YAA+C;CACxE,MAAM,gBAAgB,iBAAiB,SAAS,aAAa;CAC7D,iBAAiB,SAAS,aAAa;CAEvC,MAAM,YAAY,aAAa,aAAa;CAE5C,IAAI,SAAS,UAAU,MACrB,MAAMA,kBAAgB,eAAe,EAAE,UAAU,KAAK,CAAC;MAClD,IAAI,OAAO,SAAS,UAAU,aACnC,MAAMA,kBAAgB,aAAa;CAGrC,MAAM,EAAE,eAAe,YAAY,cAAc;CACjD,MAAM,OAAO,SAAS,QAAQ;CAC9B,MAAM,aAAa,SAAS,gBAAgB;CAE5C,MAAM,gBAAgB,SAAS,gBAC3B,YAAY,QAAQ,aAAa,IACjC;CAEJ,MAAM,WAAW,MAAM,QAAQ,eAAe,SAAS,SAAS;CAEhE,IAAI,CAAC,UAAU,aAAa,OAAO,EAAE,QAAQ,eAAe;CAE5D,MAAM,EAAE,UAAU,UAAU,eAAe;CAE3C,IAAI,cAAc,YAAY,UAAU;EACtC,MAAM,EAAE,aAAa,UAAU,MAAM,SAAS,iBAAiB,QAAQ;EACvE,IAAI,CAAC,aAAa;GAChB,UAAU,GAAG,EAAE,GAAG,OAAO;GACzB,OAAO;IAAE,QAAQ;IAAgB;GAAM;EACzC;CACF;CAEA,MAAM,6BACJ,MAAM,8BAA8B,OAAO;CAM7C,MAAM,oBAAoB,CACxB,GAAG,IAAI,IACL,2BACG,QAAQ,uBACP,CAAC,SAAS,QAAQ,CAAC,CAAC,SAAS,mBAAmB,QAAS,CAC3D,CAAC,CACA,KAAK,uBAAuB,mBAAmB,QAAQ,CAAC,CACxD,OAAO,OAAO,CACnB,CACF;CACA,MAAM,qBAAqB,MAAM,wBAC/B,kBAAkB,KAAK,eACrB,KAAK,cAAc,OAAO,SAAS,UAAU,CAC/C,GACA,eACA,QACA,EACE,UAAU,MACZ,CACF;CAEA,MAAM,qCAAqB,IAAI,IAA8B;CAE7D,KAAK,MAAM,cAAc,oBACvB,IAAI,WAAW,UACb,mBAAmB,IACjB,WAAW,UACX,WAAW,IACb;CAIJ,MAAM,yCAAyB,IAAI,IAAY;CAE/C,2BAA2B,SAAS,SAAS;EAC3C,uBAAuB,IAAI,KAAK,GAAG;CACrC,CAAC;CAED,MAAM,gBAAgB,MAAM,KAAK,sBAAsB;CAEvD,UAAU,CACR,4CACA,cAAc,SAAS,IACnB,cAAc,KAAK,QAAQ,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,IACtD,SAAS,iBAAiB,WAAW,MAAM,CACjD,CAAC;CAED,IAAI,cAAc,WAAW,GAAG,OAAO,EAAE,QAAQ,kBAAkB;;;;;;;;CASnE,MAAM,mBAAsC,sBAC1C,2BAA2B,KAAK,eAAe,WAAW,OAAQ,GAClE,eACA,MACA,YACA,aACF;CAGA,MAAM,2BACJ,SAAS,4BAA4B;CACvC,MAAM,gBAAgB,iBAAiB,wBAAwB;CAG/D,MAAM,oBAAoB,KAAK,IAC7B,GACA,KAAK,IACH,SAAS,qBAAqB,0BAC9B,iBAAiB,MACnB,CACF;CAEA,MAAM,cAAc,eAAe,iBAAiB;CAEpD,IAAI,eAAe;CAEnB,MAAM,UAAU,iBAAiB,KAAK,SACpC,YAAY,YAAY;EACtB,MAAM,eAAe,SACnB,eAAe,QAAQ,WAAW,QAAQ,IAAI,GAC9C,MAAM,sBAAsB,EAC9B;EAGA,UACE,GAAG,KAAK,iBAAiB,cAAc,aAAa,SAAS,YAAY,CAAC,KAC1E,EAAE,OAAO,OAAO,CAClB;EAEA,MAAM,wBAAwB,MAAM,oBAClC,MACA,eACA;GACE;GACA,WAAW,SAAS;GACpB,cAAc,CAAC,SAAS;GACxB,UAAU;GACV;GACA;EACF,CACF;EAEA,IAAI,CAAC,uBAAuB,kBAAkB;EAE9C,MAAM,EAAE,kBAAkB,iBAAiB;EAW3C,MAAM,eAAe,mBAAmB,IACtC,iBAAiB,YAAY,EAC/B;EAKA,MAAM,WACJ,iBAAiB,SAAY,eAAe,iBAAiB;EAE/D,MAAM,yBACJ,OAAO,aAAa,YACpB,OAAO,aAAa,cACnB,OAAO,aAAa,YAAY,aAAa;EAEhD,MAAM,cAAc,OAAO,iBAAiB,WAAW;EAEvD,MAAM,gBAAgB,yBAClB,WACA,cACG,cAAc,YAAY,QAAQ,OACnC;EAON,IAJE,OAAO,kBAAkB,YACzB,OAAO,kBAAkB,cACxB,OAAO,kBAAkB,YAAY,kBAAkB,MAErC;GACnB,MAAM,UACJ;IACE,GAAG;IAEH,MAAM;GACR,GACA,eACA,CAAC,YAAY,GACb,aACF;GACA;EACF,OAAO;GACL,MAAM,wBAAwB,kBAAkB,aAAa;GAC7D;GAEA,IAAI,iBAAiB,UACnB,UACE,GAAG,KAAK,iBAAiB,kCAAkC,WAAW,SAAS,iBAAiB,QAAQ,CAAC,KACzG,EAAE,OAAO,OAAO,CAClB;EAEJ;CACF,CAAC,CACH;CAEA,MAAM,QAAQ,IAAI,OAAO;CACzB,MAAO,cAAsB,OAAO;CAEpC,OAAO;EACL,QAAQ;EACR,gBAAgB;EAChB,WAAW,iBAAiB;EAC5B;CACF;AACF"}
@@ -1,10 +1,10 @@
1
- import { basename, dirname, isAbsolute, normalize, resolve } from "node:path";
2
1
  import { GREY_DARK } from "@intlayer/config/colors";
3
2
  import { colorize, colorizePath } from "@intlayer/config/logger";
3
+ import { basename, dirname, isAbsolute, normalize, resolve } from "node:path";
4
4
  import { getFormatFromExtension } from "@intlayer/engine/utils";
5
5
  import { parseFilePathPattern } from "@intlayer/config/utils";
6
6
 
7
- //#region src/fill/formatAutoFilledFilePath.ts
7
+ // src/fill/formatAutoFilledFilePath.ts
8
8
  const formatAutoFilledFilePath = async (autoFillField, dictionaryKey, dictionaryFilePath, baseDir, configuration, locale) => {
9
9
  if (!autoFillField) throw new Error("autoFillField must be provided");
10
10
  if (!dictionaryKey || typeof dictionaryKey !== "string") throw new Error("dictionaryKey must be a non-empty string");
@@ -40,6 +40,5 @@ const formatAutoFilledFilePath = async (autoFillField, dictionaryKey, dictionary
40
40
  return normalize(result);
41
41
  };
42
42
 
43
- //#endregion
44
43
  export { formatAutoFilledFilePath };
45
44
  //# sourceMappingURL=formatAutoFilledFilePath.mjs.map
@@ -2,7 +2,7 @@ import { getPatternForLocale, resolveOutputPattern } from "../utils/getOutputFil
2
2
  import { basename, dirname, extname, relative } from "node:path";
3
3
  import { getFormatFromExtension } from "@intlayer/engine/utils";
4
4
 
5
- //#region src/fill/formatFillData.ts
5
+ // src/fill/formatFillData.ts
6
6
  /** Merge FillData entries that resolve to the same file path. */
7
7
  const groupByFilePath = (entries) => entries.reduce((acc, curr) => {
8
8
  const existing = acc.find((item) => item.filePath === curr.filePath);
@@ -89,6 +89,5 @@ const formatFillData = async (fillField, localeList, filePath, dictionaryKey, co
89
89
  return processPattern(fillField, localeList);
90
90
  };
91
91
 
92
- //#endregion
93
92
  export { formatFillData };
94
93
  //# sourceMappingURL=formatFillData.mjs.map
@@ -1,4 +1,4 @@
1
- //#region src/fill/getAvailableLocalesInDictionary.ts
1
+ // src/fill/getAvailableLocalesInDictionary.ts
2
2
  /**
3
3
  * Recursively traverses dictionary content to find all locales that have actual translations.
4
4
  * Returns a Set of locale strings that are present in the translation nodes.
@@ -20,6 +20,5 @@ const getAvailableLocalesInDictionary = (dictionary) => {
20
20
  return Array.from(localesSet);
21
21
  };
22
22
 
23
- //#endregion
24
23
  export { getAvailableLocalesInDictionary };
25
24
  //# sourceMappingURL=getAvailableLocalesInDictionary.mjs.map
@@ -1,4 +1,4 @@
1
- //#region src/fill/getFilterMissingContentPerLocale.ts
1
+ // src/fill/getFilterMissingContentPerLocale.ts
2
2
  /**
3
3
  * Recursively compares source content with target content and returns only the missing parts.
4
4
  * For per-locale files where content is simple JSON (not translation nodes).
@@ -45,6 +45,5 @@ const getFilterMissingContentPerLocale = (sourceDictionary, targetDictionary) =>
45
45
  };
46
46
  };
47
47
 
48
- //#endregion
49
48
  export { getFilterMissingContentPerLocale };
50
49
  //# sourceMappingURL=getFilterMissingContentPerLocale.mjs.map
@@ -1,4 +1,4 @@
1
- import { writeFill } from "./writeFill.mjs";
2
1
  import { fill } from "./fill.mjs";
2
+ import { writeFill } from "./writeFill.mjs";
3
3
 
4
4
  export { fill, writeFill };
@@ -1,13 +1,13 @@
1
1
  import { listMissingTranslationsWithConfig } from "../test/listMissingTranslations.mjs";
2
- import { basename } from "node:path";
3
2
  import * as ANSIColors from "@intlayer/config/colors";
4
3
  import { colon, colorize, colorizeKey, colorizePath, getAppLogger } from "@intlayer/config/logger";
4
+ import { basename } from "node:path";
5
5
  import { formatLocale } from "@intlayer/engine/utils";
6
6
  import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
7
7
  import { getFilterTranslationsOnlyDictionary } from "@intlayer/core/plugins";
8
8
  import { getDictionaries } from "@intlayer/dictionaries-entry";
9
9
 
10
- //#region src/fill/listTranslationsTasks.ts
10
+ // src/fill/listTranslationsTasks.ts
11
11
  const listTranslationsTasks = (localIds, outputLocales, mode, baseLocale, configuration) => {
12
12
  const appLogger = getAppLogger(configuration);
13
13
  const mergedDictionariesRecord = getDictionaries(configuration);
@@ -65,6 +65,5 @@ const listTranslationsTasks = (localIds, outputLocales, mode, baseLocale, config
65
65
  return translationTasks;
66
66
  };
67
67
 
68
- //#endregion
69
68
  export { listTranslationsTasks };
70
69
  //# sourceMappingURL=listTranslationsTasks.mjs.map
@@ -1,15 +1,15 @@
1
1
  import { getAuthenticatedAPI } from "../utils/checkAccess.mjs";
2
2
  import { deepMergeContent } from "./deepMergeContent.mjs";
3
3
  import { extractTranslatableContent, reinsertTranslatedContent } from "./extractTranslatableContent.mjs";
4
- import { basename } from "node:path";
5
4
  import * as ANSIColors from "@intlayer/config/colors";
6
5
  import { colon, colorize, colorizeNumber, colorizePath, getAppLogger } from "@intlayer/config/logger";
6
+ import { basename } from "node:path";
7
7
  import { chunkJSON, excludeObjectFormat, formatLocale, mergeChunks, reconstructFromSingleChunk, verifyIdenticObjectFormat } from "@intlayer/engine/utils";
8
8
  import { retryManager } from "@intlayer/config/utils";
9
9
  import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
10
10
  import { getFilterMissingTranslationsDictionary, getMultilingualDictionary, getPerLocaleDictionary, insertContentInDictionary } from "@intlayer/core/plugins";
11
11
 
12
- //#region src/fill/translateDictionary.ts
12
+ // src/fill/translateDictionary.ts
13
13
  const createChunkPreset = (chunkIndex, totalChunks) => {
14
14
  if (totalChunks <= 1) return "";
15
15
  return colon([
@@ -212,6 +212,5 @@ const translateDictionary = async (task, configuration, options) => {
212
212
  })();
213
213
  };
214
214
 
215
- //#endregion
216
215
  export { translateDictionary };
217
216
  //# sourceMappingURL=translateDictionary.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"translateDictionary.mjs","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig } from '@intlayer/ai';\nimport type { AIOptions } from '@intlayer/api';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport { getUnmergedDictionaries } from '@intlayer/dictionaries-entry/unmerged';\nimport {\n chunkJSON,\n excludeObjectFormat,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n verifyIdenticObjectFormat,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getAuthenticatedAPI } from '../utils/checkAccess';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport {\n extractTranslatableContent,\n reinsertTranslatedContent,\n} from './extractTranslatableContent';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/engine/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst createChunkPreset = (chunkIndex: number, totalChunks: number) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\nconst serializeError = (error: unknown): string => {\n if (error instanceof Error) {\n return error.cause\n ? `${error.message} (cause: ${String(error.cause)})`\n : error.message;\n }\n if (typeof error === 'string') return error;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n};\n\nconst CHUNK_SIZE = 4000;\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = await getAuthenticatedAPI(configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Look up the target locale dictionary by its locale property.\n // This handles both directory-based paths (/en/file.json → /es/file.json)\n // AND filename-based paths (messages_ICU/en.json → messages_ICU/es.json).\n // Plugins like syncJSON set `dict.locale` correctly, so matching by locale\n // is the only reliable strategy — path string-replacement fails when the\n // locale appears in the filename rather than a directory segment.\n const targetUnmergedDictionary = unmergedDictionariesRecord[\n task.dictionaryKey\n ]?.find(\n (dict) =>\n dict.locale === targetLocale &&\n dict.filePath !== baseUnmergedDictionary.filePath\n );\n\n // Derive the target file path for the fallback (when the file doesn't exist yet).\n // Try directory-style first (/en/ → /es/), then filename-style (en.json → es.json).\n const sourceFilePath = baseUnmergedDictionary.filePath ?? '';\n const derivedTargetFilePath = sourceFilePath.includes(\n `/${task.sourceLocale}/`\n )\n ? sourceFilePath.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n )\n : sourceFilePath.replace(\n new RegExp(`(^|/)${task.sourceLocale}(\\\\.[^/]+)$`),\n `$1${targetLocale}$2`\n );\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: derivedTargetFilePath || undefined,\n locale: targetLocale,\n };\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n // Filter to only untranslated fields, preserving explicit null values as\n // default-locale fallback markers. Applied after both paths converge so\n // the same logic covers per-locale and multilingual dictionaries.\n if (mode === 'complete') {\n dictionaryToProcess = {\n ...dictionaryToProcess,\n content:\n excludeObjectFormat(\n dictionaryToProcess.content,\n targetLocaleDictionary.content\n ) ?? {},\n };\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n dictionaryToProcess.content as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map(async (chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n const reconstructedChunk = reconstructFromSingleChunk(chunk);\n const {\n extractedContent: chunkExtractedContent,\n translatableDictionary: chunkTranslatableDictionary,\n } = extractTranslatableContent(reconstructedChunk);\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic, error } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkTranslatableDictionary\n );\n\n if (!isIdentic) {\n throw new Error(\n `Translation result does not match expected format: ${error}`\n );\n }\n\n notifySuccess();\n return reinsertTranslatedContent(\n reconstructedChunk,\n chunkExtractedContent,\n translationResult.fileContent as Record<number, string>\n );\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge translated chunk contents back into a single content object\n const reinsertedContent = mergeChunks(chunkResult);\n\n const merged = {\n ...dictionaryToProcess,\n content: reinsertedContent,\n };\n\n // Merge newly translated content (including explicit null fallbacks) back\n // into the existing target locale content. Applies to both per-locale and\n // multilingual paths so the target always retains previously translated\n // fields and receives null markers where the source has no translation.\n const finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n merged.content\n );\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n // The dict-level `fill` value may have been lost during JSON serialisation\n // (functions can't be serialised to JSON). Fall back to the config-level\n // fill so that an explicit fill in intlayer.config.ts is honoured.\n const effectiveFillForCheck =\n baseUnmergedDictionary.fill ?? configuration.dictionary?.fill;\n\n if (\n baseUnmergedDictionary.locale &&\n (effectiveFillForCheck === true ||\n effectiveFillForCheck === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"translateDictionary.mjs","names":[],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig, AIOptions } from '@intlayer/ai';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport { getUnmergedDictionaries } from '@intlayer/dictionaries-entry/unmerged';\nimport {\n chunkJSON,\n excludeObjectFormat,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n verifyIdenticObjectFormat,\n} from '@intlayer/engine/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getAuthenticatedAPI } from '../utils/checkAccess';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport {\n extractTranslatableContent,\n reinsertTranslatedContent,\n} from './extractTranslatableContent';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/engine/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst createChunkPreset = (chunkIndex: number, totalChunks: number) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\nconst serializeError = (error: unknown): string => {\n if (error instanceof Error) {\n return error.cause\n ? `${error.message} (cause: ${String(error.cause)})`\n : error.message;\n }\n if (typeof error === 'string') return error;\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n};\n\nconst CHUNK_SIZE = 4000;\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = await getAuthenticatedAPI(configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Look up the target locale dictionary by its locale property.\n // This handles both directory-based paths (/en/file.json → /es/file.json)\n // AND filename-based paths (messages_ICU/en.json → messages_ICU/es.json).\n // Plugins like syncJSON set `dict.locale` correctly, so matching by locale\n // is the only reliable strategy — path string-replacement fails when the\n // locale appears in the filename rather than a directory segment.\n const targetUnmergedDictionary = unmergedDictionariesRecord[\n task.dictionaryKey\n ]?.find(\n (dict) =>\n dict.locale === targetLocale &&\n dict.filePath !== baseUnmergedDictionary.filePath\n );\n\n // Derive the target file path for the fallback (when the file doesn't exist yet).\n // Try directory-style first (/en/ → /es/), then filename-style (en.json → es.json).\n const sourceFilePath = baseUnmergedDictionary.filePath ?? '';\n const derivedTargetFilePath = sourceFilePath.includes(\n `/${task.sourceLocale}/`\n )\n ? sourceFilePath.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n )\n : sourceFilePath.replace(\n new RegExp(`(^|/)${task.sourceLocale}(\\\\.[^/]+)$`),\n `$1${targetLocale}$2`\n );\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: derivedTargetFilePath || undefined,\n locale: targetLocale,\n };\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n // Filter to only untranslated fields, preserving explicit null values as\n // default-locale fallback markers. Applied after both paths converge so\n // the same logic covers per-locale and multilingual dictionaries.\n if (mode === 'complete') {\n dictionaryToProcess = {\n ...dictionaryToProcess,\n content:\n excludeObjectFormat(\n dictionaryToProcess.content,\n targetLocaleDictionary.content\n ) ?? {},\n };\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n dictionaryToProcess.content as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map(async (chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n const reconstructedChunk = reconstructFromSingleChunk(chunk);\n const {\n extractedContent: chunkExtractedContent,\n translatableDictionary: chunkTranslatableDictionary,\n } = extractTranslatableContent(reconstructedChunk);\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent:\n chunkTranslatableDictionary as unknown as JSON,\n presetOutputContent: chunkTranslatableDictionary,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent: chunkTranslatableDictionary,\n presetOutputContent: chunkTranslatableDictionary,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic, error } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkTranslatableDictionary\n );\n\n if (!isIdentic) {\n throw new Error(\n `Translation result does not match expected format: ${error}`\n );\n }\n\n notifySuccess();\n return reinsertTranslatedContent(\n reconstructedChunk,\n chunkExtractedContent,\n translationResult.fileContent as Record<number, string>\n );\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge translated chunk contents back into a single content object\n const reinsertedContent = mergeChunks(chunkResult);\n\n const merged = {\n ...dictionaryToProcess,\n content: reinsertedContent,\n };\n\n // Merge newly translated content (including explicit null fallbacks) back\n // into the existing target locale content. Applies to both per-locale and\n // multilingual paths so the target always retains previously translated\n // fields and receives null markers where the source has no translation.\n const finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n merged.content\n );\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n // The dict-level `fill` value may have been lost during JSON serialisation\n // (functions can't be serialised to JSON). Fall back to the config-level\n // fill so that an explicit fill in intlayer.config.ts is honoured.\n const effectiveFillForCheck =\n baseUnmergedDictionary.fill ?? configuration.dictionary?.fill;\n\n if (\n baseUnmergedDictionary.locale &&\n (effectiveFillForCheck === true ||\n effectiveFillForCheck === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(serializeError(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":";;;;;;;;;;;;AAyDA,MAAM,qBAAqB,YAAoB,gBAAwB;CACrE,IAAI,eAAe,GAAG,OAAO;CAC7B,OAAO,MACL;EACE,SAAS,KAAK,WAAW,SAAS;EAClC,eAAe,aAAa,CAAC;EAC7B,SAAS,IAAI,eAAe,WAAW,SAAS;EAChD,SAAS,KAAK,WAAW,SAAS;CACpC,CAAC,CAAC,KAAK,EAAE,GACT,EAAE,SAAS,EAAE,CACf;AACF;AAEA,MAAM,sBAAsB,eAC1B,CAAC,WAAW,eAAe,CAAC,WAAW,SAAS,CAAC,WAAW;AAE9D,MAAM,kBAAkB,UAA2B;CACjD,IAAI,iBAAiB,OACnB,OAAO,MAAM,QACT,GAAG,MAAM,QAAQ,WAAW,OAAO,MAAM,KAAK,EAAE,KAChD,MAAM;CAEZ,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI;EACF,OAAO,KAAK,UAAU,KAAK;CAC7B,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF;AAEA,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,YAAY;AAClB,MAAM,cAAc;AAEpB,MAAM,uBAAuB;AAC7B,IAAI,kBAAkB;AAEtB,MAAa,sBAAsB,OACjC,MACA,eACA,YACuC;CACvC,MAAM,YAAY,aAAa,aAAa;CAC5C,MAAM,cAAc,MAAM,oBAAoB,aAAa;CAE3D,MAAM,EAAE,MAAM,WAAW,cAAc,UAAU,aAAa;EAC5D,MAAM;EACN,cAAc;EACd,GAAG;CACL;CAEA,MAAM,sBAAsB;EAC1B,kBAAkB;EAClB,SAAS,YAAY;CACvB;CAyaA,OAAO,MAvac,aACnB,YAAY;EACV,MAAM,6BAA6B,wBAAwB,aAAa;EAExE,MAAM,yBACJ,2BAA2B,KAAK,cAAc,EAAE,MAC7C,SAAS,KAAK,YAAY,KAAK,iBAClC;EAEF,IAAI,CAAC,wBAAwB;GAC3B,UACE,GAAG,KAAK,iBAAiB,gEACzB,EACE,OAAO,OACT,CACF;GACA,OAAO;IAAE,GAAG;IAAM,kBAAkB;GAAK;EAC3C;EAEA,IAAI;EAIJ,IACE,iBACC,mBAAmB,sBAAsB,KAAK,SAAS,WACxD;GACA,MAAM,0BAA0B,uBAC9B,wBACA,cAAc,qBAAqB,aACrC;GAEA,UACE,GAAG,KAAK,iBAAiB,gCAAgC,aAAa,SAAS,uBAAuB,QAAS,CAAC,KAChH,EACE,OAAO,OACT,CACF;GAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,YAAY,UAMd,OAAO,EACL,MAAM,MANa,SAAS,wBAAwB;KACpD,aAAa,KAAK,UAAU,uBAAuB;KACnD;IACF,CAAC,EAID;IAGF,OAAO,MAAM,YAAY,GAAG,gCAAgC;KAC1D,aAAa,KAAK,UAAU,uBAAuB;KACnD;IACF,CAAC;GACH;GAMA,YAJuB,SAAS,WAC5B,MAAM,QAAQ,SAAS,QAAQ,IAC/B,MAAM,SAAS,EAEM,CAAC,MAAM;EAClC;EAEA,MAAM,2BAA2B,MAAM,QAAQ,IAC7C,KAAK,cAAc,IAAI,OAAO,iBAAiB;;;;;;;;;;GAW7C,IAAI,sBAAsB,gBAAgB,sBAAsB;GAEhE,IAAI;GAEJ,IAAI,OAAO,uBAAuB,WAAW,UAAU;IAUrD,MAAM,2BAA2B,2BAC/B,KAAK,cACN,EAAE,MACA,SACC,KAAK,WAAW,gBAChB,KAAK,aAAa,uBAAuB,QAC7C;IAIA,MAAM,iBAAiB,uBAAuB,YAAY;IAC1D,MAAM,wBAAwB,eAAe,SAC3C,IAAI,KAAK,aAAa,EACxB,IACI,eAAe,QACb,IAAI,OAAO,IAAI,KAAK,aAAa,IAAI,GAAG,GACxC,IAAI,aAAa,EACnB,IACA,eAAe,QACb,IAAI,OAAO,QAAQ,KAAK,aAAa,YAAY,GACjD,KAAK,aAAa,GACpB;IAEJ,yBAAyB,4BAA4B;KACnD,KAAK,uBAAuB;KAC5B,SAAS,CAAC;KACV,UAAU,yBAAyB;KACnC,QAAQ;IACV;GACF,OAAO;IAEL,IAAI,SAAS,YAEX,sBAAsB,uCACpB,qBACA,YACF;IAGF,sBAAsB,uBACpB,qBACA,KAAK,YACP;IAEA,yBAAyB,uBACvB,wBACA,YACF;GACF;GAKA,IAAI,SAAS,YACX,sBAAsB;IACpB,GAAG;IACH,SACE,oBACE,oBAAoB,SACpB,uBAAuB,OACzB,KAAK,CAAC;GACV;GAGF,MAAM,eAAe,MACnB;IACE,SAAS,KAAK,WAAW,SAAS;IAClC,aAAa,YAAY;IACzB,SAAS,KAAK,WAAW,SAAS;GACpC,CAAC,CAAC,KAAK,EAAE,GACT,EAAE,SAAS,GAAG,CAChB;GAEA,UACE,GAAG,KAAK,mBAAmB,aAAa,aAAa,aAAa,SAAS,uBAAuB,QAAS,CAAC,KAC5G,EACE,OAAO,OACT,CACF;GAEA,MAAM,qBAAkC,UACtC,oBAAoB,SACpB,UACF;GAEA,MAAM,aAAa,mBAAmB;GAEtC,IAAI,aAAa,GACf,UACE,GAAG,KAAK,mBAAmB,aAAa,cAAc,eAAe,UAAU,EAAE,0BACjF,EACE,OAAO,OACT,CACF;GAGF,MAAM,cAA2B,CAAC;GAGlC,MAAM,gBAAgB,mBAAmB,IAAI,OAAO,UAAU;IAC5D,MAAM,cAAc,kBAAkB,MAAM,OAAO,MAAM,KAAK;IAE9D,IAAI,aAAa,GACf,UACE,GAAG,KAAK,mBAAmB,eAAe,YAAY,qBACtD,EACE,OAAO,OACT,CACF;IAGF,MAAM,qBAAqB,2BAA2B,KAAK;IAC3D,MAAM,EACJ,kBAAkB,uBAClB,wBAAwB,gCACtB,2BAA2B,kBAAkB;IAEjD,MAAM,qBAAqB,YAAY;KACrC,OAAO,MAAM,aACX,YAAY;MACV,IAAI;MAEJ,IAAI,YAAY,UACd,oBAAoB,MAAM,SAAS,cAAc;OAC/C,kBACE;OACF,qBAAqB;OACrB,uBACE,oBAAoB,eACpB,UAAU,eACV;OACF,aAAa,KAAK;OAClB,cAAc;OACd;OACA;MACF,CAAC;WAED,oBAAoB,MAAM,YAAY,GACnC,cAAc;OACb,kBAAkB;OAClB,qBAAqB;OACrB,uBACE,oBAAoB,eACpB,UAAU,eACV;OACF,aAAa,KAAK;OAClB,cAAc;OACd;OACA;MACF,CAAC,CAAC,CACD,MAAM,WAAW,OAAO,IAAI;MAGjC,IAAI,CAAC,mBAAmB,aACtB,MAAM,IAAI,MAAM,mBAAmB;MAGrC,MAAM,EAAE,WAAW,UAAU,0BAC3B,kBAAkB,aAClB,2BACF;MAEA,IAAI,CAAC,WACH,MAAM,IAAI,MACR,sDAAsD,OACxD;MAGF,cAAc;MACd,OAAO,0BACL,oBACA,uBACA,kBAAkB,WACpB;KACF,GACA;MACE,UAAU;MACV,OAAO;MACP,UAAU,EAAE,OAAO,SAAS,eAAe;OACzC,MAAM,cAAc,kBAClB,MAAM,OACN,MAAM,KACR;OACA,UACE,GAAG,KAAK,mBAAmB,eAAe,YAAY,GAAG,SAAS,kBAAkB,WAAW,GAAG,EAAE,GAAG,SAAS,eAAe,KAAK,GAAG,WAAW,SAAS,EAAE,aAAa,eAAe,UAAU,CAAC,EAAE,MAAM,eAAe,QAAQ,KACnO,EACE,OAAO,QACT,CACF;OAEA,mBAAmB;OAEnB,IAAI,mBAAmB,sBAAsB;QAC3C,UAAU,6BAA6B,EACrC,OAAO,QACT,CAAC;QACD,QAAQ,KAAK,CAAC;OAChB;MACF;KACF,CACF,CAAC,CAAC;IACJ;IAMA,QAJgB,SAAS,WACrB,QAAQ,SAAS,kBAAkB,IACnC,mBAAmB,EAET,CAAC,MAAM,YAAY;KAAE;KAAO;IAAO,EAAE;GACrD,CAAC;GAMD,OAH2B,QAAQ,IAAI,aAAa,EAGxC,CACT,MAAM,QAAQ,WAAW,OAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,CAAC,CACjE,SAAS,EAAE,aAAa;IACvB,YAAY,KAAK,MAAM;GACzB,CAAC;GAGH,MAAM,oBAAoB,YAAY,WAAW;GAEjD,MAAM,SAAS;IACb,GAAG;IACH,SAAS;GACX;GAWA,OAAO,CAAC,cALa,iBACnB,uBAAuB,WAAW,CAAC,GACnC,OAAO,OAGwB,CAAC;EACpC,CAAC,CACH;EAEA,MAAM,oBACJ,OAAO,YAAY,wBAAwB;EAE7C,MAAM,iBAAiB,uBAAuB,SAC1C;GACE,GAAG;GACH,KAAK,uBAAuB;GAC5B,SAAS,CAAC;EACZ,IACA;EAEJ,IAAI,mBAA+B;GACjC,GAAG,0BAA0B,cAAc;GAC3C,QAAQ;GACR,GAAG;EACL;EAEA,KAAK,MAAM,gBAAgB,KAAK,eAC9B,IAAI,kBAAkB,eACpB,mBAAmB,0BACjB,kBACA,kBAAkB,eAClB,YACF;EAIJ,UACE,GAAG,KAAK,iBAAiB,GAAG,SAAS,sCAAsC,WAAW,KAAK,EAAE,OAAO,aAAa,SAAS,iBAAiB,QAAS,CAAC,KACrJ,EACE,OAAO,OACT,CACF;EAKA,MAAM,wBACJ,uBAAuB,QAAQ,cAAc,YAAY;EAE3D,IACE,uBAAuB,WACtB,0BAA0B,QACzB,0BAA0B,WAC5B,uBAAuB,aAAa,SACpC;GACA,MAAM,qBAAqB,uBACxB,SAAU,MAAM,GAAG,CAAC,CACpB,MAAM,GAAG,EAAE;GAEd,MAAM,eAAe,mBAAmB,mBAAmB,SAAS;GAEpE,OAAO,KAAK,MACV,KAAK,UAAU;IACb,GAAG;IACH,kBAAkB;KAChB,GAAG;KACH,MAAM;KACN,QAAQ;IACV;GACF,CAAC,CAAC,CAAC,WACD,IAAI,OAAO,MAAM,aAAa,kBAAkB,GAAG,GACnD,WAAW,aAAa,MAC1B,CACF;EACF;EAEA,OAAO;GACL,GAAG;GACH;EACF;CACF,GACA;EACE,UAAU;EACV,OAAO;EACP,UAAU,EAAE,OAAO,SAAS,eAC1B,UACE,GAAG,KAAK,iBAAiB,GAAG,SAAS,UAAU,WAAW,GAAG,EAAE,GAAG,SAAS,eAAe,KAAK,GAAG,WAAW,SAAS,EAAE,aAAa,eAAe,UAAU,CAAC,EAAE,MAAM,eAAe,QAAQ,KAC9L,EACE,OAAO,QACT,CACF;EACF,kBAAkB,EAAE,YAClB,UACE,GAAG,KAAK,iBAAiB,GAAG,SAAS,sCAAsC,WAAW,GAAG,EAAE,GAAG,SAAS,eAAe,KAAK,GAAG,WAAW,SAAS,KAClJ,EACE,OAAO,QACT,CACF;CACJ,CACF,CAAC,CAAC;AAGJ"}
@@ -1,12 +1,12 @@
1
1
  import { formatFillData } from "./formatFillData.mjs";
2
2
  import { getAvailableLocalesInDictionary } from "./getAvailableLocalesInDictionary.mjs";
3
- import { resolve } from "node:path";
4
3
  import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
4
+ import { resolve } from "node:path";
5
5
  import { formatLocale, formatPath } from "@intlayer/engine/utils";
6
6
  import { writeContentDeclaration } from "@intlayer/engine/build";
7
7
  import { getDictionaries } from "@intlayer/dictionaries-entry";
8
8
 
9
- //#region src/fill/writeFill.ts
9
+ // src/fill/writeFill.ts
10
10
  const writeFill = async (contentDeclarationFile, outputLocales, parentLocales, configuration) => {
11
11
  const appLogger = getAppLogger(configuration);
12
12
  const fullDictionary = getDictionaries(configuration)[contentDeclarationFile.key];
@@ -46,6 +46,5 @@ const writeFill = async (contentDeclarationFile, outputLocales, parentLocales, c
46
46
  }
47
47
  };
48
48
 
49
- //#endregion
50
49
  export { writeFill };
51
50
  //# sourceMappingURL=writeFill.mjs.map
@@ -1,9 +1,9 @@
1
+ import { getConfiguration } from "@intlayer/config/node";
1
2
  import { listGitFiles } from "@intlayer/engine/cli";
2
3
  import { join, relative } from "node:path";
3
- import { getConfiguration } from "@intlayer/config/node";
4
4
  import { getUnmergedDictionaries } from "@intlayer/dictionaries-entry/unmerged";
5
5
 
6
- //#region src/getTargetDictionary.ts
6
+ // src/getTargetDictionary.ts
7
7
  const ensureArray = (value) => [value].flat();
8
8
  const getTargetUnmergedDictionaries = async (options) => {
9
9
  const configuration = getConfiguration(options?.configOptions);
@@ -29,6 +29,5 @@ const getTargetUnmergedDictionaries = async (options) => {
29
29
  return result;
30
30
  };
31
31
 
32
- //#endregion
33
32
  export { ensureArray, getTargetUnmergedDictionaries };
34
33
  //# sourceMappingURL=getTargetDictionary.mjs.map