@extrahorizon/exh-cli 1.6.0-dev-38-562ae51 → 1.6.0-dev-40-7e1d3a2

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 (31) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/build/commands/localizations/sync.d.ts +4 -17
  3. package/build/commands/localizations/sync.js +5 -44
  4. package/build/commands/sync.d.ts +19 -11
  5. package/build/commands/sync.js +19 -6
  6. package/build/helpers/repoConfig.d.ts +1 -1
  7. package/build/helpers/repoConfig.js +3 -6
  8. package/build/helpers/util.d.ts +0 -1
  9. package/build/helpers/util.js +1 -7
  10. package/build/repositories/localizations.d.ts +4 -0
  11. package/build/repositories/localizations.js +11 -0
  12. package/build/services/localizations/assertValidFileContent.d.ts +1 -0
  13. package/build/services/localizations/assertValidFileContent.js +14 -0
  14. package/build/services/localizations/assertValidLanguageCode.d.ts +2 -0
  15. package/build/{commands/localizations/utils → services/localizations}/assertValidLanguageCode.js +3 -3
  16. package/build/services/localizations/index.d.ts +2 -0
  17. package/build/services/localizations/index.js +25 -0
  18. package/build/services/localizations/readFiles.d.ts +2 -0
  19. package/build/services/localizations/readFiles.js +44 -0
  20. package/build/services/localizations/syncLocalizations.d.ts +3 -0
  21. package/build/services/localizations/syncLocalizations.js +23 -0
  22. package/package.json +2 -2
  23. package/build/commands/localizations/utils/assertValidKey.d.ts +0 -1
  24. package/build/commands/localizations/utils/assertValidKey.js +0 -10
  25. package/build/commands/localizations/utils/assertValidLanguageCode.d.ts +0 -2
  26. package/build/commands/localizations/utils/fetchLocalizations.d.ts +0 -2
  27. package/build/commands/localizations/utils/fetchLocalizations.js +0 -20
  28. package/build/commands/localizations/utils/readLocalizationFiles.d.ts +0 -6
  29. package/build/commands/localizations/utils/readLocalizationFiles.js +0 -39
  30. package/build/commands/localizations/utils/syncLocalizations.d.ts +0 -2
  31. package/build/commands/localizations/utils/syncLocalizations.js +0 -46
package/CHANGELOG.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Extra Horizon CLI changelog
2
2
 
3
3
  ### v1.6.0
4
+ * Fixed some security vulnerabilities in the dependencies (Thanks @tran-simon)
4
5
  * Removed the update notifier. The version of the package we used had security vulnerabilities and we're unable to migrate to the latest version right now. We'll look into this again in the future.
5
6
  * Added the `exh whoami` command, showing the currently logged in user
7
+ * Added the `exh localizations sync` command, allowing you to sync localizations from a folder containing your translations (Thanks @Lroemon for the initial implementation)
8
+ * Added localization syncing support to the `exh sync` command
9
+ * The `exh sync` command now also supports absolute paths for the `--path` argument
6
10
 
7
11
  ### v1.5.1
8
12
  * Now also publishing to the NPM registry, no longer needing to authenticate with GitHub Packages to install the CLI
@@ -2,28 +2,15 @@
2
2
  import { OAuth1Client } from '@extrahorizon/javascript-sdk';
3
3
  export declare const command = "sync";
4
4
  export declare const desc = "Sync all localizations in a directory with the ExH cloud";
5
- export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs").Omit<{}, "keys" | "path" | "languages"> & import("yargs").InferredOptionTypes<{
5
+ export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs").Omit<{}, "path"> & import("yargs").InferredOptionTypes<{
6
6
  path: {
7
7
  demandOption: false;
8
8
  describe: string;
9
9
  type: "string";
10
- };
11
- keys: {
12
- demandOption: false;
13
- describe: string;
14
- type: "string";
15
- };
16
- languages: {
17
- demandOption: false;
18
- describe: string;
19
- type: "string";
10
+ default: string;
20
11
  };
