locize-cli 11.0.0 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +1 -1
  3. package/README.md +1 -0
  4. package/dist/cjs/add.js +90 -0
  5. package/{bin/locize → dist/cjs/cli.js} +390 -670
  6. package/dist/cjs/combineSubkeyPreprocessor.js +155 -0
  7. package/dist/cjs/convertToDesiredFormat.js +205 -0
  8. package/dist/cjs/convertToFlatFormat.js +231 -0
  9. package/dist/cjs/copyVersion.js +60 -0
  10. package/dist/cjs/createBranch.js +59 -0
  11. package/dist/cjs/deleteBranch.js +89 -0
  12. package/dist/cjs/deleteNamespace.js +37 -0
  13. package/dist/cjs/download.js +376 -0
  14. package/dist/cjs/filterNamespaces.js +13 -0
  15. package/dist/cjs/format.js +156 -0
  16. package/dist/cjs/formats.js +33 -0
  17. package/dist/cjs/get.js +66 -0
  18. package/dist/cjs/getBranches.js +37 -0
  19. package/dist/cjs/getJob.js +37 -0
  20. package/dist/cjs/getProjectStats.js +37 -0
  21. package/dist/cjs/getRemoteLanguages.js +38 -0
  22. package/dist/cjs/getRemoteNamespace.js +125 -0
  23. package/dist/cjs/index.js +37 -0
  24. package/dist/cjs/isValidUuid.js +6 -0
  25. package/dist/cjs/lngs.js +215 -0
  26. package/dist/cjs/mapLimit.js +22 -0
  27. package/dist/cjs/mergeBranch.js +80 -0
  28. package/dist/cjs/migrate.js +239 -0
  29. package/dist/cjs/missing.js +162 -0
  30. package/dist/cjs/package.json +5 -0
  31. package/{parseLocalLanguage.js → dist/cjs/parseLocalLanguage.js} +135 -142
  32. package/dist/cjs/parseLocalLanguages.js +18 -0
  33. package/dist/cjs/parseLocalReference.js +11 -0
  34. package/dist/cjs/publishVersion.js +42 -0
  35. package/dist/cjs/removeUndefinedFromArrays.js +19 -0
  36. package/dist/cjs/removeVersion.js +42 -0
  37. package/dist/cjs/request.js +66 -0
  38. package/dist/cjs/shouldUnflatten.js +21 -0
  39. package/dist/cjs/sortFlatResources.js +13 -0
  40. package/dist/cjs/sync.js +772 -0
  41. package/dist/cjs/unflatten.js +81 -0
  42. package/dist/esm/add.js +88 -0
  43. package/dist/esm/cli.js +1020 -0
  44. package/{combineSubkeyPreprocessor.js → dist/esm/combineSubkeyPreprocessor.js} +70 -73
  45. package/dist/esm/convertToDesiredFormat.js +203 -0
  46. package/dist/esm/convertToFlatFormat.js +229 -0
  47. package/dist/esm/copyVersion.js +58 -0
  48. package/dist/esm/createBranch.js +57 -0
  49. package/dist/esm/deleteBranch.js +87 -0
  50. package/dist/esm/deleteNamespace.js +35 -0
  51. package/dist/esm/download.js +374 -0
  52. package/{filterNamespaces.js → dist/esm/filterNamespaces.js} +4 -4
  53. package/dist/esm/format.js +154 -0
  54. package/{formats.js → dist/esm/formats.js} +7 -11
  55. package/dist/esm/get.js +64 -0
  56. package/dist/esm/getBranches.js +35 -0
  57. package/dist/esm/getJob.js +35 -0
  58. package/dist/esm/getProjectStats.js +35 -0
  59. package/dist/esm/getRemoteLanguages.js +36 -0
  60. package/dist/esm/getRemoteNamespace.js +123 -0
  61. package/dist/esm/index.js +16 -0
  62. package/dist/esm/isValidUuid.js +4 -0
  63. package/dist/esm/lngs.js +213 -0
  64. package/dist/esm/mapLimit.js +20 -0
  65. package/dist/esm/mergeBranch.js +78 -0
  66. package/dist/esm/migrate.js +237 -0
  67. package/dist/esm/missing.js +160 -0
  68. package/dist/esm/parseLocalLanguage.js +194 -0
  69. package/dist/esm/parseLocalLanguages.js +16 -0
  70. package/dist/esm/parseLocalReference.js +9 -0
  71. package/dist/esm/publishVersion.js +40 -0
  72. package/{removeUndefinedFromArrays.js → dist/esm/removeUndefinedFromArrays.js} +5 -5
  73. package/dist/esm/removeVersion.js +40 -0
  74. package/dist/esm/request.js +64 -0
  75. package/{shouldUnflatten.js → dist/esm/shouldUnflatten.js} +7 -7
  76. package/dist/esm/sortFlatResources.js +11 -0
  77. package/dist/esm/sync.js +770 -0
  78. package/{unflatten.js → dist/esm/unflatten.js} +36 -34
  79. package/package.json +39 -18
  80. package/rollup.config.js +57 -0
  81. package/add.js +0 -105
  82. package/convertToDesiredFormat.js +0 -268
  83. package/convertToFlatFormat.js +0 -322
  84. package/copyVersion.js +0 -69
  85. package/createBranch.js +0 -61
  86. package/deleteBranch.js +0 -97
  87. package/deleteNamespace.js +0 -39
  88. package/download.js +0 -516
  89. package/format.js +0 -206
  90. package/get.js +0 -81
  91. package/getBranches.js +0 -40
  92. package/getJob.js +0 -40
  93. package/getProjectStats.js +0 -40
  94. package/getRemoteLanguages.js +0 -40
  95. package/getRemoteNamespace.js +0 -122
  96. package/index.js +0 -9
  97. package/isValidUuid.js +0 -2
  98. package/lngs.json +0 -211
  99. package/mergeBranch.js +0 -102
  100. package/migrate.js +0 -314
  101. package/missing.js +0 -169
  102. package/parseLocalLanguages.js +0 -22
  103. package/parseLocalReference.js +0 -10
  104. package/publishVersion.js +0 -64
  105. package/removeVersion.js +0 -64
  106. package/request.js +0 -64
  107. package/sortFlatResources.js +0 -9
  108. package/sync.js +0 -786
