@tolgee/cli 2.13.0 → 2.14.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.
@@ -1,5 +1,7 @@
1
+ import { getUnresolvedConflictsMessage } from '../utils/printFailedKeys.js';
1
2
  export const addErrorDetails = (loadable, showBeError = true) => {
2
- var _a, _b, _c;
3
+ var _a, _b, _c, _d, _e;
4
+ let additionalInfo = '';
3
5
  const items = [];
4
6
  items.push(`status: ${loadable.response.status}`);
5
7
  if (showBeError && ((_a = loadable.error) === null || _a === void 0 ? void 0 : _a.code)) {
@@ -8,7 +10,11 @@ export const addErrorDetails = (loadable, showBeError = true) => {
8
10
  if (loadable.response.status === 403 && ((_c = (_b = loadable.error) === null || _b === void 0 ? void 0 : _b.params) === null || _c === void 0 ? void 0 : _c[0])) {
9
11
  items.push(`missing scope: ${loadable.error.params[0]}`);
10
12
  }
11
- return `[${items.join(', ')}]`;
13
+ if (((_d = loadable.error) === null || _d === void 0 ? void 0 : _d.code) === 'conflict_is_not_resolved' &&
14
+ typeof ((_e = loadable.error.params) === null || _e === void 0 ? void 0 : _e[0]) === 'object') {
15
+ additionalInfo += getUnresolvedConflictsMessage(loadable.error.params, true);
16
+ }
17
+ return `[${items.join(', ')}]${additionalInfo ? '\n' + additionalInfo : ''}`;
12
18
  };
13
19
  export const errorFromLoadable = (loadable) => {
14
20
  switch (loadable.response.status) {
@@ -30,6 +30,7 @@ function fetchZipBlob(opts) {
30
30
  filterTagIn: opts.tags,
31
31
  filterTagNotIn: opts.excludeTags,
32
32
  fileStructureTemplate: opts.fileStructureTemplate,
33
+ escapeHtml: false,
33
34
  });
34
35
  handleLoadableError(loadable);
35
36
  return loadable.data;
@@ -18,6 +18,7 @@ import { mapImportFormat } from '../utils/mapImportFormat.js';
18
18
  import { handleLoadableError } from '../client/TolgeeClient.js';
19
19
  import { findFilesByTemplate } from '../utils/filesTemplate.js';
20
20
  import { valueToArray } from '../utils/valueToArray.js';
21
+ import { printUnresolvedConflicts } from '../utils/printFailedKeys.js';
21
22
  function allInPattern(pattern) {
22
23
  return __awaiter(this, void 0, void 0, function* () {
23
24
  const files = [];
@@ -100,7 +101,7 @@ function handleMappingError(fileMappings) {
100
101
  }
101
102
  const pushHandler = (config) => function () {
102
103
  return __awaiter(this, void 0, void 0, function* () {
103
- var _a, _b, _c, _d;
104
+ var _a, _b, _c, _d, _e, _f, _g;
104
105
  const opts = this.optsWithGlobals();
105
106
  let allMatchers = [];
106
107
  const filesTemplate = opts.filesTemplate;
@@ -130,12 +131,25 @@ const pushHandler = (config) => function () {
130
131
  error('Nothing to import.');
131
132
  return;
132
133
  }
134
+ let errorOnUnresolvedConflict;
135
+ switch (opts.errorOnUnresolvedConflict) {
136
+ case 'auto':
137
+ errorOnUnresolvedConflict = undefined;
138
+ break;
139
+ case 'yes':
140
+ errorOnUnresolvedConflict = true;
141
+ break;
142
+ case 'no':
143
+ errorOnUnresolvedConflict = false;
144
+ break;
145
+ }
133
146
  const params = {
134
147
  createNewKeys: true,
135
148
  forceMode: opts.forceMode,
136
149
  overrideKeyDescriptions: opts.overrideKeyDescriptions,
137
150
  convertPlaceholdersToIcu: opts.convertPlaceholdersToIcu,
138
151
  tagNewKeys: (_d = opts.tagNewKeys) !== null && _d !== void 0 ? _d : [],
152
+ overrideMode: (_e = opts.overrideMode) !== null && _e !== void 0 ? _e : 'RECOMMENDED',
139
153
  fileMappings: files.map((f) => {
140
154
  var _a;
141
155
  const format = mapImportFormat(opts.format, extname(f.name));
@@ -153,30 +167,34 @@ const pushHandler = (config) => function () {
153
167
  };
154
168
  }),
155
169
  removeOtherKeys: opts.removeOtherKeys,
170
+ errorOnUnresolvedConflict: errorOnUnresolvedConflict,
156
171
  };
157
- const attempt1 = yield loading('Importing...', importData(opts.client, {
172
+ let attempt = yield loading('Importing...', importData(opts.client, {
158
173
  files,
159
174
  params,
160
175
  }));
161
- if (attempt1.error) {
162
- if (attempt1.error.code === 'existing_language_not_selected') {
176
+ if (attempt.error) {
177
+ if (attempt.error.code === 'existing_language_not_selected') {
163
178
  handleMappingError(params.fileMappings);
164
179
  }
165
- if (attempt1.error.code !== 'conflict_is_not_resolved') {
166
- handleLoadableError(attempt1);
180
+ if (attempt.error.code !== 'conflict_is_not_resolved') {
181
+ handleLoadableError(attempt);
167
182
  }
168
183
  const forceMode = yield promptConflicts(opts);
169
- const attempt2 = yield loading('Overriding...', importData(opts.client, {
184
+ attempt = yield loading('Overriding...', importData(opts.client, {
170
185
  files,
171
186
  params: Object.assign(Object.assign({}, params), { forceMode }),
172
187
  }));
173
- handleLoadableError(attempt2);
188
+ handleLoadableError(attempt);
189
+ }
190
+ if ((_g = (_f = attempt.data) === null || _f === void 0 ? void 0 : _f.unresolvedConflicts) === null || _g === void 0 ? void 0 : _g.length) {
191
+ printUnresolvedConflicts(attempt.data.unresolvedConflicts, false);
174
192
  }
175
193
  success('Done!');
176
194
  });
177
195
  };
178
196
  export default (config) => {
179
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
197
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
180
198
  return new Command()
181
199
  .name('push')
182
200
  .description('Pushes translations to Tolgee')
@@ -191,5 +209,11 @@ export default (config) => {
191
209
  .addOption(new Option('-n, --namespaces <namespaces...>', 'Specifies which namespaces should be pushed (see push.files in config).').default((_h = config.push) === null || _h === void 0 ? void 0 : _h.namespaces))
192
210
  .addOption(new Option('--tag-new-keys <tags...>', 'Specify tags that will be added to newly created keys.').default((_j = config.push) === null || _j === void 0 ? void 0 : _j.tagNewKeys))
193
211
  .addOption(new Option('--remove-other-keys', 'Remove keys which are not present in the import (within imported namespaces).').default((_k = config.push) === null || _k === void 0 ? void 0 : _k.removeOtherKeys))
212
+ .addOption(new Option('--override-mode <mode>', 'Specifies what is considered non-overridable translation.')
213
+ .choices(['RECOMMENDED', 'ALL'])
214
+ .default((_l = config.push) === null || _l === void 0 ? void 0 : _l.overrideMode))
215
+ .addOption(new Option('--error-on-unresolved-conflict <choice>', 'Fail the whole import if there are unresolved conflicts.')
216
+ .choices(['yes', 'no', 'auto'])
217
+ .default((_o = (_m = config.push) === null || _m === void 0 ? void 0 : _m.errorOnUnresolvedConflict) !== null && _o !== void 0 ? _o : 'auto'))
194
218
  .action(pushHandler(config));
195
219
  };
@@ -24,6 +24,7 @@ function backup(client, dest) {
24
24
  supportArrays: false,
25
25
  filterState: ['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'],
26
26
  structureDelimiter: '',
27
+ escapeHtml: false,
27
28
  });
28
29
  handleLoadableError(loadable);
29
30
  const blob = loadable.data;
@@ -5,15 +5,20 @@ import ansi from 'ansi-colors';
5
5
  * @param key The key to print.
6
6
  * @param deletion True if the key is about to be deleted.
7
7
  */
8
- export function printKey(key, deletion) {
8
+ export function printKey(key, deletion, color, note) {
9
+ const colorFunc = color !== null && color !== void 0 ? color : (deletion ? ansi.red : ansi.green);
9
10
  const namespace = key.namespace
10
11
  ? ` ${ansi.italic(`(namespace: ${key.namespace})`)}`
11
12
  : '';
12
- if (deletion) {
13
- console.log(`${ansi.red(`- ${key.keyName}`)}${namespace}`);
13
+ const renderedNote = note ? ` ${note}` : '';
14
+ if (deletion === undefined) {
15
+ console.log(`${colorFunc(`${key.keyName}`)}${namespace}${renderedNote}`);
16
+ }
17
+ else if (deletion) {
18
+ console.log(`${colorFunc(`- ${key.keyName}`)}${namespace}${renderedNote}`);
14
19
  }
15
20
  else {
16
- console.log(`${ansi.green(`+ ${key.keyName}`)}${namespace}`);
21
+ console.log(`${colorFunc(`+ ${key.keyName}`)}${namespace}${renderedNote}`);
17
22
  }
18
23
  }
19
24
  /**
@@ -1,7 +1,9 @@
1
1
  export const getStackTrace = () => {
2
- const obj = {};
3
- Error.captureStackTrace(obj, getStackTrace);
4
- const stack = obj.stack;
5
- const parts = stack.split('\n');
6
- return parts.slice(2).join('\n');
2
+ var _a, _b;
3
+ if (typeof Error.captureStackTrace === 'function') {
4
+ const obj = {};
5
+ Error.captureStackTrace(obj, getStackTrace);
6
+ return (_a = obj.stack) !== null && _a !== void 0 ? _a : '';
7
+ }
8
+ return (_b = new Error().stack) !== null && _b !== void 0 ? _b : '';
7
9
  };
@@ -0,0 +1,26 @@
1
+ import ansi from 'ansi-colors';
2
+ export function renderKey(key, note) {
3
+ const colorFunc = ansi.yellow;
4
+ const namespace = key.namespace
5
+ ? ` ${ansi.italic(`(namespace: ${key.namespace})`)}`
6
+ : '';
7
+ const renderedNote = note ? ` ${note}` : '';
8
+ return `${colorFunc(`${key.keyName}`)}${namespace}${renderedNote}`;
9
+ }
10
+ export function getUnresolvedConflictsMessage(translations, isError) {
11
+ const someOverridable = Boolean(translations.find((c) => c.isOverridable));
12
+ const result = [''];
13
+ result.push(`🟡 Some translations cannot be updated:`);
14
+ translations.forEach((c) => {
15
+ result.push(renderKey({ keyName: c.keyName, namespace: c.keyNamespace }, `${c.language}` + (c.isOverridable ? ' (overridable)' : '')));
16
+ });
17
+ result.push('');
18
+ if (someOverridable) {
19
+ result.push('HINT: Overridable translations can be updated with the `--override-mode ALL`');
20
+ result.push('');
21
+ }
22
+ return result.join('\n');
23
+ }
24
+ export function printUnresolvedConflicts(translations, isError) {
25
+ console.log(getUnresolvedConflictsMessage(translations, isError));
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolgee/cli",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "type": "module",
5
5
  "description": "A tool to interact with the Tolgee Platform through CLI",
6
6
  "repository": {
package/schema.json CHANGED
@@ -90,6 +90,12 @@
90
90
  "removeOtherKeys": {
91
91
  "description": "Remove keys which are not present in the import (within imported namespaces).",
92
92
  "type": "boolean"
93
+ },
94
+ "errorOnUnresolvedConflict": {
95
+ "$ref": "#/$defs/errorOnUnresolvedConflict"
96
+ },
97
+ "overrideMode": {
98
+ "$ref": "#/$defs/overrideMode"
93
99
  }
94
100
  }
95
101
  },
@@ -251,6 +257,16 @@
251
257
  "type": "string",
252
258
  "enum": ["OVERRIDE", "KEEP", "NO_FORCE"]
253
259
  },
260
+ "overrideMode": {
261
+ "description": "Specifies what is considered non-overridable translation: \n - RECOMMENDED - protected reviewed translations are considered as non-overridable\n - ALL - translations that user has permissions for",
262
+ "type": "string",
263
+ "enum": ["ALL", "RECOMMENDED"]
264
+ },
265
+ "errorOnUnresolvedConflict": {
266
+ "description": "Fail the whole import if there are unresolved conflicts in import: \n - yes - fail if any unresolved conflict is present\n no - don't fail and just print unresolved conflicts\n auto - fails when when forceMode=KEEP, otherwise doesn't fail",
267
+ "type": "string",
268
+ "enum": ["yes", "no", "auto"]
269
+ },
254
270
  "path": {
255
271
  "description": "File glob specifying which files to include.",
256
272
  "type": "string"