@intlayer/cli 5.5.10 → 5.5.11

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 (189) hide show
  1. package/dist/cjs/cli.cjs +78 -6
  2. package/dist/cjs/cli.cjs.map +1 -1
  3. package/dist/cjs/cli.test.cjs +435 -0
  4. package/dist/cjs/cli.test.cjs.map +1 -0
  5. package/dist/cjs/fill.cjs +8 -12
  6. package/dist/cjs/fill.cjs.map +1 -1
  7. package/dist/cjs/index.cjs +5 -1
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/prompts/REVIEW_PROMPT.md +37 -0
  10. package/dist/cjs/prompts/TRANSLATE_PROMPT.md +38 -0
  11. package/dist/cjs/pull.cjs +10 -2
  12. package/dist/cjs/pull.cjs.map +1 -1
  13. package/dist/cjs/pushConfig.cjs +5 -1
  14. package/dist/cjs/pushConfig.cjs.map +1 -1
  15. package/dist/cjs/reviewDoc.cjs +203 -0
  16. package/dist/cjs/reviewDoc.cjs.map +1 -0
  17. package/dist/cjs/translateDoc.cjs +201 -0
  18. package/dist/cjs/translateDoc.cjs.map +1 -0
  19. package/dist/cjs/utils/calculateChunks.cjs +120 -0
  20. package/dist/cjs/utils/calculateChunks.cjs.map +1 -0
  21. package/dist/cjs/utils/calculateChunks.test.cjs +104 -0
  22. package/dist/cjs/utils/calculateChunks.test.cjs.map +1 -0
  23. package/dist/cjs/utils/calculrateChunkTest.md +9 -0
  24. package/dist/cjs/utils/checkAIAccess.cjs +40 -0
  25. package/dist/cjs/utils/checkAIAccess.cjs.map +1 -0
  26. package/dist/cjs/utils/checkFileModifiedRange.cjs +97 -0
  27. package/dist/cjs/utils/checkFileModifiedRange.cjs.map +1 -0
  28. package/dist/cjs/utils/checkFileModifiedRange.test.cjs +175 -0
  29. package/dist/cjs/utils/checkFileModifiedRange.test.cjs.map +1 -0
  30. package/dist/cjs/utils/checkLastUpdateTime.cjs +33 -0
  31. package/dist/cjs/utils/checkLastUpdateTime.cjs.map +1 -0
  32. package/dist/cjs/utils/chunkInference.cjs +58 -0
  33. package/dist/cjs/utils/chunkInference.cjs.map +1 -0
  34. package/dist/cjs/utils/fixChunkStartEndChars.cjs +47 -0
  35. package/dist/cjs/utils/fixChunkStartEndChars.cjs.map +1 -0
  36. package/dist/cjs/utils/fixChunkStartEndChars.test.cjs +81 -0
  37. package/dist/cjs/utils/fixChunkStartEndChars.test.cjs.map +1 -0
  38. package/dist/cjs/utils/formatTimeDiff.cjs +46 -0
  39. package/dist/cjs/utils/formatTimeDiff.cjs.map +1 -0
  40. package/dist/cjs/utils/formatTimeDiff.test.cjs +32 -0
  41. package/dist/cjs/utils/formatTimeDiff.test.cjs.map +1 -0
  42. package/dist/cjs/utils/getChunk.cjs +77 -0
  43. package/dist/cjs/utils/getChunk.cjs.map +1 -0
  44. package/dist/cjs/utils/getChunk.test.cjs +46 -0
  45. package/dist/cjs/utils/getChunk.test.cjs.map +1 -0
  46. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs +36 -0
  47. package/dist/cjs/utils/getIsFileUpdatedRecently.cjs.map +1 -0
  48. package/dist/cjs/utils/getOutputFilePath.cjs +89 -0
  49. package/dist/cjs/utils/getOutputFilePath.cjs.map +1 -0
  50. package/dist/cjs/utils/getOutputFilePath.test.cjs +73 -0
  51. package/dist/cjs/utils/getOutputFilePath.test.cjs.map +1 -0
  52. package/dist/cjs/utils/getParentPackageJSON.cjs +47 -0
  53. package/dist/cjs/utils/getParentPackageJSON.cjs.map +1 -0
  54. package/dist/cjs/utils/listSpecialChars.cjs +78 -0
  55. package/dist/cjs/utils/listSpecialChars.cjs.map +1 -0
  56. package/dist/cjs/utils/listSpecialChars.test.cjs +58 -0
  57. package/dist/cjs/utils/listSpecialChars.test.cjs.map +1 -0
  58. package/dist/cjs/utils/reorderParagraphs.cjs +125 -0
  59. package/dist/cjs/utils/reorderParagraphs.cjs.map +1 -0
  60. package/dist/cjs/utils/reorderParagraphs.test.cjs +71 -0
  61. package/dist/cjs/utils/reorderParagraphs.test.cjs.map +1 -0
  62. package/dist/cjs/utils/splitTextByLine.cjs +35 -0
  63. package/dist/cjs/utils/splitTextByLine.cjs.map +1 -0
  64. package/dist/cjs/utils/splitTextByLine.test.cjs +14 -0
  65. package/dist/cjs/utils/splitTextByLine.test.cjs.map +1 -0
  66. package/dist/esm/cli.mjs +79 -7
  67. package/dist/esm/cli.mjs.map +1 -1
  68. package/dist/esm/cli.test.mjs +412 -0
  69. package/dist/esm/cli.test.mjs.map +1 -0
  70. package/dist/esm/fill.mjs +8 -12
  71. package/dist/esm/fill.mjs.map +1 -1
  72. package/dist/esm/index.mjs +2 -0
  73. package/dist/esm/index.mjs.map +1 -1
  74. package/dist/esm/prompts/REVIEW_PROMPT.md +37 -0
  75. package/dist/esm/prompts/TRANSLATE_PROMPT.md +38 -0
  76. package/dist/esm/pull.mjs +10 -2
  77. package/dist/esm/pull.mjs.map +1 -1
  78. package/dist/esm/pushConfig.mjs +5 -1
  79. package/dist/esm/pushConfig.mjs.map +1 -1
  80. package/dist/esm/reviewDoc.mjs +172 -0
  81. package/dist/esm/reviewDoc.mjs.map +1 -0
  82. package/dist/esm/translateDoc.mjs +170 -0
  83. package/dist/esm/translateDoc.mjs.map +1 -0
  84. package/dist/esm/utils/calculateChunks.mjs +96 -0
  85. package/dist/esm/utils/calculateChunks.mjs.map +1 -0
  86. package/dist/esm/utils/calculateChunks.test.mjs +103 -0
  87. package/dist/esm/utils/calculateChunks.test.mjs.map +1 -0
  88. package/dist/esm/utils/calculrateChunkTest.md +9 -0
  89. package/dist/esm/utils/checkAIAccess.mjs +16 -0
  90. package/dist/esm/utils/checkAIAccess.mjs.map +1 -0
  91. package/dist/esm/utils/checkFileModifiedRange.mjs +73 -0
  92. package/dist/esm/utils/checkFileModifiedRange.mjs.map +1 -0
  93. package/dist/esm/utils/checkFileModifiedRange.test.mjs +181 -0
  94. package/dist/esm/utils/checkFileModifiedRange.test.mjs.map +1 -0
  95. package/dist/esm/utils/checkLastUpdateTime.mjs +9 -0
  96. package/dist/esm/utils/checkLastUpdateTime.mjs.map +1 -0
  97. package/dist/esm/utils/chunkInference.mjs +34 -0
  98. package/dist/esm/utils/chunkInference.mjs.map +1 -0
  99. package/dist/esm/utils/fixChunkStartEndChars.mjs +23 -0
  100. package/dist/esm/utils/fixChunkStartEndChars.mjs.map +1 -0
  101. package/dist/esm/utils/fixChunkStartEndChars.test.mjs +80 -0
  102. package/dist/esm/utils/fixChunkStartEndChars.test.mjs.map +1 -0
  103. package/dist/esm/utils/formatTimeDiff.mjs +22 -0
  104. package/dist/esm/utils/formatTimeDiff.mjs.map +1 -0
  105. package/dist/esm/utils/formatTimeDiff.test.mjs +31 -0
  106. package/dist/esm/utils/formatTimeDiff.test.mjs.map +1 -0
  107. package/dist/esm/utils/getChunk.mjs +53 -0
  108. package/dist/esm/utils/getChunk.mjs.map +1 -0
  109. package/dist/esm/utils/getChunk.test.mjs +45 -0
  110. package/dist/esm/utils/getChunk.test.mjs.map +1 -0
  111. package/dist/esm/utils/getIsFileUpdatedRecently.mjs +12 -0
  112. package/dist/esm/utils/getIsFileUpdatedRecently.mjs.map +1 -0
  113. package/dist/esm/utils/getOutputFilePath.mjs +65 -0
  114. package/dist/esm/utils/getOutputFilePath.mjs.map +1 -0
  115. package/dist/esm/utils/getOutputFilePath.test.mjs +72 -0
  116. package/dist/esm/utils/getOutputFilePath.test.mjs.map +1 -0
  117. package/dist/esm/utils/getParentPackageJSON.mjs +23 -0
  118. package/dist/esm/utils/getParentPackageJSON.mjs.map +1 -0
  119. package/dist/esm/utils/listSpecialChars.mjs +54 -0
  120. package/dist/esm/utils/listSpecialChars.mjs.map +1 -0
  121. package/dist/esm/utils/listSpecialChars.test.mjs +57 -0
  122. package/dist/esm/utils/listSpecialChars.test.mjs.map +1 -0
  123. package/dist/esm/utils/reorderParagraphs.mjs +101 -0
  124. package/dist/esm/utils/reorderParagraphs.mjs.map +1 -0
  125. package/dist/esm/utils/reorderParagraphs.test.mjs +70 -0
  126. package/dist/esm/utils/reorderParagraphs.test.mjs.map +1 -0
  127. package/dist/esm/utils/splitTextByLine.mjs +11 -0
  128. package/dist/esm/utils/splitTextByLine.mjs.map +1 -0
  129. package/dist/esm/utils/splitTextByLine.test.mjs +13 -0
  130. package/dist/esm/utils/splitTextByLine.test.mjs.map +1 -0
  131. package/dist/types/cli.d.ts.map +1 -1
  132. package/dist/types/cli.test.d.ts +2 -0
  133. package/dist/types/cli.test.d.ts.map +1 -0
  134. package/dist/types/fill.d.ts.map +1 -1
  135. package/dist/types/index.d.ts +2 -0
  136. package/dist/types/index.d.ts.map +1 -1
  137. package/dist/types/pull.d.ts.map +1 -1
  138. package/dist/types/pushConfig.d.ts.map +1 -1
  139. package/dist/types/reviewDoc.d.ts +25 -0
  140. package/dist/types/reviewDoc.d.ts.map +1 -0
  141. package/dist/types/translateDoc.d.ts +25 -0
  142. package/dist/types/translateDoc.d.ts.map +1 -0
  143. package/dist/types/utils/calculateChunks.d.ts +9 -0
  144. package/dist/types/utils/calculateChunks.d.ts.map +1 -0
  145. package/dist/types/utils/calculateChunks.test.d.ts +2 -0
  146. package/dist/types/utils/calculateChunks.test.d.ts.map +1 -0
  147. package/dist/types/utils/checkAIAccess.d.ts +4 -0
  148. package/dist/types/utils/checkAIAccess.d.ts.map +1 -0
  149. package/dist/types/utils/checkFileModifiedRange.d.ts +11 -0
  150. package/dist/types/utils/checkFileModifiedRange.d.ts.map +1 -0
  151. package/dist/types/utils/checkFileModifiedRange.test.d.ts +2 -0
  152. package/dist/types/utils/checkFileModifiedRange.test.d.ts.map +1 -0
  153. package/dist/types/utils/checkLastUpdateTime.d.ts +9 -0
  154. package/dist/types/utils/checkLastUpdateTime.d.ts.map +1 -0
  155. package/dist/types/utils/chunkInference.d.ts +12 -0
  156. package/dist/types/utils/chunkInference.d.ts.map +1 -0
  157. package/dist/types/utils/fixChunkStartEndChars.d.ts +2 -0
  158. package/dist/types/utils/fixChunkStartEndChars.d.ts.map +1 -0
  159. package/dist/types/utils/fixChunkStartEndChars.test.d.ts +2 -0
  160. package/dist/types/utils/fixChunkStartEndChars.test.d.ts.map +1 -0
  161. package/dist/types/utils/formatTimeDiff.d.ts +2 -0
  162. package/dist/types/utils/formatTimeDiff.d.ts.map +1 -0
  163. package/dist/types/utils/formatTimeDiff.test.d.ts +2 -0
  164. package/dist/types/utils/formatTimeDiff.test.d.ts.map +1 -0
  165. package/dist/types/utils/getChunk.d.ts +9 -0
  166. package/dist/types/utils/getChunk.d.ts.map +1 -0
  167. package/dist/types/utils/getChunk.test.d.ts +2 -0
  168. package/dist/types/utils/getChunk.test.d.ts.map +1 -0
  169. package/dist/types/utils/getIsFileUpdatedRecently.d.ts +5 -0
  170. package/dist/types/utils/getIsFileUpdatedRecently.d.ts.map +1 -0
  171. package/dist/types/utils/getOutputFilePath.d.ts +26 -0
  172. package/dist/types/utils/getOutputFilePath.d.ts.map +1 -0
  173. package/dist/types/utils/getOutputFilePath.test.d.ts +2 -0
  174. package/dist/types/utils/getOutputFilePath.test.d.ts.map +1 -0
  175. package/dist/types/utils/getParentPackageJSON.d.ts +32 -0
  176. package/dist/types/utils/getParentPackageJSON.d.ts.map +1 -0
  177. package/dist/types/utils/listSpecialChars.d.ts +10 -0
  178. package/dist/types/utils/listSpecialChars.d.ts.map +1 -0
  179. package/dist/types/utils/listSpecialChars.test.d.ts +2 -0
  180. package/dist/types/utils/listSpecialChars.test.d.ts.map +1 -0
  181. package/dist/types/utils/reorderParagraphs.d.ts +8 -0
  182. package/dist/types/utils/reorderParagraphs.d.ts.map +1 -0
  183. package/dist/types/utils/reorderParagraphs.test.d.ts +2 -0
  184. package/dist/types/utils/reorderParagraphs.test.d.ts.map +1 -0
  185. package/dist/types/utils/splitTextByLine.d.ts +2 -0
  186. package/dist/types/utils/splitTextByLine.d.ts.map +1 -0
  187. package/dist/types/utils/splitTextByLine.test.d.ts +2 -0
  188. package/dist/types/utils/splitTextByLine.test.d.ts.map +1 -0
  189. package/package.json +16 -14
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli.test.ts"],"sourcesContent":["import { TextDecoder, TextEncoder } from 'util';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\n\n// Polyfill TextEncoder/TextDecoder for Node versions < 19\nif (!(globalThis as any).TextEncoder) {\n (globalThis as any).TextEncoder = TextEncoder;\n}\nif (!(globalThis as any).TextDecoder) {\n (globalThis as any).TextDecoder = TextDecoder;\n}\n\n// Keep references to the original argv so we can restore it after each test\nconst ORIGINAL_ARGV = [...process.argv];\n\n// Helper to reset process.argv for each test\nconst setProcessArgv = (args: string[]) => {\n process.argv = ['node', 'intlayer', ...args];\n};\n\n// Dynamic mock placeholders\nlet buildMock: ReturnType<typeof vi.fn>;\nlet pushMock: ReturnType<typeof vi.fn>;\nlet pullMock: ReturnType<typeof vi.fn>;\nlet fillMock: ReturnType<typeof vi.fn>;\nlet getConfigMock: ReturnType<typeof vi.fn>;\nlet pushConfigMock: ReturnType<typeof vi.fn>;\nlet listContentDeclarationMock: ReturnType<typeof vi.fn>;\nlet translateDocMock: ReturnType<typeof vi.fn>;\nlet reviewDocMock: ReturnType<typeof vi.fn>;\n\n// Hoisted mocks – evaluated before the tested module is imported\nvi.mock('./build', () => ({\n build: (...args: any[]) => buildMock(...args),\n}));\n\nvi.mock('./push', () => ({\n push: (...args: any[]) => pushMock(...args),\n}));\n\nvi.mock('./pull', () => ({\n pull: (...args: any[]) => pullMock(...args),\n}));\n\nvi.mock('./fill', () => ({\n fill: (...args: any[]) => fillMock(...args),\n}));\n\nvi.mock('./config', () => ({\n getConfig: (...args: any[]) => getConfigMock(...args),\n}));\n\nvi.mock('./pushConfig', () => ({\n pushConfig: (...args: any[]) => pushConfigMock(...args),\n}));\n\nvi.mock('./listContentDeclaration', () => ({\n listContentDeclaration: (...args: any[]) =>\n listContentDeclarationMock(...args),\n}));\n\nvi.mock('./translateDoc', () => ({\n translateDoc: (...args: any[]) => translateDocMock(...args),\n}));\n\nvi.mock('./reviewDoc', () => ({\n reviewDoc: (...args: any[]) => reviewDocMock(...args),\n}));\n\n// Mock getParentPackageJSON utility\nvi.mock('./utils/getParentPackageJSON', () => ({\n getParentPackageJSON: vi.fn(() => ({\n name: '@intlayer/cli',\n version: '1.0.0',\n description: 'Intlayer CLI',\n })),\n}));\n\n// Mock configuration import that the CLI relies on for defaults\nvi.mock('@intlayer/config/built', () => ({\n __esModule: true,\n default: {},\n}));\n\nvi.mock('@intlayer/config', () => ({\n __esModule: true,\n isESModule: false,\n}));\n\ndescribe('Intlayer CLI', () => {\n beforeEach(() => {\n // Reset module cache to load a fresh CLI instance for each test\n vi.resetModules();\n\n // Re-initialise mocks for each test run\n buildMock = vi.fn();\n pushMock = vi.fn();\n pullMock = vi.fn();\n fillMock = vi.fn();\n getConfigMock = vi.fn();\n pushConfigMock = vi.fn();\n listContentDeclarationMock = vi.fn();\n translateDocMock = vi.fn();\n reviewDocMock = vi.fn();\n });\n\n afterEach(() => {\n // Restore the original process arguments after each test\n process.argv = [...ORIGINAL_ARGV];\n vi.clearAllMocks();\n });\n\n describe('build command', () => {\n it('triggers build with the --watch flag', async () => {\n setProcessArgv(['build', '--watch']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(buildMock).toHaveBeenCalledTimes(1);\n expect(buildMock).toHaveBeenCalledWith(\n expect.objectContaining({ watch: true })\n );\n });\n\n it('triggers build with verbose and custom prefix', async () => {\n setProcessArgv(['build', '--verbose', '--prefix', 'TEST']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(buildMock).toHaveBeenCalledTimes(1);\n expect(buildMock).toHaveBeenCalledWith(\n expect.objectContaining({\n verbose: true,\n prefix: 'TEST',\n configOptions: expect.objectContaining({\n override: expect.objectContaining({\n log: expect.objectContaining({\n verbose: true,\n prefix: 'TEST',\n }),\n }),\n }),\n })\n );\n });\n\n it('triggers build with environment and base directory options', async () => {\n setProcessArgv([\n 'build',\n '--env',\n 'production',\n '--base-dir',\n '/custom/path',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(buildMock).toHaveBeenCalledTimes(1);\n expect(buildMock).toHaveBeenCalledWith(\n expect.objectContaining({\n env: 'production',\n baseDir: '/custom/path',\n configOptions: expect.objectContaining({\n env: 'production',\n baseDir: '/custom/path',\n }),\n })\n );\n });\n });\n\n describe('push command', () => {\n it('triggers push with dictionaries and delete flag', async () => {\n setProcessArgv([\n 'push',\n '--dictionaries',\n 'id1',\n 'id2',\n '--deleteLocaleDictionary',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pushMock).toHaveBeenCalledTimes(1);\n\n const pushOptions = pushMock.mock.calls[0][0] as Record<string, unknown>;\n expect(pushOptions.dictionaries).toEqual(['id1', 'id2']);\n expect(pushOptions.deleteLocaleDictionary).toBe(true);\n });\n\n it('triggers push with keep flag and git options', async () => {\n setProcessArgv([\n 'push',\n '--keepLocaleDictionary',\n '--uncommitted',\n '--git-diff-base',\n 'main',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pushMock).toHaveBeenCalledTimes(1);\n\n const pushOptions = pushMock.mock.calls[0][0] as Record<string, unknown>;\n expect(pushOptions.keepLocaleDictionary).toBe(true);\n expect(pushOptions.gitOptions).toEqual(\n expect.objectContaining({\n mode: ['uncommitted'],\n baseRef: 'main',\n absolute: true,\n })\n );\n });\n });\n\n describe('pull command', () => {\n it('triggers pull with specific dictionaries', async () => {\n setProcessArgv(['pull', '--dictionaries', 'dict1', 'dict2']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pullMock).toHaveBeenCalledTimes(1);\n expect(pullMock).toHaveBeenCalledWith(\n expect.objectContaining({\n dictionaries: ['dict1', 'dict2'],\n })\n );\n });\n\n it('triggers pull with custom path for new dictionaries', async () => {\n setProcessArgv(['pull', '--newDictionariesPath', '/custom/dict/path']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pullMock).toHaveBeenCalledTimes(1);\n expect(pullMock).toHaveBeenCalledWith(\n expect.objectContaining({\n newDictionariesPath: '/custom/dict/path',\n })\n );\n });\n });\n\n describe('config commands', () => {\n it('triggers config get with environment options', async () => {\n setProcessArgv(['config', 'get', '--env-file', '.env.local']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(getConfigMock).toHaveBeenCalledTimes(1);\n expect(getConfigMock).toHaveBeenCalledWith(\n expect.objectContaining({\n envFile: '.env.local',\n configOptions: expect.objectContaining({\n envFile: '.env.local',\n }),\n })\n );\n });\n\n it('triggers config push', async () => {\n setProcessArgv(['config', 'push']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pushConfigMock).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('fill command', () => {\n it('triggers fill with complete mode and AI options', async () => {\n setProcessArgv([\n 'fill',\n '--mode',\n 'complete',\n '--provider',\n 'openai',\n '--model',\n 'gpt-4',\n '--temperature',\n '0.7',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(fillMock).toHaveBeenCalledTimes(1);\n expect(fillMock).toHaveBeenCalledWith(\n expect.objectContaining({\n mode: 'complete',\n provider: 'openai',\n model: 'gpt-4',\n temperature: '0.7',\n aiOptions: expect.objectContaining({\n provider: 'openai',\n model: 'gpt-4',\n temperature: '0.7',\n }),\n })\n );\n });\n\n it('triggers fill with file filters and locale options', async () => {\n setProcessArgv([\n 'fill',\n '--file',\n 'dict1.ts',\n 'dict2.ts',\n '--source-locale',\n 'en',\n '--output-locales',\n 'fr',\n 'es',\n '--keys',\n 'button',\n 'title',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(fillMock).toHaveBeenCalledTimes(1);\n expect(fillMock).toHaveBeenCalledWith(\n expect.objectContaining({\n file: ['dict1.ts', 'dict2.ts'],\n sourceLocale: 'en',\n outputLocales: ['fr', 'es'],\n keys: ['button', 'title'],\n })\n );\n });\n\n it('triggers fill with path filters and excluded keys', async () => {\n setProcessArgv([\n 'fill',\n '--path-filter',\n 'src/**/*.ts',\n '--excluded-keys',\n 'debug',\n 'test',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(fillMock).toHaveBeenCalledTimes(1);\n expect(fillMock).toHaveBeenCalledWith(\n expect.objectContaining({\n pathFilter: ['src/**/*.ts'],\n excludedKeys: ['debug', 'test'],\n })\n );\n });\n });\n\n describe('doc commands', () => {\n it('triggers doc translate with AI and locale options', async () => {\n setProcessArgv([\n 'doc',\n 'translate',\n '--doc-pattern',\n 'docs/**/*.md',\n '--locales',\n 'fr',\n 'es',\n '--base-locale',\n 'en',\n '--api-key',\n 'test-key',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(translateDocMock).toHaveBeenCalledTimes(1);\n expect(translateDocMock).toHaveBeenCalledWith(\n expect.objectContaining({\n docPattern: ['docs/**/*.md'],\n locales: ['fr', 'es'],\n baseLocale: 'en',\n aiOptions: expect.objectContaining({\n apiKey: 'test-key',\n }),\n })\n );\n });\n\n it('triggers doc review with excluded patterns and processing options', async () => {\n setProcessArgv([\n 'doc',\n 'review',\n '--excluded-glob-pattern',\n 'node_modules/**',\n '--nb-simultaneous-file-processed',\n '5',\n '--custom-prompt',\n 'Review for clarity',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(reviewDocMock).toHaveBeenCalledTimes(1);\n expect(reviewDocMock).toHaveBeenCalledWith(\n expect.objectContaining({\n excludedGlobPattern: ['node_modules/**'],\n nbSimultaneousFileProcessed: '5',\n aiOptions: expect.objectContaining({\n customPrompt: 'Review for clarity',\n }),\n })\n );\n });\n });\n\n describe('content list command', () => {\n it('triggers content list', async () => {\n setProcessArgv(['content', 'list']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(listContentDeclarationMock).toHaveBeenCalledTimes(1);\n });\n });\n\n describe('git options integration', () => {\n it('correctly parses git diff options', async () => {\n setProcessArgv([\n 'fill',\n '--git-diff',\n '--git-diff-base',\n 'origin/main',\n '--git-diff-current',\n 'HEAD',\n '--untracked',\n ]);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(fillMock).toHaveBeenCalledTimes(1);\n expect(fillMock).toHaveBeenCalledWith(\n expect.objectContaining({\n gitOptions: expect.objectContaining({\n mode: ['gitDiff', 'untracked'],\n baseRef: 'origin/main',\n currentRef: 'HEAD',\n absolute: true,\n }),\n })\n );\n });\n });\n\n describe('alias commands', () => {\n it('supports dictionary alias for build', async () => {\n setProcessArgv(['dictionary', 'build', '--watch']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(buildMock).toHaveBeenCalledTimes(1);\n expect(buildMock).toHaveBeenCalledWith(\n expect.objectContaining({ watch: true })\n );\n });\n\n it('supports dic alias for push', async () => {\n setProcessArgv(['dic', 'push', '--dictionaries', 'test']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(pushMock).toHaveBeenCalledTimes(1);\n expect(pushMock).toHaveBeenCalledWith(\n expect.objectContaining({\n dictionaries: ['test'],\n })\n );\n });\n\n it('supports conf alias for config get', async () => {\n setProcessArgv(['conf', 'get']);\n\n const { setAPI } = await import('./cli');\n setAPI();\n\n expect(getConfigMock).toHaveBeenCalledTimes(1);\n });\n });\n});\n"],"mappings":"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","names":[]}
package/dist/cjs/fill.cjs CHANGED
@@ -38,6 +38,7 @@ var import_core = require("@intlayer/core");
38
38
  var import_dictionaries_entry = __toESM(require("@intlayer/dictionaries-entry"));
