@tolgee/cli 1.0.0-prerelease.1
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/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/client/errors.js +36 -0
- package/dist/client/export.js +23 -0
- package/dist/client/import.js +61 -0
- package/dist/client/index.js +79 -0
- package/dist/client/internal/requester.js +145 -0
- package/dist/client/internal/schema.generated.js +6 -0
- package/dist/client/internal/schema.utils.js +2 -0
- package/dist/client/languages.js +16 -0
- package/dist/client/project.js +44 -0
- package/dist/commands/extract/check.js +41 -0
- package/dist/commands/extract/print.js +51 -0
- package/dist/commands/extract.js +14 -0
- package/dist/commands/login.js +49 -0
- package/dist/commands/pull.js +38 -0
- package/dist/commands/push.js +122 -0
- package/dist/commands/sync/compare.js +48 -0
- package/dist/commands/sync/sync.js +110 -0
- package/dist/commands/sync/syncUtils.js +64 -0
- package/dist/config/credentials.js +125 -0
- package/dist/config/tolgeerc.js +64 -0
- package/dist/constants.js +18 -0
- package/dist/extractor/index.js +2 -0
- package/dist/extractor/machines/react.js +728 -0
- package/dist/extractor/machines/shared/comments.js +82 -0
- package/dist/extractor/machines/shared/properties.js +226 -0
- package/dist/extractor/presets/react.js +29 -0
- package/dist/extractor/runner.js +39 -0
- package/dist/extractor/tokenizer.js +102 -0
- package/dist/extractor/warnings.js +89 -0
- package/dist/extractor/worker.js +82 -0
- package/dist/index.js +151 -0
- package/dist/options.js +37 -0
- package/dist/utils/ask.js +34 -0
- package/dist/utils/configPath.js +17 -0
- package/dist/utils/deferred.js +11 -0
- package/dist/utils/logger.js +93 -0
- package/dist/utils/moduleLoader.js +43 -0
- package/dist/utils/overwriteDir.js +38 -0
- package/dist/utils/zip.js +83 -0
- package/extractor.d.ts +21 -0
- package/package.json +98 -0
- package/textmate/THIRD_PARTY_NOTICE +31 -0
- package/textmate/TypeScript.tmLanguage +9728 -0
- package/textmate/TypeScriptReact.tmLanguage +10158 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022-Present Tolgee s.r.o.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Tolgee CLI 🐁
|
2
|
+
|
3
|
+
An experimental 🧪 command line tool to interact with Tolgee directly from your terminal.
|
4
|
+
|
5
|
+
The CLI lets you pull strings from the Tolgee platform into your projects, push local strings to the Tolgee platform,
|
6
|
+
extract strings from your code, and much more!
|
7
|
+
|
8
|
+
- Pull requests welcome! 🤩
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
The Tolgee CLI is published as a NPM package. You simply need to install it, and you're good to go!
|
12
|
+
```sh
|
13
|
+
npm i --global @tolgee/cli
|
14
|
+
```
|
15
|
+
|
16
|
+
> **Warning**: The Tolgee CLI is currently experimental and subject to bugs. Breaking changes may happen before stable release!
|
17
|
+
>
|
18
|
+
> Help us reach stable version faster by reporting any bug you encounter on the [issue tracker](https://github.com/tolgee/tolgee-cli/issues/new?labels=bug).
|
19
|
+
> Feedback is also greatly appreciated!
|
20
|
+
|
21
|
+
See our [documentation](https://tolgee.io/tolgee-cli/installation) for more information.
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
Once installed, you'll have access to the `tolgee` command. Run `tolgee help` to see all the supported commands, their
|
25
|
+
options and arguments.
|
26
|
+
|
27
|
+
Make sure to give the [docs](https://tolgee.io/tolgee-cli/usage) a look!
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
Contributions are welcome! Check out [HACKING.md](HACKING.md) for some information about the project internals and
|
31
|
+
information about the workflow.
|
32
|
+
|
33
|
+
----
|
34
|
+
🧀
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.HttpError = void 0;
|
4
|
+
class HttpError extends Error {
|
5
|
+
request;
|
6
|
+
response;
|
7
|
+
constructor(request, response, options) {
|
8
|
+
super(response.statusText, options);
|
9
|
+
this.request = request;
|
10
|
+
this.response = response;
|
11
|
+
}
|
12
|
+
getErrorText() {
|
13
|
+
// Unauthorized
|
14
|
+
if (this.response.status === 400) {
|
15
|
+
return 'Invalid request data.';
|
16
|
+
}
|
17
|
+
// Unauthorized
|
18
|
+
if (this.response.status === 401) {
|
19
|
+
return 'Missing or invalid authentication token.';
|
20
|
+
}
|
21
|
+
// Forbidden
|
22
|
+
if (this.response.status === 403) {
|
23
|
+
return 'You are not allowed to perform this operation.';
|
24
|
+
}
|
25
|
+
// Service Unavailable
|
26
|
+
if (this.response.status === 503) {
|
27
|
+
return 'API is temporarily unavailable. Please try again later.';
|
28
|
+
}
|
29
|
+
// Server error
|
30
|
+
if (this.response.status >= 500) {
|
31
|
+
return `API reported a server error (${this.response.status}). Please try again later.`;
|
32
|
+
}
|
33
|
+
return `Unknown error (HTTP ${this.response.status})`;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
exports.HttpError = HttpError;
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
class ExportClient {
|
4
|
+
requester;
|
5
|
+
constructor(requester) {
|
6
|
+
this.requester = requester;
|
7
|
+
}
|
8
|
+
async export(req) {
|
9
|
+
return this.requester.requestBlob({
|
10
|
+
method: 'POST',
|
11
|
+
path: `${this.requester.projectUrl}/export`,
|
12
|
+
body: { ...req, zip: true },
|
13
|
+
});
|
14
|
+
}
|
15
|
+
async exportSingle(req) {
|
16
|
+
return this.requester.requestJson({
|
17
|
+
method: 'POST',
|
18
|
+
path: `${this.requester.projectUrl}/export`,
|
19
|
+
body: { ...req, zip: false },
|
20
|
+
});
|
21
|
+
}
|
22
|
+
}
|
23
|
+
exports.default = ExportClient;
|
@@ -0,0 +1,61 @@
|
|
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 form_data_1 = __importDefault(require("form-data"));
|
7
|
+
const errors_1 = require("./errors");
|
8
|
+
class ImportClient {
|
9
|
+
requester;
|
10
|
+
constructor(requester) {
|
11
|
+
this.requester = requester;
|
12
|
+
}
|
13
|
+
async addFiles(req) {
|
14
|
+
const body = new form_data_1.default();
|
15
|
+
for (const file of req.files) {
|
16
|
+
body.append('files', file.data, { filepath: file.name });
|
17
|
+
}
|
18
|
+
return this.requester.requestJson({
|
19
|
+
method: 'POST',
|
20
|
+
path: `${this.requester.projectUrl}/import`,
|
21
|
+
body: body,
|
22
|
+
});
|
23
|
+
}
|
24
|
+
async conflictsOverrideAll(languageId) {
|
25
|
+
await this.requester.requestVoid({
|
26
|
+
method: 'PUT',
|
27
|
+
path: `${this.requester.projectUrl}/import/result/languages/${languageId}/resolve-all/set-override`,
|
28
|
+
});
|
29
|
+
}
|
30
|
+
async conflictsKeepExistingAll(languageId) {
|
31
|
+
await this.requester.requestVoid({
|
32
|
+
method: 'PUT',
|
33
|
+
path: `${this.requester.projectUrl}/import/result/languages/${languageId}/resolve-all/set-keep-existing`,
|
34
|
+
});
|
35
|
+
}
|
36
|
+
async applyImport(req) {
|
37
|
+
await this.requester.requestVoid({
|
38
|
+
method: 'PUT',
|
39
|
+
path: `${this.requester.projectUrl}/import/apply`,
|
40
|
+
query: { forceMode: req?.forceMode },
|
41
|
+
});
|
42
|
+
}
|
43
|
+
async deleteImport() {
|
44
|
+
await this.requester.requestVoid({
|
45
|
+
method: 'DELETE',
|
46
|
+
path: `${this.requester.projectUrl}/import`,
|
47
|
+
});
|
48
|
+
}
|
49
|
+
/* Helper functions */
|
50
|
+
async deleteImportIfExists() {
|
51
|
+
try {
|
52
|
+
await this.deleteImport();
|
53
|
+
}
|
54
|
+
catch (e) {
|
55
|
+
if (e instanceof errors_1.HttpError && e.response.status === 404)
|
56
|
+
return;
|
57
|
+
throw e;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
exports.default = ImportClient;
|
@@ -0,0 +1,79 @@
|
|
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 base32_decode_1 = __importDefault(require("base32-decode"));
|
7
|
+
const requester_1 = __importDefault(require("./internal/requester"));
|
8
|
+
const project_1 = __importDefault(require("./project"));
|
9
|
+
const languages_1 = __importDefault(require("./languages"));
|
10
|
+
const import_1 = __importDefault(require("./import"));
|
11
|
+
const export_1 = __importDefault(require("./export"));
|
12
|
+
const constants_1 = require("../constants");
|
13
|
+
class RestClient {
|
14
|
+
params;
|
15
|
+
requester;
|
16
|
+
project;
|
17
|
+
languages;
|
18
|
+
import;
|
19
|
+
export;
|
20
|
+
constructor(params) {
|
21
|
+
this.params = params;
|
22
|
+
this.requester = new requester_1.default(params);
|
23
|
+
this.project = new project_1.default(this.requester);
|
24
|
+
this.languages = new languages_1.default(this.requester);
|
25
|
+
this.import = new import_1.default(this.requester);
|
26
|
+
this.export = new export_1.default(this.requester);
|
27
|
+
}
|
28
|
+
async getProjectApiKeyInformation() {
|
29
|
+
return this.requester.requestJson({
|
30
|
+
path: '/v2/api-keys/current',
|
31
|
+
method: 'GET',
|
32
|
+
});
|
33
|
+
}
|
34
|
+
getProjectId() {
|
35
|
+
return this.params.projectId;
|
36
|
+
}
|
37
|
+
static projectIdFromKey(key) {
|
38
|
+
const keyBuffer = (0, base32_decode_1.default)(key.slice(constants_1.API_KEY_PAK_PREFIX.length).toUpperCase(), 'RFC4648');
|
39
|
+
const decoded = Buffer.from(keyBuffer).toString('utf8');
|
40
|
+
return Number(decoded.split('_')[0]);
|
41
|
+
}
|
42
|
+
static getProjectApiKeyInformation(api, key) {
|
43
|
+
return new requester_1.default({ apiUrl: api, apiKey: key }).requestJson({
|
44
|
+
path: '/v2/api-keys/current',
|
45
|
+
method: 'GET',
|
46
|
+
});
|
47
|
+
}
|
48
|
+
static getPersonalAccessTokenInformation(api, key) {
|
49
|
+
return new requester_1.default({ apiUrl: api, apiKey: key }).requestJson({
|
50
|
+
path: '/v2/pats/current',
|
51
|
+
method: 'GET',
|
52
|
+
});
|
53
|
+
}
|
54
|
+
static async getApiKeyInformation(api, key) {
|
55
|
+
if (key.startsWith(constants_1.API_KEY_PAK_PREFIX)) {
|
56
|
+
const info = await RestClient.getProjectApiKeyInformation(api, key);
|
57
|
+
const username = info.userFullName || info.username || '<unknown user>';
|
58
|
+
return {
|
59
|
+
type: 'PAK',
|
60
|
+
key: key,
|
61
|
+
username: username,
|
62
|
+
project: {
|
63
|
+
id: info.projectId,
|
64
|
+
name: info.projectName,
|
65
|
+
},
|
66
|
+
expires: info.expiresAt ?? 0,
|
67
|
+
};
|
68
|
+
}
|
69
|
+
const info = await RestClient.getPersonalAccessTokenInformation(api, key);
|
70
|
+
const username = info.user.name || info.user.username;
|
71
|
+
return {
|
72
|
+
type: 'PAT',
|
73
|
+
key: key,
|
74
|
+
username: username,
|
75
|
+
expires: info.expiresAt ?? 0,
|
76
|
+
};
|
77
|
+
}
|
78
|
+
}
|
79
|
+
exports.default = RestClient;
|
@@ -0,0 +1,145 @@
|
|
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 undici_1 = require("undici");
|
7
|
+
const undici_2 = require("undici");
|
8
|
+
const form_data_1 = __importDefault(require("form-data"));
|
9
|
+
const errors_1 = require("../errors");
|
10
|
+
const logger_1 = require("../../utils/logger");
|
11
|
+
const constants_1 = require("../../constants");
|
12
|
+
class Requester {
|
13
|
+
params;
|
14
|
+
constructor(params) {
|
15
|
+
this.params = params;
|
16
|
+
}
|
17
|
+
get projectUrl() {
|
18
|
+
return `/v2/projects/${this.params.projectId}`;
|
19
|
+
}
|
20
|
+
/**
|
21
|
+
* Performs an HTTP request to the API
|
22
|
+
*
|
23
|
+
* @param req Request data
|
24
|
+
* @returns The response
|
25
|
+
*/
|
26
|
+
async request(req) {
|
27
|
+
const url = new URL(req.path, this.params.apiUrl);
|
28
|
+
if (req.query) {
|
29
|
+
for (const param in req.query) {
|
30
|
+
if (param in req.query) {
|
31
|
+
const val = req.query[param];
|
32
|
+
if (val !== undefined) {
|
33
|
+
if (Array.isArray(val)) {
|
34
|
+
for (const v of val) {
|
35
|
+
url.searchParams.append(param, String(v));
|
36
|
+
}
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
url.searchParams.set(param, String(val));
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
const headers = {
|
46
|
+
...(req.headers || {}),
|
47
|
+
'user-agent': constants_1.USER_AGENT,
|
48
|
+
'x-api-key': this.params.apiKey,
|
49
|
+
};
|
50
|
+
let body = undefined;
|
51
|
+
if (req.body) {
|
52
|
+
if (req.body instanceof form_data_1.default) {
|
53
|
+
const header = `multipart/form-data; boundary=${req.body.getBoundary()}`;
|
54
|
+
headers['content-type'] = header;
|
55
|
+
body = req.body.getBuffer();
|
56
|
+
}
|
57
|
+
else {
|
58
|
+
headers['content-type'] = 'application/json';
|
59
|
+
body = JSON.stringify(req.body);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
const request = new undici_1.Request(url, {
|
63
|
+
method: req.method,
|
64
|
+
headers: headers,
|
65
|
+
body: body,
|
66
|
+
});
|
67
|
+
(0, logger_1.debug)(`[HTTP] Requesting: ${request.method} ${request.url}`);
|
68
|
+
const response = await (0, undici_2.fetch)(request);
|
69
|
+
(0, logger_1.debug)(`[HTTP] ${request.method} ${request.url} -> ${response.status} ${response.statusText}`);
|
70
|
+
if (!response.ok)
|
71
|
+
throw new errors_1.HttpError(request, response);
|
72
|
+
return response;
|
73
|
+
}
|
74
|
+
/**
|
75
|
+
* Performs an HTTP request to the API and returns the result as a JSON object
|
76
|
+
*
|
77
|
+
* @param req Request data
|
78
|
+
* @returns The response data
|
79
|
+
*/
|
80
|
+
async requestJson(req) {
|
81
|
+
return this.request(req).then((r) => r.json());
|
82
|
+
}
|
83
|
+
/**
|
84
|
+
* Performs an HTTP request to the API and returns the result as a Blob
|
85
|
+
*
|
86
|
+
* @param req Request data
|
87
|
+
* @returns The response blob
|
88
|
+
*/
|
89
|
+
async requestBlob(req) {
|
90
|
+
return this.request(req).then((r) => r.blob());
|
91
|
+
}
|
92
|
+
/**
|
93
|
+
* Performs an HTTP request to the API and forces the consumption of the body, according to recommendations by Undici
|
94
|
+
*
|
95
|
+
* @see https://github.com/nodejs/undici#garbage-collection
|
96
|
+
* @param req Request data
|
97
|
+
*/
|
98
|
+
async requestVoid(req) {
|
99
|
+
const res = await this.request(req);
|
100
|
+
if (res.body) {
|
101
|
+
for await (const _chunk of res.body)
|
102
|
+
;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
/**
|
106
|
+
* Performs an HTTP request to the API to a resource which is paginated.
|
107
|
+
* The returned result is a view with helpers to get next (or previous) data.
|
108
|
+
*
|
109
|
+
* @param req Request data
|
110
|
+
*/
|
111
|
+
async requestPaginatedResource(req) {
|
112
|
+
const res = await this.requestJson(req);
|
113
|
+
const _this = this;
|
114
|
+
const view = {
|
115
|
+
data: res._embedded,
|
116
|
+
page: res.page,
|
117
|
+
hasNext() {
|
118
|
+
return !!res._links.next?.href;
|
119
|
+
},
|
120
|
+
hasPrevious() {
|
121
|
+
return !!res._links.prev?.href;
|
122
|
+
},
|
123
|
+
async next() {
|
124
|
+
if (!this.hasNext())
|
125
|
+
return null;
|
126
|
+
return _this.requestPaginatedResource({
|
127
|
+
...req,
|
128
|
+
path: res._links.next.href,
|
129
|
+
query: undefined,
|
130
|
+
});
|
131
|
+
},
|
132
|
+
async previous() {
|
133
|
+
if (!this.hasPrevious())
|
134
|
+
return null;
|
135
|
+
return _this.requestPaginatedResource({
|
136
|
+
...req,
|
137
|
+
path: res._links.prev.href,
|
138
|
+
query: undefined,
|
139
|
+
});
|
140
|
+
},
|
141
|
+
};
|
142
|
+
return view;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
exports.default = Requester;
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
class LanguagesClient {
|
4
|
+
requester;
|
5
|
+
constructor(requester) {
|
6
|
+
this.requester = requester;
|
7
|
+
}
|
8
|
+
async getLanguages(req) {
|
9
|
+
return this.requester.requestPaginatedResource({
|
10
|
+
method: 'GET',
|
11
|
+
path: `${this.requester.projectUrl}/languages`,
|
12
|
+
query: req,
|
13
|
+
});
|
14
|
+
}
|
15
|
+
}
|
16
|
+
exports.default = LanguagesClient;
|
@@ -0,0 +1,44 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
class ProjectClient {
|
4
|
+
requester;
|
5
|
+
constructor(requester) {
|
6
|
+
this.requester = requester;
|
7
|
+
}
|
8
|
+
async fetchProjectInformation() {
|
9
|
+
return this.requester.requestJson({
|
10
|
+
method: 'GET',
|
11
|
+
path: this.requester.projectUrl,
|
12
|
+
});
|
13
|
+
}
|
14
|
+
async fetchAllKeys() {
|
15
|
+
return this.requester
|
16
|
+
.requestJson({
|
17
|
+
method: 'GET',
|
18
|
+
path: `${this.requester.projectUrl}/all-keys`,
|
19
|
+
})
|
20
|
+
.then((r) => r._embedded?.keys || []);
|
21
|
+
}
|
22
|
+
async createKey(key) {
|
23
|
+
return this.requester.requestJson({
|
24
|
+
method: 'POST',
|
25
|
+
path: `${this.requester.projectUrl}/keys`,
|
26
|
+
body: key,
|
27
|
+
});
|
28
|
+
}
|
29
|
+
async createBulkKey(keys) {
|
30
|
+
return this.requester.requestVoid({
|
31
|
+
method: 'POST',
|
32
|
+
path: `${this.requester.projectUrl}/keys/import`,
|
33
|
+
body: { keys },
|
34
|
+
});
|
35
|
+
}
|
36
|
+
async deleteBulkKeys(keyIds) {
|
37
|
+
return this.requester.requestVoid({
|
38
|
+
method: 'DELETE',
|
39
|
+
path: `${this.requester.projectUrl}/keys`,
|
40
|
+
body: { ids: keyIds },
|
41
|
+
});
|
42
|
+
}
|
43
|
+
}
|
44
|
+
exports.default = ProjectClient;
|
@@ -0,0 +1,41 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const path_1 = require("path");
|
4
|
+
const commander_1 = require("commander");
|
5
|
+
const runner_1 = require("../../extractor/runner");
|
6
|
+
const warnings_1 = require("../../extractor/warnings");
|
7
|
+
const logger_1 = require("../../utils/logger");
|
8
|
+
async function lintHandler(filesPattern) {
|
9
|
+
const opts = this.optsWithGlobals();
|
10
|
+
const extracted = await (0, logger_1.loading)('Analyzing code...', (0, runner_1.extractKeysOfFiles)(filesPattern, opts.extractor));
|
11
|
+
let warningCount = 0;
|
12
|
+
let filesCount = 0;
|
13
|
+
for (const [file, { warnings }] of extracted) {
|
14
|
+
if (warnings.length) {
|
15
|
+
warningCount += warnings.length;
|
16
|
+
filesCount++;
|
17
|
+
const relFile = (0, path_1.relative)(process.cwd(), file);
|
18
|
+
console.log('%s:', relFile);
|
19
|
+
for (const warning of warnings) {
|
20
|
+
if (warning.warning in warnings_1.WarningMessages) {
|
21
|
+
const { name } = warnings_1.WarningMessages[warning.warning];
|
22
|
+
console.log('\tline %d: %s', warning.line, name);
|
23
|
+
}
|
24
|
+
else {
|
25
|
+
console.log('\tline %d: %s', warning.line, warning.warning);
|
26
|
+
}
|
27
|
+
(0, warnings_1.emitGitHubWarning)(warning.warning, file, warning.line);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
if (warningCount !== 0) {
|
32
|
+
console.log();
|
33
|
+
console.log('Total: %d warning%s in %d file%s', warningCount, warningCount !== 1 ? 's' : '', filesCount, filesCount !== 1 ? 's' : '');
|
34
|
+
process.exit(1);
|
35
|
+
}
|
36
|
+
console.log('No issues found.');
|
37
|
+
}
|
38
|
+
exports.default = new commander_1.Command('check')
|
39
|
+
.description('Checks if the keys can be extracted automatically, and reports problems if any')
|
40
|
+
.argument('<pattern>', 'File pattern to include (hint: make sure to escape it in quotes, or your shell might attempt to unroll some tokens like *)')
|
41
|
+
.action(lintHandler);
|
@@ -0,0 +1,51 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const path_1 = require("path");
|
4
|
+
const commander_1 = require("commander");
|
5
|
+
const runner_1 = require("../../extractor/runner");
|
6
|
+
const warnings_1 = require("../../extractor/warnings");
|
7
|
+
const logger_1 = require("../../utils/logger");
|
8
|
+
async function printHandler(filesPattern) {
|
9
|
+
const opts = this.optsWithGlobals();
|
10
|
+
const extracted = await (0, logger_1.loading)('Analyzing code...', (0, runner_1.extractKeysOfFiles)(filesPattern, opts.extractor));
|
11
|
+
let warningCount = 0;
|
12
|
+
const keySet = new Set();
|
13
|
+
for (const [file, { keys, warnings }] of extracted) {
|
14
|
+
if (keys.length) {
|
15
|
+
const relFile = (0, path_1.relative)(process.cwd(), file);
|
16
|
+
console.log('%d key%s found in %s:', keys.length, keys.length !== 1 ? 's' : '', relFile);
|
17
|
+
for (const key of keys) {
|
18
|
+
keySet.add(key);
|
19
|
+
console.log('\tline %d: %s', key.line, key.keyName);
|
20
|
+
if (key.namespace) {
|
21
|
+
console.log('\t\tnamespace: %s', key.namespace);
|
22
|
+
}
|
23
|
+
if (key.defaultValue) {
|
24
|
+
console.log('\t\tdefault: %s', key.defaultValue);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
if (warnings.length) {
|
29
|
+
warningCount += warnings.length;
|
30
|
+
console.log('%d warning%s %s emitted during extraction:', warnings.length, warnings.length !== 1 ? 's' : '', warnings.length !== 1 ? 'were' : 'was');
|
31
|
+
for (const warning of warnings) {
|
32
|
+
if (warning.warning in warnings_1.WarningMessages) {
|
33
|
+
const { name } = warnings_1.WarningMessages[warning.warning];
|
34
|
+
console.log('\tline %d: %s', warning.line, name);
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
console.log('\tline %d: %s', warning.line, warning.warning);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
if (keys.length || warnings.length) {
|
42
|
+
console.log();
|
43
|
+
}
|
44
|
+
}
|
45
|
+
console.log('Total unique keys found: %d', keySet.size);
|
46
|
+
console.log('Total warnings: %d', warningCount);
|
47
|
+
}
|
48
|
+
exports.default = new commander_1.Command('print')
|
49
|
+
.description('Prints extracted data to the console')
|
50
|
+
.argument('<pattern>', 'File glob pattern to include (hint: make sure to escape it in quotes, or your shell might attempt to unroll some tokens like *)')
|
51
|
+
.action(printHandler);
|
@@ -0,0 +1,14 @@
|
|
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 commander_1 = require("commander");
|
7
|
+
const print_1 = __importDefault(require("./extract/print"));
|
8
|
+
const check_1 = __importDefault(require("./extract/check"));
|
9
|
+
const options_1 = require("../options");
|
10
|
+
exports.default = new commander_1.Command('extract')
|
11
|
+
.description('Extracts strings from your projects')
|
12
|
+
.addOption(options_1.EXTRACTOR)
|
13
|
+
.addCommand(print_1.default)
|
14
|
+
.addCommand(check_1.default);
|
@@ -0,0 +1,49 @@
|
|
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
|
+
exports.Logout = exports.Login = void 0;
|
7
|
+
const commander_1 = require("commander");
|
8
|
+
const client_1 = __importDefault(require("../client"));
|
9
|
+
const errors_1 = require("../client/errors");
|
10
|
+
const credentials_1 = require("../config/credentials");
|
11
|
+
const logger_1 = require("../utils/logger");
|
12
|
+
async function loginHandler(key) {
|
13
|
+
const opts = this.optsWithGlobals();
|
14
|
+
let keyInfo;
|
15
|
+
try {
|
16
|
+
keyInfo = await client_1.default.getApiKeyInformation(opts.apiUrl, key);
|
17
|
+
}
|
18
|
+
catch (e) {
|
19
|
+
if (e instanceof errors_1.HttpError && e.response.status === 403) {
|
20
|
+
(0, logger_1.error)("Couldn't log in: the API key you provided is invalid.");
|
21
|
+
process.exit(1);
|
22
|
+
}
|
23
|
+
throw e;
|
24
|
+
}
|
25
|
+
await (0, credentials_1.saveApiKey)(opts.apiUrl, keyInfo);
|
26
|
+
(0, logger_1.success)(keyInfo.type === 'PAK'
|
27
|
+
? `Logged in as ${keyInfo.username} on ${opts.apiUrl.hostname} for project ${keyInfo.project.name} (#${keyInfo.project.id}). Welcome back!`
|
28
|
+
: `Logged in as ${keyInfo.username} on ${opts.apiUrl.hostname}. Welcome back!`);
|
29
|
+
}
|
30
|
+
async function logoutHandler() {
|
31
|
+
const opts = this.optsWithGlobals();
|
32
|
+
if (opts.all) {
|
33
|
+
await (0, credentials_1.clearAuthStore)();
|
34
|
+
(0, logger_1.success)("You've been logged out of all Tolgee instances you were logged in.");
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
await (0, credentials_1.removeApiKeys)(opts.apiUrl);
|
38
|
+
(0, logger_1.success)(`You're now logged out of ${opts.apiUrl.hostname}.`);
|
39
|
+
}
|
40
|
+
exports.Login = new commander_1.Command()
|
41
|
+
.name('login')
|
42
|
+
.description('Login to Tolgee with an API key. You can be logged into multiple Tolgee instances at the same time by using --api-url')
|
43
|
+
.argument('<API Key>', 'The API key. Can be either a personal access token, or a project key')
|
44
|
+
.action(loginHandler);
|
45
|
+
exports.Logout = new commander_1.Command()
|
46
|
+
.name('logout')
|
47
|
+
.description('Logs out of Tolgee')
|
48
|
+
.option('--all', "Log out of *ALL* Tolgee instances you're logged into")
|
49
|
+
.action(logoutHandler);
|
@@ -0,0 +1,38 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const commander_1 = require("commander");
|
4
|
+
const zip_1 = require("../utils/zip");
|
5
|
+
const overwriteDir_1 = require("../utils/overwriteDir");
|
6
|
+
const logger_1 = require("../utils/logger");
|
7
|
+
async function fetchZipBlob(opts) {
|
8
|
+
return opts.client.export.export({
|
9
|
+
format: opts.format,
|
10
|
+
languages: opts.languages,
|
11
|
+
filterState: opts.states,
|
12
|
+
structureDelimiter: opts.delimiter,
|
13
|
+
});
|
14
|
+
}
|
15
|
+
async function pullHandler(path) {
|
16
|
+
const opts = this.optsWithGlobals();
|
17
|
+
await (0, overwriteDir_1.overwriteDir)(path, opts.overwrite);
|
18
|
+
const zipBlob = await (0, logger_1.loading)('Fetching strings from Tolgee...', fetchZipBlob(opts));
|
19
|
+
await (0, logger_1.loading)('Extracting strings...', (0, zip_1.unzipBuffer)(zipBlob, path));
|
20
|
+
(0, logger_1.success)('Done!');
|
21
|
+
}
|
22
|
+
exports.default = new commander_1.Command()
|
23
|
+
.name('pull')
|
24
|
+
.description('Pulls translations to Tolgee')
|
25
|
+
.argument('<path>', 'Destination path where translation files will be stored in')
|
26
|
+
.addOption(new commander_1.Option('-f, --format <format>', 'Format of the exported files')
|
27
|
+
.choices(['JSON', 'XLIFF'])
|
28
|
+
.default('JSON')
|
29
|
+
.argParser((v) => v.toUpperCase()))
|
30
|
+
.option('-l, --languages <languages...>', 'List of languages to pull. Leave unspecified to export them all')
|
31
|
+
.addOption(new commander_1.Option('-s, --states <states...>', 'List of translation states to include. Defaults all except untranslated')
|
32
|
+
.choices(['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'])
|
33
|
+
.argParser((v, a) => [v.toUpperCase(), ...(a || [])]))
|
34
|
+
.addOption(new commander_1.Option('-d, --delimiter', 'Structure delimiter to use. By default, Tolgee interprets `.` as a nested structure. You can change the delimiter, or disable structure formatting by not specifying any value to the option')
|
35
|
+
.default('.')
|
36
|
+
.argParser((v) => v || ''))
|
37
|
+
.option('-o, --overwrite', 'Whether to automatically overwrite existing files. BE CAREFUL, THIS WILL WIPE *ALL* THE CONTENTS OF THE TARGET FOLDER. If unspecified, the user will be prompted interactively, or the command will fail when in non-interactive')
|
38
|
+
.action(pullHandler);
|