@tolkbot/cli 0.3.0 → 0.4.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/index.js +1573 -1750
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -105,7 +105,7 @@ var require_path = __commonJS({
|
|
|
105
105
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
106
106
|
exports.convertPosixPathToPattern = exports.convertWindowsPathToPattern = exports.convertPathToPattern = exports.escapePosixPath = exports.escapeWindowsPath = exports.escape = exports.removeLeadingDotSegment = exports.makeAbsolute = exports.unixify = void 0;
|
|
107
107
|
var os3 = __require("os");
|
|
108
|
-
var
|
|
108
|
+
var path11 = __require("path");
|
|
109
109
|
var IS_WINDOWS_PLATFORM = os3.platform() === "win32";
|
|
110
110
|
var LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2;
|
|
111
111
|
var POSIX_UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\()|\\(?![!()*+?@[\]{|}]))/g;
|
|
@@ -117,7 +117,7 @@ var require_path = __commonJS({
|
|
|
117
117
|
}
|
|
118
118
|
exports.unixify = unixify;
|
|
119
119
|
function makeAbsolute(cwd, filepath) {
|
|
120
|
-
return
|
|
120
|
+
return path11.resolve(cwd, filepath);
|
|
121
121
|
}
|
|
122
122
|
exports.makeAbsolute = makeAbsolute;
|
|
123
123
|
function removeLeadingDotSegment(entry) {
|
|
@@ -1416,7 +1416,7 @@ var require_braces = __commonJS({
|
|
|
1416
1416
|
var require_constants2 = __commonJS({
|
|
1417
1417
|
"../../node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/constants.js"(exports, module) {
|
|
1418
1418
|
"use strict";
|
|
1419
|
-
var
|
|
1419
|
+
var path11 = __require("path");
|
|
1420
1420
|
var WIN_SLASH = "\\\\/";
|
|
1421
1421
|
var WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
1422
1422
|
var DEFAULT_MAX_EXTGLOB_RECURSION = 0;
|
|
@@ -1590,7 +1590,7 @@ var require_constants2 = __commonJS({
|
|
|
1590
1590
|
/* | */
|
|
1591
1591
|
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
|
|
1592
1592
|
/* \uFEFF */
|
|
1593
|
-
SEP:
|
|
1593
|
+
SEP: path11.sep,
|
|
1594
1594
|
/**
|
|
1595
1595
|
* Create EXTGLOB_CHARS
|
|
1596
1596
|
*/
|
|
@@ -1617,7 +1617,7 @@ var require_constants2 = __commonJS({
|
|
|
1617
1617
|
var require_utils2 = __commonJS({
|
|
1618
1618
|
"../../node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/utils.js"(exports) {
|
|
1619
1619
|
"use strict";
|
|
1620
|
-
var
|
|
1620
|
+
var path11 = __require("path");
|
|
1621
1621
|
var win32 = process.platform === "win32";
|
|
1622
1622
|
var {
|
|
1623
1623
|
REGEX_BACKSLASH,
|
|
@@ -1646,7 +1646,7 @@ var require_utils2 = __commonJS({
|
|
|
1646
1646
|
if (options && typeof options.windows === "boolean") {
|
|
1647
1647
|
return options.windows;
|
|
1648
1648
|
}
|
|
1649
|
-
return win32 === true ||
|
|
1649
|
+
return win32 === true || path11.sep === "\\";
|
|
1650
1650
|
};
|
|
1651
1651
|
exports.escapeLast = (input2, char, lastIdx) => {
|
|
1652
1652
|
const idx = input2.lastIndexOf(char, lastIdx);
|
|
@@ -3010,7 +3010,7 @@ var require_parse2 = __commonJS({
|
|
|
3010
3010
|
var require_picomatch = __commonJS({
|
|
3011
3011
|
"../../node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/picomatch.js"(exports, module) {
|
|
3012
3012
|
"use strict";
|
|
3013
|
-
var
|
|
3013
|
+
var path11 = __require("path");
|
|
3014
3014
|
var scan = require_scan();
|
|
3015
3015
|
var parse4 = require_parse2();
|
|
3016
3016
|
var utils = require_utils2();
|
|
@@ -3095,7 +3095,7 @@ var require_picomatch = __commonJS({
|
|
|
3095
3095
|
};
|
|
3096
3096
|
picomatch.matchBase = (input2, glob, options, posix = utils.isWindows(options)) => {
|
|
3097
3097
|
const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
|
|
3098
|
-
return regex.test(
|
|
3098
|
+
return regex.test(path11.basename(input2));
|
|
3099
3099
|
};
|
|
3100
3100
|
picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
|
|
3101
3101
|
picomatch.parse = (pattern, options) => {
|
|
@@ -3322,7 +3322,7 @@ var require_pattern = __commonJS({
|
|
|
3322
3322
|
"use strict";
|
|
3323
3323
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3324
3324
|
exports.isAbsolute = exports.partitionAbsoluteAndRelative = exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0;
|
|
3325
|
-
var
|
|
3325
|
+
var path11 = __require("path");
|
|
3326
3326
|
var globParent = require_glob_parent();
|
|
3327
3327
|
var micromatch = require_micromatch();
|
|
3328
3328
|
var GLOBSTAR = "**";
|
|
@@ -3417,7 +3417,7 @@ var require_pattern = __commonJS({
|
|
|
3417
3417
|
}
|
|
3418
3418
|
exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
|
3419
3419
|
function isAffectDepthOfReadingPattern(pattern) {
|
|
3420
|
-
const basename =
|
|
3420
|
+
const basename = path11.basename(pattern);
|
|
3421
3421
|
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
|
|
3422
3422
|
}
|
|
3423
3423
|
exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
|
@@ -3475,7 +3475,7 @@ var require_pattern = __commonJS({
|
|
|
3475
3475
|
}
|
|
3476
3476
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
3477
3477
|
function isAbsolute(pattern) {
|
|
3478
|
-
return
|
|
3478
|
+
return path11.isAbsolute(pattern);
|
|
3479
3479
|
}
|
|
3480
3480
|
exports.isAbsolute = isAbsolute;
|
|
3481
3481
|
}
|
|
@@ -3650,10 +3650,10 @@ var require_utils3 = __commonJS({
|
|
|
3650
3650
|
exports.array = array2;
|
|
3651
3651
|
var errno = require_errno();
|
|
3652
3652
|
exports.errno = errno;
|
|
3653
|
-
var
|
|
3654
|
-
exports.fs =
|
|
3655
|
-
var
|
|
3656
|
-
exports.path =
|
|
3653
|
+
var fs8 = require_fs();
|
|
3654
|
+
exports.fs = fs8;
|
|
3655
|
+
var path11 = require_path();
|
|
3656
|
+
exports.path = path11;
|
|
3657
3657
|
var pattern = require_pattern();
|
|
3658
3658
|
exports.pattern = pattern;
|
|
3659
3659
|
var stream = require_stream();
|
|
@@ -3765,8 +3765,8 @@ var require_async = __commonJS({
|
|
|
3765
3765
|
"use strict";
|
|
3766
3766
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3767
3767
|
exports.read = void 0;
|
|
3768
|
-
function read(
|
|
3769
|
-
settings.fs.lstat(
|
|
3768
|
+
function read(path11, settings, callback) {
|
|
3769
|
+
settings.fs.lstat(path11, (lstatError, lstat) => {
|
|
3770
3770
|
if (lstatError !== null) {
|
|
3771
3771
|
callFailureCallback(callback, lstatError);
|
|
3772
3772
|
return;
|
|
@@ -3775,7 +3775,7 @@ var require_async = __commonJS({
|
|
|
3775
3775
|
callSuccessCallback(callback, lstat);
|
|
3776
3776
|
return;
|
|
3777
3777
|
}
|
|
3778
|
-
settings.fs.stat(
|
|
3778
|
+
settings.fs.stat(path11, (statError, stat) => {
|
|
3779
3779
|
if (statError !== null) {
|
|
3780
3780
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
|
3781
3781
|
callFailureCallback(callback, statError);
|
|
@@ -3807,13 +3807,13 @@ var require_sync = __commonJS({
|
|
|
3807
3807
|
"use strict";
|
|
3808
3808
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3809
3809
|
exports.read = void 0;
|
|
3810
|
-
function read(
|
|
3811
|
-
const lstat = settings.fs.lstatSync(
|
|
3810
|
+
function read(path11, settings) {
|
|
3811
|
+
const lstat = settings.fs.lstatSync(path11);
|
|
3812
3812
|
if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) {
|
|
3813
3813
|
return lstat;
|
|
3814
3814
|
}
|
|
3815
3815
|
try {
|
|
3816
|
-
const stat = settings.fs.statSync(
|
|
3816
|
+
const stat = settings.fs.statSync(path11);
|
|
3817
3817
|
if (settings.markSymbolicLink) {
|
|
3818
3818
|
stat.isSymbolicLink = () => true;
|
|
3819
3819
|
}
|
|
@@ -3835,12 +3835,12 @@ var require_fs2 = __commonJS({
|
|
|
3835
3835
|
"use strict";
|
|
3836
3836
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3837
3837
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
|
3838
|
-
var
|
|
3838
|
+
var fs8 = __require("fs");
|
|
3839
3839
|
exports.FILE_SYSTEM_ADAPTER = {
|
|
3840
|
-
lstat:
|
|
3841
|
-
stat:
|
|
3842
|
-
lstatSync:
|
|
3843
|
-
statSync:
|
|
3840
|
+
lstat: fs8.lstat,
|
|
3841
|
+
stat: fs8.stat,
|
|
3842
|
+
lstatSync: fs8.lstatSync,
|
|
3843
|
+
statSync: fs8.statSync
|
|
3844
3844
|
};
|
|
3845
3845
|
function createFileSystemAdapter(fsMethods) {
|
|
3846
3846
|
if (fsMethods === void 0) {
|
|
@@ -3857,12 +3857,12 @@ var require_settings = __commonJS({
|
|
|
3857
3857
|
"../../node_modules/.pnpm/@nodelib+fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/settings.js"(exports) {
|
|
3858
3858
|
"use strict";
|
|
3859
3859
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3860
|
-
var
|
|
3860
|
+
var fs8 = require_fs2();
|
|
3861
3861
|
var Settings = class {
|
|
3862
3862
|
constructor(_options = {}) {
|
|
3863
3863
|
this._options = _options;
|
|
3864
3864
|
this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true);
|
|
3865
|
-
this.fs =
|
|
3865
|
+
this.fs = fs8.createFileSystemAdapter(this._options.fs);
|
|
3866
3866
|
this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false);
|
|
3867
3867
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
|
3868
3868
|
}
|
|
@@ -3884,17 +3884,17 @@ var require_out = __commonJS({
|
|
|
3884
3884
|
var sync = require_sync();
|
|
3885
3885
|
var settings_1 = require_settings();
|
|
3886
3886
|
exports.Settings = settings_1.default;
|
|
3887
|
-
function stat(
|
|
3887
|
+
function stat(path11, optionsOrSettingsOrCallback, callback) {
|
|
3888
3888
|
if (typeof optionsOrSettingsOrCallback === "function") {
|
|
3889
|
-
async.read(
|
|
3889
|
+
async.read(path11, getSettings(), optionsOrSettingsOrCallback);
|
|
3890
3890
|
return;
|
|
3891
3891
|
}
|
|
3892
|
-
async.read(
|
|
3892
|
+
async.read(path11, getSettings(optionsOrSettingsOrCallback), callback);
|
|
3893
3893
|
}
|
|
3894
3894
|
exports.stat = stat;
|
|
3895
|
-
function statSync2(
|
|
3895
|
+
function statSync2(path11, optionsOrSettings) {
|
|
3896
3896
|
const settings = getSettings(optionsOrSettings);
|
|
3897
|
-
return sync.read(
|
|
3897
|
+
return sync.read(path11, settings);
|
|
3898
3898
|
}
|
|
3899
3899
|
exports.statSync = statSync2;
|
|
3900
3900
|
function getSettings(settingsOrOptions = {}) {
|
|
@@ -4019,8 +4019,8 @@ var require_utils4 = __commonJS({
|
|
|
4019
4019
|
"use strict";
|
|
4020
4020
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4021
4021
|
exports.fs = void 0;
|
|
4022
|
-
var
|
|
4023
|
-
exports.fs =
|
|
4022
|
+
var fs8 = require_fs3();
|
|
4023
|
+
exports.fs = fs8;
|
|
4024
4024
|
}
|
|
4025
4025
|
});
|
|
4026
4026
|
|
|
@@ -4112,16 +4112,16 @@ var require_async2 = __commonJS({
|
|
|
4112
4112
|
return;
|
|
4113
4113
|
}
|
|
4114
4114
|
const tasks = names.map((name) => {
|
|
4115
|
-
const
|
|
4115
|
+
const path11 = common.joinPathSegments(directory, name, settings.pathSegmentSeparator);
|
|
4116
4116
|
return (done) => {
|
|
4117
|
-
fsStat.stat(
|
|
4117
|
+
fsStat.stat(path11, settings.fsStatSettings, (error62, stats) => {
|
|
4118
4118
|
if (error62 !== null) {
|
|
4119
4119
|
done(error62);
|
|
4120
4120
|
return;
|
|
4121
4121
|
}
|
|
4122
4122
|
const entry = {
|
|
4123
4123
|
name,
|
|
4124
|
-
path:
|
|
4124
|
+
path: path11,
|
|
4125
4125
|
dirent: utils.fs.createDirentFromStats(name, stats)
|
|
4126
4126
|
};
|
|
4127
4127
|
if (settings.stats) {
|
|
@@ -4215,14 +4215,14 @@ var require_fs4 = __commonJS({
|
|
|
4215
4215
|
"use strict";
|
|
4216
4216
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4217
4217
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
|
4218
|
-
var
|
|
4218
|
+
var fs8 = __require("fs");
|
|
4219
4219
|
exports.FILE_SYSTEM_ADAPTER = {
|
|
4220
|
-
lstat:
|
|
4221
|
-
stat:
|
|
4222
|
-
lstatSync:
|
|
4223
|
-
statSync:
|
|
4224
|
-
readdir:
|
|
4225
|
-
readdirSync:
|
|
4220
|
+
lstat: fs8.lstat,
|
|
4221
|
+
stat: fs8.stat,
|
|
4222
|
+
lstatSync: fs8.lstatSync,
|
|
4223
|
+
statSync: fs8.statSync,
|
|
4224
|
+
readdir: fs8.readdir,
|
|
4225
|
+
readdirSync: fs8.readdirSync
|
|
4226
4226
|
};
|
|
4227
4227
|
function createFileSystemAdapter(fsMethods) {
|
|
4228
4228
|
if (fsMethods === void 0) {
|
|
@@ -4239,15 +4239,15 @@ var require_settings2 = __commonJS({
|
|
|
4239
4239
|
"../../node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/settings.js"(exports) {
|
|
4240
4240
|
"use strict";
|
|
4241
4241
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4242
|
-
var
|
|
4242
|
+
var path11 = __require("path");
|
|
4243
4243
|
var fsStat = require_out();
|
|
4244
|
-
var
|
|
4244
|
+
var fs8 = require_fs4();
|
|
4245
4245
|
var Settings = class {
|
|
4246
4246
|
constructor(_options = {}) {
|
|
4247
4247
|
this._options = _options;
|
|
4248
4248
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
|
|
4249
|
-
this.fs =
|
|
4250
|
-
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator,
|
|
4249
|
+
this.fs = fs8.createFileSystemAdapter(this._options.fs);
|
|
4250
|
+
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path11.sep);
|
|
4251
4251
|
this.stats = this._getValue(this._options.stats, false);
|
|
4252
4252
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
|
4253
4253
|
this.fsStatSettings = new fsStat.Settings({
|
|
@@ -4274,17 +4274,17 @@ var require_out2 = __commonJS({
|
|
|
4274
4274
|
var sync = require_sync2();
|
|
4275
4275
|
var settings_1 = require_settings2();
|
|
4276
4276
|
exports.Settings = settings_1.default;
|
|
4277
|
-
function scandir(
|
|
4277
|
+
function scandir(path11, optionsOrSettingsOrCallback, callback) {
|
|
4278
4278
|
if (typeof optionsOrSettingsOrCallback === "function") {
|
|
4279
|
-
async.read(
|
|
4279
|
+
async.read(path11, getSettings(), optionsOrSettingsOrCallback);
|
|
4280
4280
|
return;
|
|
4281
4281
|
}
|
|
4282
|
-
async.read(
|
|
4282
|
+
async.read(path11, getSettings(optionsOrSettingsOrCallback), callback);
|
|
4283
4283
|
}
|
|
4284
4284
|
exports.scandir = scandir;
|
|
4285
|
-
function scandirSync(
|
|
4285
|
+
function scandirSync(path11, optionsOrSettings) {
|
|
4286
4286
|
const settings = getSettings(optionsOrSettings);
|
|
4287
|
-
return sync.read(
|
|
4287
|
+
return sync.read(path11, settings);
|
|
4288
4288
|
}
|
|
4289
4289
|
exports.scandirSync = scandirSync;
|
|
4290
4290
|
function getSettings(settingsOrOptions = {}) {
|
|
@@ -4932,7 +4932,7 @@ var require_settings3 = __commonJS({
|
|
|
4932
4932
|
"../../node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/settings.js"(exports) {
|
|
4933
4933
|
"use strict";
|
|
4934
4934
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4935
|
-
var
|
|
4935
|
+
var path11 = __require("path");
|
|
4936
4936
|
var fsScandir = require_out2();
|
|
4937
4937
|
var Settings = class {
|
|
4938
4938
|
constructor(_options = {}) {
|
|
@@ -4942,7 +4942,7 @@ var require_settings3 = __commonJS({
|
|
|
4942
4942
|
this.deepFilter = this._getValue(this._options.deepFilter, null);
|
|
4943
4943
|
this.entryFilter = this._getValue(this._options.entryFilter, null);
|
|
4944
4944
|
this.errorFilter = this._getValue(this._options.errorFilter, null);
|
|
4945
|
-
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator,
|
|
4945
|
+
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path11.sep);
|
|
4946
4946
|
this.fsScandirSettings = new fsScandir.Settings({
|
|
4947
4947
|
followSymbolicLinks: this._options.followSymbolicLinks,
|
|
4948
4948
|
fs: this._options.fs,
|
|
@@ -5004,7 +5004,7 @@ var require_reader2 = __commonJS({
|
|
|
5004
5004
|
"../../node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/readers/reader.js"(exports) {
|
|
5005
5005
|
"use strict";
|
|
5006
5006
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5007
|
-
var
|
|
5007
|
+
var path11 = __require("path");
|
|
5008
5008
|
var fsStat = require_out();
|
|
5009
5009
|
var utils = require_utils3();
|
|
5010
5010
|
var Reader = class {
|
|
@@ -5017,7 +5017,7 @@ var require_reader2 = __commonJS({
|
|
|
5017
5017
|
});
|
|
5018
5018
|
}
|
|
5019
5019
|
_getFullEntryPath(filepath) {
|
|
5020
|
-
return
|
|
5020
|
+
return path11.resolve(this._settings.cwd, filepath);
|
|
5021
5021
|
}
|
|
5022
5022
|
_makeEntry(stats, pattern) {
|
|
5023
5023
|
const entry = {
|
|
@@ -5433,7 +5433,7 @@ var require_provider = __commonJS({
|
|
|
5433
5433
|
"../../node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/providers/provider.js"(exports) {
|
|
5434
5434
|
"use strict";
|
|
5435
5435
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5436
|
-
var
|
|
5436
|
+
var path11 = __require("path");
|
|
5437
5437
|
var deep_1 = require_deep();
|
|
5438
5438
|
var entry_1 = require_entry();
|
|
5439
5439
|
var error_1 = require_error();
|
|
@@ -5447,7 +5447,7 @@ var require_provider = __commonJS({
|
|
|
5447
5447
|
this.entryTransformer = new entry_2.default(this._settings);
|
|
5448
5448
|
}
|
|
5449
5449
|
_getRootDirectory(task) {
|
|
5450
|
-
return
|
|
5450
|
+
return path11.resolve(this._settings.cwd, task.base);
|
|
5451
5451
|
}
|
|
5452
5452
|
_getReaderOptions(task) {
|
|
5453
5453
|
const basePath = task.base === "." ? "" : task.base;
|
|
@@ -5628,16 +5628,16 @@ var require_settings4 = __commonJS({
|
|
|
5628
5628
|
"use strict";
|
|
5629
5629
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5630
5630
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
|
|
5631
|
-
var
|
|
5631
|
+
var fs8 = __require("fs");
|
|
5632
5632
|
var os3 = __require("os");
|
|
5633
5633
|
var CPU_COUNT = Math.max(os3.cpus().length, 1);
|
|
5634
5634
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = {
|
|
5635
|
-
lstat:
|
|
5636
|
-
lstatSync:
|
|
5637
|
-
stat:
|
|
5638
|
-
statSync:
|
|
5639
|
-
readdir:
|
|
5640
|
-
readdirSync:
|
|
5635
|
+
lstat: fs8.lstat,
|
|
5636
|
+
lstatSync: fs8.lstatSync,
|
|
5637
|
+
stat: fs8.stat,
|
|
5638
|
+
statSync: fs8.statSync,
|
|
5639
|
+
readdir: fs8.readdir,
|
|
5640
|
+
readdirSync: fs8.readdirSync
|
|
5641
5641
|
};
|
|
5642
5642
|
var Settings = class {
|
|
5643
5643
|
constructor(_options = {}) {
|
|
@@ -5784,10 +5784,10 @@ var require_out4 = __commonJS({
|
|
|
5784
5784
|
});
|
|
5785
5785
|
|
|
5786
5786
|
// src/main.ts
|
|
5787
|
-
import { Command as
|
|
5787
|
+
import { Command as Command7, CommanderError, Option } from "commander";
|
|
5788
5788
|
|
|
5789
5789
|
// package.json
|
|
5790
|
-
var version = "0.
|
|
5790
|
+
var version = "0.4.0";
|
|
5791
5791
|
|
|
5792
5792
|
// src/commands/auth/index.ts
|
|
5793
5793
|
import { Command } from "commander";
|
|
@@ -26598,7 +26598,7 @@ function installDependencies(cwd, packageManager, deps) {
|
|
|
26598
26598
|
const cmd = packageManager === "npm" ? `npm install ${deps.join(" ")}` : packageManager === "yarn" ? `yarn add ${deps.join(" ")}` : `pnpm add ${deps.join(" ")}`;
|
|
26599
26599
|
execSync(cmd, { stdio: ["ignore", 2, 2], cwd });
|
|
26600
26600
|
}
|
|
26601
|
-
async function createLocaleFiles(cwd, framework, baseLocale,
|
|
26601
|
+
async function createLocaleFiles(cwd, framework, baseLocale, targetLocales2) {
|
|
26602
26602
|
const setup = FRAMEWORK_SETUPS[framework];
|
|
26603
26603
|
const createdFiles = [];
|
|
26604
26604
|
for (const file2 of setup.files) {
|
|
@@ -26614,7 +26614,7 @@ async function createLocaleFiles(cwd, framework, baseLocale, targetLocales) {
|
|
|
26614
26614
|
await fs3.mkdir(path32.dirname(baseFilePath), { recursive: true });
|
|
26615
26615
|
await fs3.writeFile(baseFilePath, file2.content);
|
|
26616
26616
|
createdFiles.push(basePath);
|
|
26617
|
-
for (const locale of
|
|
26617
|
+
for (const locale of targetLocales2) {
|
|
26618
26618
|
const targetPath = file2.path.replace("{{lng}}", locale);
|
|
26619
26619
|
const targetFilePath = path32.join(cwd, targetPath);
|
|
26620
26620
|
await fs3.mkdir(path32.dirname(targetFilePath), { recursive: true });
|
|
@@ -26625,14 +26625,14 @@ async function createLocaleFiles(cwd, framework, baseLocale, targetLocales) {
|
|
|
26625
26625
|
}
|
|
26626
26626
|
return createdFiles;
|
|
26627
26627
|
}
|
|
26628
|
-
async function setupFramework(cwd, framework, baseLocale,
|
|
26628
|
+
async function setupFramework(cwd, framework, baseLocale, targetLocales2, packageManager) {
|
|
26629
26629
|
const setup = FRAMEWORK_SETUPS[framework];
|
|
26630
26630
|
installDependencies(cwd, packageManager, setup.dependencies);
|
|
26631
26631
|
const createdFiles = await createLocaleFiles(
|
|
26632
26632
|
cwd,
|
|
26633
26633
|
framework,
|
|
26634
26634
|
baseLocale,
|
|
26635
|
-
|
|
26635
|
+
targetLocales2
|
|
26636
26636
|
);
|
|
26637
26637
|
return {
|
|
26638
26638
|
installedDeps: setup.dependencies,
|
|
@@ -27544,9 +27544,9 @@ function runtimeProperties(cliVersion) {
|
|
|
27544
27544
|
};
|
|
27545
27545
|
}
|
|
27546
27546
|
var activeCommand = null;
|
|
27547
|
-
function beginCommand(
|
|
27547
|
+
function beginCommand(path11, cliVersion) {
|
|
27548
27548
|
activeCommand = {
|
|
27549
|
-
path:
|
|
27549
|
+
path: path11,
|
|
27550
27550
|
startedAt: Date.now(),
|
|
27551
27551
|
cliVersion,
|
|
27552
27552
|
reported: false
|
|
@@ -28088,24 +28088,14 @@ function InitWizard({
|
|
|
28088
28088
|
".json"
|
|
28089
28089
|
] }),
|
|
28090
28090
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
28091
|
-
"3. Run: tolkbot translate
|
|
28092
|
-
" ",
|
|
28093
|
-
setupData.baseLanguage,
|
|
28094
|
-
" --dst",
|
|
28091
|
+
"3. Run: tolkbot translate --dst",
|
|
28095
28092
|
" ",
|
|
28096
28093
|
setupData.targetLanguages[0] ?? "no"
|
|
28097
28094
|
] })
|
|
28098
28095
|
] }),
|
|
28099
28096
|
!setupData.shouldInstall && /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
28100
28097
|
/* @__PURE__ */ jsx9(Text9, { children: "Next steps:" }),
|
|
28101
|
-
/* @__PURE__ */
|
|
28102
|
-
"Run: tolkbot translate plan --src",
|
|
28103
|
-
" ",
|
|
28104
|
-
setupData.baseLanguage,
|
|
28105
|
-
" --dst",
|
|
28106
|
-
" ",
|
|
28107
|
-
setupData.targetLanguages[0] ?? "no"
|
|
28108
|
-
] })
|
|
28098
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "See what a run would do: tolkbot translate --all --dry-run" })
|
|
28109
28099
|
] })
|
|
28110
28100
|
] });
|
|
28111
28101
|
}
|
|
@@ -28299,380 +28289,66 @@ async function runInteractive(opts) {
|
|
|
28299
28289
|
}
|
|
28300
28290
|
}
|
|
28301
28291
|
|
|
28302
|
-
// src/commands/
|
|
28292
|
+
// src/commands/jobs/index.ts
|
|
28303
28293
|
import { Command as Command3 } from "commander";
|
|
28304
28294
|
import { z as z5 } from "zod/v4";
|
|
28305
28295
|
|
|
28306
|
-
// src/commands/
|
|
28307
|
-
import {
|
|
28296
|
+
// src/commands/jobs/apply.tsx
|
|
28297
|
+
import { useState as useState2 } from "react";
|
|
28298
|
+
import { MultiSelect } from "@inkjs/ui";
|
|
28308
28299
|
import { Box as Box11, render as render11, Text as Text11 } from "ink";
|
|
28309
|
-
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
28310
|
-
function ArchiveUI({ state }) {
|
|
28311
|
-
if (state.type === "archiving") {
|
|
28312
|
-
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
28313
|
-
/* @__PURE__ */ jsx11(Spinner9, {}),
|
|
28314
|
-
/* @__PURE__ */ jsx11(Text11, { children: " Archiving learning\u2026" })
|
|
28315
|
-
] });
|
|
28316
|
-
}
|
|
28317
|
-
return /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
28318
|
-
/* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713" }),
|
|
28319
|
-
/* @__PURE__ */ jsx11(Text11, { children: " Learning archived" })
|
|
28320
|
-
] });
|
|
28321
|
-
}
|
|
28322
|
-
async function runLearningsArchive(opts) {
|
|
28323
|
-
const run = async () => {
|
|
28324
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
28325
|
-
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28326
|
-
await convexClient.mutation(api.learnings.archive, {
|
|
28327
|
-
organizationId: organizationId2,
|
|
28328
|
-
learningId: opts.id
|
|
28329
|
-
});
|
|
28330
|
-
};
|
|
28331
|
-
if (opts.json) {
|
|
28332
|
-
await run();
|
|
28333
|
-
jsonSuccess({ id: opts.id, ok: true });
|
|
28334
|
-
}
|
|
28335
|
-
const { unmount } = renderTransient(
|
|
28336
|
-
/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "archiving" } })
|
|
28337
|
-
);
|
|
28338
|
-
try {
|
|
28339
|
-
await run();
|
|
28340
|
-
} finally {
|
|
28341
|
-
unmount();
|
|
28342
|
-
}
|
|
28343
|
-
render11(/* @__PURE__ */ jsx11(ArchiveUI, { state: { type: "success" } })).unmount();
|
|
28344
|
-
}
|
|
28345
28300
|
|
|
28346
|
-
// src/commands/
|
|
28347
|
-
import
|
|
28348
|
-
|
|
28349
|
-
|
|
28350
|
-
|
|
28351
|
-
|
|
28352
|
-
|
|
28353
|
-
|
|
28354
|
-
"technical-accuracy": "Technical accuracy",
|
|
28355
|
-
other: "Other"
|
|
28356
|
-
};
|
|
28357
|
-
function LearningsListUI({ state }) {
|
|
28358
|
-
if (state.type === "loading") {
|
|
28359
|
-
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
28360
|
-
/* @__PURE__ */ jsx12(Spinner10, {}),
|
|
28361
|
-
/* @__PURE__ */ jsx12(Text12, { children: " Loading learnings\u2026" })
|
|
28362
|
-
] });
|
|
28363
|
-
}
|
|
28364
|
-
const { learnings } = state;
|
|
28365
|
-
if (learnings.length === 0) {
|
|
28366
|
-
return /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "No learnings found." }) });
|
|
28367
|
-
}
|
|
28368
|
-
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
28369
|
-
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
|
|
28370
|
-
learnings.length,
|
|
28371
|
-
" learning",
|
|
28372
|
-
learnings.length === 1 ? "" : "s"
|
|
28373
|
-
] }) }),
|
|
28374
|
-
learnings.map((l) => /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
|
|
28375
|
-
/* @__PURE__ */ jsxs12(Box12, { gap: 1, children: [
|
|
28376
|
-
/* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
|
|
28377
|
-
l.srcLocale,
|
|
28378
|
-
" \u2192 ",
|
|
28379
|
-
l.dstLocale
|
|
28380
|
-
] }),
|
|
28381
|
-
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
28382
|
-
"[",
|
|
28383
|
-
feedbackLabels[l.feedbackType] ?? l.feedbackType,
|
|
28384
|
-
"]"
|
|
28385
|
-
] }),
|
|
28386
|
-
l.status === "archived" && /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "[archived]" })
|
|
28387
|
-
] }),
|
|
28388
|
-
/* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
28389
|
-
'"',
|
|
28390
|
-
l.sourceText,
|
|
28391
|
-
'" \u2192 "',
|
|
28392
|
-
l.suggestedTranslation,
|
|
28393
|
-
'"'
|
|
28394
|
-
] }) }),
|
|
28395
|
-
l.reasoning && /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
28396
|
-
"Reason: ",
|
|
28397
|
-
l.reasoning
|
|
28398
|
-
] }) }),
|
|
28399
|
-
/* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
28400
|
-
"ID: ",
|
|
28401
|
-
l._id
|
|
28402
|
-
] }) })
|
|
28403
|
-
] }, l._id))
|
|
28404
|
-
] });
|
|
28405
|
-
}
|
|
28406
|
-
async function loadLearnings(opts) {
|
|
28407
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
28408
|
-
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28409
|
-
return convexClient.query(api.learnings.listByProject, {
|
|
28410
|
-
organizationId: organizationId2,
|
|
28411
|
-
projectId,
|
|
28412
|
-
status: opts.status,
|
|
28413
|
-
limit: opts.limit
|
|
28301
|
+
// src/commands/translate/context.ts
|
|
28302
|
+
import path5 from "path";
|
|
28303
|
+
async function loadPlanContext(cwd) {
|
|
28304
|
+
const config3 = readConfig(cwd);
|
|
28305
|
+
if (!config3) throw noConfigError();
|
|
28306
|
+
const client3 = await getAuthenticatedConvexClient();
|
|
28307
|
+
const project = await client3.query(api.projects.getProjectForCli, {
|
|
28308
|
+
projectId: config3.config.projectId
|
|
28414
28309
|
});
|
|
28415
|
-
|
|
28416
|
-
|
|
28417
|
-
|
|
28418
|
-
|
|
28419
|
-
|
|
28420
|
-
|
|
28421
|
-
|
|
28422
|
-
sourceText: l.sourceText,
|
|
28423
|
-
suggestedTranslation: l.suggestedTranslation,
|
|
28424
|
-
srcLocale: l.srcLocale,
|
|
28425
|
-
dstLocale: l.dstLocale,
|
|
28426
|
-
feedbackType: l.feedbackType,
|
|
28427
|
-
reasoning: l.reasoning,
|
|
28428
|
-
status: l.status,
|
|
28429
|
-
source: l.source
|
|
28430
|
-
}))
|
|
28431
|
-
});
|
|
28432
|
-
}
|
|
28433
|
-
const { unmount } = renderTransient(
|
|
28434
|
-
/* @__PURE__ */ jsx12(LearningsListUI, { state: { type: "loading" } })
|
|
28435
|
-
);
|
|
28436
|
-
let learnings;
|
|
28437
|
-
try {
|
|
28438
|
-
learnings = await loadLearnings(opts);
|
|
28439
|
-
} finally {
|
|
28440
|
-
unmount();
|
|
28441
|
-
}
|
|
28442
|
-
render12(
|
|
28443
|
-
/* @__PURE__ */ jsx12(LearningsListUI, { state: { type: "success", learnings } })
|
|
28444
|
-
).unmount();
|
|
28445
|
-
}
|
|
28446
|
-
|
|
28447
|
-
// src/commands/learnings/remove.tsx
|
|
28448
|
-
import { Spinner as Spinner11 } from "@inkjs/ui";
|
|
28449
|
-
import { Box as Box13, render as render13, Text as Text13 } from "ink";
|
|
28450
|
-
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
28451
|
-
function RemoveUI2({ state }) {
|
|
28452
|
-
if (state.type === "removing") {
|
|
28453
|
-
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
28454
|
-
/* @__PURE__ */ jsx13(Spinner11, {}),
|
|
28455
|
-
/* @__PURE__ */ jsx13(Text13, { children: " Removing learning\u2026" })
|
|
28456
|
-
] });
|
|
28457
|
-
}
|
|
28458
|
-
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
28459
|
-
/* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
|
|
28460
|
-
/* @__PURE__ */ jsx13(Text13, { children: " Learning removed" })
|
|
28461
|
-
] });
|
|
28462
|
-
}
|
|
28463
|
-
async function runLearningsRemove(opts) {
|
|
28464
|
-
const run = async () => {
|
|
28465
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
28466
|
-
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
28467
|
-
await convexClient.mutation(api.learnings.remove, {
|
|
28468
|
-
organizationId: organizationId2,
|
|
28469
|
-
learningId: opts.id
|
|
28470
|
-
});
|
|
28310
|
+
if (!project) throw projectNotFoundError();
|
|
28311
|
+
setTelemetryOrganization(project.organizationId);
|
|
28312
|
+
return {
|
|
28313
|
+
root: path5.dirname(config3.path),
|
|
28314
|
+
project,
|
|
28315
|
+
client: client3,
|
|
28316
|
+
framework: config3.config.framework
|
|
28471
28317
|
};
|
|
28472
|
-
if (opts.json) {
|
|
28473
|
-
await run();
|
|
28474
|
-
jsonSuccess({ id: opts.id, ok: true });
|
|
28475
|
-
}
|
|
28476
|
-
const { unmount } = renderTransient(
|
|
28477
|
-
/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "removing" } })
|
|
28478
|
-
);
|
|
28479
|
-
try {
|
|
28480
|
-
await run();
|
|
28481
|
-
} finally {
|
|
28482
|
-
unmount();
|
|
28483
|
-
}
|
|
28484
|
-
render13(/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "success" } })).unmount();
|
|
28485
28318
|
}
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
|
|
28490
|
-
|
|
28491
|
-
|
|
28492
|
-
if (
|
|
28493
|
-
|
|
28494
|
-
|
|
28495
|
-
/* @__PURE__ */ jsx14(Text14, { children: " Loading learning\u2026" })
|
|
28496
|
-
] });
|
|
28497
|
-
}
|
|
28498
|
-
const { learning } = state;
|
|
28499
|
-
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
|
|
28500
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28501
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Source text:" }),
|
|
28502
|
-
/* @__PURE__ */ jsx14(Text14, { children: learning.sourceText })
|
|
28503
|
-
] }),
|
|
28504
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28505
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Suggested translation:" }),
|
|
28506
|
-
/* @__PURE__ */ jsx14(Text14, { children: learning.suggestedTranslation })
|
|
28507
|
-
] }),
|
|
28508
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28509
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Locales:" }),
|
|
28510
|
-
/* @__PURE__ */ jsxs14(Text14, { children: [
|
|
28511
|
-
learning.srcLocale,
|
|
28512
|
-
" \u2192 ",
|
|
28513
|
-
learning.dstLocale
|
|
28514
|
-
] })
|
|
28515
|
-
] }),
|
|
28516
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28517
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Feedback type:" }),
|
|
28518
|
-
/* @__PURE__ */ jsx14(Text14, { children: learning.feedbackType })
|
|
28519
|
-
] }),
|
|
28520
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28521
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Reasoning:" }),
|
|
28522
|
-
/* @__PURE__ */ jsx14(Text14, { children: learning.reasoning })
|
|
28523
|
-
] }),
|
|
28524
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28525
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Status:" }),
|
|
28526
|
-
/* @__PURE__ */ jsx14(Text14, { color: learning.status === "active" ? "green" : "yellow", children: learning.status })
|
|
28527
|
-
] }),
|
|
28528
|
-
/* @__PURE__ */ jsxs14(Box14, { gap: 1, children: [
|
|
28529
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: "Source:" }),
|
|
28530
|
-
/* @__PURE__ */ jsx14(Text14, { children: learning.source })
|
|
28531
|
-
] }),
|
|
28532
|
-
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
|
|
28533
|
-
"ID: ",
|
|
28534
|
-
learning._id
|
|
28535
|
-
] }) })
|
|
28536
|
-
] });
|
|
28319
|
+
function describePlan(plan) {
|
|
28320
|
+
const { summary } = plan;
|
|
28321
|
+
const lines = [
|
|
28322
|
+
`${summary.totalKeys} keys expected, ${summary.upToDate} up to date`
|
|
28323
|
+
];
|
|
28324
|
+
if (summary.missing > 0) lines.push(`${summary.missing} missing`);
|
|
28325
|
+
if (summary.changed > 0)
|
|
28326
|
+
lines.push(`${summary.changed} changed since translated`);
|
|
28327
|
+
return [...lines, ...describeLeftOut(plan)];
|
|
28537
28328
|
}
|
|
28538
|
-
|
|
28539
|
-
const
|
|
28540
|
-
const
|
|
28541
|
-
const
|
|
28542
|
-
|
|
28543
|
-
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
throw new CliError(
|
|
28547
|
-
`Learning ${id} was not found.`,
|
|
28548
|
-
"List them with 'tolkbot learnings list'.",
|
|
28549
|
-
"not_found"
|
|
28329
|
+
function describeLeftOut(plan) {
|
|
28330
|
+
const { summary } = plan;
|
|
28331
|
+
const lines = [];
|
|
28332
|
+
const protectedCount = plan.protectedKeys.length;
|
|
28333
|
+
if (protectedCount > 0) {
|
|
28334
|
+
const reasons = Object.entries(summary.protected).map(([reason, n]) => `${n} ${reason}`).join(", ");
|
|
28335
|
+
lines.push(
|
|
28336
|
+
`${protectedCount} changed but kept for a person (${reasons})`
|
|
28550
28337
|
);
|
|
28551
28338
|
}
|
|
28552
|
-
|
|
28553
|
-
}
|
|
28554
|
-
|
|
28555
|
-
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
id: learning2._id,
|
|
28559
|
-
sourceText: learning2.sourceText,
|
|
28560
|
-
suggestedTranslation: learning2.suggestedTranslation,
|
|
28561
|
-
srcLocale: learning2.srcLocale,
|
|
28562
|
-
dstLocale: learning2.dstLocale,
|
|
28563
|
-
feedbackType: learning2.feedbackType,
|
|
28564
|
-
reasoning: learning2.reasoning,
|
|
28565
|
-
status: learning2.status,
|
|
28566
|
-
source: learning2.source
|
|
28567
|
-
});
|
|
28568
|
-
}
|
|
28569
|
-
const { unmount } = renderTransient(
|
|
28570
|
-
/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "loading" } })
|
|
28571
|
-
);
|
|
28572
|
-
let learning;
|
|
28573
|
-
try {
|
|
28574
|
-
learning = await loadLearning(opts.id);
|
|
28575
|
-
} finally {
|
|
28576
|
-
unmount();
|
|
28577
|
-
}
|
|
28578
|
-
render14(/* @__PURE__ */ jsx14(LearningShowUI, { state: { type: "success", learning } })).unmount();
|
|
28579
|
-
}
|
|
28580
|
-
|
|
28581
|
-
// src/commands/learnings/index.ts
|
|
28582
|
-
var learningsCommand = new Command3("learnings").description(
|
|
28583
|
-
"View and manage translation learnings"
|
|
28584
|
-
);
|
|
28585
|
-
learningsCommand.command("list").description("List translation learnings for the current project").option("--status <status>", "Filter by status (active or archived)").option("--limit <n>", "Max number of results", (v) => Number(v), 100).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
28586
|
-
const schema = z5.object({
|
|
28587
|
-
status: z5.enum(["active", "archived"]).optional(),
|
|
28588
|
-
limit: z5.coerce.number().int().positive().default(100),
|
|
28589
|
-
json: z5.boolean().optional()
|
|
28590
|
-
});
|
|
28591
|
-
const parsed = validateOrExit(schema, opts);
|
|
28592
|
-
await runLearningsList(parsed);
|
|
28593
|
-
});
|
|
28594
|
-
learningsCommand.command("show").description("Show details of a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
28595
|
-
const schema = z5.object({
|
|
28596
|
-
id: z5.string().min(1),
|
|
28597
|
-
json: z5.boolean().optional()
|
|
28598
|
-
});
|
|
28599
|
-
const parsed = validateOrExit(schema, opts);
|
|
28600
|
-
await runLearningsShow(parsed);
|
|
28601
|
-
});
|
|
28602
|
-
learningsCommand.command("archive").description("Archive a learning (keeps it but excludes from translations)").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
28603
|
-
const schema = z5.object({
|
|
28604
|
-
id: z5.string().min(1),
|
|
28605
|
-
json: z5.boolean().optional()
|
|
28606
|
-
});
|
|
28607
|
-
const parsed = validateOrExit(schema, opts);
|
|
28608
|
-
await runLearningsArchive(parsed);
|
|
28609
|
-
});
|
|
28610
|
-
learningsCommand.command("remove").description("Permanently remove a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
28611
|
-
const schema = z5.object({
|
|
28612
|
-
id: z5.string().min(1),
|
|
28613
|
-
json: z5.boolean().optional()
|
|
28614
|
-
});
|
|
28615
|
-
const parsed = validateOrExit(schema, opts);
|
|
28616
|
-
await runLearningsRemove(parsed);
|
|
28617
|
-
});
|
|
28618
|
-
|
|
28619
|
-
// src/commands/telemetry.ts
|
|
28620
|
-
import { Command as Command4 } from "commander";
|
|
28621
|
-
import { z as z6 } from "zod/v4";
|
|
28622
|
-
var REASONS = {
|
|
28623
|
-
env: `disabled by $${TELEMETRY_ENV_VAR}`,
|
|
28624
|
-
"do-not-track": `disabled by $${DO_NOT_TRACK_ENV_VAR}`,
|
|
28625
|
-
preference: "disabled with `tolkbot telemetry off`",
|
|
28626
|
-
"no-key": "disabled: this build has no telemetry key",
|
|
28627
|
-
default: "enabled"
|
|
28628
|
-
};
|
|
28629
|
-
async function printStatus(json2) {
|
|
28630
|
-
const state = await telemetryState();
|
|
28631
|
-
if (json2) {
|
|
28632
|
-
jsonResult({ ...state, preferenceFile: getPreferencePath() }, 0);
|
|
28633
|
-
return;
|
|
28339
|
+
if (summary.held > 0)
|
|
28340
|
+
lines.push(`${summary.held} missing but held by a person`);
|
|
28341
|
+
if (summary.untracked > 0) {
|
|
28342
|
+
lines.push(
|
|
28343
|
+
plan.tasks.some((t) => t.reason === "untracked") ? `${summary.untracked} untracked, being retranslated` : `${summary.untracked} untracked translations kept: TolkBot has no record of them. Run "tolkbot translate adopt" to keep them as reviewed, or translate with --retranslate-untracked to replace them.`
|
|
28344
|
+
);
|
|
28634
28345
|
}
|
|
28635
|
-
|
|
28636
|
-
`Telemetry is ${state.enabled ? "on" : "off"} (${REASONS[state.reason]}).`
|
|
28637
|
-
);
|
|
28638
|
-
console.log(
|
|
28639
|
-
`Turn it off with \`tolkbot telemetry off\`, $${TELEMETRY_ENV_VAR}=0 or $${DO_NOT_TRACK_ENV_VAR}=1.`
|
|
28640
|
-
);
|
|
28346
|
+
return lines;
|
|
28641
28347
|
}
|
|
28642
|
-
var telemetryCommand = new Command4("telemetry").description(
|
|
28643
|
-
"Control anonymous usage reporting"
|
|
28644
|
-
);
|
|
28645
|
-
telemetryCommand.command("status", { isDefault: true }).description("Show whether telemetry is on and why").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
28646
|
-
const parsed = validateOrExit(
|
|
28647
|
-
z6.object({ json: z6.boolean().optional() }),
|
|
28648
|
-
opts
|
|
28649
|
-
);
|
|
28650
|
-
await printStatus(parsed.json);
|
|
28651
|
-
});
|
|
28652
|
-
telemetryCommand.command("off").description("Stop reporting usage from this machine").action(async () => {
|
|
28653
|
-
await setTelemetryPreference(false);
|
|
28654
|
-
console.log(
|
|
28655
|
-
`Telemetry is off. Preference saved to ${getPreferencePath()}.`
|
|
28656
|
-
);
|
|
28657
|
-
});
|
|
28658
|
-
telemetryCommand.command("on").description("Resume reporting usage from this machine").action(async () => {
|
|
28659
|
-
await setTelemetryPreference(true);
|
|
28660
|
-
await printStatus(false);
|
|
28661
|
-
});
|
|
28662
|
-
|
|
28663
|
-
// src/commands/translate/index.ts
|
|
28664
|
-
import { Command as Command5 } from "commander";
|
|
28665
|
-
import { z as z7 } from "zod/v4";
|
|
28666
28348
|
|
|
28667
|
-
// src/commands/translate/
|
|
28668
|
-
import fs8 from "fs/promises";
|
|
28669
|
-
import path8 from "path";
|
|
28670
|
-
import { Spinner as Spinner13 } from "@inkjs/ui";
|
|
28671
|
-
import { Box as Box15, render as render15, Text as Text15 } from "ink";
|
|
28672
|
-
|
|
28673
|
-
// src/commands/translate/task-generator.ts
|
|
28349
|
+
// src/commands/translate/apply-core.ts
|
|
28674
28350
|
import fs7 from "fs/promises";
|
|
28675
|
-
import
|
|
28351
|
+
import path8 from "path";
|
|
28676
28352
|
|
|
28677
28353
|
// ../../node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.mjs
|
|
28678
28354
|
var emoji_regex_default = () => {
|
|
@@ -28766,7 +28442,7 @@ function eastAsianWidth(codePoint, { ambiguousAsWide = false } = {}) {
|
|
|
28766
28442
|
// ../parser/dist/index.js
|
|
28767
28443
|
var import_fast_glob = __toESM(require_out4(), 1);
|
|
28768
28444
|
var import_fast_glob2 = __toESM(require_out4(), 1);
|
|
28769
|
-
import
|
|
28445
|
+
import path6 from "path";
|
|
28770
28446
|
import fs5 from "fs/promises";
|
|
28771
28447
|
import path23 from "path";
|
|
28772
28448
|
import fs23 from "fs/promises";
|
|
@@ -28875,11 +28551,11 @@ function flattenValues(data) {
|
|
|
28875
28551
|
const values = /* @__PURE__ */ new Map();
|
|
28876
28552
|
function walk(obj, prefix) {
|
|
28877
28553
|
for (const [key, value] of Object.entries(obj)) {
|
|
28878
|
-
const
|
|
28554
|
+
const fullKey5 = prefix ? `${prefix}.${key}` : key;
|
|
28879
28555
|
if (typeof value === "string") {
|
|
28880
|
-
values.set(
|
|
28556
|
+
values.set(fullKey5, value);
|
|
28881
28557
|
} else if (typeof value === "object" && value !== null) {
|
|
28882
|
-
walk(value,
|
|
28558
|
+
walk(value, fullKey5);
|
|
28883
28559
|
}
|
|
28884
28560
|
}
|
|
28885
28561
|
}
|
|
@@ -29917,7 +29593,7 @@ var BaseAdapter = class {
|
|
|
29917
29593
|
parseErrors: []
|
|
29918
29594
|
};
|
|
29919
29595
|
}
|
|
29920
|
-
const relativePaths = files.map((f) =>
|
|
29596
|
+
const relativePaths = files.map((f) => path6.relative(baseDir, f));
|
|
29921
29597
|
const groupedPaths = groupByLocale(
|
|
29922
29598
|
relativePaths,
|
|
29923
29599
|
options?.knownLocales
|
|
@@ -29927,7 +29603,7 @@ var BaseAdapter = class {
|
|
|
29927
29603
|
for (const [locale, localePaths] of groupedPaths) {
|
|
29928
29604
|
const translationFiles = [];
|
|
29929
29605
|
for (const relativePath of localePaths) {
|
|
29930
|
-
const absolutePath =
|
|
29606
|
+
const absolutePath = path6.join(baseDir, relativePath);
|
|
29931
29607
|
try {
|
|
29932
29608
|
const file2 = await this.parse(absolutePath);
|
|
29933
29609
|
translationFiles.push(file2);
|
|
@@ -30868,10 +30544,175 @@ function pluralFormContext(plural2, locale, sourceKey) {
|
|
|
30868
30544
|
return parts.join(" ");
|
|
30869
30545
|
}
|
|
30870
30546
|
|
|
30871
|
-
// src/commands/translate/
|
|
30547
|
+
// src/commands/translate/project-files.ts
|
|
30548
|
+
import fs6 from "fs/promises";
|
|
30549
|
+
import path7 from "path";
|
|
30550
|
+
function fullPattern(scope) {
|
|
30551
|
+
return scope.subpath ? path7.posix.join(scope.subpath, scope.sourcePattern) : scope.sourcePattern;
|
|
30552
|
+
}
|
|
30553
|
+
function formatOf(scope) {
|
|
30554
|
+
return detectFormat(scope.sourcePattern) ?? "json";
|
|
30555
|
+
}
|
|
30556
|
+
async function listLocaleFiles(root, scope) {
|
|
30557
|
+
const pattern = fullPattern(scope);
|
|
30558
|
+
const adapter = createAdapter(formatOf(scope));
|
|
30559
|
+
const match = createPathMatcher(pattern);
|
|
30560
|
+
const found = await adapter.findFiles(root, withNamespaceWildcard(pattern));
|
|
30561
|
+
const files = [];
|
|
30562
|
+
for (const absolutePath of found.sort()) {
|
|
30563
|
+
const relativePath = path7.relative(root, absolutePath).split(path7.sep).join("/");
|
|
30564
|
+
const m = match(relativePath);
|
|
30565
|
+
if (!m) continue;
|
|
30566
|
+
files.push({
|
|
30567
|
+
pathTemplate: replaceNamespacePlaceholder(
|
|
30568
|
+
scope.sourcePattern,
|
|
30569
|
+
m.namespace ?? ""
|
|
30570
|
+
),
|
|
30571
|
+
namespace: m.namespace,
|
|
30572
|
+
locale: m.locale,
|
|
30573
|
+
relativePath,
|
|
30574
|
+
absolutePath
|
|
30575
|
+
});
|
|
30576
|
+
}
|
|
30577
|
+
return files;
|
|
30578
|
+
}
|
|
30579
|
+
function filesForLocale(files, locale) {
|
|
30580
|
+
return files.filter((file2) => sameLocale(file2.locale, locale));
|
|
30581
|
+
}
|
|
30582
|
+
function targetFileFor(source, targetLocale, files, scope) {
|
|
30583
|
+
const existing = files.find(
|
|
30584
|
+
(file2) => file2.pathTemplate === source.pathTemplate && sameLocale(file2.locale, targetLocale)
|
|
30585
|
+
) ?? null;
|
|
30586
|
+
if (existing) return { relativePath: existing.relativePath, existing };
|
|
30587
|
+
const spelling = files.find((file2) => sameLocale(file2.locale, targetLocale))?.locale ?? scope.detectedLocales?.find((l) => sameLocale(l, targetLocale)) ?? targetLocale;
|
|
30588
|
+
const relativePath = replaceLocalePlaceholder(
|
|
30589
|
+
replaceNamespacePlaceholder(fullPattern(scope), source.namespace ?? ""),
|
|
30590
|
+
spelling
|
|
30591
|
+
);
|
|
30592
|
+
return { relativePath, existing: null };
|
|
30593
|
+
}
|
|
30594
|
+
async function readTranslationFile(absolutePath, format) {
|
|
30595
|
+
try {
|
|
30596
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
30597
|
+
const file2 = await createAdapter(format).parse(absolutePath);
|
|
30598
|
+
return { data: file2.data, content };
|
|
30599
|
+
} catch (error62) {
|
|
30600
|
+
return {
|
|
30601
|
+
error: error62 instanceof Error ? error62.message : String(error62)
|
|
30602
|
+
};
|
|
30603
|
+
}
|
|
30604
|
+
}
|
|
30605
|
+
|
|
30606
|
+
// src/commands/translate/apply-core.ts
|
|
30872
30607
|
function fullKey(namespace, key) {
|
|
30873
30608
|
return namespace ? `${namespace}.${key}` : key;
|
|
30874
30609
|
}
|
|
30610
|
+
async function applyTasks(input2) {
|
|
30611
|
+
const { root, scope, dstLocale } = input2;
|
|
30612
|
+
const format = formatOf(scope);
|
|
30613
|
+
const files = await listLocaleFiles(root, scope);
|
|
30614
|
+
const result = { written: [], refused: [] };
|
|
30615
|
+
const refuse = (task, reason) => result.refused.push({ key: fullKey(task.namespace, task.key), reason });
|
|
30616
|
+
const bySourcePath = /* @__PURE__ */ new Map();
|
|
30617
|
+
for (const task of input2.tasks) {
|
|
30618
|
+
if (task.status !== "succeeded" || !task.dst || !task.path) {
|
|
30619
|
+
refuse(task, "not_translated");
|
|
30620
|
+
continue;
|
|
30621
|
+
}
|
|
30622
|
+
const group = bySourcePath.get(task.path) ?? [];
|
|
30623
|
+
group.push(task);
|
|
30624
|
+
bySourcePath.set(task.path, group);
|
|
30625
|
+
}
|
|
30626
|
+
for (const [sourcePath, tasks] of bySourcePath) {
|
|
30627
|
+
const sourceFile = files.find(
|
|
30628
|
+
(file2) => file2.relativePath === sourcePath
|
|
30629
|
+
);
|
|
30630
|
+
const source = sourceFile ? await readTranslationFile(sourceFile.absolutePath, format) : void 0;
|
|
30631
|
+
if (!sourceFile || !source || source.error !== void 0) {
|
|
30632
|
+
for (const task of tasks) refuse(task, "no_source_file");
|
|
30633
|
+
continue;
|
|
30634
|
+
}
|
|
30635
|
+
const baseKeys = new Set(getKeys(source.data));
|
|
30636
|
+
const target = targetFileFor(sourceFile, dstLocale, files, scope);
|
|
30637
|
+
const targetPath = path8.join(root, ...target.relativePath.split("/"));
|
|
30638
|
+
const existing = target.existing ? await readTranslationFile(target.existing.absolutePath, format) : void 0;
|
|
30639
|
+
if (existing?.error !== void 0) {
|
|
30640
|
+
for (const task of tasks) refuse(task, "target_unreadable");
|
|
30641
|
+
continue;
|
|
30642
|
+
}
|
|
30643
|
+
let data = existing?.data ?? {};
|
|
30644
|
+
const written = {
|
|
30645
|
+
pathTemplate: sourceFile.pathTemplate,
|
|
30646
|
+
relativePath: target.relativePath,
|
|
30647
|
+
entries: []
|
|
30648
|
+
};
|
|
30649
|
+
for (const task of tasks) {
|
|
30650
|
+
const sourceKey = sourceKeyFor(task.key, baseKeys);
|
|
30651
|
+
const currentSource = sourceKey === null ? void 0 : getByPath(source.data, sourceKey);
|
|
30652
|
+
if (typeof currentSource !== "string" || computeValueHash(currentSource) !== computeValueHash(task.src)) {
|
|
30653
|
+
refuse(task, "source_changed");
|
|
30654
|
+
continue;
|
|
30655
|
+
}
|
|
30656
|
+
const translatedValue = task.dst ?? "";
|
|
30657
|
+
const current = getByPath(data, task.key);
|
|
30658
|
+
const currentHash = typeof current === "string" ? computeValueHash(current) : null;
|
|
30659
|
+
if (task.expectedTargetHash !== void 0 && task.expectedTargetHash !== currentHash) {
|
|
30660
|
+
refuse(
|
|
30661
|
+
task,
|
|
30662
|
+
current === translatedValue ? "already_applied" : "target_changed"
|
|
30663
|
+
);
|
|
30664
|
+
continue;
|
|
30665
|
+
}
|
|
30666
|
+
data = setByPath(data, task.key, translatedValue);
|
|
30667
|
+
written.entries.push({
|
|
30668
|
+
key: task.key,
|
|
30669
|
+
namespace: task.namespace,
|
|
30670
|
+
baseValue: task.src,
|
|
30671
|
+
translatedValue,
|
|
30672
|
+
changesFile: current !== translatedValue
|
|
30673
|
+
});
|
|
30674
|
+
}
|
|
30675
|
+
if (written.entries.length === 0) continue;
|
|
30676
|
+
result.written.push(written);
|
|
30677
|
+
if (input2.dryRun) continue;
|
|
30678
|
+
await writeTranslationFile({
|
|
30679
|
+
format,
|
|
30680
|
+
targetPath,
|
|
30681
|
+
data,
|
|
30682
|
+
dstLocale,
|
|
30683
|
+
existingContent: existing?.content,
|
|
30684
|
+
sourceContent: source.content
|
|
30685
|
+
});
|
|
30686
|
+
}
|
|
30687
|
+
return result;
|
|
30688
|
+
}
|
|
30689
|
+
async function writeTranslationFile(input2) {
|
|
30690
|
+
const { targetPath, data } = input2;
|
|
30691
|
+
const model = input2.existingContent ?? input2.sourceContent;
|
|
30692
|
+
await fs7.mkdir(path8.dirname(targetPath), { recursive: true });
|
|
30693
|
+
if (input2.format === "json") {
|
|
30694
|
+
await fs7.writeFile(
|
|
30695
|
+
targetPath,
|
|
30696
|
+
serializeJsonContent(data, detectJsonStyle(model)),
|
|
30697
|
+
"utf-8"
|
|
30698
|
+
);
|
|
30699
|
+
return;
|
|
30700
|
+
}
|
|
30701
|
+
const style = detectTsStyle(model);
|
|
30702
|
+
const shape = detectTsExportFormat(model);
|
|
30703
|
+
const content = serializeTsContent(data, {
|
|
30704
|
+
...style,
|
|
30705
|
+
printWidth: style.printWidth ?? void 0,
|
|
30706
|
+
exportName: input2.existingContent ? shape?.exportName ?? void 0 : localeExportIdentifier(input2.dstLocale),
|
|
30707
|
+
useDefaultExport: shape?.useDefaultExport ?? false
|
|
30708
|
+
});
|
|
30709
|
+
await fs7.writeFile(targetPath, content, "utf-8");
|
|
30710
|
+
}
|
|
30711
|
+
|
|
30712
|
+
// src/commands/translate/plan-core.ts
|
|
30713
|
+
function fullKey2(namespace, key) {
|
|
30714
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
30715
|
+
}
|
|
30875
30716
|
function expectedFullKeys(sources, targetLocale) {
|
|
30876
30717
|
const keys = [];
|
|
30877
30718
|
for (const { file: file2, data } of sources) {
|
|
@@ -30879,7 +30720,7 @@ function expectedFullKeys(sources, targetLocale) {
|
|
|
30879
30720
|
getKeys(data),
|
|
30880
30721
|
targetLocale
|
|
30881
30722
|
).keys())
|
|
30882
|
-
keys.push(
|
|
30723
|
+
keys.push(fullKey2(file2.namespace, key));
|
|
30883
30724
|
}
|
|
30884
30725
|
return keys;
|
|
30885
30726
|
}
|
|
@@ -30927,7 +30768,7 @@ function planLocale(input2) {
|
|
|
30927
30768
|
const src = getByPath(data, sourceKey);
|
|
30928
30769
|
if (typeof src !== "string") continue;
|
|
30929
30770
|
plan.summary.totalKeys++;
|
|
30930
|
-
const full =
|
|
30771
|
+
const full = fullKey2(file2.namespace, key);
|
|
30931
30772
|
const hold = input2.holds.get(full);
|
|
30932
30773
|
const current = getByPath(targetData, key);
|
|
30933
30774
|
const targetValue = typeof current === "string" ? current : void 0;
|
|
@@ -30998,74 +30839,8 @@ function planLocale(input2) {
|
|
|
30998
30839
|
return plan;
|
|
30999
30840
|
}
|
|
31000
30841
|
|
|
31001
|
-
// src/commands/translate/project-files.ts
|
|
31002
|
-
import fs6 from "fs/promises";
|
|
31003
|
-
import path6 from "path";
|
|
31004
|
-
function fullPattern(scope) {
|
|
31005
|
-
return scope.subpath ? path6.posix.join(scope.subpath, scope.sourcePattern) : scope.sourcePattern;
|
|
31006
|
-
}
|
|
31007
|
-
function formatOf(scope) {
|
|
31008
|
-
return detectFormat(scope.sourcePattern) ?? "json";
|
|
31009
|
-
}
|
|
31010
|
-
async function listLocaleFiles(root, scope) {
|
|
31011
|
-
const pattern = fullPattern(scope);
|
|
31012
|
-
const adapter = createAdapter(formatOf(scope));
|
|
31013
|
-
const match = createPathMatcher(pattern);
|
|
31014
|
-
const found = await adapter.findFiles(root, withNamespaceWildcard(pattern));
|
|
31015
|
-
const files = [];
|
|
31016
|
-
for (const absolutePath of found.sort()) {
|
|
31017
|
-
const relativePath = path6.relative(root, absolutePath).split(path6.sep).join("/");
|
|
31018
|
-
const m = match(relativePath);
|
|
31019
|
-
if (!m) continue;
|
|
31020
|
-
files.push({
|
|
31021
|
-
pathTemplate: replaceNamespacePlaceholder(
|
|
31022
|
-
scope.sourcePattern,
|
|
31023
|
-
m.namespace ?? ""
|
|
31024
|
-
),
|
|
31025
|
-
namespace: m.namespace,
|
|
31026
|
-
locale: m.locale,
|
|
31027
|
-
relativePath,
|
|
31028
|
-
absolutePath
|
|
31029
|
-
});
|
|
31030
|
-
}
|
|
31031
|
-
return files;
|
|
31032
|
-
}
|
|
31033
|
-
function filesForLocale(files, locale) {
|
|
31034
|
-
return files.filter((file2) => sameLocale(file2.locale, locale));
|
|
31035
|
-
}
|
|
31036
|
-
function targetFileFor(source, targetLocale, files, scope) {
|
|
31037
|
-
const existing = files.find(
|
|
31038
|
-
(file2) => file2.pathTemplate === source.pathTemplate && sameLocale(file2.locale, targetLocale)
|
|
31039
|
-
) ?? null;
|
|
31040
|
-
if (existing) return { relativePath: existing.relativePath, existing };
|
|
31041
|
-
const spelling = files.find((file2) => sameLocale(file2.locale, targetLocale))?.locale ?? scope.detectedLocales?.find((l) => sameLocale(l, targetLocale)) ?? targetLocale;
|
|
31042
|
-
const relativePath = replaceLocalePlaceholder(
|
|
31043
|
-
replaceNamespacePlaceholder(fullPattern(scope), source.namespace ?? ""),
|
|
31044
|
-
spelling
|
|
31045
|
-
);
|
|
31046
|
-
return { relativePath, existing: null };
|
|
31047
|
-
}
|
|
31048
|
-
async function readTranslationFile(absolutePath, format) {
|
|
31049
|
-
try {
|
|
31050
|
-
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
31051
|
-
const file2 = await createAdapter(format).parse(absolutePath);
|
|
31052
|
-
return { data: file2.data, content };
|
|
31053
|
-
} catch (error62) {
|
|
31054
|
-
return {
|
|
31055
|
-
error: error62 instanceof Error ? error62.message : String(error62)
|
|
31056
|
-
};
|
|
31057
|
-
}
|
|
31058
|
-
}
|
|
31059
|
-
|
|
31060
30842
|
// src/commands/translate/task-generator.ts
|
|
31061
30843
|
var HOLD_CHUNK = 2e3;
|
|
31062
|
-
async function loadTasksFromFile(tasksPath) {
|
|
31063
|
-
const abs = path7.resolve(process.cwd(), tasksPath);
|
|
31064
|
-
const raw = await fs7.readFile(abs, "utf8");
|
|
31065
|
-
const parsed = JSON.parse(raw);
|
|
31066
|
-
if (!Array.isArray(parsed)) throw new Error("tasks file must be an array");
|
|
31067
|
-
return parsed;
|
|
31068
|
-
}
|
|
31069
30844
|
function scopeOf(project) {
|
|
31070
30845
|
return {
|
|
31071
30846
|
sourcePattern: project.sourcePattern,
|
|
@@ -31150,364 +30925,16 @@ async function generateTasks(ctx, srcLocale, dstLocale, opts = {}) {
|
|
|
31150
30925
|
};
|
|
31151
30926
|
}
|
|
31152
30927
|
|
|
31153
|
-
// src/commands/
|
|
31154
|
-
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
31155
|
-
async function loadPlanContext(cwd) {
|
|
31156
|
-
const config3 = readConfig(cwd);
|
|
31157
|
-
if (!config3) throw noConfigError();
|
|
31158
|
-
const client3 = await getAuthenticatedConvexClient();
|
|
31159
|
-
const project = await client3.query(api.projects.getProjectForCli, {
|
|
31160
|
-
projectId: config3.config.projectId
|
|
31161
|
-
});
|
|
31162
|
-
if (!project) throw projectNotFoundError();
|
|
31163
|
-
setTelemetryOrganization(project.organizationId);
|
|
31164
|
-
return {
|
|
31165
|
-
root: path8.dirname(config3.path),
|
|
31166
|
-
project,
|
|
31167
|
-
client: client3,
|
|
31168
|
-
framework: config3.config.framework
|
|
31169
|
-
};
|
|
31170
|
-
}
|
|
31171
|
-
function describePlan(plan) {
|
|
31172
|
-
const { summary } = plan;
|
|
31173
|
-
const lines = [
|
|
31174
|
-
`${summary.totalKeys} keys expected, ${summary.upToDate} up to date`
|
|
31175
|
-
];
|
|
31176
|
-
if (summary.missing > 0) lines.push(`${summary.missing} missing`);
|
|
31177
|
-
if (summary.changed > 0)
|
|
31178
|
-
lines.push(`${summary.changed} changed since translated`);
|
|
31179
|
-
const protectedCount = plan.protectedKeys.length;
|
|
31180
|
-
if (protectedCount > 0) {
|
|
31181
|
-
const reasons = Object.entries(summary.protected).map(([reason, n]) => `${n} ${reason}`).join(", ");
|
|
31182
|
-
lines.push(
|
|
31183
|
-
`${protectedCount} changed but kept for a person (${reasons})`
|
|
31184
|
-
);
|
|
31185
|
-
}
|
|
31186
|
-
if (summary.held > 0)
|
|
31187
|
-
lines.push(`${summary.held} missing but held by a person`);
|
|
31188
|
-
if (summary.untracked > 0) {
|
|
31189
|
-
lines.push(
|
|
31190
|
-
plan.tasks.some((t) => t.reason === "untracked") ? `${summary.untracked} untracked, being retranslated` : `${summary.untracked} untracked translations kept: TolkBot has no record of them. Run "tolkbot translate adopt" to keep them as reviewed, or plan with --retranslate-untracked to replace them.`
|
|
31191
|
-
);
|
|
31192
|
-
}
|
|
31193
|
-
return lines;
|
|
31194
|
-
}
|
|
31195
|
-
function PlanUI({
|
|
31196
|
-
state,
|
|
31197
|
-
src,
|
|
31198
|
-
dst
|
|
31199
|
-
}) {
|
|
31200
|
-
if (state.type === "loading") {
|
|
31201
|
-
return /* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31202
|
-
/* @__PURE__ */ jsx15(Spinner13, {}),
|
|
31203
|
-
/* @__PURE__ */ jsx15(Text15, { children: " Loading configuration\u2026" })
|
|
31204
|
-
] });
|
|
31205
|
-
}
|
|
31206
|
-
if (state.type === "planning") {
|
|
31207
|
-
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31208
|
-
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31209
|
-
/* @__PURE__ */ jsx15(Spinner13, {}),
|
|
31210
|
-
/* @__PURE__ */ jsx15(Text15, { children: " Analyzing translation needs\u2026" })
|
|
31211
|
-
] }),
|
|
31212
|
-
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31213
|
-
src,
|
|
31214
|
-
" \u2192 ",
|
|
31215
|
-
dst
|
|
31216
|
-
] }) })
|
|
31217
|
-
] });
|
|
31218
|
-
}
|
|
31219
|
-
const { plan } = state;
|
|
31220
|
-
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31221
|
-
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31222
|
-
/* @__PURE__ */ jsx15(Text15, { color: "green", children: "\u2713" }),
|
|
31223
|
-
/* @__PURE__ */ jsx15(Text15, { children: " Translation plan generated" })
|
|
31224
|
-
] }),
|
|
31225
|
-
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31226
|
-
src,
|
|
31227
|
-
" \u2192 ",
|
|
31228
|
-
dst,
|
|
31229
|
-
": ",
|
|
31230
|
-
plan.tasks.length,
|
|
31231
|
-
" task(s)"
|
|
31232
|
-
] }) }),
|
|
31233
|
-
describePlan(plan).map((line) => /* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: line }) }, line)),
|
|
31234
|
-
plan.tasks.length === 0 && /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: "yellow", children: [
|
|
31235
|
-
"No translations needed - ",
|
|
31236
|
-
dst,
|
|
31237
|
-
" is up to date."
|
|
31238
|
-
] }) })
|
|
31239
|
-
] });
|
|
31240
|
-
}
|
|
31241
|
-
function planOutput(plan) {
|
|
31242
|
-
return {
|
|
31243
|
-
tasks: plan.tasks,
|
|
31244
|
-
summary: plan.summary,
|
|
31245
|
-
protectedKeys: plan.protectedKeys,
|
|
31246
|
-
heldKeys: plan.heldKeys,
|
|
31247
|
-
untrackedKeys: plan.untrackedKeys.map((k) => k.key)
|
|
31248
|
-
};
|
|
31249
|
-
}
|
|
31250
|
-
async function runPlan(opts) {
|
|
31251
|
-
const cwd = process.cwd();
|
|
31252
|
-
if (opts.json) {
|
|
31253
|
-
const ctx = await loadPlanContext(cwd);
|
|
31254
|
-
const src2 = opts.src ?? ctx.project.baseLanguage;
|
|
31255
|
-
const plan2 = await planForLocale(ctx, src2, opts.dst, {
|
|
31256
|
-
retranslateUntracked: opts.retranslateUntracked
|
|
31257
|
-
});
|
|
31258
|
-
track("cli_translate_completed", {
|
|
31259
|
-
mode: "plan",
|
|
31260
|
-
key_count: plan2.tasks.length,
|
|
31261
|
-
locale_count: 1,
|
|
31262
|
-
job_count: 0,
|
|
31263
|
-
json: true
|
|
31264
|
-
});
|
|
31265
|
-
if (opts.write) {
|
|
31266
|
-
const outputPath = await writeTasks(cwd, opts.write, plan2);
|
|
31267
|
-
jsonSuccess({ ...planOutput(plan2), writtenTo: outputPath });
|
|
31268
|
-
}
|
|
31269
|
-
jsonSuccess(planOutput(plan2));
|
|
31270
|
-
}
|
|
31271
|
-
let src = opts.src ?? "";
|
|
31272
|
-
const progress = renderTransient(
|
|
31273
|
-
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "loading" }, src, dst: opts.dst })
|
|
31274
|
-
);
|
|
31275
|
-
let plan;
|
|
31276
|
-
try {
|
|
31277
|
-
const ctx = await loadPlanContext(cwd);
|
|
31278
|
-
src = opts.src ?? ctx.project.baseLanguage;
|
|
31279
|
-
progress.rerender(
|
|
31280
|
-
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "planning" }, src, dst: opts.dst })
|
|
31281
|
-
);
|
|
31282
|
-
plan = await planForLocale(ctx, src, opts.dst, {
|
|
31283
|
-
retranslateUntracked: opts.retranslateUntracked
|
|
31284
|
-
});
|
|
31285
|
-
} finally {
|
|
31286
|
-
progress.unmount();
|
|
31287
|
-
}
|
|
31288
|
-
render15(
|
|
31289
|
-
/* @__PURE__ */ jsx15(PlanUI, { state: { type: "success", plan }, src, dst: opts.dst })
|
|
31290
|
-
).unmount();
|
|
31291
|
-
track("cli_translate_completed", {
|
|
31292
|
-
mode: "plan",
|
|
31293
|
-
key_count: plan.tasks.length,
|
|
31294
|
-
locale_count: 1,
|
|
31295
|
-
job_count: 0,
|
|
31296
|
-
json: false
|
|
31297
|
-
});
|
|
31298
|
-
if (opts.write) {
|
|
31299
|
-
const outputPath = await writeTasks(cwd, opts.write, plan);
|
|
31300
|
-
console.log(`
|
|
31301
|
-
Tasks written to: ${outputPath}`);
|
|
31302
|
-
} else {
|
|
31303
|
-
console.log("\n" + JSON.stringify(plan.tasks, null, 2));
|
|
31304
|
-
}
|
|
31305
|
-
}
|
|
31306
|
-
async function writeTasks(cwd, file2, plan) {
|
|
31307
|
-
const outputPath = path8.resolve(cwd, file2);
|
|
31308
|
-
await fs8.writeFile(
|
|
31309
|
-
outputPath,
|
|
31310
|
-
JSON.stringify(plan.tasks, null, 2) + "\n",
|
|
31311
|
-
"utf-8"
|
|
31312
|
-
);
|
|
31313
|
-
return outputPath;
|
|
31314
|
-
}
|
|
31315
|
-
|
|
31316
|
-
// src/commands/translate/adopt.tsx
|
|
30928
|
+
// src/commands/jobs/apply-job.ts
|
|
31317
30929
|
var STAMP_CHUNK = 200;
|
|
31318
|
-
async function adoptLocale(ctx, srcLocale, dstLocale) {
|
|
31319
|
-
const plan = await planForLocale(ctx, srcLocale, dstLocale);
|
|
31320
|
-
const locale = governanceLocale(ctx.project, dstLocale);
|
|
31321
|
-
const byTemplate = /* @__PURE__ */ new Map();
|
|
31322
|
-
for (const key of plan.untrackedKeys) {
|
|
31323
|
-
const group = byTemplate.get(key.pathTemplate) ?? [];
|
|
31324
|
-
group.push(key);
|
|
31325
|
-
byTemplate.set(key.pathTemplate, group);
|
|
31326
|
-
}
|
|
31327
|
-
const outcome = {
|
|
31328
|
-
dstLocale,
|
|
31329
|
-
untracked: plan.untrackedKeys.length,
|
|
31330
|
-
adopted: 0,
|
|
31331
|
-
skipped: 0
|
|
31332
|
-
};
|
|
31333
|
-
for (const [pathTemplate, keys] of byTemplate) {
|
|
31334
|
-
for (let i = 0; i < keys.length; i += STAMP_CHUNK) {
|
|
31335
|
-
const result = await ctx.client.mutation(
|
|
31336
|
-
api.localeKeyStates.adoptUntrackedForCli,
|
|
31337
|
-
{
|
|
31338
|
-
organizationId: ctx.project.organizationId,
|
|
31339
|
-
projectId: ctx.project._id,
|
|
31340
|
-
locale,
|
|
31341
|
-
pathTemplate,
|
|
31342
|
-
entries: keys.slice(i, i + STAMP_CHUNK).map((key) => ({
|
|
31343
|
-
key: key.fileKey,
|
|
31344
|
-
namespace: key.namespace,
|
|
31345
|
-
baseValue: key.src,
|
|
31346
|
-
translatedValue: key.dst
|
|
31347
|
-
}))
|
|
31348
|
-
}
|
|
31349
|
-
);
|
|
31350
|
-
outcome.adopted += result.adopted;
|
|
31351
|
-
outcome.skipped += result.skipped;
|
|
31352
|
-
}
|
|
31353
|
-
}
|
|
31354
|
-
return outcome;
|
|
31355
|
-
}
|
|
31356
|
-
async function runAdopt(opts) {
|
|
31357
|
-
const run = async () => {
|
|
31358
|
-
const ctx = await loadPlanContext(process.cwd());
|
|
31359
|
-
const src = opts.src ?? ctx.project.baseLanguage;
|
|
31360
|
-
const locales = opts.all ? ctx.project.targetLanguages : opts.dst ? [opts.dst] : [];
|
|
31361
|
-
if (locales.length === 0) {
|
|
31362
|
-
throw opts.all ? new CliError(
|
|
31363
|
-
"No target languages configured for this project.",
|
|
31364
|
-
void 0,
|
|
31365
|
-
"no_target_languages"
|
|
31366
|
-
) : new UsageError(
|
|
31367
|
-
"Either --dst <locale> or --all must be provided."
|
|
31368
|
-
);
|
|
31369
|
-
}
|
|
31370
|
-
const outcomes2 = [];
|
|
31371
|
-
for (const dst of locales)
|
|
31372
|
-
outcomes2.push(await adoptLocale(ctx, src, dst));
|
|
31373
|
-
track("cli_translate_completed", {
|
|
31374
|
-
mode: "adopt",
|
|
31375
|
-
key_count: outcomes2.reduce((n, o) => n + o.adopted, 0),
|
|
31376
|
-
locale_count: outcomes2.length,
|
|
31377
|
-
job_count: 0,
|
|
31378
|
-
json: opts.json === true
|
|
31379
|
-
});
|
|
31380
|
-
return outcomes2;
|
|
31381
|
-
};
|
|
31382
|
-
if (opts.json) {
|
|
31383
|
-
jsonSuccess({ adopted: await run() });
|
|
31384
|
-
}
|
|
31385
|
-
const outcomes = await run();
|
|
31386
|
-
for (const o of outcomes) {
|
|
31387
|
-
console.log(
|
|
31388
|
-
`\u2713 ${o.dstLocale}: adopted ${o.adopted} untracked translation(s)` + (o.skipped > 0 ? `, ${o.skipped} already tracked` : "")
|
|
31389
|
-
);
|
|
31390
|
-
}
|
|
31391
|
-
}
|
|
31392
|
-
|
|
31393
|
-
// src/commands/translate/apply.tsx
|
|
31394
|
-
import { useState as useState2 } from "react";
|
|
31395
|
-
import { MultiSelect } from "@inkjs/ui";
|
|
31396
|
-
import { Box as Box16, render as render16, Text as Text16 } from "ink";
|
|
31397
|
-
|
|
31398
|
-
// src/commands/translate/apply-core.ts
|
|
31399
|
-
import fs9 from "fs/promises";
|
|
31400
|
-
import path9 from "path";
|
|
31401
|
-
function fullKey2(namespace, key) {
|
|
31402
|
-
return namespace ? `${namespace}.${key}` : key;
|
|
31403
|
-
}
|
|
31404
|
-
async function applyTasks(input2) {
|
|
31405
|
-
const { root, scope, dstLocale } = input2;
|
|
31406
|
-
const format = formatOf(scope);
|
|
31407
|
-
const files = await listLocaleFiles(root, scope);
|
|
31408
|
-
const result = { written: [], refused: [] };
|
|
31409
|
-
const refuse = (task, reason) => result.refused.push({ key: fullKey2(task.namespace, task.key), reason });
|
|
31410
|
-
const bySourcePath = /* @__PURE__ */ new Map();
|
|
31411
|
-
for (const task of input2.tasks) {
|
|
31412
|
-
if (task.status !== "succeeded" || !task.dst || !task.path) {
|
|
31413
|
-
refuse(task, "not_translated");
|
|
31414
|
-
continue;
|
|
31415
|
-
}
|
|
31416
|
-
const group = bySourcePath.get(task.path) ?? [];
|
|
31417
|
-
group.push(task);
|
|
31418
|
-
bySourcePath.set(task.path, group);
|
|
31419
|
-
}
|
|
31420
|
-
for (const [sourcePath, tasks] of bySourcePath) {
|
|
31421
|
-
const sourceFile = files.find(
|
|
31422
|
-
(file2) => file2.relativePath === sourcePath
|
|
31423
|
-
);
|
|
31424
|
-
const source = sourceFile ? await readTranslationFile(sourceFile.absolutePath, format) : void 0;
|
|
31425
|
-
if (!sourceFile || !source || source.error !== void 0) {
|
|
31426
|
-
for (const task of tasks) refuse(task, "no_source_file");
|
|
31427
|
-
continue;
|
|
31428
|
-
}
|
|
31429
|
-
const baseKeys = new Set(getKeys(source.data));
|
|
31430
|
-
const target = targetFileFor(sourceFile, dstLocale, files, scope);
|
|
31431
|
-
const targetPath = path9.join(root, ...target.relativePath.split("/"));
|
|
31432
|
-
const existing = target.existing ? await readTranslationFile(target.existing.absolutePath, format) : void 0;
|
|
31433
|
-
if (existing?.error !== void 0) {
|
|
31434
|
-
for (const task of tasks) refuse(task, "target_unreadable");
|
|
31435
|
-
continue;
|
|
31436
|
-
}
|
|
31437
|
-
let data = existing?.data ?? {};
|
|
31438
|
-
const written = {
|
|
31439
|
-
pathTemplate: sourceFile.pathTemplate,
|
|
31440
|
-
relativePath: target.relativePath,
|
|
31441
|
-
entries: []
|
|
31442
|
-
};
|
|
31443
|
-
for (const task of tasks) {
|
|
31444
|
-
const sourceKey = sourceKeyFor(task.key, baseKeys);
|
|
31445
|
-
const currentSource = sourceKey === null ? void 0 : getByPath(source.data, sourceKey);
|
|
31446
|
-
if (typeof currentSource !== "string" || computeValueHash(currentSource) !== computeValueHash(task.src)) {
|
|
31447
|
-
refuse(task, "source_changed");
|
|
31448
|
-
continue;
|
|
31449
|
-
}
|
|
31450
|
-
const translatedValue = task.dst ?? "";
|
|
31451
|
-
const current = getByPath(data, task.key);
|
|
31452
|
-
const currentHash = typeof current === "string" ? computeValueHash(current) : null;
|
|
31453
|
-
if (task.expectedTargetHash !== void 0 && task.expectedTargetHash !== currentHash) {
|
|
31454
|
-
refuse(
|
|
31455
|
-
task,
|
|
31456
|
-
current === translatedValue ? "already_applied" : "target_changed"
|
|
31457
|
-
);
|
|
31458
|
-
continue;
|
|
31459
|
-
}
|
|
31460
|
-
data = setByPath(data, task.key, translatedValue);
|
|
31461
|
-
written.entries.push({
|
|
31462
|
-
key: task.key,
|
|
31463
|
-
namespace: task.namespace,
|
|
31464
|
-
baseValue: task.src,
|
|
31465
|
-
translatedValue,
|
|
31466
|
-
changesFile: current !== translatedValue
|
|
31467
|
-
});
|
|
31468
|
-
}
|
|
31469
|
-
if (written.entries.length === 0) continue;
|
|
31470
|
-
result.written.push(written);
|
|
31471
|
-
if (input2.dryRun) continue;
|
|
31472
|
-
await writeTranslationFile({
|
|
31473
|
-
format,
|
|
31474
|
-
targetPath,
|
|
31475
|
-
data,
|
|
31476
|
-
dstLocale,
|
|
31477
|
-
existingContent: existing?.content,
|
|
31478
|
-
sourceContent: source.content
|
|
31479
|
-
});
|
|
31480
|
-
}
|
|
31481
|
-
return result;
|
|
31482
|
-
}
|
|
31483
|
-
async function writeTranslationFile(input2) {
|
|
31484
|
-
const { targetPath, data } = input2;
|
|
31485
|
-
const model = input2.existingContent ?? input2.sourceContent;
|
|
31486
|
-
await fs9.mkdir(path9.dirname(targetPath), { recursive: true });
|
|
31487
|
-
if (input2.format === "json") {
|
|
31488
|
-
await fs9.writeFile(
|
|
31489
|
-
targetPath,
|
|
31490
|
-
serializeJsonContent(data, detectJsonStyle(model)),
|
|
31491
|
-
"utf-8"
|
|
31492
|
-
);
|
|
31493
|
-
return;
|
|
31494
|
-
}
|
|
31495
|
-
const style = detectTsStyle(model);
|
|
31496
|
-
const shape = detectTsExportFormat(model);
|
|
31497
|
-
const content = serializeTsContent(data, {
|
|
31498
|
-
...style,
|
|
31499
|
-
printWidth: style.printWidth ?? void 0,
|
|
31500
|
-
exportName: input2.existingContent ? shape?.exportName ?? void 0 : localeExportIdentifier(input2.dstLocale),
|
|
31501
|
-
useDefaultExport: shape?.useDefaultExport ?? false
|
|
31502
|
-
});
|
|
31503
|
-
await fs9.writeFile(targetPath, content, "utf-8");
|
|
31504
|
-
}
|
|
31505
|
-
|
|
31506
|
-
// src/commands/translate/apply.tsx
|
|
31507
|
-
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
31508
|
-
var STAMP_CHUNK2 = 200;
|
|
31509
30930
|
var RECENT_JOBS = 50;
|
|
31510
30931
|
var APPLICABLE_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
|
|
30932
|
+
function isApplicable(status) {
|
|
30933
|
+
return APPLICABLE_STATUSES.has(status);
|
|
30934
|
+
}
|
|
30935
|
+
function isForWorkingTree(job) {
|
|
30936
|
+
return job.source === void 0 || job.source === "cli";
|
|
30937
|
+
}
|
|
31511
30938
|
function fullKey3(namespace, key) {
|
|
31512
30939
|
return namespace ? `${namespace}.${key}` : key;
|
|
31513
30940
|
}
|
|
@@ -31518,17 +30945,17 @@ async function readJob(ctx, jobId) {
|
|
|
31518
30945
|
organizationId: organizationId2,
|
|
31519
30946
|
jobId
|
|
31520
30947
|
});
|
|
31521
|
-
if (
|
|
30948
|
+
if (job?.projectId !== project._id) {
|
|
31522
30949
|
throw new CliError(
|
|
31523
30950
|
`Translation job ${jobId} was not found in this project.`,
|
|
31524
|
-
|
|
30951
|
+
"List the project's jobs with 'tolkbot jobs list'.",
|
|
31525
30952
|
"job_not_found"
|
|
31526
30953
|
);
|
|
31527
30954
|
}
|
|
31528
|
-
if (!
|
|
30955
|
+
if (!isApplicable(job.status)) {
|
|
31529
30956
|
throw new CliError(
|
|
31530
30957
|
`Translation job ${jobId} is ${job.status}. Only a finished job can be applied.`,
|
|
31531
|
-
`Check it with 'tolkbot
|
|
30958
|
+
`Check it with 'tolkbot jobs show ${jobId}'.`,
|
|
31532
30959
|
"job_not_finished"
|
|
31533
30960
|
);
|
|
31534
30961
|
}
|
|
@@ -31558,13 +30985,13 @@ async function applyJob(ctx, jobId) {
|
|
|
31558
30985
|
translatedValue
|
|
31559
30986
|
})
|
|
31560
30987
|
);
|
|
31561
|
-
for (let i = 0; i < entries.length; i +=
|
|
30988
|
+
for (let i = 0; i < entries.length; i += STAMP_CHUNK) {
|
|
31562
30989
|
await client3.mutation(api.localeKeyStates.stampAppliedForCli, {
|
|
31563
30990
|
organizationId: project.organizationId,
|
|
31564
30991
|
projectId: project._id,
|
|
31565
30992
|
locale,
|
|
31566
30993
|
pathTemplate: file2.pathTemplate,
|
|
31567
|
-
entries: entries.slice(i, i +
|
|
30994
|
+
entries: entries.slice(i, i + STAMP_CHUNK)
|
|
31568
30995
|
});
|
|
31569
30996
|
}
|
|
31570
30997
|
}
|
|
@@ -31572,7 +30999,7 @@ async function applyJob(ctx, jobId) {
|
|
|
31572
30999
|
dstLocale: job.dstLocale,
|
|
31573
31000
|
files: result.written.length,
|
|
31574
31001
|
keys: result.written.reduce((n, f) => n + f.entries.length, 0),
|
|
31575
|
-
|
|
31002
|
+
notApplied: result.refused.filter(
|
|
31576
31003
|
(r) => r.reason !== "not_translated" && r.reason !== "already_applied"
|
|
31577
31004
|
)
|
|
31578
31005
|
};
|
|
@@ -31585,18 +31012,13 @@ var REFUSAL_TEXT = {
|
|
|
31585
31012
|
target_unreadable: "the target file cannot be read",
|
|
31586
31013
|
target_changed: "the target was edited after it was planned"
|
|
31587
31014
|
};
|
|
31588
|
-
function
|
|
31589
|
-
return
|
|
31015
|
+
function describeNotApplied(notApplied) {
|
|
31016
|
+
return notApplied.map(
|
|
31590
31017
|
(r) => ` ${r.key}: not applied, ${REFUSAL_TEXT[r.reason]}`
|
|
31591
31018
|
);
|
|
31592
31019
|
}
|
|
31593
|
-
|
|
31020
|
+
function pendingCounter(ctx) {
|
|
31594
31021
|
const { client: client3, project } = ctx;
|
|
31595
|
-
const jobs = await client3.query(api.translations.listJobsByProject, {
|
|
31596
|
-
organizationId: project.organizationId,
|
|
31597
|
-
projectId: project._id,
|
|
31598
|
-
limit: RECENT_JOBS
|
|
31599
|
-
});
|
|
31600
31022
|
const wanted = /* @__PURE__ */ new Map();
|
|
31601
31023
|
const plannedKeys = (srcLocale, dstLocale) => {
|
|
31602
31024
|
const id = `${srcLocale}\0${dstLocale}`;
|
|
@@ -31613,9 +31035,7 @@ async function findUnappliedJobs(ctx) {
|
|
|
31613
31035
|
}
|
|
31614
31036
|
return keys;
|
|
31615
31037
|
};
|
|
31616
|
-
|
|
31617
|
-
for (const job of jobs) {
|
|
31618
|
-
if (!APPLICABLE_STATUSES.has(job.status)) continue;
|
|
31038
|
+
return async (job) => {
|
|
31619
31039
|
const tasks = await client3.query(api.translations.listTasksByJobId, {
|
|
31620
31040
|
organizationId: project.organizationId,
|
|
31621
31041
|
jobId: job._id
|
|
@@ -31638,6 +31058,23 @@ async function findUnappliedJobs(ctx) {
|
|
|
31638
31058
|
}
|
|
31639
31059
|
}
|
|
31640
31060
|
}
|
|
31061
|
+
return pending2;
|
|
31062
|
+
};
|
|
31063
|
+
}
|
|
31064
|
+
async function listRecentJobs(ctx, limit) {
|
|
31065
|
+
return await ctx.client.query(api.translations.listJobsByProject, {
|
|
31066
|
+
organizationId: ctx.project.organizationId,
|
|
31067
|
+
projectId: ctx.project._id,
|
|
31068
|
+
limit
|
|
31069
|
+
});
|
|
31070
|
+
}
|
|
31071
|
+
async function findUnappliedJobs(ctx) {
|
|
31072
|
+
const jobs = await listRecentJobs(ctx, RECENT_JOBS);
|
|
31073
|
+
const pendingOf = pendingCounter(ctx);
|
|
31074
|
+
const unapplied = [];
|
|
31075
|
+
for (const job of jobs) {
|
|
31076
|
+
if (!isApplicable(job.status) || !isForWorkingTree(job)) continue;
|
|
31077
|
+
const pending2 = await pendingOf(job);
|
|
31641
31078
|
if (pending2 === 0) continue;
|
|
31642
31079
|
unapplied.push({
|
|
31643
31080
|
jobId: job._id,
|
|
@@ -31664,13 +31101,16 @@ function chooseJobs(unapplied) {
|
|
|
31664
31101
|
jobs: [...unapplied].sort((a, b) => a.createdAt - b.createdAt)
|
|
31665
31102
|
};
|
|
31666
31103
|
}
|
|
31104
|
+
|
|
31105
|
+
// src/commands/jobs/apply.tsx
|
|
31106
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
31667
31107
|
function describeJob(job) {
|
|
31668
31108
|
return `${job.jobId} (${job.srcLocale} \u2192 ${job.dstLocale}, ${job.pending} key(s), ${new Date(job.createdAt).toLocaleString()})`;
|
|
31669
31109
|
}
|
|
31670
31110
|
function nothingToApply() {
|
|
31671
31111
|
return new CliError(
|
|
31672
31112
|
"No finished translation job is waiting to be applied.",
|
|
31673
|
-
"Run 'tolkbot translate
|
|
31113
|
+
"Run 'tolkbot translate --all' to translate what is missing, or pass a job id to apply a job again.",
|
|
31674
31114
|
"nothing_to_apply"
|
|
31675
31115
|
);
|
|
31676
31116
|
}
|
|
@@ -31681,7 +31121,7 @@ function ambiguousJobs(unapplied, locales) {
|
|
|
31681
31121
|
);
|
|
31682
31122
|
return new UsageError(
|
|
31683
31123
|
`More than one translation job is waiting to be applied for ${locales.join(", ")}: ${waiting.map(describeJob).join("; ")}.`,
|
|
31684
|
-
`Pass
|
|
31124
|
+
`Pass the job id(s) to apply. The newest is usually the one: 'tolkbot jobs apply ${newest.map((job) => job.jobId).join(" ")}'.`
|
|
31685
31125
|
);
|
|
31686
31126
|
}
|
|
31687
31127
|
function JobPicker({
|
|
@@ -31689,10 +31129,10 @@ function JobPicker({
|
|
|
31689
31129
|
onSubmit
|
|
31690
31130
|
}) {
|
|
31691
31131
|
const [selected, setSelected] = useState2([]);
|
|
31692
|
-
return /* @__PURE__ */
|
|
31693
|
-
/* @__PURE__ */
|
|
31694
|
-
/* @__PURE__ */
|
|
31695
|
-
/* @__PURE__ */
|
|
31132
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
31133
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, children: "Select translation jobs to apply:" }),
|
|
31134
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "Space to select, Enter to confirm, Ctrl+C to cancel" }),
|
|
31135
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(
|
|
31696
31136
|
MultiSelect,
|
|
31697
31137
|
{
|
|
31698
31138
|
options: jobs.map((job) => ({
|
|
@@ -31707,8 +31147,8 @@ function JobPicker({
|
|
|
31707
31147
|
}
|
|
31708
31148
|
async function pickJobs(jobs) {
|
|
31709
31149
|
let chosen = [];
|
|
31710
|
-
const ui =
|
|
31711
|
-
/* @__PURE__ */
|
|
31150
|
+
const ui = render11(
|
|
31151
|
+
/* @__PURE__ */ jsx11(
|
|
31712
31152
|
JobPicker,
|
|
31713
31153
|
{
|
|
31714
31154
|
jobs,
|
|
@@ -31734,15 +31174,15 @@ async function resolveJobIds(ctx, json2) {
|
|
|
31734
31174
|
if (picked.length === 0) {
|
|
31735
31175
|
throw new UsageError(
|
|
31736
31176
|
"No job selected.",
|
|
31737
|
-
"Pass
|
|
31177
|
+
"Pass the job id(s) to apply: 'tolkbot jobs apply <id\u2026>'."
|
|
31738
31178
|
);
|
|
31739
31179
|
}
|
|
31740
31180
|
return picked;
|
|
31741
31181
|
}
|
|
31742
|
-
async function
|
|
31182
|
+
async function runJobsApply(opts) {
|
|
31743
31183
|
const ctx = await loadPlanContext(process.cwd());
|
|
31744
31184
|
const json2 = opts.json ?? false;
|
|
31745
|
-
const jobIds = opts.
|
|
31185
|
+
const jobIds = opts.jobIds.length > 0 ? opts.jobIds : await resolveJobIds(ctx, json2);
|
|
31746
31186
|
if (json2) {
|
|
31747
31187
|
const applied = [];
|
|
31748
31188
|
for (const jobId of jobIds) {
|
|
@@ -31760,16 +31200,19 @@ async function runApply(opts) {
|
|
|
31760
31200
|
console.log(`Applying ${jobIds.length} job(s)...`);
|
|
31761
31201
|
let totalFiles = 0;
|
|
31762
31202
|
let totalKeys = 0;
|
|
31763
|
-
let
|
|
31203
|
+
let totalNotApplied = 0;
|
|
31764
31204
|
const locales = /* @__PURE__ */ new Set();
|
|
31765
31205
|
for (const [i, jobId] of jobIds.entries()) {
|
|
31766
31206
|
console.log(` [${i + 1}/${jobIds.length}] ${jobId}`);
|
|
31767
|
-
const { dstLocale, files, keys,
|
|
31207
|
+
const { dstLocale, files, keys, notApplied } = await applyJob(
|
|
31208
|
+
ctx,
|
|
31209
|
+
jobId
|
|
31210
|
+
);
|
|
31768
31211
|
locales.add(dstLocale);
|
|
31769
31212
|
totalFiles += files;
|
|
31770
31213
|
totalKeys += keys;
|
|
31771
|
-
|
|
31772
|
-
for (const line of
|
|
31214
|
+
totalNotApplied += notApplied.length;
|
|
31215
|
+
for (const line of describeNotApplied(notApplied)) console.log(line);
|
|
31773
31216
|
}
|
|
31774
31217
|
track("cli_translate_completed", {
|
|
31775
31218
|
mode: "apply",
|
|
@@ -31779,187 +31222,89 @@ async function runApply(opts) {
|
|
|
31779
31222
|
json: false
|
|
31780
31223
|
});
|
|
31781
31224
|
console.log(
|
|
31782
|
-
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${jobIds.length} job(s)` + (
|
|
31225
|
+
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${jobIds.length} job(s)` + (totalNotApplied > 0 ? `, ${totalNotApplied} not applied` : "")
|
|
31783
31226
|
);
|
|
31784
31227
|
}
|
|
31785
31228
|
|
|
31786
|
-
// src/commands/
|
|
31787
|
-
|
|
31788
|
-
import { Box as Box17, render as render17, Text as Text17 } from "ink";
|
|
31789
|
-
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
31790
|
-
function CancelUI({ state }) {
|
|
31791
|
-
if (state.type === "cancelling") {
|
|
31792
|
-
return /* @__PURE__ */ jsxs17(Box17, { children: [
|
|
31793
|
-
/* @__PURE__ */ jsx17(Spinner14, {}),
|
|
31794
|
-
/* @__PURE__ */ jsx17(Text17, { children: " Cancelling job\u2026" })
|
|
31795
|
-
] });
|
|
31796
|
-
}
|
|
31797
|
-
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
31798
|
-
/* @__PURE__ */ jsxs17(Box17, { children: [
|
|
31799
|
-
/* @__PURE__ */ jsx17(Text17, { color: "green", children: "\u2713" }),
|
|
31800
|
-
/* @__PURE__ */ jsx17(Text17, { children: " Job cancelled successfully" })
|
|
31801
|
-
] }),
|
|
31802
|
-
/* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
31803
|
-
"Job ID: ",
|
|
31804
|
-
state.jobId
|
|
31805
|
-
] }) })
|
|
31806
|
-
] });
|
|
31807
|
-
}
|
|
31808
|
-
async function cancelJob(jobId) {
|
|
31229
|
+
// src/commands/jobs/cancel.ts
|
|
31230
|
+
async function runJobsCancel(opts) {
|
|
31809
31231
|
const { project } = await loadPlanContext(process.cwd());
|
|
31810
31232
|
const client3 = await getAuthenticatedConvexClient();
|
|
31811
31233
|
await client3.action(api.translations.cancelJob, {
|
|
31812
31234
|
organizationId: project.organizationId,
|
|
31813
|
-
jobId
|
|
31235
|
+
jobId: opts.jobId
|
|
31814
31236
|
});
|
|
31815
|
-
}
|
|
31816
|
-
|
|
31817
|
-
if (opts.json) {
|
|
31818
|
-
await cancelJob(opts.jobId);
|
|
31819
|
-
jsonSuccess({ jobId: opts.jobId, ok: true });
|
|
31820
|
-
}
|
|
31821
|
-
const { unmount } = renderTransient(
|
|
31822
|
-
/* @__PURE__ */ jsx17(CancelUI, { state: { type: "cancelling" } })
|
|
31823
|
-
);
|
|
31824
|
-
try {
|
|
31825
|
-
await cancelJob(opts.jobId);
|
|
31826
|
-
} finally {
|
|
31827
|
-
unmount();
|
|
31828
|
-
}
|
|
31829
|
-
render17(
|
|
31830
|
-
/* @__PURE__ */ jsx17(CancelUI, { state: { type: "success", jobId: opts.jobId } })
|
|
31831
|
-
).unmount();
|
|
31237
|
+
if (opts.json) jsonSuccess({ jobId: opts.jobId, ok: true });
|
|
31238
|
+
console.log(`\u2713 Cancelled job ${opts.jobId}`);
|
|
31832
31239
|
}
|
|
31833
31240
|
|
|
31834
|
-
// src/commands/
|
|
31835
|
-
|
|
31836
|
-
|
|
31837
|
-
|
|
31838
|
-
|
|
31839
|
-
|
|
31840
|
-
|
|
31841
|
-
|
|
31842
|
-
|
|
31843
|
-
|
|
31844
|
-
|
|
31845
|
-
|
|
31846
|
-
|
|
31847
|
-
|
|
31848
|
-
|
|
31849
|
-
|
|
31850
|
-
|
|
31851
|
-
|
|
31852
|
-
|
|
31853
|
-
|
|
31854
|
-
|
|
31855
|
-
|
|
31856
|
-
|
|
31857
|
-
|
|
31858
|
-
|
|
31859
|
-
|
|
31860
|
-
|
|
31861
|
-
}
|
|
31862
|
-
}
|
|
31863
|
-
return files[0] ?? null;
|
|
31864
|
-
}
|
|
31865
|
-
function resolveKeyInFile(key, targetFile, fileCount, namespace) {
|
|
31866
|
-
if (fileCount <= 1 || namespace) return key;
|
|
31867
|
-
const fileName = path10.parse(path10.basename(targetFile)).name;
|
|
31868
|
-
if (key.startsWith(`${fileName}.`)) {
|
|
31869
|
-
return key.slice(fileName.length + 1);
|
|
31870
|
-
}
|
|
31871
|
-
return key;
|
|
31872
|
-
}
|
|
31873
|
-
|
|
31874
|
-
// src/commands/translate/edit.tsx
|
|
31875
|
-
import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
31876
|
-
function EditUI({ state }) {
|
|
31877
|
-
if (state.type === "editing") {
|
|
31878
|
-
return /* @__PURE__ */ jsxs18(Box18, { children: [
|
|
31879
|
-
/* @__PURE__ */ jsx18(Spinner15, {}),
|
|
31880
|
-
/* @__PURE__ */ jsx18(Text18, { children: " Editing translation\u2026" })
|
|
31881
|
-
] });
|
|
31241
|
+
// src/commands/jobs/list.ts
|
|
31242
|
+
async function describeJobs(ctx, jobs) {
|
|
31243
|
+
const pendingOf = pendingCounter(ctx);
|
|
31244
|
+
const listed = [];
|
|
31245
|
+
for (const job of jobs) {
|
|
31246
|
+
let applied = "none";
|
|
31247
|
+
let pending2 = 0;
|
|
31248
|
+
if (!isForWorkingTree(job)) {
|
|
31249
|
+
} else if (job.status === "pending" || job.status === "running") {
|
|
31250
|
+
applied = "running";
|
|
31251
|
+
} else if (isApplicable(job.status)) {
|
|
31252
|
+
pending2 = await pendingOf(job);
|
|
31253
|
+
applied = pending2 > 0 ? "waiting" : "applied";
|
|
31254
|
+
}
|
|
31255
|
+
listed.push({
|
|
31256
|
+
jobId: job._id,
|
|
31257
|
+
srcLocale: job.srcLocale,
|
|
31258
|
+
dstLocale: job.dstLocale,
|
|
31259
|
+
status: job.status,
|
|
31260
|
+
source: job.source ?? null,
|
|
31261
|
+
createdAt: new Date(job._creationTime).toISOString(),
|
|
31262
|
+
total: job.total,
|
|
31263
|
+
completed: job.completed,
|
|
31264
|
+
failed: job.failed,
|
|
31265
|
+
creditsUsed: job.creditsUsed,
|
|
31266
|
+
applied,
|
|
31267
|
+
pending: pending2
|
|
31268
|
+
});
|
|
31882
31269
|
}
|
|
31883
|
-
return
|
|
31884
|
-
/* @__PURE__ */ jsxs18(Box18, { children: [
|
|
31885
|
-
/* @__PURE__ */ jsx18(Text18, { color: "green", children: "\u2713" }),
|
|
31886
|
-
/* @__PURE__ */ jsxs18(Text18, { children: [
|
|
31887
|
-
" ",
|
|
31888
|
-
"Updated ",
|
|
31889
|
-
/* @__PURE__ */ jsx18(Text18, { bold: true, children: state.key })
|
|
31890
|
-
] })
|
|
31891
|
-
] }),
|
|
31892
|
-
/* @__PURE__ */ jsx18(Box18, { paddingLeft: 2, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: state.value }) }),
|
|
31893
|
-
/* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
31894
|
-
"File: ",
|
|
31895
|
-
state.filePath
|
|
31896
|
-
] }) })
|
|
31897
|
-
] });
|
|
31270
|
+
return listed;
|
|
31898
31271
|
}
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
31905
|
-
|
|
31906
|
-
|
|
31907
|
-
|
|
31908
|
-
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
|
|
31912
|
-
|
|
31913
|
-
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
|
|
31272
|
+
var APPLIED_TEXT = {
|
|
31273
|
+
applied: "applied",
|
|
31274
|
+
waiting: "waiting to apply",
|
|
31275
|
+
running: "",
|
|
31276
|
+
none: ""
|
|
31277
|
+
};
|
|
31278
|
+
function formatRow(job) {
|
|
31279
|
+
const when = job.createdAt.slice(0, 16).replace("T", " ");
|
|
31280
|
+
const progress = `${job.completed}/${job.total}`;
|
|
31281
|
+
const applied = job.applied === "waiting" ? `${APPLIED_TEXT.waiting} (${job.pending})` : APPLIED_TEXT[job.applied];
|
|
31282
|
+
return [
|
|
31283
|
+
job.jobId,
|
|
31284
|
+
when,
|
|
31285
|
+
`${job.srcLocale} \u2192 ${job.dstLocale}`.padEnd(14),
|
|
31286
|
+
job.status.padEnd(9),
|
|
31287
|
+
progress.padStart(9),
|
|
31288
|
+
(job.source ?? "cli").padEnd(9),
|
|
31289
|
+
applied
|
|
31290
|
+
].join(" ").trimEnd();
|
|
31291
|
+
}
|
|
31292
|
+
async function runJobsList(opts) {
|
|
31293
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
31294
|
+
const jobs = await describeJobs(ctx, await listRecentJobs(ctx, opts.limit));
|
|
31295
|
+
if (opts.json) jsonSuccess({ jobs });
|
|
31296
|
+
if (jobs.length === 0) {
|
|
31297
|
+
console.log(
|
|
31298
|
+
"No translation jobs yet. Run 'tolkbot translate --all' to start one."
|
|
31917
31299
|
);
|
|
31300
|
+
return;
|
|
31918
31301
|
}
|
|
31919
|
-
const
|
|
31920
|
-
|
|
31921
|
-
|
|
31922
|
-
files.length,
|
|
31923
|
-
opts.namespace
|
|
31924
|
-
);
|
|
31925
|
-
const adapter = createAdapter("json");
|
|
31926
|
-
let existingData = {};
|
|
31927
|
-
try {
|
|
31928
|
-
const parsed = await adapter.parse(targetFile);
|
|
31929
|
-
existingData = parsed.data;
|
|
31930
|
-
} catch {
|
|
31931
|
-
}
|
|
31932
|
-
const updatedData = setByPath(
|
|
31933
|
-
existingData,
|
|
31934
|
-
keyInFile,
|
|
31935
|
-
opts.value
|
|
31936
|
-
);
|
|
31937
|
-
await adapter.serialize(targetFile, updatedData, { merge: false });
|
|
31938
|
-
const relativePath = path11.relative(baseDir, targetFile);
|
|
31939
|
-
return { filePath: relativePath, key: opts.key, value: opts.value };
|
|
31940
|
-
}
|
|
31941
|
-
async function runEdit(opts) {
|
|
31942
|
-
if (opts.json) {
|
|
31943
|
-
const result2 = await performEdit(opts);
|
|
31944
|
-
jsonSuccess({
|
|
31945
|
-
file: result2.filePath,
|
|
31946
|
-
key: result2.key,
|
|
31947
|
-
value: result2.value
|
|
31948
|
-
});
|
|
31949
|
-
}
|
|
31950
|
-
const { unmount } = renderTransient(/* @__PURE__ */ jsx18(EditUI, { state: { type: "editing" } }));
|
|
31951
|
-
let result;
|
|
31952
|
-
try {
|
|
31953
|
-
result = await performEdit(opts);
|
|
31954
|
-
} finally {
|
|
31955
|
-
unmount();
|
|
31302
|
+
for (const job of jobs) console.log(formatRow(job));
|
|
31303
|
+
if (jobs.some((job) => job.applied === "waiting")) {
|
|
31304
|
+
console.log("\nApply the waiting ones with 'tolkbot jobs apply'.");
|
|
31956
31305
|
}
|
|
31957
|
-
render18(/* @__PURE__ */ jsx18(EditUI, { state: { type: "success", ...result } })).unmount();
|
|
31958
31306
|
}
|
|
31959
31307
|
|
|
31960
|
-
// src/commands/translate/run.tsx
|
|
31961
|
-
import { render as render19 } from "ink";
|
|
31962
|
-
|
|
31963
31308
|
// ../shared/src/conditions/index.ts
|
|
31964
31309
|
var BILLING_BLOCKED = "BILLING_BLOCKED";
|
|
31965
31310
|
|
|
@@ -32061,10 +31406,10 @@ function serverMessage(message) {
|
|
|
32061
31406
|
}
|
|
32062
31407
|
function argumentError(message) {
|
|
32063
31408
|
if (!message.includes("ArgumentValidationError")) return null;
|
|
32064
|
-
const
|
|
31409
|
+
const path11 = /Path: \.?(\S+)/.exec(message)?.[1];
|
|
32065
31410
|
const value = /Value: (.+)/.exec(message)?.[1];
|
|
32066
31411
|
return new UsageError(
|
|
32067
|
-
|
|
31412
|
+
path11 && value ? `TolkBot refused ${value} as ${path11}.` : "TolkBot refused the arguments.",
|
|
32068
31413
|
"Check the id or value passed; list ids with the command's list subcommand."
|
|
32069
31414
|
);
|
|
32070
31415
|
}
|
|
@@ -32075,694 +31420,1193 @@ function toCliError(error62) {
|
|
|
32075
31420
|
const known = fromConvexError(convex.data);
|
|
32076
31421
|
if (known) return known;
|
|
32077
31422
|
}
|
|
32078
|
-
if (isUnauthenticatedError(error62)) {
|
|
32079
|
-
return new CliError(
|
|
32080
|
-
"Not authenticated.",
|
|
32081
|
-
"Run 'tolkbot auth login', or set $TOLKBOT_TOKEN for CI and headless environments.",
|
|
32082
|
-
"not_authenticated"
|
|
31423
|
+
if (isUnauthenticatedError(error62)) {
|
|
31424
|
+
return new CliError(
|
|
31425
|
+
"Not authenticated.",
|
|
31426
|
+
"Run 'tolkbot auth login', or set $TOLKBOT_TOKEN for CI and headless environments.",
|
|
31427
|
+
"not_authenticated"
|
|
31428
|
+
);
|
|
31429
|
+
}
|
|
31430
|
+
if (isNetworkError(error62)) {
|
|
31431
|
+
return new CliError(
|
|
31432
|
+
"Could not reach TolkBot.",
|
|
31433
|
+
"Check the network connection and try again.",
|
|
31434
|
+
"network"
|
|
31435
|
+
);
|
|
31436
|
+
}
|
|
31437
|
+
const message = describeError(error62);
|
|
31438
|
+
const refused = argumentError(message);
|
|
31439
|
+
if (refused) return refused;
|
|
31440
|
+
if (message.includes("Server Error")) {
|
|
31441
|
+
return new CliError(serverMessage(message));
|
|
31442
|
+
}
|
|
31443
|
+
const internal = new CliError(message, void 0, "internal");
|
|
31444
|
+
if (error62 instanceof Error) internal.stack = error62.stack;
|
|
31445
|
+
return internal;
|
|
31446
|
+
}
|
|
31447
|
+
function errorPayload(error62) {
|
|
31448
|
+
return {
|
|
31449
|
+
error: {
|
|
31450
|
+
code: error62.code,
|
|
31451
|
+
message: error62.baseMessage,
|
|
31452
|
+
hint: error62.hint ?? null
|
|
31453
|
+
}
|
|
31454
|
+
};
|
|
31455
|
+
}
|
|
31456
|
+
function reportError(error62) {
|
|
31457
|
+
const cli = toCliError(error62);
|
|
31458
|
+
if (wantsJson()) {
|
|
31459
|
+
console.error(JSON.stringify(errorPayload(cli), null, 2));
|
|
31460
|
+
} else {
|
|
31461
|
+
console.error(`\u2717 ${cli.baseMessage}`);
|
|
31462
|
+
if (cli.hint) console.error(` ${cli.hint}`);
|
|
31463
|
+
if (cli.code === "internal" && cli.stack) {
|
|
31464
|
+
console.error("");
|
|
31465
|
+
console.error(cli.stack);
|
|
31466
|
+
}
|
|
31467
|
+
}
|
|
31468
|
+
const code = exitCodeFor(cli.code);
|
|
31469
|
+
process.exitCode = code;
|
|
31470
|
+
return code;
|
|
31471
|
+
}
|
|
31472
|
+
function fail(error62) {
|
|
31473
|
+
const cli = toCliError(error62);
|
|
31474
|
+
throw requestExit(exitCodeFor(cli.code), () => {
|
|
31475
|
+
reportError(cli);
|
|
31476
|
+
});
|
|
31477
|
+
}
|
|
31478
|
+
|
|
31479
|
+
// src/commands/translate/translate-job.ts
|
|
31480
|
+
var FINISHED_STATUSES = /* @__PURE__ */ new Set([
|
|
31481
|
+
"succeeded",
|
|
31482
|
+
"partial",
|
|
31483
|
+
"failed",
|
|
31484
|
+
"cancelled"
|
|
31485
|
+
]);
|
|
31486
|
+
function isJobFinished(status) {
|
|
31487
|
+
return FINISHED_STATUSES.has(status);
|
|
31488
|
+
}
|
|
31489
|
+
function unsuccessfulJobError(jobId, status) {
|
|
31490
|
+
if (status === "cancelled") {
|
|
31491
|
+
return new CliError(
|
|
31492
|
+
`Translation job ${jobId} was cancelled.`,
|
|
31493
|
+
void 0,
|
|
31494
|
+
"job_cancelled"
|
|
31495
|
+
);
|
|
31496
|
+
}
|
|
31497
|
+
if (status === "failed") {
|
|
31498
|
+
return new CliError(
|
|
31499
|
+
`Translation job ${jobId} failed.`,
|
|
31500
|
+
`See it at ${env.TOLKBOT_WEB_URL}/translation-jobs/${jobId}.`,
|
|
31501
|
+
"job_failed"
|
|
31502
|
+
);
|
|
31503
|
+
}
|
|
31504
|
+
return null;
|
|
31505
|
+
}
|
|
31506
|
+
var JOB_WAIT_TIMEOUT_MS = 60 * 6e4;
|
|
31507
|
+
var JOB_POLL_MS = 500;
|
|
31508
|
+
var JobWaitTimeoutError = class extends CliError {
|
|
31509
|
+
constructor(jobId, timeoutMs) {
|
|
31510
|
+
super(
|
|
31511
|
+
`Translation job ${jobId} was still running after ${Math.round(timeoutMs / 6e4)} minutes, so the CLI stopped waiting.`,
|
|
31512
|
+
`The job keeps running. Check it with 'tolkbot jobs show ${jobId}' and apply it with 'tolkbot jobs apply ${jobId}' once it finishes.`,
|
|
31513
|
+
"job_timed_out"
|
|
31514
|
+
);
|
|
31515
|
+
this.jobId = jobId;
|
|
31516
|
+
this.name = "JobWaitTimeoutError";
|
|
31517
|
+
}
|
|
31518
|
+
jobId;
|
|
31519
|
+
};
|
|
31520
|
+
async function waitForJob(jobId, read, opts = {}) {
|
|
31521
|
+
const timeoutMs = opts.timeoutMs ?? JOB_WAIT_TIMEOUT_MS;
|
|
31522
|
+
const pollMs = opts.pollMs ?? JOB_POLL_MS;
|
|
31523
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
31524
|
+
const now = opts.now ?? Date.now;
|
|
31525
|
+
const deadline = now() + timeoutMs;
|
|
31526
|
+
for (; ; ) {
|
|
31527
|
+
const summary = await read();
|
|
31528
|
+
if (!summary) {
|
|
31529
|
+
throw new CliError(
|
|
31530
|
+
`Translation job ${jobId} was not found.`,
|
|
31531
|
+
void 0,
|
|
31532
|
+
"job_not_found"
|
|
31533
|
+
);
|
|
31534
|
+
}
|
|
31535
|
+
opts.onProgress?.(summary);
|
|
31536
|
+
if (isJobFinished(summary.status)) return summary;
|
|
31537
|
+
if (now() >= deadline) {
|
|
31538
|
+
throw new JobWaitTimeoutError(jobId, timeoutMs);
|
|
31539
|
+
}
|
|
31540
|
+
await sleep(pollMs);
|
|
31541
|
+
}
|
|
31542
|
+
}
|
|
31543
|
+
async function startTranslationJob(params) {
|
|
31544
|
+
const { projectId, organizationId: organizationId2, framework, srcLocale, dstLocale } = params;
|
|
31545
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31546
|
+
return await convexClient.action(api.translations.startJob, {
|
|
31547
|
+
organizationId: organizationId2,
|
|
31548
|
+
projectId,
|
|
31549
|
+
srcLocale,
|
|
31550
|
+
dstLocale,
|
|
31551
|
+
framework,
|
|
31552
|
+
tasks: params.tasks.map((t) => ({
|
|
31553
|
+
key: t.key,
|
|
31554
|
+
namespace: t.namespace,
|
|
31555
|
+
path: t.path,
|
|
31556
|
+
src: t.src,
|
|
31557
|
+
hash: t.hash,
|
|
31558
|
+
context: t.context,
|
|
31559
|
+
// What `jobs apply` checks the target against. Without it every
|
|
31560
|
+
// result was written over whatever the file held by then.
|
|
31561
|
+
expectedTargetHash: t.expectedTargetHash
|
|
31562
|
+
}))
|
|
31563
|
+
});
|
|
31564
|
+
}
|
|
31565
|
+
async function waitForTranslationJob(organizationId2, jobId, onProgress) {
|
|
31566
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31567
|
+
return waitForJob(
|
|
31568
|
+
jobId,
|
|
31569
|
+
() => convexClient.query(api.translations.getJobSummary, {
|
|
31570
|
+
organizationId: organizationId2,
|
|
31571
|
+
jobId
|
|
31572
|
+
}),
|
|
31573
|
+
{ onProgress }
|
|
31574
|
+
);
|
|
31575
|
+
}
|
|
31576
|
+
|
|
31577
|
+
// src/commands/jobs/show.ts
|
|
31578
|
+
var STATUS_TEXT = {
|
|
31579
|
+
pending: "is waiting to start",
|
|
31580
|
+
running: "is running",
|
|
31581
|
+
succeeded: "finished",
|
|
31582
|
+
partial: "finished with some keys failed",
|
|
31583
|
+
failed: "failed",
|
|
31584
|
+
cancelled: "was cancelled"
|
|
31585
|
+
};
|
|
31586
|
+
function describe3(job) {
|
|
31587
|
+
const processed = job.completed + job.failed;
|
|
31588
|
+
const percent = job.total > 0 ? Math.round(processed / job.total * 100) : 0;
|
|
31589
|
+
const lines = [
|
|
31590
|
+
`Job ${job.jobId} ${STATUS_TEXT[job.status]}`,
|
|
31591
|
+
` ${job.srcLocale} \u2192 ${job.dstLocale}, started ${job.createdAt.slice(0, 16).replace("T", " ")} UTC`,
|
|
31592
|
+
` ${job.completed} translated, ${job.failed} failed, ${job.total} in all (${percent}%)`,
|
|
31593
|
+
` ${job.creditsUsed} credits used`
|
|
31594
|
+
];
|
|
31595
|
+
if (job.applied === "applied") lines.push(" Applied to this working tree");
|
|
31596
|
+
if (job.applied === "waiting") {
|
|
31597
|
+
lines.push(
|
|
31598
|
+
` ${job.pending} key(s) waiting: 'tolkbot jobs apply ${job.jobId}'`
|
|
31599
|
+
);
|
|
31600
|
+
}
|
|
31601
|
+
lines.push(` ${env.TOLKBOT_WEB_URL}/translation-jobs/${job.jobId}`);
|
|
31602
|
+
return lines;
|
|
31603
|
+
}
|
|
31604
|
+
async function runJobsShow(opts) {
|
|
31605
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
31606
|
+
const found = await ctx.client.query(api.translations.getJobDetails, {
|
|
31607
|
+
organizationId: ctx.project.organizationId,
|
|
31608
|
+
jobId: opts.jobId
|
|
31609
|
+
});
|
|
31610
|
+
if (found?.projectId !== ctx.project._id) {
|
|
31611
|
+
throw new CliError(
|
|
31612
|
+
`Translation job ${opts.jobId} was not found in this project.`,
|
|
31613
|
+
"List the project's jobs with 'tolkbot jobs list'.",
|
|
31614
|
+
"job_not_found"
|
|
31615
|
+
);
|
|
31616
|
+
}
|
|
31617
|
+
const [job] = await describeJobs(ctx, [found]);
|
|
31618
|
+
if (!job)
|
|
31619
|
+
throw new CliError(`Translation job ${opts.jobId} was not found.`);
|
|
31620
|
+
const failure2 = unsuccessfulJobError(job.jobId, job.status);
|
|
31621
|
+
if (opts.json) {
|
|
31622
|
+
if (!failure2) jsonSuccess(job);
|
|
31623
|
+
jsonResult(job, 1);
|
|
31624
|
+
fail(failure2);
|
|
31625
|
+
}
|
|
31626
|
+
for (const line of describe3(job)) console.log(line);
|
|
31627
|
+
if (failure2) fail(failure2);
|
|
31628
|
+
}
|
|
31629
|
+
|
|
31630
|
+
// src/commands/jobs/index.ts
|
|
31631
|
+
var jobsCommand = new Command3("jobs").description(
|
|
31632
|
+
"List, inspect, apply and cancel translation jobs"
|
|
31633
|
+
);
|
|
31634
|
+
var JSON_HELP = "Output as JSON (machine-readable)";
|
|
31635
|
+
jobsCommand.command("list").description(
|
|
31636
|
+
"Recent translation jobs for the project, with their status and whether their results are in this working tree"
|
|
31637
|
+
).option("--limit <n>", "How many jobs to list", "20").option("--json", JSON_HELP).action(async (opts) => {
|
|
31638
|
+
const schema = z5.object({
|
|
31639
|
+
limit: z5.coerce.number().int().min(1).max(100),
|
|
31640
|
+
json: z5.boolean().optional()
|
|
31641
|
+
});
|
|
31642
|
+
await runJobsList(validateOrExit(schema, opts));
|
|
31643
|
+
});
|
|
31644
|
+
jobsCommand.command("show").description("Show one translation job").argument("<id>", "Job ID").option("--json", JSON_HELP).action(async (jobId, opts) => {
|
|
31645
|
+
const schema = z5.object({
|
|
31646
|
+
jobId: z5.string().min(1),
|
|
31647
|
+
json: z5.boolean().optional()
|
|
31648
|
+
});
|
|
31649
|
+
await runJobsShow(validateOrExit(schema, { ...opts, jobId }));
|
|
31650
|
+
});
|
|
31651
|
+
jobsCommand.command("apply").description(
|
|
31652
|
+
"Write finished jobs' results into the working tree. With no ID: the finished jobs not applied yet, one per language"
|
|
31653
|
+
).argument("[ids...]", "Job ID(s) to apply").option("--json", JSON_HELP).action(async (jobIds, opts) => {
|
|
31654
|
+
const schema = z5.object({
|
|
31655
|
+
jobIds: z5.array(z5.string().min(1)),
|
|
31656
|
+
json: z5.boolean().optional()
|
|
31657
|
+
});
|
|
31658
|
+
await runJobsApply(validateOrExit(schema, { ...opts, jobIds }));
|
|
31659
|
+
});
|
|
31660
|
+
jobsCommand.command("cancel").description("Cancel a running translation job").argument("<id>", "Job ID").option("--json", JSON_HELP).action(async (jobId, opts) => {
|
|
31661
|
+
const schema = z5.object({
|
|
31662
|
+
jobId: z5.string().min(1),
|
|
31663
|
+
json: z5.boolean().optional()
|
|
31664
|
+
});
|
|
31665
|
+
await runJobsCancel(validateOrExit(schema, { ...opts, jobId }));
|
|
31666
|
+
});
|
|
31667
|
+
|
|
31668
|
+
// src/commands/learnings/index.ts
|
|
31669
|
+
import { Command as Command4 } from "commander";
|
|
31670
|
+
import { z as z6 } from "zod/v4";
|
|
31671
|
+
|
|
31672
|
+
// src/commands/learnings/archive.tsx
|
|
31673
|
+
import { Spinner as Spinner9 } from "@inkjs/ui";
|
|
31674
|
+
import { Box as Box12, render as render12, Text as Text12 } from "ink";
|
|
31675
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
31676
|
+
function ArchiveUI({ state }) {
|
|
31677
|
+
if (state.type === "archiving") {
|
|
31678
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
31679
|
+
/* @__PURE__ */ jsx12(Spinner9, {}),
|
|
31680
|
+
/* @__PURE__ */ jsx12(Text12, { children: " Archiving learning\u2026" })
|
|
31681
|
+
] });
|
|
31682
|
+
}
|
|
31683
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
31684
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
|
|
31685
|
+
/* @__PURE__ */ jsx12(Text12, { children: " Learning archived" })
|
|
31686
|
+
] });
|
|
31687
|
+
}
|
|
31688
|
+
async function runLearningsArchive(opts) {
|
|
31689
|
+
const run = async () => {
|
|
31690
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31691
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31692
|
+
await convexClient.mutation(api.learnings.archive, {
|
|
31693
|
+
organizationId: organizationId2,
|
|
31694
|
+
learningId: opts.id
|
|
31695
|
+
});
|
|
31696
|
+
};
|
|
31697
|
+
if (opts.json) {
|
|
31698
|
+
await run();
|
|
31699
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
31700
|
+
}
|
|
31701
|
+
const { unmount } = renderTransient(
|
|
31702
|
+
/* @__PURE__ */ jsx12(ArchiveUI, { state: { type: "archiving" } })
|
|
31703
|
+
);
|
|
31704
|
+
try {
|
|
31705
|
+
await run();
|
|
31706
|
+
} finally {
|
|
31707
|
+
unmount();
|
|
31708
|
+
}
|
|
31709
|
+
render12(/* @__PURE__ */ jsx12(ArchiveUI, { state: { type: "success" } })).unmount();
|
|
31710
|
+
}
|
|
31711
|
+
|
|
31712
|
+
// src/commands/learnings/list.tsx
|
|
31713
|
+
import { Spinner as Spinner10 } from "@inkjs/ui";
|
|
31714
|
+
import { Box as Box13, render as render13, Text as Text13 } from "ink";
|
|
31715
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
31716
|
+
var feedbackLabels = {
|
|
31717
|
+
"incorrect-term": "Incorrect term",
|
|
31718
|
+
"wrong-tone": "Wrong tone",
|
|
31719
|
+
"cultural-context": "Cultural context",
|
|
31720
|
+
"technical-accuracy": "Technical accuracy",
|
|
31721
|
+
other: "Other"
|
|
31722
|
+
};
|
|
31723
|
+
function LearningsListUI({ state }) {
|
|
31724
|
+
if (state.type === "loading") {
|
|
31725
|
+
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
31726
|
+
/* @__PURE__ */ jsx13(Spinner10, {}),
|
|
31727
|
+
/* @__PURE__ */ jsx13(Text13, { children: " Loading learnings\u2026" })
|
|
31728
|
+
] });
|
|
31729
|
+
}
|
|
31730
|
+
const { learnings } = state;
|
|
31731
|
+
if (learnings.length === 0) {
|
|
31732
|
+
return /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "No learnings found." }) });
|
|
31733
|
+
}
|
|
31734
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
31735
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
31736
|
+
learnings.length,
|
|
31737
|
+
" learning",
|
|
31738
|
+
learnings.length === 1 ? "" : "s"
|
|
31739
|
+
] }) }),
|
|
31740
|
+
learnings.map((l) => /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
31741
|
+
/* @__PURE__ */ jsxs13(Box13, { gap: 1, children: [
|
|
31742
|
+
/* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
31743
|
+
l.srcLocale,
|
|
31744
|
+
" \u2192 ",
|
|
31745
|
+
l.dstLocale
|
|
31746
|
+
] }),
|
|
31747
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31748
|
+
"[",
|
|
31749
|
+
feedbackLabels[l.feedbackType] ?? l.feedbackType,
|
|
31750
|
+
"]"
|
|
31751
|
+
] }),
|
|
31752
|
+
l.status === "archived" && /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: "[archived]" })
|
|
31753
|
+
] }),
|
|
31754
|
+
/* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
31755
|
+
'"',
|
|
31756
|
+
l.sourceText,
|
|
31757
|
+
'" \u2192 "',
|
|
31758
|
+
l.suggestedTranslation,
|
|
31759
|
+
'"'
|
|
31760
|
+
] }) }),
|
|
31761
|
+
l.reasoning && /* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31762
|
+
"Reason: ",
|
|
31763
|
+
l.reasoning
|
|
31764
|
+
] }) }),
|
|
31765
|
+
/* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31766
|
+
"ID: ",
|
|
31767
|
+
l._id
|
|
31768
|
+
] }) })
|
|
31769
|
+
] }, l._id))
|
|
31770
|
+
] });
|
|
31771
|
+
}
|
|
31772
|
+
async function loadLearnings(opts) {
|
|
31773
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31774
|
+
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31775
|
+
return convexClient.query(api.learnings.listByProject, {
|
|
31776
|
+
organizationId: organizationId2,
|
|
31777
|
+
projectId,
|
|
31778
|
+
status: opts.status,
|
|
31779
|
+
limit: opts.limit
|
|
31780
|
+
});
|
|
31781
|
+
}
|
|
31782
|
+
async function runLearningsList(opts) {
|
|
31783
|
+
if (opts.json) {
|
|
31784
|
+
const learnings2 = await loadLearnings(opts);
|
|
31785
|
+
jsonSuccess({
|
|
31786
|
+
learnings: learnings2.map((l) => ({
|
|
31787
|
+
id: l._id,
|
|
31788
|
+
sourceText: l.sourceText,
|
|
31789
|
+
suggestedTranslation: l.suggestedTranslation,
|
|
31790
|
+
srcLocale: l.srcLocale,
|
|
31791
|
+
dstLocale: l.dstLocale,
|
|
31792
|
+
feedbackType: l.feedbackType,
|
|
31793
|
+
reasoning: l.reasoning,
|
|
31794
|
+
status: l.status,
|
|
31795
|
+
source: l.source
|
|
31796
|
+
}))
|
|
31797
|
+
});
|
|
31798
|
+
}
|
|
31799
|
+
const { unmount } = renderTransient(
|
|
31800
|
+
/* @__PURE__ */ jsx13(LearningsListUI, { state: { type: "loading" } })
|
|
31801
|
+
);
|
|
31802
|
+
let learnings;
|
|
31803
|
+
try {
|
|
31804
|
+
learnings = await loadLearnings(opts);
|
|
31805
|
+
} finally {
|
|
31806
|
+
unmount();
|
|
31807
|
+
}
|
|
31808
|
+
render13(
|
|
31809
|
+
/* @__PURE__ */ jsx13(LearningsListUI, { state: { type: "success", learnings } })
|
|
31810
|
+
).unmount();
|
|
31811
|
+
}
|
|
31812
|
+
|
|
31813
|
+
// src/commands/learnings/remove.tsx
|
|
31814
|
+
import { Spinner as Spinner11 } from "@inkjs/ui";
|
|
31815
|
+
import { Box as Box14, render as render14, Text as Text14 } from "ink";
|
|
31816
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
31817
|
+
function RemoveUI2({ state }) {
|
|
31818
|
+
if (state.type === "removing") {
|
|
31819
|
+
return /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
31820
|
+
/* @__PURE__ */ jsx14(Spinner11, {}),
|
|
31821
|
+
/* @__PURE__ */ jsx14(Text14, { children: " Removing learning\u2026" })
|
|
31822
|
+
] });
|
|
31823
|
+
}
|
|
31824
|
+
return /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
31825
|
+
/* @__PURE__ */ jsx14(Text14, { color: "green", children: "\u2713" }),
|
|
31826
|
+
/* @__PURE__ */ jsx14(Text14, { children: " Learning removed" })
|
|
31827
|
+
] });
|
|
31828
|
+
}
|
|
31829
|
+
async function runLearningsRemove(opts) {
|
|
31830
|
+
const run = async () => {
|
|
31831
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31832
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31833
|
+
await convexClient.mutation(api.learnings.remove, {
|
|
31834
|
+
organizationId: organizationId2,
|
|
31835
|
+
learningId: opts.id
|
|
31836
|
+
});
|
|
31837
|
+
};
|
|
31838
|
+
if (opts.json) {
|
|
31839
|
+
await run();
|
|
31840
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
31841
|
+
}
|
|
31842
|
+
const { unmount } = renderTransient(
|
|
31843
|
+
/* @__PURE__ */ jsx14(RemoveUI2, { state: { type: "removing" } })
|
|
31844
|
+
);
|
|
31845
|
+
try {
|
|
31846
|
+
await run();
|
|
31847
|
+
} finally {
|
|
31848
|
+
unmount();
|
|
31849
|
+
}
|
|
31850
|
+
render14(/* @__PURE__ */ jsx14(RemoveUI2, { state: { type: "success" } })).unmount();
|
|
31851
|
+
}
|
|
31852
|
+
|
|
31853
|
+
// src/commands/learnings/show.tsx
|
|
31854
|
+
import { Spinner as Spinner12 } from "@inkjs/ui";
|
|
31855
|
+
import { Box as Box15, render as render15, Text as Text15 } from "ink";
|
|
31856
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
31857
|
+
function LearningShowUI({ state }) {
|
|
31858
|
+
if (state.type === "loading") {
|
|
31859
|
+
return /* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31860
|
+
/* @__PURE__ */ jsx15(Spinner12, {}),
|
|
31861
|
+
/* @__PURE__ */ jsx15(Text15, { children: " Loading learning\u2026" })
|
|
31862
|
+
] });
|
|
31863
|
+
}
|
|
31864
|
+
const { learning } = state;
|
|
31865
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31866
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31867
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Source text:" }),
|
|
31868
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.sourceText })
|
|
31869
|
+
] }),
|
|
31870
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31871
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Suggested translation:" }),
|
|
31872
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.suggestedTranslation })
|
|
31873
|
+
] }),
|
|
31874
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31875
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Locales:" }),
|
|
31876
|
+
/* @__PURE__ */ jsxs15(Text15, { children: [
|
|
31877
|
+
learning.srcLocale,
|
|
31878
|
+
" \u2192 ",
|
|
31879
|
+
learning.dstLocale
|
|
31880
|
+
] })
|
|
31881
|
+
] }),
|
|
31882
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31883
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Feedback type:" }),
|
|
31884
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.feedbackType })
|
|
31885
|
+
] }),
|
|
31886
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31887
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Reasoning:" }),
|
|
31888
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.reasoning })
|
|
31889
|
+
] }),
|
|
31890
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31891
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Status:" }),
|
|
31892
|
+
/* @__PURE__ */ jsx15(Text15, { color: learning.status === "active" ? "green" : "yellow", children: learning.status })
|
|
31893
|
+
] }),
|
|
31894
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31895
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Source:" }),
|
|
31896
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.source })
|
|
31897
|
+
] }),
|
|
31898
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31899
|
+
"ID: ",
|
|
31900
|
+
learning._id
|
|
31901
|
+
] }) })
|
|
31902
|
+
] });
|
|
31903
|
+
}
|
|
31904
|
+
async function loadLearning(id) {
|
|
31905
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31906
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31907
|
+
const learning = await convexClient.query(api.learnings.getById, {
|
|
31908
|
+
organizationId: organizationId2,
|
|
31909
|
+
learningId: id
|
|
31910
|
+
});
|
|
31911
|
+
if (!learning) {
|
|
31912
|
+
throw new CliError(
|
|
31913
|
+
`Learning ${id} was not found.`,
|
|
31914
|
+
"List them with 'tolkbot learnings list'.",
|
|
31915
|
+
"not_found"
|
|
31916
|
+
);
|
|
31917
|
+
}
|
|
31918
|
+
return learning;
|
|
31919
|
+
}
|
|
31920
|
+
async function runLearningsShow(opts) {
|
|
31921
|
+
if (opts.json) {
|
|
31922
|
+
const learning2 = await loadLearning(opts.id);
|
|
31923
|
+
jsonSuccess({
|
|
31924
|
+
id: learning2._id,
|
|
31925
|
+
sourceText: learning2.sourceText,
|
|
31926
|
+
suggestedTranslation: learning2.suggestedTranslation,
|
|
31927
|
+
srcLocale: learning2.srcLocale,
|
|
31928
|
+
dstLocale: learning2.dstLocale,
|
|
31929
|
+
feedbackType: learning2.feedbackType,
|
|
31930
|
+
reasoning: learning2.reasoning,
|
|
31931
|
+
status: learning2.status,
|
|
31932
|
+
source: learning2.source
|
|
31933
|
+
});
|
|
31934
|
+
}
|
|
31935
|
+
const { unmount } = renderTransient(
|
|
31936
|
+
/* @__PURE__ */ jsx15(LearningShowUI, { state: { type: "loading" } })
|
|
31937
|
+
);
|
|
31938
|
+
let learning;
|
|
31939
|
+
try {
|
|
31940
|
+
learning = await loadLearning(opts.id);
|
|
31941
|
+
} finally {
|
|
31942
|
+
unmount();
|
|
31943
|
+
}
|
|
31944
|
+
render15(/* @__PURE__ */ jsx15(LearningShowUI, { state: { type: "success", learning } })).unmount();
|
|
31945
|
+
}
|
|
31946
|
+
|
|
31947
|
+
// src/commands/learnings/index.ts
|
|
31948
|
+
var learningsCommand = new Command4("learnings").description(
|
|
31949
|
+
"View and manage translation learnings"
|
|
31950
|
+
);
|
|
31951
|
+
learningsCommand.command("list").description("List translation learnings for the current project").option("--status <status>", "Filter by status (active or archived)").option("--limit <n>", "Max number of results", (v) => Number(v), 100).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31952
|
+
const schema = z6.object({
|
|
31953
|
+
status: z6.enum(["active", "archived"]).optional(),
|
|
31954
|
+
limit: z6.coerce.number().int().positive().default(100),
|
|
31955
|
+
json: z6.boolean().optional()
|
|
31956
|
+
});
|
|
31957
|
+
const parsed = validateOrExit(schema, opts);
|
|
31958
|
+
await runLearningsList(parsed);
|
|
31959
|
+
});
|
|
31960
|
+
learningsCommand.command("show").description("Show details of a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31961
|
+
const schema = z6.object({
|
|
31962
|
+
id: z6.string().min(1),
|
|
31963
|
+
json: z6.boolean().optional()
|
|
31964
|
+
});
|
|
31965
|
+
const parsed = validateOrExit(schema, opts);
|
|
31966
|
+
await runLearningsShow(parsed);
|
|
31967
|
+
});
|
|
31968
|
+
learningsCommand.command("archive").description("Archive a learning (keeps it but excludes from translations)").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31969
|
+
const schema = z6.object({
|
|
31970
|
+
id: z6.string().min(1),
|
|
31971
|
+
json: z6.boolean().optional()
|
|
31972
|
+
});
|
|
31973
|
+
const parsed = validateOrExit(schema, opts);
|
|
31974
|
+
await runLearningsArchive(parsed);
|
|
31975
|
+
});
|
|
31976
|
+
learningsCommand.command("remove").description("Permanently remove a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31977
|
+
const schema = z6.object({
|
|
31978
|
+
id: z6.string().min(1),
|
|
31979
|
+
json: z6.boolean().optional()
|
|
31980
|
+
});
|
|
31981
|
+
const parsed = validateOrExit(schema, opts);
|
|
31982
|
+
await runLearningsRemove(parsed);
|
|
31983
|
+
});
|
|
31984
|
+
|
|
31985
|
+
// src/commands/telemetry.ts
|
|
31986
|
+
import { Command as Command5 } from "commander";
|
|
31987
|
+
import { z as z7 } from "zod/v4";
|
|
31988
|
+
var REASONS = {
|
|
31989
|
+
env: `disabled by $${TELEMETRY_ENV_VAR}`,
|
|
31990
|
+
"do-not-track": `disabled by $${DO_NOT_TRACK_ENV_VAR}`,
|
|
31991
|
+
preference: "disabled with `tolkbot telemetry off`",
|
|
31992
|
+
"no-key": "disabled: this build has no telemetry key",
|
|
31993
|
+
default: "enabled"
|
|
31994
|
+
};
|
|
31995
|
+
async function printStatus(json2) {
|
|
31996
|
+
const state = await telemetryState();
|
|
31997
|
+
if (json2) {
|
|
31998
|
+
jsonResult({ ...state, preferenceFile: getPreferencePath() }, 0);
|
|
31999
|
+
return;
|
|
32000
|
+
}
|
|
32001
|
+
console.log(
|
|
32002
|
+
`Telemetry is ${state.enabled ? "on" : "off"} (${REASONS[state.reason]}).`
|
|
32003
|
+
);
|
|
32004
|
+
console.log(
|
|
32005
|
+
`Turn it off with \`tolkbot telemetry off\`, $${TELEMETRY_ENV_VAR}=0 or $${DO_NOT_TRACK_ENV_VAR}=1.`
|
|
32006
|
+
);
|
|
32007
|
+
}
|
|
32008
|
+
var telemetryCommand = new Command5("telemetry").description(
|
|
32009
|
+
"Control anonymous usage reporting"
|
|
32010
|
+
);
|
|
32011
|
+
telemetryCommand.command("status", { isDefault: true }).description("Show whether telemetry is on and why").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32012
|
+
const parsed = validateOrExit(
|
|
32013
|
+
z7.object({ json: z7.boolean().optional() }),
|
|
32014
|
+
opts
|
|
32015
|
+
);
|
|
32016
|
+
await printStatus(parsed.json);
|
|
32017
|
+
});
|
|
32018
|
+
telemetryCommand.command("off").description("Stop reporting usage from this machine").action(async () => {
|
|
32019
|
+
await setTelemetryPreference(false);
|
|
32020
|
+
console.log(
|
|
32021
|
+
`Telemetry is off. Preference saved to ${getPreferencePath()}.`
|
|
32022
|
+
);
|
|
32023
|
+
});
|
|
32024
|
+
telemetryCommand.command("on").description("Resume reporting usage from this machine").action(async () => {
|
|
32025
|
+
await setTelemetryPreference(true);
|
|
32026
|
+
await printStatus(false);
|
|
32027
|
+
});
|
|
32028
|
+
|
|
32029
|
+
// src/commands/translate/index.ts
|
|
32030
|
+
import { Command as Command6 } from "commander";
|
|
32031
|
+
import { z as z8 } from "zod/v4";
|
|
32032
|
+
|
|
32033
|
+
// src/commands/translate/adopt.tsx
|
|
32034
|
+
var STAMP_CHUNK2 = 200;
|
|
32035
|
+
async function adoptLocale(ctx, srcLocale, dstLocale) {
|
|
32036
|
+
const plan = await planForLocale(ctx, srcLocale, dstLocale);
|
|
32037
|
+
const locale = governanceLocale(ctx.project, dstLocale);
|
|
32038
|
+
const byTemplate = /* @__PURE__ */ new Map();
|
|
32039
|
+
for (const key of plan.untrackedKeys) {
|
|
32040
|
+
const group = byTemplate.get(key.pathTemplate) ?? [];
|
|
32041
|
+
group.push(key);
|
|
32042
|
+
byTemplate.set(key.pathTemplate, group);
|
|
32043
|
+
}
|
|
32044
|
+
const outcome = {
|
|
32045
|
+
dstLocale,
|
|
32046
|
+
untracked: plan.untrackedKeys.length,
|
|
32047
|
+
adopted: 0,
|
|
32048
|
+
skipped: 0
|
|
32049
|
+
};
|
|
32050
|
+
for (const [pathTemplate, keys] of byTemplate) {
|
|
32051
|
+
for (let i = 0; i < keys.length; i += STAMP_CHUNK2) {
|
|
32052
|
+
const result = await ctx.client.mutation(
|
|
32053
|
+
api.localeKeyStates.adoptUntrackedForCli,
|
|
32054
|
+
{
|
|
32055
|
+
organizationId: ctx.project.organizationId,
|
|
32056
|
+
projectId: ctx.project._id,
|
|
32057
|
+
locale,
|
|
32058
|
+
pathTemplate,
|
|
32059
|
+
entries: keys.slice(i, i + STAMP_CHUNK2).map((key) => ({
|
|
32060
|
+
key: key.fileKey,
|
|
32061
|
+
namespace: key.namespace,
|
|
32062
|
+
baseValue: key.src,
|
|
32063
|
+
translatedValue: key.dst
|
|
32064
|
+
}))
|
|
32065
|
+
}
|
|
32066
|
+
);
|
|
32067
|
+
outcome.adopted += result.adopted;
|
|
32068
|
+
outcome.skipped += result.skipped;
|
|
32069
|
+
}
|
|
32070
|
+
}
|
|
32071
|
+
return outcome;
|
|
32072
|
+
}
|
|
32073
|
+
async function runAdopt(opts) {
|
|
32074
|
+
const run = async () => {
|
|
32075
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
32076
|
+
const src = opts.src ?? ctx.project.baseLanguage;
|
|
32077
|
+
const locales = opts.all ? ctx.project.targetLanguages : opts.dst ? [opts.dst] : [];
|
|
32078
|
+
if (locales.length === 0) {
|
|
32079
|
+
throw opts.all ? new CliError(
|
|
32080
|
+
"No target languages configured for this project.",
|
|
32081
|
+
void 0,
|
|
32082
|
+
"no_target_languages"
|
|
32083
|
+
) : new UsageError(
|
|
32084
|
+
"Either --dst <locale> or --all must be provided."
|
|
32085
|
+
);
|
|
32086
|
+
}
|
|
32087
|
+
const outcomes2 = [];
|
|
32088
|
+
for (const dst of locales)
|
|
32089
|
+
outcomes2.push(await adoptLocale(ctx, src, dst));
|
|
32090
|
+
track("cli_translate_completed", {
|
|
32091
|
+
mode: "adopt",
|
|
32092
|
+
key_count: outcomes2.reduce((n, o) => n + o.adopted, 0),
|
|
32093
|
+
locale_count: outcomes2.length,
|
|
32094
|
+
job_count: 0,
|
|
32095
|
+
json: opts.json === true
|
|
32096
|
+
});
|
|
32097
|
+
return outcomes2;
|
|
32098
|
+
};
|
|
32099
|
+
if (opts.json) {
|
|
32100
|
+
jsonSuccess({ adopted: await run() });
|
|
32101
|
+
}
|
|
32102
|
+
const outcomes = await run();
|
|
32103
|
+
for (const o of outcomes) {
|
|
32104
|
+
console.log(
|
|
32105
|
+
`\u2713 ${o.dstLocale}: adopted ${o.adopted} untracked translation(s)` + (o.skipped > 0 ? `, ${o.skipped} already tracked` : "")
|
|
32083
32106
|
);
|
|
32084
32107
|
}
|
|
32085
|
-
|
|
32086
|
-
|
|
32087
|
-
|
|
32088
|
-
|
|
32089
|
-
|
|
32108
|
+
}
|
|
32109
|
+
|
|
32110
|
+
// src/commands/translate/edit.tsx
|
|
32111
|
+
import path10 from "path";
|
|
32112
|
+
import { Spinner as Spinner13 } from "@inkjs/ui";
|
|
32113
|
+
import { Box as Box16, render as render16, Text as Text16 } from "ink";
|
|
32114
|
+
|
|
32115
|
+
// src/utils/locale-files.ts
|
|
32116
|
+
import path9 from "path";
|
|
32117
|
+
async function findLocaleFiles(baseDir) {
|
|
32118
|
+
const adapter = createAdapter("json");
|
|
32119
|
+
const result = await adapter.discover(baseDir);
|
|
32120
|
+
const localeFiles = /* @__PURE__ */ new Map();
|
|
32121
|
+
for (const [locale, files] of result.filesByLocale) {
|
|
32122
|
+
localeFiles.set(
|
|
32123
|
+
locale,
|
|
32124
|
+
files.map((f) => f.path)
|
|
32090
32125
|
);
|
|
32091
32126
|
}
|
|
32092
|
-
|
|
32093
|
-
const refused = argumentError(message);
|
|
32094
|
-
if (refused) return refused;
|
|
32095
|
-
if (message.includes("Server Error")) {
|
|
32096
|
-
return new CliError(serverMessage(message));
|
|
32097
|
-
}
|
|
32098
|
-
const internal = new CliError(message, void 0, "internal");
|
|
32099
|
-
if (error62 instanceof Error) internal.stack = error62.stack;
|
|
32100
|
-
return internal;
|
|
32101
|
-
}
|
|
32102
|
-
function errorPayload(error62) {
|
|
32103
|
-
return {
|
|
32104
|
-
error: {
|
|
32105
|
-
code: error62.code,
|
|
32106
|
-
message: error62.baseMessage,
|
|
32107
|
-
hint: error62.hint ?? null
|
|
32108
|
-
}
|
|
32109
|
-
};
|
|
32127
|
+
return localeFiles;
|
|
32110
32128
|
}
|
|
32111
|
-
function
|
|
32112
|
-
|
|
32113
|
-
if (
|
|
32114
|
-
|
|
32115
|
-
|
|
32116
|
-
|
|
32117
|
-
if (
|
|
32118
|
-
|
|
32119
|
-
console.error("");
|
|
32120
|
-
console.error(cli.stack);
|
|
32129
|
+
function resolveFileForKey(files, key, namespace) {
|
|
32130
|
+
if (files.length === 0) return null;
|
|
32131
|
+
if (files.length === 1) return files[0] ?? null;
|
|
32132
|
+
const ns = namespace ?? key.split(".")[0];
|
|
32133
|
+
for (const file2 of files) {
|
|
32134
|
+
const fileName = path9.parse(path9.basename(file2)).name;
|
|
32135
|
+
if (fileName === ns) {
|
|
32136
|
+
return file2;
|
|
32121
32137
|
}
|
|
32122
32138
|
}
|
|
32123
|
-
|
|
32124
|
-
process.exitCode = code;
|
|
32125
|
-
return code;
|
|
32139
|
+
return files[0] ?? null;
|
|
32126
32140
|
}
|
|
32127
|
-
function
|
|
32128
|
-
|
|
32129
|
-
|
|
32130
|
-
|
|
32131
|
-
|
|
32141
|
+
function resolveKeyInFile(key, targetFile, fileCount, namespace) {
|
|
32142
|
+
if (fileCount <= 1 || namespace) return key;
|
|
32143
|
+
const fileName = path9.parse(path9.basename(targetFile)).name;
|
|
32144
|
+
if (key.startsWith(`${fileName}.`)) {
|
|
32145
|
+
return key.slice(fileName.length + 1);
|
|
32146
|
+
}
|
|
32147
|
+
return key;
|
|
32132
32148
|
}
|
|
32133
32149
|
|
|
32134
|
-
// src/commands/translate/
|
|
32135
|
-
import {
|
|
32136
|
-
|
|
32137
|
-
|
|
32138
|
-
|
|
32139
|
-
|
|
32140
|
-
|
|
32141
|
-
"Job:",
|
|
32142
|
-
" ",
|
|
32143
|
-
/* @__PURE__ */ jsx19(Text19, { color: "blue", underline: true, children: url2 })
|
|
32144
|
-
] });
|
|
32145
|
-
}
|
|
32146
|
-
function PlanNotes({ notes }) {
|
|
32147
|
-
if (!notes || notes.length === 0) return null;
|
|
32148
|
-
return /* @__PURE__ */ jsx19(Box19, { flexDirection: "column", paddingLeft: 2, children: notes.map((note) => /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: note }, note)) });
|
|
32149
|
-
}
|
|
32150
|
-
function JobRow({ srcLocale, job }) {
|
|
32151
|
-
const { dstLocale, status, taskCount, progress, jobId, error: error62, notes } = job;
|
|
32152
|
-
if (status === "waiting") {
|
|
32153
|
-
return /* @__PURE__ */ jsxs19(Box19, { children: [
|
|
32154
|
-
/* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "\u23F8 " }),
|
|
32155
|
-
/* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
32156
|
-
srcLocale,
|
|
32157
|
-
" \u2192 ",
|
|
32158
|
-
dstLocale,
|
|
32159
|
-
": Waiting..."
|
|
32160
|
-
] })
|
|
32150
|
+
// src/commands/translate/edit.tsx
|
|
32151
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
32152
|
+
function EditUI({ state }) {
|
|
32153
|
+
if (state.type === "editing") {
|
|
32154
|
+
return /* @__PURE__ */ jsxs16(Box16, { children: [
|
|
32155
|
+
/* @__PURE__ */ jsx16(Spinner13, {}),
|
|
32156
|
+
/* @__PURE__ */ jsx16(Text16, { children: " Editing translation\u2026" })
|
|
32161
32157
|
] });
|
|
32162
32158
|
}
|
|
32163
|
-
|
|
32164
|
-
|
|
32165
|
-
/* @__PURE__ */
|
|
32166
|
-
/* @__PURE__ */
|
|
32159
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
32160
|
+
/* @__PURE__ */ jsxs16(Box16, { children: [
|
|
32161
|
+
/* @__PURE__ */ jsx16(Text16, { color: "green", children: "\u2713" }),
|
|
32162
|
+
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
32167
32163
|
" ",
|
|
32168
|
-
|
|
32169
|
-
|
|
32170
|
-
dstLocale,
|
|
32171
|
-
": Generating tasks..."
|
|
32164
|
+
"Updated ",
|
|
32165
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: state.key })
|
|
32172
32166
|
] })
|
|
32173
|
-
] })
|
|
32167
|
+
] }),
|
|
32168
|
+
/* @__PURE__ */ jsx16(Box16, { paddingLeft: 2, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: state.value }) }),
|
|
32169
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
|
|
32170
|
+
"File: ",
|
|
32171
|
+
state.filePath
|
|
32172
|
+
] }) })
|
|
32173
|
+
] });
|
|
32174
|
+
}
|
|
32175
|
+
async function performEdit(opts) {
|
|
32176
|
+
const baseDir = process.cwd();
|
|
32177
|
+
const config3 = readConfig(baseDir);
|
|
32178
|
+
if (!config3) throw noConfigError();
|
|
32179
|
+
const localeFiles = await findLocaleFiles(baseDir);
|
|
32180
|
+
const files = localeFiles.get(opts.locale);
|
|
32181
|
+
if (!files || files.length === 0) {
|
|
32182
|
+
throw new CliError(
|
|
32183
|
+
`No translation files found for locale "${opts.locale}".`,
|
|
32184
|
+
`Available locales: ${[...localeFiles.keys()].join(", ") || "none"}.`,
|
|
32185
|
+
"no_translation_files"
|
|
32186
|
+
);
|
|
32174
32187
|
}
|
|
32175
|
-
|
|
32176
|
-
|
|
32177
|
-
|
|
32178
|
-
|
|
32179
|
-
|
|
32180
|
-
|
|
32181
|
-
/* @__PURE__ */ jsxs19(Text19, { children: [
|
|
32182
|
-
" ",
|
|
32183
|
-
srcLocale,
|
|
32184
|
-
" \u2192 ",
|
|
32185
|
-
dstLocale,
|
|
32186
|
-
": Translating... ",
|
|
32187
|
-
processed,
|
|
32188
|
-
"/",
|
|
32189
|
-
progress.total,
|
|
32190
|
-
" (",
|
|
32191
|
-
percent,
|
|
32192
|
-
"%)"
|
|
32193
|
-
] })
|
|
32194
|
-
] }),
|
|
32195
|
-
jobId && /* @__PURE__ */ jsx19(Box19, { paddingLeft: 2, children: /* @__PURE__ */ jsx19(JobLink, { jobId }) })
|
|
32196
|
-
] });
|
|
32188
|
+
const targetFile = resolveFileForKey(files, opts.key, opts.namespace);
|
|
32189
|
+
if (!targetFile) {
|
|
32190
|
+
throw new CliError(
|
|
32191
|
+
`Could not determine target file for key "${opts.key}" in locale "${opts.locale}".`,
|
|
32192
|
+
"Pass --namespace to say which file the key belongs to."
|
|
32193
|
+
);
|
|
32197
32194
|
}
|
|
32198
|
-
|
|
32199
|
-
|
|
32200
|
-
|
|
32201
|
-
|
|
32202
|
-
|
|
32203
|
-
|
|
32204
|
-
|
|
32205
|
-
|
|
32206
|
-
|
|
32207
|
-
|
|
32208
|
-
|
|
32209
|
-
|
|
32210
|
-
progress.completed,
|
|
32211
|
-
"/",
|
|
32212
|
-
progress.total,
|
|
32213
|
-
" completed",
|
|
32214
|
-
progress.failed > 0 && ` (${progress.failed} failed)`
|
|
32215
|
-
] })
|
|
32216
|
-
] }),
|
|
32217
|
-
jobId && /* @__PURE__ */ jsx19(Box19, { paddingLeft: 2, children: /* @__PURE__ */ jsx19(JobLink, { jobId }) }),
|
|
32218
|
-
/* @__PURE__ */ jsx19(PlanNotes, { notes })
|
|
32219
|
-
] });
|
|
32195
|
+
const keyInFile = resolveKeyInFile(
|
|
32196
|
+
opts.key,
|
|
32197
|
+
targetFile,
|
|
32198
|
+
files.length,
|
|
32199
|
+
opts.namespace
|
|
32200
|
+
);
|
|
32201
|
+
const adapter = createAdapter("json");
|
|
32202
|
+
let existingData = {};
|
|
32203
|
+
try {
|
|
32204
|
+
const parsed = await adapter.parse(targetFile);
|
|
32205
|
+
existingData = parsed.data;
|
|
32206
|
+
} catch {
|
|
32220
32207
|
}
|
|
32221
|
-
|
|
32222
|
-
|
|
32223
|
-
|
|
32224
|
-
|
|
32225
|
-
|
|
32226
|
-
|
|
32227
|
-
|
|
32228
|
-
|
|
32229
|
-
|
|
32230
|
-
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
|
|
32234
|
-
|
|
32208
|
+
const updatedData = setByPath(
|
|
32209
|
+
existingData,
|
|
32210
|
+
keyInFile,
|
|
32211
|
+
opts.value
|
|
32212
|
+
);
|
|
32213
|
+
await adapter.serialize(targetFile, updatedData, { merge: false });
|
|
32214
|
+
const relativePath = path10.relative(baseDir, targetFile);
|
|
32215
|
+
return { filePath: relativePath, key: opts.key, value: opts.value };
|
|
32216
|
+
}
|
|
32217
|
+
async function runEdit(opts) {
|
|
32218
|
+
if (opts.json) {
|
|
32219
|
+
const result2 = await performEdit(opts);
|
|
32220
|
+
jsonSuccess({
|
|
32221
|
+
file: result2.filePath,
|
|
32222
|
+
key: result2.key,
|
|
32223
|
+
value: result2.value
|
|
32224
|
+
});
|
|
32235
32225
|
}
|
|
32236
|
-
|
|
32237
|
-
|
|
32238
|
-
|
|
32239
|
-
|
|
32240
|
-
|
|
32241
|
-
|
|
32242
|
-
" \u2192 ",
|
|
32243
|
-
dstLocale,
|
|
32244
|
-
": Up to date (no translations needed)"
|
|
32245
|
-
] })
|
|
32246
|
-
] }),
|
|
32247
|
-
/* @__PURE__ */ jsx19(PlanNotes, { notes })
|
|
32248
|
-
] });
|
|
32226
|
+
const { unmount } = renderTransient(/* @__PURE__ */ jsx16(EditUI, { state: { type: "editing" } }));
|
|
32227
|
+
let result;
|
|
32228
|
+
try {
|
|
32229
|
+
result = await performEdit(opts);
|
|
32230
|
+
} finally {
|
|
32231
|
+
unmount();
|
|
32249
32232
|
}
|
|
32250
|
-
|
|
32251
|
-
}
|
|
32252
|
-
function RunUI({ srcLocale, jobs }) {
|
|
32253
|
-
const hasMultiple = jobs.length > 1;
|
|
32254
|
-
const allCompleted = jobs.every(
|
|
32255
|
-
(j) => j.status === "completed" || j.status === "error"
|
|
32256
|
-
);
|
|
32257
|
-
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
32258
|
-
hasMultiple && /* @__PURE__ */ jsx19(Box19, { marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text19, { bold: true, children: [
|
|
32259
|
-
"\u{1F30D} Translating from ",
|
|
32260
|
-
srcLocale,
|
|
32261
|
-
" to ",
|
|
32262
|
-
jobs.length,
|
|
32263
|
-
" ",
|
|
32264
|
-
"languages"
|
|
32265
|
-
] }) }),
|
|
32266
|
-
jobs.map((job) => /* @__PURE__ */ jsx19(JobRow, { srcLocale, job }, job.dstLocale)),
|
|
32267
|
-
hasMultiple && allCompleted && /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
|
|
32268
|
-
/* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "\u2500".repeat(60) }),
|
|
32269
|
-
/* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsx19(Text19, { bold: true, children: "\u{1F4CA} Summary" }) }),
|
|
32270
|
-
jobs.map((job) => {
|
|
32271
|
-
if (!job.progress) return null;
|
|
32272
|
-
const icon = job.progress.status === "succeeded" ? "\u2713" : job.progress.status === "partial" ? "\u26A0" : "\u2717";
|
|
32273
|
-
return /* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsxs19(Text19, { children: [
|
|
32274
|
-
icon,
|
|
32275
|
-
" ",
|
|
32276
|
-
srcLocale,
|
|
32277
|
-
" \u2192 ",
|
|
32278
|
-
job.dstLocale,
|
|
32279
|
-
":",
|
|
32280
|
-
" ",
|
|
32281
|
-
job.progress.completed,
|
|
32282
|
-
"/",
|
|
32283
|
-
job.progress.total,
|
|
32284
|
-
job.progress.failed > 0 && ` (${job.progress.failed} failed)`
|
|
32285
|
-
] }) }, job.dstLocale);
|
|
32286
|
-
}),
|
|
32287
|
-
/* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "\u2500".repeat(60) })
|
|
32288
|
-
] })
|
|
32289
|
-
] });
|
|
32233
|
+
render16(/* @__PURE__ */ jsx16(EditUI, { state: { type: "success", ...result } })).unmount();
|
|
32290
32234
|
}
|
|
32291
32235
|
|
|
32292
|
-
// src/commands/translate/translate
|
|
32293
|
-
|
|
32294
|
-
|
|
32295
|
-
|
|
32296
|
-
|
|
32297
|
-
|
|
32298
|
-
|
|
32299
|
-
|
|
32300
|
-
|
|
32236
|
+
// src/commands/translate/translate.tsx
|
|
32237
|
+
import { Spinner as Spinner14 } from "@inkjs/ui";
|
|
32238
|
+
import { Box as Box17, Text as Text17 } from "ink";
|
|
32239
|
+
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
32240
|
+
function planOutput(plan) {
|
|
32241
|
+
return {
|
|
32242
|
+
...plan.summary,
|
|
32243
|
+
protectedKeys: plan.protectedKeys,
|
|
32244
|
+
heldKeys: plan.heldKeys,
|
|
32245
|
+
untrackedKeys: plan.untrackedKeys.map((k) => k.key)
|
|
32246
|
+
};
|
|
32301
32247
|
}
|
|
32302
|
-
function
|
|
32303
|
-
return
|
|
32248
|
+
function fullKey4(namespace, key) {
|
|
32249
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
32304
32250
|
}
|
|
32305
|
-
function
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
|
|
32310
|
-
|
|
32251
|
+
function characters(text) {
|
|
32252
|
+
return Array.from(text).length;
|
|
32253
|
+
}
|
|
32254
|
+
function targetLocales(ctx, opts) {
|
|
32255
|
+
if (opts.dst) return [opts.dst];
|
|
32256
|
+
const locales = ctx.project.targetLanguages;
|
|
32257
|
+
if (locales.length === 0) {
|
|
32258
|
+
throw new CliError(
|
|
32259
|
+
"No target languages configured.",
|
|
32260
|
+
`Configure them in the project settings at ${env.TOLKBOT_WEB_URL}/projects/${ctx.project.slug}/settings.`,
|
|
32261
|
+
"no_target_languages"
|
|
32311
32262
|
);
|
|
32312
32263
|
}
|
|
32313
|
-
|
|
32314
|
-
|
|
32315
|
-
|
|
32316
|
-
|
|
32317
|
-
|
|
32318
|
-
|
|
32264
|
+
return locales;
|
|
32265
|
+
}
|
|
32266
|
+
async function runTranslate(opts) {
|
|
32267
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
32268
|
+
const srcLocale = opts.src ?? ctx.project.baseLanguage;
|
|
32269
|
+
const dstLocales = targetLocales(ctx, opts);
|
|
32270
|
+
if (opts.dryRun) {
|
|
32271
|
+
await runDryRun(ctx, srcLocale, dstLocales, opts);
|
|
32272
|
+
return;
|
|
32319
32273
|
}
|
|
32320
|
-
|
|
32274
|
+
await runLanguages(ctx, srcLocale, dstLocales, opts);
|
|
32321
32275
|
}
|
|
32322
|
-
|
|
32323
|
-
|
|
32324
|
-
|
|
32325
|
-
|
|
32326
|
-
|
|
32327
|
-
|
|
32328
|
-
|
|
32329
|
-
|
|
32330
|
-
);
|
|
32331
|
-
this.jobId = jobId;
|
|
32332
|
-
this.name = "JobWaitTimeoutError";
|
|
32276
|
+
async function creditBalance(ctx) {
|
|
32277
|
+
try {
|
|
32278
|
+
const balance = await ctx.client.query(api.usage.getCreditBalance, {
|
|
32279
|
+
organizationId: ctx.project.organizationId
|
|
32280
|
+
});
|
|
32281
|
+
return balance?.total ?? null;
|
|
32282
|
+
} catch {
|
|
32283
|
+
return null;
|
|
32333
32284
|
}
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
async function
|
|
32337
|
-
const
|
|
32338
|
-
const
|
|
32339
|
-
|
|
32340
|
-
|
|
32341
|
-
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
|
|
32348
|
-
|
|
32285
|
+
}
|
|
32286
|
+
var NO_ESTIMATE = "No credit estimate exists for this project yet. Keys and characters are what a run would send.";
|
|
32287
|
+
async function runDryRun(ctx, srcLocale, dstLocales, opts) {
|
|
32288
|
+
const languages = [];
|
|
32289
|
+
const progress = renderTransient(
|
|
32290
|
+
/* @__PURE__ */ jsx17(Working, { text: `Planning ${dstLocales.join(", ")}\u2026` })
|
|
32291
|
+
);
|
|
32292
|
+
try {
|
|
32293
|
+
for (const dstLocale of dstLocales) {
|
|
32294
|
+
const { plan } = await generateTasks(ctx, srcLocale, dstLocale, {
|
|
32295
|
+
retranslateUntracked: opts.retranslateUntracked
|
|
32296
|
+
});
|
|
32297
|
+
languages.push({
|
|
32298
|
+
dstLocale,
|
|
32299
|
+
keys: plan.tasks.length,
|
|
32300
|
+
characters: plan.tasks.reduce(
|
|
32301
|
+
(n, task) => n + characters(task.src),
|
|
32302
|
+
0
|
|
32303
|
+
),
|
|
32304
|
+
tasks: plan.tasks.map((task) => ({
|
|
32305
|
+
key: fullKey4(task.namespace, task.key),
|
|
32306
|
+
path: task.path,
|
|
32307
|
+
reason: task.reason,
|
|
32308
|
+
...opts.verbose ? { src: task.src } : {}
|
|
32309
|
+
})),
|
|
32310
|
+
plan
|
|
32311
|
+
});
|
|
32312
|
+
}
|
|
32313
|
+
} finally {
|
|
32314
|
+
progress.unmount();
|
|
32315
|
+
}
|
|
32316
|
+
const total = {
|
|
32317
|
+
keys: languages.reduce((n, l) => n + l.keys, 0),
|
|
32318
|
+
characters: languages.reduce((n, l) => n + l.characters, 0)
|
|
32319
|
+
};
|
|
32320
|
+
const balance = await creditBalance(ctx);
|
|
32321
|
+
track("cli_translate_completed", {
|
|
32322
|
+
mode: "plan",
|
|
32323
|
+
key_count: total.keys,
|
|
32324
|
+
locale_count: languages.length,
|
|
32325
|
+
job_count: 0,
|
|
32326
|
+
json: opts.json ?? false
|
|
32327
|
+
});
|
|
32328
|
+
if (opts.json) {
|
|
32329
|
+
jsonSuccess({
|
|
32330
|
+
dryRun: true,
|
|
32331
|
+
srcLocale,
|
|
32332
|
+
languages: languages.map(({ plan, ...language }) => ({
|
|
32333
|
+
...language,
|
|
32334
|
+
plan: planOutput(plan)
|
|
32335
|
+
})),
|
|
32336
|
+
total,
|
|
32337
|
+
estimate: null,
|
|
32338
|
+
estimateNote: NO_ESTIMATE,
|
|
32339
|
+
credits: { balance }
|
|
32340
|
+
});
|
|
32341
|
+
}
|
|
32342
|
+
for (const language of languages) {
|
|
32343
|
+
const head = `${srcLocale} \u2192 ${language.dstLocale}`;
|
|
32344
|
+
if (language.keys === 0) {
|
|
32345
|
+
console.log(`\u2713 ${head}: up to date`);
|
|
32346
|
+
} else {
|
|
32347
|
+
console.log(
|
|
32348
|
+
`\u2022 ${head}: ${language.keys} key(s) to translate, ${language.characters} characters`
|
|
32349
32349
|
);
|
|
32350
32350
|
}
|
|
32351
|
-
|
|
32352
|
-
|
|
32353
|
-
|
|
32354
|
-
|
|
32351
|
+
for (const line of describePlan(language.plan)) {
|
|
32352
|
+
console.log(` ${line}`);
|
|
32353
|
+
}
|
|
32354
|
+
if (opts.verbose) {
|
|
32355
|
+
for (const task of language.tasks) {
|
|
32356
|
+
console.log(` ${task.key}: ${JSON.stringify(task.src)}`);
|
|
32357
|
+
}
|
|
32355
32358
|
}
|
|
32356
|
-
await sleep(pollMs);
|
|
32357
32359
|
}
|
|
32358
|
-
|
|
32359
|
-
|
|
32360
|
-
|
|
32361
|
-
projectId,
|
|
32362
|
-
organizationId: organizationId2,
|
|
32363
|
-
framework,
|
|
32364
|
-
srcLocale,
|
|
32365
|
-
dstLocale,
|
|
32366
|
-
tasks,
|
|
32367
|
-
onProgress
|
|
32368
|
-
} = params;
|
|
32369
|
-
const convexClient = await getAuthenticatedConvexClient();
|
|
32370
|
-
const jobId = await convexClient.action(api.translations.startJob, {
|
|
32371
|
-
organizationId: organizationId2,
|
|
32372
|
-
projectId,
|
|
32373
|
-
srcLocale,
|
|
32374
|
-
dstLocale,
|
|
32375
|
-
framework,
|
|
32376
|
-
tasks: tasks.map((t) => ({
|
|
32377
|
-
key: t.key,
|
|
32378
|
-
namespace: t.namespace,
|
|
32379
|
-
path: t.path,
|
|
32380
|
-
src: t.src,
|
|
32381
|
-
hash: t.hash,
|
|
32382
|
-
context: t.context
|
|
32383
|
-
}))
|
|
32384
|
-
});
|
|
32385
|
-
const summary = await waitForJob(
|
|
32386
|
-
jobId,
|
|
32387
|
-
() => convexClient.query(api.translations.getJobSummary, {
|
|
32388
|
-
organizationId: organizationId2,
|
|
32389
|
-
jobId
|
|
32390
|
-
}),
|
|
32391
|
-
{ onProgress }
|
|
32360
|
+
console.log(
|
|
32361
|
+
`
|
|
32362
|
+
In all: ${total.keys} key(s), ${total.characters} characters.` + (balance === null ? "" : ` Credit balance: ${balance}.`)
|
|
32392
32363
|
);
|
|
32393
|
-
|
|
32364
|
+
console.log(NO_ESTIMATE);
|
|
32365
|
+
console.log("Nothing was started. Run without --dry-run to translate.");
|
|
32394
32366
|
}
|
|
32395
|
-
|
|
32396
|
-
|
|
32397
|
-
|
|
32398
|
-
|
|
32399
|
-
|
|
32367
|
+
function Working({ text }) {
|
|
32368
|
+
return /* @__PURE__ */ jsxs17(Box17, { children: [
|
|
32369
|
+
/* @__PURE__ */ jsx17(Spinner14, {}),
|
|
32370
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
32371
|
+
" ",
|
|
32372
|
+
text
|
|
32373
|
+
] })
|
|
32374
|
+
] });
|
|
32375
|
+
}
|
|
32376
|
+
function progressText(dstLocale, summary) {
|
|
32377
|
+
if (!summary) return `${dstLocale}: starting\u2026`;
|
|
32378
|
+
const done = summary.completed + summary.failed;
|
|
32379
|
+
return `${dstLocale}: translating ${done}/${summary.total}\u2026`;
|
|
32380
|
+
}
|
|
32381
|
+
function describeOutcome(outcome) {
|
|
32382
|
+
const { dstLocale, status, keys, written } = outcome;
|
|
32383
|
+
const wrote = written ? `, ${written.keys} written to ${written.files} file(s)` : "";
|
|
32384
|
+
const notApplied = outcome.notApplied.length > 0 ? `, ${outcome.notApplied.length} not applied` : "";
|
|
32385
|
+
switch (status) {
|
|
32386
|
+
case "up-to-date":
|
|
32387
|
+
return `\u2713 ${dstLocale}: up to date`;
|
|
32388
|
+
case "started":
|
|
32389
|
+
return `\u2192 ${dstLocale}: started job ${outcome.jobId} with ${keys} key(s)`;
|
|
32390
|
+
case "succeeded":
|
|
32391
|
+
return `\u2713 ${dstLocale}: ${outcome.completed} translated${wrote}${notApplied}`;
|
|
32392
|
+
case "partial":
|
|
32393
|
+
return `\u26A0 ${dstLocale}: ${outcome.completed} of ${keys} translated, ${outcome.failed} failed${wrote}${notApplied}`;
|
|
32394
|
+
case "failed":
|
|
32395
|
+
return `\u2717 ${dstLocale}: the job failed (${outcome.jobId})${wrote}${notApplied}`;
|
|
32396
|
+
case "cancelled":
|
|
32397
|
+
return `\u2717 ${dstLocale}: the job was cancelled (${outcome.jobId})`;
|
|
32398
|
+
case "timed-out":
|
|
32399
|
+
return `\u2717 ${dstLocale}: ${outcome.error}`;
|
|
32400
|
+
case "not-started":
|
|
32401
|
+
return `\u2717 ${dstLocale}: not started. ${outcome.error}`;
|
|
32402
|
+
case "pending":
|
|
32403
|
+
case "running":
|
|
32404
|
+
return `\u2026 ${dstLocale}: ${status}`;
|
|
32405
|
+
}
|
|
32406
|
+
}
|
|
32407
|
+
async function runLanguages(ctx, srcLocale, dstLocales, opts) {
|
|
32400
32408
|
const { project, framework } = ctx;
|
|
32401
|
-
const
|
|
32402
|
-
const
|
|
32403
|
-
let
|
|
32404
|
-
|
|
32405
|
-
|
|
32406
|
-
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
|
|
32410
|
-
|
|
32411
|
-
|
|
32412
|
-
|
|
32413
|
-
|
|
32414
|
-
|
|
32415
|
-
|
|
32416
|
-
throw new UsageError(
|
|
32417
|
-
"Either --dst <locale> or --all must be provided."
|
|
32409
|
+
const json2 = opts.json ?? false;
|
|
32410
|
+
const outcomes = [];
|
|
32411
|
+
let failure2 = null;
|
|
32412
|
+
let stopped = null;
|
|
32413
|
+
const report = (outcome) => {
|
|
32414
|
+
outcomes.push(outcome);
|
|
32415
|
+
if (json2) return;
|
|
32416
|
+
console.log(describeOutcome(outcome));
|
|
32417
|
+
for (const line of describeNotApplied(outcome.notApplied)) {
|
|
32418
|
+
console.log(` ${line}`);
|
|
32419
|
+
}
|
|
32420
|
+
};
|
|
32421
|
+
for (const dstLocale of dstLocales) {
|
|
32422
|
+
const progress = renderTransient(
|
|
32423
|
+
/* @__PURE__ */ jsx17(Working, { text: `${dstLocale}: planning\u2026` })
|
|
32418
32424
|
);
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
|
|
32423
|
-
|
|
32424
|
-
|
|
32425
|
-
|
|
32426
|
-
const
|
|
32427
|
-
|
|
32425
|
+
try {
|
|
32426
|
+
const { tasks, plan } = await generateTasks(
|
|
32427
|
+
ctx,
|
|
32428
|
+
srcLocale,
|
|
32429
|
+
dstLocale,
|
|
32430
|
+
{ retranslateUntracked: opts.retranslateUntracked }
|
|
32431
|
+
);
|
|
32432
|
+
const base = {
|
|
32433
|
+
dstLocale,
|
|
32434
|
+
status: "up-to-date",
|
|
32435
|
+
jobId: null,
|
|
32436
|
+
keys: tasks.length,
|
|
32437
|
+
completed: null,
|
|
32438
|
+
failed: null,
|
|
32439
|
+
written: null,
|
|
32440
|
+
notApplied: [],
|
|
32441
|
+
error: null,
|
|
32442
|
+
plan: planOutput(plan)
|
|
32443
|
+
};
|
|
32444
|
+
const leftOut = describeLeftOut(plan);
|
|
32445
|
+
if (stopped !== null) {
|
|
32446
|
+
progress.unmount();
|
|
32447
|
+
report({ ...base, status: "not-started", error: stopped });
|
|
32448
|
+
continue;
|
|
32449
|
+
}
|
|
32428
32450
|
if (tasks.length === 0) {
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
plan: summary
|
|
32433
|
-
});
|
|
32451
|
+
progress.unmount();
|
|
32452
|
+
report(base);
|
|
32453
|
+
printNotes(json2, leftOut);
|
|
32434
32454
|
continue;
|
|
32435
32455
|
}
|
|
32456
|
+
progress.rerender(/* @__PURE__ */ jsx17(Working, { text: progressText(dstLocale, null) }));
|
|
32457
|
+
let jobId;
|
|
32436
32458
|
try {
|
|
32437
|
-
|
|
32438
|
-
projectId,
|
|
32459
|
+
jobId = await startTranslationJob({
|
|
32460
|
+
projectId: project._id,
|
|
32439
32461
|
organizationId: project.organizationId,
|
|
32440
32462
|
framework,
|
|
32441
32463
|
srcLocale,
|
|
32442
32464
|
dstLocale,
|
|
32443
32465
|
tasks
|
|
32444
32466
|
});
|
|
32445
|
-
results.push({
|
|
32446
|
-
dstLocale,
|
|
32447
|
-
jobId,
|
|
32448
|
-
status: job.status,
|
|
32449
|
-
completed: job.completed,
|
|
32450
|
-
failed: job.failed,
|
|
32451
|
-
total: job.total,
|
|
32452
|
-
error: job.status === "cancelled" ? "The job was cancelled." : void 0,
|
|
32453
|
-
plan: summary
|
|
32454
|
-
});
|
|
32455
|
-
failure3 ??= unsuccessfulJobError(jobId, job.status);
|
|
32456
32467
|
} catch (error62) {
|
|
32457
|
-
|
|
32458
|
-
|
|
32459
|
-
|
|
32460
|
-
|
|
32461
|
-
|
|
32462
|
-
|
|
32463
|
-
|
|
32468
|
+
const refused = toCliError(error62);
|
|
32469
|
+
failure2 ??= refused;
|
|
32470
|
+
progress.unmount();
|
|
32471
|
+
report({
|
|
32472
|
+
...base,
|
|
32473
|
+
status: "not-started",
|
|
32474
|
+
error: refused.baseMessage
|
|
32464
32475
|
});
|
|
32465
|
-
|
|
32466
|
-
|
|
32476
|
+
if (exitCodeFor(refused.code) === EXIT_CODES.billing) {
|
|
32477
|
+
stopped = `TolkBot refused the ${dstLocale} job: ${refused.baseMessage}`;
|
|
32478
|
+
}
|
|
32479
|
+
continue;
|
|
32467
32480
|
}
|
|
32468
|
-
if (
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32472
|
-
key_count: results.reduce((n, r) => n + (r.total ?? 0), 0),
|
|
32473
|
-
locale_count: results.length,
|
|
32474
|
-
job_count: results.filter((r) => r.jobId).length,
|
|
32475
|
-
json: true
|
|
32476
|
-
});
|
|
32477
|
-
if (!failure3) jsonSuccess({ jobs: results });
|
|
32478
|
-
jsonResult({ jobs: results }, 1);
|
|
32479
|
-
fail(failure3);
|
|
32480
|
-
}
|
|
32481
|
-
const jobs = dstLocales.map((locale) => ({
|
|
32482
|
-
dstLocale: locale,
|
|
32483
|
-
status: "waiting"
|
|
32484
|
-
}));
|
|
32485
|
-
const { rerender, unmount } = render19(
|
|
32486
|
-
/* @__PURE__ */ jsx20(RunUI, { srcLocale, jobs })
|
|
32487
|
-
);
|
|
32488
|
-
const updateJob = (locale, update) => {
|
|
32489
|
-
const index = jobs.findIndex((j) => j.dstLocale === locale);
|
|
32490
|
-
const currentJob = jobs[index];
|
|
32491
|
-
if (index !== -1 && currentJob) {
|
|
32492
|
-
jobs[index] = {
|
|
32493
|
-
...currentJob,
|
|
32494
|
-
...update,
|
|
32495
|
-
dstLocale: currentJob.dstLocale,
|
|
32496
|
-
status: update.status ?? currentJob.status
|
|
32497
|
-
};
|
|
32498
|
-
rerender(/* @__PURE__ */ jsx20(RunUI, { srcLocale, jobs }));
|
|
32499
|
-
}
|
|
32500
|
-
};
|
|
32501
|
-
const skipRemaining = (locale, why) => {
|
|
32502
|
-
for (const later of dstLocales.slice(dstLocales.indexOf(locale) + 1)) {
|
|
32503
|
-
updateJob(later, {
|
|
32504
|
-
status: "error",
|
|
32505
|
-
error: `Not started, because ${why} the ${locale} job.`
|
|
32506
|
-
});
|
|
32507
|
-
}
|
|
32508
|
-
};
|
|
32509
|
-
let failure2 = null;
|
|
32510
|
-
try {
|
|
32511
|
-
for (const dstLocale of dstLocales) {
|
|
32512
|
-
updateJob(dstLocale, { status: "generating" });
|
|
32513
|
-
const { tasks, plan } = await tasksFor(dstLocale);
|
|
32514
|
-
const notes = plan ? describePlan(plan) : [];
|
|
32515
|
-
if (tasks.length === 0) {
|
|
32516
|
-
updateJob(dstLocale, {
|
|
32517
|
-
status: "completed",
|
|
32518
|
-
taskCount: 0,
|
|
32519
|
-
notes
|
|
32520
|
-
});
|
|
32481
|
+
if (!opts.wait) {
|
|
32482
|
+
progress.unmount();
|
|
32483
|
+
report({ ...base, status: "started", jobId });
|
|
32484
|
+
printNotes(json2, leftOut);
|
|
32521
32485
|
continue;
|
|
32522
32486
|
}
|
|
32523
|
-
|
|
32524
|
-
status: "translating",
|
|
32525
|
-
taskCount: tasks.length,
|
|
32526
|
-
notes
|
|
32527
|
-
});
|
|
32528
|
-
let outcome;
|
|
32487
|
+
let summary;
|
|
32529
32488
|
try {
|
|
32530
|
-
|
|
32531
|
-
|
|
32532
|
-
|
|
32533
|
-
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
|
|
32537
|
-
onProgress: (progress) => {
|
|
32538
|
-
updateJob(dstLocale, { progress });
|
|
32539
|
-
}
|
|
32540
|
-
});
|
|
32489
|
+
summary = await waitForTranslationJob(
|
|
32490
|
+
project.organizationId,
|
|
32491
|
+
jobId,
|
|
32492
|
+
(latest) => progress.rerender(
|
|
32493
|
+
/* @__PURE__ */ jsx17(Working, { text: progressText(dstLocale, latest) })
|
|
32494
|
+
)
|
|
32495
|
+
);
|
|
32541
32496
|
} catch (error62) {
|
|
32542
32497
|
if (!(error62 instanceof JobWaitTimeoutError)) throw error62;
|
|
32543
|
-
updateJob(dstLocale, {
|
|
32544
|
-
status: "error",
|
|
32545
|
-
jobId: error62.jobId,
|
|
32546
|
-
error: error62.message
|
|
32547
|
-
});
|
|
32548
32498
|
failure2 ??= error62;
|
|
32549
|
-
|
|
32550
|
-
|
|
32551
|
-
|
|
32552
|
-
|
|
32553
|
-
if (summary.status === "failed") {
|
|
32554
|
-
updateJob(dstLocale, {
|
|
32555
|
-
status: "error",
|
|
32556
|
-
progress: summary,
|
|
32557
|
-
jobId,
|
|
32558
|
-
error: "Translation failed"
|
|
32559
|
-
});
|
|
32560
|
-
failure2 ??= unsuccessfulJobError(jobId, summary.status);
|
|
32561
|
-
} else if (summary.status === "cancelled") {
|
|
32562
|
-
updateJob(dstLocale, {
|
|
32563
|
-
status: "error",
|
|
32564
|
-
progress: summary,
|
|
32499
|
+
progress.unmount();
|
|
32500
|
+
report({
|
|
32501
|
+
...base,
|
|
32502
|
+
status: "timed-out",
|
|
32565
32503
|
jobId,
|
|
32566
|
-
error:
|
|
32567
|
-
});
|
|
32568
|
-
failure2 ??= unsuccessfulJobError(jobId, summary.status);
|
|
32569
|
-
skipRemaining(dstLocale, "someone cancelled");
|
|
32570
|
-
break;
|
|
32571
|
-
} else {
|
|
32572
|
-
updateJob(dstLocale, {
|
|
32573
|
-
status: "completed",
|
|
32574
|
-
progress: summary,
|
|
32575
|
-
jobId
|
|
32504
|
+
error: error62.baseMessage
|
|
32576
32505
|
});
|
|
32506
|
+
stopped = `The CLI stopped waiting for the ${dstLocale} job.`;
|
|
32507
|
+
continue;
|
|
32508
|
+
}
|
|
32509
|
+
const finished = {
|
|
32510
|
+
...base,
|
|
32511
|
+
status: summary.status,
|
|
32512
|
+
jobId,
|
|
32513
|
+
completed: summary.completed,
|
|
32514
|
+
failed: summary.failed
|
|
32515
|
+
};
|
|
32516
|
+
if (summary.status !== "cancelled") {
|
|
32517
|
+
progress.rerender(
|
|
32518
|
+
/* @__PURE__ */ jsx17(Working, { text: `${dstLocale}: writing the files\u2026` })
|
|
32519
|
+
);
|
|
32520
|
+
const applied = await applyJob(ctx, jobId);
|
|
32521
|
+
finished.written = { files: applied.files, keys: applied.keys };
|
|
32522
|
+
finished.notApplied = applied.notApplied;
|
|
32523
|
+
}
|
|
32524
|
+
const unsuccessful = unsuccessfulJobError(jobId, summary.status);
|
|
32525
|
+
if (unsuccessful) {
|
|
32526
|
+
failure2 ??= unsuccessful;
|
|
32527
|
+
finished.error = unsuccessful.baseMessage;
|
|
32577
32528
|
}
|
|
32529
|
+
progress.unmount();
|
|
32530
|
+
report(finished);
|
|
32531
|
+
printNotes(json2, leftOut);
|
|
32532
|
+
if (summary.status === "cancelled") {
|
|
32533
|
+
stopped = `The ${dstLocale} job was cancelled.`;
|
|
32534
|
+
}
|
|
32535
|
+
} finally {
|
|
32536
|
+
progress.unmount();
|
|
32578
32537
|
}
|
|
32579
|
-
track("cli_translate_completed", {
|
|
32580
|
-
mode: "run",
|
|
32581
|
-
key_count: jobs.reduce((n, j) => n + (j.taskCount ?? 0), 0),
|
|
32582
|
-
locale_count: dstLocales.length,
|
|
32583
|
-
job_count: jobs.filter((j) => j.jobId).length,
|
|
32584
|
-
json: false
|
|
32585
|
-
});
|
|
32586
|
-
} finally {
|
|
32587
|
-
unmount();
|
|
32588
|
-
}
|
|
32589
|
-
if (failure2) fail(failure2);
|
|
32590
|
-
}
|
|
32591
|
-
function summarize(plan) {
|
|
32592
|
-
return {
|
|
32593
|
-
...plan.summary,
|
|
32594
|
-
protectedKeys: plan.protectedKeys,
|
|
32595
|
-
heldKeys: plan.heldKeys,
|
|
32596
|
-
untrackedKeys: plan.untrackedKeys.map((k) => k.key)
|
|
32597
|
-
};
|
|
32598
|
-
}
|
|
32599
|
-
|
|
32600
|
-
// src/commands/translate/status.tsx
|
|
32601
|
-
import { Spinner as Spinner17 } from "@inkjs/ui";
|
|
32602
|
-
import { Box as Box20, render as render20, Text as Text20 } from "ink";
|
|
32603
|
-
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
32604
|
-
function StatusUI({ state }) {
|
|
32605
|
-
if (state.type === "loading") {
|
|
32606
|
-
return /* @__PURE__ */ jsxs20(Box20, { children: [
|
|
32607
|
-
/* @__PURE__ */ jsx21(Spinner17, {}),
|
|
32608
|
-
/* @__PURE__ */ jsx21(Text20, { children: " Loading job status\u2026" })
|
|
32609
|
-
] });
|
|
32610
|
-
}
|
|
32611
|
-
const { summary } = state;
|
|
32612
|
-
const processed = summary.completed + summary.failed;
|
|
32613
|
-
const progress = summary.total > 0 ? Math.round(processed / summary.total * 100) : 0;
|
|
32614
|
-
if (state.type === "running") {
|
|
32615
|
-
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", children: [
|
|
32616
|
-
/* @__PURE__ */ jsxs20(Box20, { children: [
|
|
32617
|
-
/* @__PURE__ */ jsx21(Spinner17, {}),
|
|
32618
|
-
/* @__PURE__ */ jsx21(Text20, { children: " Job is running\u2026" })
|
|
32619
|
-
] }),
|
|
32620
|
-
/* @__PURE__ */ jsx21(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { children: [
|
|
32621
|
-
"Progress: ",
|
|
32622
|
-
processed,
|
|
32623
|
-
"/",
|
|
32624
|
-
summary.total,
|
|
32625
|
-
" (",
|
|
32626
|
-
progress,
|
|
32627
|
-
"%)"
|
|
32628
|
-
] }) }),
|
|
32629
|
-
/* @__PURE__ */ jsx21(Box20, { children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
|
|
32630
|
-
"Completed: ",
|
|
32631
|
-
summary.completed,
|
|
32632
|
-
" | Failed:",
|
|
32633
|
-
" ",
|
|
32634
|
-
summary.failed
|
|
32635
|
-
] }) }),
|
|
32636
|
-
/* @__PURE__ */ jsx21(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
|
|
32637
|
-
"Status: ",
|
|
32638
|
-
summary.status
|
|
32639
|
-
] }) })
|
|
32640
|
-
] });
|
|
32641
32538
|
}
|
|
32642
|
-
|
|
32643
|
-
|
|
32644
|
-
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
32648
|
-
/* @__PURE__ */ jsxs20(Text20, { children: [
|
|
32649
|
-
" Job ",
|
|
32650
|
-
statusText
|
|
32651
|
-
] })
|
|
32652
|
-
] }),
|
|
32653
|
-
/* @__PURE__ */ jsx21(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { children: [
|
|
32654
|
-
"Completed: ",
|
|
32655
|
-
summary.completed,
|
|
32656
|
-
" | Failed: ",
|
|
32657
|
-
summary.failed,
|
|
32658
|
-
" | Total: ",
|
|
32659
|
-
summary.total
|
|
32660
|
-
] }) }),
|
|
32661
|
-
/* @__PURE__ */ jsx21(Box20, { marginTop: 1, children: /* @__PURE__ */ jsxs20(Text20, { dimColor: true, children: [
|
|
32662
|
-
"Status: ",
|
|
32663
|
-
summary.status
|
|
32664
|
-
] }) })
|
|
32665
|
-
] });
|
|
32666
|
-
}
|
|
32667
|
-
async function fetchJobSummary(jobId) {
|
|
32668
|
-
const { client: client3, project } = await loadPlanContext(process.cwd());
|
|
32669
|
-
const summary = await client3.query(api.translations.getJobSummary, {
|
|
32670
|
-
organizationId: project.organizationId,
|
|
32671
|
-
jobId
|
|
32539
|
+
track("cli_translate_completed", {
|
|
32540
|
+
mode: "run",
|
|
32541
|
+
key_count: outcomes.reduce((n, o) => n + o.keys, 0),
|
|
32542
|
+
locale_count: outcomes.length,
|
|
32543
|
+
job_count: outcomes.filter((o) => o.jobId).length,
|
|
32544
|
+
json: json2
|
|
32672
32545
|
});
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
|
|
32546
|
+
const payload = { srcLocale, languages: outcomes };
|
|
32547
|
+
if (json2) {
|
|
32548
|
+
if (!failure2) jsonSuccess(payload);
|
|
32549
|
+
jsonResult(payload, 1);
|
|
32550
|
+
fail(failure2);
|
|
32551
|
+
}
|
|
32552
|
+
if (!opts.wait && outcomes.some((o) => o.status === "started")) {
|
|
32553
|
+
console.log(
|
|
32554
|
+
"\nThe jobs keep running. Follow them with 'tolkbot jobs list' and write them into the files with 'tolkbot jobs apply'."
|
|
32678
32555
|
);
|
|
32679
32556
|
}
|
|
32680
|
-
|
|
32557
|
+
if (failure2) fail(failure2);
|
|
32681
32558
|
}
|
|
32682
|
-
|
|
32683
|
-
if (
|
|
32684
|
-
|
|
32685
|
-
|
|
32686
|
-
|
|
32687
|
-
|
|
32688
|
-
|
|
32689
|
-
|
|
32690
|
-
|
|
32691
|
-
|
|
32692
|
-
const failure2 = unsuccessfulJobError(opts.jobId, summary2.status);
|
|
32693
|
-
if (!failure2) jsonSuccess(output2);
|
|
32694
|
-
jsonResult(output2, 1);
|
|
32695
|
-
fail(failure2);
|
|
32559
|
+
function printNotes(json2, notes) {
|
|
32560
|
+
if (json2) return;
|
|
32561
|
+
for (const note of notes) console.log(` ${note}`);
|
|
32562
|
+
}
|
|
32563
|
+
function checkTranslateOptions(opts) {
|
|
32564
|
+
if (!opts.dst && !opts.all) {
|
|
32565
|
+
throw new UsageError(
|
|
32566
|
+
"Either --dst <locale> or --all must be provided.",
|
|
32567
|
+
"Run 'tolkbot translate --help'."
|
|
32568
|
+
);
|
|
32696
32569
|
}
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
);
|
|
32700
|
-
let summary;
|
|
32701
|
-
try {
|
|
32702
|
-
summary = await fetchJobSummary(opts.jobId);
|
|
32703
|
-
} finally {
|
|
32704
|
-
unmount();
|
|
32570
|
+
if (opts.dst && opts.all) {
|
|
32571
|
+
throw new UsageError("Cannot use both --dst and --all.");
|
|
32705
32572
|
}
|
|
32706
|
-
|
|
32707
|
-
|
|
32708
|
-
|
|
32709
|
-
|
|
32710
|
-
state: {
|
|
32711
|
-
type: isJobFinished(summary.status) ? "completed" : "running",
|
|
32712
|
-
summary
|
|
32713
|
-
}
|
|
32714
|
-
}
|
|
32715
|
-
)
|
|
32716
|
-
);
|
|
32717
|
-
finalUi.unmount();
|
|
32718
|
-
if (isJobUnsuccessful(summary.status)) {
|
|
32719
|
-
const failure2 = unsuccessfulJobError(opts.jobId, summary.status);
|
|
32720
|
-
if (failure2) fail(failure2);
|
|
32573
|
+
if (opts.dryRun && !opts.wait) {
|
|
32574
|
+
throw new UsageError(
|
|
32575
|
+
"Cannot use --dry-run with --no-wait: a dry run starts nothing."
|
|
32576
|
+
);
|
|
32721
32577
|
}
|
|
32722
32578
|
}
|
|
32723
32579
|
|
|
32724
32580
|
// src/commands/translate/index.ts
|
|
32725
|
-
var translateCommand = new Command5("translate").description(
|
|
32726
|
-
"Translate with TolkBot"
|
|
32727
|
-
);
|
|
32728
32581
|
var RETRANSLATE_UNTRACKED_HELP = "Also retranslate translations TolkBot has no record of (kept by default)";
|
|
32729
|
-
translateCommand
|
|
32730
|
-
"
|
|
32582
|
+
var translateCommand = new Command6("translate").description(
|
|
32583
|
+
"Translate what is missing or changed, and write the results into your locale files"
|
|
32731
32584
|
).option(
|
|
32732
32585
|
"--src <locale>",
|
|
32733
32586
|
"Source locale (defaults to the project's base language)"
|
|
32734
|
-
).
|
|
32735
|
-
|
|
32736
|
-
|
|
32737
|
-
|
|
32738
|
-
|
|
32739
|
-
|
|
32740
|
-
json: z7.boolean().optional()
|
|
32741
|
-
});
|
|
32742
|
-
const parsed = validateOrExit(schema, opts);
|
|
32743
|
-
await runPlan(parsed);
|
|
32744
|
-
});
|
|
32745
|
-
translateCommand.command("run").description("Run translation job (auto-generates tasks if not provided)").option(
|
|
32746
|
-
"--src <locale>",
|
|
32747
|
-
"Source locale (defaults to repository base language)"
|
|
32748
|
-
).option("--dst <locale>", "Destination locale").option("--all", "Translate to all target languages").option("--tasks <path>", "Path to tasks JSON array (optional)").option("--retranslate-untracked", RETRANSLATE_UNTRACKED_HELP).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32749
|
-
const schema = z7.object({
|
|
32750
|
-
src: z7.string().optional(),
|
|
32751
|
-
dst: z7.string().optional(),
|
|
32752
|
-
all: z7.boolean().optional(),
|
|
32753
|
-
tasks: z7.string().optional(),
|
|
32754
|
-
retranslateUntracked: z7.boolean().optional(),
|
|
32755
|
-
json: z7.boolean().optional()
|
|
32756
|
-
});
|
|
32757
|
-
const parsed = validateOrExit(schema, opts);
|
|
32758
|
-
if (!parsed.dst && !parsed.all) {
|
|
32587
|
+
).option("--dst <locale>", "Destination locale").option("--all", "Every target language").option("--dry-run", "Print what a run would translate, and start nothing").option(
|
|
32588
|
+
"--no-wait",
|
|
32589
|
+
"Start the jobs and print their IDs without waiting; apply them later with 'tolkbot jobs apply'"
|
|
32590
|
+
).option("--retranslate-untracked", RETRANSLATE_UNTRACKED_HELP).option("--verbose", "With --dry-run, include each key's source string").option("--json", "Output as JSON (machine-readable)").allowExcessArguments(true).enablePositionalOptions().action(async (opts, command) => {
|
|
32591
|
+
const [word] = command.args;
|
|
32592
|
+
if (word !== void 0) {
|
|
32759
32593
|
throw new UsageError(
|
|
32760
|
-
|
|
32594
|
+
`unknown command 'translate ${word}'`,
|
|
32595
|
+
"Run 'tolkbot translate --help'. Jobs are under 'tolkbot jobs'."
|
|
32761
32596
|
);
|
|
32762
32597
|
}
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32598
|
+
const schema = z8.object({
|
|
32599
|
+
src: z8.string().min(1).optional(),
|
|
32600
|
+
dst: z8.string().min(1).optional(),
|
|
32601
|
+
all: z8.boolean().optional(),
|
|
32602
|
+
dryRun: z8.boolean().optional(),
|
|
32603
|
+
wait: z8.boolean(),
|
|
32604
|
+
retranslateUntracked: z8.boolean().optional(),
|
|
32605
|
+
verbose: z8.boolean().optional(),
|
|
32606
|
+
json: z8.boolean().optional()
|
|
32607
|
+
});
|
|
32608
|
+
const parsed = validateOrExit(schema, opts);
|
|
32609
|
+
checkTranslateOptions(parsed);
|
|
32766
32610
|
await runTranslate(parsed);
|
|
32767
32611
|
});
|
|
32768
32612
|
translateCommand.command("adopt").description(
|
|
@@ -32771,11 +32615,11 @@ translateCommand.command("adopt").description(
|
|
|
32771
32615
|
"--src <locale>",
|
|
32772
32616
|
"Source locale (defaults to the project's base language)"
|
|
32773
32617
|
).option("--dst <locale>", "Destination locale").option("--all", "Every target language").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32774
|
-
const schema =
|
|
32775
|
-
src:
|
|
32776
|
-
dst:
|
|
32777
|
-
all:
|
|
32778
|
-
json:
|
|
32618
|
+
const schema = z8.object({
|
|
32619
|
+
src: z8.string().min(1).optional(),
|
|
32620
|
+
dst: z8.string().min(1).optional(),
|
|
32621
|
+
all: z8.boolean().optional(),
|
|
32622
|
+
json: z8.boolean().optional()
|
|
32779
32623
|
});
|
|
32780
32624
|
const parsed = validateOrExit(schema, opts);
|
|
32781
32625
|
if (parsed.dst && parsed.all) {
|
|
@@ -32783,46 +32627,20 @@ translateCommand.command("adopt").description(
|
|
|
32783
32627
|
}
|
|
32784
32628
|
await runAdopt(parsed);
|
|
32785
32629
|
});
|
|
32786
|
-
translateCommand.command("cancel").description("Cancel translation job by job ID").requiredOption("--job-id <id>", "Job ID to cancel").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32787
|
-
const schema = z7.object({
|
|
32788
|
-
jobId: z7.string().min(1),
|
|
32789
|
-
json: z7.boolean().optional()
|
|
32790
|
-
});
|
|
32791
|
-
const parsed = validateOrExit(schema, opts);
|
|
32792
|
-
await runCancel(parsed);
|
|
32793
|
-
});
|
|
32794
|
-
translateCommand.command("apply").description(
|
|
32795
|
-
"Apply finished translation jobs to the working tree. Without --job-id: the one unapplied job per language, or a picker in a terminal"
|
|
32796
|
-
).option("--job-id <id...>", "Job ID(s) to apply").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32797
|
-
const schema = z7.object({
|
|
32798
|
-
jobId: z7.array(z7.string()).optional(),
|
|
32799
|
-
json: z7.boolean().optional()
|
|
32800
|
-
});
|
|
32801
|
-
const parsed = validateOrExit(schema, opts);
|
|
32802
|
-
await runApply(parsed);
|
|
32803
|
-
});
|
|
32804
32630
|
translateCommand.command("edit").description("Edit a translation value in a locale file").requiredOption("--key <key>", "Translation key (e.g. common.hello)").requiredOption("--locale <locale>", "Target locale (e.g. fr, de)").requiredOption("--value <text>", "New translation value").option("--namespace <ns>", "Namespace to resolve file (if ambiguous)").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32805
|
-
const schema =
|
|
32806
|
-
key:
|
|
32807
|
-
locale:
|
|
32808
|
-
value:
|
|
32809
|
-
namespace:
|
|
32810
|
-
json:
|
|
32631
|
+
const schema = z8.object({
|
|
32632
|
+
key: z8.string().min(1),
|
|
32633
|
+
locale: z8.string().min(1),
|
|
32634
|
+
value: z8.string().min(1),
|
|
32635
|
+
namespace: z8.string().optional(),
|
|
32636
|
+
json: z8.boolean().optional()
|
|
32811
32637
|
});
|
|
32812
32638
|
const parsed = validateOrExit(schema, opts);
|
|
32813
32639
|
await runEdit(parsed);
|
|
32814
32640
|
});
|
|
32815
|
-
translateCommand.command("status").description("Check status of translation job by job ID").requiredOption("--job-id <id>", "Job ID to check status").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32816
|
-
const schema = z7.object({
|
|
32817
|
-
jobId: z7.string().min(1),
|
|
32818
|
-
json: z7.boolean().optional()
|
|
32819
|
-
});
|
|
32820
|
-
const parsed = validateOrExit(schema, opts);
|
|
32821
|
-
await runStatus(parsed);
|
|
32822
|
-
});
|
|
32823
32641
|
|
|
32824
32642
|
// src/commands/validate.tsx
|
|
32825
|
-
import { Box as
|
|
32643
|
+
import { Box as Box18, render as render17, Text as Text18 } from "ink";
|
|
32826
32644
|
|
|
32827
32645
|
// ../ai/src/validators/placeholders.ts
|
|
32828
32646
|
var IDENTIFIER = /^[A-Za-z0-9_$][A-Za-z0-9_$.[\]-]*$/;
|
|
@@ -33915,7 +33733,7 @@ function finalize2(issues, stats, strict) {
|
|
|
33915
33733
|
}
|
|
33916
33734
|
|
|
33917
33735
|
// src/commands/validate.tsx
|
|
33918
|
-
import { jsx as
|
|
33736
|
+
import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
33919
33737
|
var LEVEL_COLOR = {
|
|
33920
33738
|
error: "red",
|
|
33921
33739
|
warning: "yellow",
|
|
@@ -33924,12 +33742,12 @@ var LEVEL_COLOR = {
|
|
|
33924
33742
|
var GUTTER = 9;
|
|
33925
33743
|
function IssueLine({ issue: issue2 }) {
|
|
33926
33744
|
const location = [issue2.file, issue2.key].filter(Boolean).join(" \xB7 ");
|
|
33927
|
-
return /* @__PURE__ */
|
|
33928
|
-
/* @__PURE__ */
|
|
33929
|
-
/* @__PURE__ */
|
|
33930
|
-
/* @__PURE__ */
|
|
33745
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
33746
|
+
/* @__PURE__ */ jsxs18(Box18, { children: [
|
|
33747
|
+
/* @__PURE__ */ jsx18(Box18, { width: GUTTER, flexShrink: 0, children: /* @__PURE__ */ jsx18(Text18, { color: LEVEL_COLOR[issue2.level], children: issue2.level }) }),
|
|
33748
|
+
/* @__PURE__ */ jsx18(Box18, { flexGrow: 1, children: /* @__PURE__ */ jsx18(Text18, { children: issue2.message }) })
|
|
33931
33749
|
] }),
|
|
33932
|
-
/* @__PURE__ */
|
|
33750
|
+
/* @__PURE__ */ jsx18(Box18, { marginLeft: GUTTER, children: /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
33933
33751
|
issue2.rule,
|
|
33934
33752
|
location ? ` \u2014 ${location}` : ""
|
|
33935
33753
|
] }) })
|
|
@@ -33938,20 +33756,20 @@ function IssueLine({ issue: issue2 }) {
|
|
|
33938
33756
|
function ReportUI({ report }) {
|
|
33939
33757
|
const { counts, stats } = report;
|
|
33940
33758
|
const summary = ["error", "warning", "info"].filter((level) => counts[level] > 0).map((level) => `${counts[level]} ${level}(s)`).join(", ");
|
|
33941
|
-
return /* @__PURE__ */
|
|
33942
|
-
/* @__PURE__ */
|
|
33943
|
-
/* @__PURE__ */
|
|
33944
|
-
/* @__PURE__ */
|
|
33759
|
+
return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
|
|
33760
|
+
/* @__PURE__ */ jsxs18(Box18, { children: [
|
|
33761
|
+
/* @__PURE__ */ jsx18(Text18, { color: report.valid ? "green" : "red", children: report.valid ? "\u2713" : "\u2717" }),
|
|
33762
|
+
/* @__PURE__ */ jsxs18(Text18, { children: [
|
|
33945
33763
|
" Validation ",
|
|
33946
33764
|
report.valid ? "passed" : "failed"
|
|
33947
33765
|
] }),
|
|
33948
|
-
summary ? /* @__PURE__ */
|
|
33766
|
+
summary ? /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
33949
33767
|
" \u2014 ",
|
|
33950
33768
|
summary
|
|
33951
33769
|
] }) : null
|
|
33952
33770
|
] }),
|
|
33953
|
-
stats.locales.length > 0 && /* @__PURE__ */
|
|
33954
|
-
/* @__PURE__ */
|
|
33771
|
+
stats.locales.length > 0 && /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
|
|
33772
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
33955
33773
|
stats.filesChecked,
|
|
33956
33774
|
" ",
|
|
33957
33775
|
stats.format,
|
|
@@ -33960,18 +33778,18 @@ function ReportUI({ report }) {
|
|
|
33960
33778
|
stats.totalKeys,
|
|
33961
33779
|
" keys"
|
|
33962
33780
|
] }),
|
|
33963
|
-
/* @__PURE__ */
|
|
33781
|
+
/* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
33964
33782
|
"Locales: ",
|
|
33965
33783
|
stats.locales.join(", "),
|
|
33966
33784
|
stats.baseLocale ? ` (base: ${stats.baseLocale})` : ""
|
|
33967
33785
|
] }),
|
|
33968
|
-
stats.pattern && /* @__PURE__ */
|
|
33786
|
+
stats.pattern && /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
|
|
33969
33787
|
"Pattern: ",
|
|
33970
33788
|
stats.pattern
|
|
33971
33789
|
] })
|
|
33972
33790
|
] }),
|
|
33973
|
-
report.issues.length > 0 && /* @__PURE__ */
|
|
33974
|
-
!report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */
|
|
33791
|
+
report.issues.length > 0 && /* @__PURE__ */ jsx18(Box18, { marginTop: 1, flexDirection: "column", gap: 1, children: report.issues.map((issue2, idx) => /* @__PURE__ */ jsx18(IssueLine, { issue: issue2 }, idx)) }),
|
|
33792
|
+
!report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Warnings did not fail this run. Use --strict to treat them as errors." }) })
|
|
33975
33793
|
] });
|
|
33976
33794
|
}
|
|
33977
33795
|
function emit(report, json2) {
|
|
@@ -33991,7 +33809,7 @@ function emit(report, json2) {
|
|
|
33991
33809
|
if (!report.valid) fail(validationFailure(report));
|
|
33992
33810
|
return;
|
|
33993
33811
|
}
|
|
33994
|
-
|
|
33812
|
+
render17(/* @__PURE__ */ jsx18(ReportUI, { report })).unmount();
|
|
33995
33813
|
process.exitCode = exitCode;
|
|
33996
33814
|
}
|
|
33997
33815
|
function validationFailure(report) {
|
|
@@ -34050,12 +33868,9 @@ function commandPath(command) {
|
|
|
34050
33868
|
return names.join(" ");
|
|
34051
33869
|
}
|
|
34052
33870
|
function buildProgram() {
|
|
34053
|
-
const program = new
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
);
|
|
34057
|
-
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
34058
|
-
const { token } = thisCommand.opts();
|
|
33871
|
+
const program = new Command7().name("tolkbot").description("Localization workflow for your repo").version(version, "-v, --version", "Output the version number").helpOption("-h, --help", "Show help").helpCommand("help [command]", "Show help for a command").option("--token <token>", TOKEN_HELP).enablePositionalOptions();
|
|
33872
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
33873
|
+
const { token } = actionCommand.optsWithGlobals();
|
|
34059
33874
|
setTokenOverride(token);
|
|
34060
33875
|
resolveJsonOption(actionCommand);
|
|
34061
33876
|
beginCommand(commandPath(actionCommand), version);
|
|
@@ -34081,11 +33896,13 @@ function buildProgram() {
|
|
|
34081
33896
|
);
|
|
34082
33897
|
program.addCommand(authCommand);
|
|
34083
33898
|
program.addCommand(glossaryCommand);
|
|
33899
|
+
program.addCommand(jobsCommand);
|
|
34084
33900
|
program.addCommand(learningsCommand);
|
|
34085
33901
|
program.addCommand(telemetryCommand);
|
|
34086
33902
|
program.addCommand(translateCommand);
|
|
34087
33903
|
program.addHelpText("after", PROGRAM_HELP_FOOTER);
|
|
34088
33904
|
forEachCommand(program, (command) => {
|
|
33905
|
+
if (command !== program) addTokenOption(command);
|
|
34089
33906
|
addNoJsonOption(command);
|
|
34090
33907
|
reportUsageErrors(command);
|
|
34091
33908
|
});
|
|
@@ -34095,6 +33912,11 @@ function forEachCommand(command, visit2) {
|
|
|
34095
33912
|
visit2(command);
|
|
34096
33913
|
for (const sub of command.commands) forEachCommand(sub, visit2);
|
|
34097
33914
|
}
|
|
33915
|
+
var TOKEN_HELP = `Auth token to use for this invocation (overrides $${TOKEN_ENV_VAR} and the stored token)`;
|
|
33916
|
+
function addTokenOption(command) {
|
|
33917
|
+
if (command.options.some((o) => o.long === "--token")) return;
|
|
33918
|
+
command.addOption(new Option("--token <token>", TOKEN_HELP).hideHelp());
|
|
33919
|
+
}
|
|
34098
33920
|
function jsonOptionOf(command) {
|
|
34099
33921
|
return command.options.find((option) => option.long === "--json");
|
|
34100
33922
|
}
|
|
@@ -34133,8 +33955,9 @@ var PROGRAM_HELP_FOOTER = `
|
|
|
34133
33955
|
Examples:
|
|
34134
33956
|
$ tolkbot auth login
|
|
34135
33957
|
$ tolkbot init
|
|
34136
|
-
$ tolkbot translate
|
|
34137
|
-
$ tolkbot translate
|
|
33958
|
+
$ tolkbot translate --all
|
|
33959
|
+
$ tolkbot translate --all --dry-run
|
|
33960
|
+
$ tolkbot jobs list
|
|
34138
33961
|
$ tolkbot validate
|
|
34139
33962
|
|
|
34140
33963
|
Without a terminal (an agent, CI), nothing prompts: commands take their
|