@@ -0,0 +1,213 @@
1
+ const lngCodes = [
2
+ 'dev',
3
+ 'ach',
4
+ 'arn',
5
+ 'ast',
6
+ 'cgg',
7
+ 'csb',
8
+ 'dz',
9
+ 'es_ar',
10
+ 'es-ar',
11
+ 'fil',
12
+ 'fur',
13
+ 'gun',
14
+ 'jbo',
15
+ 'mai',
16
+ 'mfe',
17
+ 'mnk',
18
+ 'nah',
19
+ 'nap',
20
+ 'nso',
21
+ 'pap',
22
+ 'pms',
23
+ 'pt_br',
24
+ 'pt-br',
25
+ 'sah',
26
+ 'sco',
27
+ 'son',
28
+ 'ab',
29
+ 'aa',
30
+ 'af',
31
+ 'ak',
32
+ 'sq',
33
+ 'am',
34
+ 'ar',
35
+ 'an',
36
+ 'hy',
37
+ 'as',
38
+ 'av',
39
+ 'ae',
40
+ 'ay',
41
+ 'az',
42
+ 'bm',
43
+ 'ba',
44
+ 'eu',
45
+ 'be',
46
+ 'bn',
47
+ 'bh',
48
+ 'bi',
49
+ 'bs',
50
+ 'br',
51
+ 'bg',
52
+ 'my',
53
+ 'ca',
54
+ 'ch',
55
+ 'ce',
56
+ 'ny',
57
+ 'zh',
58
+ 'cv',
59
+ 'kw',
60
+ 'co',
61
+ 'cr',
62
+ 'hr',
63
+ 'cs',
64
+ 'da',
65
+ 'dv',
66
+ 'nl',
67
+ 'en',
68
+ 'eo',
69
+ 'et',
70
+ 'ee',
71
+ 'fo',
72
+ 'fj',
73
+ 'fi',
74
+ 'fr',
75
+ 'ff',
76
+ 'gl',
77
+ 'ka',
78
+ 'de',
79
+ 'el',
80
+ 'gn',
81
+ 'gu',
82
+ 'ht',
83
+ 'ha',
84
+ 'he',
85
+ 'hz',
86
+ 'hi',
87
+ 'ho',
88
+ 'hu',
89
+ 'ia',
90
+ 'id',
91
+ 'ie',
92
+ 'ga',
93
+ 'ig',
94
+ 'ik',
95
+ 'io',
96
+ 'is',
97
+ 'it',
98
+ 'iu',
99
+ 'ja',
100
+ 'jv',
101
+ 'kl',
102
+ 'kn',
103
+ 'kr',
104
+ 'ks',
105
+ 'kk',
106
+ 'km',
107
+ 'ki',
108
+ 'rw',
109
+ 'ky',
110
+ 'kv',
111
+ 'kg',
112
+ 'ko',
113
+ 'ku',
114
+ 'ckb',
115
+ 'kj',
116
+ 'la',
117
+ 'lb',
118
+ 'lg',
119
+ 'li',
120
+ 'ln',
121
+ 'lo',
122
+ 'lt',
123
+ 'lu',
124
+ 'lv',
125
+ 'gv',
126
+ 'mk',
127
+ 'mg',
128
+ 'ms',
129
+ 'ml',
130
+ 'mt',
131
+ 'mi',
132
+ 'mr',
133
+ 'mh',
134
+ 'mn',
135
+ 'na',
136
+ 'nv',
137
+ 'nb',
138
+ 'nd',
139
+ 'ne',
140
+ 'ng',
141
+ 'nn',
142
+ 'no',
143
+ 'ii',
144
+ 'nr',
145
+ 'oc',
146
+ 'oj',
147
+ 'cu',
148
+ 'om',
149
+ 'or',
150
+ 'os',
151
+ 'pa',
152
+ 'pi',
153
+ 'fa',
154
+ 'pl',
155
+ 'ps',
156
+ 'pt',
157
+ 'qu',
158
+ 'rm',
159
+ 'rn',
160
+ 'ro',
161
+ 'ru',
162
+ 'sa',
163
+ 'sc',
164
+ 'sd',
165
+ 'se',
166
+ 'sm',
167
+ 'sg',
168
+ 'sr',
169
+ 'gd',
170
+ 'sn',
171
+ 'si',
172
+ 'sk',
173
+ 'sl',
174
+ 'so',
175
+ 'st',
176
+ 'es',
177
+ 'su',
178
+ 'sw',
179
+ 'ss',
180
+ 'sv',
181
+ 'ta',
182
+ 'te',
183
+ 'tg',
184
+ 'th',
185
+ 'ti',
186
+ 'bo',
187
+ 'tk',
188
+ 'tl',
189
+ 'tn',
190
+ 'to',
191
+ 'tr',
192
+ 'ts',
193
+ 'tt',
194
+ 'tw',
195
+ 'ty',
196
+ 'ug',
197
+ 'uk',
198
+ 'ur',
199
+ 'uz',
200
+ 've',
201
+ 'vi',
202
+ 'vo',
203
+ 'wa',
204
+ 'cy',
205
+ 'wo',
206
+ 'fy',
207
+ 'xh',
208
+ 'yi',
209
+ 'yo',
210
+ 'za'
211
+ ];
212
+
213
+ export { lngCodes as default };
@@ -0,0 +1,20 @@
1
+ async function mapLimit (arr, limit, asyncFn) {
2
+ const ret = [];
3
+ let i = 0;
4
+ let active = 0;
5
+ return new Promise((resolve, reject) => {
6
+ function next () {
7
+ if (i === arr.length && active === 0) return resolve(ret)
8
+ while (active < limit && i < arr.length) {
9
+ const cur = i++;
10
+ active++;
11
+ Promise.resolve(asyncFn(arr[cur], cur, arr))
12
+ .then((res) => { ret[cur] = res; active--; next(); })
13
+ .catch(reject);
14
+ }
15
+ }
16
+ next();
17
+ })
18
+ }
19
+
20
+ export { mapLimit as default };
@@ -0,0 +1,78 @@
1
+ import colors from 'colors';
2
+ import request from './request.js';
3
+ import getBranches from './getBranches.js';
4
+ import isValidUuid from './isValidUuid.js';
5
+ import getJob from './getJob.js';
6
+
7
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
8
+
9
+ const merge = async (opt) => {
10
+ const queryParams = new URLSearchParams();
11
+ if (opt.delete) {
12
+ queryParams.append('delete', 'true');
13
+ }
14
+ const queryString = queryParams.size > 0 ? '?' + queryParams.toString() : '';
15
+ const { res, obj, err } = await request(opt.apiEndpoint + '/branch/merge/' + opt.branch + queryString, {
16
+ method: 'post',
17
+ headers: {
18
+ Authorization: opt.apiKey
19
+ }
20
+ });
21
+ if (err || (obj && (obj.errorMessage || obj.message))) {
22
+ console.log(colors.red('merging branch failed...'));
23
+ if (err) {
24
+ console.error(colors.red(err.message));
25
+ throw err
26
+ }
27
+ if (obj && (obj.errorMessage || obj.message)) {
28
+ console.error(colors.red((obj.errorMessage || obj.message)));
29
+ throw new Error((obj.errorMessage || obj.message))
30
+ }
31
+ }
32
+ if (res.status === 404) {
33
+ console.error(colors.yellow(res.statusText + ' (' + res.status + ')'));
34
+ return null
35
+ }
36
+ if (res.status >= 300) {
37
+ console.error(colors.red(res.statusText + ' (' + res.status + ')'));
38
+ throw new Error(res.statusText + ' (' + res.status + ')')
39
+ }
40
+ if (!obj || !obj.jobId) {
41
+ console.error(colors.red('No jobId! Something went wrong!'));
42
+ throw new Error('No jobId! Something went wrong!')
43
+ }
44
+ let job;
45
+ while (true) {
46
+ job = await getJob({
47
+ apiEndpoint: opt.apiEndpoint,
48
+ apiKey: opt.apiKey,
49
+ projectId: opt.branch
50
+ }, obj.jobId);
51
+ if (job && !job.timeouted) {
52
+ await sleep(2000);
53
+ continue
54
+ }
55
+ if (job && job.timeouted) {
56
+ console.error(colors.red('Job timeouted!'));
57
+ throw new Error('Job timeouted!')
58
+ }
59
+ break
60
+ }
61
+ console.log(colors.green('merging branch successful'));
62
+ };
63
+
64
+ // handleError removed (unused)
65
+
66
+ const mergeBranch = async (opt) => {
67
+ const branches = await getBranches(opt);
68
+ let b;
69
+ if (isValidUuid(opt.branch)) b = branches.find((br) => br.id === opt.branch);
70
+ if (!b) b = branches.find((br) => br.name === opt.branch);
71
+ if (!b) {
72
+ throw new Error(`Branch ${opt.branch} not found!`)
73
+ }
74
+ opt.branch = b.id;
75
+ await merge(opt);
76
+ };
77
+
78
+ export { mergeBranch as default };
@@ -0,0 +1,237 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import flatten from 'flat';
4
+ import colors from 'colors';
5
+ import request from './request.js';
6
+ import getRemoteLanguages from './getRemoteLanguages.js';
7
+ import os from 'node:os';
8
+ import mapLimit from './mapLimit.js';
9
+
10
+ const getDirectories = (srcpath) => {
11
+ return fs.readdirSync(srcpath).filter(function (file) {
12
+ return fs.statSync(path.join(srcpath, file)).isDirectory()
13
+ })
14
+ };
15
+
16
+ const getFiles = (srcpath) => {
17
+ return fs.readdirSync(srcpath).filter(function (file) {
18
+ return !fs.statSync(path.join(srcpath, file)).isDirectory()
19
+ })
20
+ };
21
+
22
+ const load = async (namespaces) => {
23
+ await Promise.all(namespaces.map(async (ns) => {
24
+ try {
25
+ const data = await fs.promises.readFile(ns.path, 'utf8');
26
+ ns.value = flatten(JSON.parse(data));
27
+ } catch (err) {
28
+ console.error(colors.red(err.stack));
29
+ ns.value = {};
30
+ }
31
+ }));
32
+ return namespaces
33
+ };
34
+
35
+ const parseLanguage = async (p) => {
36
+ const dirs = getDirectories(p);
37
+ const namespaces = [];
38
+ dirs.forEach((lng) => {
39
+ const files = getFiles(path.join(p, lng));
40
+ files.forEach((file) => {
41
+ if (path.extname(file) !== '.json') return
42
+ namespaces.push({
43
+ language: lng,
44
+ namespace: path.basename(file, '.json'),
45
+ path: path.join(p, lng, file)
46
+ });
47
+ });
48
+ });
49
+ return await load(namespaces)
50
+ };
51
+
52
+ const transfer = async (opt, ns) => {
53
+ let url = `${opt.apiEndpoint}/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}`
54
+ .replace('{{projectId}}', opt.projectId)
55
+ .replace('{{ver}}', opt.version)
56
+ .replace('{{version}}', opt.version)
57
+ .replace('{{language}}', ns.language)
58
+ .replace('{{lng}}', ns.language)
59
+ .replace('{{ns}}', ns.namespace)
60
+ .replace('{{namespace}}', ns.namespace);
61
+
62
+ console.log(colors.yellow(`transfering ${opt.version}/${ns.language}/${ns.namespace}...`));
63
+
64
+ if (!opt.replace) url = url.replace('/update/', '/missing/');
65
+
66
+ const data = ns.value;
67
+ const keysToSend = Object.keys(data).length;
68
+ if (keysToSend === 0) return
69
+
70
+ const payloadKeysLimit = 1000;
71
+
72
+ async function send (d, so, isFirst, isRetrying = false) {
73
+ const queryParams = new URLSearchParams();
74
+ if (so) {
75
+ queryParams.append('omitstatsgeneration', 'true');
76
+ }
77
+ if (isFirst && opt.replace) {
78
+ queryParams.append('replace', 'true');
79
+ }
80
+ const queryString = queryParams.size > 0 ? '?' + queryParams.toString() : '';
81
+ try {
82
+ const { res, obj } = await request(url + queryString, {
83
+ method: 'post',
84
+ body: d,
85
+ headers: {
86
+ Authorization: opt.apiKey
87
+ }
88
+ });
89
+ if (url.indexOf('/missing/') > -1 && res.status === 412) {
90
+ console.log(colors.green(`transfered ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace} (but all keys already existed)...`));
91
+ return
92
+ }
93
+ if (res.status === 504 && !isRetrying) {
94
+ await new Promise(resolve => setTimeout(resolve, 3000));
95
+ return send(d, so, isFirst, true)
96
+ }
97
+ if (res.status >= 300 && res.status !== 412) {
98
+ if (obj && (obj.errorMessage || obj.message)) {
99
+ throw new Error((obj.errorMessage || obj.message))
100
+ }
101
+ throw new Error(res.statusText + ' (' + res.status + ')')
102
+ }
103
+ console.log(colors.green(`transfered ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace}...`));
104
+ } catch (err) {
105
+ console.log(colors.red(`transfer failed for ${Object.keys(d).length} keys ${opt.version}/${ns.language}/${ns.namespace}...`));
106
+ throw err
107
+ }
108
+ }
109
+
110
+ if (keysToSend > payloadKeysLimit) {
111
+ const keysInObj = Object.keys(data);
112
+ let isFirst = true;
113
+ while (keysInObj.length > payloadKeysLimit) {
114
+ const pagedData = {};
115
+ keysInObj.splice(0, payloadKeysLimit).forEach((k) => { pagedData[k] = data[k]; });
116
+ const hasMoreKeys = keysInObj.length > 0;
117
+ await send(pagedData, hasMoreKeys, isFirst);
118
+ isFirst = false;
119
+ }
120
+ if (keysInObj.length === 0) return
121
+ const finalPagedData = {};
122
+ keysInObj.splice(0, keysInObj.length).forEach((k) => { finalPagedData[k] = data[k]; });
123
+ await send(finalPagedData, false, isFirst);
124
+ return
125
+ }
126
+
127
+ await send(data, false, true);
128
+ };
129
+
130
+ const upload = async (opt, nss) => {
131
+ const concurrency = os.cpus().length;
132
+ if (!opt.referenceLanguage) {
133
+ await mapLimit(nss, concurrency, async (ns) => transfer(opt, ns));
134
+ return
135
+ }
136
+
137
+ const nssRefLng = nss.filter((n) => n.language === opt.referenceLanguage);
138
+ const nssNonRefLng = nss.filter((n) => n.language !== opt.referenceLanguage);
139
+
140
+ // Reference language first, then others, but each group in parallel
141
+ await mapLimit(nssRefLng, concurrency, async (ns) => transfer(opt, ns));
142
+ await mapLimit(nssNonRefLng, concurrency, async (ns) => transfer(opt, ns));
143
+ };
144
+
145
+ const addLanguage = async (opt, l) => {
146
+ const url = opt.apiEndpoint + '/language/' + opt.projectId + '/' + l;
147
+ try {
148
+ const { res } = await request(url, {
149
+ method: 'post',
150
+ headers: {
151
+ Authorization: opt.apiKey
152
+ }
153
+ });
154
+ if (res.status >= 300 && res.status !== 412) throw new Error(res.statusText + ' (' + res.status + ')')
155
+ console.log(colors.green(`added language ${l}...`));
156
+ } catch (err) {
157
+ console.log(colors.red(`failed to add language ${l}...`));
158
+ throw err
159
+ }
160
+ };
161
+
162
+ const migrate = async (opt) => {
163
+ if (opt.format !== 'json') {
164
+ throw new Error(`Format ${opt.format} is not accepted!`)
165
+ }
166
+
167
+ opt.apiEndpoint = opt.apiEndpoint || 'https://api.locize.app';
168
+
169
+ if (opt.language) {
170
+ const files = getFiles(opt.path);
171
+ const namespaces = files.map((file) => ({
172
+ language: opt.language,
173
+ namespace: path.basename(file, '.json'),
174
+ path: path.join(opt.path, file)
175
+ }));
176
+ let nss;
177
+ try {
178
+ nss = await load(namespaces);
179
+ } catch (err) {
180
+ console.error(colors.red(err.stack));
181
+ process.exit(1);
182
+ }
183
+ try {
184
+ await upload(opt, nss);
185
+ console.log(colors.green('FINISHED'));
186
+ } catch (err) {
187
+ console.error(colors.red(err.stack));
188
+ process.exit(1);
189
+ }
190
+ return
191
+ }
192
+
193
+ if (opt.parseLanguage) {
194
+ let nss;
195
+ try {
196
+ nss = await parseLanguage(opt.path);
197
+ } catch (err) {
198
+ console.error(colors.red(err.stack));
199
+ process.exit(1);
200
+ }
201
+ let remoteLanguages;
202
+ try {
203
+ remoteLanguages = await getRemoteLanguages(opt);
204
+ } catch (err) {
205
+ console.error(colors.red(err.stack));
206
+ process.exit(1);
207
+ }
208
+ const localLanguages = [];
209
+ nss.forEach((n) => {
210
+ if (localLanguages.indexOf(n.language) < 0) localLanguages.push(n.language);
211
+ });
212
+ const notExistingLanguages = localLanguages.filter((l) => remoteLanguages.indexOf(l) < 0);
213
+ if (notExistingLanguages.length === 0) {
214
+ try {
215
+ await upload(opt, nss);
216
+ console.log(colors.green('FINISHED'));
217
+ } catch (err) {
218
+ console.error(colors.red(err.stack));
219
+ process.exit(1);
220
+ }
221
+ return
222
+ }
223
+ try {
224
+ for (const l of notExistingLanguages) {
225
+ await addLanguage(opt, l);
226
+ }
227
+ await new Promise(resolve => setTimeout(resolve, 5000));
228
+ await upload(opt, nss);
229
+ console.log(colors.green('FINISHED'));
230
+ } catch (err) {
231
+ console.error(colors.red(err.stack));
232
+ process.exit(1);
233
+ }
234
+ }
235
+ };
236
+
237
+ export { migrate as default };
@@ -0,0 +1,160 @@
1
+ import colors from 'colors';
2
+ import path from 'node:path';
3
+ import request from './request.js';
4
+ import { reversedFileExtensionsMap as reversedFileExtensionsMap$1 } from './formats.js';
5
+ import getRemoteLanguages from './getRemoteLanguages.js';
6
+ import parseLocalReference from './parseLocalReference.js';
7
+ import parseLocalLanguages from './parseLocalLanguages.js';
8
+ import getRemoteNamespace from './getRemoteNamespace.js';
9
+ import os from 'node:os';
10
+ import mapLimit from './mapLimit.js';
11
+
12
+ const reversedFileExtensionsMap = reversedFileExtensionsMap$1;
13
+
14
+ const compareNamespace = (local, remote) => {
15
+ const diff = {
16
+ toAdd: []
17
+ };
18
+ local = local || {};
19
+ remote = remote || {};
20
+ Object.keys(local).forEach((k) => {
21
+ if (remote[k] === '' && local[k] === '') return
22
+ if (!remote[k]) {
23
+ diff.toAdd.push(k);
24
+ }
25
+ });
26
+ return diff
27
+ };
28
+
29
+ const compareNamespaces = async (opt, localNamespaces) => {
30
+ return await Promise.all(localNamespaces.map(async (ns) => {
31
+ const { result: remoteNamespace } = await getRemoteNamespace(opt, ns.language, ns.namespace);
32
+ const diff = compareNamespace(ns.content, remoteNamespace);
33
+ ns.diff = diff;
34
+ ns.remoteContent = remoteNamespace;
35
+ return ns
36
+ }))
37
+ };
38
+
39
+ const saveMissing = async (opt, lng, ns) => {
40
+ const data = {};
41
+ ns.diff.toAdd.forEach((k) => { data[k] = ns.content[k]; });
42
+ if (Object.keys(data).length === 0 || opt.dry) return
43
+ const payloadKeysLimit = 1000;
44
+ async function send (d, isRetrying = false) {
45
+ const { res, obj } = await request(opt.apiEndpoint + '/missing/' + opt.projectId + '/' + opt.version + '/' + lng + '/' + ns.namespace, {
46
+ method: 'post',
47
+ body: d,
48
+ headers: {
49
+ Authorization: opt.apiKey
50
+ }
51
+ });
52
+ if (res.status === 504 && !isRetrying) {
53
+ await new Promise(resolve => setTimeout(resolve, 3000));
54
+ return send(d, true)
55
+ }
56
+ if (res.status >= 300 && res.status !== 412) {
57
+ if (obj && (obj.errorMessage || obj.message)) {
58
+ throw new Error((obj.errorMessage || obj.message))
59
+ }
60
+ throw new Error(res.statusText + ' (' + res.status + ')')
61
+ }
62
+ await new Promise(resolve => setTimeout(resolve, 1000));
63
+ }
64
+ if (Object.keys(data).length > payloadKeysLimit) {
65
+ const keysInObj = Object.keys(data);
66
+ while (keysInObj.length > payloadKeysLimit) {
67
+ const pagedData = {};
68
+ keysInObj.splice(0, payloadKeysLimit).forEach((k) => { pagedData[k] = data[k]; });
69
+ await send(pagedData);
70
+ }
71
+ if (keysInObj.length === 0) return
72
+ const finalPagedData = {};
73
+ keysInObj.splice(0, keysInObj.length).forEach((k) => { finalPagedData[k] = data[k]; });
74
+ await send(finalPagedData);
75
+ return
76
+ }
77
+ await send(data);
78
+ };
79
+
80
+ const handleError = (err) => {
81
+ if (err) {
82
+ console.error(colors.red(err.stack));
83
+ process.exit(1);
84
+ }
85
+ };
86
+
87
+ const handleMissing = async (opt, localNamespaces) => {
88
+ if (!localNamespaces || localNamespaces.length === 0) {
89
+ handleError(new Error('No local namespaces found!'));
90
+ return
91
+ }
92
+ let compared;
93
+ try {
94
+ compared = await compareNamespaces(opt, localNamespaces);
95
+ } catch (err) {
96
+ handleError(err);
97
+ return
98
+ }
99
+ const concurrency = os.cpus().length;
100
+ await mapLimit(compared, concurrency, async (ns) => {
101
+ if (ns.diff.toAdd.length > 0) {
102
+ console.log(colors.green(`adding ${ns.diff.toAdd.length} keys in ${ns.language}/${ns.namespace}...`));
103
+ if (opt.dry) console.log(colors.green(`would add ${ns.diff.toAdd.join(', ')} in ${ns.language}/${ns.namespace}...`));
104
+ }
105
+ try {
106
+ await saveMissing(opt, ns.language, ns);
107
+ } catch (err) {
108
+ handleError(err);
109
+ // Don't return here, continue with others
110
+ }
111
+ });
112
+ console.log(colors.green('FINISHED'));
113
+ };
114
+
115
+ const missing = async (opt) => {
116
+ if (!reversedFileExtensionsMap[opt.format]) {
117
+ handleError(new Error(`${opt.format} is not a valid format!`));
118
+ return
119
+ }
120
+ if (opt.namespace && opt.namespace.indexOf(',') > 0) {
121
+ opt.namespaces = opt.namespace.split(',');
122
+ delete opt.namespace;
123
+ }
124
+ opt.pathMaskInterpolationPrefix = opt.pathMaskInterpolationPrefix || '{{';
125
+ opt.pathMaskInterpolationSuffix = opt.pathMaskInterpolationSuffix || '}}';
126
+ opt.pathMask = opt.pathMask || `${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}${path.sep}${opt.pathMaskInterpolationPrefix}namespace${opt.pathMaskInterpolationSuffix}`;
127
+ opt.languageFolderPrefix = opt.languageFolderPrefix || '';
128
+ opt.pathMask = opt.pathMask.replace(`${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`, `${opt.languageFolderPrefix}${opt.pathMaskInterpolationPrefix}language${opt.pathMaskInterpolationSuffix}`);
129
+ let remoteLanguages;
130
+ try {
131
+ remoteLanguages = await getRemoteLanguages(opt);
132
+ } catch (err) {
133
+ handleError(err);
134
+ return
135
+ }
136
+ if (opt.referenceLanguageOnly && opt.language && opt.referenceLanguage !== opt.language) {
137
+ opt.referenceLanguage = opt.language;
138
+ }
139
+ if (opt.referenceLanguageOnly) {
140
+ let localNamespaces;
141
+ try {
142
+ localNamespaces = await parseLocalReference(opt);
143
+ } catch (err) {
144
+ handleError(err);
145
+ return
146
+ }
147
+ await handleMissing(opt, localNamespaces);
148
+ return
149
+ }
150
+ let localNamespaces;
151
+ try {
152
+ localNamespaces = await parseLocalLanguages(opt, remoteLanguages);
153
+ } catch (err) {
154
+ handleError(err);
155
+ return
156
+ }
157
+ await handleMissing(opt, localNamespaces);
158
+ };
159
+
160
+ export { missing as default };