@tolgee/cli 2.8.4 → 2.10.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 (56) hide show
  1. package/dist/cli.js +116 -94
  2. package/dist/client/ApiClient.js +40 -32
  3. package/dist/client/ExportClient.js +24 -11
  4. package/dist/client/ImportClient.js +25 -16
  5. package/dist/client/TolgeeClient.js +1 -5
  6. package/dist/client/errorFromLoadable.js +3 -2
  7. package/dist/client/getApiKeyInformation.js +16 -6
  8. package/dist/commands/extract/check.js +38 -27
  9. package/dist/commands/extract/print.js +46 -35
  10. package/dist/commands/login.js +39 -26
  11. package/dist/commands/pull.js +60 -43
  12. package/dist/commands/push.js +167 -117
  13. package/dist/commands/sync/compare.js +43 -31
  14. package/dist/commands/sync/sync.js +118 -99
  15. package/dist/commands/sync/syncUtils.js +2 -1
  16. package/dist/commands/tag.js +52 -38
  17. package/dist/config/credentials.js +110 -93
  18. package/dist/config/tolgeerc.js +51 -35
  19. package/dist/extractor/extractor.js +45 -31
  20. package/dist/extractor/parser/extractComment.js +1 -1
  21. package/dist/extractor/parser/generateReport.js +8 -6
  22. package/dist/extractor/parser/iterator.js +2 -1
  23. package/dist/extractor/parser/mergerMachine.js +2 -11
  24. package/dist/extractor/parser/nodeUtils.js +1 -1
  25. package/dist/extractor/parser/parser.js +4 -2
  26. package/dist/extractor/parser/rules/tNsSourceGeneral.js +1 -1
  27. package/dist/extractor/parser/tree/getTranslateProps.js +21 -16
  28. package/dist/extractor/parser/tree/getValue.js +1 -1
  29. package/dist/extractor/parser/tree/parseTag.js +1 -1
  30. package/dist/extractor/parserNgx/ParserNgx.js +1 -3
  31. package/dist/extractor/parserNgx/ngxMapper.js +2 -1
  32. package/dist/extractor/parserNgx/ngxTreeTransform.js +3 -2
  33. package/dist/extractor/parserNgx/rules/translatePipe.js +1 -1
  34. package/dist/extractor/parserReact/ParserReact.js +1 -3
  35. package/dist/extractor/parserSvelte/ParserSvelte.js +1 -3
  36. package/dist/extractor/parserVue/ParserVue.js +1 -3
  37. package/dist/extractor/parserVue/tokenMergers/hyphenPropsMerger.js +1 -4
  38. package/dist/extractor/parserVue/vueTreeTransform.js +13 -2
  39. package/dist/extractor/runner.js +53 -39
  40. package/dist/extractor/tokenizer.js +50 -35
  41. package/dist/extractor/visualizers/printTokens.js +2 -1
  42. package/dist/extractor/visualizers/visualizeRules.js +4 -7
  43. package/dist/extractor/warnings.js +3 -2
  44. package/dist/extractor/worker.js +29 -16
  45. package/dist/options.js +2 -1
  46. package/dist/utils/apiKeyList.js +31 -19
  47. package/dist/utils/ask.js +35 -21
  48. package/dist/utils/checkPathNotAFile.js +22 -11
  49. package/dist/utils/filesTemplate.js +147 -0
  50. package/dist/utils/mapExportFormat.js +2 -0
  51. package/dist/utils/mapImportFormat.js +1 -1
  52. package/dist/utils/moduleLoader.js +37 -23
  53. package/dist/utils/prepareDir.js +20 -9
  54. package/dist/utils/valueToArray.js +8 -0
  55. package/package.json +2 -2
  56. package/schema.json +20 -4
@@ -1,48 +1,59 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { relative } from 'path';
2
11
  import { Command } from 'commander';
3
12
  import { extractKeysOfFiles } from '../../extractor/runner.js';
4
13
  import { WarningMessages } from '../../extractor/warnings.js';
5
14
  import { loading } from '../../utils/logger.js';
