@logto/cli 1.11.0 → 1.12.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,11 +1,11 @@
1
1
  type FileStructure = {
2
- [key: string]: {
2
+ [key: string | number]: {
3
3
  filePath?: string;
4
4
  structure: FileStructure;
5
5
  };
6
6
  };
7
7
  type NestedPhraseObject = {
8
- [key: string]: [phrase: string, isTranslated: boolean] | NestedPhraseObject;
8
+ [key: string | number]: [phrase: string, isTranslated: boolean] | NestedPhraseObject;
9
9
  };
10
10
  type ParsedTuple = readonly [NestedPhraseObject, FileStructure];
11
11
  /**
@@ -4,6 +4,28 @@ import path from 'node:path';
4
4
  import { tryThat } from '@silverhand/essentials';
5
5
  import ts from 'typescript';
6
6
  import { consoleLog } from '../../../utils.js';
7
+ const getValue = (property) => {
8
+ if (ts.isStringLiteral(property.initializer) ||
9
+ ts.isNoSubstitutionTemplateLiteral(property.initializer)) {
10
+ return property.initializer.getText();
11
+ }
12
+ consoleLog.fatal('Unsupported property:', property.getFullText());
13
+ };
14
+ const hasLeadingUntranslatedComment = (fullText) => {
15
+ const commentRanges = ts.getLeadingCommentRanges(fullText, 0);
16
+ if (commentRanges?.[0]) {
17
+ if (commentRanges.length > 1) {
18
+ consoleLog.fatal('Multiple comments found for property.');
19
+ }
20
+ const commentRange = commentRanges[0];
21
+ const comment = fullText.slice(commentRange.pos, commentRange.end);
22
+ if (comment.includes(untranslatedMark)) {
23
+ return true;
24
+ }
25
+ consoleLog.warn('Unsupported comment:', comment);
26
+ }
27
+ return false;
28
+ };
7
29
  const untranslatedMark = 'UNTRANSLATED';
8
30
  /**
9
31
  * Given a entrypoint file path of a language, parse the nested object of
@@ -94,33 +116,27 @@ export const parseLocaleFiles = (filePath) => {
94
116
  fileStructure[key] = { structure };
95
117
  /* eslint-enable @silverhand/fp/no-mutation */
96
118
  }
97
- else if (ts.isStringLiteral(property.initializer) ||
98
- ts.isNoSubstitutionTemplateLiteral(property.initializer)) {
99
- const value = property.initializer.getText();
100
- const commentRanges = ts.getLeadingCommentRanges(property.getFullText(), 0);
101
- if (commentRanges?.[0]) {
102
- if (commentRanges.length > 1) {
103
- consoleLog.fatal('Multiple comments found for property:', property);
104
- }
105
- const commentRange = commentRanges[0];
106
- const comment = property.getFullText().slice(commentRange.pos, commentRange.end);
107
- if (comment.includes(untranslatedMark)) {
108
- // eslint-disable-next-line @silverhand/fp/no-mutation
109
- nestedObject[key] = [value, false];
110
- }
111
- else {
112
- // eslint-disable-next-line @silverhand/fp/no-mutation
113
- nestedObject[key] = [value, true];
114
- consoleLog.warn('Unsupported comment:', comment);
119
+ else if (ts.isArrayLiteralExpression(property.initializer)) {
120
+ const elements = property.initializer.elements.map((element) => {
121
+ if (ts.isStringLiteralLike(element)) {
122
+ return [
123
+ [element.getText(), !hasLeadingUntranslatedComment(element.getFullText())],
124
+ {},
125
+ ];
115
126
  }
116
- }
117
- else {
118
- // eslint-disable-next-line @silverhand/fp/no-mutation
119
- nestedObject[key] = [value, true];
120
- }
127
+ return traverseNode(element, {}, {});
128
+ });
129
+ /* eslint-disable @silverhand/fp/no-mutation */
130
+ nestedObject[key] = Object.fromEntries(elements.map(([phrases], index) => [index, phrases]));
131
+ fileStructure[key] = {
132
+ structure: Object.fromEntries(elements.map(([, structure], index) => [index, { structure }])),
133
+ };
134
+ /* eslint-enable @silverhand/fp/no-mutation */
121
135
  }
122
136
  else {
123
- consoleLog.fatal('Unsupported property:', property);
137
+ const value = getValue(property);
138
+ // eslint-disable-next-line @silverhand/fp/no-mutation
139
+ nestedObject[key] = [value, !hasLeadingUntranslatedComment(property.getFullText())];
124
140
  }
125
141
  }
126
142
  };