39
39
  var import_unmerged_dictionaries_entry = __toESM(require("@intlayer/unmerged-dictionaries-entry"));
40
40
  var import_path = require("path");
41
+ var import_checkAIAccess = require('./utils/checkAIAccess.cjs');
41
42
  const ensureArray = (value) => [value].flat();
42
43
  const getTargetDictionary = async (options) => {
43
44
  const configuration = (0, import_config.getConfiguration)(options.configOptions);
@@ -214,14 +215,7 @@ const fill = async (options) => {
214
215
  const { defaultLocale, locales } = configuration.internationalization;
215
216
  const mode = options.mode ?? "review";
216
217
  const baseLocale = options.sourceLocale ?? defaultLocale;
217
- if (!configuration.editor.clientId && !options.aiOptions?.apiKey) {
218
- appLogger("AI options or API key not provided. Skipping AI translation.", {
219
- level: "error"
220
- });
221
- throw new Error(
222
- "AI options or API key not provided. Skipping AI translation."
223
- );
224
- }
218
+ (0, import_checkAIAccess.checkAIAccess)(configuration, options.aiOptions);
225
219
  let oAuth2AccessToken;
226
220
  if (configuration.editor.clientId) {
227
221
  const intlayerAuthAPI = (0, import_api.getAuthAPI)(void 0, configuration);
@@ -314,11 +308,13 @@ const fill = async (options) => {
314
308
  mode,
315
309
  aiOptions: options.aiOptions
316
310
  },
317
- oAuth2AccessToken ? {
318
- headers: {
319
- Authorization: `Bearer ${oAuth2AccessToken}`
311
+ {
312
+ ...oAuth2AccessToken && {
313
+ headers: {
314
+ Authorization: `Bearer ${oAuth2AccessToken}`
315
+ }
320
316
  }
321
- } : void 0
317
+ }
322
318
  );
323
319
  if (!translationResult.data?.fileContent) {
324
320
  appLogger(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fill.ts"],"sourcesContent":["import { AIOptions, getAiAPI, getAuthAPI } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n getFilteredLocalesContent,\n listGitFiles,\n ListGitFilesOptions,\n mergeDictionaries,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n type IntlayerConfig,\n Locales,\n} from '@intlayer/config';\nimport {\n type AutoFill,\n type ContentNode,\n type Dictionary,\n getLocalisedContent,\n} from '@intlayer/core';\nimport dictionariesRecord from '@intlayer/dictionaries-entry';\nimport unmergedDictionariesRecord from '@intlayer/unmerged-dictionaries-entry';\nimport { dirname, extname, join } from 'path';\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n file?: string | string[];\n mode?: 'complete' | 'review';\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n};\n\nconst ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\nconst getTargetDictionary = async (options: FillOptions) => {\n const configuration = getConfiguration(options.configOptions);\n\n const { baseDir } = configuration.content;\n\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options.file !== 'undefined') {\n const fileArray = ensureArray(options.file);\n const absoluteFilePaths = fileArray.map((file) => join(baseDir, file));\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n (absoluteFilePaths.includes(dict.filePath) ||\n absoluteFilePaths.includes(join(baseDir, dict.filePath)))\n );\n }\n\n if (typeof options.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options.filter !== 'undefined') {\n result = result.filter(options.filter);\n }\n\n const gitOptions = options.gitOptions;\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result.filter((dict) => !dict.autoFilled);\n};\n\nconst transformUriToAbsolutePath = (\n uri: string,\n filePath: string,\n baseDir: string\n) => {\n if (uri.startsWith('/')) {\n return join(baseDir, uri);\n }\n\n if (uri.startsWith('./')) {\n return join(dirname(filePath), uri);\n }\n\n return filePath;\n};\n\nexport type AutoFillData = {\n localeList: Locales[];\n filePath: string;\n};\n\nconst formatAutoFillData = (\n autoFillOptions: AutoFill,\n localeList: Locales[],\n filePath: string,\n dictionaryKey: string,\n configuration: IntlayerConfig\n): AutoFillData[] => {\n const outputContentDeclarationFile: AutoFillData[] = [];\n\n if (!Boolean(autoFillOptions)) return outputContentDeclarationFile;\n\n if (autoFillOptions === true) {\n // wanted jsonFilePath: /..../src/components/home/index.content.json\n // replace file extension in json\n let jsonFilePath = filePath.replace(extname(filePath), '.json');\n\n // if both filePath jsonFilePath are same path, change it as : /..../src/components/home/index.fill.content.json\n if (filePath === jsonFilePath) {\n jsonFilePath = jsonFilePath.replace(extname(jsonFilePath), '.fill.json');\n }\n\n outputContentDeclarationFile.push({\n localeList,\n filePath: jsonFilePath,\n });\n }\n\n if (typeof autoFillOptions === 'string') {\n if (autoFillOptions.includes('{{locale}}')) {\n const output = localeList.map((locale) => ({\n localeList: [locale],\n filePath: transformUriToAbsolutePath(\n autoFillOptions\n .replace('{{locale}}', locale)\n .replace('{{key}}', dictionaryKey),\n filePath,\n configuration.content.baseDir\n ),\n }));\n\n outputContentDeclarationFile.push(...output);\n } else {\n outputContentDeclarationFile.push({\n localeList,\n filePath: transformUriToAbsolutePath(\n autoFillOptions,\n filePath,\n configuration.content.baseDir\n ),\n });\n }\n\n return outputContentDeclarationFile;\n }\n\n if (typeof autoFillOptions === 'object') {\n const localeList = Object.keys(autoFillOptions).filter(\n (locale) => typeof autoFillOptions[locale] === 'string'\n ) as Locales[];\n\n const output: AutoFillData[] = localeList.map((locale) => ({\n localeList: [locale],\n filePath: transformUriToAbsolutePath(\n autoFillOptions[locale].replace('{{key}}', dictionaryKey),\n filePath,\n configuration.content.baseDir\n ),\n }));\n\n // Group by filePath and merge localeList\n const groupedByFilePath = output.reduce((acc, curr) => {\n const existing = acc.find((item) => item.filePath === curr.filePath);\n if (existing) {\n existing.localeList.push(...curr.localeList);\n } else {\n acc.push(curr);\n }\n return acc;\n }, [] as AutoFillData[]);\n\n outputContentDeclarationFile.push(...groupedByFilePath);\n }\n\n return outputContentDeclarationFile;\n};\n\nconst autoFill = async (\n fullDictionary: Dictionary,\n contentDeclarationFile: Dictionary,\n autoFillOptions: AutoFill,\n outputLocales: Locales[],\n parentLocales: Locales[],\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n let localeList: Locales[] = (\n outputLocales ?? configuration.internationalization.locales\n ).filter((locale) => !parentLocales?.includes(locale));\n\n const filePath = contentDeclarationFile.filePath;\n\n if (!filePath) {\n appLogger('No file path found for dictionary', {\n level: 'error',\n });\n return;\n }\n\n const autoFillData: AutoFillData[] = formatAutoFillData(\n autoFillOptions,\n localeList,\n filePath,\n fullDictionary.key,\n configuration\n );\n\n appLogger(`Auto fill data: ${JSON.stringify(autoFillData, null, 2)}`, {\n level: 'info',\n isVerbose: true,\n });\n\n for await (const output of autoFillData) {\n const reducedDictionary = reduceDictionaryContent(\n fullDictionary,\n contentDeclarationFile\n );\n\n const isPerLocaleDeclarationFile = output.localeList.length === 1;\n\n if (isPerLocaleDeclarationFile) {\n const sourceLocale = output.localeList[0];\n\n const sourceLocaleContent = getLocalisedContent(\n reducedDictionary as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey: reducedDictionary.key, keyPath: [] }\n );\n\n await writeContentDeclaration({\n ...fullDictionary,\n locale: sourceLocale,\n autoFilled: true,\n autoFill: undefined,\n content: sourceLocaleContent.content,\n filePath: output.filePath,\n });\n } else {\n const content = getFilteredLocalesContent(\n reducedDictionary.content as unknown as ContentNode,\n output.localeList,\n { dictionaryKey: reducedDictionary.key, keyPath: [] }\n );\n\n // write file\n await writeContentDeclaration({\n ...fullDictionary,\n autoFilled: true,\n autoFill: undefined,\n content,\n filePath: output.filePath,\n });\n }\n }\n};\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration);\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'review';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n\n if (!configuration.editor.clientId && !options.aiOptions?.apiKey) {\n appLogger('AI options or API key not provided. Skipping AI translation.', {\n level: 'error',\n });\n // Potentially handle this case differently, e.g., by using a different translation method or stopping.\n\n throw new Error(\n 'AI options or API key not provided. Skipping AI translation.'\n );\n }\n\n let oAuth2AccessToken: string | undefined;\n if (configuration.editor.clientId) {\n const intlayerAuthAPI = getAuthAPI(undefined, configuration);\n const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();\n\n oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n }\n\n appLogger('Starting fill function', {\n level: 'info',\n });\n\n const targetUnmergedDictionaries = await getTargetDictionary(options);\n\n // Determine output locales\n const outputLocalesList: Locales[] = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) =>\n // If mode is review, translate all locales\n // If mode is complete, translate only the locales that are not the source locale\n mode === 'review' ? true : locale !== baseLocale\n );\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger(\n [\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys).join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `Dictionary with key \"${dictionaryKey}\" not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n appLogger(\n `Processing content declaration: ${targetUnmergedDictionary.filePath}`,\n {\n isVerbose: true,\n }\n );\n\n const sourceLocaleContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `No content found for dictionary ${dictionaryKey} in source locale ${sourceLocale}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const result: Dictionary[] = [];\n\n // 5. for each locale to translate (exclude base locale) generate json translations\n for await (const targetLocale of outputLocalesList) {\n appLogger(\n `Preparing translation for ${dictionaryKey} from ${sourceLocale} to ${targetLocale}`,\n {\n isVerbose: true,\n }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n targetLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await getAiAPI(\n undefined,\n configuration\n ).translateJSON(\n {\n entryFileContent: sourceLocaleContent.content, // Should be JSON, ensure getLocalisedContent provides this.\n presetOutputContent: presetOutputContent.content, // Should be JSON\n dictionaryDescription: mainDictionaryToProcess.description,\n entryLocale: sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions: options.aiOptions,\n },\n oAuth2AccessToken\n ? {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }\n : undefined\n );\n\n if (!translationResult.data?.fileContent) {\n appLogger(\n `No content result found for ${dictionaryKey} to ${targetLocale}`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: targetLocale,\n });\n\n result.push(processedPerLocaleDictionary);\n } catch (error) {\n appLogger(\n `Error filling ${dictionaryKey} to ${targetLocale}:` + error,\n {\n level: 'error',\n }\n );\n }\n }\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...result, mainDictionaryToProcess] // Mode review: generated content will override the base one\n : [mainDictionaryToProcess, ...result]; // Mode complete: base content will override the generated one\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAgD;AAChD,sBAQO;AACP,oBAMO;AACP,kBAKO;AACP,gCAA+B;AAC/B,yCAAuC;AACvC,kBAAuC;AAkBvC,MAAM,cAAc,CAAI,UAAwB,CAAC,KAAK,EAAE,KAAK;AAE7D,MAAM,sBAAsB,OAAO,YAAyB;AAC1D,QAAM,oBAAgB,gCAAiB,QAAQ,aAAa;AAE5D,QAAM,EAAE,QAAQ,IAAI,cAAc;AAElC,MAAI,SAAS,OAAO,OAAO,mCAAAA,OAA0B,EAAE,KAAK;AAG5D,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,UAAM,YAAY,YAAY,QAAQ,IAAI;AAC1C,UAAM,oBAAoB,UAAU,IAAI,CAAC,aAAS,kBAAK,SAAS,IAAI,CAAC;AAErE,aAAS,OAAO;AAAA,MACd,CAAC,SACC,KAAK,aACJ,kBAAkB,SAAS,KAAK,QAAQ,KACvC,kBAAkB,aAAS,kBAAK,SAAS,KAAK,QAAQ,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,IAAI,GAAG,SAAS,KAAK,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,iBAAiB,aAAa;AAC/C,aAAS,OAAO;AAAA,MACd,CAAC,SAAS,CAAC,YAAY,QAAQ,YAAY,GAAG,SAAS,KAAK,GAAG;AAAA,IACjE;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,eAAe,aAAa;AAC7C,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,UAAU,GAAG,SAAS,KAAK,YAAY,EAAE;AAAA,IAC/D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,WAAW,aAAa;AACzC,aAAS,OAAO,OAAO,QAAQ,MAAM;AAAA,EACvC;AAEA,QAAM,aAAa,QAAQ;AAC3B,MAAI,YAAY;AACd,UAAM,kBAAkB,UAAM,8BAAa,UAAU;AAErD,QAAI,iBAAiB;AAInB,eAAS,OAAO,OAAO,CAAC,SAAS;AAC/B,YAAI,CAAC,KAAK,SAAU,QAAO;AAE3B,eAAO,gBAAgB,KAAK,CAAC,YAAY,KAAK,aAAa,OAAO;AAAA,MACpE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,UAAU;AACjD;AAEA,MAAM,6BAA6B,CACjC,KACA,UACA,YACG;AACH,MAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAO,kBAAK,SAAS,GAAG;AAAA,EAC1B;AAEA,MAAI,IAAI,WAAW,IAAI,GAAG;AACxB,eAAO,sBAAK,qBAAQ,QAAQ,GAAG,GAAG;AAAA,EACpC;AAEA,SAAO;AACT;AAOA,MAAM,qBAAqB,CACzB,iBACA,YACA,UACA,eACA,kBACmB;AACnB,QAAM,+BAA+C,CAAC;AAEtD,MAAI,CAAC,QAAQ,eAAe,EAAG,QAAO;AAEtC,MAAI,oBAAoB,MAAM;AAG5B,QAAI,eAAe,SAAS,YAAQ,qBAAQ,QAAQ,GAAG,OAAO;AAG9D,QAAI,aAAa,cAAc;AAC7B,qBAAe,aAAa,YAAQ,qBAAQ,YAAY,GAAG,YAAY;AAAA,IACzE;AAEA,iCAA6B,KAAK;AAAA,MAChC;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,oBAAoB,UAAU;AACvC,QAAI,gBAAgB,SAAS,YAAY,GAAG;AAC1C,YAAM,SAAS,WAAW,IAAI,CAAC,YAAY;AAAA,QACzC,YAAY,CAAC,MAAM;AAAA,QACnB,UAAU;AAAA,UACR,gBACG,QAAQ,cAAc,MAAM,EAC5B,QAAQ,WAAW,aAAa;AAAA,UACnC;AAAA,UACA,cAAc,QAAQ;AAAA,QACxB;AAAA,MACF,EAAE;AAEF,mCAA6B,KAAK,GAAG,MAAM;AAAA,IAC7C,OAAO;AACL,mCAA6B,KAAK;AAAA,QAChC;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA;AAAA,UACA,cAAc,QAAQ;AAAA,QACxB;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,oBAAoB,UAAU;AACvC,UAAMC,cAAa,OAAO,KAAK,eAAe,EAAE;AAAA,MAC9C,CAAC,WAAW,OAAO,gBAAgB,MAAM,MAAM;AAAA,IACjD;AAEA,UAAM,SAAyBA,YAAW,IAAI,CAAC,YAAY;AAAA,MACzD,YAAY,CAAC,MAAM;AAAA,MACnB,UAAU;AAAA,QACR,gBAAgB,MAAM,EAAE,QAAQ,WAAW,aAAa;AAAA,QACxD;AAAA,QACA,cAAc,QAAQ;AAAA,MACxB;AAAA,IACF,EAAE;AAGF,UAAM,oBAAoB,OAAO,OAAO,CAAC,KAAK,SAAS;AACrD,YAAM,WAAW,IAAI,KAAK,CAAC,SAAS,KAAK,aAAa,KAAK,QAAQ;AACnE,UAAI,UAAU;AACZ,iBAAS,WAAW,KAAK,GAAG,KAAK,UAAU;AAAA,MAC7C,OAAO;AACL,YAAI,KAAK,IAAI;AAAA,MACf;AACA,aAAO;AAAA,IACT,GAAG,CAAC,CAAmB;AAEvB,iCAA6B,KAAK,GAAG,iBAAiB;AAAA,EACxD;AAEA,SAAO;AACT;AAEA,MAAM,WAAW,OACf,gBACA,wBACA,iBACA,eACA,eACA,kBACG;AACH,QAAM,gBAAY,4BAAa,aAAa;AAC5C,MAAI,cACF,iBAAiB,cAAc,qBAAqB,SACpD,OAAO,CAAC,WAAW,CAAC,eAAe,SAAS,MAAM,CAAC;AAErD,QAAM,WAAW,uBAAuB;AAExC,MAAI,CAAC,UAAU;AACb,cAAU,qCAAqC;AAAA,MAC7C,OAAO;AAAA,IACT,CAAC;AACD;AAAA,EACF;AAEA,QAAM,eAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,EACF;AAEA,YAAU,mBAAmB,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC,IAAI;AAAA,IACpE,OAAO;AAAA,IACP,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB,UAAU,cAAc;AACvC,UAAM,wBAAoB;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,6BAA6B,OAAO,WAAW,WAAW;AAEhE,QAAI,4BAA4B;AAC9B,YAAM,eAAe,OAAO,WAAW,CAAC;AAExC,YAAM,0BAAsB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,EAAE,eAAe,kBAAkB,KAAK,SAAS,CAAC,EAAE;AAAA,MACtD;AAEA,gBAAM,yCAAwB;AAAA,QAC5B,GAAG;AAAA,QACH,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,SAAS,oBAAoB;AAAA,QAC7B,UAAU,OAAO;AAAA,MACnB,CAAC;AAAA,IACH,OAAO;AACL,YAAM,cAAU;AAAA,QACd,kBAAkB;AAAA,QAClB,OAAO;AAAA,QACP,EAAE,eAAe,kBAAkB,KAAK,SAAS,CAAC,EAAE;AAAA,MACtD;AAGA,gBAAM,yCAAwB;AAAA,QAC5B,GAAG;AAAA,QACH,YAAY;AAAA,QACZ,UAAU;AAAA,QACV;AAAA,QACA,UAAU,OAAO;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKO,MAAM,OAAO,OAAO,YAAwC;AACjE,QAAM,oBAAgB,gCAAiB,QAAQ,aAAa;AAC5D,QAAM,gBAAY,4BAAa,aAAa;AAE5C,QAAM,EAAE,eAAe,QAAQ,IAAI,cAAc;AACjD,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,aAAa,QAAQ,gBAAgB;AAE3C,MAAI,CAAC,cAAc,OAAO,YAAY,CAAC,QAAQ,WAAW,QAAQ;AAChE,cAAU,gEAAgE;AAAA,MACxE,OAAO;AAAA,IACT,CAAC;AAGD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,cAAc,OAAO,UAAU;AACjC,UAAM,sBAAkB,uBAAW,QAAW,aAAa;AAC3D,UAAM,oBAAoB,MAAM,gBAAgB,qBAAqB;AAErE,wBAAoB,kBAAkB,MAAM;AAAA,EAC9C;AAEA,YAAU,0BAA0B;AAAA,IAClC,OAAO;AAAA,EACT,CAAC;AAED,QAAM,6BAA6B,MAAM,oBAAoB,OAAO;AAGpE,QAAM,qBACJ,QAAQ,gBAAgB,YAAY,QAAQ,aAAa,IAAI,SAC7D;AAAA,IAAO,CAAC;AAAA;AAAA;AAAA,MAGR,SAAS,WAAW,OAAO,WAAW;AAAA;AAAA,EACxC;AAEA,QAAM,yBAAyB,oBAAI,IAAY;AAC/C,6BAA2B,QAAQ,CAAC,SAAS;AAC3C,2BAAuB,IAAI,KAAK,GAAG;AAAA,EACrC,CAAC;AAED;AAAA,IACE;AAAA,MACE;AAAA,MACA,MAAM,KAAK,sBAAsB,EAAE,KAAK,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,MACE,WAAW;AAAA,IACb;AAAA,EACF;AAEA,aAAW,4BAA4B,4BAA4B;AACjE,UAAM,gBAAgB,yBAAyB;AAC/C,UAAM,0BAA0B,0BAAAC,QAAmB,aAAa;AAChE,UAAM,eACH,yBAAyB,UAAsB;AAElD,QAAI,CAAC,yBAAyB;AAC5B;AAAA,QACE,wBAAwB,aAAa;AAAA,QACrC;AAAA,UACE,OAAO;AAAA,QACT;AAAA,MACF;AACA;AAAA,IACF;AAEA;AAAA,MACE,mCAAmC,yBAAyB,QAAQ;AAAA,MACpE;AAAA,QACE,WAAW;AAAA,MACb;AAAA,IACF;AAEA,UAAM,0BAAsB;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,EAAE,eAAe,SAAS,CAAC,EAAE;AAAA,IAC/B;AAEA,QAAI,OAAO,KAAK,mBAAmB,EAAE,WAAW,GAAG;AACjD;AAAA,QACE,mCAAmC,aAAa,qBAAqB,YAAY;AAAA,QACjF;AAAA,UACE,OAAO;AAAA,QACT;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,SAAuB,CAAC;AAG9B,qBAAiB,gBAAgB,mBAAmB;AAClD;AAAA,QACE,6BAA6B,aAAa,SAAS,YAAY,OAAO,YAAY;AAAA,QAClF;AAAA,UACE,WAAW;AAAA,QACb;AAAA,MACF;AAEA,YAAM,0BAAsB;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,EAAE,eAAe,SAAS,CAAC,EAAE;AAAA,MAC/B;AAEA,UAAI;AACF,cAAM,oBAAoB,UAAM;AAAA,UAC9B;AAAA,UACA;AAAA,QACF,EAAE;AAAA,UACA;AAAA,YACE,kBAAkB,oBAAoB;AAAA;AAAA,YACtC,qBAAqB,oBAAoB;AAAA;AAAA,YACzC,uBAAuB,wBAAwB;AAAA,YAC/C,aAAa;AAAA,YACb,cAAc;AAAA,YACd;AAAA,YACA,WAAW,QAAQ;AAAA,UACrB;AAAA,UACA,oBACI;AAAA,YACE,SAAS;AAAA,cACP,eAAe,UAAU,iBAAiB;AAAA,YAC5C;AAAA,UACF,IACA;AAAA,QACN;AAEA,YAAI,CAAC,kBAAkB,MAAM,aAAa;AACxC;AAAA,YACE,+BAA+B,aAAa,OAAO,YAAY;AAAA,YAC/D;AAAA,cACE,OAAO;AAAA,YACT;AAAA,UACF;AACA;AAAA,QACF;AAEA,cAAM,mCAA+B,4CAA2B;AAAA,UAC9D,GAAG;AAAA,UACH,SAAS,kBAAkB,MAAM;AAAA,UACjC,QAAQ;AAAA,QACV,CAAC;AAED,eAAO,KAAK,4BAA4B;AAAA,MAC1C,SAAS,OAAO;AACd;AAAA,UACE,iBAAiB,aAAa,OAAO,YAAY,MAAM;AAAA,UACvD;AAAA,YACE,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,oBACJ,SAAS,WACL,CAAC,GAAG,QAAQ,uBAAuB,IACnC,CAAC,yBAAyB,GAAG,MAAM;AAEzC,UAAM,oBAAgB,mCAAkB,iBAAiB;AAEzD,QAAI,gBAAgB;AAEpB,QAAI,cAAc,QAAQ;AACxB,YAAM,0BAAsB;AAAA,QAC1B;AAAA,QACA,cAAc;AAAA,QACd,EAAE,eAAe,SAAS,CAAC,EAAE;AAAA,MAC/B;AACA,sBAAgB;AAAA,QACd,GAAG;AAAA,QACH,SAAS,oBAAoB;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,oBAAgB,yCAAwB,eAAe,aAAa;AAE1E,cAAM;AAAA,MACJ,EAAE,GAAG,eAAe,SAAS,cAAc,QAAQ;AAAA,MACnD;AAAA,MACA,cAAc;AAAA,IAChB;AAEA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,CAAC,YAAY;AAAA,MACb;AAAA,IACF;AAAA,EACF;AACF;","names":["unmergedDictionariesRecord","localeList","dictionariesRecord"]}
1
+ {"version":3,"sources":["../../src/fill.ts"],"sourcesContent":["import { AIOptions, getAiAPI, getAuthAPI } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n getFilteredLocalesContent,\n listGitFiles,\n ListGitFilesOptions,\n mergeDictionaries,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n type IntlayerConfig,\n Locales,\n} from '@intlayer/config';\nimport {\n type AutoFill,\n type ContentNode,\n type Dictionary,\n getLocalisedContent,\n} from '@intlayer/core';\nimport dictionariesRecord from '@intlayer/dictionaries-entry';\nimport unmergedDictionariesRecord from '@intlayer/unmerged-dictionaries-entry';\nimport { dirname, extname, join } from 'path';\nimport { checkAIAccess } from './utils/checkAIAccess';\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n file?: string | string[];\n mode?: 'complete' | 'review';\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n};\n\nconst ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\nconst getTargetDictionary = async (options: FillOptions) => {\n const configuration = getConfiguration(options.configOptions);\n\n const { baseDir } = configuration.content;\n\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options.file !== 'undefined') {\n const fileArray = ensureArray(options.file);\n const absoluteFilePaths = fileArray.map((file) => join(baseDir, file));\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n (absoluteFilePaths.includes(dict.filePath) ||\n absoluteFilePaths.includes(join(baseDir, dict.filePath)))\n );\n }\n\n if (typeof options.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options.filter !== 'undefined') {\n result = result.filter(options.filter);\n }\n\n const gitOptions = options.gitOptions;\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result.filter((dict) => !dict.autoFilled);\n};\n\nconst transformUriToAbsolutePath = (\n uri: string,\n filePath: string,\n baseDir: string\n) => {\n if (uri.startsWith('/')) {\n return join(baseDir, uri);\n }\n\n if (uri.startsWith('./')) {\n return join(dirname(filePath), uri);\n }\n\n return filePath;\n};\n\nexport type AutoFillData = {\n localeList: Locales[];\n filePath: string;\n};\n\nconst formatAutoFillData = (\n autoFillOptions: AutoFill,\n localeList: Locales[],\n filePath: string,\n dictionaryKey: string,\n configuration: IntlayerConfig\n): AutoFillData[] => {\n const outputContentDeclarationFile: AutoFillData[] = [];\n\n if (!Boolean(autoFillOptions)) return outputContentDeclarationFile;\n\n if (autoFillOptions === true) {\n // wanted jsonFilePath: /..../src/components/home/index.content.json\n // replace file extension in json\n let jsonFilePath = filePath.replace(extname(filePath), '.json');\n\n // if both filePath jsonFilePath are same path, change it as : /..../src/components/home/index.fill.content.json\n if (filePath === jsonFilePath) {\n jsonFilePath = jsonFilePath.replace(extname(jsonFilePath), '.fill.json');\n }\n\n outputContentDeclarationFile.push({\n localeList,\n filePath: jsonFilePath,\n });\n }\n\n if (typeof autoFillOptions === 'string') {\n if (autoFillOptions.includes('{{locale}}')) {\n const output = localeList.map((locale) => ({\n localeList: [locale],\n filePath: transformUriToAbsolutePath(\n autoFillOptions\n .replace('{{locale}}', locale)\n .replace('{{key}}', dictionaryKey),\n filePath,\n configuration.content.baseDir\n ),\n }));\n\n outputContentDeclarationFile.push(...output);\n } else {\n outputContentDeclarationFile.push({\n localeList,\n filePath: transformUriToAbsolutePath(\n autoFillOptions,\n filePath,\n configuration.content.baseDir\n ),\n });\n }\n\n return outputContentDeclarationFile;\n }\n\n if (typeof autoFillOptions === 'object') {\n const localeList = Object.keys(autoFillOptions).filter(\n (locale) => typeof autoFillOptions[locale] === 'string'\n ) as Locales[];\n\n const output: AutoFillData[] = localeList.map((locale) => ({\n localeList: [locale],\n filePath: transformUriToAbsolutePath(\n autoFillOptions[locale].replace('{{key}}', dictionaryKey),\n filePath,\n configuration.content.baseDir\n ),\n }));\n\n // Group by filePath and merge localeList\n const groupedByFilePath = output.reduce((acc, curr) => {\n const existing = acc.find((item) => item.filePath === curr.filePath);\n if (existing) {\n existing.localeList.push(...curr.localeList);\n } else {\n acc.push(curr);\n }\n return acc;\n }, [] as AutoFillData[]);\n\n outputContentDeclarationFile.push(...groupedByFilePath);\n }\n\n return outputContentDeclarationFile;\n};\n\nconst autoFill = async (\n fullDictionary: Dictionary,\n contentDeclarationFile: Dictionary,\n autoFillOptions: AutoFill,\n outputLocales: Locales[],\n parentLocales: Locales[],\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n let localeList: Locales[] = (\n outputLocales ?? configuration.internationalization.locales\n ).filter((locale) => !parentLocales?.includes(locale));\n\n const filePath = contentDeclarationFile.filePath;\n\n if (!filePath) {\n appLogger('No file path found for dictionary', {\n level: 'error',\n });\n return;\n }\n\n const autoFillData: AutoFillData[] = formatAutoFillData(\n autoFillOptions,\n localeList,\n filePath,\n fullDictionary.key,\n configuration\n );\n\n appLogger(`Auto fill data: ${JSON.stringify(autoFillData, null, 2)}`, {\n level: 'info',\n isVerbose: true,\n });\n\n for await (const output of autoFillData) {\n const reducedDictionary = reduceDictionaryContent(\n fullDictionary,\n contentDeclarationFile\n );\n\n const isPerLocaleDeclarationFile = output.localeList.length === 1;\n\n if (isPerLocaleDeclarationFile) {\n const sourceLocale = output.localeList[0];\n\n const sourceLocaleContent = getLocalisedContent(\n reducedDictionary as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey: reducedDictionary.key, keyPath: [] }\n );\n\n await writeContentDeclaration({\n ...fullDictionary,\n locale: sourceLocale,\n autoFilled: true,\n autoFill: undefined,\n content: sourceLocaleContent.content,\n filePath: output.filePath,\n });\n } else {\n const content = getFilteredLocalesContent(\n reducedDictionary.content as unknown as ContentNode,\n output.localeList,\n { dictionaryKey: reducedDictionary.key, keyPath: [] }\n );\n\n // write file\n await writeContentDeclaration({\n ...fullDictionary,\n autoFilled: true,\n autoFill: undefined,\n content,\n filePath: output.filePath,\n });\n }\n }\n};\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration);\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'review';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n\n checkAIAccess(configuration, options.aiOptions);\n\n let oAuth2AccessToken: string | undefined;\n if (configuration.editor.clientId) {\n const intlayerAuthAPI = getAuthAPI(undefined, configuration);\n const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();\n\n oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n }\n\n appLogger('Starting fill function', {\n level: 'info',\n });\n\n const targetUnmergedDictionaries = await getTargetDictionary(options);\n\n // Determine output locales\n const outputLocalesList: Locales[] = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) =>\n // If mode is review, translate all locales\n // If mode is complete, translate only the locales that are not the source locale\n mode === 'review' ? true : locale !== baseLocale\n );\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger(\n [\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys).join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `Dictionary with key \"${dictionaryKey}\" not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n appLogger(\n `Processing content declaration: ${targetUnmergedDictionary.filePath}`,\n {\n isVerbose: true,\n }\n );\n\n const sourceLocaleContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `No content found for dictionary ${dictionaryKey} in source locale ${sourceLocale}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const result: Dictionary[] = [];\n\n // 5. for each locale to translate (exclude base locale) generate json translations\n for await (const targetLocale of outputLocalesList) {\n appLogger(\n `Preparing translation for ${dictionaryKey} from ${sourceLocale} to ${targetLocale}`,\n {\n isVerbose: true,\n }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n targetLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await getAiAPI(\n undefined,\n configuration\n ).translateJSON(\n {\n entryFileContent: sourceLocaleContent.content, // Should be JSON, ensure getLocalisedContent provides this.\n presetOutputContent: presetOutputContent.content, // Should be JSON\n dictionaryDescription: mainDictionaryToProcess.description,\n entryLocale: sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions: options.aiOptions,\n },\n {\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n }\n );\n\n if (!translationResult.data?.fileContent) {\n appLogger(\n `No content result found for ${dictionaryKey} to ${targetLocale}`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: targetLocale,\n });\n\n result.push(processedPerLocaleDictionary);\n } catch (error) {\n appLogger(\n `Error filling ${dictionaryKey} to ${targetLocale}:` + error,\n {\n level: 'error',\n }\n );\n }\n }\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...result, mainDictionaryToProcess] // Mode review: generated content will override the base one\n : [mainDictionaryToProcess, ...result]; // Mode complete: base content will override the generated one\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n }\n};\n"],"mappings":"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","names":["unmergedDictionariesRecord","localeList","dictionariesRecord"]}
@@ -22,6 +22,8 @@ __reExport(index_exports, require('./listContentDeclaration.cjs'), module.export
22
22
  __reExport(index_exports, require('./pull.cjs'), module.exports);
23
23
  __reExport(index_exports, require('./push.cjs'), module.exports);
24
24
  __reExport(index_exports, require('./pushConfig.cjs'), module.exports);
25
+ __reExport(index_exports, require('./reviewDoc.cjs'), module.exports);
26
+ __reExport(index_exports, require('./translateDoc.cjs'), module.exports);
25
27
  // Annotate the CommonJS export names for ESM import in node:
26
28
  0 && (module.exports = {
27
29
  ...require('./build.cjs'),
@@ -30,6 +32,8 @@ __reExport(index_exports, require('./pushConfig.cjs'), module.exports);
30
32
  ...require('./listContentDeclaration.cjs'),
31
33
  ...require('./pull.cjs'),
32
34
  ...require('./push.cjs'),
33
- ...require('./pushConfig.cjs')
35
+ ...require('./pushConfig.cjs'),
36
+ ...require('./reviewDoc.cjs'),
37
+ ...require('./translateDoc.cjs')
34
38
  });
35
39
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './build';\nexport * from './cli';\nexport * from './fill';\nexport * from './listContentDeclaration';\nexport * from './pull';\nexport * from './push';\nexport * from './pushConfig';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,oBAAd;AACA,0BAAc,kBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,qCAHd;AAIA,0BAAc,mBAJd;AAKA,0BAAc,mBALd;AAMA,0BAAc,yBANd;","names":[]}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["export * from './build';\nexport * from './cli';\nexport * from './fill';\nexport * from './listContentDeclaration';\nexport * from './pull';\nexport * from './push';\nexport * from './pushConfig';\nexport * from './reviewDoc';\nexport * from './translateDoc';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,oBAAd;AACA,0BAAc,kBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,qCAHd;AAIA,0BAAc,mBAJd;AAKA,0BAAc,mBALd;AAMA,0BAAc,yBANd;AAOA,0BAAc,wBAPd;AAQA,0BAAc,2BARd;","names":[]}
@@ -0,0 +1,37 @@
1
+ You are an expert in internationalisation, technical documentation and multilingual content auditing. Your task is to **audit an existing translation** against its {{baseLocaleName}} source and make it fully up-to-date.
2
+
3
+ # Base instructions
4
+
5
+ 1. **Goal of the audit**
6
+
7
+ - Insert any **missing paragraphs, lists, tables, code blocks or images** that have been introduced in the base file since the last translation.
8
+ - **Correct spelling, grammar, punctuation or Markdown syntax errors** that may exist in the current translation.
9
+ - Keep all **code blocks** and **variable names** in the same language as the base file, but translate the **comments** inside the code blocks to {{localeName}}. Example: `internationalization: {` should stay `internationalization: {`. You should not replace the `z` by a `s`.
10
+ - Do **not** change sentences that are already correct. Do not reflow or re-word translated sentences unless it is strictly necessary for correctness.
11
+ - To ensure all characters are included in the reviewed content, you should use "///chunkStart///" and "///chunkEnd///" to delimit the chunk of the reviewed content. You should not use "---" or "```" to delimit the chunk of the reviewed content.
12
+
13
+ 2. **Output requirements**
14
+
15
+ - Return **only the full, updated file content in {{localeName}}** ‑ no explanations or surrounding code fences.
16
+ - All existing correct content must remain **byte-for-byte identical** unless a fix is required (see above).
17
+ - New or updated content must be placed in the correct position so that the overall structure mirrors the English file.
18
+ - If overlapping chunks are found, you should include the exact same content than provided in the user message.
19
+
20
+ 3. **Locales**
21
+
22
+ - Source locale: {{baseLocaleName}}
23
+ - Target locale: {{localeName}}
24
+
25
+ 4. **Output Example:**
26
+
27
+ Entry (en - English (US)): "///chunkStart/// - Here the translated content///chunkEnd///"
28
+ Entry (fr - French): "///chunkStart/// - Ici le contenu trraduit ce mardi 25 juin 2025///chunkEnd///"
29
+ Expected Output (fr - French): "///chunkStart/// - Ici le contenu traduit///chunkEnd///"
30
+
31
+ # Custom instructions
32
+
33
+ {{customInstructions}}
34
+
35
+ # Application context
36
+
37
+ {{applicationContext}}
@@ -0,0 +1,38 @@
1
+ You are an expert in internationalization and content management. Your task is to translate the following documentation into {{localeName}}.
2
+
3
+ # Base instructions
4
+
5
+ 1. **Requirement:**
6
+
7
+ - You should only translate the text, and titles of the file.
8
+ - You should not alter the structure of the file.
9
+ - You should not alter the code logic of code elements.
10
+ - You should consider the context of the solution (Intlayer), and write for the user of the solution (developer).
11
+ - In the code elements, the naming of the variables should be made in the same language as the base file. But the comments should be in {{localeName}}.
12
+ - You should return the translated file in {{localeName}} without any additional comments or explanations.
13
+ - To ensure all characters are included in the translated content, you should use "///chunkStart///" and "///chunkEnd///" to delimit the translated content. You should not use "---" or "```" to delimit the translated content.
14
+ - Anyway the length of the chunk to translate, you should translate the whole chunk and do not skip any part. You should ensure that each part of the chunk is translated.
15
+ - Each title should be translated and present in the output file.
16
+ - Each listing point should be translated and present in the output file.
17
+ - Each code element should be translated and present in the output file.
18
+ - Each image should be translated and present in the output file.
19
+ - Each link should be translated and present in the output file.
20
+ - If overlapping chunks are found, you should include the exact same content than provided in the user message.
21
+
22
+ 2. **Locales:**
23
+
24
+ - Base file locale: {{baseLocaleName}}
25
+ - Desired Final file language: {{localeName}}
26
+
27
+ 3. **Output Example:**
28
+
29
+ Entry (en - English (US)): "///chunkStart/// - Here the translated content///chunkEnd///"
30
+ Expected Output (fr - French): "///chunkStart/// - Ici le contenu traduit///chunkEnd///"
31
+
32
+ # Custom instructions
33
+
34
+ {{customInstructions}}
35
+
36
+ # Application context
37
+
38
+ {{applicationContext}}
package/dist/cjs/pull.cjs CHANGED
@@ -58,7 +58,11 @@ const pull = async (options) => {
58
58
  const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();
59
59
  const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;
60
60
  const getDictionariesKeysResult = await intlayerAPI.dictionary.getDictionariesKeys({
61
- headers: { Authorization: `Bearer ${oAuth2AccessToken}` }
61
+ ...oAuth2AccessToken && {
62
+ headers: {
63
+ Authorization: `Bearer ${oAuth2AccessToken}`
64
+ }
65
+ }
62
66
  });
63
67
  if (!getDictionariesKeysResult.data) {
64
68
  throw new Error("No distant dictionaries found");
@@ -98,7 +102,11 @@ const pull = async (options) => {
98
102
  statusObj.dictionaryKey,
99
103
  void 0,
100
104
  {
101
- headers: { Authorization: `Bearer ${oAuth2AccessToken}` }
105
+ ...oAuth2AccessToken && {
106
+ headers: {
107
+ Authorization: `Bearer ${oAuth2AccessToken}`
108
+ }
109
+ }
102
110
  }
103
111
  );
104
112
  const distantDictionary = getDictionaryResult.data;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPI } from '@intlayer/api';\nimport {\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\n\nimport type { Dictionary } from '@intlayer/core';\nimport pLimit from 'p-limit';\nimport * as readline from 'readline';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus;\n icon: string;\n index: number;\n error?: Error;\n errorMessage?: string;\n spinnerFrameIndex?: number;\n};\n\nconst spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst RESET = '\\x1b[0m';\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst BLUE = '\\x1b[34m';\nconst GREY = '\\x1b[90m';\nconst YELLOW = '\\x1b[33m';\nconst GREY_DARK = '\\x1b[90m';\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n throw new Error(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.'\n );\n }\n\n const intlayerAPI = getIntlayerAPI(undefined, config);\n\n const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();\n\n const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await intlayerAPI.dictionary.getDictionariesKeys({\n headers: { Authorization: `Bearer ${oAuth2AccessToken}` },\n });\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesKeys: string[] = getDictionariesKeysResult.data;\n\n if (options?.dictionaries) {\n // Filter the dictionaries from the provided list of IDs\n distantDictionariesKeys = distantDictionariesKeys.filter(\n (dictionaryKey) => options.dictionaries!.includes(dictionaryKey)\n );\n }\n\n // Check if dictionaries list is empty\n if (distantDictionariesKeys.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] =\n distantDictionariesKeys.map((dictionaryKey, index) => ({\n dictionaryKey,\n icon: getStatusIcon('pending'),\n status: 'pending',\n index,\n spinnerFrameIndex: 0,\n }));\n\n // Output initial statuses\n for (const statusObj of dictionariesStatuses) {\n process.stdout.write(getStatusLine(statusObj) + '\\n');\n }\n\n // Start spinner timer\n const spinnerTimer = setInterval(() => {\n updateAllStatusLines(dictionariesStatuses);\n }, 100); // Update every 100ms\n\n // Process dictionaries in parallel with a concurrency limit\n const limit = pLimit(5); // Limit the number of concurrent requests\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'fetching';\n try {\n // Fetch the dictionary\n const getDictionaryResult = await intlayerAPI.dictionary.getDictionary(\n statusObj.dictionaryKey,\n undefined,\n {\n headers: { Authorization: `Bearer ${oAuth2AccessToken}` },\n }\n );\n\n const distantDictionary = getDictionaryResult.data;\n\n if (!distantDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n distantDictionary,\n config,\n options?.newDictionariesPath\n );\n\n statusObj.status = status;\n\n successfullyFetchedDictionaries.push(distantDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n }\n };\n\n const fetchPromises = dictionariesStatuses.map((statusObj) =>\n limit(() => processDictionary(statusObj))\n );\n\n await Promise.all(fetchPromises);\n\n // Stop the spinner timer\n clearInterval(spinnerTimer);\n\n // Update statuses one last time\n updateAllStatusLines(dictionariesStatuses);\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst getStatusIcon = (status: string): string => {\n const statusIcons: Record<string, string> = {\n pending: '⏲',\n fetching: '', // Spinner handled separately\n 'up-to-date': '✔',\n updated: '✔',\n fetched: '✔',\n error: '✖',\n };\n return statusIcons[status] ?? '';\n};\n\nconst getStatusLine = (statusObj: DictionariesStatus): string => {\n let icon = getStatusIcon(statusObj.status);\n let colorStart = '';\n let colorEnd = '';\n\n if (statusObj.status === 'fetching') {\n // Use spinner frame\n icon = spinnerFrames[statusObj.spinnerFrameIndex! % spinnerFrames.length];\n colorStart = BLUE;\n colorEnd = RESET;\n } else if (statusObj.status === 'error') {\n colorStart = RED;\n colorEnd = RESET;\n } else if (\n statusObj.status === 'fetched' ||\n statusObj.status === 'imported' ||\n statusObj.status === 'updated' ||\n statusObj.status === 'up-to-date'\n ) {\n colorStart = GREEN;\n colorEnd = RESET;\n } else if (\n statusObj.status === 'reimported in JSON' ||\n statusObj.status === 'reimported in new location'\n ) {\n colorStart = YELLOW;\n colorEnd = RESET;\n } else {\n colorStart = GREY;\n colorEnd = RESET;\n }\n\n return `- ${statusObj.dictionaryKey} ${GREY_DARK}[${colorStart}${icon}${statusObj.status}${GREY_DARK}]${colorEnd}`;\n};\n\nconst updateAllStatusLines = (dictionariesStatuses: DictionariesStatus[]) => {\n // Move cursor up to the first status line\n readline.moveCursor(process.stdout, 0, -dictionariesStatuses.length);\n for (const statusObj of dictionariesStatuses) {\n // Clear the line\n readline.clearLine(process.stdout, 0);\n\n if (statusObj.status === 'fetching') {\n // Update spinner frame\n statusObj.spinnerFrameIndex =\n (statusObj.spinnerFrameIndex! + 1) % spinnerFrames.length;\n }\n\n // Write the status line\n process.stdout.write(getStatusLine(statusObj) + '\\n');\n }\n};\n"],"mappings":"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","names":["pLimit"]}
1
+ {"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPI } from '@intlayer/api';\nimport {\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\n\nimport type { Dictionary } from '@intlayer/core';\nimport pLimit from 'p-limit';\nimport * as readline from 'readline';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus;\n icon: string;\n index: number;\n error?: Error;\n errorMessage?: string;\n spinnerFrameIndex?: number;\n};\n\nconst spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst RESET = '\\x1b[0m';\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst BLUE = '\\x1b[34m';\nconst GREY = '\\x1b[90m';\nconst YELLOW = '\\x1b[33m';\nconst GREY_DARK = '\\x1b[90m';\n\n/**\n * Fetch distant dictionaries and write them locally,\n * with progress indicators and concurrency control.\n */\nexport const pull = async (options?: PullOptions): Promise<void> => {\n const appLogger = getAppLogger(options?.configOptions?.override);\n\n try {\n const config = getConfiguration(options?.configOptions);\n const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n throw new Error(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.'\n );\n }\n\n const intlayerAPI = getIntlayerAPI(undefined, config);\n\n const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();\n\n const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await intlayerAPI.dictionary.getDictionariesKeys({\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n });\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesKeys: string[] = getDictionariesKeysResult.data;\n\n if (options?.dictionaries) {\n // Filter the dictionaries from the provided list of IDs\n distantDictionariesKeys = distantDictionariesKeys.filter(\n (dictionaryKey) => options.dictionaries!.includes(dictionaryKey)\n );\n }\n\n // Check if dictionaries list is empty\n if (distantDictionariesKeys.length === 0) {\n appLogger('No dictionaries to fetch', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Fetching dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] =\n distantDictionariesKeys.map((dictionaryKey, index) => ({\n dictionaryKey,\n icon: getStatusIcon('pending'),\n status: 'pending',\n index,\n spinnerFrameIndex: 0,\n }));\n\n // Output initial statuses\n for (const statusObj of dictionariesStatuses) {\n process.stdout.write(getStatusLine(statusObj) + '\\n');\n }\n\n // Start spinner timer\n const spinnerTimer = setInterval(() => {\n updateAllStatusLines(dictionariesStatuses);\n }, 100); // Update every 100ms\n\n // Process dictionaries in parallel with a concurrency limit\n const limit = pLimit(5); // Limit the number of concurrent requests\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'fetching';\n try {\n // Fetch the dictionary\n const getDictionaryResult = await intlayerAPI.dictionary.getDictionary(\n statusObj.dictionaryKey,\n undefined,\n {\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n }\n );\n\n const distantDictionary = getDictionaryResult.data;\n\n if (!distantDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n distantDictionary,\n config,\n options?.newDictionariesPath\n );\n\n statusObj.status = status;\n\n successfullyFetchedDictionaries.push(distantDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n }\n };\n\n const fetchPromises = dictionariesStatuses.map((statusObj) =>\n limit(() => processDictionary(statusObj))\n );\n\n await Promise.all(fetchPromises);\n\n // Stop the spinner timer\n clearInterval(spinnerTimer);\n\n // Update statuses one last time\n updateAllStatusLines(dictionariesStatuses);\n\n // Output any error messages\n for (const statusObj of dictionariesStatuses) {\n if (statusObj.errorMessage) {\n appLogger(statusObj.errorMessage, {\n level: 'error',\n });\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst getStatusIcon = (status: string): string => {\n const statusIcons: Record<string, string> = {\n pending: '⏲',\n fetching: '', // Spinner handled separately\n 'up-to-date': '✔',\n updated: '✔',\n fetched: '✔',\n error: '✖',\n };\n return statusIcons[status] ?? '';\n};\n\nconst getStatusLine = (statusObj: DictionariesStatus): string => {\n let icon = getStatusIcon(statusObj.status);\n let colorStart = '';\n let colorEnd = '';\n\n if (statusObj.status === 'fetching') {\n // Use spinner frame\n icon = spinnerFrames[statusObj.spinnerFrameIndex! % spinnerFrames.length];\n colorStart = BLUE;\n colorEnd = RESET;\n } else if (statusObj.status === 'error') {\n colorStart = RED;\n colorEnd = RESET;\n } else if (\n statusObj.status === 'fetched' ||\n statusObj.status === 'imported' ||\n statusObj.status === 'updated' ||\n statusObj.status === 'up-to-date'\n ) {\n colorStart = GREEN;\n colorEnd = RESET;\n } else if (\n statusObj.status === 'reimported in JSON' ||\n statusObj.status === 'reimported in new location'\n ) {\n colorStart = YELLOW;\n colorEnd = RESET;\n } else {\n colorStart = GREY;\n colorEnd = RESET;\n }\n\n return `- ${statusObj.dictionaryKey} ${GREY_DARK}[${colorStart}${icon}${statusObj.status}${GREY_DARK}]${colorEnd}`;\n};\n\nconst updateAllStatusLines = (dictionariesStatuses: DictionariesStatus[]) => {\n // Move cursor up to the first status line\n readline.moveCursor(process.stdout, 0, -dictionariesStatuses.length);\n for (const statusObj of dictionariesStatuses) {\n // Clear the line\n readline.clearLine(process.stdout, 0);\n\n if (statusObj.status === 'fetching') {\n // Update spinner frame\n statusObj.spinnerFrameIndex =\n (statusObj.spinnerFrameIndex! + 1) % spinnerFrames.length;\n }\n\n // Write the status line\n process.stdout.write(getStatusLine(statusObj) + '\\n');\n }\n};\n"],"mappings":"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","names":["pLimit"]}
@@ -43,7 +43,11 @@ const pushConfig = async (options) => {
43
43
  const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();
44
44
  const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;
45
45
  const getDictionariesKeysResult = await intlayerAPI.project.pushProjectConfiguration(config, {
46
- headers: { Authorization: `Bearer ${oAuth2AccessToken}` }
46
+ ...oAuth2AccessToken && {
47
+ headers: {
48
+ Authorization: `Bearer ${oAuth2AccessToken}`
49
+ }
50
+ }
47
51
  });
48
52
  if (!getDictionariesKeysResult.data) {
49
53
  throw new Error("Error pushing project configuration");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pushConfig.ts"],"sourcesContent":["import { getIntlayerAPI } from '@intlayer/api';\nimport {\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\n\ntype PushOptions = {\n logPrefix?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const pushConfig = async (options?: PushOptions) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n appLogger(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.',\n {\n level: 'error',\n config: {\n prefix: options?.logPrefix,\n },\n }\n );\n return;\n }\n\n const intlayerAPI = getIntlayerAPI(undefined, config);\n\n const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();\n\n const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await intlayerAPI.project.pushProjectConfiguration(config, {\n headers: { Authorization: `Bearer ${oAuth2AccessToken}` },\n });\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('Error pushing project configuration');\n }\n\n appLogger('Project configuration pushed successfully', {\n config: {\n prefix: options?.logPrefix,\n },\n });\n\n appLogger(JSON.stringify(getDictionariesKeysResult.data, null, 2), {\n config: {\n prefix: options?.logPrefix,\n },\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAA+B;AAC/B,oBAIO;AAOA,MAAM,aAAa,OAAO,YAA0B;AACzD,QAAM,aAAS,gCAAiB,SAAS,aAAa;AACtD,QAAM,gBAAY,4BAAa,MAAM;AAErC,QAAM,EAAE,UAAU,aAAa,IAAI,OAAO;AAE1C,MAAI,CAAC,YAAY,CAAC,cAAc;AAC9B;AAAA,MACE;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,QAAQ,SAAS;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,kBAAc,2BAAe,QAAW,MAAM;AAEpD,QAAM,oBAAoB,MAAM,YAAY,KAAK,qBAAqB;AAEtE,QAAM,oBAAoB,kBAAkB,MAAM;AAGlD,QAAM,4BACJ,MAAM,YAAY,QAAQ,yBAAyB,QAAQ;AAAA,IACzD,SAAS,EAAE,eAAe,UAAU,iBAAiB,GAAG;AAAA,EAC1D,CAAC;AAEH,MAAI,CAAC,0BAA0B,MAAM;AACnC,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,YAAU,6CAA6C;AAAA,IACrD,QAAQ;AAAA,MACN,QAAQ,SAAS;AAAA,IACnB;AAAA,EACF,CAAC;AAED,YAAU,KAAK,UAAU,0BAA0B,MAAM,MAAM,CAAC,GAAG;AAAA,IACjE,QAAQ;AAAA,MACN,QAAQ,SAAS;AAAA,IACnB;AAAA,EACF,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../src/pushConfig.ts"],"sourcesContent":["import { getIntlayerAPI } from '@intlayer/api';\nimport {\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\n\ntype PushOptions = {\n logPrefix?: string;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const pushConfig = async (options?: PushOptions) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n appLogger(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.',\n {\n level: 'error',\n config: {\n prefix: options?.logPrefix,\n },\n }\n );\n return;\n }\n\n const intlayerAPI = getIntlayerAPI(undefined, config);\n\n const oAuth2TokenResult = await intlayerAPI.auth.getOAuth2AccessToken();\n\n const oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await intlayerAPI.project.pushProjectConfiguration(config, {\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n });\n\n if (!getDictionariesKeysResult.data) {\n throw new Error('Error pushing project configuration');\n }\n\n appLogger('Project configuration pushed successfully', {\n config: {\n prefix: options?.logPrefix,\n },\n });\n\n appLogger(JSON.stringify(getDictionariesKeysResult.data, null, 2), {\n config: {\n prefix: options?.logPrefix,\n },\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAA+B;AAC/B,oBAIO;AAOA,MAAM,aAAa,OAAO,YAA0B;AACzD,QAAM,aAAS,gCAAiB,SAAS,aAAa;AACtD,QAAM,gBAAY,4BAAa,MAAM;AAErC,QAAM,EAAE,UAAU,aAAa,IAAI,OAAO;AAE1C,MAAI,CAAC,YAAY,CAAC,cAAc;AAC9B;AAAA,MACE;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,QAAQ,SAAS;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA;AAAA,EACF;AAEA,QAAM,kBAAc,2BAAe,QAAW,MAAM;AAEpD,QAAM,oBAAoB,MAAM,YAAY,KAAK,qBAAqB;AAEtE,QAAM,oBAAoB,kBAAkB,MAAM;AAGlD,QAAM,4BACJ,MAAM,YAAY,QAAQ,yBAAyB,QAAQ;AAAA,IACzD,GAAI,qBAAqB;AAAA,MACvB,SAAS;AAAA,QACP,eAAe,UAAU,iBAAiB;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,CAAC;AAEH,MAAI,CAAC,0BAA0B,MAAM;AACnC,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,YAAU,6CAA6C;AAAA,IACrD,QAAQ;AAAA,MACN,QAAQ,SAAS;AAAA,IACnB;AAAA,EACF,CAAC;AAED,YAAU,KAAK,UAAU,0BAA0B,MAAM,MAAM,CAAC,GAAG;AAAA,IACjE,QAAQ;AAAA,MACN,QAAQ,SAAS;AAAA,IACnB;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var reviewDoc_exports = {};
30
+ __export(reviewDoc_exports, {
31
+ reviewDoc: () => reviewDoc,
32
+ reviewFile: () => reviewFile
33
+ });
34
+ module.exports = __toCommonJS(reviewDoc_exports);
35
+ var import_api = require("@intlayer/api");
36
+ var import_config = require("@intlayer/config");
37
+ var import_core = require("@intlayer/core");
38
+ var import_fast_glob = __toESM(require("fast-glob"));
39
+ var import_fs = require("fs");
40
+ var import_promises = require("fs/promises");
41
+ var import_p_limit = __toESM(require("p-limit"));
42
+ var import_path = require("path");
43
+ var import_url = require("url");
44
+ var import_calculateChunks = require('./utils/calculateChunks.cjs');
45
+ var import_checkAIAccess = require('./utils/checkAIAccess.cjs');
46
+ var import_checkFileModifiedRange = require('./utils/checkFileModifiedRange.cjs');
47
+ var import_chunkInference = require('./utils/chunkInference.cjs');
48
+ var import_fixChunkStartEndChars = require('./utils/fixChunkStartEndChars.cjs');
49
+ var import_getChunk = require('./utils/getChunk.cjs');
50
+ var import_getOutputFilePath = require('./utils/getOutputFilePath.cjs');
51
+ const import_meta = {};
52
+ const isESModule = typeof import_meta.url === "string";
53
+ const dir = isESModule ? (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url)) : __dirname;
54
+ const reviewFile = async (baseFilePath, outputFilePath, locale, baseLocale, aiOptions, configOptions, oAuth2AccessToken, customInstructions) => {
55
+ try {
56
+ const configuration = (0, import_config.getConfiguration)(configOptions);
57
+ const appLogger = (0, import_config.getAppLogger)(configuration);
58
+ const basedFileContent = await (0, import_promises.readFile)(baseFilePath, "utf-8");
59
+ const fileToReviewContent = await (0, import_promises.readFile)(outputFilePath, "utf-8");
60
+ let updatedFileContent = fileToReviewContent;
61
+ let fileResultContent = "";
62
+ const basePrompt = (await (0, import_promises.readFile)((0, import_path.join)(dir, "./prompts/REVIEW_PROMPT.md"), "utf-8")).replaceAll(
63
+ "{{localeName}}",
64
+ `${(0, import_core.getLocaleName)(locale, import_config.Locales.ENGLISH)} (${locale})`
65
+ ).replaceAll(
66
+ "{{baseLocaleName}}",
67
+ `${(0, import_core.getLocaleName)(baseLocale, import_config.Locales.ENGLISH)} (${baseLocale})`
68
+ ).replace("{{applicationContext}}", aiOptions?.applicationContext ?? "-").replace("{{customInstructions}}", customInstructions ?? "-");
69
+ const baseChunks = (0, import_calculateChunks.chunkText)(basedFileContent, 800, 0);
70
+ appLogger(` Base file splitted into ${baseChunks.length} chunks`);
71
+ for (let i = 0; i < baseChunks.length; i++) {
72
+ const baseChunkContext = baseChunks[i];
73
+ const getBaseChunkContextPrompt = () => `**CHUNK ${i + 1} to ${Math.min(i + 3, baseChunks.length)} of ${baseChunks.length}** is the base chunk in ${(0, import_core.getLocaleName)(baseLocale, import_config.Locales.ENGLISH)} (${baseLocale}) as reference.
74
+ ///chunksStart///` + (baseChunks[i - 1]?.content ?? "") + baseChunkContext.content + (baseChunks[i + 1]?.content ?? "") + `///chunksEnd///`;
75
+ const getChunkToReviewPrompt = () => `**CHUNK ${i + 1} to ${Math.min(i + 3, baseChunks.length)} of ${baseChunks.length}** is the current chunk to review in ${(0, import_core.getLocaleName)(locale, import_config.Locales.ENGLISH)} (${locale}) as reference.
76
+ ///chunksStart///` + (0, import_getChunk.getChunk)(updatedFileContent, {
77
+ lineStart: baseChunks[i - 1]?.lineStart ?? 0,
78
+ lineLength: (baseChunks[i - 1]?.lineLength ?? 0) + baseChunkContext.lineLength + (baseChunks[i + 1]?.lineLength ?? 0)
79
+ }) + `///chunksEnd///`;
80
+ let reviewedChunkResult = await (0, import_config.retryManager)(async () => {
81
+ const result = await (0, import_chunkInference.chunkInference)(
82
+ [
83
+ { role: "system", content: basePrompt },
84
+ { role: "system", content: getBaseChunkContextPrompt() },
85
+ { role: "system", content: getChunkToReviewPrompt() },
86
+ {
87
+ role: "system",
88
+ content: `The next user message will be the **CHUNK ${i + 1} of ${baseChunks.length}** that should be translated in ${(0, import_core.getLocaleName)(locale, import_config.Locales.ENGLISH)} (${locale}).`
89
+ },
90
+ { role: "user", content: baseChunkContext.content }
91
+ ],
92
+ aiOptions,
93
+ oAuth2AccessToken
94
+ );
95
+ appLogger(
96
+ ` -> ${result.tokenUsed} tokens used - CHUNK ${i + 1} of ${baseChunks.length}`
97
+ );
98
+ const fixedReviewedChunkResult = (0, import_fixChunkStartEndChars.fixChunkStartEndChars)(
99
+ result?.fileContent,
100
+ baseChunkContext.content
101
+ );
102
+ return fixedReviewedChunkResult;
103
+ })();
104
+ updatedFileContent = updatedFileContent.replace(
105
+ baseChunkContext.content,
106
+ reviewedChunkResult
107
+ );
108
+ fileResultContent += reviewedChunkResult;
109
+ }
110
+ (0, import_fs.mkdirSync)((0, import_path.dirname)(outputFilePath), { recursive: true });
111
+ (0, import_fs.writeFileSync)(outputFilePath, fileResultContent);
112
+ appLogger(` File ${outputFilePath} created/updated successfully.`);
113
+ } catch (error) {
114
+ console.error(error);
115
+ }
116
+ };
117
+ const reviewDoc = async ({
118
+ docPattern,
119
+ locales,
120
+ excludedGlobPattern,
121
+ baseLocale,
122
+ aiOptions,
123
+ nbSimultaneousFileProcessed,
124
+ configOptions,
125
+ customInstructions,
126
+ skipIfModifiedBefore,
127
+ skipIfModifiedAfter
128
+ }) => {
129
+ const configuration = (0, import_config.getConfiguration)(configOptions);
130
+ const appLogger = (0, import_config.getAppLogger)(configuration);
131
+ if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {
132
+ appLogger(
133
+ `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`
134
+ );
135
+ nbSimultaneousFileProcessed = 10;
136
+ }
137
+ const limit = (0, import_p_limit.default)(nbSimultaneousFileProcessed ?? 3);
138
+ const docList = import_fast_glob.default.sync(docPattern, {
139
+ ignore: excludedGlobPattern
140
+ });
141
+ (0, import_checkAIAccess.checkAIAccess)(configuration, aiOptions);
142
+ let oAuth2AccessToken;
143
+ if (configuration.editor.clientId) {
144
+ const intlayerAuthAPI = (0, import_api.getAuthAPI)(void 0, configuration);
145
+ const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();
146
+ oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;
147
+ }
148
+ appLogger(
149
+ `Base locale is ${(0, import_core.getLocaleName)(baseLocale, import_config.Locales.ENGLISH)} (${baseLocale})`
150
+ );
151
+ appLogger(
152
+ `Reviewing ${locales.length} locales: [ ${locales.map((locale) => `${(0, import_core.getLocaleName)(locale, import_config.Locales.ENGLISH)} (${locale})`).join(", ")} ]`
153
+ );
154
+ appLogger(`Reviewing ${docList.length} files:`);
155
+ appLogger(docList.map((path) => ` - ${path}
156
+ `));
157
+ const tasks = docList.map(
158
+ (docPath) => locales.flatMap(
159
+ (locale) => limit(async () => {
160
+ appLogger(
161
+ `Reviewing file: ${docPath} to ${(0, import_core.getLocaleName)(
162
+ locale,
163
+ import_config.Locales.ENGLISH
164
+ )} (${locale})`
165
+ );
166
+ const absoluteBaseFilePath = (0, import_path.join)(
167
+ configuration.content.baseDir,
168
+ docPath
169
+ );
170
+ const outputFilePath = (0, import_getOutputFilePath.getOutputFilePath)(
171
+ absoluteBaseFilePath,
172
+ locale,
173
+ baseLocale
174
+ );
175
+ const fileModificationData = (0, import_checkFileModifiedRange.checkFileModifiedRange)(outputFilePath, {
176
+ skipIfModifiedBefore,
177
+ skipIfModifiedAfter
178
+ });
179
+ if (fileModificationData.isSkipped) {
180
+ appLogger(fileModificationData.message);
181
+ return;
182
+ }
183
+ await reviewFile(
184
+ absoluteBaseFilePath,
185
+ outputFilePath,
186
+ locale,
187
+ baseLocale,
188
+ aiOptions,
189
+ configOptions,
190
+ oAuth2AccessToken,
191
+ customInstructions
192
+ );
193
+ })
194
+ )
195
+ );
196
+ await Promise.all(tasks);
197
+ };
198
+ // Annotate the CommonJS export names for ESM import in node:
199
+ 0 && (module.exports = {
200
+ reviewDoc,
201
+ reviewFile
202
+ });
203
+ //# sourceMappingURL=reviewDoc.cjs.map