@tolgee/cli 2.9.0 → 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.
- package/dist/cli.js +116 -94
- package/dist/client/ApiClient.js +40 -32
- package/dist/client/ExportClient.js +24 -11
- package/dist/client/ImportClient.js +25 -16
- package/dist/client/TolgeeClient.js +1 -5
- package/dist/client/errorFromLoadable.js +3 -2
- package/dist/client/getApiKeyInformation.js +16 -6
- package/dist/commands/extract/check.js +38 -27
- package/dist/commands/extract/print.js +46 -35
- package/dist/commands/login.js +39 -26
- package/dist/commands/pull.js +60 -43
- package/dist/commands/push.js +157 -123
- package/dist/commands/sync/compare.js +43 -31
- package/dist/commands/sync/sync.js +118 -99
- package/dist/commands/sync/syncUtils.js +2 -1
- package/dist/commands/tag.js +52 -38
- package/dist/config/credentials.js +110 -93
- package/dist/config/tolgeerc.js +51 -35
- package/dist/extractor/extractor.js +45 -31
- package/dist/extractor/parser/extractComment.js +1 -1
- package/dist/extractor/parser/generateReport.js +8 -6
- package/dist/extractor/parser/iterator.js +2 -1
- package/dist/extractor/parser/mergerMachine.js +2 -11
- package/dist/extractor/parser/nodeUtils.js +1 -1
- package/dist/extractor/parser/parser.js +4 -2
- package/dist/extractor/parser/rules/tNsSourceGeneral.js +1 -1
- package/dist/extractor/parser/tree/getTranslateProps.js +21 -16
- package/dist/extractor/parser/tree/getValue.js +1 -1
- package/dist/extractor/parser/tree/parseTag.js +1 -1
- package/dist/extractor/parserNgx/ParserNgx.js +1 -3
- package/dist/extractor/parserNgx/ngxMapper.js +2 -1
- package/dist/extractor/parserNgx/ngxTreeTransform.js +3 -2
- package/dist/extractor/parserNgx/rules/translatePipe.js +1 -1
- package/dist/extractor/parserReact/ParserReact.js +1 -3
- package/dist/extractor/parserSvelte/ParserSvelte.js +1 -3
- package/dist/extractor/parserVue/ParserVue.js +1 -3
- package/dist/extractor/parserVue/tokenMergers/hyphenPropsMerger.js +1 -4
- package/dist/extractor/parserVue/vueTreeTransform.js +13 -2
- package/dist/extractor/runner.js +53 -39
- package/dist/extractor/tokenizer.js +50 -35
- package/dist/extractor/visualizers/printTokens.js +2 -1
- package/dist/extractor/visualizers/visualizeRules.js +4 -7
- package/dist/extractor/warnings.js +3 -2
- package/dist/extractor/worker.js +29 -16
- package/dist/options.js +1 -1
- package/dist/utils/apiKeyList.js +31 -19
- package/dist/utils/ask.js +35 -21
- package/dist/utils/checkPathNotAFile.js +22 -11
- package/dist/utils/filesTemplate.js +147 -0
- package/dist/utils/mapImportFormat.js +1 -1
- package/dist/utils/moduleLoader.js +37 -23
- package/dist/utils/prepareDir.js +20 -9
- package/dist/utils/valueToArray.js +8 -0
- package/package.json +2 -2
- package/schema.json +14 -2
@@ -1,16 +1,27 @@
|
|
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 { stat } from 'fs/promises';
|
2
11
|
import { exitWithError } from './logger.js';
|
3
|
-
export
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
12
|
+
export function checkPathNotAFile(path) {
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
14
|
+
try {
|
15
|
+
const stats = yield stat(path);
|
16
|
+
if (!stats.isDirectory()) {
|
17
|
+
exitWithError('The specified path already exists and is not a directory.');
|
18
|
+
}
|
8
19
|
}
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
20
|
+
catch (e) {
|
21
|
+
// Ignore "file doesn't exist" error
|
22
|
+
if (e.code !== 'ENOENT') {
|
23
|
+
throw e;
|
24
|
+
}
|
14
25
|
}
|
15
|
-
}
|
26
|
+
});
|
16
27
|
}
|
@@ -0,0 +1,147 @@
|
|
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
|
+
};
|
10
|
+
import { glob } from 'tinyglobby';
|
11
|
+
import { exitWithError } from './logger.js';
|
12
|
+
const GLOB_EXISTING_DOUBLE_STAR = /(\*\*)/g;
|
13
|
+
const GLOB_EXISTING_STAR = /(\*)/g;
|
14
|
+
const GLOB_EXISTING_ENUM = /\{([^}]*?,[^}]*?)\}/g;
|
15
|
+
const PLACEHOLDER_DOUBLE_ASTERISK = '__double_asterisk';
|
16
|
+
const PLACEHOLDER_ASTERISK = '__asterisk';
|
17
|
+
const PLACEHOLDER_ENUM_PREFIX = '__enum:';
|
18
|
+
export class FileMatcherException extends Error {
|
19
|
+
constructor(message) {
|
20
|
+
super(message);
|
21
|
+
this.name = this.constructor.name;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
function splitToParts(template) {
|
25
|
+
return template.split(/(\{.*?\})/g).filter(Boolean);
|
26
|
+
}
|
27
|
+
function getVariableName(part) {
|
28
|
+
if (part.startsWith('{') && part.endsWith('}')) {
|
29
|
+
return part.substring(1, part.length - 1).trim();
|
30
|
+
}
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
export function sanitizeTemplate(template) {
|
34
|
+
var _a;
|
35
|
+
let value = template;
|
36
|
+
const matchedEnums = [...(((_a = value.match(GLOB_EXISTING_ENUM)) === null || _a === void 0 ? void 0 : _a.values()) || [])];
|
37
|
+
matchedEnums.forEach((val) => {
|
38
|
+
value = value.replace(val, `{${PLACEHOLDER_ENUM_PREFIX}${getVariableName(val)}}`);
|
39
|
+
});
|
40
|
+
value = value.replaceAll(GLOB_EXISTING_DOUBLE_STAR, '{' + PLACEHOLDER_DOUBLE_ASTERISK + '}');
|
41
|
+
value = value.replaceAll(GLOB_EXISTING_STAR, '{' + PLACEHOLDER_ASTERISK + '}');
|
42
|
+
return value;
|
43
|
+
}
|
44
|
+
export function getFileMatcher(file, template) {
|
45
|
+
let fileName = file;
|
46
|
+
const allVariables = {};
|
47
|
+
const templateParts = splitToParts(template);
|
48
|
+
for (const [i, part] of templateParts.entries()) {
|
49
|
+
const variable = getVariableName(part);
|
50
|
+
if (!variable) {
|
51
|
+
if (fileName.startsWith(part)) {
|
52
|
+
fileName = fileName.substring(part.length);
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
throw new FileMatcherException(`Unexpected part "${part}"`);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
const next = templateParts[i + 1];
|
60
|
+
if (next) {
|
61
|
+
const variableEnd = fileName.indexOf(next);
|
62
|
+
if (getVariableName(next) || variableEnd === -1) {
|
63
|
+
throw new FileMatcherException(`Can't have two variables without separator (${part} + ${next})`);
|
64
|
+
}
|
65
|
+
else {
|
66
|
+
allVariables[variable] = fileName.substring(0, variableEnd);
|
67
|
+
fileName = fileName.substring(variableEnd);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
else {
|
71
|
+
allVariables[variable] = fileName;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
const result = { path: file };
|
76
|
+
for (const [variable, value] of Object.entries(allVariables)) {
|
77
|
+
if (variable === 'languageTag') {
|
78
|
+
result.language = value;
|
79
|
+
}
|
80
|
+
else if (variable === 'snakeLanguageTag') {
|
81
|
+
result.language = value.replaceAll('_', '-');
|
82
|
+
}
|
83
|
+
else if (variable === 'androidLanguageTag') {
|
84
|
+
if (value[3] === 'r') {
|
85
|
+
result.language =
|
86
|
+
value.substring(0, 3) + value.substring(4, value.length);
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
result.language = value;
|
90
|
+
}
|
91
|
+
}
|
92
|
+
else if (variable === 'namespace') {
|
93
|
+
result.namespace = value;
|
94
|
+
}
|
95
|
+
else if (variable !== 'extension' &&
|
96
|
+
![PLACEHOLDER_ASTERISK, PLACEHOLDER_DOUBLE_ASTERISK].includes(variable) &&
|
97
|
+
!variable.startsWith(PLACEHOLDER_ENUM_PREFIX)) {
|
98
|
+
throw new FileMatcherException(`Unknown variable "${variable}"`);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return result;
|
102
|
+
}
|
103
|
+
export function getGlobPattern(template) {
|
104
|
+
let value = template.replaceAll(GLOB_EXISTING_DOUBLE_STAR, '{__double_asterisk}');
|
105
|
+
value = value.replaceAll(GLOB_EXISTING_STAR, '{__asterisk}');
|
106
|
+
const parts = splitToParts(value);
|
107
|
+
const globPattern = parts
|
108
|
+
.map((part) => {
|
109
|
+
const variableName = getVariableName(part);
|
110
|
+
if (variableName) {
|
111
|
+
if (variableName === PLACEHOLDER_DOUBLE_ASTERISK) {
|
112
|
+
return '**';
|
113
|
+
}
|
114
|
+
else if (variableName.startsWith(PLACEHOLDER_ENUM_PREFIX)) {
|
115
|
+
return ('{' + variableName.substring(PLACEHOLDER_ENUM_PREFIX.length) + '}');
|
116
|
+
}
|
117
|
+
else {
|
118
|
+
return '*';
|
119
|
+
}
|
120
|
+
}
|
121
|
+
else {
|
122
|
+
return part;
|
123
|
+
}
|
124
|
+
})
|
125
|
+
.join('');
|
126
|
+
return globPattern;
|
127
|
+
}
|
128
|
+
export function findFilesByTemplate(template) {
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
130
|
+
try {
|
131
|
+
const sanitized = sanitizeTemplate(template);
|
132
|
+
const globPattern = getGlobPattern(sanitized);
|
133
|
+
const files = yield glob(globPattern, { onlyFiles: true, absolute: true });
|
134
|
+
return files.map((file) => {
|
135
|
+
return getFileMatcher(file, sanitized);
|
136
|
+
});
|
137
|
+
}
|
138
|
+
catch (e) {
|
139
|
+
if (e instanceof FileMatcherException) {
|
140
|
+
exitWithError(e.message + ` in template ${template}`);
|
141
|
+
}
|
142
|
+
else {
|
143
|
+
throw e;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
});
|
147
|
+
}
|
@@ -1,28 +1,42 @@
|
|
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 { pathToFileURL } from 'url';
|
2
11
|
let jiti;
|
3
12
|
// https://github.com/eslint/eslint/blob/6f37b0747a14dfa9a9e3bdebc5caed1f39b6b0e2/lib/config/config-loader.js#L164-L197
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
function importTypeScript(file) {
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
15
|
+
// @ts-ignore
|
16
|
+
if (!!globalThis.Bun || !!globalThis.Deno) {
|
17
|
+
// We're in an env that natively supports TS
|
18
|
+
return import(file);
|
19
|
+
}
|
20
|
+
if (!jiti) {
|
21
|
+
const { createJiti } = yield import('jiti').catch(() => {
|
22
|
+
throw new Error("The 'jiti' library is required for loading TypeScript extractors. Make sure to install it.");
|
23
|
+
});
|
24
|
+
jiti = createJiti(import.meta.url);
|
25
|
+
}
|
26
|
+
return jiti.import(file);
|
27
|
+
});
|
17
28
|
}
|
18
|
-
export
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
export function loadModule(module) {
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
31
|
+
var _a;
|
32
|
+
if (module.endsWith('.ts')) {
|
33
|
+
return importTypeScript(module);
|
34
|
+
}
|
35
|
+
const fileUrl = pathToFileURL(module);
|
36
|
+
const mdl = yield import(fileUrl.href);
|
37
|
+
if ((_a = mdl.default) === null || _a === void 0 ? void 0 : _a.default) {
|
38
|
+
return mdl.default;
|
39
|
+
}
|
40
|
+
return mdl;
|
41
|
+
});
|
28
42
|
}
|
package/dist/utils/prepareDir.js
CHANGED
@@ -1,12 +1,23 @@
|
|
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 { mkdir, rm } from 'fs/promises';
|
2
11
|
import { existsSync } from 'fs';
|
3
|
-
export
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
export function prepareDir(path, emptyDir) {
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
14
|
+
const exists = existsSync(path);
|
15
|
+
if (emptyDir && exists) {
|
16
|
+
yield rm(path, { recursive: true });
|
17
|
+
}
|
18
|
+
if (!exists || emptyDir) {
|
19
|
+
// Create the directory
|
20
|
+
yield mkdir(path, { recursive: true });
|
21
|
+
}
|
22
|
+
});
|
12
23
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@tolgee/cli",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.10.0",
|
4
4
|
"type": "module",
|
5
5
|
"description": "A tool to interact with the Tolgee Platform through CLI",
|
6
6
|
"repository": {
|
@@ -37,7 +37,7 @@
|
|
37
37
|
"json5": "^2.2.3",
|
38
38
|
"jsonschema": "^1.4.1",
|
39
39
|
"openapi-fetch": "0.13.1",
|
40
|
-
"tinyglobby": "^0.2.
|
40
|
+
"tinyglobby": "^0.2.12",
|
41
41
|
"unescape-js": "^1.1.4",
|
42
42
|
"vscode-oniguruma": "^2.0.1",
|
43
43
|
"vscode-textmate": "^9.1.0",
|
package/schema.json
CHANGED
@@ -42,8 +42,20 @@
|
|
42
42
|
"push": {
|
43
43
|
"type": "object",
|
44
44
|
"properties": {
|
45
|
+
"filesTemplate": {
|
46
|
+
"description": "A template that describes the structure of the local files and their location with file [structure template format](https://docs.tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format).\n\nExample: `./public/{namespace}/{languageTag}.json`",
|
47
|
+
"anyOf": [
|
48
|
+
{
|
49
|
+
"type": "string"
|
50
|
+
},
|
51
|
+
{
|
52
|
+
"type": "array",
|
53
|
+
"items": { "type": "string" }
|
54
|
+
}
|
55
|
+
]
|
56
|
+
},
|
45
57
|
"files": {
|
46
|
-
"description": "Define, which files should be pushed and attach language/namespace to them. By default Tolgee pushes all files specified here, you can filter them by languages and namespaces properties.",
|
58
|
+
"description": "More explicit alternative to `filesTemplate`. Define, which files should be pushed and attach language/namespace to them. By default Tolgee pushes all files specified here, you can filter them by languages and namespaces properties.",
|
47
59
|
"type": "array",
|
48
60
|
"items": { "$ref": "#/$defs/fileMatch" }
|
49
61
|
},
|
@@ -128,7 +140,7 @@
|
|
128
140
|
"type": "boolean"
|
129
141
|
},
|
130
142
|
"fileStructureTemplate": {
|
131
|
-
"description": "Defines exported file structure: https://tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format",
|
143
|
+
"description": "Defines exported file structure: https://docs.tolgee.io/tolgee-cli/push-pull-strings#file-structure-template-format",
|
132
144
|
"type": "string"
|
133
145
|
},
|
134
146
|
"emptyDir": {
|