@@ -210,10 +226,13 @@ const traverseNode = async (baseline, targetObject, targetFilePath, isRoot = fal
210
226
  await fs.appendFile(targetFilePath, `const ${identifier} = {\n`);
211
227
  // Recursively traverse the nested object of phrases and the file structure
212
228
  // of the baseline language
229
+ // eslint-disable-next-line complexity
213
230
  const traverseObject = async (baseline, targetObject, tabSize) => {
214
231
  const [baselineObject, baselineStructure] = baseline;
232
+ const isBaselineArray = 0 in baselineObject;
215
233
  for (const [key, value] of Object.entries(baselineObject)) {
216
234
  const existingValue = targetObject[key];
235
+ const keyOutput = isBaselineArray ? '' : `${key}: `;
217
236
  if (Array.isArray(value)) {
218
237
  const [phrase] = value;
219
238
  // If the key exists in the target language and the value is a translated
@@ -221,11 +240,11 @@ const traverseNode = async (baseline, targetObject, targetFilePath, isRoot = fal
221
240
  // of the baseline language and add a comment to indicate that the phrase
222
241
  // is untranslated to help identify missing translations.
223
242
  if (Array.isArray(existingValue) && existingValue[1]) {
224
- await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${key}: ${existingValue[0]},\n`);
243
+ await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${keyOutput}${existingValue[0]},\n`);
225
244
  }
226
245
  else {
227
246
  await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}/** ${untranslatedMark} */\n`);
228
- await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${key}: ${phrase},\n`);
247
+ await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${keyOutput}${phrase},\n`);
229
248
  }
230
249
  }
231
250
  // Not a string, treat it as a nested object or an import
@@ -236,11 +255,12 @@ const traverseNode = async (baseline, targetObject, targetFilePath, isRoot = fal
236
255
  await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${key},\n`);
237
256
  await traverseNode([value, keyStructure.structure], Array.isArray(existingValue) ? {} : existingValue ?? {}, path.join(targetDirectory, keyStructure.filePath));
238
257
  }
239
- // Otherwise, treat it as a nested object.
258
+ // Otherwise, treat it as a nested object or array.
240
259
  else {
241
- await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${key}: {\n`);
260
+ const isValueArray = 0 in value;
261
+ await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${keyOutput}${isValueArray ? '[' : '{'}\n`);
242
262
  await traverseObject([value, keyStructure?.structure ?? {}], Array.isArray(existingValue) ? {} : existingValue ?? {}, tabSize + 2);
243
- await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}},\n`);
263
+ await fs.appendFile(targetFilePath, `${' '.repeat(tabSize)}${isValueArray ? ']' : '}'},\n`);
244
264
  }
245
265
  }
246
266
  }
@@ -8,7 +8,7 @@ const sync = {
8
8
  command: ['sync'],
9
9
  describe: 'Translate all untranslated phrases using ChatGPT. Note the environment variable `OPENAI_API_KEY` is required to work.',
10
10
  handler: async ({ path: inputPath }) => {
11
- const queue = new PQueue({ concurrency: 1 });
11
+ const queue = new PQueue({ concurrency: 5 });
12
12
  const instancePath = await inquireInstancePath(inputPath);
13
13
  for (const languageTag of Object.keys(languages)) {
14
14
  if (languageTag === baseLanguage) {
@@ -1,6 +1,6 @@
1
1
  export const packageJson = {
2
2
  "name": "@logto/cli",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -44,11 +44,11 @@ export const packageJson = {
44
44
  },
45
45
  "dependencies": {
46
46
  "@logto/connector-kit": "workspace:^2.0.0",
47
- "@logto/core-kit": "workspace:^2.2.0",
47
+ "@logto/core-kit": "workspace:^2.2.1",
48
48
  "@logto/language-kit": "workspace:^1.0.0",
49
- "@logto/phrases": "workspace:^1.7.0",
50
- "@logto/phrases-experience": "workspace:^1.4.0",
51
- "@logto/schemas": "workspace:1.11.0",
49
+ "@logto/phrases": "workspace:^1.8.0",
50
+ "@logto/phrases-experience": "workspace:^1.5.0",
51
+ "@logto/schemas": "workspace:1.12.0",
52
52
  "@logto/shared": "workspace:^3.0.0",
53
53
  "@silverhand/essentials": "^2.8.4",
54
54
  "chalk": "^5.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/cli",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -29,11 +29,11 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@logto/connector-kit": "^2.0.0",
32
- "@logto/core-kit": "^2.2.0",
32
+ "@logto/core-kit": "^2.2.1",
33
33
  "@logto/language-kit": "^1.0.0",
34
- "@logto/phrases": "^1.7.0",
35
- "@logto/phrases-experience": "^1.4.0",
36
- "@logto/schemas": "1.11.0",
34
+ "@logto/phrases": "^1.8.0",
35
+ "@logto/phrases-experience": "^1.5.0",
36
+ "@logto/schemas": "1.12.0",
37
37
  "@logto/shared": "^3.0.0",
38
38
  "@silverhand/essentials": "^2.8.4",
39
39
  "chalk": "^5.0.0",