@intlayer/cli 8.4.2 → 8.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/auth/login.cjs +2 -2
- package/dist/cjs/auth/login.cjs.map +1 -1
- package/dist/cjs/cli.cjs +1 -1
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/extract.cjs +1 -1
- package/dist/cjs/extract.cjs.map +1 -1
- package/dist/cjs/fill/fill.cjs +1 -1
- package/dist/cjs/fill/fill.cjs.map +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs +1 -1
- package/dist/cjs/fill/listTranslationsTasks.cjs.map +1 -1
- package/dist/cjs/fill/translateDictionary.cjs +1 -1
- package/dist/cjs/fill/translateDictionary.cjs.map +1 -1
- package/dist/cjs/pull.cjs +1 -1
- package/dist/cjs/pull.cjs.map +1 -1
- package/dist/cjs/push/pullLog.cjs +3 -3
- package/dist/cjs/push/pullLog.cjs.map +1 -1
- package/dist/cjs/push/push.cjs +1 -1
- package/dist/cjs/push/push.cjs.map +1 -1
- package/dist/cjs/pushLog.cjs +3 -3
- package/dist/cjs/pushLog.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs +1 -1
- package/dist/cjs/reviewDoc/reviewDoc.cjs.map +1 -1
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs +1 -1
- package/dist/cjs/reviewDoc/reviewDocBlockAware.cjs.map +1 -1
- package/dist/cjs/test/test.cjs +1 -1
- package/dist/cjs/test/test.cjs.map +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs +1 -1
- package/dist/cjs/translateDoc/translateDoc.cjs.map +1 -1
- package/dist/cjs/translateDoc/translateFile.cjs +2 -2
- package/dist/cjs/translateDoc/translateFile.cjs.map +1 -1
- package/dist/cjs/utils/checkAccess.cjs +1 -1
- package/dist/cjs/utils/checkAccess.cjs.map +1 -1
- package/dist/cjs/utils/setupAI.cjs +1 -1
- package/dist/cjs/utils/setupAI.cjs.map +1 -1
- package/dist/esm/auth/login.mjs +1 -1
- package/dist/esm/auth/login.mjs.map +1 -1
- package/dist/esm/cli.mjs +1 -1
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/extract.mjs +1 -1
- package/dist/esm/extract.mjs.map +1 -1
- package/dist/esm/fill/fill.mjs +1 -1
- package/dist/esm/fill/fill.mjs.map +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs +1 -1
- package/dist/esm/fill/listTranslationsTasks.mjs.map +1 -1
- package/dist/esm/fill/translateDictionary.mjs +1 -1
- package/dist/esm/fill/translateDictionary.mjs.map +1 -1
- package/dist/esm/pull.mjs +1 -1
- package/dist/esm/pull.mjs.map +1 -1
- package/dist/esm/push/pullLog.mjs +1 -1
- package/dist/esm/push/pullLog.mjs.map +1 -1
- package/dist/esm/push/push.mjs +1 -1
- package/dist/esm/push/push.mjs.map +1 -1
- package/dist/esm/pushLog.mjs +1 -1
- package/dist/esm/pushLog.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs +1 -1
- package/dist/esm/reviewDoc/reviewDoc.mjs.map +1 -1
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs +1 -1
- package/dist/esm/reviewDoc/reviewDocBlockAware.mjs.map +1 -1
- package/dist/esm/test/test.mjs +1 -1
- package/dist/esm/test/test.mjs.map +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs +1 -1
- package/dist/esm/translateDoc/translateDoc.mjs.map +1 -1
- package/dist/esm/translateDoc/translateFile.mjs +1 -1
- package/dist/esm/translateDoc/translateFile.mjs.map +1 -1
- package/dist/esm/utils/checkAccess.mjs +1 -1
- package/dist/esm/utils/checkAccess.mjs.map +1 -1
- package/dist/esm/utils/setupAI.mjs +1 -1
- package/dist/esm/utils/setupAI.mjs.map +1 -1
- package/dist/types/auth/login.d.ts.map +1 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/push/pullLog.d.ts.map +1 -1
- package/dist/types/push/push.d.ts.map +1 -1
- package/dist/types/setupAI-Bosjx7ah.d.ts.map +1 -1
- package/dist/types/utils/checkAccess.d.ts.map +1 -1
- package/package.json +12 -12
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`})
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`./deepMergeContent.cjs`),n=require(`./getFilterMissingContentPerLocale.cjs`);let r=require(`node:path`),i=require(`@intlayer/chokidar/utils`),a=require(`@intlayer/config/colors`);a=e.t(a);let o=require(`@intlayer/config/logger`),s=require(`@intlayer/unmerged-dictionaries-entry`),c=require(`@intlayer/core/plugins`),l=require(`@intlayer/api`),u=require(`@intlayer/config/utils`);const d=e=>!e.description||!e.title||!e.tags,f=e=>{if(typeof e!=`object`||!e||Array.isArray(e))return{content:e,nulls:void 0,hasNulls:!1};let t={},n={},r=!1;for(let[i,a]of Object.entries(e))if(a===null)n[i]=null,r=!0;else{let e=f(a);t[i]=e.content,e.hasNulls&&(n[i]=e.nulls,r=!0)}return{content:t,nulls:r?n:void 0,hasNulls:r}},p=1e3*10;let m=0;const h=async(e,h,g)=>{let _=(0,o.getAppLogger)(h),v=(0,l.getIntlayerAPIProxy)(void 0,h),{mode:y,aiOptions:b,fillMetadata:x,aiClient:S,aiConfig:C}={mode:`complete`,fillMetadata:!0,...g},w=()=>{m=0,g?.onSuccess?.()};return await(0,u.retryManager)(async()=>{let l=(0,s.getUnmergedDictionaries)(h),T=l[e.dictionaryKey].find(t=>t.localId===e.dictionaryLocalId);if(!T)return _(`${e.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,{level:`warn`}),{...e,dictionaryOutput:null};let E;if(x&&(d(T)||y===`review`)){let t=(0,c.getPerLocaleDictionary)(T,h.internationalization.defaultLocale);_(`${e.dictionaryPreset} Filling missing metadata for ${(0,o.colorizePath)((0,r.basename)(T.filePath))}`,{level:`info`});let n=async()=>S&&C?{data:await S.auditDictionaryMetadata({fileContent:JSON.stringify(t),aiConfig:C})}:await v.ai.auditContentDeclarationMetadata({fileContent:JSON.stringify(t),aiOptions:b});E=(g?.onHandle?await g.onHandle(n):await n()).data?.fileContent}let D=await Promise.all(e.targetLocales.map(async s=>{let d=structuredClone(T),h;if(typeof T.locale==`string`){let t=T.filePath?.replace(RegExp(`/${e.sourceLocale}/`,`g`),`/${s}/`),r=t?l[e.dictionaryKey]?.find(e=>e.filePath===t&&e.locale===s):void 0;h=r??{key:T.key,content:{},filePath:t,locale:s},y===`complete`&&(d=n.getFilterMissingContentPerLocale(d,r))}else y===`complete`&&(d=(0,c.getFilterMissingTranslationsDictionary)(d,s)),d=(0,c.getPerLocaleDictionary)(d,e.sourceLocale),h=(0,c.getPerLocaleDictionary)(T,s);let x=(0,o.colon)([(0,o.colorize)(`[`,a.GREY_DARK),(0,i.formatLocale)(s),(0,o.colorize)(`]`,a.GREY_DARK)].join(``),{colSize:18}),D=(e,t)=>t<=1?``:(0,o.colon)([(0,o.colorize)(`[`,a.GREY_DARK),(0,o.colorizeNumber)(e+1),(0,o.colorize)(`/${t}`,a.GREY_DARK),(0,o.colorize)(`]`,a.GREY_DARK)].join(``),{colSize:5});_(`${e.dictionaryPreset}${x} Preparing ${(0,o.colorizePath)((0,r.basename)(h.filePath))}`,{level:`info`});let O=typeof d.content==`object`&&d.content!==null||Array.isArray(d.content),{content:k,nulls:A}=f(O?d.content:{__INTLAYER_ROOT_PRIMITIVE_CONTENT__:d.content}),j=(0,i.chunkJSON)(k,7e3),M=j.length;M>1&&_(`${e.dictionaryPreset}${x} Split into ${(0,o.colorizeNumber)(M)} chunks for translation`,{level:`info`});let N=[],P=j.map(t=>{let n=D(t.index,t.total);M>1&&_(`${e.dictionaryPreset}${x}${n} Translating chunk`,{level:`info`});let r=(0,i.reconstructFromSingleChunk)(t),c=(0,i.reduceObjectFormat)(O?h.content:{__INTLAYER_ROOT_PRIMITIVE_CONTENT__:h.content},r),l=async()=>await(0,u.retryManager)(async()=>{let t;if(t=S&&C?await S.translateJSON({entryFileContent:r,presetOutputContent:c,dictionaryDescription:d.description??E?.description??``,entryLocale:e.sourceLocale,outputLocale:s,mode:y,aiConfig:C}):await v.ai.translateJSON({entryFileContent:r,presetOutputContent:c,dictionaryDescription:d.description??E?.description??``,entryLocale:e.sourceLocale,outputLocale:s,mode:y,aiOptions:b}).then(e=>e.data),!t?.fileContent)throw Error(`No content result`);let{isIdentic:n}=(0,i.verifyIdenticObjectFormat)(t.fileContent,r);if(!n)throw Error(`Translation result does not match expected format`);return w(),t.fileContent},{maxRetry:3,delay:p,onError:({error:n,attempt:r,maxRetry:i})=>{let s=D(t.index,t.total);_(`${e.dictionaryPreset}${x}${s} ${(0,o.colorize)(`Error filling:`,a.RED)} ${(0,o.colorize)(typeof n==`string`?n:JSON.stringify(n),a.GREY_DARK)} - Attempt ${(0,o.colorizeNumber)(r+1)} of ${(0,o.colorizeNumber)(i)}`,{level:`error`}),m+=1,m>=10&&(_(`There is something wrong.`,{level:`error`}),process.exit(1))}})();return(g?.onHandle?g.onHandle(l):l()).then(e=>({chunk:t,result:e}))});(await Promise.all(P)).sort((e,t)=>e.chunk.index-t.chunk.index).forEach(({result:e})=>{N.push(e)});let F=(0,i.mergeChunks)(N);A&&(F=t.deepMergeContent(F,A));let I={...d,content:F}.content;return O||(I=I?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__),typeof T.locale==`string`&&(I=t.deepMergeContent(h.content??{},I)),[s,I]})),O=Object.fromEntries(D),k={...(0,c.getMultilingualDictionary)(T.locale?{...T,key:T.key,content:{}}:T),locale:void 0,...E};for(let t of e.targetLocales)O[t]&&(k=(0,c.insertContentInDictionary)(k,O[t],t));if(_(`${e.dictionaryPreset} ${(0,o.colorize)(`Translation completed successfully`,a.GREEN)} for ${(0,o.colorizePath)((0,r.basename)(k.filePath))}`,{level:`info`}),T.locale&&(T.fill===!0||T.fill===void 0)&&T.location===`local`){let t=T.filePath.split(`.`).slice(0,-1),n=t[t.length-1];return JSON.parse(JSON.stringify({...e,dictionaryOutput:{...k,fill:void 0,filled:!0}}).replaceAll(RegExp(`\\.${n}\\.[a-zA-Z0-9]+`,`g`),`.filled.${n}.json`))}return{...e,dictionaryOutput:k}},{maxRetry:2,delay:p,onError:({error:t,attempt:n,maxRetry:r})=>_(`${e.dictionaryPreset} ${(0,o.colorize)(`Error:`,a.RED)} ${(0,o.colorize)(typeof t==`string`?t:JSON.stringify(t),a.GREY_DARK)} - Attempt ${(0,o.colorizeNumber)(n+1)} of ${(0,o.colorizeNumber)(r)}`,{level:`error`}),onMaxTryReached:({error:t})=>_(`${e.dictionaryPreset} ${(0,o.colorize)(`Maximum number of retries reached:`,a.RED)} ${(0,o.colorize)(typeof t==`string`?t:JSON.stringify(t),a.GREY_DARK)}`,{level:`error`})})()};exports.translateDictionary=h;
|
|
2
2
|
//# sourceMappingURL=translateDictionary.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translateDictionary.cjs","names":["getFilterMissingContentPerLocale","ANSIColors","deepMergeContent"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig } from '@intlayer/ai';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport { getFilterMissingContentPerLocale } from './getFilterMissingContentPerLocale';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/chokidar/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\n/**\n * Recursively strips null values from an object, returning the cleaned content\n * and a separate object containing only the null-valued paths so they can be\n * re-injected after AI translation (nulls don't need translation).\n */\nconst stripNullValues = (\n obj: any\n): { content: any; nulls: any; hasNulls: boolean } => {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return { content: obj, nulls: undefined, hasNulls: false };\n }\n\n const content: any = {};\n const nulls: any = {};\n let hasNulls = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (value === null) {\n nulls[key] = null;\n hasNulls = true;\n } else {\n const child = stripNullValues(value);\n content[key] = child.content;\n if (child.hasNulls) {\n nulls[key] = child.nulls;\n hasNulls = true;\n }\n }\n }\n\n return { content, nulls: hasNulls ? nulls : undefined, hasNulls };\n};\n\nconst CHUNK_SIZE = 7000; // GPT-5 Mini safe input size\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey].find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Load the existing target locale dictionary\n const targetLocaleFilePath =\n baseUnmergedDictionary.filePath?.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n );\n\n // Find the target locale dictionary in unmerged dictionaries\n const targetUnmergedDictionary = targetLocaleFilePath\n ? unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) =>\n dict.filePath === targetLocaleFilePath &&\n dict.locale === targetLocale\n )\n : undefined;\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: targetLocaleFilePath,\n locale: targetLocale,\n };\n\n // In complete mode, filter out already translated content\n if (mode === 'complete') {\n dictionaryToProcess = getFilterMissingContentPerLocale(\n dictionaryToProcess,\n targetUnmergedDictionary\n );\n }\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n const createChunkPreset = (\n chunkIndex: number,\n totalChunks: number\n ) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n };\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const isContentStructured =\n (typeof dictionaryToProcess.content === 'object' &&\n dictionaryToProcess.content !== null) ||\n Array.isArray(dictionaryToProcess.content);\n\n const rawContentToProcess = isContentStructured\n ? dictionaryToProcess.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n dictionaryToProcess.content,\n };\n\n // Strip null values before sending to AI — nulls need no translation\n // and confuse the model. They will be re-injected after merging.\n const { content: contentToProcess, nulls: strippedNullValues } =\n stripNullValues(rawContentToProcess);\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n contentToProcess as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map((chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n // Reconstruct partial JSON content from this chunk's patches\n const chunkContent = reconstructFromSingleChunk(chunk);\n const presetOutputContent = reduceObjectFormat(\n isContentStructured\n ? targetLocaleDictionary.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n targetLocaleDictionary.content,\n },\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkContent\n );\n\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge partial JSON objects produced from each chunk into a single object\n let mergedContent = mergeChunks(chunkResult);\n\n // Re-inject null values that were stripped before AI translation\n if (strippedNullValues) {\n mergedContent = deepMergeContent(mergedContent, strippedNullValues);\n }\n\n const merged = {\n ...dictionaryToProcess,\n content: mergedContent,\n };\n\n // For per-locale files, merge the newly translated content with existing target content\n let finalContent = merged.content;\n\n if (!isContentStructured) {\n finalContent = (finalContent as any)\n ?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__;\n }\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // Deep merge: existing content + newly translated content\n finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n finalContent\n );\n }\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n if (\n baseUnmergedDictionary.locale &&\n (baseUnmergedDictionary.fill === true ||\n baseUnmergedDictionary.fill === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"translateDictionary.cjs","names":["getFilterMissingContentPerLocale","ANSIColors","deepMergeContent"],"sources":["../../../src/fill/translateDictionary.ts"],"sourcesContent":["import { basename } from 'node:path';\nimport type { AIConfig } from '@intlayer/ai';\nimport { type AIOptions, getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n chunkJSON,\n formatLocale,\n type JsonChunk,\n mergeChunks,\n reconstructFromSingleChunk,\n reduceObjectFormat,\n verifyIdenticObjectFormat,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeNumber,\n colorizePath,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport { retryManager } from '@intlayer/config/utils';\nimport {\n getFilterMissingTranslationsDictionary,\n getMultilingualDictionary,\n getPerLocaleDictionary,\n insertContentInDictionary,\n} from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport type { AIClient } from '../utils/setupAI';\nimport { deepMergeContent } from './deepMergeContent';\nimport { getFilterMissingContentPerLocale } from './getFilterMissingContentPerLocale';\nimport type { TranslationTask } from './listTranslationsTasks';\n\ntype TranslateDictionaryResult = TranslationTask & {\n dictionaryOutput: Dictionary | null;\n};\n\ntype TranslateDictionaryOptions = {\n mode: 'complete' | 'review';\n aiOptions?: AIOptions;\n fillMetadata?: boolean;\n onHandle?: ReturnType<\n typeof import('@intlayer/chokidar/utils').getGlobalLimiter\n >;\n onSuccess?: () => void;\n onError?: (error: unknown) => void;\n getAbortError?: () => Error | null;\n aiClient?: AIClient;\n aiConfig?: AIConfig;\n};\n\nconst hasMissingMetadata = (dictionary: Dictionary) =>\n !dictionary.description || !dictionary.title || !dictionary.tags;\n\n/**\n * Recursively strips null values from an object, returning the cleaned content\n * and a separate object containing only the null-valued paths so they can be\n * re-injected after AI translation (nulls don't need translation).\n */\nconst stripNullValues = (\n obj: any\n): { content: any; nulls: any; hasNulls: boolean } => {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return { content: obj, nulls: undefined, hasNulls: false };\n }\n\n const content: any = {};\n const nulls: any = {};\n let hasNulls = false;\n\n for (const [key, value] of Object.entries(obj)) {\n if (value === null) {\n nulls[key] = null;\n hasNulls = true;\n } else {\n const child = stripNullValues(value);\n content[key] = child.content;\n if (child.hasNulls) {\n nulls[key] = child.nulls;\n hasNulls = true;\n }\n }\n }\n\n return { content, nulls: hasNulls ? nulls : undefined, hasNulls };\n};\n\nconst CHUNK_SIZE = 7000; // GPT-5 Mini safe input size\nconst GROUP_MAX_RETRY = 2;\nconst MAX_RETRY = 3;\nconst RETRY_DELAY = 1000 * 10; // 10 seconds\n\nconst MAX_FOLLOWING_ERRORS = 10; // 10 errors in a row, hard exit the process\nlet followingErrors = 0;\n\nexport const translateDictionary = async (\n task: TranslationTask,\n configuration: IntlayerConfig,\n options?: TranslateDictionaryOptions\n): Promise<TranslateDictionaryResult> => {\n const appLogger = getAppLogger(configuration);\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const { mode, aiOptions, fillMetadata, aiClient, aiConfig } = {\n mode: 'complete',\n fillMetadata: true,\n ...options,\n } as const;\n\n const notifySuccess = () => {\n followingErrors = 0;\n options?.onSuccess?.();\n };\n\n const result = await retryManager(\n async () => {\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n\n const baseUnmergedDictionary: Dictionary | undefined =\n unmergedDictionariesRecord[task.dictionaryKey].find(\n (dict) => dict.localId === task.dictionaryLocalId\n );\n\n if (!baseUnmergedDictionary) {\n appLogger(\n `${task.dictionaryPreset}Dictionary not found in unmergedDictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n return { ...task, dictionaryOutput: null };\n }\n\n let metadata:\n | Pick<Dictionary, 'description' | 'title' | 'tags'>\n | undefined;\n\n if (\n fillMetadata &&\n (hasMissingMetadata(baseUnmergedDictionary) || mode === 'review')\n ) {\n const defaultLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n configuration.internationalization.defaultLocale\n );\n\n appLogger(\n `${task.dictionaryPreset} Filling missing metadata for ${colorizePath(basename(baseUnmergedDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const runAudit = async () => {\n if (aiClient && aiConfig) {\n const result = await aiClient.auditDictionaryMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiConfig,\n });\n\n return {\n data: result,\n };\n }\n\n return await intlayerAPI.ai.auditContentDeclarationMetadata({\n fileContent: JSON.stringify(defaultLocaleDictionary),\n aiOptions,\n });\n };\n\n const metadataResult = options?.onHandle\n ? await options.onHandle(runAudit)\n : await runAudit();\n\n metadata = metadataResult.data?.fileContent;\n }\n\n const translatedContentResults = await Promise.all(\n task.targetLocales.map(async (targetLocale) => {\n /**\n * In complete mode, for large dictionaries, we want to filter all content that is already translated\n *\n * targetLocale: fr\n *\n * { test1: t({ ar: 'Hello', en: 'Hello', fr: 'Bonjour' } }) -> {}\n * { test2: t({ ar: 'Hello', en: 'Hello' }) } -> { test2: t({ ar: 'Hello', en: 'Hello' }) }\n *\n */\n // Reset to base dictionary for each locale to ensure we filter from the original\n let dictionaryToProcess = structuredClone(baseUnmergedDictionary);\n\n let targetLocaleDictionary: Dictionary;\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // For per-locale files, the content is already in simple JSON format (not translation nodes)\n // The base dictionary is already the source locale content\n\n // Load the existing target locale dictionary\n const targetLocaleFilePath =\n baseUnmergedDictionary.filePath?.replace(\n new RegExp(`/${task.sourceLocale}/`, 'g'),\n `/${targetLocale}/`\n );\n\n // Find the target locale dictionary in unmerged dictionaries\n const targetUnmergedDictionary = targetLocaleFilePath\n ? unmergedDictionariesRecord[task.dictionaryKey]?.find(\n (dict) =>\n dict.filePath === targetLocaleFilePath &&\n dict.locale === targetLocale\n )\n : undefined;\n\n targetLocaleDictionary = targetUnmergedDictionary ?? {\n key: baseUnmergedDictionary.key,\n content: {},\n filePath: targetLocaleFilePath,\n locale: targetLocale,\n };\n\n // In complete mode, filter out already translated content\n if (mode === 'complete') {\n dictionaryToProcess = getFilterMissingContentPerLocale(\n dictionaryToProcess,\n targetUnmergedDictionary\n );\n }\n } else {\n // For multilingual dictionaries\n if (mode === 'complete') {\n // Remove all nodes that don't have any content to translate\n dictionaryToProcess = getFilterMissingTranslationsDictionary(\n dictionaryToProcess,\n targetLocale\n );\n }\n\n dictionaryToProcess = getPerLocaleDictionary(\n dictionaryToProcess,\n task.sourceLocale\n );\n\n targetLocaleDictionary = getPerLocaleDictionary(\n baseUnmergedDictionary,\n targetLocale\n );\n }\n\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 18 }\n );\n\n const createChunkPreset = (\n chunkIndex: number,\n totalChunks: number\n ) => {\n if (totalChunks <= 1) return '';\n return colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n colorizeNumber(chunkIndex + 1),\n colorize(`/${totalChunks}`, ANSIColors.GREY_DARK),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: 5 }\n );\n };\n\n appLogger(\n `${task.dictionaryPreset}${localePreset} Preparing ${colorizePath(basename(targetLocaleDictionary.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n const isContentStructured =\n (typeof dictionaryToProcess.content === 'object' &&\n dictionaryToProcess.content !== null) ||\n Array.isArray(dictionaryToProcess.content);\n\n const rawContentToProcess = isContentStructured\n ? dictionaryToProcess.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n dictionaryToProcess.content,\n };\n\n // Strip null values before sending to AI — nulls need no translation\n // and confuse the model. They will be re-injected after merging.\n const { content: contentToProcess, nulls: strippedNullValues } =\n stripNullValues(rawContentToProcess);\n\n const chunkedJsonContent: JsonChunk[] = chunkJSON(\n contentToProcess as unknown as Record<string, any>,\n CHUNK_SIZE\n );\n\n const nbOfChunks = chunkedJsonContent.length;\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset} Split into ${colorizeNumber(nbOfChunks)} chunks for translation`,\n {\n level: 'info',\n }\n );\n }\n\n const chunkResult: JsonChunk[] = [];\n\n // Process chunks in parallel (globally throttled) to allow concurrent translation\n const chunkPromises = chunkedJsonContent.map((chunk) => {\n const chunkPreset = createChunkPreset(chunk.index, chunk.total);\n\n if (nbOfChunks > 1) {\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} Translating chunk`,\n {\n level: 'info',\n }\n );\n }\n\n // Reconstruct partial JSON content from this chunk's patches\n const chunkContent = reconstructFromSingleChunk(chunk);\n const presetOutputContent = reduceObjectFormat(\n isContentStructured\n ? targetLocaleDictionary.content\n : {\n __INTLAYER_ROOT_PRIMITIVE_CONTENT__:\n targetLocaleDictionary.content,\n },\n chunkContent\n ) as unknown as JSON;\n\n const executeTranslation = async () => {\n return await retryManager(\n async () => {\n let translationResult: any;\n\n if (aiClient && aiConfig) {\n translationResult = await aiClient.translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiConfig,\n });\n } else {\n translationResult = await intlayerAPI.ai\n .translateJSON({\n entryFileContent: chunkContent as unknown as JSON,\n presetOutputContent,\n dictionaryDescription:\n dictionaryToProcess.description ??\n metadata?.description ??\n '',\n entryLocale: task.sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions,\n })\n .then((result) => result.data);\n }\n\n if (!translationResult?.fileContent) {\n throw new Error('No content result');\n }\n\n const { isIdentic } = verifyIdenticObjectFormat(\n translationResult.fileContent,\n chunkContent\n );\n\n if (!isIdentic) {\n throw new Error(\n 'Translation result does not match expected format'\n );\n }\n\n notifySuccess();\n return translationResult.fileContent;\n },\n {\n maxRetry: MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) => {\n const chunkPreset = createChunkPreset(\n chunk.index,\n chunk.total\n );\n appLogger(\n `${task.dictionaryPreset}${localePreset}${chunkPreset} ${colorize('Error filling:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n );\n\n followingErrors += 1;\n\n if (followingErrors >= MAX_FOLLOWING_ERRORS) {\n appLogger(`There is something wrong.`, {\n level: 'error',\n });\n process.exit(1); // 1 for error\n }\n },\n }\n )();\n };\n\n const wrapped = options?.onHandle\n ? options.onHandle(executeTranslation) // queued in global limiter\n : executeTranslation(); // no global limiter\n\n return wrapped.then((result) => ({ chunk, result }));\n });\n\n // Wait for all chunks for this locale in parallel (still capped by global limiter)\n const chunkResults = await Promise.all(chunkPromises);\n\n // Maintain order\n chunkResults\n .sort((chunkA, chunkB) => chunkA.chunk.index - chunkB.chunk.index)\n .forEach(({ result }) => {\n chunkResult.push(result);\n });\n\n // Merge partial JSON objects produced from each chunk into a single object\n let mergedContent = mergeChunks(chunkResult);\n\n // Re-inject null values that were stripped before AI translation\n if (strippedNullValues) {\n mergedContent = deepMergeContent(mergedContent, strippedNullValues);\n }\n\n const merged = {\n ...dictionaryToProcess,\n content: mergedContent,\n };\n\n // For per-locale files, merge the newly translated content with existing target content\n let finalContent = merged.content;\n\n if (!isContentStructured) {\n finalContent = (finalContent as any)\n ?.__INTLAYER_ROOT_PRIMITIVE_CONTENT__;\n }\n\n if (typeof baseUnmergedDictionary.locale === 'string') {\n // Deep merge: existing content + newly translated content\n finalContent = deepMergeContent(\n targetLocaleDictionary.content ?? {},\n finalContent\n );\n }\n\n return [targetLocale, finalContent] as const;\n })\n );\n\n const translatedContent: Partial<Record<Locale, Dictionary['content']>> =\n Object.fromEntries(translatedContentResults);\n\n const baseDictionary = baseUnmergedDictionary.locale\n ? {\n ...baseUnmergedDictionary,\n key: baseUnmergedDictionary.key!,\n content: {},\n }\n : baseUnmergedDictionary;\n\n let dictionaryOutput: Dictionary = {\n ...getMultilingualDictionary(baseDictionary),\n locale: undefined, // Ensure the dictionary is multilingual\n ...metadata,\n };\n\n for (const targetLocale of task.targetLocales) {\n if (translatedContent[targetLocale]) {\n dictionaryOutput = insertContentInDictionary(\n dictionaryOutput,\n translatedContent[targetLocale],\n targetLocale\n );\n }\n }\n\n appLogger(\n `${task.dictionaryPreset} ${colorize('Translation completed successfully', ANSIColors.GREEN)} for ${colorizePath(basename(dictionaryOutput.filePath!))}`,\n {\n level: 'info',\n }\n );\n\n if (\n baseUnmergedDictionary.locale &&\n (baseUnmergedDictionary.fill === true ||\n baseUnmergedDictionary.fill === undefined) &&\n baseUnmergedDictionary.location === 'local'\n ) {\n const dictionaryFilePath = baseUnmergedDictionary\n .filePath!.split('.')\n .slice(0, -1);\n\n const contentIndex = dictionaryFilePath[dictionaryFilePath.length - 1];\n\n return JSON.parse(\n JSON.stringify({\n ...task,\n dictionaryOutput: {\n ...dictionaryOutput,\n fill: undefined,\n filled: true,\n },\n }).replaceAll(\n new RegExp(`\\\\.${contentIndex}\\\\.[a-zA-Z0-9]+`, 'g'),\n `.filled.${contentIndex}.json`\n )\n );\n }\n\n return {\n ...task,\n dictionaryOutput,\n };\n },\n {\n maxRetry: GROUP_MAX_RETRY,\n delay: RETRY_DELAY,\n onError: ({ error, attempt, maxRetry }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Error:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)} - Attempt ${colorizeNumber(attempt + 1)} of ${colorizeNumber(maxRetry)}`,\n {\n level: 'error',\n }\n ),\n onMaxTryReached: ({ error }) =>\n appLogger(\n `${task.dictionaryPreset} ${colorize('Maximum number of retries reached:', ANSIColors.RED)} ${colorize(typeof error === 'string' ? error : JSON.stringify(error), ANSIColors.GREY_DARK)}`,\n {\n level: 'error',\n }\n ),\n }\n )();\n\n return result as TranslateDictionaryResult;\n};\n"],"mappings":"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"}
|
package/dist/cjs/pull.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`})
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunk-Bmb41Sf3.cjs`),t=require(`./utils/checkAccess.cjs`),n=require(`./push/pullLog.cjs`);let r=require(`node:fs`),i=require(`node:path`),a=require(`@intlayer/chokidar/build`),o=require(`@intlayer/chokidar/cli`),s=require(`@intlayer/chokidar/utils`),c=require(`@intlayer/config/colors`);c=e.t(c);let l=require(`@intlayer/config/logger`),u=require(`@intlayer/config/node`),d=require(`@intlayer/unmerged-dictionaries-entry`),f=require(`@intlayer/api`),p=require(`@intlayer/config/utils`);const m=async e=>{let m=(0,l.getAppLogger)(e?.configOptions?.override);try{let l=(0,u.getConfiguration)(e?.configOptions);if((0,o.logConfigDetails)(e?.configOptions),!await t.checkCMSAuth(l))return;let h=(0,f.getIntlayerAPIProxy)(void 0,l),g=(0,d.getUnmergedDictionaries)(l),_=await h.dictionary.getDictionariesUpdateTimestamp();if(!_.data)throw Error(`No distant dictionaries found`);let v=_.data;e?.dictionaries&&(v=Object.fromEntries(Object.entries(v).filter(([t])=>e.dictionaries?.includes(t)))),v=Object.fromEntries(Object.entries(v).filter(([e])=>{let t=g[e]?.[0]?.location??l.dictionary?.location??`remote`;return t===`remote`||t===`hybrid`}));let y=(0,i.join)(l.system.mainDir,`remote_dictionaries.cjs`),b=l.build?.require??(0,p.getProjectRequire)(),x=(0,r.existsSync)(y)?b(y):{},S=Object.entries(v),C=S.filter(([e,t])=>{if(!t)return!0;let n=typeof t==`object`?t.updatedAt:t,r=x[e];if(!r)return!0;let i=r?.updatedAt,a=typeof i==`number`?i:i?new Date(i).getTime():void 0;return typeof a==`number`?n>a:!0}).map(([e])=>e),w=S.filter(([e,t])=>{let n=typeof t==`object`?t.updatedAt:t,r=x[e]?.updatedAt,i=typeof r==`number`?r:r?new Date(r).getTime():void 0;return typeof i==`number`&&typeof n==`number`&&i>=n}).map(([e])=>e);if(S.length===0){m(`No dictionaries to fetch`,{level:`error`});return}m(`Fetching dictionaries:`);let T=[...w.map(e=>({dictionaryKey:e,status:`imported`})),...C.map(e=>({dictionaryKey:e,status:`pending`}))],E=new n.PullLogger;E.update(T.map(e=>({dictionaryKey:e.dictionaryKey,status:e.status})));let D=[];await(0,s.parallelize)(T,async t=>{let n=t.status===`imported`||t.status===`up-to-date`;n||(t.status=`fetching`,E.update([{dictionaryKey:t.dictionaryKey,status:`fetching`}]));try{let r;if(n&&(r=x[t.dictionaryKey]),r||=(await h.dictionary.getDictionary(t.dictionaryKey)).data,!r)throw Error(`Dictionary ${t.dictionaryKey} not found on remote`);let i=g[t.dictionaryKey]?.find(e=>e.location===`hybrid`);i&&(r={...r,location:`hybrid`,filePath:i.filePath,localId:i.localId});let{status:o}=await(0,a.writeContentDeclaration)(r,l,e);t.status=o,E.update([{dictionaryKey:t.dictionaryKey,status:o}]),D.push(r)}catch(e){t.status=`error`,t.error=e,t.errorMessage=`Error fetching dictionary ${t.dictionaryKey}: ${e}`,E.update([{dictionaryKey:t.dictionaryKey,status:`error`}])}},5),E.finish();let O=e=>{switch(e){case`fetched`:case`imported`:case`updated`:case`up-to-date`:case`reimported in JSON`:case`new content file`:return`✔`;case`error`:return`✖`;default:return`⏲`}},k=e=>{switch(e){case`fetched`:case`imported`:case`updated`:case`up-to-date`:return c.GREEN;case`reimported in JSON`:case`new content file`:return c.YELLOW;case`error`:return c.RED;default:return c.BLUE}};for(let e of T){let t=O(e.status),n=k(e.status);m(` - ${e.dictionaryKey} ${c.GREY}[${n}${t} ${e.status}${c.GREY}]${c.RESET}`)}for(let e of T)e.errorMessage&&m(e.errorMessage,{level:`error`})}catch(e){m(e,{level:`error`})}};exports.pull=m;
|
|
2
2
|
//# sourceMappingURL=pull.cjs.map
|
package/dist/cjs/pull.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport { parallelize } from '@intlayer/chokidar/utils';\nimport { ANSIColors, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\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 logConfigDetails(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'hybrid';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.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 ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Check if there is a local version of this dictionary that is hybrid\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'hybrid'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'hybrid',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pull.cjs","names":["checkCMSAuth","PullLogger","ANSIColors"],"sources":["../../src/pull.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n type DictionaryStatus,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport { logConfigDetails } from '@intlayer/chokidar/cli';\nimport { parallelize } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport { getProjectRequire } from '@intlayer/config/utils';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PullLogger, type PullStatus } from './push/pullLog';\nimport { checkCMSAuth } from './utils/checkAccess';\n\ntype PullOptions = {\n dictionaries?: string[];\n newDictionariesPath?: string;\n configOptions?: GetConfigurationOptions;\n};\n\ntype DictionariesStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching' | 'error';\n error?: Error;\n errorMessage?: string;\n};\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 logConfigDetails(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n // Get remote update timestamps map\n const getDictionariesUpdateTimestampResult =\n await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();\n\n if (!getDictionariesUpdateTimestampResult.data) {\n throw new Error('No distant dictionaries found');\n }\n\n let distantDictionariesUpdateTimeStamp: Record<string, any> =\n getDictionariesUpdateTimestampResult.data;\n\n // Optional filtering by requested dictionaries\n if (options?.dictionaries) {\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) =>\n options.dictionaries?.includes(key)\n )\n );\n }\n\n // Filter by location\n distantDictionariesUpdateTimeStamp = Object.fromEntries(\n Object.entries(distantDictionariesUpdateTimeStamp).filter(([key]) => {\n const localDictionaries = unmergedDictionariesRecord[key];\n const location =\n localDictionaries?.[0]?.location ??\n config.dictionary?.location ??\n 'remote';\n\n return location === 'remote' || location === 'hybrid';\n })\n );\n\n // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.system.mainDir,\n 'remote_dictionaries.cjs'\n );\n const requireFunction = config.build?.require ?? getProjectRequire();\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (requireFunction(remoteDictionariesPath) as any)\n : {};\n\n // Determine which keys need fetching by comparing updatedAt with local cache\n const entries = Object.entries(distantDictionariesUpdateTimeStamp);\n const keysToFetch = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n if (!remoteUpdatedAtValue) return true;\n\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n if (!local) return true;\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n if (typeof localUpdatedAt !== 'number') return true;\n return remoteUpdatedAt > localUpdatedAt;\n })\n .map(([key]) => key);\n\n const cachedKeys = entries\n .filter(([key, remoteUpdatedAtValue]) => {\n const remoteUpdatedAt =\n typeof remoteUpdatedAtValue === 'object'\n ? (remoteUpdatedAtValue as any).updatedAt\n : remoteUpdatedAtValue;\n\n const local = (remoteDictionariesRecord as any)[key];\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n })\n .map(([key]) => key);\n\n // Check if dictionaries list is empty\n if (entries.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 ...cachedKeys.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'imported' as DictionaryStatus,\n })),\n ...keysToFetch.map((dictionaryKey) => ({\n dictionaryKey,\n status: 'pending' as const,\n })),\n ];\n\n // Initialize aggregated logger\n const logger = new PullLogger();\n logger.update(\n dictionariesStatuses.map<PullStatus>((s) => ({\n dictionaryKey: s.dictionaryKey,\n status: s.status,\n }))\n );\n\n const successfullyFetchedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n const isCached =\n statusObj.status === 'imported' || statusObj.status === 'up-to-date';\n\n if (!isCached) {\n statusObj.status = 'fetching';\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'fetching' },\n ]);\n }\n\n try {\n let sourceDictionary: Dictionary | undefined;\n\n if (isCached) {\n sourceDictionary = remoteDictionariesRecord[\n statusObj.dictionaryKey\n ] as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n // Fetch the dictionary\n const getDictionaryResult =\n await intlayerAPI.dictionary.getDictionary(statusObj.dictionaryKey);\n\n sourceDictionary = getDictionaryResult.data as Dictionary | undefined;\n }\n\n if (!sourceDictionary) {\n throw new Error(\n `Dictionary ${statusObj.dictionaryKey} not found on remote`\n );\n }\n\n // Check if there is a local version of this dictionary that is hybrid\n const localDictionaries =\n unmergedDictionariesRecord[statusObj.dictionaryKey];\n const localAndRemoteDictionary = localDictionaries?.find(\n (d) => d.location === 'hybrid'\n );\n\n if (localAndRemoteDictionary) {\n // We want to preserve the local properties but use the remote content\n sourceDictionary = {\n ...sourceDictionary,\n location: 'hybrid',\n filePath: localAndRemoteDictionary.filePath,\n localId: localAndRemoteDictionary.localId,\n };\n }\n\n // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options\n );\n\n statusObj.status = status;\n logger.update([{ dictionaryKey: statusObj.dictionaryKey, status }]);\n\n successfullyFetchedDictionaries.push(sourceDictionary);\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error fetching dictionary ${statusObj.dictionaryKey}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionaryKey, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with concurrency limit\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n // Per-dictionary summary\n const iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n case 'reimported in JSON':\n case 'new content file':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n };\n\n const colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'fetched':\n case 'imported':\n case 'updated':\n case 'up-to-date':\n return ANSIColors.GREEN;\n case 'reimported in JSON':\n case 'new content file':\n return ANSIColors.YELLOW;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n };\n\n for (const s of dictionariesStatuses) {\n const icon = iconFor(s.status);\n const color = colorFor(s.status);\n appLogger(\n ` - ${s.dictionaryKey} ${ANSIColors.GREY}[${color}${icon} ${s.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\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"],"mappings":"4jBAsCA,MAAa,EAAO,KAAO,IAAyC,CAClE,IAAM,GAAA,EAAA,EAAA,cAAyB,GAAS,eAAe,SAAS,CAEhE,GAAI,CACF,IAAM,GAAA,EAAA,EAAA,kBAA0B,GAAS,cAAc,CAKvD,IAJA,EAAA,EAAA,kBAAiB,GAAS,cAAc,CAIpC,CAFe,MAAMA,EAAAA,aAAa,EAAO,CAE5B,OAEjB,IAAM,GAAA,EAAA,EAAA,qBAAkC,IAAA,GAAW,EAAO,CAEpD,GAAA,EAAA,EAAA,yBAAqD,EAAO,CAG5D,EACJ,MAAM,EAAY,WAAW,gCAAgC,CAE/D,GAAI,CAAC,EAAqC,KACxC,MAAU,MAAM,gCAAgC,CAGlD,IAAI,EACF,EAAqC,KAGnC,GAAS,eACX,EAAqC,OAAO,YAC1C,OAAO,QAAQ,EAAmC,CAAC,QAAQ,CAAC,KAC1D,EAAQ,cAAc,SAAS,EAAI,CACpC,CACF,EAIH,EAAqC,OAAO,YAC1C,OAAO,QAAQ,EAAmC,CAAC,QAAQ,CAAC,KAAS,CAEnE,IAAM,EADoB,EAA2B,KAE/B,IAAI,UACxB,EAAO,YAAY,UACnB,SAEF,OAAO,IAAa,UAAY,IAAa,UAC7C,CACH,CAGD,IAAM,GAAA,EAAA,EAAA,MACJ,EAAO,OAAO,QACd,0BACD,CACK,EAAkB,EAAO,OAAO,UAAA,EAAA,EAAA,oBAA8B,CAC9D,GAAA,EAAA,EAAA,YACJ,EACD,CACI,EAAgB,EAAuB,CACxC,EAAE,CAGA,EAAU,OAAO,QAAQ,EAAmC,CAC5D,EAAc,EACjB,QAAQ,CAAC,EAAK,KAA0B,CACvC,GAAI,CAAC,EAAsB,MAAO,GAElC,IAAM,EACJ,OAAO,GAAyB,SAC3B,EAA6B,UAC9B,EAEA,EAAS,EAAiC,GAChD,GAAI,CAAC,EAAO,MAAO,GACnB,IAAM,EAAqB,GAAe,UAIpC,EACJ,OAAO,GAAsB,SACzB,EACA,EACE,IAAI,KAAK,EAAkB,CAAC,SAAS,CACrC,IAAA,GAER,OADI,OAAO,GAAmB,SACvB,EAAkB,EADsB,IAE/C,CACD,KAAK,CAAC,KAAS,EAAI,CAEhB,EAAa,EAChB,QAAQ,CAAC,EAAK,KAA0B,CACvC,IAAM,EACJ,OAAO,GAAyB,SAC3B,EAA6B,UAC9B,EAGA,EADS,EAAiC,IACN,UAIpC,EACJ,OAAO,GAAsB,SACzB,EACA,EACE,IAAI,KAAK,EAAkB,CAAC,SAAS,CACrC,IAAA,GACR,OACE,OAAO,GAAmB,UAC1B,OAAO,GAAoB,UAC3B,GAAkB,GAEpB,CACD,KAAK,CAAC,KAAS,EAAI,CAGtB,GAAI,EAAQ,SAAW,EAAG,CACxB,EAAU,2BAA4B,CACpC,MAAO,QACR,CAAC,CACF,OAGF,EAAU,yBAAyB,CAGnC,IAAM,EAA6C,CACjD,GAAG,EAAW,IAAK,IAAmB,CACpC,gBACA,OAAQ,WACT,EAAE,CACH,GAAG,EAAY,IAAK,IAAmB,CACrC,gBACA,OAAQ,UACT,EAAE,CACJ,CAGK,EAAS,IAAIC,EAAAA,WACnB,EAAO,OACL,EAAqB,IAAiB,IAAO,CAC3C,cAAe,EAAE,cACjB,OAAQ,EAAE,OACX,EAAE,CACJ,CAED,IAAM,EAAgD,EAAE,CA6ExD,MAAA,EAAA,EAAA,aAAkB,EA3EQ,KACxB,IACkB,CAClB,IAAM,EACJ,EAAU,SAAW,YAAc,EAAU,SAAW,aAErD,IACH,EAAU,OAAS,WACnB,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,cAAe,OAAQ,WAAY,CAC/D,CAAC,EAGJ,GAAI,CACF,IAAI,EAgBJ,GAdI,IACF,EAAmB,EACjB,EAAU,gBAId,AAKE,KAFE,MAAM,EAAY,WAAW,cAAc,EAAU,cAAc,EAE9B,KAGrC,CAAC,EACH,MAAU,MACR,cAAc,EAAU,cAAc,sBACvC,CAMH,IAAM,EADJ,EAA2B,EAAU,gBACa,KACjD,GAAM,EAAE,WAAa,SACvB,CAEG,IAEF,EAAmB,CACjB,GAAG,EACH,SAAU,SACV,SAAU,EAAyB,SACnC,QAAS,EAAyB,QACnC,EAIH,GAAM,CAAE,UAAW,MAAA,EAAA,EAAA,yBACjB,EACA,EACA,EACD,CAED,EAAU,OAAS,EACnB,EAAO,OAAO,CAAC,CAAE,cAAe,EAAU,cAAe,SAAQ,CAAC,CAAC,CAEnE,EAAgC,KAAK,EAAiB,OAC/C,EAAO,CACd,EAAU,OAAS,QACnB,EAAU,MAAQ,EAClB,EAAU,aAAe,6BAA6B,EAAU,cAAc,IAAI,IAClF,EAAO,OAAO,CACZ,CAAE,cAAe,EAAU,cAAe,OAAQ,QAAS,CAC5D,CAAC,GAKqD,EAAE,CAG7D,EAAO,QAAQ,CAGf,IAAM,EAAW,GAAyC,CACxD,OAAQ,EAAR,CACE,IAAK,UACL,IAAK,WACL,IAAK,UACL,IAAK,aACL,IAAK,qBACL,IAAK,mBACH,MAAO,IACT,IAAK,QACH,MAAO,IACT,QACE,MAAO,MAIP,EAAY,GAAyC,CACzD,OAAQ,EAAR,CACE,IAAK,UACL,IAAK,WACL,IAAK,UACL,IAAK,aACH,OAAOC,EAAW,MACpB,IAAK,qBACL,IAAK,mBACH,OAAOA,EAAW,OACpB,IAAK,QACH,OAAOA,EAAW,IACpB,QACE,OAAOA,EAAW,OAIxB,IAAK,IAAM,KAAK,EAAsB,CACpC,IAAM,EAAO,EAAQ,EAAE,OAAO,CACxB,EAAQ,EAAS,EAAE,OAAO,CAChC,EACE,MAAM,EAAE,cAAc,GAAGA,EAAW,KAAK,GAAG,IAAQ,EAAK,GAAG,EAAE,SAASA,EAAW,KAAK,GAAGA,EAAW,QACtG,CAIH,IAAK,IAAM,KAAa,EAClB,EAAU,cACZ,EAAU,EAAU,aAAc,CAChC,MAAO,QACR,CAAC,OAGC,EAAO,CACd,EAAU,EAAO,CACf,MAAO,QACR,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`})
|
|
2
|
-
`);if(
|
|
3
|
-
`)}this.renderedLines=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`);let t=require(`@intlayer/config/colors`);t=e.t(t);let n=require(`@intlayer/config/logger`),r=require(`@intlayer/config/node`);var i=class{statuses=[];spinnerTimer=null;spinnerIndex=0;renderedLines=0;spinnerFrames=n.spinnerFrames;isFinished=!1;prefix;lastRenderedState=``;constructor(){this.prefix=(0,r.getConfiguration)().log.prefix}update(e){if(!this.isFinished){for(let t of e){let e=this.statuses.findIndex(e=>e.dictionaryKey===t.dictionaryKey);e>=0?this.statuses[e]=t:this.statuses.push(t)}this.startSpinner(),this.render()}}finish(){this.isFinished=!0,this.stopSpinner(),this.render()}startSpinner(){this.spinnerTimer||this.isFinished||(this.spinnerTimer=setInterval(()=>{this.spinnerIndex=(this.spinnerIndex+1)%this.spinnerFrames.length,this.render()},100))}stopSpinner(){this.spinnerTimer&&=(clearInterval(this.spinnerTimer),null)}render(){let{total:e,done:r,success:i,errors:a}=this.computeProgress(),o=this.spinnerFrames[this.spinnerIndex],s=[],c=r===e,l=`dictionaries: ${r}/${e}`,u=[];i>0&&u.push(`ok: ${i}`),a>0&&u.push((0,n.colorize)(`errors: ${a}`,t.RED));let d=u.length>0?` (${u.join(`, `)})`:``;c?s.push(`${this.prefix} ${(0,n.colorize)(`✔`,t.GREEN)} fetched ${l}${d}`):s.push(`${this.prefix} ${(0,n.colorize)(o,t.BLUE)} fetching ${l}${d}`);let f=s.join(`
|
|
2
|
+
`);if(f===this.lastRenderedState)return;this.lastRenderedState=f,this.renderedLines>0&&process.stdout.write(`\x1b[${this.renderedLines}F`);let p=Math.max(this.renderedLines,s.length);for(let e=0;e<p;e++){process.stdout.write(`\x1B[2K`);let t=s[e];t!==void 0&&process.stdout.write(t),process.stdout.write(`
|
|
3
|
+
`)}this.renderedLines=s.length}computeProgress(){let e=new Set(this.statuses.map(e=>e.dictionaryKey)),t=new Set([`fetched`,`imported`,`updated`,`up-to-date`,`reimported in JSON`,`new content file`,`error`]),n=new Set([`fetched`,`imported`,`updated`,`up-to-date`,`reimported in JSON`,`new content file`]),r=this.statuses.filter(e=>t.has(e.status)).length,i=this.statuses.filter(e=>n.has(e.status)).length,a=this.statuses.filter(e=>e.status===`error`).length;return{total:e.size,done:r,success:i,errors:a}}};exports.PullLogger=i;
|
|
4
4
|
//# sourceMappingURL=pullLog.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pullLog.cjs","names":["spinnerFrames","ANSIColors"],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":["import type { DictionaryStatus } from '@intlayer/chokidar/build';\nimport
|
|
1
|
+
{"version":3,"file":"pullLog.cjs","names":["spinnerFrames","ANSIColors"],"sources":["../../../src/push/pullLog.ts"],"sourcesContent":["import type { DictionaryStatus } from '@intlayer/chokidar/build';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, spinnerFrames } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\n\nexport type PullStatus = {\n dictionaryKey: string;\n status: DictionaryStatus | 'pending' | 'fetching';\n errorMessage?: string;\n};\n\nexport class PullLogger {\n private statuses: PullStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private readonly prefix: string;\n private lastRenderedState: string = '';\n\n constructor() {\n const configuration = getConfiguration();\n this.prefix = configuration.log.prefix;\n }\n\n update(newStatuses: PullStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, success, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n const progressLabel = `dictionaries: ${done}/${total}`;\n const details: string[] = [];\n if (success > 0) details.push(`ok: ${success}`);\n if (errors > 0) details.push(colorize(`errors: ${errors}`, ANSIColors.RED));\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${this.prefix} ${colorize('✔', ANSIColors.GREEN)} fetched ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${this.prefix} ${colorize(frame, ANSIColors.BLUE)} fetching ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const doneSet = new Set<DictionaryStatus | 'error'>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n 'error',\n ] as const);\n\n const successesSet = new Set<DictionaryStatus>([\n 'fetched',\n 'imported',\n 'updated',\n 'up-to-date',\n 'reimported in JSON',\n 'new content file',\n ] as const);\n\n const done = this.statuses.filter((s) =>\n doneSet.has(s.status as any)\n ).length;\n const success = this.statuses.filter((s) =>\n successesSet.has(s.status as any)\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n\n return {\n total: keys.size,\n done,\n success,\n errors,\n } as const;\n }\n}\n"],"mappings":"0OAWA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiCA,EAAAA,cACjC,WAAqB,GACrB,OACA,kBAAoC,GAEpC,aAAc,CAEZ,KAAK,QAAA,EAAA,EAAA,mBADmC,CACZ,IAAI,OAGlC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,UAAS,UAAW,KAAK,iBAAiB,CAEzD,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAClB,EAAgB,iBAAiB,EAAK,GAAG,IACzC,EAAoB,EAAE,CACxB,EAAU,GAAG,EAAQ,KAAK,OAAO,IAAU,CAC3C,EAAS,GAAG,EAAQ,MAAA,EAAA,EAAA,UAAc,WAAW,IAAUC,EAAW,IAAI,CAAC,CAE3E,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,GAAG,KAAK,OAAO,IAAA,EAAA,EAAA,UAAY,IAAKA,EAAW,MAAM,CAAC,WAAW,IAAgB,IAC9E,CAED,EAAM,KACJ,GAAG,KAAK,OAAO,IAAA,EAAA,EAAA,UAAY,EAAOA,EAAW,KAAK,CAAC,YAAY,IAAgB,IAChF,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAU,IAAI,IAAgC,CAClD,UACA,WACA,UACA,aACA,qBACA,mBACA,QACD,CAAU,CAEL,EAAe,IAAI,IAAsB,CAC7C,UACA,WACA,UACA,aACA,qBACA,mBACD,CAAU,CAEL,EAAO,KAAK,SAAS,OAAQ,GACjC,EAAQ,IAAI,EAAE,OAAc,CAC7B,CAAC,OACI,EAAU,KAAK,SAAS,OAAQ,GACpC,EAAa,IAAI,EAAE,OAAc,CAClC,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAEjE,MAAO,CACL,MAAO,EAAK,KACZ,OACA,UACA,SACD"}
|
package/dist/cjs/push/push.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`../utils/checkAccess.cjs`),n=require(`../pushLog.cjs`);let r=require(`node:path`),i=require(`@intlayer/chokidar/build`),a=require(`@intlayer/chokidar/cli`),o=require(`@intlayer/chokidar/utils`),s=require(`@intlayer/config/logger`),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`../utils/checkAccess.cjs`),n=require(`../pushLog.cjs`);let r=require(`node:path`),i=require(`@intlayer/chokidar/build`),a=require(`@intlayer/chokidar/cli`),o=require(`@intlayer/chokidar/utils`),s=require(`@intlayer/config/colors`);s=e.t(s);let c=require(`@intlayer/config/logger`),l=require(`@intlayer/config/node`),u=require(`@intlayer/unmerged-dictionaries-entry`),d=require(`@intlayer/api`),f=require(`node:fs/promises`);f=e.t(f);const p={pushed:{icon:`✔`,color:s.GREEN},modified:{icon:`✔`,color:s.GREEN},error:{icon:`✖`,color:s.RED},default:{icon:`⏲`,color:s.BLUE}},m=e=>p[e]??p.default,h=async e=>{let f=(0,l.getConfiguration)(e?.configOptions);(0,a.logConfigDetails)(e?.configOptions);let p=(0,c.getAppLogger)(f);e?.build===!0?await(0,i.prepareIntlayer)(f,{forceRun:!0}):e?.build===void 0&&await(0,i.prepareIntlayer)(f);try{if(!await t.checkCMSAuth(f))return;let l=(0,d.getIntlayerAPIProxy)(void 0,f),h=(0,u.getUnmergedDictionaries)(f),_=Object.values(h).flat(),v=Array.from(new Set(_.map(e=>e.location).filter(e=>e&&![`remote`,`local`,`hybrid`].includes(e)))),y=[];if(v.length>0){let{multiselect:e,confirm:t,isCancel:n}=await import(`@clack/prompts`);if(v.length===1){let e=await t({message:`Do you want to push dictionaries with custom location ${(0,c.colorize)(v[0],s.BLUE,s.RESET)}?`,initialValue:!1});if(n(e))return;e&&(y=[v[0]])}else{let t=await e({message:`Select custom locations to push:`,options:v.map(e=>({value:e,label:e})),required:!1});if(n(t))return;y=t}}let b=_.filter(e=>{let t=e.location??f.dictionary?.location??`local`;return t===`remote`||t===`hybrid`||y.includes(t)});if(b.length===0){p(`No dictionaries found to push. Only dictionaries with location ${(0,c.colorize)(`remote`,s.BLUE,s.RESET)}, ${(0,c.colorize)(`hybrid`,s.BLUE,s.RESET)} or selected custom locations are pushed.`,{level:`warn`}),p(`You can set the location in your dictionary file (e.g. ${(0,c.colorize)(`{ key: 'my-key', location: 'hybrid', ... }`,s.BLUE,s.RESET)} or globally in your intlayer.config.ts file (e.g. ${(0,c.colorize)(`{ dictionary: { location: 'hybrid' } }`,s.BLUE,s.RESET)}).`,{level:`info`});return}let x=Object.keys(h);if(e?.dictionaries){let t=e.dictionaries.filter(e=>!x.includes(e));t.length>0&&p(`The following dictionaries do not exist: ${t.join(`, `)} and have been ignored.`,{level:`error`}),b=b.filter(t=>e.dictionaries?.includes(t.key))}if(e?.gitOptions){let t=await(0,a.listGitFiles)(e.gitOptions);b=b.filter(e=>t.includes((0,r.join)(f.system.baseDir,e.filePath??``)))}if(b.length===0){p(`No local dictionaries found`,{level:`error`});return}p(`Pushing dictionaries:`);let S=b.map(e=>({dictionary:e,status:`pending`})),C=new n.PushLogger;C.update(S.map(e=>({dictionaryKey:e.dictionary.key,status:`pending`})));let w=[];await(0,o.parallelize)(S,async e=>{e.status=`pushing`,C.update([{dictionaryKey:e.dictionary.key,status:`pushing`}]);try{let t=await l.dictionary.pushDictionaries([e.dictionary]),n=t.data?.updatedDictionaries??[],r=t.data?.newDictionaries??[],a=[...n,...r];for(let e of a){let t=h[e.key]?.find(t=>t.localId===e.localId);t&&await(0,i.writeContentDeclaration)({...t,id:e.id},f)}n.some(t=>t.key===e.dictionary.key)?(e.status=`modified`,w.push(e.dictionary),C.update([{dictionaryKey:e.dictionary.key,status:`modified`}])):r.some(t=>t.key===e.dictionary.key)?(e.status=`pushed`,w.push(e.dictionary),C.update([{dictionaryKey:e.dictionary.key,status:`pushed`}])):e.status=`unknown`}catch(t){e.status=`error`,e.error=t,e.errorMessage=`Error pushing dictionary ${e.dictionary.key}: ${t}`,C.update([{dictionaryKey:e.dictionary.key,status:`error`}])}},5),C.finish();for(let e of S){let{icon:t,color:n}=m(e.status);p(` - ${(0,c.colorizeKey)(e.dictionary.key)} ${s.GREY}[${n}${t} ${e.status}${s.GREY}]${s.RESET}`)}for(let e of S)e.errorMessage&&p(e.errorMessage,{level:`error`});let T=e?.deleteLocaleDictionary,E=e?.keepLocaleDictionary;if(T&&E)throw Error(`Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.`);if(T)await g(w,e);else if(!E){let t=w.filter(e=>e.location===`remote`),n=t.map(e=>e.key);if(t.length>0){let{confirm:r,isCancel:i}=await import(`@clack/prompts`),a=await r({message:`Do you want to delete the local dictionaries that were successfully pushed? ${(0,c.colorize)(`(Dictionaries:`,s.GREY,s.RESET)} ${(0,c.colorizeKey)(n)}${(0,c.colorize)(`)`,s.GREY,s.RESET)}`,initialValue:!1});if(i(a))return;a&&await g(t,e)}}}catch(e){p(e,{level:`error`})}},g=async(e,t)=>{let n=(0,c.getAppLogger)((0,l.getConfiguration)(t?.configOptions)),r=new Set;for(let t of e){let{filePath:e}=t;if(!e){n(`Dictionary ${(0,c.colorizeKey)(t.key)} does not have a file path`,{level:`error`});continue}r.add(e)}for(let e of r)try{let t=await f.lstat(e);t.isFile()?(await f.unlink(e),n(`Deleted file ${(0,o.formatPath)(e)}`,{})):t.isDirectory()?n(`Path is a directory ${(0,o.formatPath)(e)}, skipping.`,{}):n(`Unknown file type for ${(0,o.formatPath)(e)}, skipping.`,{})}catch(t){n(`Error deleting ${(0,o.formatPath)(e)}: ${t}`,{level:`error`})}};exports.push=h;
|
|
2
2
|
//# sourceMappingURL=push.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport { formatPath, parallelize } from '@intlayer/chokidar/utils';\nimport {\n ANSIColors,\n colorize,\n colorizeKey,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location && !['remote', 'local', 'hybrid'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'hybrid' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('hybrid', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'hybrid', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'hybrid' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.system.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\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\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"push.cjs","names":["ANSIColors","checkCMSAuth","PushLogger","fsPromises"],"sources":["../../../src/push/push.ts"],"sourcesContent":["import * as fsPromises from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n prepareIntlayer,\n writeContentDeclaration,\n} from '@intlayer/chokidar/build';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport { formatPath, parallelize } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { PushLogger, type PushStatus } from '../pushLog';\nimport { checkCMSAuth } from '../utils/checkAccess';\n\ntype PushOptions = {\n deleteLocaleDictionary?: boolean;\n keepLocaleDictionary?: boolean;\n dictionaries?: string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n build?: boolean;\n};\n\ntype DictionariesStatus = {\n dictionary: Dictionary;\n status: 'pending' | 'pushing' | 'modified' | 'pushed' | 'unknown' | 'error';\n error?: Error;\n errorMessage?: string;\n};\n\n// Print per-dictionary summary similar to loadDictionaries\nconst statusIconsAndColors = {\n pushed: { icon: '✔', color: ANSIColors.GREEN },\n modified: { icon: '✔', color: ANSIColors.GREEN },\n error: { icon: '✖', color: ANSIColors.RED },\n default: { icon: '⏲', color: ANSIColors.BLUE },\n};\n\nconst getIconAndColor = (status: DictionariesStatus['status']) => {\n return (\n statusIconsAndColors[status as keyof typeof statusIconsAndColors] ??\n statusIconsAndColors.default\n );\n};\n\n/**\n * Get all local dictionaries and push them simultaneously.\n */\nexport const push = async (options?: PushOptions): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n logConfigDetails(options?.configOptions);\n\n const appLogger = getAppLogger(config);\n\n if (options?.build === true) {\n await prepareIntlayer(config, { forceRun: true });\n } else if (typeof options?.build === 'undefined') {\n await prepareIntlayer(config);\n }\n\n try {\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, config);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n const allDictionaries = Object.values(unmergedDictionariesRecord).flat();\n\n const customLocations = Array.from(\n new Set(\n allDictionaries\n .map((dictionary) => dictionary.location)\n .filter(\n (location) =>\n location && !['remote', 'local', 'hybrid'].includes(location)\n )\n )\n ) as string[];\n\n let selectedCustomLocations: string[] = [];\n\n if (customLocations.length > 0) {\n const { multiselect, confirm, isCancel } = await import('@clack/prompts');\n\n if (customLocations.length === 1) {\n const shouldPush = await confirm({\n message: `Do you want to push dictionaries with custom location ${colorize(customLocations[0], ANSIColors.BLUE, ANSIColors.RESET)}?`,\n initialValue: false,\n });\n\n if (isCancel(shouldPush)) {\n return;\n }\n\n if (shouldPush) {\n selectedCustomLocations = [customLocations[0]];\n }\n } else {\n const selected = await multiselect({\n message: 'Select custom locations to push:',\n options: customLocations.map((location) => ({\n value: location,\n label: location,\n })),\n required: false,\n });\n\n if (isCancel(selected)) {\n return;\n }\n\n selectedCustomLocations = selected as string[];\n }\n }\n\n let dictionaries: Dictionary[] = allDictionaries.filter((dictionary) => {\n const location =\n dictionary.location ?? config.dictionary?.location ?? 'local';\n\n return (\n location === 'remote' ||\n location === 'hybrid' ||\n selectedCustomLocations.includes(location)\n );\n });\n\n // Check if the dictionaries list is empty after filtering by location\n if (dictionaries.length === 0) {\n appLogger(\n `No dictionaries found to push. Only dictionaries with location ${colorize('remote', ANSIColors.BLUE, ANSIColors.RESET)}, ${colorize('hybrid', ANSIColors.BLUE, ANSIColors.RESET)} or selected custom locations are pushed.`,\n { level: 'warn' }\n );\n appLogger(\n `You can set the location in your dictionary file (e.g. ${colorize(\"{ key: 'my-key', location: 'hybrid', ... }\", ANSIColors.BLUE, ANSIColors.RESET)} or globally in your intlayer.config.ts file (e.g. ${colorize(\"{ dictionary: { location: 'hybrid' } }\", ANSIColors.BLUE, ANSIColors.RESET)}).`,\n { level: 'info' }\n );\n return;\n }\n\n const existingDictionariesKeys: string[] = Object.keys(\n unmergedDictionariesRecord\n );\n\n if (options?.dictionaries) {\n // Check if the provided dictionaries exist\n const noneExistingDictionariesOption = options.dictionaries.filter(\n (dictionaryId) => !existingDictionariesKeys.includes(dictionaryId)\n );\n\n if (noneExistingDictionariesOption.length > 0) {\n appLogger(\n `The following dictionaries do not exist: ${noneExistingDictionariesOption.join(\n ', '\n )} and have been ignored.`,\n {\n level: 'error',\n }\n );\n }\n\n // Filter the dictionaries from the provided list of IDs\n dictionaries = dictionaries.filter((dictionary) =>\n options.dictionaries?.includes(dictionary.key)\n );\n }\n\n if (options?.gitOptions) {\n const gitFiles = await listGitFiles(options.gitOptions);\n\n dictionaries = dictionaries.filter((dictionary) =>\n gitFiles.includes(\n join(config.system.baseDir, dictionary.filePath ?? '')\n )\n );\n }\n\n // Check if the dictionaries list is empty\n if (dictionaries.length === 0) {\n appLogger('No local dictionaries found', {\n level: 'error',\n });\n return;\n }\n\n appLogger('Pushing dictionaries:');\n\n // Prepare dictionaries statuses\n const dictionariesStatuses: DictionariesStatus[] = dictionaries.map(\n (dictionary) => ({\n dictionary,\n status: 'pending',\n })\n );\n\n // Initialize aggregated logger similar to loadDictionaries\n const logger = new PushLogger();\n logger.update(\n dictionariesStatuses.map<PushStatus>((s) => ({\n dictionaryKey: s.dictionary.key,\n status: 'pending',\n }))\n );\n\n const successfullyPushedDictionaries: Dictionary[] = [];\n\n const processDictionary = async (\n statusObj: DictionariesStatus\n ): Promise<void> => {\n statusObj.status = 'pushing';\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushing' },\n ]);\n\n try {\n const pushResult = await intlayerAPI.dictionary.pushDictionaries([\n statusObj.dictionary,\n ]);\n\n const updatedDictionaries = pushResult.data?.updatedDictionaries ?? [];\n const newDictionaries = pushResult.data?.newDictionaries ?? [];\n\n const allDictionaries = [...updatedDictionaries, ...newDictionaries];\n\n for (const remoteDictionaryData of allDictionaries) {\n const localDictionary = unmergedDictionariesRecord[\n remoteDictionaryData.key\n ]?.find(\n (dictionary) => dictionary.localId === remoteDictionaryData.localId\n );\n\n if (!localDictionary) continue;\n\n await writeContentDeclaration(\n { ...localDictionary, id: remoteDictionaryData.id },\n config\n );\n }\n\n if (\n updatedDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'modified';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'modified' },\n ]);\n } else if (\n newDictionaries.some(\n (dictionary) => dictionary.key === statusObj.dictionary.key\n )\n ) {\n statusObj.status = 'pushed';\n successfullyPushedDictionaries.push(statusObj.dictionary);\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'pushed' },\n ]);\n } else {\n statusObj.status = 'unknown';\n }\n } catch (error) {\n statusObj.status = 'error';\n statusObj.error = error as Error;\n statusObj.errorMessage = `Error pushing dictionary ${statusObj.dictionary.key}: ${error}`;\n logger.update([\n { dictionaryKey: statusObj.dictionary.key, status: 'error' },\n ]);\n }\n };\n\n // Process dictionaries in parallel with a concurrency limit (reuse parallelize)\n await parallelize(dictionariesStatuses, processDictionary, 5);\n\n // Stop the logger and render final state\n logger.finish();\n\n for (const dictionaryStatus of dictionariesStatuses) {\n const { icon, color } = getIconAndColor(dictionaryStatus.status);\n appLogger(\n ` - ${colorizeKey(dictionaryStatus.dictionary.key)} ${ANSIColors.GREY}[${color}${icon} ${dictionaryStatus.status}${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\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\n // Handle delete or keep options\n const deleteOption = options?.deleteLocaleDictionary;\n const keepOption = options?.keepLocaleDictionary;\n\n if (deleteOption && keepOption) {\n throw new Error(\n 'Cannot specify both --deleteLocaleDictionary and --keepLocaleDictionary options.'\n );\n }\n\n if (deleteOption) {\n // Delete only the successfully pushed dictionaries\n await deleteLocalDictionaries(successfullyPushedDictionaries, options);\n } else if (keepOption) {\n // Do nothing, keep the local dictionaries\n } else {\n // Ask the user\n const remoteDictionaries = successfullyPushedDictionaries.filter(\n (dictionary) => dictionary.location === 'remote'\n );\n const remoteDictionariesKeys = remoteDictionaries.map(\n (dictionary) => dictionary.key\n );\n\n if (remoteDictionaries.length > 0) {\n const { confirm, isCancel } = await import('@clack/prompts');\n\n const shouldDelete = await confirm({\n message: `Do you want to delete the local dictionaries that were successfully pushed? ${colorize('(Dictionaries:', ANSIColors.GREY, ANSIColors.RESET)} ${colorizeKey(remoteDictionariesKeys)}${colorize(')', ANSIColors.GREY, ANSIColors.RESET)}`,\n initialValue: false,\n });\n\n if (isCancel(shouldDelete)) {\n return;\n }\n\n if (shouldDelete) {\n await deleteLocalDictionaries(remoteDictionaries, options);\n }\n }\n }\n } catch (error) {\n appLogger(error, {\n level: 'error',\n });\n }\n};\n\nconst deleteLocalDictionaries = async (\n dictionariesToDelete: Dictionary[],\n options?: PushOptions\n): Promise<void> => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config);\n\n // Use a Set to collect all unique file paths\n const filePathsSet: Set<string> = new Set();\n\n for (const dictionary of dictionariesToDelete) {\n const { filePath } = dictionary;\n\n if (!filePath) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} does not have a file path`,\n {\n level: 'error',\n }\n );\n continue;\n }\n\n filePathsSet.add(filePath);\n }\n\n for (const filePath of filePathsSet) {\n try {\n const stats = await fsPromises.lstat(filePath);\n\n if (stats.isFile()) {\n await fsPromises.unlink(filePath);\n appLogger(`Deleted file ${formatPath(filePath)}`, {});\n } else if (stats.isDirectory()) {\n appLogger(`Path is a directory ${formatPath(filePath)}, skipping.`, {});\n } else {\n appLogger(\n `Unknown file type for ${formatPath(filePath)}, skipping.`,\n {}\n );\n }\n } catch (err) {\n appLogger(`Error deleting ${formatPath(filePath)}: ${err}`, {\n level: 'error',\n });\n }\n }\n};\n"],"mappings":"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"}
|
package/dist/cjs/pushLog.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`})
|
|
2
|
-
`);if(
|
|
3
|
-
`)}this.renderedLines=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./chunk-Bmb41Sf3.cjs`);let t=require(`@intlayer/config/colors`);t=e.t(t);let n=require(`@intlayer/config/logger`);var r=class{statuses=[];spinnerTimer=null;spinnerIndex=0;renderedLines=0;spinnerFrames=n.spinnerFrames;isFinished=!1;lastRenderedState=``;update(e){if(!this.isFinished){for(let t of e){let e=this.statuses.findIndex(e=>e.dictionaryKey===t.dictionaryKey);e>=0?this.statuses[e]=t:this.statuses.push(t)}this.startSpinner(),this.render()}}finish(){this.isFinished=!0,this.stopSpinner(),this.render()}startSpinner(){this.spinnerTimer||this.isFinished||(this.spinnerTimer=setInterval(()=>{this.spinnerIndex=(this.spinnerIndex+1)%this.spinnerFrames.length,this.render()},100))}stopSpinner(){this.spinnerTimer&&=(clearInterval(this.spinnerTimer),null)}render(){let{total:e,done:r,pushed:i,modified:a,errors:o}=this.computeProgress(),s=this.spinnerFrames[this.spinnerIndex],c=[],l=r===e,u=`dictionaries: ${(0,n.colorizeNumber)(r)}/${(0,n.colorizeNumber)(e)}`,d=[];i>0&&d.push(`new: ${(0,n.colorizeNumber)(i)}`),a>0&&d.push(`modified: ${(0,n.colorizeNumber)(a)}`),o>0&&d.push((0,n.colorize)(`errors: ${(0,n.colorizeNumber)(o)}`,t.RED));let f=d.length>0?` (${d.join(`, `)})`:``;l?c.push(`${(0,n.colorize)(`✔`,t.GREEN)} pushed ${u}${f}`):c.push(`${(0,n.colorize)(s,t.BLUE)} pushing ${u}${f}`);let p=c.join(`
|
|
2
|
+
`);if(p===this.lastRenderedState)return;this.lastRenderedState=p,this.renderedLines>0&&process.stdout.write(`\x1b[${this.renderedLines}F`);let m=Math.max(this.renderedLines,c.length);for(let e=0;e<m;e++){process.stdout.write(`\x1B[2K`);let t=c[e];t!==void 0&&process.stdout.write(t),process.stdout.write(`
|
|
3
|
+
`)}this.renderedLines=c.length}computeProgress(){let e=new Set(this.statuses.map(e=>e.dictionaryKey)),t=this.statuses.filter(e=>e.status===`pushed`).length,n=this.statuses.filter(e=>e.status===`modified`).length,r=this.statuses.filter(e=>e.status===`error`).length,i=t+n+r;return{total:e.size,done:i,pushed:t,modified:n,errors:r}}};exports.PushLogger=r;
|
|
4
4
|
//# sourceMappingURL=pushLog.cjs.map
|
package/dist/cjs/pushLog.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushLog.cjs","names":["spinnerFrames","ANSIColors"],"sources":["../../src/pushLog.ts"],"sourcesContent":["import {\n
|
|
1
|
+
{"version":3,"file":"pushLog.cjs","names":["spinnerFrames","ANSIColors"],"sources":["../../src/pushLog.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeNumber,\n spinnerFrames,\n} from '@intlayer/config/logger';\n\nexport type PushStatus = {\n dictionaryKey: string;\n status: 'pending' | 'pushing' | 'pushed' | 'modified' | 'error';\n errorMessage?: string;\n};\n\nexport class PushLogger {\n private statuses: PushStatus[] = [];\n private spinnerTimer: NodeJS.Timeout | null = null;\n private spinnerIndex = 0;\n private renderedLines = 0;\n private readonly spinnerFrames = spinnerFrames;\n private isFinished = false;\n private lastRenderedState: string = '';\n\n update(newStatuses: PushStatus[]) {\n if (this.isFinished) return;\n for (const status of newStatuses) {\n const index = this.statuses.findIndex(\n (s) => s.dictionaryKey === status.dictionaryKey\n );\n if (index >= 0) {\n this.statuses[index] = status;\n } else {\n this.statuses.push(status);\n }\n }\n\n this.startSpinner();\n this.render();\n }\n\n finish() {\n this.isFinished = true;\n this.stopSpinner();\n this.render();\n }\n\n private startSpinner() {\n if (this.spinnerTimer || this.isFinished) return;\n this.spinnerTimer = setInterval(() => {\n this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;\n this.render();\n }, 100);\n }\n\n private stopSpinner() {\n if (!this.spinnerTimer) return;\n clearInterval(this.spinnerTimer);\n this.spinnerTimer = null;\n }\n\n private render() {\n const { total, done, pushed, modified, errors } = this.computeProgress();\n\n const frame = this.spinnerFrames[this.spinnerIndex];\n const lines: string[] = [];\n\n const isDone = done === total;\n\n const progressLabel = `dictionaries: ${colorizeNumber(done)}/${colorizeNumber(total)}`;\n const details: string[] = [];\n if (pushed > 0) details.push(`new: ${colorizeNumber(pushed)}`);\n if (modified > 0) details.push(`modified: ${colorizeNumber(modified)}`);\n if (errors > 0)\n details.push(\n colorize(`errors: ${colorizeNumber(errors)}`, ANSIColors.RED)\n );\n\n const suffix = details.length > 0 ? ` (${details.join(', ')})` : '';\n\n if (isDone) {\n lines.push(\n `${colorize('✔', ANSIColors.GREEN)} pushed ${progressLabel}${suffix}`\n );\n } else {\n lines.push(\n `${colorize(frame, ANSIColors.BLUE)} pushing ${progressLabel}${suffix}`\n );\n }\n\n const currentState = lines.join('\\n');\n if (currentState === this.lastRenderedState) {\n return;\n }\n this.lastRenderedState = currentState;\n\n if (this.renderedLines > 0) {\n process.stdout.write(`\\x1b[${this.renderedLines}F`);\n }\n\n const totalLinesToClear = Math.max(this.renderedLines, lines.length);\n for (let i = 0; i < totalLinesToClear; i++) {\n process.stdout.write('\\x1b[2K');\n const line = lines[i];\n if (line !== undefined) {\n process.stdout.write(line);\n }\n process.stdout.write('\\n');\n }\n\n this.renderedLines = lines.length;\n }\n\n private computeProgress() {\n const keys = new Set(this.statuses.map((s) => s.dictionaryKey));\n\n const pushed = this.statuses.filter((s) => s.status === 'pushed').length;\n const modified = this.statuses.filter(\n (s) => s.status === 'modified'\n ).length;\n const errors = this.statuses.filter((s) => s.status === 'error').length;\n const done = pushed + modified + errors;\n\n return {\n total: keys.size,\n done,\n pushed,\n modified,\n errors,\n } as const;\n }\n}\n"],"mappings":"sMAaA,IAAa,EAAb,KAAwB,CACtB,SAAiC,EAAE,CACnC,aAA8C,KAC9C,aAAuB,EACvB,cAAwB,EACxB,cAAiCA,EAAAA,cACjC,WAAqB,GACrB,kBAAoC,GAEpC,OAAO,EAA2B,CAC5B,SAAK,WACT,KAAK,IAAM,KAAU,EAAa,CAChC,IAAM,EAAQ,KAAK,SAAS,UACzB,GAAM,EAAE,gBAAkB,EAAO,cACnC,CACG,GAAS,EACX,KAAK,SAAS,GAAS,EAEvB,KAAK,SAAS,KAAK,EAAO,CAI9B,KAAK,cAAc,CACnB,KAAK,QAAQ,EAGf,QAAS,CACP,KAAK,WAAa,GAClB,KAAK,aAAa,CAClB,KAAK,QAAQ,CAGf,cAAuB,CACjB,KAAK,cAAgB,KAAK,aAC9B,KAAK,aAAe,gBAAkB,CACpC,KAAK,cAAgB,KAAK,aAAe,GAAK,KAAK,cAAc,OACjE,KAAK,QAAQ,EACZ,IAAI,EAGT,aAAsB,CACf,AAEL,KAAK,gBADL,cAAc,KAAK,aAAa,CACZ,MAGtB,QAAiB,CACf,GAAM,CAAE,QAAO,OAAM,SAAQ,WAAU,UAAW,KAAK,iBAAiB,CAElE,EAAQ,KAAK,cAAc,KAAK,cAChC,EAAkB,EAAE,CAEpB,EAAS,IAAS,EAElB,EAAgB,kBAAA,EAAA,EAAA,gBAAgC,EAAK,CAAC,IAAA,EAAA,EAAA,gBAAkB,EAAM,GAC9E,EAAoB,EAAE,CACxB,EAAS,GAAG,EAAQ,KAAK,SAAA,EAAA,EAAA,gBAAuB,EAAO,GAAG,CAC1D,EAAW,GAAG,EAAQ,KAAK,cAAA,EAAA,EAAA,gBAA4B,EAAS,GAAG,CACnE,EAAS,GACX,EAAQ,MAAA,EAAA,EAAA,UACG,YAAA,EAAA,EAAA,gBAA0B,EAAO,GAAIC,EAAW,IAAI,CAC9D,CAEH,IAAM,EAAS,EAAQ,OAAS,EAAI,KAAK,EAAQ,KAAK,KAAK,CAAC,GAAK,GAE7D,EACF,EAAM,KACJ,IAAA,EAAA,EAAA,UAAY,IAAKA,EAAW,MAAM,CAAC,UAAU,IAAgB,IAC9D,CAED,EAAM,KACJ,IAAA,EAAA,EAAA,UAAY,EAAOA,EAAW,KAAK,CAAC,WAAW,IAAgB,IAChE,CAGH,IAAM,EAAe,EAAM,KAAK;EAAK,CACrC,GAAI,IAAiB,KAAK,kBACxB,OAEF,KAAK,kBAAoB,EAErB,KAAK,cAAgB,GACvB,QAAQ,OAAO,MAAM,QAAQ,KAAK,cAAc,GAAG,CAGrD,IAAM,EAAoB,KAAK,IAAI,KAAK,cAAe,EAAM,OAAO,CACpE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAmB,IAAK,CAC1C,QAAQ,OAAO,MAAM,UAAU,CAC/B,IAAM,EAAO,EAAM,GACf,IAAS,IAAA,IACX,QAAQ,OAAO,MAAM,EAAK,CAE5B,QAAQ,OAAO,MAAM;EAAK,CAG5B,KAAK,cAAgB,EAAM,OAG7B,iBAA0B,CACxB,IAAM,EAAO,IAAI,IAAI,KAAK,SAAS,IAAK,GAAM,EAAE,cAAc,CAAC,CAEzD,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,SAAS,CAAC,OAC5D,EAAW,KAAK,SAAS,OAC5B,GAAM,EAAE,SAAW,WACrB,CAAC,OACI,EAAS,KAAK,SAAS,OAAQ,GAAM,EAAE,SAAW,QAAQ,CAAC,OAC3D,EAAO,EAAS,EAAW,EAEjC,MAAO,CACL,MAAO,EAAK,KACZ,OACA,SACA,WACA,SACD"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./reviewDocBlockAware.cjs`);let a=require(`node:fs`),o=require(`node:path`),s=require(`@intlayer/chokidar/cli`),c=require(`@intlayer/chokidar/utils`),l=require(`@intlayer/config/logger`),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../chunk-Bmb41Sf3.cjs`),t=require(`../utils/setupAI.cjs`),n=require(`../utils/checkFileModifiedRange.cjs`),r=require(`../utils/getOutputFilePath.cjs`),i=require(`./reviewDocBlockAware.cjs`);let a=require(`node:fs`),o=require(`node:path`),s=require(`@intlayer/chokidar/cli`),c=require(`@intlayer/chokidar/utils`),l=require(`@intlayer/config/colors`);l=e.t(l);let u=require(`@intlayer/config/logger`),d=require(`@intlayer/config/node`),f=require(`fast-glob`);f=e.t(f);const p=async({docPattern:e,locales:p,excludedGlobPattern:m,baseLocale:h,aiOptions:g,nbSimultaneousFileProcessed:_,configOptions:v,customInstructions:y,skipIfModifiedBefore:b,skipIfModifiedAfter:x,skipIfExists:S,gitOptions:C})=>{let w=(0,d.getConfiguration)(v);(0,s.logConfigDetails)(v);let T=(0,u.getAppLogger)(w),E=await t.setupAI(w,g);if(!E?.hasAIAccess)return;let{aiClient:D,aiConfig:O}=E;_&&_>10&&(T(`Warning: nbSimultaneousFileProcessed is set to ${_}, which is greater than 10. Setting it to 10.`),_=10);let k=await(0,f.default)(e,{ignore:m});if(C){let e=await(0,s.listGitFiles)(C);e&&(k=k.filter(t=>e.some(e=>(0,o.join)(process.cwd(),t)===e)))}T(`Base locale is ${(0,c.formatLocale)(h)}`),T(`Reviewing ${(0,u.colorizeNumber)(p.length)} locales: [ ${(0,c.formatLocale)(p)} ]`),T(`Reviewing ${(0,u.colorizeNumber)(k.length)} files:`),T(k.map(e=>` - ${(0,c.formatPath)(e)}\n`)),await(0,c.parallelize)(k.flatMap(e=>p.map(t=>async()=>{T(`Reviewing file: ${(0,c.formatPath)(e)} to ${(0,c.formatLocale)(t)}`);let d=(0,o.join)(w.system.baseDir,e),f=r.getOutputFilePath(d,t,h);if(S&&(0,a.existsSync)(f)){let e=(0,o.relative)(w.system.baseDir,f);T(`${(0,u.colorize)(`⊘`,l.YELLOW)} File ${(0,c.formatPath)(e)} already exists, skipping.`);return}if((0,a.existsSync)(f)){let e=n.checkFileModifiedRange(f,{skipIfModifiedBefore:b,skipIfModifiedAfter:x});if(e.isSkipped){T(e.message);return}}else (b||x)&&T(`${(0,u.colorize)(`!`,l.YELLOW)} File ${(0,c.formatPath)(f)} does not exist, skipping modification date check.`);let p;if(C){let e=await(0,s.listGitLines)(d,C);T(`Git changed lines: ${e.join(`, `)}`),p=e}await i.reviewFileBlockAware(d,f,t,h,g,v,y,p,D,O)})),e=>e(),_??3)};exports.reviewDoc=p;
|
|
2
2
|
//# sourceMappingURL=reviewDoc.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviewDoc.cjs","names":["setupAI","getOutputFilePath","ANSIColors","checkFileModifiedRange","reviewFileBlockAware"],"sources":["../../../src/reviewDoc/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport {\n formatLocale,\n formatPath,\n parallelize,\n} from '@intlayer/chokidar/utils';\nimport {\n
|
|
1
|
+
{"version":3,"file":"reviewDoc.cjs","names":["setupAI","getOutputFilePath","ANSIColors","checkFileModifiedRange","reviewFileBlockAware"],"sources":["../../../src/reviewDoc/reviewDoc.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type { AIOptions } from '@intlayer/api';\nimport {\n type ListGitFilesOptions,\n listGitFiles,\n listGitLines,\n logConfigDetails,\n} from '@intlayer/chokidar/cli';\nimport {\n formatLocale,\n formatPath,\n parallelize,\n} from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport {\n colorize,\n colorizeNumber,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport {\n type GetConfigurationOptions,\n getConfiguration,\n} from '@intlayer/config/node';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport fg from 'fast-glob';\nimport { checkFileModifiedRange } from '../utils/checkFileModifiedRange';\nimport { getOutputFilePath } from '../utils/getOutputFilePath';\nimport { setupAI } from '../utils/setupAI';\nimport { reviewFileBlockAware } from './reviewDocBlockAware';\n\ntype ReviewDocOptions = {\n docPattern: string[];\n locales: Locale[];\n excludedGlobPattern: string[];\n baseLocale: Locale;\n aiOptions?: AIOptions;\n nbSimultaneousFileProcessed?: number;\n configOptions?: GetConfigurationOptions;\n customInstructions?: string;\n skipIfModifiedBefore?: number | string | Date;\n skipIfModifiedAfter?: number | string | Date;\n skipIfExists?: boolean;\n gitOptions?: ListGitFilesOptions;\n};\n\n/**\n * Main audit function: scans all .md files in \"en/\" (unless you specified DOC_LIST),\n * then audits them to each locale in LOCALE_LIST.\n */\nexport const reviewDoc = async ({\n docPattern,\n locales,\n excludedGlobPattern,\n baseLocale,\n aiOptions,\n nbSimultaneousFileProcessed,\n configOptions,\n customInstructions,\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n skipIfExists,\n gitOptions,\n}: ReviewDocOptions) => {\n const configuration = getConfiguration(configOptions);\n logConfigDetails(configOptions);\n\n const appLogger = getAppLogger(configuration);\n\n const aiResult = await setupAI(configuration, aiOptions);\n\n if (!aiResult?.hasAIAccess) return;\n\n const { aiClient, aiConfig } = aiResult;\n\n if (nbSimultaneousFileProcessed && nbSimultaneousFileProcessed > 10) {\n appLogger(\n `Warning: nbSimultaneousFileProcessed is set to ${nbSimultaneousFileProcessed}, which is greater than 10. Setting it to 10.`\n );\n nbSimultaneousFileProcessed = 10; // Limit the number of simultaneous file processed to 10\n }\n\n let docList: string[] = await fg(docPattern, {\n ignore: excludedGlobPattern,\n });\n\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 docList = docList.filter((path) =>\n gitChangedFiles.some((gitFile) => join(process.cwd(), path) === gitFile)\n );\n }\n }\n\n // OAuth handled by API proxy internally\n\n appLogger(`Base locale is ${formatLocale(baseLocale)}`);\n appLogger(\n `Reviewing ${colorizeNumber(locales.length)} locales: [ ${formatLocale(locales)} ]`\n );\n\n appLogger(`Reviewing ${colorizeNumber(docList.length)} files:`);\n appLogger(docList.map((path) => ` - ${formatPath(path)}\\n`));\n\n // Create all tasks to be processed\n const allTasks = docList.flatMap((docPath) =>\n locales.map((locale) => async () => {\n appLogger(\n `Reviewing file: ${formatPath(docPath)} to ${formatLocale(locale)}`\n );\n\n const absoluteBaseFilePath = join(configuration.system.baseDir, docPath);\n const outputFilePath = getOutputFilePath(\n absoluteBaseFilePath,\n locale,\n baseLocale\n );\n\n // Skip if file exists and skipIfExists option is enabled\n if (skipIfExists && existsSync(outputFilePath)) {\n const relativePath = relative(\n configuration.system.baseDir,\n outputFilePath\n );\n appLogger(\n `${colorize('⊘', ANSIColors.YELLOW)} File ${formatPath(relativePath)} already exists, skipping.`\n );\n return;\n }\n\n // Check modification range only if the file exists\n if (existsSync(outputFilePath)) {\n const fileModificationData = checkFileModifiedRange(outputFilePath, {\n skipIfModifiedBefore,\n skipIfModifiedAfter,\n });\n\n if (fileModificationData.isSkipped) {\n appLogger(fileModificationData.message);\n return;\n }\n } else if (skipIfModifiedBefore || skipIfModifiedAfter) {\n // Log if we intended to check modification time but couldn't because the file doesn't exist\n appLogger(\n `${colorize('!', ANSIColors.YELLOW)} File ${formatPath(outputFilePath)} does not exist, skipping modification date check.`\n );\n }\n\n let changedLines: number[] | undefined;\n // FIXED: Enable git optimization that was previously commented out\n if (gitOptions) {\n const gitChangedLines = await listGitLines(\n absoluteBaseFilePath,\n gitOptions\n );\n\n appLogger(`Git changed lines: ${gitChangedLines.join(', ')}`);\n changedLines = gitChangedLines;\n }\n\n await reviewFileBlockAware(\n absoluteBaseFilePath,\n outputFilePath,\n locale as Locale,\n baseLocale,\n aiOptions,\n configOptions,\n customInstructions,\n changedLines,\n aiClient,\n aiConfig\n );\n })\n );\n\n await parallelize(\n allTasks,\n (task) => task(),\n nbSimultaneousFileProcessed ?? 3\n );\n};\n"],"mappings":"siBAkDA,MAAa,EAAY,MAAO,CAC9B,aACA,UACA,sBACA,aACA,YACA,8BACA,gBACA,qBACA,uBACA,sBACA,eACA,gBACsB,CACtB,IAAM,GAAA,EAAA,EAAA,kBAAiC,EAAc,EACrD,EAAA,EAAA,kBAAiB,EAAc,CAE/B,IAAM,GAAA,EAAA,EAAA,cAAyB,EAAc,CAEvC,EAAW,MAAMA,EAAAA,QAAQ,EAAe,EAAU,CAExD,GAAI,CAAC,GAAU,YAAa,OAE5B,GAAM,CAAE,WAAU,YAAa,EAE3B,GAA+B,EAA8B,KAC/D,EACE,kDAAkD,EAA4B,+CAC/E,CACD,EAA8B,IAGhC,IAAI,EAAoB,MAAA,EAAA,EAAA,SAAS,EAAY,CAC3C,OAAQ,EACT,CAAC,CAEF,GAAI,EAAY,CACd,IAAM,EAAkB,MAAA,EAAA,EAAA,cAAmB,EAAW,CAElD,IAIF,EAAU,EAAQ,OAAQ,GACxB,EAAgB,KAAM,IAAA,EAAA,EAAA,MAAiB,QAAQ,KAAK,CAAE,EAAK,GAAK,EAAQ,CACzE,EAML,EAAU,mBAAA,EAAA,EAAA,cAA+B,EAAW,GAAG,CACvD,EACE,cAAA,EAAA,EAAA,gBAA4B,EAAQ,OAAO,CAAC,eAAA,EAAA,EAAA,cAA2B,EAAQ,CAAC,IACjF,CAED,EAAU,cAAA,EAAA,EAAA,gBAA4B,EAAQ,OAAO,CAAC,SAAS,CAC/D,EAAU,EAAQ,IAAK,GAAS,OAAA,EAAA,EAAA,YAAiB,EAAK,CAAC,IAAI,CAAC,CAyE5D,MAAA,EAAA,EAAA,aAtEiB,EAAQ,QAAS,GAChC,EAAQ,IAAK,GAAW,SAAY,CAClC,EACE,oBAAA,EAAA,EAAA,YAA8B,EAAQ,CAAC,OAAA,EAAA,EAAA,cAAmB,EAAO,GAClE,CAED,IAAM,GAAA,EAAA,EAAA,MAA4B,EAAc,OAAO,QAAS,EAAQ,CAClE,EAAiBC,EAAAA,kBACrB,EACA,EACA,EACD,CAGD,GAAI,IAAA,EAAA,EAAA,YAA2B,EAAe,CAAE,CAC9C,IAAM,GAAA,EAAA,EAAA,UACJ,EAAc,OAAO,QACrB,EACD,CACD,EACE,IAAA,EAAA,EAAA,UAAY,IAAKC,EAAW,OAAO,CAAC,SAAA,EAAA,EAAA,YAAmB,EAAa,CAAC,4BACtE,CACD,OAIF,IAAA,EAAA,EAAA,YAAe,EAAe,CAAE,CAC9B,IAAM,EAAuBC,EAAAA,uBAAuB,EAAgB,CAClE,uBACA,sBACD,CAAC,CAEF,GAAI,EAAqB,UAAW,CAClC,EAAU,EAAqB,QAAQ,CACvC,cAEO,GAAwB,IAEjC,EACE,IAAA,EAAA,EAAA,UAAY,IAAKD,EAAW,OAAO,CAAC,SAAA,EAAA,EAAA,YAAmB,EAAe,CAAC,oDACxE,CAGH,IAAI,EAEJ,GAAI,EAAY,CACd,IAAM,EAAkB,MAAA,EAAA,EAAA,cACtB,EACA,EACD,CAED,EAAU,sBAAsB,EAAgB,KAAK,KAAK,GAAG,CAC7D,EAAe,EAGjB,MAAME,EAAAA,qBACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACD,EACD,CACH,CAIE,GAAS,GAAM,CAChB,GAA+B,EAChC"}
|