6
- const printHandler = (config) => async function () {
7
- const opts = this.optsWithGlobals();
8
- const extracted = await loading('Analyzing code...', extractKeysOfFiles(opts));
9
- let warningCount = 0;
10
- const keySet = new Set();
11
- for (const [file, { keys, warnings }] of extracted) {
12
- if (keys.length) {
13
- const relFile = relative(process.cwd(), file);
14
- console.log('%d key%s found in %s:', keys.length, keys.length !== 1 ? 's' : '', relFile);
15
- for (const key of keys) {
16
- keySet.add(key);
17
- console.log('\tline %d: %s', key.line, key.keyName);
18
- if (key.namespace) {
19
- console.log('\t\tnamespace: %s', key.namespace);
20
- }
21
- if (key.defaultValue) {
22
- console.log('\t\tdefault: %s', key.defaultValue);
15
+ const printHandler = (config) => function () {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ const opts = this.optsWithGlobals();
18
+ const extracted = yield loading('Analyzing code...', extractKeysOfFiles(opts));
19
+ let warningCount = 0;
20
+ const keySet = new Set();
21
+ for (const [file, { keys, warnings }] of extracted) {
22
+ if (keys.length) {
23
+ const relFile = relative(process.cwd(), file);
24
+ console.log('%d key%s found in %s:', keys.length, keys.length !== 1 ? 's' : '', relFile);
25
+ for (const key of keys) {
26
+ keySet.add(key);
27
+ console.log('\tline %d: %s', key.line, key.keyName);
28
+ if (key.namespace) {
29
+ console.log('\t\tnamespace: %s', key.namespace);
30
+ }
31
+ if (key.defaultValue) {
32
+ console.log('\t\tdefault: %s', key.defaultValue);
33
+ }
23
34
  }
24
35
  }
25
- }
26
- if (warnings?.length) {
27
- warningCount += warnings.length;
28
- console.log('%d warning%s %s emitted during extraction:', warnings.length, warnings.length !== 1 ? 's' : '', warnings.length !== 1 ? 'were' : 'was');
29
- for (const warning of warnings) {
30
- if (warning.warning in WarningMessages) {
31
- const warn = warning.warning;
32
- const { name } = WarningMessages[warn];
33
- console.log('\tline %d: %s', warning.line, name);
34
- }
35
- else {
36
- console.log('\tline %d: %s', warning.line, warning.warning);
36
+ if (warnings === null || warnings === void 0 ? void 0 : warnings.length) {
37
+ warningCount += warnings.length;
38
+ console.log('%d warning%s %s emitted during extraction:', warnings.length, warnings.length !== 1 ? 's' : '', warnings.length !== 1 ? 'were' : 'was');
39
+ for (const warning of warnings) {
40
+ if (warning.warning in WarningMessages) {
41
+ const warn = warning.warning;
42
+ const { name } = WarningMessages[warn];
43
+ console.log('\tline %d: %s', warning.line, name);
44
+ }
45
+ else {
46
+ console.log('\tline %d: %s', warning.line, warning.warning);
47
+ }
37
48
  }
38
49
  }
50
+ if (keys.length || (warnings === null || warnings === void 0 ? void 0 : warnings.length)) {
51
+ console.log();
52
+ }
39
53
  }
40
- if (keys.length || warnings?.length) {
41
- console.log();
42
- }
43
- }
44
- console.log('Total unique keys found: %d', keySet.size);
45
- console.log('Total warnings: %d', warningCount);
54
+ console.log('Total unique keys found: %d', keySet.size);
55
+ console.log('Total warnings: %d', warningCount);
56
+ });
46
57
  };
47
58
  export default (config) => new Command('print')
48
59
  .description('Prints extracted data to the console')
@@ -1,36 +1,49 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { Command } from 'commander';
2
11
  import ansi from 'ansi-colors';
3
12
  import { saveApiKey, removeApiKeys, clearAuthStore, } from '../config/credentials.js';
4
13
  import { exitWithError, success } from '../utils/logger.js';
5
14
  import { createTolgeeClient } from '../client/TolgeeClient.js';
6
15
  import { printApiKeyLists } from '../utils/apiKeyList.js';
7
- async function loginHandler(key) {
8
- const opts = this.optsWithGlobals();
9
- if (opts.list) {
10
- printApiKeyLists();
11
- return;
12
- }
13
- else if (!key) {
14
- exitWithError('Missing argument [API Key]');
15
- }
16
- const keyInfo = await createTolgeeClient({
17
- baseUrl: opts.apiUrl.toString(),
18
- apiKey: key,
19
- }).getApiKeyInfo();
20
- await saveApiKey(opts.apiUrl, keyInfo);
21
- success(keyInfo.type === 'PAK'
22
- ? `Logged in as ${keyInfo.username} on ${ansi.blue(opts.apiUrl.hostname)} for project ${ansi.blue(String(keyInfo.project.id))} (${keyInfo.project.name}). Welcome back!`
23
- : `Logged in as ${keyInfo.username} on ${ansi.blue(opts.apiUrl.hostname)}. Welcome back!`);
16
+ function loginHandler(key) {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ const opts = this.optsWithGlobals();
19
+ if (opts.list) {
20
+ printApiKeyLists();
21
+ return;
22
+ }
23
+ else if (!key) {
24
+ exitWithError('Missing argument [API Key]');
25
+ }
26
+ const keyInfo = yield createTolgeeClient({
27
+ baseUrl: opts.apiUrl.toString(),
28
+ apiKey: key,
29
+ }).getApiKeyInfo();
30
+ yield saveApiKey(opts.apiUrl, keyInfo);
31
+ success(keyInfo.type === 'PAK'
32
+ ? `Logged in as ${keyInfo.username} on ${ansi.blue(opts.apiUrl.hostname)} for project ${ansi.blue(String(keyInfo.project.id))} (${keyInfo.project.name}). Welcome back!`
33
+ : `Logged in as ${keyInfo.username} on ${ansi.blue(opts.apiUrl.hostname)}. Welcome back!`);
34
+ });
24
35
  }
25
- async function logoutHandler() {
26
- const opts = this.optsWithGlobals();
27
- if (opts.all) {
28
- await clearAuthStore();
29
- success("You've been logged out of all Tolgee instances you were logged in.");
30
- return;
31
- }
32
- await removeApiKeys(opts.apiUrl);
33
- success(`You're now logged out of ${opts.apiUrl.hostname}.`);
36
+ function logoutHandler() {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ const opts = this.optsWithGlobals();
39
+ if (opts.all) {
40
+ yield clearAuthStore();
41
+ success("You've been logged out of all Tolgee instances you were logged in.");
42
+ return;
43
+ }
44
+ yield removeApiKeys(opts.apiUrl);
45
+ success(`You're now logged out of ${opts.apiUrl.hostname}.`);
46
+ });
34
47
  }
35
48
  export const Login = new Command()
36
49
  .name('login')
@@ -1,3 +1,12 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { Command, Option } from 'commander';
2
11
  import { unzipBuffer } from '../utils/zip.js';
3
12
  import { prepareDir } from '../utils/prepareDir.js';
@@ -5,49 +14,57 @@ import { exitWithError, loading, success } from '../utils/logger.js';
5
14
  import { checkPathNotAFile } from '../utils/checkPathNotAFile.js';
6
15
  import { mapExportFormat } from '../utils/mapExportFormat.js';
7
16
  import { handleLoadableError } from '../client/TolgeeClient.js';
8
- async function fetchZipBlob(opts) {
9
- const exportFormat = mapExportFormat(opts.format);
10
- const { format, messageFormat } = exportFormat;
11
- const loadable = await opts.client.export.export({
12
- format,
13
- messageFormat,
14
- supportArrays: opts.supportArrays,
15
- languages: opts.languages,
16
- filterState: opts.states,
17
- structureDelimiter: opts.delimiter ?? '',
18
- filterNamespace: opts.namespaces,
19
- filterTagIn: opts.tags,
20
- filterTagNotIn: opts.excludeTags,
21
- fileStructureTemplate: opts.fileStructureTemplate,
17
+ function fetchZipBlob(opts) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ var _a;
20
+ const exportFormat = mapExportFormat(opts.format);
21
+ const { format, messageFormat } = exportFormat;
22
+ const loadable = yield opts.client.export.export({
23
+ format,
24
+ messageFormat,
25
+ supportArrays: opts.supportArrays,
26
+ languages: opts.languages,
27
+ filterState: opts.states,
28
+ structureDelimiter: (_a = opts.delimiter) !== null && _a !== void 0 ? _a : '',
29
+ filterNamespace: opts.namespaces,
30
+ filterTagIn: opts.tags,
31
+ filterTagNotIn: opts.excludeTags,
32
+ fileStructureTemplate: opts.fileStructureTemplate,
33
+ });
34
+ handleLoadableError(loadable);
35
+ return loadable.data;
22
36
  });
23
- handleLoadableError(loadable);
24
- return loadable.data;
25
37
  }
26
- const pullHandler = () => async function () {
27
- const opts = this.optsWithGlobals();
28
- if (!opts.path) {
29
- exitWithError('Missing option --path <path> or `pull.path` in tolgee config');
30
- }
31
- await checkPathNotAFile(opts.path);
32
- const zipBlob = await loading('Fetching strings from Tolgee...', fetchZipBlob(opts));
33
- await prepareDir(opts.path, opts.emptyDir);
34
- await loading('Extracting strings...', unzipBuffer(zipBlob, opts.path));
35
- success('Done!');
38
+ const pullHandler = () => function () {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ const opts = this.optsWithGlobals();
41
+ if (!opts.path) {
42
+ exitWithError('Missing option --path <path> or `pull.path` in tolgee config');
43
+ }
44
+ yield checkPathNotAFile(opts.path);
45
+ const zipBlob = yield loading('Fetching strings from Tolgee...', fetchZipBlob(opts));
46
+ yield prepareDir(opts.path, opts.emptyDir);
47
+ yield loading('Extracting strings...', unzipBuffer(zipBlob, opts.path));
48
+ success('Done!');
49
+ });
50
+ };
51
+ export default (config) => {
52
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
53
+ return new Command()
54
+ .name('pull')
55
+ .description('Pulls translations from Tolgee')
56
+ .addOption(new Option('--path <path>', 'Destination of a folder where translation files will be stored in').default((_a = config.pull) === null || _a === void 0 ? void 0 : _a.path))
57
+ .addOption(new Option('-l, --languages <languages...>', 'List of languages to pull. Leave unspecified to export them all').default((_b = config.pull) === null || _b === void 0 ? void 0 : _b.languages))
58
+ .addOption(new Option('-s, --states <states...>', 'List of translation states to include. Defaults all except untranslated')
59
+ .choices(['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'])
60
+ .default((_c = config.pull) === null || _c === void 0 ? void 0 : _c.states)
61
+ .argParser((v, a) => [v.toUpperCase(), ...(a || [])]))
62
+ .addOption(new Option('-d, --delimiter <delimiter>', 'Structure delimiter to use. By default, Tolgee interprets `.` as a nested structure. You can change the delimiter, or disable structure formatting by not specifying any value to the option').default(((_d = config.pull) === null || _d === void 0 ? void 0 : _d.delimiter) === undefined ? '.' : config.pull.delimiter))
63
+ .addOption(new Option('-n, --namespaces <namespaces...>', 'List of namespaces to pull. Defaults to all namespaces').default((_e = config.pull) === null || _e === void 0 ? void 0 : _e.namespaces))
64
+ .addOption(new Option('-t, --tags <tags...>', 'List of tags which to include. Keys tagged by at least one of these tags will be included.').default((_f = config.pull) === null || _f === void 0 ? void 0 : _f.tags))
65
+ .addOption(new Option('--exclude-tags <tags...>', 'List of tags which to exclude. Keys tagged by at least one of these tags will be excluded.').default((_g = config.pull) === null || _g === void 0 ? void 0 : _g.excludeTags))
66
+ .addOption(new Option('--support-arrays', 'Export keys with array syntax (e.g. item[0]) as arrays.').default((_j = (_h = config.pull) === null || _h === void 0 ? void 0 : _h.supportArrays) !== null && _j !== void 0 ? _j : false))
67
+ .addOption(new Option('--empty-dir', 'Empty target directory before inserting pulled files.').default((_k = config.pull) === null || _k === void 0 ? void 0 : _k.emptyDir))
68
+ .addOption(new Option('--file-structure-template <template>', 'Defines exported file structure: https://tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format').default((_l = config.pull) === null || _l === void 0 ? void 0 : _l.fileStructureTemplate))
69
+ .action(pullHandler());
36
70
  };
37
- export default (config) => new Command()
38
- .name('pull')
39
- .description('Pulls translations from Tolgee')
40
- .addOption(new Option('--path <path>', 'Destination of a folder where translation files will be stored in').default(config.pull?.path))
41
- .addOption(new Option('-l, --languages <languages...>', 'List of languages to pull. Leave unspecified to export them all').default(config.pull?.languages))
42
- .addOption(new Option('-s, --states <states...>', 'List of translation states to include. Defaults all except untranslated')
43
- .choices(['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'])
44
- .default(config.pull?.states)
45
- .argParser((v, a) => [v.toUpperCase(), ...(a || [])]))
46
- .addOption(new Option('-d, --delimiter <delimiter>', 'Structure delimiter to use. By default, Tolgee interprets `.` as a nested structure. You can change the delimiter, or disable structure formatting by not specifying any value to the option').default(config.pull?.delimiter === undefined ? '.' : config.pull.delimiter))
47
- .addOption(new Option('-n, --namespaces <namespaces...>', 'List of namespaces to pull. Defaults to all namespaces').default(config.pull?.namespaces))
48
- .addOption(new Option('-t, --tags <tags...>', 'List of tags which to include. Keys tagged by at least one of these tags will be included.').default(config.pull?.tags))
49
- .addOption(new Option('--exclude-tags <tags...>', 'List of tags which to exclude. Keys tagged by at least one of these tags will be excluded.').default(config.pull?.excludeTags))
50
- .addOption(new Option('--support-arrays', 'Export keys with array syntax (e.g. item[0]) as arrays.').default(config.pull?.supportArrays ?? false))
51
- .addOption(new Option('--empty-dir', 'Empty target directory before inserting pulled files.').default(config.pull?.emptyDir))
52
- .addOption(new Option('--file-structure-template <template>', 'Defines exported file structure: https://tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format').default(config.pull?.fileStructureTemplate))
53
- .action(pullHandler());
@@ -1,140 +1,190 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { extname, join } from 'path';
2
11
  import { readdir, readFile, stat } from 'fs/promises';
3
12
  import { Command, Option } from 'commander';
4
13
  import { glob } from 'tinyglobby';
14
+ import { exit } from 'process';
5
15
  import { loading, success, error, warn, exitWithError, } from '../utils/logger.js';
6
16
  import { askString } from '../utils/ask.js';
7
17
  import { mapImportFormat } from '../utils/mapImportFormat.js';
8
18
  import { handleLoadableError } from '../client/TolgeeClient.js';
9
- async function allInPattern(pattern) {
10
- const files = [];
11
- const items = await glob(pattern);
12
- for (const item of items) {
13
- if ((await stat(item)).isDirectory()) {
14
- files.push(...(await readDirectory(item)));
19
+ import { findFilesByTemplate } from '../utils/filesTemplate.js';
20
+ import { valueToArray } from '../utils/valueToArray.js';
21
+ function allInPattern(pattern) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const files = [];
24
+ const items = yield glob(pattern);
25
+ for (const item of items) {
26
+ if ((yield stat(item)).isDirectory()) {
27
+ files.push(...(yield readDirectory(item)));
28
+ }
29
+ else {
30
+ const blob = yield readFile(item);
31
+ files.push({ name: item, data: blob });
32
+ }
15
33
  }
16
- else {
17
- const blob = await readFile(item);
18
- files.push({ name: item, data: blob });
34
+ return files;
35
+ });
36
+ }
37
+ function readDirectory(directory_1) {
38
+ return __awaiter(this, arguments, void 0, function* (directory, base = '') {
39
+ const files = [];
40
+ const dir = yield readdir(directory);
41
+ for (const file of dir) {
42
+ const filePath = join(directory, file);
43
+ const fileStat = yield stat(filePath);
44
+ if (fileStat.isDirectory()) {
45
+ const dirFiles = yield readDirectory(filePath, `${file}/`);
46
+ files.push(...dirFiles);
47
+ }
48
+ else {
49
+ const blob = yield readFile(filePath);
50
+ files.push({ name: base + file, data: blob });
51
+ }
19
52
  }
20
- }
21
- return files;
53
+ return files;
54
+ });
22
55
  }
23
- async function readDirectory(directory, base = '') {
24
- const files = [];
25
- const dir = await readdir(directory);
26
- for (const file of dir) {
27
- const filePath = join(directory, file);
28
- const fileStat = await stat(filePath);
29
- if (fileStat.isDirectory()) {
30
- const dirFiles = await readDirectory(filePath, `${file}/`);
31
- files.push(...dirFiles);
56
+ function promptConflicts(opts) {
57
+ return __awaiter(this, void 0, void 0, function* () {
58
+ if (opts.forceMode === 'NO_FORCE') {
59
+ exitWithError(`There are conflicts in the import and the force mode is set to "NO_FORCE". Set it to "KEEP" or "OVERRIDE" to continue.`);
32
60
  }
33
- else {
34
- const blob = await readFile(filePath);
35
- files.push({ name: base + file, data: blob });
61
+ if (opts.forceMode) {
62
+ return opts.forceMode;
36
63
  }
37
- }
38
- return files;
39
- }
40
- async function promptConflicts(opts) {
41
- if (opts.forceMode === 'NO_FORCE') {
42
- exitWithError(`There are conflicts in the import and the force mode is set to "NO_FORCE". Set it to "KEEP" or "OVERRIDE" to continue.`);
43
- }
44
- if (opts.forceMode) {
45
- return opts.forceMode;
46
- }
47
- if (!process.stdout.isTTY) {
48
- exitWithError(`There are conflicts in the import. Please specify a --force-mode.`);
49
- }
50
- warn('There are conflicts in the import. What do you want to do?');
51
- const resp = await askString('Type "KEEP" to preserve the version on the server, "OVERRIDE" to use the version from the import, and nothing to abort: ');
52
- if (resp !== 'KEEP' && resp !== 'OVERRIDE') {
53
- exitWithError(`Aborting.`);
54
- }
55
- return resp;
64
+ if (!process.stdout.isTTY) {
65
+ exitWithError(`There are conflicts in the import. Please specify a --force-mode.`);
66
+ }
67
+ warn('There are conflicts in the import. What do you want to do?');
68
+ const resp = yield askString('Type "KEEP" to preserve the version on the server, "OVERRIDE" to use the version from the import, and nothing to abort: ');
69
+ if (resp !== 'KEEP' && resp !== 'OVERRIDE') {
70
+ exitWithError(`Aborting.`);
71
+ }
72
+ return resp;
73
+ });
56
74
  }
57
- async function importData(client, data) {
58
- return loading('Uploading files...', client.import.import(data));
75
+ function importData(client, data) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ return loading('Uploading files...', client.import.import(data));
78
+ });
59
79
  }
60
- async function readRecords(matchers) {
61
- const result = [];
62
- for (const matcher of matchers) {
63
- const files = await allInPattern(matcher.path);
64
- files.forEach((file) => {
65
- result.push({
66
- ...matcher,
67
- data: file.data,
68
- name: file.name,
80
+ function readRecords(matchers) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const result = [];
83
+ for (const matcher of matchers) {
84
+ const files = yield allInPattern(matcher.path);
85
+ files.forEach((file) => {
86
+ result.push(Object.assign(Object.assign({}, matcher), { data: file.data, name: file.name }));
69
87
  });
70
- });
71
- }
72
- return result;
88
+ }
89
+ return result;
90
+ });
91
+ }
92
+ function handleMappingError(fileMappings) {
93
+ error('Not able to map files to existing languages in the platform');
94
+ console.log(`Pushed files:`);
95
+ fileMappings.forEach(({ fileName, languageTag }) => {
96
+ console.log(`"${fileName}"${languageTag ? ` => "${languageTag}"` : ''}`);
97
+ });
98
+ console.log('\nYou can use `push.files[*].language` property in `tolgeerc` file to map language correctly');
99
+ exit(1);
73
100
  }
74
- const pushHandler = (config) => async function () {
75
- const opts = this.optsWithGlobals();
76
- if (!config.push?.files) {
77
- exitWithError('Missing option `push.files` in configuration file.');
78
- }
79
- const filteredMatchers = config.push.files.filter((r) => {
80
- if (opts.languages && !opts.languages.includes(r.language)) {
81
- return false;
101
+ const pushHandler = (config) => function () {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ var _a, _b, _c, _d;
104
+ const opts = this.optsWithGlobals();
105
+ let allMatchers = [];
106
+ const filesTemplate = opts.filesTemplate;
107
+ if (!filesTemplate && !((_b = (_a = config.push) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b.length)) {
108
+ exitWithError('Missing option `push.filesTemplate` or `push.files`.');
82
109
  }
83
- if (opts.namespaces && !opts.namespaces.includes(r.namespace ?? '')) {
84
- return false;
110
+ if (filesTemplate) {
111
+ for (const template of filesTemplate) {
112
+ allMatchers = allMatchers.concat(...(yield findFilesByTemplate(template)));
113
+ }
85
114
  }
86
- return true;
87
- });
88
- const files = await loading('Reading files...', readRecords(filteredMatchers));
89
- if (files.length === 0) {
90
- error('Nothing to import.');
91
- return;
92
- }
93
- const params = {
94
- createNewKeys: true,
95
- forceMode: opts.forceMode,
96
- overrideKeyDescriptions: opts.overrideKeyDescriptions,
97
- convertPlaceholdersToIcu: opts.convertPlaceholdersToIcu,
98
- tagNewKeys: opts.tagNewKeys ?? [],
99
- fileMappings: files.map((f) => {
100
- const format = mapImportFormat(opts.format, extname(f.name));
101
- return {
102
- fileName: f.name,
103
- format: format,
104
- languageTag: f.language,
105
- namespace: f.namespace ?? '',
106
- };
107
- }),
108
- removeOtherKeys: opts.removeOtherKeys,
109
- };
110
- const attempt1 = await loading('Importing...', importData(opts.client, {
111
- files,
112
- params,
113
- }));
114
- if (attempt1.error) {
115
- if (attempt1.error.code !== 'conflict_is_not_resolved') {
116
- handleLoadableError(attempt1);
115
+ allMatchers = allMatchers.concat(...(((_c = config.push) === null || _c === void 0 ? void 0 : _c.files) || []));
116
+ const filteredMatchers = allMatchers.filter((r) => {
117
+ var _a;
118
+ if (r.language &&
119
+ opts.languages &&
120
+ !opts.languages.includes(r.language)) {
121
+ return false;
122
+ }
123
+ if (opts.namespaces && !opts.namespaces.includes((_a = r.namespace) !== null && _a !== void 0 ? _a : '')) {
124
+ return false;
125
+ }
126
+ return true;
127
+ });
128
+ const files = yield loading('Reading files...', readRecords(filteredMatchers));
129
+ if (files.length === 0) {
130
+ error('Nothing to import.');
131
+ return;
117
132
  }
118
- const forceMode = await promptConflicts(opts);
119
- const attempt2 = await loading('Overriding...', importData(opts.client, {
133
+ const params = {
134
+ createNewKeys: true,
135
+ forceMode: opts.forceMode,
136
+ overrideKeyDescriptions: opts.overrideKeyDescriptions,
137
+ convertPlaceholdersToIcu: opts.convertPlaceholdersToIcu,
138
+ tagNewKeys: (_d = opts.tagNewKeys) !== null && _d !== void 0 ? _d : [],
139
+ fileMappings: files.map((f) => {
140
+ var _a;
141
+ const format = mapImportFormat(opts.format, extname(f.name));
142
+ return {
143
+ fileName: f.name,
144
+ format: format,
145
+ languageTag: f.language,
146
+ namespace: (_a = f.namespace) !== null && _a !== void 0 ? _a : '',
147
+ languageTagsToImport: opts.languages,
148
+ };
149
+ }),
150
+ removeOtherKeys: opts.removeOtherKeys,
151
+ };
152
+ const attempt1 = yield loading('Importing...', importData(opts.client, {
120
153
  files,
121
- params: { ...params, forceMode },
154
+ params,
122
155
  }));
123
- handleLoadableError(attempt2);
124
- }
125
- success('Done!');
156
+ if (attempt1.error) {
157
+ if (attempt1.error.code === 'existing_language_not_selected') {
158
+ handleMappingError(params.fileMappings);
159
+ }
160
+ if (attempt1.error.code !== 'conflict_is_not_resolved') {
161
+ handleLoadableError(attempt1);
162
+ }
163
+ const forceMode = yield promptConflicts(opts);
164
+ const attempt2 = yield loading('Overriding...', importData(opts.client, {
165
+ files,
166
+ params: Object.assign(Object.assign({}, params), { forceMode }),
167
+ }));
168
+ handleLoadableError(attempt2);
169
+ }
170
+ success('Done!');
171
+ });
172
+ };
173
+ export default (config) => {
174
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
175
+ return new Command()
176
+ .name('push')
177
+ .description('Pushes translations to Tolgee')
178
+ .addOption(new Option('-ft, --files-template <templates...>', 'A template that describes the structure of the local files and their location with file structure template format (more at: https://docs.tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format).\n\nExample: `./public/{namespace}/{languageTag}.json`\n\n').default(valueToArray((_a = config.push) === null || _a === void 0 ? void 0 : _a.filesTemplate)))
179
+ .addOption(new Option('-f, --force-mode <mode>', 'What should we do with possible conflicts? If unspecified, the user will be prompted interactively, or the command will fail when in non-interactive')
180
+ .choices(['OVERRIDE', 'KEEP', 'NO_FORCE'])
181
+ .argParser((v) => v.toUpperCase())
182
+ .default((_b = config.push) === null || _b === void 0 ? void 0 : _b.forceMode))
183
+ .addOption(new Option('--override-key-descriptions', 'Override existing key descriptions from local files (only relevant for some formats).').default((_d = (_c = config.push) === null || _c === void 0 ? void 0 : _c.overrideKeyDescriptions) !== null && _d !== void 0 ? _d : true))
184
+ .addOption(new Option('--convert-placeholders-to-icu', 'Convert placeholders in local files to ICU format.').default((_f = (_e = config.push) === null || _e === void 0 ? void 0 : _e.convertPlaceholdersToIcu) !== null && _f !== void 0 ? _f : true))
185
+ .addOption(new Option('-l, --languages <languages...>', 'Specifies which languages should be pushed (see push.files in config).').default((_g = config.push) === null || _g === void 0 ? void 0 : _g.languages))
186
+ .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))
187
+ .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))
188
+ .addOption(new Option('--remove-other-keys', 'Remove keys which are not present in the import.').default((_k = config.push) === null || _k === void 0 ? void 0 : _k.removeOtherKeys))
189
+ .action(pushHandler(config));
126
190
  };
127
- export default (config) => new Command()
128
- .name('push')
129
- .description('Pushes translations to Tolgee')
130
- .addOption(new Option('-f, --force-mode <mode>', 'What should we do with possible conflicts? If unspecified, the user will be prompted interactively, or the command will fail when in non-interactive')
131
- .choices(['OVERRIDE', 'KEEP', 'NO_FORCE'])
132
- .argParser((v) => v.toUpperCase())
133
- .default(config.push?.forceMode))
134
- .addOption(new Option('--override-key-descriptions', 'Override existing key descriptions from local files (only relevant for some formats).').default(config.push?.overrideKeyDescriptions ?? true))
135
- .addOption(new Option('--convert-placeholders-to-icu', 'Convert placeholders in local files to ICU format.').default(config.push?.convertPlaceholdersToIcu ?? true))
136
- .addOption(new Option('-l, --languages <languages...>', 'Specifies which languages should be pushed (see push.files in config).').default(config.push?.languages))
137
- .addOption(new Option('-n, --namespaces <namespaces...>', 'Specifies which namespaces should be pushed (see push.files in config).').default(config.push?.namespaces))
138
- .addOption(new Option('--tag-new-keys <tags...>', 'Specify tags that will be added to newly created keys.').default(config.push?.tagNewKeys))
139
- .addOption(new Option('--remove-other-keys', 'Remove keys which are not present in the import.').default(config.push?.removeOtherKeys))
140
- .action(pushHandler(config));