@tolgee/cli 1.0.1 → 1.1.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.
@@ -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
52
  if (namespace in local && local[namespace].size) {
53
- for (const [keyName, defaultValue] of local[namespace].entries()) {
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)) {
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = require("path");
7
+ const xstate_1 = require("xstate");
8
+ const react_1 = __importDefault(require("./machines/react"));
9
+ const svelte_1 = __importDefault(require("./machines/svelte"));
10
+ const comments_1 = __importDefault(require("./machines/comments"));
11
+ const tokenizer_1 = __importDefault(require("./tokenizer"));
12
+ const REACT_EXTS = [
13
+ '.js',
14
+ '.mjs',
15
+ '.cjs',
16
+ '.ts',
17
+ '.mts',
18
+ '.cts',
19
+ '.jsx',
20
+ '.tsx',
21
+ ];
22
+ const ALL_EXTS = [
23
+ '.js',
24
+ '.mjs',
25
+ '.cjs',
26
+ '.ts',
27
+ '.mts',
28
+ '.cts',
29
+ '.jsx',
30
+ '.tsx',
31
+ '.svelte',
32
+ ];
33
+ function pickMachine(code, fileName) {
34
+ const ext = (0, path_1.extname)(fileName);
35
+ if (REACT_EXTS.includes(ext) && code.includes('@tolgee/react')) {
36
+ return react_1.default;
37
+ }
38
+ if (ext === '.svelte' && code.includes('@tolgee/svelte')) {
39
+ return svelte_1.default;
40
+ }
41
+ if (ALL_EXTS.includes(ext) &&
42
+ (code.includes('@tolgee-key') || code.includes('@tolgee-ignore'))) {
43
+ return comments_1.default;
44
+ }
45
+ return null;
46
+ }
47
+ async function extractor(code, fileName) {
48
+ const machineSpec = pickMachine(code, fileName);
49
+ if (!machineSpec) {
50
+ return { warnings: [], keys: [] };
51
+ }
52
+ const tokens = await (0, tokenizer_1.default)(code, fileName);
53
+ // @ts-ignore -- Types are whacky, complains about withConfig but it's not a problem here.
54
+ const machine = (0, xstate_1.interpret)(machineSpec);
55
+ machine.start();
56
+ for (const token of tokens) {
57
+ machine.send(token);
58
+ }
59
+ const snapshot = machine.getSnapshot();
60
+ return {
61
+ warnings: snapshot.context.warnings,
62
+ keys: snapshot.context.keys,
63
+ };
64
+ }
65
+ exports.default = extractor;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const xstate_1 = require("xstate");
7
+ const comments_1 = __importDefault(require("./shared/comments"));
8
+ exports.default = (0, xstate_1.createMachine)({
9
+ predictableActionArguments: true,
10
+ id: 'commentsExtractor',
11
+ context: {
12
+ keys: [],
13
+ warnings: [],
14
+ },
15
+ invoke: {
16
+ id: 'comments',
17
+ src: () => comments_1.default,
18
+ },
19
+ on: {
20
+ // Service messages
21
+ MAGIC_COMMENT: [
22
+ {
23
+ actions: 'warnUnusedIgnore',
24
+ cond: (_ctx, evt) => evt.kind === 'ignore',
25
+ },
26
+ {
27
+ actions: 'pushKey',
28
+ cond: (_ctx, evt) => evt.kind === 'key',
29
+ },
30
+ ],
31
+ WARNING: {
32
+ actions: 'pushWarning',
33
+ },
34
+ // Code messages
35
+ 'comment.line.double-slash.ts': {
36
+ actions: (0, xstate_1.send)((_ctx, evt) => ({
37
+ type: 'COMMENT',
38
+ data: evt.token,
39
+ line: evt.line,
40
+ }), { to: 'comments' }),
41
+ },
42
+ 'comment.block.ts': {
43
+ actions: (0, xstate_1.send)((_ctx, evt) => ({
44
+ type: 'COMMENT',
45
+ data: evt.token,
46
+ line: evt.line,
47
+ }), { to: 'comments' }),
48
+ },
49
+ 'comment.block.svelte': {
50
+ actions: (0, xstate_1.send)((_ctx, evt) => ({
51
+ type: 'COMMENT',
52
+ data: evt.token,
53
+ line: evt.line,
54
+ }), { to: 'comments' }),
55
+ },
56
+ },
57
+ }, {
58
+ actions: {
59
+ warnUnusedIgnore: (0, xstate_1.assign)({
60
+ warnings: (ctx, evt) => [
61
+ ...ctx.warnings,
62
+ { warning: 'W_UNUSED_IGNORE', line: evt.line },
63
+ ],
64
+ }),
65
+ pushKey: (0, xstate_1.assign)({
66
+ keys: (ctx, evt) => [
67
+ ...ctx.keys,
68
+ {
69
+ keyName: evt.keyName,
70
+ namespace: evt.namespace,
71
+ defaultValue: evt.defaultValue,
72
+ line: evt.line,
73
+ },
74
+ ],
75
+ }),
76
+ pushWarning: (0, xstate_1.assign)({
77
+ warnings: (ctx, evt) => [
78
+ ...ctx.warnings,
79
+ { warning: evt.kind, line: evt.line },
80
+ ],
81
+ }),
82
+ },
83
+ });
@@ -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'],
@@ -648,7 +672,7 @@ exports.default = (0, xstate_1.createMachine)({
648
672
  ...ctx.key,
649
673
  namespace: ctx.hooks.length
650
674
  ? ctx.hooks[ctx.hooks.length - 1].namespace
651
- : void 0,
675
+ : undefined,
652
676
  }),
653
677
  }),