21
12
  }>>;
22
- interface HandlerInput {
13
+ export declare const handler: ({ sdk, path }: {
23
14
  sdk: OAuth1Client;
24
15
  path?: string;
25
- languages?: string;
26
- keys?: string;
27
- }
28
- export declare const handler: ({ keys: rawKeys, languages: rawLanguages, path, sdk }: HandlerInput) => Promise<void>;
29
- export {};
16
+ }) => Promise<void>;
@@ -1,59 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handler = exports.builder = exports.desc = exports.command = void 0;
4
- const fs = require("fs");
5
- const ospath = require("path");
6
4
  const util_1 = require("../../helpers/util");
7
- const assertValidKey_1 = require("./utils/assertValidKey");
8
- const assertValidLanguageCode_1 = require("./utils/assertValidLanguageCode");
9
- const fetchLocalizations_1 = require("./utils/fetchLocalizations");
10
- const readLocalizationFiles_1 = require("./utils/readLocalizationFiles");
11
- const syncLocalizations_1 = require("./utils/syncLocalizations");
5
+ const localizationsService = require("../../services/localizations");
12
6
  exports.command = 'sync';
13
7
  exports.desc = 'Sync all localizations in a directory with the ExH cloud';
14
8
  const builder = (yargs) => (0, util_1.epilogue)(yargs).options({
15
9
  path: {
16
10
  demandOption: false,
17
- describe: 'Directory containing the localizations which need to be synced in a JSON format. By Default: /locales',
11
+ describe: 'Directory containing the localizations which need to be synced in a JSON format. By Default: ./localizations',
18
12
  type: 'string',
13
+ default: './localizations',
19
14
  },
20
- keys: {
21
- demandOption: false,
22
- describe: 'The keys separated by a "," to sync',
23
- type: 'string',
24
- },
25
- languages: {
26
- demandOption: false,
27
- describe: 'The languages separated by a "," to sync',
28
- type: 'string',
29
- },
30
- }).check(({ path, keys: rawKeys, languages: rawLanguages }) => {
31
- if (path && !fs.existsSync(ospath.join(process.cwd(), path))) {
32
- throw new Error('please provide a valid file path for your localizations');
33
- }
34
- if (rawLanguages) {
35
- const languageCodes = rawLanguages.toLocaleUpperCase().split(',');
36
- for (const languageCode of languageCodes) {
37
- (0, assertValidLanguageCode_1.assertValidLanguageCode)(languageCode);
38
- }
39
- }
40
- if (rawKeys) {
41
- const keys = rawKeys.split(',');
42
- for (const key of keys) {
43
- (0, assertValidKey_1.assertValidKey)(key);
44
- }
45
- }
46
- return true;
47
15
  });
48
16
  exports.builder = builder;
