@tolgee/cli 1.0.0 → 1.0.2

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.
@@ -23,17 +23,19 @@ async function compareHandler(pattern) {
23
23
  }
24
24
  console.log('Your code project and Tolgee project are out of sync.');
25
25
  if (diff.added.length) {
26
- console.log(ansi_colors_1.default.green.bold(`${diff.added.length} new strings`));
26
+ const key = diff.added.length === 1 ? 'key' : 'keys';
27
+ console.log(ansi_colors_1.default.green.bold(`${diff.added.length} new ${key} found`));
27
28
  for (const key of diff.added) {
28
- (0, syncUtils_1.printKey)(key, 'added');
29
+ (0, syncUtils_1.printKey)(key, false);
29
30
  }
30
31
  // Line break
31
32
  console.log('');
32
33
  }
33
34
  if (diff.removed.length) {
34
- console.log(ansi_colors_1.default.red.bold(`${diff.removed.length} removed strings`));
35
+ const key = diff.removed.length === 1 ? 'key' : 'keys';
36
+ console.log(ansi_colors_1.default.red.bold(`${diff.removed.length} unused ${key}`));
35
37
  for (const key of diff.removed) {
36
- (0, syncUtils_1.printKey)(key, 'removed');
38
+ (0, syncUtils_1.printKey)(key, true);
37
39
  }
38
40
  // Line break
39
41
  console.log('');
@@ -27,8 +27,8 @@ async function askForConfirmation(keys, operation) {
27
27
  process.exit(1);
28
28
  }
29
29
  const str = `The following keys will be ${operation}:`;
30
- console.log(operation === 'added' ? ansi_colors_1.default.bold.green(str) : ansi_colors_1.default.bold.red(str));
31
- keys.forEach((k) => (0, syncUtils_1.printKey)(k, operation));
30
+ console.log(operation === 'created' ? ansi_colors_1.default.bold.green(str) : ansi_colors_1.default.bold.red(str));
31
+ keys.forEach((k) => (0, syncUtils_1.printKey)(k, operation === 'deleted'));
32
32
  const shouldContinue = await (0, ask_1.askBoolean)('Does this look correct?', true);
33
33
  if (!shouldContinue) {
34
34
  (0, logger_1.error)('Aborting.');
@@ -65,7 +65,7 @@ async function syncHandler(pattern) {
65
65
  // Create new keys
66
66
  if (diff.added.length) {
67
67
  if (!opts.yes) {
68
- await askForConfirmation(diff.added, 'added');
68
+ await askForConfirmation(diff.added, 'created');
69
69
  }
70
70
  const keys = diff.added.map((key) => ({
71
71
  name: key.keyName,
@@ -80,7 +80,7 @@ async function syncHandler(pattern) {
80
80
  // Delete unused keys.
81
81
  if (diff.removed.length) {
82
82
  if (!opts.yes) {
83
- await askForConfirmation(diff.removed, 'removed');
83
+ await askForConfirmation(diff.removed, 'deleted');
84
84
  }
85
85
  const ids = await diff.removed.map((k) => k.id);
86
86
  await (0, logger_1.loading)('Deleting unused keys...', opts.client.project.deleteBulkKeys(ids));
@@ -10,17 +10,17 @@ const ansi_colors_1 = __importDefault(require("ansi-colors"));
10
10
  * Prints information about a key, with coloring and formatting.
11
11
  *
12
12
  * @param key The key to print.
13
- * @param type Whether this is an addition or a removal.
13
+ * @param deletion True if the key is about to be deleted.
14
14
  */
15
- function printKey(key, type) {
15
+ function printKey(key, deletion) {
16
16
  const namespace = key.namespace
17
17
  ? ` ${ansi_colors_1.default.italic(`(namespace: ${key.namespace})`)}`
18
18
  : '';
19
- if (type === 'added') {
20
- console.log(`${ansi_colors_1.default.green(`+ ${key.keyName}`)}${namespace}`);
19
+ if (deletion) {
20
+ console.log(`${ansi_colors_1.default.red(`- ${key.keyName}`)}${namespace}`);
21
21
  }
22
22
  else {
23
- console.log(`${ansi_colors_1.default.red(`- ${key.keyName}`)}${namespace}`);
23
+ console.log(`${ansi_colors_1.default.green(`+ ${key.keyName}`)}${namespace}`);
24
24
  }
25
25
  }
26
26
  exports.printKey = printKey;
@@ -47,18 +47,36 @@ function compareKeys(local, remote) {
47
47
  }
48
48
  }
49
49
  // Added keys
50
- const namespaces = [...Object.keys(local), runner_1.NullNamespace];
50
+ const namespaces = [runner_1.NullNamespace, ...Object.keys(local).sort()];
51
51
  for (const namespace of namespaces) {
52
- if (local[namespace].size) {
53
- for (const [keyName, defaultValue] of local[namespace].entries()) {
52
+ if (namespace in local && local[namespace].size) {
53
+ const keys = local[namespace];
54
+ const keyNames = Array.from(local[namespace].keys()).sort();
55
+ for (const keyName of keyNames) {
54
56
  result.added.push({
55
57
  keyName: keyName,
56
58
  namespace: namespace === runner_1.NullNamespace ? undefined : namespace,
57
- defaultValue: defaultValue || undefined,
59
+ defaultValue: keys.get(keyName) || undefined,
58
60
  });
59
61
  }
60
62
  }
61
63
  }
64
+ // Sort keys
65
+ // This is only necessary for unused keys, because the added keys are sorted directly as they're added.
66
+ result.removed.sort((a, b) => {
67
+ if (a.namespace === b.namespace) {
68
+ return a.keyName > b.keyName ? 1 : a.keyName < b.keyName ? -1 : 0;
69
+ }
70
+ if (!a.namespace && b.namespace)
71
+ return -1;
72
+ if (a.namespace && !b.namespace)
73
+ return 1;
74
+ return a.namespace > b.namespace
75
+ ? 1
76
+ : a.namespace < b.namespace
77
+ ? -1
78
+ : 0;
79
+ });
62
80
  return result;
63
81
  }
64
82
  exports.compareKeys = compareKeys;
@@ -26,10 +26,10 @@ function parseConfig(rc) {
26
26
  }
27
27
  }
28
28
  if ('projectId' in rc) {
29
- if (typeof rc.projectId !== 'number') {
30
- throw new Error('Invalid config: projectId is not a number');
29
+ cfg.projectId = Number(rc.projectId); // Number("") returns 0
30
+ if (!Number.isInteger(cfg.projectId) || cfg.projectId <= 0) {
31
+ throw new Error('Invalid config: projectId should be an integer representing your project Id');
31
32
  }
32
- cfg.projectId = rc.projectId;
33
33
  }
34
34
  if ('sdk' in rc) {
35
35
  if (!constants_1.SDKS.includes(rc.sdk)) {
@@ -393,6 +393,30 @@ exports.default = (0, xstate_1.createMachine)({
393
393
  target: 'idle',
394
394
  actions: ['dynamicChildren', 'pushKey'],
395
395
  },
396
+ 'variable.other.object.ts': {
397
+ target: 'idle',
398
+ actions: ['dynamicChildren', 'pushKey'],
399
+ },
400
+ 'entity.name.function.ts': {
401
+ target: 'idle',
402
+ actions: ['dynamicChildren', 'pushKey'],
403
+ },
404
+ 'storage.type.function.ts': {
405
+ target: 'idle',
406
+ actions: ['dynamicChildren', 'pushKey'],
407
+ },
408
+ 'storage.type.class.ts': {
409
+ target: 'idle',
410
+ actions: ['dynamicChildren', 'pushKey'],
411
+ },
412
+ 'keyword.operator.new.ts': {
413
+ target: 'idle',
414
+ actions: ['dynamicChildren', 'pushKey'],
415
+ },
416
+ 'punctuation.definition.block.ts': {
417
+ target: 'idle',
418
+ actions: ['dynamicChildren', 'pushKey'],
419
+ },
396
420
  'punctuation.definition.template-expression.begin.ts': {
397
421
  target: 'idle',
398
422
  actions: ['dynamicChildren', 'pushKey'],
@@ -600,7 +624,9 @@ exports.default = (0, xstate_1.createMachine)({
600
624
  // But we DO want the namespace to be overridable
601
625
  keyName: ctx.key.keyName || evt.data.keyName,
602
626
  defaultValue: ctx.key.defaultValue || evt.data.defaultValue || undefined,
603
- namespace: evt.data.namespace ?? ctx.key.namespace,
627
+ namespace: evt.data.namespace === ''
628
+ ? undefined
629
+ : evt.data.namespace ?? ctx.key.namespace,
604
630
  line: ctx.line,
605
631
  }),
606
632
  warnings: (ctx, evt) => {
@@ -105,6 +105,14 @@ exports.default = (0, xstate_1.createMachine)({
105
105
  },
106
106
  value_string: {
107
107
  on: {
108
+ 'punctuation.definition.string.end.ts': {
109
+ target: 'idle',
110
+ actions: ['storeEmptyPropertyValue', 'clearPropertyType'],
111
+ },
112
+ 'punctuation.definition.string.template.end.ts': {
113
+ target: 'idle',
114
+ actions: ['storeEmptyPropertyValue', 'clearPropertyType'],
115
+ },
108
116
  '*': [
109
117
  {
110
118
  target: 'idle',
@@ -198,6 +206,13 @@ exports.default = (0, xstate_1.createMachine)({
198
206
  defaultValue: (ctx, evt) => ctx.property === 'defaultValue' ? evt.token : ctx.defaultValue,
199
207
  namespace: (ctx, evt) => ctx.property === 'ns' ? evt.token : ctx.namespace,
200
208
  }),
209
+ storeEmptyPropertyValue: (0, xstate_1.assign)({
210
+ keyName: (ctx) => ctx.property === 'key' || ctx.property === 'keyName'
211
+ ? ''
212
+ : ctx.keyName,
213
+ defaultValue: (ctx) => ctx.property === 'defaultValue' ? '' : ctx.defaultValue,
214
+ namespace: (ctx) => (ctx.property === 'ns' ? '' : ctx.namespace),
215
+ }),
201
216
  markPropertyAsDynamic: (0, xstate_1.assign)({
202
217
  keyName: (ctx, _evt) => ctx.property === 'key' || ctx.property === 'keyName'
203
218
  ? false
package/dist/index.js CHANGED
@@ -26,36 +26,31 @@ function topLevelName(command) {
26
26
  ? topLevelName(command.parent)
27
27
  : command.name();
28
28
  }
29
- async function validateApiKey(cmd) {
29
+ async function loadApiKey(cmd) {
30
30
  const opts = cmd.optsWithGlobals();
31
- if (!opts.apiKey) {
32
- // Attempt to load --api-key from config store if not specified
33
- // This is not done as part of the init routine or via the mandatory flag, as this is dependent on the API URL.
34
- const key = await (0, credentials_1.getApiKey)(opts.apiUrl, opts.projectId);
35
- if (!key) {
36
- (0, logger_1.error)('No API key has been provided. You must either provide one via --api-key, or login via `tolgee login`.');
37
- process.exit(1);
31
+ // API Key is already loaded
32
+ if (opts.apiKey)
33
+ return;
34
+ // Attempt to load --api-key from config store if not specified
35
+ // This is not done as part of the init routine or via the mandatory flag, as this is dependent on the API URL.
36
+ const key = await (0, credentials_1.getApiKey)(opts.apiUrl, opts.projectId);
37
+ // No key in store, stop here.
38
+ if (!key)
39
+ return;
40
+ cmd.setOptionValue('apiKey', key);
41
+ program.setOptionValue('_removeApiKeyFromStore', () => {
42
+ if (key.startsWith(constants_1.API_KEY_PAT_PREFIX)) {
43
+ (0, credentials_1.savePat)(opts.apiUrl);
38
44
  }
39
- cmd.setOptionValue('apiKey', key);
40
- program.setOptionValue('_removeApiKeyFromStore', () => {
41
- if (key.startsWith(constants_1.API_KEY_PAT_PREFIX)) {
42
- (0, credentials_1.savePat)(opts.apiUrl);
43
- }
44
- else {
45
- (0, credentials_1.savePak)(opts.apiUrl, opts.projectId);
46
- }
47
- });
48
- }
45
+ else {
46
+ (0, credentials_1.savePak)(opts.apiUrl, opts.projectId);
47
+ }
48
+ });
49
49
  }
50
- function validateProjectId(cmd) {
50
+ function loadProjectId(cmd) {
51
51
  const opts = cmd.optsWithGlobals();
52
- // Validate --project-id is present when using Project API keys
53
- if (opts.projectId === -1 && opts.apiKey.startsWith(constants_1.API_KEY_PAT_PREFIX)) {
54
- (0, logger_1.error)('You must specify a Project ID.');
55
- process.exit(1);
56
- }
57
52
  if (opts.apiKey.startsWith(constants_1.API_KEY_PAK_PREFIX)) {
58
- // Parse the key to ensure we can access the specified Project ID
53
+ // Parse the key and ensure we can access the specified Project ID
59
54
  const projectId = client_1.default.projectIdFromKey(opts.apiKey);
60
55
  program.setOptionValue('projectId', projectId);
61
56
  if (opts.projectId !== -1 && opts.projectId !== projectId) {
@@ -66,10 +61,23 @@ function validateProjectId(cmd) {
66
61
  }
67
62
  }
68
63
  }
64
+ function validateOptions(cmd) {
65
+ const opts = cmd.optsWithGlobals();
66
+ if (opts.projectId === -1) {
67
+ (0, logger_1.error)('No Project ID have been specified. You must either provide one via --project-ir, or by setting up a `.tolgeerc` file.');
68
+ (0, logger_1.info)('Learn more about configuring the CLI here: https://tolgee.io/tolgee-cli/project-configuration');
69
+ process.exit(1);
70
+ }
71
+ if (!opts.apiKey) {
72
+ (0, logger_1.error)('No API key has been provided. You must either provide one via --api-key, or login via `tolgee login`.');
73
+ process.exit(1);
74
+ }
75
+ }
69
76
  async function preHandler(prog, cmd) {
70
77
  if (!NO_KEY_COMMANDS.includes(topLevelName(cmd))) {
71
- await validateApiKey(cmd);
72
- await validateProjectId(cmd);
78
+ await loadApiKey(cmd);
79
+ loadProjectId(cmd);
80
+ validateOptions(cmd);
73
81
  const opts = cmd.optsWithGlobals();
74
82
  const client = new client_1.default({
75
83
  apiUrl: opts.apiUrl,
package/dist/options.js CHANGED
@@ -7,7 +7,7 @@ const commander_1 = require("commander");
7
7
  const constants_1 = require("./constants");
8
8
  function parseProjectId(v) {
9
9
  const val = Number(v);
10
- if (isNaN(val) || val < 1) {
10
+ if (!Number.isInteger(val) || val < 1) {
11
11
  throw new commander_1.InvalidArgumentError('Not a valid project ID.');
12
12
  }
13
13
  return val;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolgee/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "commonjs",
5
5
  "description": "A tool to interact with the Tolgee Platform through CLI",
6
6
  "bin": {