654
678
  dynamicKeyName: (0, xstate_1.assign)({
@@ -42,6 +42,13 @@ exports.default = (0, xstate_1.createMachine)({
42
42
  target: 'value',
43
43
  actions: 'markAsStatic',
44
44
  },
45
+ // Svelte
46
+ 'entity.other.attribute-name.svelte': {
47
+ actions: ['markPropertyAsDynamic', 'storePropertyType'],
48
+ },
49
+ 'punctuation.separator.key-value.svelte': {
50
+ target: 'value',
51
+ },
45
52
  },
46
53
  },
47
54
  complex_key: {
@@ -69,6 +76,8 @@ exports.default = (0, xstate_1.createMachine)({
69
76
  // Extract strings
70
77
  'punctuation.definition.string.begin.ts': 'value_string',
71
78
  'punctuation.definition.string.template.begin.ts': 'value_string',
79
+ 'punctuation.definition.string.begin.svelte': 'value_string',
80
+ 'punctuation.definition.string.template.begin.svelte': 'value_string',
72
81
  // Variable
73
82
  'variable.other.readwrite.ts': {
74
83
  target: 'idle',
@@ -82,6 +91,15 @@ exports.default = (0, xstate_1.createMachine)({
82
91
  'punctuation.section.embedded.begin.tsx': {
83
92
  actions: 'unmarkAsStatic',
84
93
  },
94
+ // Svelte
95
+ 'string.unquoted.svelte': {
96
+ target: 'idle',
97
+ actions: [
98
+ 'storePropertyValue',
99
+ 'clearPropertyType',
100
+ 'unmarkAsStatic',
101
+ ],
102
+ },
85
103
  // Value end
86
104
  'punctuation.separator.comma.ts': {
87
105
  target: 'idle',
@@ -113,6 +131,14 @@ exports.default = (0, xstate_1.createMachine)({
113
131
  target: 'idle',
114
132
  actions: ['storeEmptyPropertyValue', 'clearPropertyType'],
115
133
  },
134
+ 'punctuation.definition.string.end.svelte': {
135
+ target: 'idle',
136
+ actions: ['storeEmptyPropertyValue', 'clearPropertyType'],
137
+ },
138
+ 'punctuation.definition.string.template.end.svelte': {
139
+ target: 'idle',
140
+ actions: ['storeEmptyPropertyValue', 'clearPropertyType'],
141
+ },
116
142
  '*': [
117
143
  {
118
144
  target: 'idle',
@@ -149,6 +175,19 @@ exports.default = (0, xstate_1.createMachine)({
149
175
  target: 'idle',
150
176
  actions: 'clearPropertyType',
151
177
  },
178
+ // Svelte
179
+ 'punctuation.section.embedded.begin.svelte': {
180
+ target: 'idle',
181
+ actions: ['markPropertyAsDynamic', 'clearPropertyType'],
182
+ },
183
+ 'punctuation.definition.string.end.svelte': {
184
+ target: 'idle',
185
+ actions: 'clearPropertyType',
186
+ },
187
+ 'punctuation.section.embedded.end.svelte': {
188
+ target: 'idle',
189
+ actions: 'clearPropertyType',
190
+ },
152
191
  },
153
192
  },
154
193
  end: {
@@ -180,13 +219,19 @@ exports.default = (0, xstate_1.createMachine)({
180
219
  target: 'end',
181
220
  actions: 'markPropertyAsDynamic',
182
221
  },
222
+ 'punctuation.definition.tag.end.svelte': {
223
+ target: 'end',
224
+ actions: 'markPropertyAsDynamic',
225
+ },
183
226
  },
184
227
  }, {
185
228
  guards: {
186
229
  isOpenCurly: (_ctx, evt) => evt.token === '{' &&
187
- !evt.scopes.includes('meta.embedded.expression.tsx'),
230
+ !evt.scopes.includes('meta.embedded.expression.tsx') &&
231
+ !evt.scopes.includes('meta.embedded.expression.svelte'),
188
232
  isCloseCurly: (_ctx, evt) => evt.token === '}' &&
189
- !evt.scopes.includes('meta.embedded.expression.tsx'),
233
+ !evt.scopes.includes('meta.embedded.expression.tsx') &&
234
+ !evt.scopes.includes('meta.embedded.expression.svelte'),
190
235
  isFinalCloseCurly: (ctx, evt) => evt.token === '}' && ctx.depth === 1,
191
236
  isOpenSquare: (_ctx, evt) => evt.token === '[',
192
237
  isCloseSquare: (_ctx, evt) => evt.token === ']',