@intlayer/cli 6.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/fill/index.cjs +76 -84
- package/dist/cjs/fill/index.cjs.map +1 -1
- package/dist/cjs/{fill/getTargetDictionary.cjs → getTargetDictionary.cjs} +6 -15
- package/dist/cjs/getTargetDictionary.cjs.map +1 -0
- package/dist/cjs/listContentDeclaration.cjs +3 -12
- package/dist/cjs/listContentDeclaration.cjs.map +1 -1
- package/dist/cjs/liveSync.cjs +2 -2
- package/dist/cjs/liveSync.cjs.map +1 -1
- package/dist/cjs/pull.cjs +3 -6
- package/dist/cjs/pull.cjs.map +1 -1
- package/dist/cjs/push.cjs +13 -9
- package/dist/cjs/push.cjs.map +1 -1
- package/dist/cjs/pushConfig.cjs +3 -10
- package/dist/cjs/pushConfig.cjs.map +1 -1
- package/dist/cjs/reviewDoc.cjs +3 -2
- package/dist/cjs/reviewDoc.cjs.map +1 -1
- package/dist/cjs/test/index.cjs +1 -1
- package/dist/cjs/test/index.cjs.map +1 -1
- package/dist/cjs/test/listMissingTranslations.cjs +8 -16
- package/dist/cjs/test/listMissingTranslations.cjs.map +1 -1
- package/dist/cjs/translateDoc.cjs +3 -2
- package/dist/cjs/translateDoc.cjs.map +1 -1
- package/dist/cjs/utils/checkAccess.cjs +79 -0
- package/dist/cjs/utils/checkAccess.cjs.map +1 -0
- package/dist/esm/fill/index.mjs +83 -74
- package/dist/esm/fill/index.mjs.map +1 -1
- package/dist/esm/{fill/getTargetDictionary.mjs → getTargetDictionary.mjs} +4 -3
- package/dist/esm/getTargetDictionary.mjs.map +1 -0
- package/dist/esm/listContentDeclaration.mjs +2 -1
- package/dist/esm/listContentDeclaration.mjs.map +1 -1
- package/dist/esm/liveSync.mjs +2 -2
- package/dist/esm/liveSync.mjs.map +1 -1
- package/dist/esm/pull.mjs +3 -6
- package/dist/esm/pull.mjs.map +1 -1
- package/dist/esm/push.mjs +11 -7
- package/dist/esm/push.mjs.map +1 -1
- package/dist/esm/pushConfig.mjs +3 -10
- package/dist/esm/pushConfig.mjs.map +1 -1
- package/dist/esm/reviewDoc.mjs +3 -2
- package/dist/esm/reviewDoc.mjs.map +1 -1
- package/dist/esm/test/index.mjs +1 -1
- package/dist/esm/test/index.mjs.map +1 -1
- package/dist/esm/test/listMissingTranslations.mjs +8 -4
- package/dist/esm/test/listMissingTranslations.mjs.map +1 -1
- package/dist/esm/translateDoc.mjs +3 -2
- package/dist/esm/translateDoc.mjs.map +1 -1
- package/dist/esm/utils/checkAccess.mjs +54 -0
- package/dist/esm/utils/checkAccess.mjs.map +1 -0
- package/dist/types/fill/index.d.ts +3 -9
- package/dist/types/fill/index.d.ts.map +1 -1
- package/dist/types/getTargetDictionary.d.ts +15 -0
- package/dist/types/getTargetDictionary.d.ts.map +1 -0
- package/dist/types/listContentDeclaration.d.ts.map +1 -1
- package/dist/types/pull.d.ts.map +1 -1
- package/dist/types/push.d.ts.map +1 -1
- package/dist/types/pushConfig.d.ts.map +1 -1
- package/dist/types/reviewDoc.d.ts.map +1 -1
- package/dist/types/test/listMissingTranslations.d.ts +4 -4
- package/dist/types/test/listMissingTranslations.d.ts.map +1 -1
- package/dist/types/translateDoc.d.ts.map +1 -1
- package/dist/types/utils/checkAccess.d.ts +5 -0
- package/dist/types/utils/checkAccess.d.ts.map +1 -0
- package/package.json +16 -16
- package/dist/cjs/fill/getTargetDictionary.cjs.map +0 -1
- package/dist/cjs/utils/checkAIAccess.cjs +0 -44
- package/dist/cjs/utils/checkAIAccess.cjs.map +0 -1
- package/dist/esm/fill/getTargetDictionary.mjs.map +0 -1
- package/dist/esm/utils/checkAIAccess.mjs +0 -20
- package/dist/esm/utils/checkAIAccess.mjs.map +0 -1
- package/dist/types/fill/getTargetDictionary.d.ts +0 -4
- package/dist/types/fill/getTargetDictionary.d.ts.map +0 -1
- package/dist/types/utils/checkAIAccess.d.ts +0 -4
- package/dist/types/utils/checkAIAccess.d.ts.map +0 -1
package/dist/esm/fill/index.mjs
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getIntlayerAPIProxy } from "@intlayer/api";
|
|
2
2
|
import {
|
|
3
3
|
formatLocale,
|
|
4
4
|
formatPath,
|
|
5
5
|
mergeDictionaries,
|
|
6
|
+
parallelize,
|
|
6
7
|
prepareIntlayer,
|
|
7
8
|
processPerLocaleDictionary,
|
|
8
9
|
reduceDictionaryContent,
|
|
9
10
|
writeContentDeclaration
|
|
10
11
|
} from "@intlayer/chokidar";
|
|
11
12
|
import {
|
|
13
|
+
ANSIColors,
|
|
14
|
+
colon,
|
|
15
|
+
colorize,
|
|
12
16
|
colorizeKey,
|
|
13
17
|
colorizePath,
|
|
14
18
|
getAppLogger,
|
|
@@ -19,12 +23,14 @@ import {
|
|
|
19
23
|
getLocalisedContent,
|
|
20
24
|
getMissingLocalesContent
|
|
21
25
|
} from "@intlayer/core";
|
|
22
|
-
import
|
|
23
|
-
import pLimit from "p-limit";
|
|
26
|
+
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
24
27
|
import { relative } from "path";
|
|
25
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
ensureArray,
|
|
30
|
+
getTargetUnmergedDictionaries
|
|
31
|
+
} from "../getTargetDictionary.mjs";
|
|
32
|
+
import { checkAIAccess } from "../utils/checkAccess.mjs";
|
|
26
33
|
import { autoFill } from "./autoFill.mjs";
|
|
27
|
-
import { ensureArray, getTargetDictionary } from "./getTargetDictionary.mjs";
|
|
28
34
|
const NB_CONCURRENT_TRANSLATIONS = 8;
|
|
29
35
|
const fill = async (options) => {
|
|
30
36
|
const configuration = getConfiguration(options.configOptions);
|
|
@@ -40,37 +46,40 @@ const fill = async (options) => {
|
|
|
40
46
|
const mode = options.mode ?? "complete";
|
|
41
47
|
const baseLocale = options.sourceLocale ?? defaultLocale;
|
|
42
48
|
const outputLocales = (options.outputLocales ? ensureArray(options.outputLocales) : locales).filter((locale) => locale !== baseLocale);
|
|
43
|
-
checkAIAccess(configuration, options.aiOptions);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();
|
|
48
|
-
oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;
|
|
49
|
-
}
|
|
50
|
-
appLogger("Starting fill function", {
|
|
51
|
-
level: "info"
|
|
52
|
-
});
|
|
53
|
-
const targetUnmergedDictionaries = await getTargetDictionary(options);
|
|
49
|
+
const hasAIAccess = await checkAIAccess(configuration, options.aiOptions);
|
|
50
|
+
if (!hasAIAccess) return;
|
|
51
|
+
const intlayerAPI = getIntlayerAPIProxy(void 0, configuration);
|
|
52
|
+
const targetUnmergedDictionaries = await getTargetUnmergedDictionaries(options);
|
|
54
53
|
const affectedDictionaryKeys = /* @__PURE__ */ new Set();
|
|
55
54
|
targetUnmergedDictionaries.forEach((dict) => {
|
|
56
55
|
affectedDictionaryKeys.add(dict.key);
|
|
57
56
|
});
|
|
58
|
-
appLogger(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
appLogger([
|
|
58
|
+
"Affected dictionary keys for processing:",
|
|
59
|
+
Array.from(affectedDictionaryKeys).map((key) => colorizeKey(key)).join(", ")
|
|
60
|
+
]);
|
|
61
|
+
const maxKeyLength = Math.max(
|
|
62
|
+
...targetUnmergedDictionaries.map((dict) => dict.key.length)
|
|
63
|
+
);
|
|
64
|
+
const maxLocaleLength = Math.max(
|
|
65
|
+
...locales.map((locale) => formatLocale(locale).length)
|
|
66
66
|
);
|
|
67
67
|
for (const targetUnmergedDictionary of targetUnmergedDictionaries) {
|
|
68
|
+
const dictionaryPreset = colon(
|
|
69
|
+
[
|
|
70
|
+
colorize(" - [", ANSIColors.GREY_DARK),
|
|
71
|
+
colorizeKey(targetUnmergedDictionary.key),
|
|
72
|
+
colorize("]", ANSIColors.GREY_DARK)
|
|
73
|
+
].join(""),
|
|
74
|
+
{ colSize: maxKeyLength + 6 }
|
|
75
|
+
);
|
|
68
76
|
const dictionaryKey = targetUnmergedDictionary.key;
|
|
77
|
+
const dictionariesRecord = getDictionaries(configuration);
|
|
69
78
|
const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];
|
|
70
79
|
const sourceLocale = targetUnmergedDictionary.locale ?? baseLocale;
|
|
71
80
|
if (!mainDictionaryToProcess) {
|
|
72
81
|
appLogger(
|
|
73
|
-
|
|
82
|
+
`${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,
|
|
74
83
|
{
|
|
75
84
|
level: "warn"
|
|
76
85
|
}
|
|
@@ -78,21 +87,21 @@ const fill = async (options) => {
|
|
|
78
87
|
continue;
|
|
79
88
|
}
|
|
80
89
|
if (!targetUnmergedDictionary.filePath) {
|
|
81
|
-
appLogger(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
level: "warn"
|
|
85
|
-
}
|
|
86
|
-
);
|
|
90
|
+
appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {
|
|
91
|
+
level: "warn"
|
|
92
|
+
});
|
|
87
93
|
continue;
|
|
88
94
|
}
|
|
89
95
|
const relativePath = relative(
|
|
90
96
|
configuration.content.baseDir,
|
|
91
97
|
targetUnmergedDictionary.filePath
|
|
92
98
|
);
|
|
93
|
-
appLogger(
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
appLogger(
|
|
100
|
+
`${dictionaryPreset} Processing content declaration: ${colorizePath(relativePath)}`,
|
|
101
|
+
{
|
|
102
|
+
level: "info"
|
|
103
|
+
}
|
|
104
|
+
);
|
|
96
105
|
const sourceLocaleContent = getFilterTranslationsOnlyContent(
|
|
97
106
|
mainDictionaryToProcess,
|
|
98
107
|
sourceLocale,
|
|
@@ -100,7 +109,7 @@ const fill = async (options) => {
|
|
|
100
109
|
);
|
|
101
110
|
if (Object.keys(sourceLocaleContent).length === 0) {
|
|
102
111
|
appLogger(
|
|
103
|
-
|
|
112
|
+
`${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,
|
|
104
113
|
{
|
|
105
114
|
level: "warn"
|
|
106
115
|
}
|
|
@@ -108,9 +117,6 @@ const fill = async (options) => {
|
|
|
108
117
|
continue;
|
|
109
118
|
}
|
|
110
119
|
const result = [];
|
|
111
|
-
const limit = pLimit(
|
|
112
|
-
options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
|
|
113
|
-
);
|
|
114
120
|
let outputLocalesList = outputLocales;
|
|
115
121
|
if (mode === "complete") {
|
|
116
122
|
const missingLocales = getMissingLocalesContent(
|
|
@@ -124,10 +130,28 @@ const fill = async (options) => {
|
|
|
124
130
|
);
|
|
125
131
|
outputLocalesList = missingLocales;
|
|
126
132
|
}
|
|
127
|
-
|
|
128
|
-
(
|
|
133
|
+
if (outputLocalesList.length === 0) {
|
|
134
|
+
appLogger(
|
|
135
|
+
`${dictionaryPreset} No locales to fill - Skipping dictionary`,
|
|
136
|
+
{
|
|
137
|
+
level: "warn"
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const translationResults = await parallelize(
|
|
143
|
+
outputLocalesList,
|
|
144
|
+
async (targetLocale) => {
|
|
145
|
+
const localePreset = colon(
|
|
146
|
+
[
|
|
147
|
+
colorize("[", ANSIColors.GREY_DARK),
|
|
148
|
+
formatLocale(targetLocale),
|
|
149
|
+
colorize("]", ANSIColors.GREY_DARK)
|
|
150
|
+
].join(""),
|
|
151
|
+
{ colSize: maxLocaleLength }
|
|
152
|
+
);
|
|
129
153
|
appLogger(
|
|
130
|
-
|
|
154
|
+
`${dictionaryPreset}${localePreset} Preparing translation for dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,
|
|
131
155
|
{
|
|
132
156
|
level: "info"
|
|
133
157
|
}
|
|
@@ -138,36 +162,21 @@ const fill = async (options) => {
|
|
|
138
162
|
{ dictionaryKey, keyPath: [] }
|
|
139
163
|
);
|
|
140
164
|
try {
|
|
141
|
-
const translationResult = await
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
outputLocale: targetLocale,
|
|
153
|
-
mode,
|
|
154
|
-
aiOptions: options.aiOptions
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
...oAuth2AccessToken && {
|
|
158
|
-
headers: {
|
|
159
|
-
Authorization: `Bearer ${oAuth2AccessToken}`
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
);
|
|
165
|
+
const translationResult = await intlayerAPI.ai.translateJSON({
|
|
166
|
+
entryFileContent: sourceLocaleContent.content,
|
|
167
|
+
// Should be JSON, ensure getLocalisedContent provides this.
|
|
168
|
+
presetOutputContent: presetOutputContent.content,
|
|
169
|
+
// Should be JSON
|
|
170
|
+
dictionaryDescription: mainDictionaryToProcess.description ?? "",
|
|
171
|
+
entryLocale: sourceLocale,
|
|
172
|
+
outputLocale: targetLocale,
|
|
173
|
+
mode,
|
|
174
|
+
aiOptions: options.aiOptions
|
|
175
|
+
});
|
|
164
176
|
if (!translationResult.data?.fileContent) {
|
|
165
|
-
appLogger(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
level: "error"
|
|
169
|
-
}
|
|
170
|
-
);
|
|
177
|
+
appLogger(`${dictionaryPreset}${localePreset} No content result`, {
|
|
178
|
+
level: "error"
|
|
179
|
+
});
|
|
171
180
|
return null;
|
|
172
181
|
}
|
|
173
182
|
const processedPerLocaleDictionary = processPerLocaleDictionary({
|
|
@@ -178,16 +187,16 @@ const fill = async (options) => {
|
|
|
178
187
|
return processedPerLocaleDictionary;
|
|
179
188
|
} catch (error) {
|
|
180
189
|
appLogger(
|
|
181
|
-
|
|
190
|
+
`${dictionaryPreset}${localePreset} ${colorize("Error filling", ANSIColors.RED)}: ` + error,
|
|
182
191
|
{
|
|
183
192
|
level: "error"
|
|
184
193
|
}
|
|
185
194
|
);
|
|
186
195
|
return null;
|
|
187
196
|
}
|
|
188
|
-
}
|
|
197
|
+
},
|
|
198
|
+
options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS
|
|
189
199
|
);
|
|
190
|
-
const translationResults = await Promise.all(translationPromises);
|
|
191
200
|
translationResults.forEach((translationResult) => {
|
|
192
201
|
if (translationResult) {
|
|
193
202
|
result.push(translationResult);
|
|
@@ -225,7 +234,7 @@ const fill = async (options) => {
|
|
|
225
234
|
);
|
|
226
235
|
if (formattedDict.filePath) {
|
|
227
236
|
appLogger(
|
|
228
|
-
|
|
237
|
+
`${dictionaryPreset} Content declaration written to ${formatPath(formattedDict.filePath)}`,
|
|
229
238
|
{
|
|
230
239
|
level: "info"
|
|
231
240
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/fill/index.ts"],"sourcesContent":["import { AIOptions, getAiAPI, getOAuthAPI } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n formatLocale,\n formatPath,\n ListGitFilesOptions,\n mergeDictionaries,\n prepareIntlayer,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n colorizeKey,\n colorizePath,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n Locales,\n} from '@intlayer/config';\nimport {\n type ContentNode,\n type Dictionary,\n getFilterTranslationsOnlyContent,\n getLocalisedContent,\n getMissingLocalesContent,\n} from '@intlayer/core';\nimport dictionariesRecord from '@intlayer/dictionaries-entry';\nimport pLimit from 'p-limit';\nimport { relative } from 'path';\nimport { checkAIAccess } from '../utils/checkAIAccess';\nimport { autoFill } from './autoFill';\nimport { ensureArray, getTargetDictionary } from './getTargetDictionary';\n\nconst NB_CONCURRENT_TRANSLATIONS = 8;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n file?: string | string[];\n mode?: 'complete' | 'review';\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n build?: boolean;\n};\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n if (options.build) {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'complete';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n const outputLocales = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) => locale !== baseLocale);\n\n checkAIAccess(configuration, options.aiOptions);\n\n let oAuth2AccessToken: string | undefined;\n if (configuration.editor.clientId) {\n const intlayerAuthAPI = getOAuthAPI(configuration);\n const oAuth2TokenResult = await intlayerAuthAPI.getOAuth2AccessToken();\n\n oAuth2AccessToken = oAuth2TokenResult.data?.accessToken;\n }\n\n appLogger('Starting fill function', {\n level: 'info',\n });\n\n const targetUnmergedDictionaries = await getTargetDictionary(options);\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger(\n [\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys)\n .map((key) => colorizeKey(key))\n .join(', '),\n ],\n {\n isVerbose: true,\n }\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryKey = targetUnmergedDictionary.key;\n const mainDictionaryToProcess = dictionariesRecord[dictionaryKey];\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `Dictionary with key '${colorizeKey(dictionaryKey)}' not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(\n `Dictionary with key '${colorizeKey(dictionaryKey)}' has no file path. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const relativePath = relative(\n configuration.content.baseDir,\n targetUnmergedDictionary.filePath\n );\n\n appLogger(`Processing content declaration: ${colorizePath(relativePath)}`, {\n level: 'info',\n });\n\n const sourceLocaleContent = getFilterTranslationsOnlyContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `No content found for dictionary '${colorizeKey(dictionaryKey)}' in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const result: Dictionary[] = [];\n\n // 5. for each locale to translate (exclude base locale) generate json translations\n // Limit concurrent translations to 5 at a time\n const limit = pLimit(\n options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS\n );\n\n // Determine output locales\n let outputLocalesList: Locales[] = outputLocales;\n\n // If mode is review, translate all locales\n // If mode is complete, translate only the locales that are not the source locale\n if (mode === 'complete') {\n const missingLocales = getMissingLocalesContent(\n mainDictionaryToProcess as unknown as ContentNode,\n outputLocales,\n {\n dictionaryKey: mainDictionaryToProcess.key,\n keyPath: [],\n plugins: [],\n }\n );\n\n outputLocalesList = missingLocales;\n }\n\n const translationPromises = outputLocalesList.map((targetLocale) =>\n limit(async () => {\n appLogger(\n `Preparing translation for '${colorizeKey(dictionaryKey)}' dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,\n {\n level: 'info',\n }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n targetLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await getAiAPI(\n undefined,\n configuration\n ).translateJSON(\n {\n entryFileContent: sourceLocaleContent.content, // Should be JSON, ensure getLocalisedContent provides this.\n presetOutputContent: presetOutputContent.content, // Should be JSON\n dictionaryDescription: mainDictionaryToProcess.description,\n entryLocale: sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions: options.aiOptions,\n },\n {\n ...(oAuth2AccessToken && {\n headers: {\n Authorization: `Bearer ${oAuth2AccessToken}`,\n },\n }),\n }\n );\n\n if (!translationResult.data?.fileContent) {\n appLogger(\n `No content result found for '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}`,\n {\n level: 'error',\n }\n );\n return null;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: targetLocale,\n });\n\n return processedPerLocaleDictionary;\n } catch (error) {\n appLogger(\n `Error filling '${colorizeKey(dictionaryKey)}' to ${formatLocale(targetLocale)}:` +\n error,\n {\n level: 'error',\n }\n );\n return null;\n }\n })\n );\n\n // Wait for all translations to complete\n const translationResults = await Promise.all(translationPromises);\n\n // Filter out null results and add to result array\n translationResults.forEach((translationResult) => {\n if (translationResult) {\n result.push(translationResult);\n }\n });\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...result, mainDictionaryToProcess] // Mode review: generated content will override the base one\n : [mainDictionaryToProcess, ...result]; // Mode complete: base content will override the generated one\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n if (formattedDict.autoFill || configuration.content.autoFill) {\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill ?? configuration.content.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n if (formattedDict.filePath) {\n appLogger(\n `Content declaration for '${colorizeKey(dictionaryKey)}' written to ${formatPath(formattedDict.filePath)}`,\n {\n level: 'info',\n }\n );\n }\n }\n }\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/fill/index.ts"],"sourcesContent":["import { AIOptions, getIntlayerAPIProxy } from '@intlayer/api'; // Importing only getAiAPI for now\nimport {\n formatLocale,\n formatPath,\n ListGitFilesOptions,\n mergeDictionaries,\n parallelize,\n prepareIntlayer,\n processPerLocaleDictionary,\n reduceDictionaryContent,\n writeContentDeclaration,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n colon,\n colorize,\n colorizeKey,\n colorizePath,\n getAppLogger,\n getConfiguration,\n Locales,\n} from '@intlayer/config';\nimport {\n type ContentNode,\n type Dictionary,\n getFilterTranslationsOnlyContent,\n getLocalisedContent,\n getMissingLocalesContent,\n} from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { relative } from 'path';\nimport {\n ensureArray,\n GetTargetDictionaryOptions,\n getTargetUnmergedDictionaries,\n} from '../getTargetDictionary';\nimport { checkAIAccess } from '../utils/checkAccess';\nimport { autoFill } from './autoFill';\n\nconst NB_CONCURRENT_TRANSLATIONS = 8;\n\n// Arguments for the fill function\nexport type FillOptions = {\n sourceLocale?: Locales;\n outputLocales?: Locales | Locales[];\n mode?: 'complete' | 'review';\n gitOptions?: ListGitFilesOptions;\n aiOptions?: AIOptions; // Added aiOptions to be passed to translateJSON\n verbose?: boolean;\n nbConcurrentTranslations?: number;\n build?: boolean;\n} & GetTargetDictionaryOptions;\n\n/**\n * Fill translations based on the provided options.\n */\nexport const fill = async (options: FillOptions): Promise<void> => {\n const configuration = getConfiguration(options.configOptions);\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n if (options.build) {\n await prepareIntlayer(configuration);\n }\n\n const { defaultLocale, locales } = configuration.internationalization;\n const mode = options.mode ?? 'complete';\n const baseLocale = options.sourceLocale ?? defaultLocale;\n const outputLocales = (\n options.outputLocales ? ensureArray(options.outputLocales) : locales\n ).filter((locale) => locale !== baseLocale);\n\n const hasAIAccess = await checkAIAccess(configuration, options.aiOptions);\n\n if (!hasAIAccess) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, configuration);\n\n const targetUnmergedDictionaries =\n await getTargetUnmergedDictionaries(options);\n\n const affectedDictionaryKeys = new Set<string>();\n targetUnmergedDictionaries.forEach((dict) => {\n affectedDictionaryKeys.add(dict.key);\n });\n\n appLogger([\n 'Affected dictionary keys for processing:',\n Array.from(affectedDictionaryKeys)\n .map((key) => colorizeKey(key))\n .join(', '),\n ]);\n\n const maxKeyLength = Math.max(\n ...targetUnmergedDictionaries.map((dict) => dict.key.length)\n );\n const maxLocaleLength = Math.max(\n ...locales.map((locale) => formatLocale(locale).length)\n );\n\n for (const targetUnmergedDictionary of targetUnmergedDictionaries) {\n const dictionaryPreset = colon(\n [\n colorize(' - [', ANSIColors.GREY_DARK),\n colorizeKey(targetUnmergedDictionary.key),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxKeyLength + 6 }\n );\n\n const dictionaryKey = targetUnmergedDictionary.key;\n const dictionariesRecord = getDictionaries(configuration);\n\n const mainDictionaryToProcess: Dictionary =\n dictionariesRecord[dictionaryKey];\n\n const sourceLocale: Locales =\n (targetUnmergedDictionary.locale as Locales) ?? baseLocale;\n\n if (!mainDictionaryToProcess) {\n appLogger(\n `${dictionaryPreset} Dictionary not found in dictionariesRecord. Skipping.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n if (!targetUnmergedDictionary.filePath) {\n appLogger(`${dictionaryPreset} Dictionary has no file path. Skipping.`, {\n level: 'warn',\n });\n continue;\n }\n\n const relativePath = relative(\n configuration.content.baseDir,\n targetUnmergedDictionary.filePath\n );\n\n appLogger(\n `${dictionaryPreset} Processing content declaration: ${colorizePath(relativePath)}`,\n {\n level: 'info',\n }\n );\n\n const sourceLocaleContent = getFilterTranslationsOnlyContent(\n mainDictionaryToProcess as unknown as ContentNode,\n sourceLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n if (Object.keys(sourceLocaleContent).length === 0) {\n appLogger(\n `${dictionaryPreset} No content found for dictionary in source locale ${formatLocale(sourceLocale)}. Skipping translation for this dictionary.`,\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const result: Dictionary[] = [];\n\n // Determine output locales\n let outputLocalesList: Locales[] = outputLocales;\n\n // If mode is review, translate all locales\n // If mode is complete, translate only the locales that are not the source locale\n if (mode === 'complete') {\n const missingLocales = getMissingLocalesContent(\n mainDictionaryToProcess as unknown as ContentNode,\n outputLocales,\n {\n dictionaryKey: mainDictionaryToProcess.key,\n keyPath: [],\n plugins: [],\n }\n );\n\n outputLocalesList = missingLocales;\n }\n\n if (outputLocalesList.length === 0) {\n appLogger(\n `${dictionaryPreset} No locales to fill - Skipping dictionary`,\n\n {\n level: 'warn',\n }\n );\n continue;\n }\n\n const translationResults = await parallelize(\n outputLocalesList,\n async (targetLocale) => {\n const localePreset = colon(\n [\n colorize('[', ANSIColors.GREY_DARK),\n formatLocale(targetLocale),\n colorize(']', ANSIColors.GREY_DARK),\n ].join(''),\n { colSize: maxLocaleLength }\n );\n\n appLogger(\n `${dictionaryPreset}${localePreset} Preparing translation for dictionary from ${formatLocale(sourceLocale)} to ${formatLocale(targetLocale)}`,\n {\n level: 'info',\n }\n );\n\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n targetLocale,\n { dictionaryKey, keyPath: [] }\n );\n\n try {\n const translationResult = await intlayerAPI.ai.translateJSON({\n entryFileContent: sourceLocaleContent.content, // Should be JSON, ensure getLocalisedContent provides this.\n presetOutputContent: presetOutputContent.content, // Should be JSON\n dictionaryDescription: mainDictionaryToProcess.description ?? '',\n entryLocale: sourceLocale,\n outputLocale: targetLocale,\n mode,\n aiOptions: options.aiOptions,\n });\n\n if (!translationResult.data?.fileContent) {\n appLogger(`${dictionaryPreset}${localePreset} No content result`, {\n level: 'error',\n });\n return null;\n }\n\n const processedPerLocaleDictionary = processPerLocaleDictionary({\n ...mainDictionaryToProcess,\n content: translationResult.data?.fileContent,\n locale: targetLocale,\n });\n\n return processedPerLocaleDictionary;\n } catch (error) {\n appLogger(\n `${dictionaryPreset}${localePreset} ${colorize('Error filling', ANSIColors.RED)}: ` +\n error,\n {\n level: 'error',\n }\n );\n return null;\n }\n },\n options.nbConcurrentTranslations ?? NB_CONCURRENT_TRANSLATIONS\n );\n\n // Filter out null results and add to result array\n translationResults.forEach((translationResult) => {\n if (translationResult) {\n result.push(translationResult);\n }\n });\n\n const dictionaryToMerge =\n mode === 'review'\n ? [...result, mainDictionaryToProcess] // Mode review: generated content will override the base one\n : [mainDictionaryToProcess, ...result]; // Mode complete: base content will override the generated one\n\n const mergedResults = mergeDictionaries(dictionaryToMerge);\n\n let formattedDict = targetUnmergedDictionary;\n\n if (formattedDict.locale) {\n const presetOutputContent = getLocalisedContent(\n mainDictionaryToProcess as unknown as ContentNode,\n formattedDict.locale,\n { dictionaryKey, keyPath: [] }\n );\n\n formattedDict = {\n ...formattedDict,\n content: presetOutputContent.content,\n };\n }\n\n const reducedResult = reduceDictionaryContent(mergedResults, formattedDict);\n\n if (formattedDict.autoFill || configuration.content.autoFill) {\n await autoFill(\n mergedResults,\n targetUnmergedDictionary,\n formattedDict.autoFill ?? configuration.content.autoFill,\n outputLocalesList,\n [sourceLocale],\n configuration\n );\n } else {\n await writeContentDeclaration(\n { ...formattedDict, content: reducedResult.content },\n configuration,\n formattedDict.filePath\n );\n\n if (formattedDict.filePath) {\n appLogger(\n `${dictionaryPreset} Content declaration written to ${formatPath(formattedDict.filePath)}`,\n {\n level: 'info',\n }\n );\n }\n }\n }\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { listGitFiles } from "@intlayer/chokidar";
|
|
2
2
|
import { getConfiguration } from "@intlayer/config";
|
|
3
|
-
import
|
|
3
|
+
import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
const ensureArray = (value) => [value].flat();
|
|
6
|
-
const
|
|
6
|
+
const getTargetUnmergedDictionaries = async (options) => {
|
|
7
7
|
const configuration = getConfiguration(options.configOptions);
|
|
8
8
|
const { baseDir } = configuration.content;
|
|
9
|
+
const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);
|
|
9
10
|
let result = Object.values(unmergedDictionariesRecord).flat();
|
|
10
11
|
if (typeof options.file !== "undefined") {
|
|
11
12
|
const fileArray = ensureArray(options.file);
|
|
@@ -46,6 +47,6 @@ const getTargetDictionary = async (options) => {
|
|
|
46
47
|
};
|
|
47
48
|
export {
|
|
48
49
|
ensureArray,
|
|
49
|
-
|
|
50
|
+
getTargetUnmergedDictionaries
|
|
50
51
|
};
|
|
51
52
|
//# sourceMappingURL=getTargetDictionary.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/getTargetDictionary.ts"],"sourcesContent":["import { listGitFiles, ListGitFilesOptions } from '@intlayer/chokidar';\nimport { getConfiguration, GetConfigurationOptions } from '@intlayer/config';\nimport { Dictionary } from '@intlayer/core';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { join } from 'path';\n\nexport const ensureArray = <T>(value: T | T[]): T[] => [value].flat() as T[];\n\n// Arguments for the fill function\nexport type GetTargetDictionaryOptions = {\n file?: string | string[];\n keys?: string | string[];\n excludedKeys?: string | string[];\n filter?: (entry: Dictionary) => boolean; // DictionaryEntry needs to be defined\n pathFilter?: string | string[];\n gitOptions?: ListGitFilesOptions;\n configOptions?: GetConfigurationOptions;\n};\n\nexport const getTargetUnmergedDictionaries = async (\n options: GetTargetDictionaryOptions\n): Promise<Dictionary[]> => {\n const configuration = getConfiguration(options.configOptions);\n\n const { baseDir } = configuration.content;\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(configuration);\n let result = Object.values(unmergedDictionariesRecord).flat();\n\n // 1. if filePath not defined, list all content declaration files based on unmerged dictionaries list\n if (typeof options.file !== 'undefined') {\n const fileArray = ensureArray(options.file);\n const absoluteFilePaths = fileArray.map((file) => join(baseDir, file));\n\n result = result.filter(\n (dict) =>\n dict.filePath &&\n (absoluteFilePaths.includes(dict.filePath) ||\n absoluteFilePaths.includes(join(baseDir, dict.filePath)))\n );\n }\n\n if (typeof options.keys !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.keys)?.includes(dict.key)\n );\n }\n\n if (typeof options.excludedKeys !== 'undefined') {\n result = result.filter(\n (dict) => !ensureArray(options.excludedKeys)?.includes(dict.key)\n );\n }\n\n if (typeof options.pathFilter !== 'undefined') {\n result = result.filter((dict) =>\n ensureArray(options.pathFilter)?.includes(dict.filePath ?? '')\n );\n }\n\n if (typeof options.filter !== 'undefined') {\n result = result.filter(options.filter);\n }\n\n const gitOptions = options.gitOptions;\n if (gitOptions) {\n const gitChangedFiles = await listGitFiles(gitOptions);\n\n if (gitChangedFiles) {\n // Convert dictionary file paths to be relative to git root for comparison\n\n // Filter dictionaries based on git changed files\n result = result.filter((dict) => {\n if (!dict.filePath) return false;\n\n return gitChangedFiles.some((gitFile) => dict.filePath === gitFile);\n });\n }\n }\n\n return result.filter((dict) => !dict.autoFilled);\n};\n"],"mappings":"AAAA,SAAS,oBAAyC;AAClD,SAAS,wBAAiD;AAE1D,SAAS,+BAA+B;AACxC,SAAS,YAAY;AAEd,MAAM,cAAc,CAAI,UAAwB,CAAC,KAAK,EAAE,KAAK;AAa7D,MAAM,gCAAgC,OAC3C,YAC0B;AAC1B,QAAM,gBAAgB,iBAAiB,QAAQ,aAAa;AAE5D,QAAM,EAAE,QAAQ,IAAI,cAAc;AAElC,QAAM,6BAA6B,wBAAwB,aAAa;AACxE,MAAI,SAAS,OAAO,OAAO,0BAA0B,EAAE,KAAK;AAG5D,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,UAAM,YAAY,YAAY,QAAQ,IAAI;AAC1C,UAAM,oBAAoB,UAAU,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC;AAErE,aAAS,OAAO;AAAA,MACd,CAAC,SACC,KAAK,aACJ,kBAAkB,SAAS,KAAK,QAAQ,KACvC,kBAAkB,SAAS,KAAK,SAAS,KAAK,QAAQ,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,SAAS,aAAa;AACvC,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,IAAI,GAAG,SAAS,KAAK,GAAG;AAAA,IAC9C;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,iBAAiB,aAAa;AAC/C,aAAS,OAAO;AAAA,MACd,CAAC,SAAS,CAAC,YAAY,QAAQ,YAAY,GAAG,SAAS,KAAK,GAAG;AAAA,IACjE;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,eAAe,aAAa;AAC7C,aAAS,OAAO;AAAA,MAAO,CAAC,SACtB,YAAY,QAAQ,UAAU,GAAG,SAAS,KAAK,YAAY,EAAE;AAAA,IAC/D;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,WAAW,aAAa;AACzC,aAAS,OAAO,OAAO,QAAQ,MAAM;AAAA,EACvC;AAEA,QAAM,aAAa,QAAQ;AAC3B,MAAI,YAAY;AACd,UAAM,kBAAkB,MAAM,aAAa,UAAU;AAErD,QAAI,iBAAiB;AAInB,eAAS,OAAO,OAAO,CAAC,SAAS;AAC/B,YAAI,CAAC,KAAK,SAAU,QAAO;AAE3B,eAAO,gBAAgB,KAAK,CAAC,YAAY,KAAK,aAAa,OAAO;AAAA,MACpE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,UAAU;AACjD;","names":[]}
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
getAppLogger,
|
|
7
7
|
getConfiguration
|
|
8
8
|
} from "@intlayer/config";
|
|
9
|
-
import
|
|
9
|
+
import { getUnmergedDictionaries } from "@intlayer/unmerged-dictionaries-entry";
|
|
10
10
|
import { relative } from "path";
|
|
11
11
|
const listContentDeclarationRows = (options) => {
|
|
12
12
|
const config = getConfiguration(options?.configOptions);
|
|
13
|
+
const unmergedDictionariesRecord = getUnmergedDictionaries(config);
|
|
13
14
|
const rows = Object.values(unmergedDictionariesRecord).flat().map((dictionary) => ({
|
|
14
15
|
key: dictionary.key ?? "",
|
|
15
16
|
path: relative(config.content.baseDir, dictionary.filePath ?? "Remote")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":["import { formatPath } from '@intlayer/chokidar';\nimport {\n colon,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/listContentDeclaration.ts"],"sourcesContent":["import { formatPath } from '@intlayer/chokidar';\nimport {\n colon,\n colorizeKey,\n colorizeNumber,\n getAppLogger,\n getConfiguration,\n type GetConfigurationOptions,\n} from '@intlayer/config';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { relative } from 'path';\n\ntype ListContentDeclarationOptions = {\n configOptions?: GetConfigurationOptions;\n};\n\nexport const listContentDeclarationRows = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n\n const unmergedDictionariesRecord = getUnmergedDictionaries(config);\n\n const rows = Object.values(unmergedDictionariesRecord)\n .flat()\n .map((dictionary) => ({\n key: dictionary.key ?? '',\n path: relative(config.content.baseDir, dictionary.filePath ?? 'Remote'),\n }));\n return rows;\n};\n\nexport const listContentDeclaration = (\n options?: ListContentDeclarationOptions\n) => {\n const config = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(config, {\n config: {\n prefix: '',\n },\n });\n\n const rows = listContentDeclarationRows(options);\n\n const lines = rows.map((r) =>\n [\n colon(` - ${colorizeKey(r.key)}`, {\n colSize: rows.map((r) => r.key.length),\n maxSize: 60,\n }),\n ' - ',\n formatPath(r.path),\n ].join('')\n );\n\n appLogger(`Content declaration files:`);\n\n lines.forEach((l) => {\n appLogger(l, {\n level: 'info',\n });\n });\n\n appLogger(`Total content declaration files: ${colorizeNumber(rows.length)}`);\n};\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,+BAA+B;AACxC,SAAS,gBAAgB;AAMlB,MAAM,6BAA6B,CACxC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AAEtD,QAAM,6BAA6B,wBAAwB,MAAM;AAEjE,QAAM,OAAO,OAAO,OAAO,0BAA0B,EAClD,KAAK,EACL,IAAI,CAAC,gBAAgB;AAAA,IACpB,KAAK,WAAW,OAAO;AAAA,IACvB,MAAM,SAAS,OAAO,QAAQ,SAAS,WAAW,YAAY,QAAQ;AAAA,EACxE,EAAE;AACJ,SAAO;AACT;AAEO,MAAM,yBAAyB,CACpC,YACG;AACH,QAAM,SAAS,iBAAiB,SAAS,aAAa;AACtD,QAAM,YAAY,aAAa,QAAQ;AAAA,IACrC,QAAQ;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,QAAM,OAAO,2BAA2B,OAAO;AAE/C,QAAM,QAAQ,KAAK;AAAA,IAAI,CAAC,MACtB;AAAA,MACE,MAAM,MAAM,YAAY,EAAE,GAAG,CAAC,IAAI;AAAA,QAChC,SAAS,KAAK,IAAI,CAACA,OAAMA,GAAE,IAAI,MAAM;AAAA,QACrC,SAAS;AAAA,MACX,CAAC;AAAA,MACD;AAAA,MACA,WAAW,EAAE,IAAI;AAAA,IACnB,EAAE,KAAK,EAAE;AAAA,EACX;AAEA,YAAU,4BAA4B;AAEtC,QAAM,QAAQ,CAAC,MAAM;AACnB,cAAU,GAAG;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AAED,YAAU,oCAAoC,eAAe,KAAK,MAAM,CAAC,EAAE;AAC7E;","names":["r"]}
|
package/dist/esm/liveSync.mjs
CHANGED
|
@@ -116,7 +116,7 @@ const liveSync = async (options) => {
|
|
|
116
116
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
117
117
|
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
118
118
|
});
|
|
119
|
-
const dictionaries = getDictionaries();
|
|
119
|
+
const dictionaries = getDictionaries(configuration);
|
|
120
120
|
const prefix = "/dictionaries/";
|
|
121
121
|
if (req.url.startsWith(prefix)) {
|
|
122
122
|
const [key, locale] = decodeURIComponent(req.url).slice(prefix.length).split("/");
|
|
@@ -143,7 +143,7 @@ const liveSync = async (options) => {
|
|
|
143
143
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
144
144
|
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
145
145
|
});
|
|
146
|
-
const unmergedDictionaries = getUnmergedDictionaries();
|
|
146
|
+
const unmergedDictionaries = getUnmergedDictionaries(configuration);
|
|
147
147
|
const prefix = "/unmerged_dictionaries/";
|
|
148
148
|
if (req.url.startsWith(prefix)) {
|
|
149
149
|
const key = decodeURIComponent(req.url.slice(prefix.length));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/liveSync.ts"],"sourcesContent":["import { IntlayerEventListener } from './IntlayerEventListener';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI } from '@intlayer/backend';\nimport { buildDictionary } from '@intlayer/chokidar';\nimport type { IntlayerConfig } from '@intlayer/config';\nimport { getAppLogger, getConfiguration } from '@intlayer/config';\nimport packageJson from '@intlayer/config/package.json';\nimport { getLocalisedContent } from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { ChildProcess, spawn } from 'child_process';\nimport { createServer } from 'http';\n\ntype LiveSyncOptions = {\n process?: string;\n};\n\nconst writeDictionary = async (\n dictionary: DictionaryAPI,\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n appLogger(`Writing dictionary ${dictionary.key}`);\n await buildDictionary([dictionary], configuration);\n};\n\nexport const liveSync = async (options?: LiveSyncOptions) => {\n const configuration = getConfiguration();\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const { liveSyncPort, liveSyncURL } = configuration.editor;\n\n let childProcess: ChildProcess | null = null;\n let eventListener: IntlayerEventListener | null = null;\n let isHotReloadConnected = false;\n let connectionStatus = 'disconnected'; // 'connected', 'connecting', 'reconnecting', 'disconnected', 'error'\n\n // Start the parallel process if provided\n if (options?.process) {\n const [command, ...args] = options.process.split(' ');\n\n childProcess = spawn(command, args, {\n stdio: 'inherit',\n shell: true,\n });\n\n childProcess.on('error', (error) => {\n appLogger(`Failed to start process '${options.process}':`, {\n level: 'error',\n });\n });\n\n childProcess.on('exit', (code) => {\n if (code !== 0) {\n appLogger(`Process \"${options.process}\" exited with code ${code}`);\n } else {\n appLogger(`Process \"${options.process}\" exited successfully`);\n }\n });\n }\n\n // Initialize the event listener for hot reload if configured\n if (\n configuration.editor.liveSync &&\n configuration.editor.backendURL &&\n configuration.editor.clientId &&\n configuration.editor.clientSecret\n ) {\n eventListener = new IntlayerEventListener(configuration);\n connectionStatus = 'connecting';\n\n // Set up connection callbacks\n eventListener.onConnectionOpen = () => {\n connectionStatus = 'connected';\n isHotReloadConnected = true;\n appLogger('Live sync connection established');\n };\n\n eventListener.onConnectionError = (error) => {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n const errorEvent = error as any;\n appLogger(\n `Live sync connection error: ${errorEvent.message ?? 'Unknown error'}`,\n {\n level: 'warn',\n }\n );\n\n // If this is a \"terminated: other side closed\" error, it's likely a server restart\n if (\n errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed')\n ) {\n appLogger(\n 'Server connection was terminated, automatic reconnection will be attempted...',\n {\n level: 'info',\n }\n );\n connectionStatus = 'reconnecting';\n }\n };\n\n // Set up dictionary change callbacks\n eventListener.onDictionaryAdded = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryChange = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryDeleted = (dictionary) =>\n writeDictionary(dictionary, configuration);\n\n try {\n await eventListener.initialize();\n } catch (error) {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n appLogger('Failed to initialize IntlayerEventListener:', {\n level: 'error',\n });\n appLogger(\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n {\n level: 'error',\n }\n );\n }\n } else if (!configuration.editor.liveSync) {\n appLogger(\n 'Hot reload is disabled. Please enable it in the configuration (editor.liveSync).'\n );\n } else if (\n !configuration.editor.clientId ||\n !configuration.editor.clientSecret\n ) {\n appLogger(\n 'Missing client credentials for hot reload. Please configure clientId and clientSecret'\n );\n }\n\n const server = createServer(async (req, res) => {\n // Handle CORS preflight requests\n if (req.method === 'OPTIONS') {\n res.writeHead(200, {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n\n res.end();\n return;\n }\n\n if (req.url?.startsWith('/dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const dictionaries = getDictionaries();\n\n const prefix = '/dictionaries/';\n if (req.url.startsWith(prefix)) {\n const [key, locale] = decodeURIComponent(req.url)\n .slice(prefix.length)\n .split('/');\n\n const dictionary = dictionaries[key] ?? null;\n\n if (locale) {\n const sourceLocaleContent = getLocalisedContent(dictionary, locale, {\n dictionaryKey: key,\n keyPath: [],\n });\n\n res.end(JSON.stringify(sourceLocaleContent));\n return;\n }\n\n res.end(JSON.stringify(dictionary));\n return;\n }\n\n res.end(JSON.stringify(dictionaries));\n return;\n }\n\n if (req.url?.startsWith('/unmerged_dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const unmergedDictionaries = getUnmergedDictionaries();\n\n const prefix = '/unmerged_dictionaries/';\n if (req.url.startsWith(prefix)) {\n const key = decodeURIComponent(req.url.slice(prefix.length));\n const one = unmergedDictionaries[key] ?? null;\n\n res.end(JSON.stringify(one));\n return;\n }\n\n res.end(JSON.stringify(unmergedDictionaries));\n return;\n }\n\n if (req.url === '/configuration') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n res.end(JSON.stringify(configuration));\n return;\n }\n\n if (req.url === '/health') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n res.end('Not found');\n return;\n });\n\n const getLiveSyncParam = () => {\n if (!configuration.editor.liveSync) return '\\x1b[31m✗ Disabled\\x1b[0m';\n\n return '\\x1b[32m✓ Enabled\\x1b[0m';\n };\n server.listen(liveSyncPort, () => {\n console.log(`\n \\x1b[1;90mINTLAYER v${packageJson.version}\\x1b[0m\n \n Live server running at: \\x1b[90m${liveSyncURL}\\x1b[0m\n - Backend URL: \\x1b[90m${configuration.editor.backendURL ?? '-'}\\x1b[0m\n - Live sync: ${getLiveSyncParam()}\n - Parallel process: ${options?.process === '' ? '-' : `\\x1b[90m${options?.process}\\x1b[0m`}\n - Access key: ${configuration.editor.clientId ?? '-'}\n `);\n });\n\n // Cleanup function to terminate child process and event listener when the main process exits\n const cleanup = () => {\n // Clean up event listener\n if (eventListener) {\n appLogger('Closing SSE connection...');\n eventListener.cleanup();\n }\n\n // Clean up child process\n if (childProcess && !childProcess.killed) {\n appLogger('Terminating parallel process...');\n childProcess.kill('SIGTERM');\n\n // Force kill after 5 seconds if process doesn't terminate gracefully\n setTimeout(() => {\n if (childProcess && !childProcess.killed) {\n appLogger('Force killing parallel process...');\n childProcess.kill('SIGKILL');\n }\n }, 5000);\n }\n\n server.close(() => {\n appLogger('Live sync server stopped');\n process.exit(0);\n });\n };\n\n // Handle process termination signals\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n process.on('exit', cleanup);\n};\n"],"mappings":"AAAA,SAAS,6BAA6B;AAGtC,SAAS,uBAAuB;AAEhC,SAAS,cAAc,wBAAwB;AAC/C,OAAO,iBAAiB;AACxB,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,SAAuB,aAAa;AACpC,SAAS,oBAAoB;AAM7B,MAAM,kBAAkB,OACtB,YACA,kBACG;AACH,QAAM,YAAY,aAAa,aAAa;AAC5C,YAAU,sBAAsB,WAAW,GAAG,EAAE;AAChD,QAAM,gBAAgB,CAAC,UAAU,GAAG,aAAa;AACnD;AAEO,MAAM,WAAW,OAAO,YAA8B;AAC3D,QAAM,gBAAgB,iBAAiB;AACvC,QAAM,YAAY,aAAa,eAAe;AAAA,IAC5C,QAAQ;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AAED,QAAM,EAAE,cAAc,YAAY,IAAI,cAAc;AAEpD,MAAI,eAAoC;AACxC,MAAI,gBAA8C;AAClD,MAAI,uBAAuB;AAC3B,MAAI,mBAAmB;AAGvB,MAAI,SAAS,SAAS;AACpB,UAAM,CAAC,SAAS,GAAG,IAAI,IAAI,QAAQ,QAAQ,MAAM,GAAG;AAEpD,mBAAe,MAAM,SAAS,MAAM;AAAA,MAClC,OAAO;AAAA,MACP,OAAO;AAAA,IACT,CAAC;AAED,iBAAa,GAAG,SAAS,CAAC,UAAU;AAClC,gBAAU,4BAA4B,QAAQ,OAAO,MAAM;AAAA,QACzD,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AAED,iBAAa,GAAG,QAAQ,CAAC,SAAS;AAChC,UAAI,SAAS,GAAG;AACd,kBAAU,YAAY,QAAQ,OAAO,sBAAsB,IAAI,EAAE;AAAA,MACnE,OAAO;AACL,kBAAU,YAAY,QAAQ,OAAO,uBAAuB;AAAA,MAC9D;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MACE,cAAc,OAAO,YACrB,cAAc,OAAO,cACrB,cAAc,OAAO,YACrB,cAAc,OAAO,cACrB;AACA,oBAAgB,IAAI,sBAAsB,aAAa;AACvD,uBAAmB;AAGnB,kBAAc,mBAAmB,MAAM;AACrC,yBAAmB;AACnB,6BAAuB;AACvB,gBAAU,kCAAkC;AAAA,IAC9C;AAEA,kBAAc,oBAAoB,CAAC,UAAU;AAC3C,yBAAmB;AACnB,6BAAuB;AACvB,YAAM,aAAa;AACnB;AAAA,QACE,+BAA+B,WAAW,WAAW,eAAe;AAAA,QACpE;AAAA,UACE,OAAO;AAAA,QACT;AAAA,MACF;AAGA,UACE,WAAW,SAAS,SAAS,YAAY,KACzC,WAAW,SAAS,SAAS,QAAQ,GACrC;AACA;AAAA,UACE;AAAA,UACA;AAAA,YACE,OAAO;AAAA,UACT;AAAA,QACF;AACA,2BAAmB;AAAA,MACrB;AAAA,IACF;AAGA,kBAAc,oBAAoB,CAAC,eACjC,gBAAgB,YAAY,aAAa;AAC3C,kBAAc,qBAAqB,CAAC,eAClC,gBAAgB,YAAY,aAAa;AAC3C,kBAAc,sBAAsB,CAAC,eACnC,gBAAgB,YAAY,aAAa;AAE3C,QAAI;AACF,YAAM,cAAc,WAAW;AAAA,IACjC,SAAS,OAAO;AACd,yBAAmB;AACnB,6BAAuB;AACvB,gBAAU,+CAA+C;AAAA,QACvD,OAAO;AAAA,MACT,CAAC;AACD;AAAA,QACE,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QAChE;AAAA,UACE,OAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF,WAAW,CAAC,cAAc,OAAO,UAAU;AACzC;AAAA,MACE;AAAA,IACF;AAAA,EACF,WACE,CAAC,cAAc,OAAO,YACtB,CAAC,cAAc,OAAO,cACtB;AACA;AAAA,MACE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;AAE9C,QAAI,IAAI,WAAW,WAAW;AAC5B,UAAI,UAAU,KAAK;AAAA,QACjB,+BAA+B;AAAA,QAC/B,gCAAgC;AAAA,QAChC,gCAAgC;AAAA,MAClC,CAAC;AAED,UAAI,IAAI;AACR;AAAA,IACF;AAEA,QAAI,IAAI,KAAK,WAAW,eAAe,GAAG;AACxC,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,+BAA+B;AAAA,QAC/B,gCAAgC;AAAA,QAChC,gCAAgC;AAAA,MAClC,CAAC;AACD,YAAM,eAAe,gBAAgB;AAErC,YAAM,SAAS;AACf,UAAI,IAAI,IAAI,WAAW,MAAM,GAAG;AAC9B,cAAM,CAAC,KAAK,MAAM,IAAI,mBAAmB,IAAI,GAAG,EAC7C,MAAM,OAAO,MAAM,EACnB,MAAM,GAAG;AAEZ,cAAM,aAAa,aAAa,GAAG,KAAK;AAExC,YAAI,QAAQ;AACV,gBAAM,sBAAsB,oBAAoB,YAAY,QAAQ;AAAA,YAClE,eAAe;AAAA,YACf,SAAS,CAAC;AAAA,UACZ,CAAC;AAED,cAAI,IAAI,KAAK,UAAU,mBAAmB,CAAC;AAC3C;AAAA,QACF;AAEA,YAAI,IAAI,KAAK,UAAU,UAAU,CAAC;AAClC;AAAA,MACF;AAEA,UAAI,IAAI,KAAK,UAAU,YAAY,CAAC;AACpC;AAAA,IACF;AAEA,QAAI,IAAI,KAAK,WAAW,wBAAwB,GAAG;AACjD,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,+BAA+B;AAAA,QAC/B,gCAAgC;AAAA,QAChC,gCAAgC;AAAA,MAClC,CAAC;AACD,YAAM,uBAAuB,wBAAwB;AAErD,YAAM,SAAS;AACf,UAAI,IAAI,IAAI,WAAW,MAAM,GAAG;AAC9B,cAAM,MAAM,mBAAmB,IAAI,IAAI,MAAM,OAAO,MAAM,CAAC;AAC3D,cAAM,MAAM,qBAAqB,GAAG,KAAK;AAEzC,YAAI,IAAI,KAAK,UAAU,GAAG,CAAC;AAC3B;AAAA,MACF;AAEA,UAAI,IAAI,KAAK,UAAU,oBAAoB,CAAC;AAC5C;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ,kBAAkB;AAChC,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,+BAA+B;AAAA,QAC/B,gCAAgC;AAAA,QAChC,gCAAgC;AAAA,MAClC,CAAC;AACD,UAAI,IAAI,KAAK,UAAU,aAAa,CAAC;AACrC;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ,WAAW;AACzB,UAAI,UAAU,KAAK;AAAA,QACjB,gBAAgB;AAAA,MAClB,CAAC;AACD,UAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,KAAK,CAAC,CAAC;AACxC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW;AACnB;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,MAAM;AAC7B,QAAI,CAAC,cAAc,OAAO,SAAU,QAAO;AAE3C,WAAO;AAAA,EACT;AACA,SAAO,OAAO,cAAc,MAAM;AAChC,YAAQ,IAAI;AAAA,4BACY,YAAY,OAAO;AAAA;AAAA,iDAEE,WAAW;AAAA,iDACX,cAAc,OAAO,cAAc,GAAG;AAAA,yCAC9C,iBAAiB,CAAC;AAAA,yCAClB,SAAS,YAAY,KAAK,MAAM,WAAW,SAAS,OAAO,SAAS;AAAA,yCACpE,cAAc,OAAO,YAAY,GAAG;AAAA,OACtE;AAAA,EACL,CAAC;AAGD,QAAM,UAAU,MAAM;AAEpB,QAAI,eAAe;AACjB,gBAAU,2BAA2B;AACrC,oBAAc,QAAQ;AAAA,IACxB;AAGA,QAAI,gBAAgB,CAAC,aAAa,QAAQ;AACxC,gBAAU,iCAAiC;AAC3C,mBAAa,KAAK,SAAS;AAG3B,iBAAW,MAAM;AACf,YAAI,gBAAgB,CAAC,aAAa,QAAQ;AACxC,oBAAU,mCAAmC;AAC7C,uBAAa,KAAK,SAAS;AAAA,QAC7B;AAAA,MACF,GAAG,GAAI;AAAA,IACT;AAEA,WAAO,MAAM,MAAM;AACjB,gBAAU,0BAA0B;AACpC,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACH;AAGA,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,WAAW,OAAO;AAC7B,UAAQ,GAAG,QAAQ,OAAO;AAC5B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/liveSync.ts"],"sourcesContent":["import { IntlayerEventListener } from './IntlayerEventListener';\n// @ts-ignore: @intlayer/backend is not built yet\nimport type { DictionaryAPI } from '@intlayer/backend';\nimport { buildDictionary } from '@intlayer/chokidar';\nimport type { IntlayerConfig } from '@intlayer/config';\nimport { getAppLogger, getConfiguration } from '@intlayer/config';\nimport packageJson from '@intlayer/config/package.json';\nimport { getLocalisedContent } from '@intlayer/core';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport { getUnmergedDictionaries } from '@intlayer/unmerged-dictionaries-entry';\nimport { ChildProcess, spawn } from 'child_process';\nimport { createServer } from 'http';\n\ntype LiveSyncOptions = {\n process?: string;\n};\n\nconst writeDictionary = async (\n dictionary: DictionaryAPI,\n configuration: IntlayerConfig\n) => {\n const appLogger = getAppLogger(configuration);\n appLogger(`Writing dictionary ${dictionary.key}`);\n await buildDictionary([dictionary], configuration);\n};\n\nexport const liveSync = async (options?: LiveSyncOptions) => {\n const configuration = getConfiguration();\n const appLogger = getAppLogger(configuration, {\n config: {\n prefix: '',\n },\n });\n\n const { liveSyncPort, liveSyncURL } = configuration.editor;\n\n let childProcess: ChildProcess | null = null;\n let eventListener: IntlayerEventListener | null = null;\n let isHotReloadConnected = false;\n let connectionStatus = 'disconnected'; // 'connected', 'connecting', 'reconnecting', 'disconnected', 'error'\n\n // Start the parallel process if provided\n if (options?.process) {\n const [command, ...args] = options.process.split(' ');\n\n childProcess = spawn(command, args, {\n stdio: 'inherit',\n shell: true,\n });\n\n childProcess.on('error', (error) => {\n appLogger(`Failed to start process '${options.process}':`, {\n level: 'error',\n });\n });\n\n childProcess.on('exit', (code) => {\n if (code !== 0) {\n appLogger(`Process \"${options.process}\" exited with code ${code}`);\n } else {\n appLogger(`Process \"${options.process}\" exited successfully`);\n }\n });\n }\n\n // Initialize the event listener for hot reload if configured\n if (\n configuration.editor.liveSync &&\n configuration.editor.backendURL &&\n configuration.editor.clientId &&\n configuration.editor.clientSecret\n ) {\n eventListener = new IntlayerEventListener(configuration);\n connectionStatus = 'connecting';\n\n // Set up connection callbacks\n eventListener.onConnectionOpen = () => {\n connectionStatus = 'connected';\n isHotReloadConnected = true;\n appLogger('Live sync connection established');\n };\n\n eventListener.onConnectionError = (error) => {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n const errorEvent = error as any;\n appLogger(\n `Live sync connection error: ${errorEvent.message ?? 'Unknown error'}`,\n {\n level: 'warn',\n }\n );\n\n // If this is a \"terminated: other side closed\" error, it's likely a server restart\n if (\n errorEvent.message?.includes('terminated') ||\n errorEvent.message?.includes('closed')\n ) {\n appLogger(\n 'Server connection was terminated, automatic reconnection will be attempted...',\n {\n level: 'info',\n }\n );\n connectionStatus = 'reconnecting';\n }\n };\n\n // Set up dictionary change callbacks\n eventListener.onDictionaryAdded = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryChange = (dictionary) =>\n writeDictionary(dictionary, configuration);\n eventListener.onDictionaryDeleted = (dictionary) =>\n writeDictionary(dictionary, configuration);\n\n try {\n await eventListener.initialize();\n } catch (error) {\n connectionStatus = 'error';\n isHotReloadConnected = false;\n appLogger('Failed to initialize IntlayerEventListener:', {\n level: 'error',\n });\n appLogger(\n `Error: ${error instanceof Error ? error.message : String(error)}`,\n {\n level: 'error',\n }\n );\n }\n } else if (!configuration.editor.liveSync) {\n appLogger(\n 'Hot reload is disabled. Please enable it in the configuration (editor.liveSync).'\n );\n } else if (\n !configuration.editor.clientId ||\n !configuration.editor.clientSecret\n ) {\n appLogger(\n 'Missing client credentials for hot reload. Please configure clientId and clientSecret'\n );\n }\n\n const server = createServer(async (req, res) => {\n // Handle CORS preflight requests\n if (req.method === 'OPTIONS') {\n res.writeHead(200, {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n\n res.end();\n return;\n }\n\n if (req.url?.startsWith('/dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const dictionaries = getDictionaries(configuration);\n\n const prefix = '/dictionaries/';\n if (req.url.startsWith(prefix)) {\n const [key, locale] = decodeURIComponent(req.url)\n .slice(prefix.length)\n .split('/');\n\n const dictionary = dictionaries[key] ?? null;\n\n if (locale) {\n const sourceLocaleContent = getLocalisedContent(dictionary, locale, {\n dictionaryKey: key,\n keyPath: [],\n });\n\n res.end(JSON.stringify(sourceLocaleContent));\n return;\n }\n\n res.end(JSON.stringify(dictionary));\n return;\n }\n\n res.end(JSON.stringify(dictionaries));\n return;\n }\n\n if (req.url?.startsWith('/unmerged_dictionaries')) {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n const unmergedDictionaries = getUnmergedDictionaries(configuration);\n\n const prefix = '/unmerged_dictionaries/';\n if (req.url.startsWith(prefix)) {\n const key = decodeURIComponent(req.url.slice(prefix.length));\n const one = unmergedDictionaries[key] ?? null;\n\n res.end(JSON.stringify(one));\n return;\n }\n\n res.end(JSON.stringify(unmergedDictionaries));\n return;\n }\n\n if (req.url === '/configuration') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n 'Cache-Control': 'no-store',\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n });\n res.end(JSON.stringify(configuration));\n return;\n }\n\n if (req.url === '/health') {\n res.writeHead(200, {\n 'Content-Type': 'application/json; charset=utf-8',\n });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n res.end('Not found');\n return;\n });\n\n const getLiveSyncParam = () => {\n if (!configuration.editor.liveSync) return '\\x1b[31m✗ Disabled\\x1b[0m';\n\n return '\\x1b[32m✓ Enabled\\x1b[0m';\n };\n server.listen(liveSyncPort, () => {\n console.log(`\n \\x1b[1;90mINTLAYER v${packageJson.version}\\x1b[0m\n \n Live server running at: \\x1b[90m${liveSyncURL}\\x1b[0m\n - Backend URL: \\x1b[90m${configuration.editor.backendURL ?? '-'}\\x1b[0m\n - Live sync: ${getLiveSyncParam()}\n - Parallel process: ${options?.process === '' ? '-' : `\\x1b[90m${options?.process}\\x1b[0m`}\n - Access key: ${configuration.editor.clientId ?? '-'}\n `);\n });\n\n // Cleanup function to terminate child process and event listener when the main process exits\n const cleanup = () => {\n // Clean up event listener\n if (eventListener) {\n appLogger('Closing SSE connection...');\n eventListener.cleanup();\n }\n\n // Clean up child process\n if (childProcess && !childProcess.killed) {\n appLogger('Terminating parallel process...');\n childProcess.kill('SIGTERM');\n\n // Force kill after 5 seconds if process doesn't terminate gracefully\n setTimeout(() => {\n if (childProcess && !childProcess.killed) {\n appLogger('Force killing parallel process...');\n childProcess.kill('SIGKILL');\n }\n }, 5000);\n }\n\n server.close(() => {\n appLogger('Live sync server stopped');\n process.exit(0);\n });\n };\n\n // Handle process termination signals\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n process.on('exit', cleanup);\n};\n"],"mappings":"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","names":[]}
|
package/dist/esm/pull.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { existsSync } from "fs";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
import { PullLogger } from "./pullLog.mjs";
|
|
15
|
+
import { checkCMSAuth } from "./utils/checkAccess.mjs";
|
|
15
16
|
const pull = async (options) => {
|
|
16
17
|
const appLogger = getAppLogger(options?.configOptions?.override, {
|
|
17
18
|
config: {
|
|
@@ -20,12 +21,8 @@ const pull = async (options) => {
|
|
|
20
21
|
});
|
|
21
22
|
try {
|
|
22
23
|
const config = getConfiguration(options?.configOptions);
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
25
|
-
throw new Error(
|
|
26
|
-
"Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project."
|
|
27
|
-
);
|
|
28
|
-
}
|
|
24
|
+
const hasCMSAuth = await checkCMSAuth(config);
|
|
25
|
+
if (!hasCMSAuth) return;
|
|
29
26
|
const intlayerAPI = getIntlayerAPIProxy(void 0, config);
|
|
30
27
|
const getDictionariesUpdateTimestampResult = await intlayerAPI.dictionary.getDictionariesUpdateTimestamp();
|
|
31
28
|
if (!getDictionariesUpdateTimestampResult.data) {
|
package/dist/esm/pull.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n parallelize,\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n ESMxCJSRequire,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/core';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { PullLogger, type PullStatus } from './pullLog';\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 config: {\n prefix: '',\n },\n });\n\n try {\n const config = getConfiguration(options?.configOptions);\n const { clientId, clientSecret } = config.editor;\n\n if (!clientId || !clientSecret) {\n throw new Error(\n 'Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project.'\n );\n }\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, 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, number> =\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 // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (ESMxCJSRequire(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, remoteUpdatedAt]) => {\n if (!remoteUpdatedAt) return true;\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, remoteUpdatedAt]) => {\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 // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options?.newDictionariesPath\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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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/pull.ts"],"sourcesContent":["import { getIntlayerAPIProxy } from '@intlayer/api';\nimport {\n parallelize,\n writeContentDeclaration,\n type DictionaryStatus,\n} from '@intlayer/chokidar';\nimport {\n ANSIColors,\n ESMxCJSRequire,\n getAppLogger,\n getConfiguration,\n GetConfigurationOptions,\n} from '@intlayer/config';\nimport type { Dictionary } from '@intlayer/core';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { PullLogger, type PullStatus } from './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 config: {\n prefix: '',\n },\n });\n\n try {\n const config = getConfiguration(options?.configOptions);\n\n const hasCMSAuth = await checkCMSAuth(config);\n\n if (!hasCMSAuth) return;\n\n const intlayerAPI = getIntlayerAPIProxy(undefined, 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, number> =\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 // Load local cached remote dictionaries (if any)\n const remoteDictionariesPath = join(\n config.content.mainDir,\n 'remote_dictionaries.cjs'\n );\n const remoteDictionariesRecord: Record<string, any> = existsSync(\n remoteDictionariesPath\n )\n ? (ESMxCJSRequire(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, remoteUpdatedAt]) => {\n if (!remoteUpdatedAt) return true;\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, remoteUpdatedAt]) => {\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 // Now, write the dictionary to local file\n const { status } = await writeContentDeclaration(\n sourceDictionary,\n config,\n options?.newDictionariesPath\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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","names":[]}
|
package/dist/esm/push.mjs
CHANGED
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
getAppLogger,
|
|
10
10
|
getConfiguration
|
|
11
11
|
} from "@intlayer/config";
|
|
12
|
-
import
|
|
12
|
+
import { getDictionaries } from "@intlayer/dictionaries-entry";
|
|
13
13
|
import * as fsPromises from "fs/promises";
|
|
14
|
+
import { join } from "path";
|
|
14
15
|
import * as readline from "readline";
|
|
15
16
|
import { PushLogger } from "./pushLog.mjs";
|
|
17
|
+
import { checkCMSAuth } from "./utils/checkAccess.mjs";
|
|
16
18
|
const push = async (options) => {
|
|
17
19
|
const config = getConfiguration(options?.configOptions);
|
|
18
20
|
const appLogger = getAppLogger(config, {
|
|
@@ -20,14 +22,11 @@ const push = async (options) => {
|
|
|
20
22
|
prefix: ""
|
|
21
23
|
}
|
|
22
24
|
});
|
|
23
|
-
const { clientId, clientSecret } = config.editor;
|
|
24
25
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"Missing OAuth2 client ID or client secret. To get access token go to https://intlayer.org/dashboard/project."
|
|
28
|
-
);
|
|
29
|
-
}
|
|
26
|
+
const hasCMSAuth = await checkCMSAuth(config);
|
|
27
|
+
if (!hasCMSAuth) return;
|
|
30
28
|
const intlayerAPI = getIntlayerAPIProxy(void 0, config);
|
|
29
|
+
const dictionariesRecord = getDictionaries(config);
|
|
31
30
|
let dictionaries = Object.values(dictionariesRecord);
|
|
32
31
|
const existingDictionariesKeys = Object.keys(dictionariesRecord);
|
|
33
32
|
if (options?.dictionaries) {
|
|
@@ -50,6 +49,11 @@ const push = async (options) => {
|
|
|
50
49
|
}
|
|
51
50
|
if (options?.gitOptions) {
|
|
52
51
|
const gitFiles = await listGitFiles(options.gitOptions);
|
|
52
|
+
dictionaries = dictionaries.filter(
|
|
53
|
+
(dictionary) => gitFiles.includes(
|
|
54
|
+
join(config.content.baseDir, dictionary.filePath ?? "")
|
|
55
|
+
)
|
|
56
|
+
);
|
|
53
57
|
}
|
|
54
58
|
if (dictionaries.length === 0) {
|
|
55
59
|
appLogger("No local dictionaries found", {
|