49
- const handler = async ({ keys: rawKeys, languages: rawLanguages, path, sdk }) => {
50
- const keys = rawKeys?.split(',');
51
- const selectedLanguageCodes = rawLanguages?.toUpperCase().split(',');
52
- const fullPath = ospath.join(process.cwd(), path ?? 'locales');
53
- console.log(`- Reading the localization configuration from ${fullPath}`);
54
- const { localizations, languageCodes } = (0, readLocalizationFiles_1.readLocalizationFiles)(fullPath, keys, selectedLanguageCodes);
55
- console.log('- Fetching the localization configuration from the localizations service');
56
- const savedLocalizations = await (0, fetchLocalizations_1.fetchLocalizations)(sdk, localizations.map(localization => localization.key), languageCodes);
57
- await (0, syncLocalizations_1.syncLocalizations)(sdk, localizations, savedLocalizations);
17
+ const handler = async ({ sdk, path }) => {
18
+ await localizationsService.sync(sdk, path);
58
19
  };
59
20
  exports.handler = handler;
@@ -1,7 +1,8 @@
1
1
  /// <reference types="yargs" />
2
+ import { OAuth1Client } from '@extrahorizon/javascript-sdk';
2
3
  export declare const command = "sync";
3
- export declare const desc = "Upload all schemas, templates & tasks to the cloud environment";
4
- export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs").Omit<{}, "path" | "schemas" | "tasks" | "dispatchers" | "templates" | "cleanDispatchers" | "ignoreSchemaVerificationErrors"> & import("yargs").InferredOptionTypes<{
4
+ export declare const desc = "Sync your ExH configuration to the cloud environment";
5
+ export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs").Omit<{}, "path" | "schemas" | "tasks" | "dispatchers" | "localizations" | "templates" | "cleanDispatchers" | "ignoreSchemaVerificationErrors"> & import("yargs").InferredOptionTypes<{
5
6
  path: {
6
7
  demandOption: false;
7
8
  describe: string;
@@ -36,6 +37,12 @@ export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs
36
37
  describe: string;
37
38
  type: "boolean";
38
39
  };
40
+ localizations: {
41
+ demandOption: false;
42
+ describe: string;
43
+ type: "boolean";
44
+ default: boolean;
45
+ };
39
46
  ignoreSchemaVerificationErrors: {
40
47
  demandOption: false;
41
48
  describe: string;
@@ -43,13 +50,14 @@ export declare const builder: (yargs: any) => import("yargs").Argv<import("yargs
43
50
  default: boolean;
44
51
  };
45
52
  }>>;
46
- export declare const handler: ({ sdk, path, schemas, tasks, templates, dispatchers, cleanDispatchers, ignoreSchemaVerificationErrors }: {
47
- sdk: any;
48
- path: any;
49
- schemas: any;
50
- tasks: any;
51
- templates: any;
52
- dispatchers: any;
53
- cleanDispatchers: any;
54
- ignoreSchemaVerificationErrors: any;
53
+ export declare const handler: ({ sdk, path, schemas, tasks, templates, dispatchers, cleanDispatchers, localizations, ignoreSchemaVerificationErrors, }: {
54
+ sdk: OAuth1Client;
55
+ path?: string;
56
+ schemas?: boolean;
57
+ tasks?: boolean;
58
+ templates?: boolean;
59
+ dispatchers?: boolean;
60
+ cleanDispatchers?: boolean;
61
+ localizations?: boolean;
62
+ ignoreSchemaVerificationErrors?: boolean;
55
63
  }) => Promise<void>;
@@ -8,17 +8,18 @@ const chalk = require("chalk");
8
8
  const repoConfig_1 = require("../helpers/repoConfig");
9
9
  const util_1 = require("../helpers/util");
10
10
  const dispatchers_1 = require("../services/dispatchers");
11
+ const localizations_1 = require("../services/localizations");
11
12
  const sync_1 = require("./data/schemas/sync");
12
13
  const sync_2 = require("./tasks/sync");
13
14
  const sync_3 = require("./templates/sync");
14
15
  exports.command = 'sync';
15
- exports.desc = 'Upload all schemas, templates & tasks to the cloud environment';
16
+ exports.desc = 'Sync your ExH configuration to the cloud environment';
16
17
  const builder = (yargs) => (0, util_1.epilogue)(yargs)
17
18
  .options({
18
19
  path: {
19
20
  demandOption: false,
20
21
  describe: `Path to folder which needs to be synchronized. The target folder should contain a ${repoConfig_1.REPO_CONFIG_FILE} file.
21
- If not, the local directory is assumed with a default configuration which assumes tasks are in a 'tasks' folder, schemas in a 'schemas' folder and templates in a 'templates' folder`,
22
+ If not, the local directory is assumed with a default configuration which assumes tasks are in a 'tasks' folder, schemas in a 'schemas' folder, etc...`,
22
23
  type: 'string',
23
24
  },
24
25
  schemas: {
@@ -50,6 +51,12 @@ If not, the local directory is assumed with a default configuration which assume
50
51
  describe: 'Delete Dispatchers created using the CLI, that are no longer present in the local Dispatcher file',
51
52
  type: 'boolean',
52
53
  },
54
+ localizations: {
55
+ demandOption: false,
56
+ describe: 'Sync localizations only',
57
+ type: 'boolean',
58
+ default: false,
59
+ },
53
60
  ignoreSchemaVerificationErrors: {
54
61
  demandOption: false,
55
62
  describe: 'Allow schema synchronization to proceed with validation errors.',
@@ -71,10 +78,10 @@ If not, the local directory is assumed with a default configuration which assume
71
78
  return true;
72
79
  });
73
80
  exports.builder = builder;
74
- const handler = async ({ sdk, path, schemas, tasks, templates, dispatchers, cleanDispatchers, ignoreSchemaVerificationErrors }) => {
75
- const targetPath = ospath.join(process.cwd(), path || '.');
76
- const cfg = await (0, repoConfig_1.getRepoConfig)(targetPath, true);
77
- const syncAll = !(schemas || tasks || templates || dispatchers);
81
+ const handler = async ({ sdk, path, schemas, tasks, templates, dispatchers, cleanDispatchers, localizations, ignoreSchemaVerificationErrors, }) => {
82
+ const targetPath = path || '.';
83
+ const cfg = await (0, repoConfig_1.getRepoConfig)(targetPath);
84
+ const syncAll = !(schemas || tasks || templates || dispatchers || localizations);
78
85
  if ((syncAll || schemas) && cfg.schemas) {
79
86
  console.log(chalk.green('\n ⚙️ Syncing schemas ...'));
80
87
  for (const schema of cfg.schemas) {
@@ -93,6 +100,12 @@ const handler = async ({ sdk, path, schemas, tasks, templates, dispatchers, clea
93
100
  await (0, sync_2.handler)({ sdk, path: ospath.join(targetPath, task) });
94
101
  }
95
102
  }
103
+ if ((syncAll || localizations) && cfg.localizations) {
104
+ console.log(chalk.green('\n ⚙️ Syncing localizations...'));
105
+ for (const localization of cfg.localizations) {
106
+ await (0, localizations_1.sync)(sdk, ospath.join(targetPath, localization));
107
+ }
108
+ }
96
109
  if ((syncAll || dispatchers)) {
97
110
  const dispatchersPath = ospath.join(targetPath, 'dispatchers.json');
98
111
  const isValidPath = (0, fs_1.existsSync)(dispatchersPath);
@@ -1,2 +1,2 @@
1
1
  export declare const REPO_CONFIG_FILE = "repo-config.json";
2
- export declare function getRepoConfig(targetPath: string, validate: boolean): Promise<any>;
2
+ export declare function getRepoConfig(targetPath: string): Promise<any>;
@@ -7,7 +7,7 @@ const chalk = require("chalk");
7
7
  exports.REPO_CONFIG_FILE = 'repo-config.json';
8
8
  async function getDefaultConfig(targetPath) {
9
9
  const config = {};
10
- const sections = ['schemas', 'templates', 'tasks'];
10
+ const sections = ['schemas', 'templates', 'tasks', 'localizations'];
11
11
  for (const s of sections) {
12
12
  try {
13
13
  await fs.access(ospath.join(targetPath, s));
@@ -46,15 +46,12 @@ async function validateRepoConfig(targetPath, config) {
46
46
  }
47
47
  return newConfig;
48
48
  }
49
- async function getRepoConfig(targetPath, validate) {
49
+ async function getRepoConfig(targetPath) {
50
50
  let cfg = await getDefaultConfig(targetPath);
51
51
  try {
52
52
  cfg = JSON.parse((await fs.readFile(ospath.join(targetPath, exports.REPO_CONFIG_FILE))).toString());
53
53
  }
54
54
  catch (err) { }
55
- if (validate) {
56
- return await validateRepoConfig(targetPath, cfg);
57
- }
58
- return cfg;
55
+ return await validateRepoConfig(targetPath, cfg);
59
56
  }
60
57
  exports.getRepoConfig = getRepoConfig;
@@ -1,4 +1,3 @@
1
1
  import * as yargs from 'yargs';
2
2
  export declare function epilogue(y: yargs.Argv): yargs.Argv;
3
3
  export declare function asyncExec(cmd: string): Promise<string>;
4
- export declare function readJsonFileSync<T>(path: string): T;
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readJsonFileSync = exports.asyncExec = exports.epilogue = void 0;
3
+ exports.asyncExec = exports.epilogue = void 0;
4
4
  const child_process_1 = require("child_process");
5
- const fs_1 = require("fs");
6
5
  const chalk = require("chalk");
7
6
  const error_1 = require("./error");
8
7
  function epilogue(y) {
@@ -35,8 +34,3 @@ async function asyncExec(cmd) {
35
34
  });
36
35
  }
37
36
  exports.asyncExec = asyncExec;
38
- function readJsonFileSync(path) {
39
- const fileContent = (0, fs_1.readFileSync)(path, { encoding: 'utf-8' });
40
- return JSON.parse(fileContent);
41
- }
42
- exports.readJsonFileSync = readJsonFileSync;
@@ -0,0 +1,4 @@
1
+ import { Localization, OAuth1Client } from '@extrahorizon/javascript-sdk';
2
+ export declare function create(sdk: OAuth1Client, localizations: PartialLocalization[]): Promise<import("@extrahorizon/javascript-sdk").BulkCreationResponse>;
3
+ export declare function update(sdk: OAuth1Client, localizations: PartialLocalization[]): Promise<import("@extrahorizon/javascript-sdk").BulkUpdateResponse>;
4
+ export declare type PartialLocalization = Pick<Localization, 'key' | 'text'>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.update = exports.create = void 0;
4
+ async function create(sdk, localizations) {
5
+ return await sdk.localizations.create({ localizations });
6
+ }
7
+ exports.create = create;
8
+ async function update(sdk, localizations) {
9
+ return await sdk.localizations.update({ localizations });
10
+ }
11
+ exports.update = update;
@@ -0,0 +1 @@
1
+ export declare function assertValidFileContent(fileName: string, content: unknown): asserts content is Record<string, string>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertValidFileContent = void 0;
4
+ const Joi = require("joi");
5
+ const keyPattern = /^[a-z0-9_.-]{1,200}$/i;
6
+ const localizationFileSchema = Joi.object()
7
+ .pattern(keyPattern, Joi.string());
8
+ function assertValidFileContent(fileName, content) {
9
+ const result = localizationFileSchema.validate(content);
10
+ if (result.error != null) {
11
+ throw new Error(`The content of localization file '${fileName}' is not valid: ${result.error.message}`);
12
+ }
13
+ }
14
+ exports.assertValidFileContent = assertValidFileContent;
@@ -0,0 +1,2 @@
1
+ export declare const availableLanguagesCodes: string[];
2
+ export declare function assertValidLanguageCode(languageCode: string): void;
@@ -13,9 +13,9 @@ exports.availableLanguagesCodes = [
13
13
  'TN', 'TO', 'TR', 'TS', 'TT', 'TW', 'TY', 'UG', 'UK', 'UR', 'UZ', 'VE', 'VI', 'VO', 'WA', 'WO', 'XH', 'YI', 'YO', 'ZA', 'ZH',
14
14
  'ZU',
15
15
  ];
16
- const assertValidLanguageCode = (languageCode) => {
16
+ function assertValidLanguageCode(languageCode) {
17
17
  if (!exports.availableLanguagesCodes.includes(languageCode)) {
18
- throw new Error(`The language code ${languageCode} is not available! The available language codes are [${exports.availableLanguagesCodes.join(', ')}]`);
18
+ throw new Error(`The language code ${languageCode} is not available! The available language codes are: ${exports.availableLanguagesCodes.join(', ')}`);
19
19
  }
20
- };
20
+ }
21
21
  exports.assertValidLanguageCode = assertValidLanguageCode;
@@ -0,0 +1,2 @@
1
+ import { OAuth1Client } from '@extrahorizon/javascript-sdk';
2
+ export declare function sync(sdk: OAuth1Client, path: string): Promise<void>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sync = void 0;
4
+ const chalk_1 = require("chalk");
5
+ const readFiles_1 = require("./readFiles");
6
+ const syncLocalizations_1 = require("./syncLocalizations");
7
+ async function sync(sdk, path) {
8
+ console.log((0, chalk_1.yellow)(`Synchronizing localizations from ${path}`));
9
+ const localizations = (0, readFiles_1.readFiles)(path);
10
+ if (localizations.length < 1) {
11
+ console.log((0, chalk_1.yellow)('No localizations found'));
12
+ return;
13
+ }
14
+ assertDefaultLanguageSetForAll(localizations);
15
+ await (0, syncLocalizations_1.syncLocalizations)(sdk, localizations);
16
+ }
17
+ exports.sync = sync;
18
+ function assertDefaultLanguageSetForAll(localizations) {
19
+ const defaultLanguage = 'EN';
20
+ const faultyLocalizations = localizations.filter(localization => localization.text[defaultLanguage] === undefined);
21
+ const faultyKeys = faultyLocalizations.map(localization => localization.key);
22
+ if (faultyKeys.length > 0) {
23
+ throw new Error(`The following localizations do not have a value for the default language (${defaultLanguage}): ${faultyKeys.join(', ')}`);
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ import { PartialLocalization } from '../../repositories/localizations';
2
+ export declare function readFiles(path: string): PartialLocalization[];
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readFiles = void 0;
4
+ const fs_1 = require("fs");
5
+ const osPath = require("path");
6
+ const assertValidFileContent_1 = require("./assertValidFileContent");
7
+ const assertValidLanguageCode_1 = require("./assertValidLanguageCode");
8
+ function readFiles(path) {
9
+ let fileNames;
10
+ try {
11
+ fileNames = (0, fs_1.readdirSync)(path);
12
+ }
13
+ catch (error) {
14
+ throw new Error(`Was not able to list localization files in directory '${path}': ${error}`);
15
+ }
16
+ const localizationMap = {};
17
+ for (const fileName of fileNames) {
18
+ const parsedFileName = osPath.parse(fileName);
19
+ if (parsedFileName.ext !== '.json') {
20
+ continue;
21
+ }
22
+ const languageCode = parsedFileName.name.toUpperCase();
23
+ (0, assertValidLanguageCode_1.assertValidLanguageCode)(languageCode);
24
+ const localizationJson = readLocalizationFileSync(path, fileName);
25
+ (0, assertValidFileContent_1.assertValidFileContent)(fileName, localizationJson);
26
+ for (const [key, text] of Object.entries(localizationJson)) {
27
+ if (!(key in localizationMap)) {
28
+ localizationMap[key] = { key, text: {} };
29
+ }
30
+ localizationMap[key].text[languageCode] = text;
31
+ }
32
+ }
33
+ return Object.values(localizationMap);
34
+ }
35
+ exports.readFiles = readFiles;
36
+ function readLocalizationFileSync(path, fileName) {
37
+ const fileContent = (0, fs_1.readFileSync)(osPath.join(path, fileName), { encoding: 'utf-8' });
38
+ try {
39
+ return JSON.parse(fileContent);
40
+ }
41
+ catch (error) {
42
+ throw Error(`Was not able to parse '${fileName}', not a valid JSON file`);
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ import { OAuth1Client } from '@extrahorizon/javascript-sdk';
2
+ import { PartialLocalization } from '../../repositories/localizations';
3
+ export declare const syncLocalizations: (sdk: OAuth1Client, localizations: PartialLocalization[]) => Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncLocalizations = void 0;
4
+ const chalk = require("chalk");
5
+ const lodash_1 = require("lodash");
6
+ const localizationRepository = require("../../repositories/localizations");
7
+ const chunkSize = 30;
8
+ const syncLocalizations = async (sdk, localizations) => {
9
+ console.log(`${localizations.length} localization(s) to synchronize.`);
10
+ const localizationChunks = (0, lodash_1.chunk)(localizations, chunkSize);
11
+ for (const localizationChunk of localizationChunks) {
12
+ const creationResult = await localizationRepository.create(sdk, localizationChunk);
13
+ console.log(`Created ${creationResult.created} localization(s).`);
14
+ if (creationResult.existingIds && creationResult.existingIds.length > 0) {
15
+ const existingKeys = creationResult.existingIds;
16
+ const existingLocalizations = localizationChunk.filter(localization => existingKeys.includes(localization.key));
17
+ const updateResult = await localizationRepository.update(sdk, existingLocalizations);
18
+ console.log(`Updated ${updateResult.updated} localization(s).`);
19
+ }
20
+ }
21
+ console.log(chalk.green('Successful!'));
22
+ };
23
+ exports.syncLocalizations = syncLocalizations;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extrahorizon/exh-cli",
3
- "version": "1.6.0-dev-38-562ae51",
3
+ "version": "1.6.0-dev-40-7e1d3a2",
4
4
  "main": "build/index.js",
5
5
  "exports": "./build/index.js",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "typescript": "4.5.5"
41
41
  },
42
42
  "dependencies": {
43
- "@extrahorizon/javascript-sdk": "^8.2.0",
43
+ "@extrahorizon/javascript-sdk": "^8.4.0",
44
44
  "ajv": "^8.11.0",
45
45
  "archiver": "^5.3.1",
46
46
  "chalk": "^4.0.0",
@@ -1 +0,0 @@
1
- export declare const assertValidKey: (key: string) => void;
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assertValidKey = void 0;
4
- const assertValidKey = (key) => {
5
- const validKeyRegex = /^[a-zA-Z][a-zA-Z0-9_]*$/;
6
- if (!validKeyRegex.test(key)) {
7
- throw new Error(`The key ${key} is not valid! Must follow the pattern ${validKeyRegex}!`);
8
- }
9
- };
10
- exports.assertValidKey = assertValidKey;
@@ -1,2 +0,0 @@
1
- export declare const availableLanguagesCodes: string[];
2
- export declare const assertValidLanguageCode: (languageCode: string) => void;
@@ -1,2 +0,0 @@
1
- import { Localization, OAuth1Client } from '@extrahorizon/javascript-sdk';
2
- export declare const fetchLocalizations: (sdk: OAuth1Client, keys: string[], languageCodes: string[]) => Promise<Localization[]>;
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchLocalizations = void 0;
4
- const javascript_sdk_1 = require("@extrahorizon/javascript-sdk");
5
- const lodash_1 = require("lodash");
6
- const fetchLocalizations = async (sdk, keys, languageCodes) => {
7
- const localizations = [];
8
- const keyChunks = (0, lodash_1.chunk)(keys, 5);
9
- for (const keyChunk of keyChunks) {
10
- const result = await sdk.localizations.find({
11
- rql: (0, javascript_sdk_1.rqlBuilder)()
12
- .in('key', keyChunk)
13
- .select(['key'].concat(languageCodes.map(languageCode => `text.${languageCode}`)))
14
- .build(),
15
- });
16
- localizations.push(...result.data);
17
- }
18
- return localizations;
19
- };
20
- exports.fetchLocalizations = fetchLocalizations;
@@ -1,6 +0,0 @@
1
- import { Localization } from '@extrahorizon/javascript-sdk';
2
- export interface LocalizationFilesOutput {
3
- localizations: Pick<Localization, 'key' | 'text'>[];
4
- languageCodes: string[];
5
- }
6
- export declare const readLocalizationFiles: (path: string, keys?: string[], languageCodes?: string[]) => LocalizationFilesOutput;
@@ -1,39 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readLocalizationFiles = void 0;
4
- const fs_1 = require("fs");
5
- const ospath = require("path");
6
- const util_1 = require("../../../helpers/util");
7
- const assertValidLanguageCode_1 = require("./assertValidLanguageCode");
8
- const readLocalizationFiles = (path, keys, languageCodes) => {
9
- const uniqueLanguageCodes = new Set();
10
- const localizationMap = {};
11
- const fileNames = (0, fs_1.readdirSync)(path);
12
- for (const fileName of fileNames) {
13
- const parsedFileName = ospath.parse(fileName);
14
- const languageCode = parsedFileName.name.toUpperCase();
15
- (0, assertValidLanguageCode_1.assertValidLanguageCode)(languageCode);
16
- if (parsedFileName.ext !== '.json') {
17
- continue;
18
- }
19
- if (languageCodes && !languageCodes.includes(languageCode)) {
20
- continue;
21
- }
22
- uniqueLanguageCodes.add(languageCode);
23
- const localizationJson = (0, util_1.readJsonFileSync)(ospath.join(path, fileName));
24
- for (const [key, text] of Object.entries(localizationJson)) {
25
- if (keys && !keys.includes(key)) {
26
- continue;
27
- }
28
- if (!(key in localizationMap)) {
29
- localizationMap[key] = { key, text: {} };
30
- }
31
- localizationMap[key].text[languageCode] = text;
32
- }
33
- }
34
- return {
35
- localizations: Object.values(localizationMap),
36
- languageCodes: [...uniqueLanguageCodes.values()],
37
- };
38
- };
39
- exports.readLocalizationFiles = readLocalizationFiles;
@@ -1,2 +0,0 @@
1
- import { Localization, OAuth1Client } from '@extrahorizon/javascript-sdk';
2
- export declare const syncLocalizations: (sdk: OAuth1Client, configLocalizations: Pick<Localization, 'key' | 'text'>[], fetchedLocalizations: Localization[]) => Promise<void>;
@@ -1,46 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.syncLocalizations = void 0;
4
- const chalk = require("chalk");
5
- const getMissingLocalizations = (configLocalizations, fetchedLocalizations) => configLocalizations.filter(configLocalization => fetchedLocalizations.find(fetchedLocalization => fetchedLocalization.key === configLocalization.key) === undefined);
6
- const getChangedLocalizations = (configLocalizations, fetchedLocalizations) => {
7
- const changedLocalizations = [];
8
- for (const configLocalization of configLocalizations) {
9
- const knownLocalization = fetchedLocalizations.find(fetchedLocalization => fetchedLocalization.key === configLocalization.key);
10
- if (knownLocalization) {
11
- const localizationUpdateData = {
12
- key: configLocalization.key,
13
- text: {},
14
- };
15
- for (const [newLanguage, newText] of Object.entries(configLocalization.text)) {
16
- const knownText = knownLocalization.text[newLanguage];
17
- if (!knownText || knownText !== newText) {
18
- localizationUpdateData.text[newLanguage] = newText;
19
- }
20
- }
21
- if (Object.keys(localizationUpdateData.text).length > 0) {
22
- changedLocalizations.push(localizationUpdateData);
23
- }
24
- }
25
- }
26
- return changedLocalizations;
27
- };
28
- const syncLocalizations = async (sdk, configLocalizations, fetchedLocalizations) => {
29
- const localizationsToCreate = getMissingLocalizations(configLocalizations, fetchedLocalizations);
30
- const localizationsToUpdate = getChangedLocalizations(configLocalizations, fetchedLocalizations);
31
- console.log(`- Creating of ${localizationsToCreate.length} new localization key(s).`);
32
- if (localizationsToCreate.length > 0) {
33
- await sdk.localizations.create({
34
- localizations: localizationsToCreate,
35
- });
36
- console.log(chalk.green('Successful!'));
37
- }
38
- console.log(`- Updating of ${localizationsToUpdate.length} new localization key(s).`);
39
- if (localizationsToUpdate.length > 0) {
40
- await sdk.localizations.update({
41
- localizations: localizationsToUpdate,
42
- });
43
- console.log(chalk.green('Successful!'));
44
- }
45
- };
46
- exports.syncLocalizations = syncLocalizations;