@tolgee/cli 1.1.0 → 1.1.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.
- package/README.md +11 -8
- package/dist/client/errors.js +1 -5
- package/dist/client/export.js +1 -4
- package/dist/client/import.js +5 -11
- package/dist/client/index.js +17 -23
- package/dist/client/internal/requester.js +14 -20
- package/dist/client/internal/schema.generated.js +1 -2
- package/dist/client/internal/schema.utils.js +1 -2
- package/dist/client/languages.js +1 -4
- package/dist/client/project.js +1 -4
- package/dist/commands/extract/check.js +11 -13
- package/dist/commands/extract/print.js +10 -12
- package/dist/commands/extract.js +8 -13
- package/dist/commands/login.js +16 -22
- package/dist/commands/pull.js +12 -14
- package/dist/commands/push.js +28 -30
- package/dist/commands/sync/compare.js +18 -23
- package/dist/commands/sync/sync.js +34 -39
- package/dist/commands/sync/syncUtils.js +10 -18
- package/dist/config/credentials.js +16 -25
- package/dist/config/tolgeerc.js +11 -14
- package/dist/constants.js +11 -18
- package/dist/extractor/extractor.js +13 -19
- package/dist/extractor/index.js +1 -2
- package/dist/extractor/machines/comments.js +10 -15
- package/dist/extractor/machines/react.js +36 -41
- package/dist/extractor/machines/shared/comments.js +3 -6
- package/dist/extractor/machines/shared/properties.js +13 -15
- package/dist/extractor/machines/svelte.js +43 -48
- package/dist/extractor/runner.js +8 -16
- package/dist/extractor/tokenizer.js +20 -21
- package/dist/extractor/warnings.js +9 -14
- package/dist/extractor/worker.js +23 -28
- package/dist/index.js +53 -58
- package/dist/options.js +14 -17
- package/dist/utils/ask.js +4 -12
- package/dist/utils/configPath.js +10 -10
- package/dist/utils/deferred.js +1 -5
- package/dist/utils/logger.js +8 -19
- package/dist/utils/moduleLoader.js +7 -15
- package/dist/utils/overwriteDir.js +13 -17
- package/dist/utils/zip.js +11 -16
- package/package.json +33 -30
package/dist/commands/push.js
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
const ask_1 = require("../utils/ask");
|
8
|
-
const logger_1 = require("../utils/logger");
|
1
|
+
import { join } from 'path';
|
2
|
+
import { readdir, readFile, stat } from 'fs/promises';
|
3
|
+
import { Command, Option } from 'commander';
|
4
|
+
import { HttpError } from '../client/errors.js';
|
5
|
+
import { askString } from '../utils/ask.js';
|
6
|
+
import { loading, success, warn, error } from '../utils/logger.js';
|
9
7
|
async function readDirectory(directory, base = '') {
|
10
8
|
const files = [];
|
11
|
-
const dir = await
|
9
|
+
const dir = await readdir(directory);
|
12
10
|
for (const file of dir) {
|
13
|
-
const filePath =
|
14
|
-
const fileStat = await
|
11
|
+
const filePath = join(directory, file);
|
12
|
+
const fileStat = await stat(filePath);
|
15
13
|
if (fileStat.isDirectory()) {
|
16
14
|
const dirFiles = await readDirectory(filePath, `${file}/`);
|
17
15
|
files.push(...dirFiles);
|
18
16
|
}
|
19
17
|
else {
|
20
|
-
const blob = await
|
18
|
+
const blob = await readFile(filePath);
|
21
19
|
files.push({ name: base + file, data: blob });
|
22
20
|
}
|
23
21
|
}
|
@@ -39,26 +37,26 @@ async function promptConflicts(opts) {
|
|
39
37
|
const projectId = opts.client.getProjectId();
|
40
38
|
const resolveUrl = new URL(`/projects/${projectId}/import`, opts.apiUrl).href;
|
41
39
|
if (opts.forceMode === 'NO') {
|
42
|
-
|
40
|
+
error(`There are conflicts in the import. You can resolve them and complete the import here: ${resolveUrl}.`);
|
43
41
|
process.exit(1);
|
44
42
|
}
|
45
43
|
if (opts.forceMode) {
|
46
44
|
return opts.forceMode;
|
47
45
|
}
|
48
46
|
if (!process.stdout.isTTY) {
|
49
|
-
|
47
|
+
error(`There are conflicts in the import. Please specify a --force-mode, or resolve them in your browser at ${resolveUrl}.`);
|
50
48
|
process.exit(1);
|
51
49
|
}
|
52
|
-
|
53
|
-
const resp = await
|
50
|
+
warn('There are conflicts in the import. What do you want to do?');
|
51
|
+
const resp = await askString('Type "KEEP" to preserve the version on the server, "OVERRIDE" to use the version from the import, and nothing to abort: ');
|
54
52
|
if (resp !== 'KEEP' && resp !== 'OVERRIDE') {
|
55
|
-
|
53
|
+
error(`Aborting. You can resolve the conflicts and complete the import here: ${resolveUrl}`);
|
56
54
|
process.exit(1);
|
57
55
|
}
|
58
56
|
return resp;
|
59
57
|
}
|
60
58
|
async function prepareImport(client, files) {
|
61
|
-
return
|
59
|
+
return loading('Deleting import...', client.import.deleteImportIfExists()).then(() => loading('Uploading files...', client.import.addFiles({ files: files })));
|
62
60
|
}
|
63
61
|
async function resolveConflicts(client, locales, method) {
|
64
62
|
for (const locale of locales) {
|
@@ -72,11 +70,11 @@ async function resolveConflicts(client, locales, method) {
|
|
72
70
|
}
|
73
71
|
async function applyImport(client) {
|
74
72
|
try {
|
75
|
-
await
|
73
|
+
await loading('Applying changes...', client.import.applyImport());
|
76
74
|
}
|
77
75
|
catch (e) {
|
78
|
-
if (e instanceof
|
79
|
-
|
76
|
+
if (e instanceof HttpError && e.response.status === 400) {
|
77
|
+
error("Some of the imported languages weren't recognized. Please create a language with corresponding tag in the Tolgee Platform.");
|
80
78
|
return;
|
81
79
|
}
|
82
80
|
throw e;
|
@@ -85,38 +83,38 @@ async function applyImport(client) {
|
|
85
83
|
async function pushHandler(path) {
|
86
84
|
const opts = this.optsWithGlobals();
|
87
85
|
try {
|
88
|
-
const stats = await
|
86
|
+
const stats = await stat(path);
|
89
87
|
if (!stats.isDirectory()) {
|
90
|
-
|
88
|
+
error('The specified path is not a directory.');
|
91
89
|
process.exit(1);
|
92
90
|
}
|
93
91
|
}
|
94
92
|
catch (e) {
|
95
93
|
if (e.code === 'ENOENT') {
|
96
|
-
|
94
|
+
error('The specified path does not exist.');
|
97
95
|
process.exit(1);
|
98
96
|
}
|
99
97
|
throw e;
|
100
98
|
}
|
101
|
-
const files = await
|
99
|
+
const files = await loading('Reading files...', readDirectory(path));
|
102
100
|
if (files.length === 0) {
|
103
|
-
|
101
|
+
error('Nothing to import.');
|
104
102
|
return;
|
105
103
|
}
|
106
104
|
const result = await prepareImport(opts.client, files);
|
107
105
|
const conflicts = getConflictingLanguages(result);
|
108
106
|
if (conflicts.length) {
|
109
107
|
const resolveMethod = await promptConflicts(opts);
|
110
|
-
await
|
108
|
+
await loading('Resolving conflicts...', resolveConflicts(opts.client, conflicts, resolveMethod));
|
111
109
|
}
|
112
110
|
await applyImport(opts.client);
|
113
|
-
|
111
|
+
success('Done!');
|
114
112
|
}
|
115
|
-
|
113
|
+
export default new Command()
|
116
114
|
.name('push')
|
117
115
|
.description('Pushes translations to Tolgee')
|
118
116
|
.argument('<path>', 'Path to the files to push to Tolgee')
|
119
|
-
.addOption(new
|
117
|
+
.addOption(new Option('-f, --force-mode <mode>', 'What should we do with possible conflicts? If unspecified, the user will be prompted interactively, or the command will fail when in non-interactive')
|
120
118
|
.choices(['OVERRIDE', 'KEEP', 'NO'])
|
121
119
|
.argParser((v) => v.toUpperCase()))
|
122
120
|
.action(pushHandler);
|
@@ -1,50 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
};
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
const syncUtils_1 = require("./syncUtils");
|
9
|
-
const runner_1 = require("../../extractor/runner");
|
10
|
-
const warnings_1 = require("../../extractor/warnings");
|
11
|
-
const options_1 = require("../../options");
|
12
|
-
const logger_1 = require("../../utils/logger");
|
1
|
+
import { Command } from 'commander';
|
2
|
+
import ansi from 'ansi-colors';
|
3
|
+
import { compareKeys, printKey } from './syncUtils.js';
|
4
|
+
import { extractKeysOfFiles, filterExtractionResult, } from '../../extractor/runner.js';
|
5
|
+
import { dumpWarnings } from '../../extractor/warnings.js';
|
6
|
+
import { EXTRACTOR } from '../../options.js';
|
7
|
+
import { loading } from '../../utils/logger.js';
|
13
8
|
async function compareHandler(pattern) {
|
14
9
|
const opts = this.optsWithGlobals();
|
15
|
-
const rawKeys = await
|
16
|
-
|
17
|
-
const localKeys =
|
10
|
+
const rawKeys = await loading('Analyzing code...', extractKeysOfFiles(pattern, opts.extractor));
|
11
|
+
dumpWarnings(rawKeys);
|
12
|
+
const localKeys = filterExtractionResult(rawKeys);
|
18
13
|
const remoteKeys = await opts.client.project.fetchAllKeys();
|
19
|
-
const diff =
|
14
|
+
const diff = compareKeys(localKeys, remoteKeys);
|
20
15
|
if (!diff.added.length && !diff.removed.length) {
|
21
|
-
console.log(
|
16
|
+
console.log(ansi.green('Your code project is in sync with the associated Tolgee project!'));
|
22
17
|
process.exit(0);
|
23
18
|
}
|
24
19
|
console.log('Your code project and Tolgee project are out of sync.');
|
25
20
|
if (diff.added.length) {
|
26
21
|
const key = diff.added.length === 1 ? 'key' : 'keys';
|
27
|
-
console.log(
|
22
|
+
console.log(ansi.green.bold(`${diff.added.length} new ${key} found`));
|
28
23
|
for (const key of diff.added) {
|
29
|
-
|
24
|
+
printKey(key, false);
|
30
25
|
}
|
31
26
|
// Line break
|
32
27
|
console.log('');
|
33
28
|
}
|
34
29
|
if (diff.removed.length) {
|
35
30
|
const key = diff.removed.length === 1 ? 'key' : 'keys';
|
36
|
-
console.log(
|
31
|
+
console.log(ansi.red.bold(`${diff.removed.length} unused ${key}`));
|
37
32
|
for (const key of diff.removed) {
|
38
|
-
|
33
|
+
printKey(key, true);
|
39
34
|
}
|
40
35
|
// Line break
|
41
36
|
console.log('');
|
42
37
|
}
|
43
38
|
console.log('Run `tolgee sync` to synchronize the projects.');
|
44
39
|
}
|
45
|
-
|
40
|
+
export default new Command()
|
46
41
|
.name('compare')
|
47
42
|
.description('Compares the keys in your code project and in the Tolgee project.')
|
48
43
|
.argument('<pattern>', 'File pattern to include (hint: make sure to escape it in quotes, or your shell might attempt to unroll some tokens like *)')
|
49
|
-
.addOption(
|
44
|
+
.addOption(EXTRACTOR)
|
50
45
|
.action(compareHandler);
|
@@ -1,66 +1,61 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
};
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
const overwriteDir_1 = require("../../utils/overwriteDir");
|
12
|
-
const zip_1 = require("../../utils/zip");
|
13
|
-
const ask_1 = require("../../utils/ask");
|
14
|
-
const logger_1 = require("../../utils/logger");
|
15
|
-
const options_1 = require("../../options");
|
1
|
+
import { Command } from 'commander';
|
2
|
+
import ansi from 'ansi-colors';
|
3
|
+
import { extractKeysOfFiles, filterExtractionResult, } from '../../extractor/runner.js';
|
4
|
+
import { dumpWarnings } from '../../extractor/warnings.js';
|
5
|
+
import { compareKeys, printKey } from './syncUtils.js';
|
6
|
+
import { overwriteDir } from '../../utils/overwriteDir.js';
|
7
|
+
import { unzipBuffer } from '../../utils/zip.js';
|
8
|
+
import { askBoolean } from '../../utils/ask.js';
|
9
|
+
import { loading, error } from '../../utils/logger.js';
|
10
|
+
import { EXTRACTOR } from '../../options.js';
|
16
11
|
async function backup(client, dest) {
|
17
12
|
const blob = await client.export.export({
|
18
13
|
format: 'JSON',
|
19
14
|
filterState: ['UNTRANSLATED', 'TRANSLATED', 'REVIEWED'],
|
20
15
|
structureDelimiter: '',
|
21
16
|
});
|
22
|
-
await
|
17
|
+
await unzipBuffer(blob, dest);
|
23
18
|
}
|
24
19
|
async function askForConfirmation(keys, operation) {
|
25
20
|
if (!process.stdout.isTTY) {
|
26
|
-
|
21
|
+
error('You must run this command interactively, or specify --yes to proceed.');
|
27
22
|
process.exit(1);
|
28
23
|
}
|
29
24
|
const str = `The following keys will be ${operation}:`;
|
30
|
-
console.log(operation === 'created' ?
|
31
|
-
keys.forEach((k) =>
|
32
|
-
const shouldContinue = await
|
25
|
+
console.log(operation === 'created' ? ansi.bold.green(str) : ansi.bold.red(str));
|
26
|
+
keys.forEach((k) => printKey(k, operation === 'deleted'));
|
27
|
+
const shouldContinue = await askBoolean('Does this look correct?', true);
|
33
28
|
if (!shouldContinue) {
|
34
|
-
|
29
|
+
error('Aborting.');
|
35
30
|
process.exit(1);
|
36
31
|
}
|
37
32
|
}
|
38
33
|
async function syncHandler(pattern) {
|
39
34
|
const opts = this.optsWithGlobals();
|
40
|
-
const rawKeys = await
|
41
|
-
const warnCount =
|
35
|
+
const rawKeys = await loading('Analyzing code...', extractKeysOfFiles(pattern, opts.extractor));
|
36
|
+
const warnCount = dumpWarnings(rawKeys);
|
42
37
|
if (!opts.continueOnWarning && warnCount) {
|
43
|
-
console.log(
|
38
|
+
console.log(ansi.bold.red('Aborting as warnings have been emitted.'));
|
44
39
|
process.exit(1);
|
45
40
|
}
|
46
|
-
const localKeys =
|
41
|
+
const localKeys = filterExtractionResult(rawKeys);
|
47
42
|
const remoteKeys = await opts.client.project.fetchAllKeys();
|
48
|
-
const diff =
|
43
|
+
const diff = compareKeys(localKeys, remoteKeys);
|
49
44
|
if (!diff.added.length && !diff.removed.length) {
|
50
|
-
console.log(
|
45
|
+
console.log(ansi.green('Your code project is in sync with the associated Tolgee project!'));
|
51
46
|
process.exit(0);
|
52
47
|
}
|
53
48
|
// Load project settings. We're interested in the default locale here.
|
54
49
|
const { baseLanguage } = await opts.client.project.fetchProjectInformation();
|
55
50
|
if (!baseLanguage) {
|
56
51
|
// I'm highly unsure how we could reach this state, but this is what the OAI spec tells me ¯\_(ツ)_/¯
|
57
|
-
|
52
|
+
error('Your project does not have a base language!');
|
58
53
|
process.exit(1);
|
59
54
|
}
|
60
55
|
// Prepare backup
|
61
56
|
if (opts.backup) {
|
62
|
-
await
|
63
|
-
await
|
57
|
+
await overwriteDir(opts.backup, opts.yes);
|
58
|
+
await loading('Backing up Tolgee project', backup(opts.client, opts.backup));
|
64
59
|
}
|
65
60
|
// Create new keys
|
66
61
|
if (diff.added.length) {
|
@@ -74,7 +69,7 @@ async function syncHandler(pattern) {
|
|
74
69
|
? { [baseLanguage.tag]: key.defaultValue }
|
75
70
|
: {},
|
76
71
|
}));
|
77
|
-
await
|
72
|
+
await loading('Creating missing keys...', opts.client.project.createBulkKey(keys));
|
78
73
|
}
|
79
74
|
if (opts.removeUnused) {
|
80
75
|
// Delete unused keys.
|
@@ -83,26 +78,26 @@ async function syncHandler(pattern) {
|
|
83
78
|
await askForConfirmation(diff.removed, 'deleted');
|
84
79
|
}
|
85
80
|
const ids = await diff.removed.map((k) => k.id);
|
86
|
-
await
|
81
|
+
await loading('Deleting unused keys...', opts.client.project.deleteBulkKeys(ids));
|
87
82
|
}
|
88
83
|
}
|
89
|
-
console.log(
|
90
|
-
console.log(
|
84
|
+
console.log(ansi.bold.green('Sync complete!'));
|
85
|
+
console.log(ansi.green(`+ ${diff.added.length} string${diff.added.length === 1 ? '' : 's'}`));
|
91
86
|
if (opts.removeUnused) {
|
92
|
-
console.log(
|
87
|
+
console.log(ansi.red(`- ${diff.removed.length} string${diff.removed.length === 1 ? '' : 's'}`));
|
93
88
|
}
|
94
89
|
else {
|
95
|
-
console.log(
|
90
|
+
console.log(ansi.italic(`${diff.removed.length} unused key${diff.removed.length === 1 ? '' : 's'} could be deleted.`));
|
96
91
|
}
|
97
92
|
if (opts.backup) {
|
98
|
-
console.log(
|
93
|
+
console.log(ansi.blueBright(`A backup of the project prior to the synchronization has been dumped in ${opts.backup}.`));
|
99
94
|
}
|
100
95
|
}
|
101
|
-
|
96
|
+
export default new Command()
|
102
97
|
.name('sync')
|
103
98
|
.description('Synchronizes the keys in your code project and in the Tolgee project, by creating missing keys and optionally deleting unused ones. For a dry-run, use `tolgee compare`.')
|
104
99
|
.argument('<pattern>', 'File pattern to include (hint: make sure to escape it in quotes, or your shell might attempt to unroll some tokens like *)')
|
105
|
-
.addOption(
|
100
|
+
.addOption(EXTRACTOR)
|
106
101
|
.option('-B, --backup <path>', 'Path where a backup should be downloaded before performing the sync. If something goes wrong, the backup can be used to restore the project to its previous state.')
|
107
102
|
.option('--continue-on-warning', 'Set this flag to continue the sync if warnings are detected during string extraction. By default, as warnings may indicate an invalid extraction, the CLI will abort the sync.')
|
108
103
|
.option('-Y, --yes', 'Skip prompts and automatically say yes to them. You will not be asked for confirmation before creating/deleting keys.')
|
@@ -1,29 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
-
};
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.compareKeys = exports.printKey = void 0;
|
7
|
-
const runner_1 = require("../../extractor/runner");
|
8
|
-
const ansi_colors_1 = __importDefault(require("ansi-colors"));
|
1
|
+
import { NullNamespace } from '../../extractor/runner.js';
|
2
|
+
import ansi from 'ansi-colors';
|
9
3
|
/**
|
10
4
|
* Prints information about a key, with coloring and formatting.
|
11
5
|
*
|
12
6
|
* @param key The key to print.
|
13
7
|
* @param deletion True if the key is about to be deleted.
|
14
8
|
*/
|
15
|
-
function printKey(key, deletion) {
|
9
|
+
export function printKey(key, deletion) {
|
16
10
|
const namespace = key.namespace
|
17
|
-
? ` ${
|
11
|
+
? ` ${ansi.italic(`(namespace: ${key.namespace})`)}`
|
18
12
|
: '';
|
19
13
|
if (deletion) {
|
20
|
-
console.log(`${
|
14
|
+
console.log(`${ansi.red(`- ${key.keyName}`)}${namespace}`);
|
21
15
|
}
|
22
16
|
else {
|
23
|
-
console.log(`${
|
17
|
+
console.log(`${ansi.green(`+ ${key.keyName}`)}${namespace}`);
|
24
18
|
}
|
25
19
|
}
|
26
|
-
exports.printKey = printKey;
|
27
20
|
/**
|
28
21
|
* Compares local and remote keys to detect added and deleted keys.
|
29
22
|
* **Warning**: `local` will be modified as a side-effect!
|
@@ -32,11 +25,11 @@ exports.printKey = printKey;
|
|
32
25
|
* @param remote Remote keys.
|
33
26
|
* @returns A list of added and removed keys.
|
34
27
|
*/
|
35
|
-
function compareKeys(local, remote) {
|
28
|
+
export function compareKeys(local, remote) {
|
36
29
|
const result = { added: [], removed: [] };
|
37
30
|
// Deleted keys
|
38
31
|
for (const remoteKey of remote) {
|
39
|
-
const namespace = remoteKey.namespace ||
|
32
|
+
const namespace = remoteKey.namespace || NullNamespace;
|
40
33
|
const keyExists = local[namespace]?.delete(remoteKey.name);
|
41
34
|
if (!keyExists) {
|
42
35
|
result.removed.push({
|
@@ -47,7 +40,7 @@ function compareKeys(local, remote) {
|
|
47
40
|
}
|
48
41
|
}
|
49
42
|
// Added keys
|
50
|
-
const namespaces = [
|
43
|
+
const namespaces = [NullNamespace, ...Object.keys(local).sort()];
|
51
44
|
for (const namespace of namespaces) {
|
52
45
|
if (namespace in local && local[namespace].size) {
|
53
46
|
const keys = local[namespace];
|
@@ -55,7 +48,7 @@ function compareKeys(local, remote) {
|
|
55
48
|
for (const keyName of keyNames) {
|
56
49
|
result.added.push({
|
57
50
|
keyName: keyName,
|
58
|
-
namespace: namespace ===
|
51
|
+
namespace: namespace === NullNamespace ? undefined : namespace,
|
59
52
|
defaultValue: keys.get(keyName) || undefined,
|
60
53
|
});
|
61
54
|
}
|
@@ -79,4 +72,3 @@ function compareKeys(local, remote) {
|
|
79
72
|
});
|
80
73
|
return result;
|
81
74
|
}
|
82
|
-
exports.compareKeys = compareKeys;
|
@@ -1,14 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
const
|
6
|
-
const logger_1 = require("../utils/logger");
|
7
|
-
const constants_1 = require("../constants");
|
8
|
-
exports.API_TOKENS_FILE = (0, path_1.join)(constants_1.CONFIG_PATH, 'authentication.json');
|
1
|
+
import { join, dirname } from 'path';
|
2
|
+
import { mkdir, readFile, writeFile } from 'fs/promises';
|
3
|
+
import { warn } from '../utils/logger.js';
|
4
|
+
import { CONFIG_PATH } from '../constants.js';
|
5
|
+
const API_TOKENS_FILE = join(CONFIG_PATH, 'authentication.json');
|
9
6
|
async function ensureConfigPath() {
|
10
7
|
try {
|
11
|
-
await
|
8
|
+
await mkdir(dirname(API_TOKENS_FILE));
|
12
9
|
}
|
13
10
|
catch (e) {
|
14
11
|
if (e.code !== 'EEXIST') {
|
@@ -19,7 +16,7 @@ async function ensureConfigPath() {
|
|
19
16
|
async function loadStore() {
|
20
17
|
try {
|
21
18
|
await ensureConfigPath();
|
22
|
-
const storeData = await
|
19
|
+
const storeData = await readFile(API_TOKENS_FILE, 'utf8');
|
23
20
|
return JSON.parse(storeData);
|
24
21
|
}
|
25
22
|
catch (e) {
|
@@ -31,7 +28,7 @@ async function loadStore() {
|
|
31
28
|
}
|
32
29
|
async function saveStore(store) {
|
33
30
|
const blob = JSON.stringify(store);
|
34
|
-
await
|
31
|
+
await writeFile(API_TOKENS_FILE, blob, {
|
35
32
|
mode: 0o600,
|
36
33
|
encoding: 'utf8',
|
37
34
|
});
|
@@ -57,17 +54,15 @@ async function storePak(store, instance, projectId, pak) {
|
|
57
54
|
},
|
58
55
|
});
|
59
56
|
}
|
60
|
-
async function savePat(instance, pat) {
|
57
|
+
export async function savePat(instance, pat) {
|
61
58
|
const store = await loadStore();
|
62
59
|
return storePat(store, instance, pat);
|
63
60
|
}
|
64
|
-
|
65
|
-
async function savePak(instance, projectId, pak) {
|
61
|
+
export async function savePak(instance, projectId, pak) {
|
66
62
|
const store = await loadStore();
|
67
63
|
return storePak(store, instance, projectId, pak);
|
68
64
|
}
|
69
|
-
|
70
|
-
async function getApiKey(instance, projectId) {
|
65
|
+
export async function getApiKey(instance, projectId) {
|
71
66
|
const store = await loadStore();
|
72
67
|
if (!store[instance.hostname]) {
|
73
68
|
return null;
|
@@ -76,7 +71,7 @@ async function getApiKey(instance, projectId) {
|
|
76
71
|
if (scopedStore.user) {
|
77
72
|
if (scopedStore.user.expires !== 0 &&
|
78
73
|
Date.now() > scopedStore.user.expires) {
|
79
|
-
|
74
|
+
warn(`Your personal access token for ${instance.hostname} expired.`);
|
80
75
|
await storePat(store, instance, undefined);
|
81
76
|
return null;
|
82
77
|
}
|
@@ -88,7 +83,7 @@ async function getApiKey(instance, projectId) {
|
|
88
83
|
const pak = scopedStore.projects?.[projectId.toString(10)];
|
89
84
|
if (pak) {
|
90
85
|
if (pak.expires !== 0 && Date.now() > pak.expires) {
|
91
|
-
|
86
|
+
warn(`Your project API key for project #${projectId} on ${instance.hostname} expired.`);
|
92
87
|
await storePak(store, instance, projectId, undefined);
|
93
88
|
return null;
|
94
89
|
}
|
@@ -96,8 +91,7 @@ async function getApiKey(instance, projectId) {
|
|
96
91
|
}
|
97
92
|
return null;
|
98
93
|
}
|
99
|
-
|
100
|
-
async function saveApiKey(instance, token) {
|
94
|
+
export async function saveApiKey(instance, token) {
|
101
95
|
const store = await loadStore();
|
102
96
|
if (token.type === 'PAT') {
|
103
97
|
return storePat(store, instance, {
|
@@ -110,16 +104,13 @@ async function saveApiKey(instance, token) {
|
|
110
104
|
expires: token.expires,
|
111
105
|
});
|
112
106
|
}
|
113
|
-
|
114
|
-
async function removeApiKeys(api) {
|
107
|
+
export async function removeApiKeys(api) {
|
115
108
|
const store = await loadStore();
|
116
109
|
return saveStore({
|
117
110
|
...store,
|
118
111
|
[api.hostname]: {},
|
119
112
|
});
|
120
113
|
}
|
121
|
-
|
122
|
-
async function clearAuthStore() {
|
114
|
+
export async function clearAuthStore() {
|
123
115
|
return saveStore({});
|
124
116
|
}
|
125
|
-
exports.clearAuthStore = clearAuthStore;
|
package/dist/config/tolgeerc.js
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
const
|
6
|
-
const constants_1 = require("../constants");
|
7
|
-
const explorer = (0, cosmiconfig_1.cosmiconfig)('tolgee', {
|
1
|
+
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
|
2
|
+
import { resolve } from 'path';
|
3
|
+
import { existsSync } from 'fs';
|
4
|
+
import { SDKS } from '../constants.js';
|
5
|
+
const explorer = cosmiconfig('tolgee', {
|
8
6
|
loaders: {
|
9
|
-
noExt:
|
7
|
+
noExt: defaultLoaders['.json'],
|
10
8
|
},
|
11
9
|
});
|
12
10
|
function parseConfig(rc) {
|
@@ -32,8 +30,8 @@ function parseConfig(rc) {
|
|
32
30
|
}
|
33
31
|
}
|
34
32
|
if ('sdk' in rc) {
|
35
|
-
if (!
|
36
|
-
throw new Error(`Invalid config: invalid sdk. Must be one of: ${
|
33
|
+
if (!SDKS.includes(rc.sdk)) {
|
34
|
+
throw new Error(`Invalid config: invalid sdk. Must be one of: ${SDKS.join(' ')}`);
|
37
35
|
}
|
38
36
|
cfg.sdk = rc.sdk;
|
39
37
|
}
|
@@ -41,8 +39,8 @@ function parseConfig(rc) {
|
|
41
39
|
if (typeof rc.extractor !== 'string') {
|
42
40
|
throw new Error('Invalid config: extractor is not a string');
|
43
41
|
}
|
44
|
-
const extractorPath =
|
45
|
-
if (!
|
42
|
+
const extractorPath = resolve(rc.extractor);
|
43
|
+
if (!existsSync(extractorPath)) {
|
46
44
|
throw new Error(`Invalid config: extractor points to a file that does not exists (${extractorPath})`);
|
47
45
|
}
|
48
46
|
cfg.extractor = extractorPath;
|
@@ -55,10 +53,9 @@ function parseConfig(rc) {
|
|
55
53
|
}
|
56
54
|
return cfg;
|
57
55
|
}
|
58
|
-
async function loadTolgeeRc() {
|
56
|
+
export default async function loadTolgeeRc() {
|
59
57
|
const res = await explorer.search();
|
60
58
|
if (!res || res.isEmpty)
|
61
59
|
return null;
|
62
60
|
return parseConfig(res.config);
|
63
61
|
}
|
64
|
-
exports.default = loadTolgeeRc;
|
package/dist/constants.js
CHANGED
@@ -1,18 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
const
|
8
|
-
const
|
9
|
-
const
|
10
|
-
const
|
11
|
-
const
|
12
|
-
exports.CONFIG_PATH = (0, configPath_1.default)();
|
13
|
-
exports.VERSION = JSON.parse(pkg).version;
|
14
|
-
exports.USER_AGENT = `Tolgee-CLI/${exports.VERSION} (+https://github.com/tolgee/tolgee-cli)`;
|
15
|
-
exports.DEFAULT_API_URL = new URL('https://app.tolgee.io');
|
16
|
-
exports.API_KEY_PAT_PREFIX = 'tgpat_';
|
17
|
-
exports.API_KEY_PAK_PREFIX = 'tgpak_';
|
18
|
-
exports.SDKS = ['react'];
|
1
|
+
import { readFileSync } from 'fs';
|
2
|
+
import getConfigPath from './utils/configPath.js';
|
3
|
+
const packageJson = new URL('../package.json', import.meta.url);
|
4
|
+
const pkg = readFileSync(packageJson, 'utf8');
|
5
|
+
export const CONFIG_PATH = getConfigPath();
|
6
|
+
export const VERSION = JSON.parse(pkg).version;
|
7
|
+
export const USER_AGENT = `Tolgee-CLI/${VERSION} (+https://github.com/tolgee/tolgee-cli)`;
|
8
|
+
export const DEFAULT_API_URL = new URL('https://app.tolgee.io');
|
9
|
+
export const API_KEY_PAT_PREFIX = 'tgpat_';
|
10
|
+
export const API_KEY_PAK_PREFIX = 'tgpak_';
|
11
|
+
export const SDKS = ['react'];
|