@tolkbot/cli 0.3.0 → 0.5.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 +1820 -1757
- 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) {
|
|
@@ -559,7 +559,7 @@ var require_to_regex_range = __commonJS({
|
|
|
559
559
|
let zipped = zip(start, stop);
|
|
560
560
|
let digits = zipped.length;
|
|
561
561
|
let pattern = "";
|
|
562
|
-
let
|
|
562
|
+
let count2 = 0;
|
|
563
563
|
for (let i = 0; i < digits; i++) {
|
|
564
564
|
let [startDigit, stopDigit] = zipped[i];
|
|
565
565
|
if (startDigit === stopDigit) {
|
|
@@ -567,13 +567,13 @@ var require_to_regex_range = __commonJS({
|
|
|
567
567
|
} else if (startDigit !== "0" || stopDigit !== "9") {
|
|
568
568
|
pattern += toCharacterClass(startDigit, stopDigit, options);
|
|
569
569
|
} else {
|
|
570
|
-
|
|
570
|
+
count2++;
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
|
-
if (
|
|
573
|
+
if (count2) {
|
|
574
574
|
pattern += options.shorthand === true ? "\\d" : "[0-9]";
|
|
575
575
|
}
|
|
576
|
-
return { pattern, count: [
|
|
576
|
+
return { pattern, count: [count2], digits };
|
|
577
577
|
}
|
|
578
578
|
function splitToPatterns(min, max, tok, options) {
|
|
579
579
|
let ranges = splitToRanges(min, max);
|
|
@@ -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);
|
|
@@ -2320,13 +2320,13 @@ var require_parse2 = __commonJS({
|
|
|
2320
2320
|
consume(token.value);
|
|
2321
2321
|
};
|
|
2322
2322
|
const negate = () => {
|
|
2323
|
-
let
|
|
2323
|
+
let count2 = 1;
|
|
2324
2324
|
while (peek() === "!" && (peek(2) !== "(" || peek(3) === "?")) {
|
|
2325
2325
|
advance();
|
|
2326
2326
|
state.start++;
|
|
2327
|
-
|
|
2327
|
+
count2++;
|
|
2328
2328
|
}
|
|
2329
|
-
if (
|
|
2329
|
+
if (count2 % 2 === 0) {
|
|
2330
2330
|
return false;
|
|
2331
2331
|
}
|
|
2332
2332
|
state.negated = true;
|
|
@@ -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 Command8, CommanderError, Option } from "commander";
|
|
5788
5788
|
|
|
5789
5789
|
// package.json
|
|
5790
|
-
var version = "0.
|
|
5790
|
+
var version = "0.5.0";
|
|
5791
5791
|
|
|
5792
5792
|
// src/commands/auth/index.ts
|
|
5793
5793
|
import { Command } from "commander";
|
|
@@ -8068,14 +8068,14 @@ function codePointLength(str) {
|
|
|
8068
8068
|
const units = str.length;
|
|
8069
8069
|
if (!highSurrogate.test(str))
|
|
8070
8070
|
return units;
|
|
8071
|
-
let
|
|
8071
|
+
let count2 = units;
|
|
8072
8072
|
for (let i = 0; i < units - 1; i++) {
|
|
8073
8073
|
if ((str.charCodeAt(i) & 64512) === 55296 && (str.charCodeAt(i + 1) & 64512) === 56320) {
|
|
8074
|
-
|
|
8074
|
+
count2--;
|
|
8075
8075
|
i++;
|
|
8076
8076
|
}
|
|
8077
8077
|
}
|
|
8078
|
-
return
|
|
8078
|
+
return count2;
|
|
8079
8079
|
}
|
|
8080
8080
|
function getLengthableOrigin(input2) {
|
|
8081
8081
|
if (Array.isArray(input2))
|
|
@@ -12432,8 +12432,8 @@ function az_default() {
|
|
|
12432
12432
|
localeError: error2()
|
|
12433
12433
|
};
|
|
12434
12434
|
}
|
|
12435
|
-
function getBelarusianPlural(
|
|
12436
|
-
const absCount = Math.abs(
|
|
12435
|
+
function getBelarusianPlural(count2, one, few, many) {
|
|
12436
|
+
const absCount = Math.abs(count2);
|
|
12437
12437
|
const lastDigit = absCount % 10;
|
|
12438
12438
|
const lastTwoDigits = absCount % 100;
|
|
12439
12439
|
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
@@ -15038,8 +15038,8 @@ function hu_default() {
|
|
|
15038
15038
|
localeError: error23()
|
|
15039
15039
|
};
|
|
15040
15040
|
}
|
|
15041
|
-
function getArmenianPlural(
|
|
15042
|
-
return Math.abs(
|
|
15041
|
+
function getArmenianPlural(count2, one, many) {
|
|
15042
|
+
return Math.abs(count2) === 1 ? one : many;
|
|
15043
15043
|
}
|
|
15044
15044
|
function withDefiniteArticle(word) {
|
|
15045
15045
|
if (!word)
|
|
@@ -17731,8 +17731,8 @@ function ro_default() {
|
|
|
17731
17731
|
localeError: error45()
|
|
17732
17732
|
};
|
|
17733
17733
|
}
|
|
17734
|
-
function getRussianPlural(
|
|
17735
|
-
const absCount = Math.abs(
|
|
17734
|
+
function getRussianPlural(count2, one, few, many) {
|
|
17735
|
+
const absCount = Math.abs(count2);
|
|
17736
17736
|
const lastDigit = absCount % 10;
|
|
17737
17737
|
const lastTwoDigits = absCount % 100;
|
|
17738
17738
|
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
|
@@ -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,
|
|
@@ -27354,6 +27354,7 @@ var analyticsEventNames = [
|
|
|
27354
27354
|
"glossary_imported",
|
|
27355
27355
|
"translation_job_viewed",
|
|
27356
27356
|
"translation_logs_viewed",
|
|
27357
|
+
"activity_timeline_viewed",
|
|
27357
27358
|
"cli_login_completed",
|
|
27358
27359
|
"cli_command_completed",
|
|
27359
27360
|
"cli_init_completed",
|
|
@@ -27544,9 +27545,9 @@ function runtimeProperties(cliVersion) {
|
|
|
27544
27545
|
};
|
|
27545
27546
|
}
|
|
27546
27547
|
var activeCommand = null;
|
|
27547
|
-
function beginCommand(
|
|
27548
|
+
function beginCommand(path11, cliVersion) {
|
|
27548
27549
|
activeCommand = {
|
|
27549
|
-
path:
|
|
27550
|
+
path: path11,
|
|
27550
27551
|
startedAt: Date.now(),
|
|
27551
27552
|
cliVersion,
|
|
27552
27553
|
reported: false
|
|
@@ -28088,24 +28089,14 @@ function InitWizard({
|
|
|
28088
28089
|
".json"
|
|
28089
28090
|
] }),
|
|
28090
28091
|
/* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
28091
|
-
"3. Run: tolkbot translate
|
|
28092
|
-
" ",
|
|
28093
|
-
setupData.baseLanguage,
|
|
28094
|
-
" --dst",
|
|
28092
|
+
"3. Run: tolkbot translate --dst",
|
|
28095
28093
|
" ",
|
|
28096
28094
|
setupData.targetLanguages[0] ?? "no"
|
|
28097
28095
|
] })
|
|
28098
28096
|
] }),
|
|
28099
28097
|
!setupData.shouldInstall && /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
28100
28098
|
/* @__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
|
-
] })
|
|
28099
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "See what a run would do: tolkbot translate --all --dry-run" })
|
|
28109
28100
|
] })
|
|
28110
28101
|
] });
|
|
28111
28102
|
}
|
|
@@ -28299,380 +28290,76 @@ async function runInteractive(opts) {
|
|
|
28299
28290
|
}
|
|
28300
28291
|
}
|
|
28301
28292
|
|
|
28302
|
-
// src/commands/
|
|
28293
|
+
// src/commands/jobs/index.ts
|
|
28303
28294
|
import { Command as Command3 } from "commander";
|
|
28304
28295
|
import { z as z5 } from "zod/v4";
|
|
28305
28296
|
|
|
28306
|
-
// src/commands/
|
|
28307
|
-
import {
|
|
28297
|
+
// src/commands/jobs/apply.tsx
|
|
28298
|
+
import { useState as useState2 } from "react";
|
|
28299
|
+
import { MultiSelect } from "@inkjs/ui";
|
|
28308
28300
|
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
28301
|
|
|
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
|
|
28302
|
+
// src/commands/translate/context.ts
|
|
28303
|
+
import path5 from "path";
|
|
28304
|
+
async function loadPlanContext(cwd) {
|
|
28305
|
+
const config3 = readConfig(cwd);
|
|
28306
|
+
if (!config3) throw noConfigError();
|
|
28307
|
+
const client3 = await getAuthenticatedConvexClient();
|
|
28308
|
+
const project = await client3.query(api.projects.getProjectForCli, {
|
|
28309
|
+
projectId: config3.config.projectId
|
|
28414
28310
|
});
|
|
28311
|
+
if (!project) throw projectNotFoundError();
|
|
28312
|
+
setTelemetryOrganization(project.organizationId);
|
|
28313
|
+
return {
|
|
28314
|
+
root: path5.dirname(config3.path),
|
|
28315
|
+
project,
|
|
28316
|
+
client: client3,
|
|
28317
|
+
framework: config3.config.framework
|
|
28318
|
+
};
|
|
28415
28319
|
}
|
|
28416
|
-
async function
|
|
28417
|
-
if (opts.json) {
|
|
28418
|
-
const learnings2 = await loadLearnings(opts);
|
|
28419
|
-
jsonSuccess({
|
|
28420
|
-
learnings: learnings2.map((l) => ({
|
|
28421
|
-
id: l._id,
|
|
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;
|
|
28320
|
+
async function readCreditBalance(ctx) {
|
|
28437
28321
|
try {
|
|
28438
|
-
|
|
28439
|
-
|
|
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
|
|
28322
|
+
const balance = await ctx.client.query(api.usage.getCreditBalance, {
|
|
28323
|
+
organizationId: ctx.project.organizationId
|
|
28470
28324
|
});
|
|
28471
|
-
|
|
28472
|
-
|
|
28473
|
-
|
|
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();
|
|
28325
|
+
return balance?.total ?? null;
|
|
28326
|
+
} catch {
|
|
28327
|
+
return null;
|
|
28483
28328
|
}
|
|
28484
|
-
render13(/* @__PURE__ */ jsx13(RemoveUI2, { state: { type: "success" } })).unmount();
|
|
28485
28329
|
}
|
|
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
|
-
] });
|
|
28330
|
+
function describePlan(plan) {
|
|
28331
|
+
const { summary } = plan;
|
|
28332
|
+
const lines = [
|
|
28333
|
+
`${summary.totalKeys} keys expected, ${summary.upToDate} up to date`
|
|
28334
|
+
];
|
|
28335
|
+
if (summary.missing > 0) lines.push(`${summary.missing} missing`);
|
|
28336
|
+
if (summary.changed > 0)
|
|
28337
|
+
lines.push(`${summary.changed} changed since translated`);
|
|
28338
|
+
return [...lines, ...describeLeftOut(plan)];
|
|
28537
28339
|
}
|
|
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"
|
|
28340
|
+
function describeLeftOut(plan) {
|
|
28341
|
+
const { summary } = plan;
|
|
28342
|
+
const lines = [];
|
|
28343
|
+
const protectedCount = plan.protectedKeys.length;
|
|
28344
|
+
if (protectedCount > 0) {
|
|
28345
|
+
const reasons2 = Object.entries(summary.protected).map(([reason, n]) => `${n} ${reason}`).join(", ");
|
|
28346
|
+
lines.push(
|
|
28347
|
+
`${protectedCount} changed but kept for a person (${reasons2})`
|
|
28550
28348
|
);
|
|
28551
28349
|
}
|
|
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;
|
|
28350
|
+
if (summary.held > 0)
|
|
28351
|
+
lines.push(`${summary.held} missing but held by a person`);
|
|
28352
|
+
if (summary.untracked > 0) {
|
|
28353
|
+
lines.push(
|
|
28354
|
+
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.`
|
|
28355
|
+
);
|
|
28634
28356
|
}
|
|
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
|
-
);
|
|
28357
|
+
return lines;
|
|
28641
28358
|
}
|
|
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
28359
|
|
|
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
|
|
28360
|
+
// src/commands/translate/apply-core.ts
|
|
28674
28361
|
import fs7 from "fs/promises";
|
|
28675
|
-
import
|
|
28362
|
+
import path8 from "path";
|
|
28676
28363
|
|
|
28677
28364
|
// ../../node_modules/.pnpm/emoji-regex@10.6.0/node_modules/emoji-regex/index.mjs
|
|
28678
28365
|
var emoji_regex_default = () => {
|
|
@@ -28766,7 +28453,7 @@ function eastAsianWidth(codePoint, { ambiguousAsWide = false } = {}) {
|
|
|
28766
28453
|
// ../parser/dist/index.js
|
|
28767
28454
|
var import_fast_glob = __toESM(require_out4(), 1);
|
|
28768
28455
|
var import_fast_glob2 = __toESM(require_out4(), 1);
|
|
28769
|
-
import
|
|
28456
|
+
import path6 from "path";
|
|
28770
28457
|
import fs5 from "fs/promises";
|
|
28771
28458
|
import path23 from "path";
|
|
28772
28459
|
import fs23 from "fs/promises";
|
|
@@ -28875,11 +28562,11 @@ function flattenValues(data) {
|
|
|
28875
28562
|
const values = /* @__PURE__ */ new Map();
|
|
28876
28563
|
function walk(obj, prefix) {
|
|
28877
28564
|
for (const [key, value] of Object.entries(obj)) {
|
|
28878
|
-
const
|
|
28565
|
+
const fullKey6 = prefix ? `${prefix}.${key}` : key;
|
|
28879
28566
|
if (typeof value === "string") {
|
|
28880
|
-
values.set(
|
|
28567
|
+
values.set(fullKey6, value);
|
|
28881
28568
|
} else if (typeof value === "object" && value !== null) {
|
|
28882
|
-
walk(value,
|
|
28569
|
+
walk(value, fullKey6);
|
|
28883
28570
|
}
|
|
28884
28571
|
}
|
|
28885
28572
|
}
|
|
@@ -29917,7 +29604,7 @@ var BaseAdapter = class {
|
|
|
29917
29604
|
parseErrors: []
|
|
29918
29605
|
};
|
|
29919
29606
|
}
|
|
29920
|
-
const relativePaths = files.map((f) =>
|
|
29607
|
+
const relativePaths = files.map((f) => path6.relative(baseDir, f));
|
|
29921
29608
|
const groupedPaths = groupByLocale(
|
|
29922
29609
|
relativePaths,
|
|
29923
29610
|
options?.knownLocales
|
|
@@ -29927,7 +29614,7 @@ var BaseAdapter = class {
|
|
|
29927
29614
|
for (const [locale, localePaths] of groupedPaths) {
|
|
29928
29615
|
const translationFiles = [];
|
|
29929
29616
|
for (const relativePath of localePaths) {
|
|
29930
|
-
const absolutePath =
|
|
29617
|
+
const absolutePath = path6.join(baseDir, relativePath);
|
|
29931
29618
|
try {
|
|
29932
29619
|
const file2 = await this.parse(absolutePath);
|
|
29933
29620
|
translationFiles.push(file2);
|
|
@@ -30868,10 +30555,175 @@ function pluralFormContext(plural2, locale, sourceKey) {
|
|
|
30868
30555
|
return parts.join(" ");
|
|
30869
30556
|
}
|
|
30870
30557
|
|
|
30871
|
-
// src/commands/translate/
|
|
30558
|
+
// src/commands/translate/project-files.ts
|
|
30559
|
+
import fs6 from "fs/promises";
|
|
30560
|
+
import path7 from "path";
|
|
30561
|
+
function fullPattern(scope) {
|
|
30562
|
+
return scope.subpath ? path7.posix.join(scope.subpath, scope.sourcePattern) : scope.sourcePattern;
|
|
30563
|
+
}
|
|
30564
|
+
function formatOf(scope) {
|
|
30565
|
+
return detectFormat(scope.sourcePattern) ?? "json";
|
|
30566
|
+
}
|
|
30567
|
+
async function listLocaleFiles(root, scope) {
|
|
30568
|
+
const pattern = fullPattern(scope);
|
|
30569
|
+
const adapter = createAdapter(formatOf(scope));
|
|
30570
|
+
const match = createPathMatcher(pattern);
|
|
30571
|
+
const found = await adapter.findFiles(root, withNamespaceWildcard(pattern));
|
|
30572
|
+
const files = [];
|
|
30573
|
+
for (const absolutePath of found.sort()) {
|
|
30574
|
+
const relativePath = path7.relative(root, absolutePath).split(path7.sep).join("/");
|
|
30575
|
+
const m = match(relativePath);
|
|
30576
|
+
if (!m) continue;
|
|
30577
|
+
files.push({
|
|
30578
|
+
pathTemplate: replaceNamespacePlaceholder(
|
|
30579
|
+
scope.sourcePattern,
|
|
30580
|
+
m.namespace ?? ""
|
|
30581
|
+
),
|
|
30582
|
+
namespace: m.namespace,
|
|
30583
|
+
locale: m.locale,
|
|
30584
|
+
relativePath,
|
|
30585
|
+
absolutePath
|
|
30586
|
+
});
|
|
30587
|
+
}
|
|
30588
|
+
return files;
|
|
30589
|
+
}
|
|
30590
|
+
function filesForLocale(files, locale) {
|
|
30591
|
+
return files.filter((file2) => sameLocale(file2.locale, locale));
|
|
30592
|
+
}
|
|
30593
|
+
function targetFileFor(source, targetLocale, files, scope) {
|
|
30594
|
+
const existing = files.find(
|
|
30595
|
+
(file2) => file2.pathTemplate === source.pathTemplate && sameLocale(file2.locale, targetLocale)
|
|
30596
|
+
) ?? null;
|
|
30597
|
+
if (existing) return { relativePath: existing.relativePath, existing };
|
|
30598
|
+
const spelling = files.find((file2) => sameLocale(file2.locale, targetLocale))?.locale ?? scope.detectedLocales?.find((l) => sameLocale(l, targetLocale)) ?? targetLocale;
|
|
30599
|
+
const relativePath = replaceLocalePlaceholder(
|
|
30600
|
+
replaceNamespacePlaceholder(fullPattern(scope), source.namespace ?? ""),
|
|
30601
|
+
spelling
|
|
30602
|
+
);
|
|
30603
|
+
return { relativePath, existing: null };
|
|
30604
|
+
}
|
|
30605
|
+
async function readTranslationFile(absolutePath, format) {
|
|
30606
|
+
try {
|
|
30607
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
30608
|
+
const file2 = await createAdapter(format).parse(absolutePath);
|
|
30609
|
+
return { data: file2.data, content };
|
|
30610
|
+
} catch (error62) {
|
|
30611
|
+
return {
|
|
30612
|
+
error: error62 instanceof Error ? error62.message : String(error62)
|
|
30613
|
+
};
|
|
30614
|
+
}
|
|
30615
|
+
}
|
|
30616
|
+
|
|
30617
|
+
// src/commands/translate/apply-core.ts
|
|
30872
30618
|
function fullKey(namespace, key) {
|
|
30873
30619
|
return namespace ? `${namespace}.${key}` : key;
|
|
30874
30620
|
}
|
|
30621
|
+
async function applyTasks(input2) {
|
|
30622
|
+
const { root, scope, dstLocale } = input2;
|
|
30623
|
+
const format = formatOf(scope);
|
|
30624
|
+
const files = await listLocaleFiles(root, scope);
|
|
30625
|
+
const result = { written: [], refused: [] };
|
|
30626
|
+
const refuse = (task, reason) => result.refused.push({ key: fullKey(task.namespace, task.key), reason });
|
|
30627
|
+
const bySourcePath = /* @__PURE__ */ new Map();
|
|
30628
|
+
for (const task of input2.tasks) {
|
|
30629
|
+
if (task.status !== "succeeded" || !task.dst || !task.path) {
|
|
30630
|
+
refuse(task, "not_translated");
|
|
30631
|
+
continue;
|
|
30632
|
+
}
|
|
30633
|
+
const group = bySourcePath.get(task.path) ?? [];
|
|
30634
|
+
group.push(task);
|
|
30635
|
+
bySourcePath.set(task.path, group);
|
|
30636
|
+
}
|
|
30637
|
+
for (const [sourcePath, tasks] of bySourcePath) {
|
|
30638
|
+
const sourceFile = files.find(
|
|
30639
|
+
(file2) => file2.relativePath === sourcePath
|
|
30640
|
+
);
|
|
30641
|
+
const source = sourceFile ? await readTranslationFile(sourceFile.absolutePath, format) : void 0;
|
|
30642
|
+
if (!sourceFile || !source || source.error !== void 0) {
|
|
30643
|
+
for (const task of tasks) refuse(task, "no_source_file");
|
|
30644
|
+
continue;
|
|
30645
|
+
}
|
|
30646
|
+
const baseKeys = new Set(getKeys(source.data));
|
|
30647
|
+
const target = targetFileFor(sourceFile, dstLocale, files, scope);
|
|
30648
|
+
const targetPath = path8.join(root, ...target.relativePath.split("/"));
|
|
30649
|
+
const existing = target.existing ? await readTranslationFile(target.existing.absolutePath, format) : void 0;
|
|
30650
|
+
if (existing?.error !== void 0) {
|
|
30651
|
+
for (const task of tasks) refuse(task, "target_unreadable");
|
|
30652
|
+
continue;
|
|
30653
|
+
}
|
|
30654
|
+
let data = existing?.data ?? {};
|
|
30655
|
+
const written = {
|
|
30656
|
+
pathTemplate: sourceFile.pathTemplate,
|
|
30657
|
+
relativePath: target.relativePath,
|
|
30658
|
+
entries: []
|
|
30659
|
+
};
|
|
30660
|
+
for (const task of tasks) {
|
|
30661
|
+
const sourceKey = sourceKeyFor(task.key, baseKeys);
|
|
30662
|
+
const currentSource = sourceKey === null ? void 0 : getByPath(source.data, sourceKey);
|
|
30663
|
+
if (typeof currentSource !== "string" || computeValueHash(currentSource) !== computeValueHash(task.src)) {
|
|
30664
|
+
refuse(task, "source_changed");
|
|
30665
|
+
continue;
|
|
30666
|
+
}
|
|
30667
|
+
const translatedValue = task.dst ?? "";
|
|
30668
|
+
const current = getByPath(data, task.key);
|
|
30669
|
+
const currentHash = typeof current === "string" ? computeValueHash(current) : null;
|
|
30670
|
+
if (task.expectedTargetHash !== void 0 && task.expectedTargetHash !== currentHash) {
|
|
30671
|
+
refuse(
|
|
30672
|
+
task,
|
|
30673
|
+
current === translatedValue ? "already_applied" : "target_changed"
|
|
30674
|
+
);
|
|
30675
|
+
continue;
|
|
30676
|
+
}
|
|
30677
|
+
data = setByPath(data, task.key, translatedValue);
|
|
30678
|
+
written.entries.push({
|
|
30679
|
+
key: task.key,
|
|
30680
|
+
namespace: task.namespace,
|
|
30681
|
+
baseValue: task.src,
|
|
30682
|
+
translatedValue,
|
|
30683
|
+
changesFile: current !== translatedValue
|
|
30684
|
+
});
|
|
30685
|
+
}
|
|
30686
|
+
if (written.entries.length === 0) continue;
|
|
30687
|
+
result.written.push(written);
|
|
30688
|
+
if (input2.dryRun) continue;
|
|
30689
|
+
await writeTranslationFile({
|
|
30690
|
+
format,
|
|
30691
|
+
targetPath,
|
|
30692
|
+
data,
|
|
30693
|
+
dstLocale,
|
|
30694
|
+
existingContent: existing?.content,
|
|
30695
|
+
sourceContent: source.content
|
|
30696
|
+
});
|
|
30697
|
+
}
|
|
30698
|
+
return result;
|
|
30699
|
+
}
|
|
30700
|
+
async function writeTranslationFile(input2) {
|
|
30701
|
+
const { targetPath, data } = input2;
|
|
30702
|
+
const model = input2.existingContent ?? input2.sourceContent;
|
|
30703
|
+
await fs7.mkdir(path8.dirname(targetPath), { recursive: true });
|
|
30704
|
+
if (input2.format === "json") {
|
|
30705
|
+
await fs7.writeFile(
|
|
30706
|
+
targetPath,
|
|
30707
|
+
serializeJsonContent(data, detectJsonStyle(model)),
|
|
30708
|
+
"utf-8"
|
|
30709
|
+
);
|
|
30710
|
+
return;
|
|
30711
|
+
}
|
|
30712
|
+
const style = detectTsStyle(model);
|
|
30713
|
+
const shape = detectTsExportFormat(model);
|
|
30714
|
+
const content = serializeTsContent(data, {
|
|
30715
|
+
...style,
|
|
30716
|
+
printWidth: style.printWidth ?? void 0,
|
|
30717
|
+
exportName: input2.existingContent ? shape?.exportName ?? void 0 : localeExportIdentifier(input2.dstLocale),
|
|
30718
|
+
useDefaultExport: shape?.useDefaultExport ?? false
|
|
30719
|
+
});
|
|
30720
|
+
await fs7.writeFile(targetPath, content, "utf-8");
|
|
30721
|
+
}
|
|
30722
|
+
|
|
30723
|
+
// src/commands/translate/plan-core.ts
|
|
30724
|
+
function fullKey2(namespace, key) {
|
|
30725
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
30726
|
+
}
|
|
30875
30727
|
function expectedFullKeys(sources, targetLocale) {
|
|
30876
30728
|
const keys = [];
|
|
30877
30729
|
for (const { file: file2, data } of sources) {
|
|
@@ -30879,7 +30731,7 @@ function expectedFullKeys(sources, targetLocale) {
|
|
|
30879
30731
|
getKeys(data),
|
|
30880
30732
|
targetLocale
|
|
30881
30733
|
).keys())
|
|
30882
|
-
keys.push(
|
|
30734
|
+
keys.push(fullKey2(file2.namespace, key));
|
|
30883
30735
|
}
|
|
30884
30736
|
return keys;
|
|
30885
30737
|
}
|
|
@@ -30927,7 +30779,7 @@ function planLocale(input2) {
|
|
|
30927
30779
|
const src = getByPath(data, sourceKey);
|
|
30928
30780
|
if (typeof src !== "string") continue;
|
|
30929
30781
|
plan.summary.totalKeys++;
|
|
30930
|
-
const full =
|
|
30782
|
+
const full = fullKey2(file2.namespace, key);
|
|
30931
30783
|
const hold = input2.holds.get(full);
|
|
30932
30784
|
const current = getByPath(targetData, key);
|
|
30933
30785
|
const targetValue = typeof current === "string" ? current : void 0;
|
|
@@ -30998,74 +30850,8 @@ function planLocale(input2) {
|
|
|
30998
30850
|
return plan;
|
|
30999
30851
|
}
|
|
31000
30852
|
|
|
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
30853
|
// src/commands/translate/task-generator.ts
|
|
31061
30854
|
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
30855
|
function scopeOf(project) {
|
|
31070
30856
|
return {
|
|
31071
30857
|
sourcePattern: project.sourcePattern,
|
|
@@ -31150,364 +30936,16 @@ async function generateTasks(ctx, srcLocale, dstLocale, opts = {}) {
|
|
|
31150
30936
|
};
|
|
31151
30937
|
}
|
|
31152
30938
|
|
|
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
|
|
30939
|
+
// src/commands/jobs/apply-job.ts
|
|
31317
30940
|
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
30941
|
var RECENT_JOBS = 50;
|
|
31510
30942
|
var APPLICABLE_STATUSES = /* @__PURE__ */ new Set(["succeeded", "partial", "failed"]);
|
|
30943
|
+
function isApplicable(status) {
|
|
30944
|
+
return APPLICABLE_STATUSES.has(status);
|
|
30945
|
+
}
|
|
30946
|
+
function isForWorkingTree(job) {
|
|
30947
|
+
return job.source === void 0 || job.source === "cli";
|
|
30948
|
+
}
|
|
31511
30949
|
function fullKey3(namespace, key) {
|
|
31512
30950
|
return namespace ? `${namespace}.${key}` : key;
|
|
31513
30951
|
}
|
|
@@ -31518,17 +30956,17 @@ async function readJob(ctx, jobId) {
|
|
|
31518
30956
|
organizationId: organizationId2,
|
|
31519
30957
|
jobId
|
|
31520
30958
|
});
|
|
31521
|
-
if (
|
|
30959
|
+
if (job?.projectId !== project._id) {
|
|
31522
30960
|
throw new CliError(
|
|
31523
30961
|
`Translation job ${jobId} was not found in this project.`,
|
|
31524
|
-
|
|
30962
|
+
"List the project's jobs with 'tolkbot jobs list'.",
|
|
31525
30963
|
"job_not_found"
|
|
31526
30964
|
);
|
|
31527
30965
|
}
|
|
31528
|
-
if (!
|
|
30966
|
+
if (!isApplicable(job.status)) {
|
|
31529
30967
|
throw new CliError(
|
|
31530
30968
|
`Translation job ${jobId} is ${job.status}. Only a finished job can be applied.`,
|
|
31531
|
-
`Check it with 'tolkbot
|
|
30969
|
+
`Check it with 'tolkbot jobs show ${jobId}'.`,
|
|
31532
30970
|
"job_not_finished"
|
|
31533
30971
|
);
|
|
31534
30972
|
}
|
|
@@ -31558,13 +30996,13 @@ async function applyJob(ctx, jobId) {
|
|
|
31558
30996
|
translatedValue
|
|
31559
30997
|
})
|
|
31560
30998
|
);
|
|
31561
|
-
for (let i = 0; i < entries.length; i +=
|
|
30999
|
+
for (let i = 0; i < entries.length; i += STAMP_CHUNK) {
|
|
31562
31000
|
await client3.mutation(api.localeKeyStates.stampAppliedForCli, {
|
|
31563
31001
|
organizationId: project.organizationId,
|
|
31564
31002
|
projectId: project._id,
|
|
31565
31003
|
locale,
|
|
31566
31004
|
pathTemplate: file2.pathTemplate,
|
|
31567
|
-
entries: entries.slice(i, i +
|
|
31005
|
+
entries: entries.slice(i, i + STAMP_CHUNK)
|
|
31568
31006
|
});
|
|
31569
31007
|
}
|
|
31570
31008
|
}
|
|
@@ -31572,7 +31010,7 @@ async function applyJob(ctx, jobId) {
|
|
|
31572
31010
|
dstLocale: job.dstLocale,
|
|
31573
31011
|
files: result.written.length,
|
|
31574
31012
|
keys: result.written.reduce((n, f) => n + f.entries.length, 0),
|
|
31575
|
-
|
|
31013
|
+
notApplied: result.refused.filter(
|
|
31576
31014
|
(r) => r.reason !== "not_translated" && r.reason !== "already_applied"
|
|
31577
31015
|
)
|
|
31578
31016
|
};
|
|
@@ -31585,18 +31023,13 @@ var REFUSAL_TEXT = {
|
|
|
31585
31023
|
target_unreadable: "the target file cannot be read",
|
|
31586
31024
|
target_changed: "the target was edited after it was planned"
|
|
31587
31025
|
};
|
|
31588
|
-
function
|
|
31589
|
-
return
|
|
31026
|
+
function describeNotApplied(notApplied) {
|
|
31027
|
+
return notApplied.map(
|
|
31590
31028
|
(r) => ` ${r.key}: not applied, ${REFUSAL_TEXT[r.reason]}`
|
|
31591
31029
|
);
|
|
31592
31030
|
}
|
|
31593
|
-
|
|
31031
|
+
function pendingCounter(ctx) {
|
|
31594
31032
|
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
31033
|
const wanted = /* @__PURE__ */ new Map();
|
|
31601
31034
|
const plannedKeys = (srcLocale, dstLocale) => {
|
|
31602
31035
|
const id = `${srcLocale}\0${dstLocale}`;
|
|
@@ -31613,9 +31046,7 @@ async function findUnappliedJobs(ctx) {
|
|
|
31613
31046
|
}
|
|
31614
31047
|
return keys;
|
|
31615
31048
|
};
|
|
31616
|
-
|
|
31617
|
-
for (const job of jobs) {
|
|
31618
|
-
if (!APPLICABLE_STATUSES.has(job.status)) continue;
|
|
31049
|
+
return async (job) => {
|
|
31619
31050
|
const tasks = await client3.query(api.translations.listTasksByJobId, {
|
|
31620
31051
|
organizationId: project.organizationId,
|
|
31621
31052
|
jobId: job._id
|
|
@@ -31638,6 +31069,23 @@ async function findUnappliedJobs(ctx) {
|
|
|
31638
31069
|
}
|
|
31639
31070
|
}
|
|
31640
31071
|
}
|
|
31072
|
+
return pending2;
|
|
31073
|
+
};
|
|
31074
|
+
}
|
|
31075
|
+
async function listRecentJobs(ctx, limit) {
|
|
31076
|
+
return await ctx.client.query(api.translations.listJobsByProject, {
|
|
31077
|
+
organizationId: ctx.project.organizationId,
|
|
31078
|
+
projectId: ctx.project._id,
|
|
31079
|
+
limit
|
|
31080
|
+
});
|
|
31081
|
+
}
|
|
31082
|
+
async function findUnappliedJobs(ctx) {
|
|
31083
|
+
const jobs = await listRecentJobs(ctx, RECENT_JOBS);
|
|
31084
|
+
const pendingOf = pendingCounter(ctx);
|
|
31085
|
+
const unapplied = [];
|
|
31086
|
+
for (const job of jobs) {
|
|
31087
|
+
if (!isApplicable(job.status) || !isForWorkingTree(job)) continue;
|
|
31088
|
+
const pending2 = await pendingOf(job);
|
|
31641
31089
|
if (pending2 === 0) continue;
|
|
31642
31090
|
unapplied.push({
|
|
31643
31091
|
jobId: job._id,
|
|
@@ -31664,13 +31112,16 @@ function chooseJobs(unapplied) {
|
|
|
31664
31112
|
jobs: [...unapplied].sort((a, b) => a.createdAt - b.createdAt)
|
|
31665
31113
|
};
|
|
31666
31114
|
}
|
|
31115
|
+
|
|
31116
|
+
// src/commands/jobs/apply.tsx
|
|
31117
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
31667
31118
|
function describeJob(job) {
|
|
31668
31119
|
return `${job.jobId} (${job.srcLocale} \u2192 ${job.dstLocale}, ${job.pending} key(s), ${new Date(job.createdAt).toLocaleString()})`;
|
|
31669
31120
|
}
|
|
31670
31121
|
function nothingToApply() {
|
|
31671
31122
|
return new CliError(
|
|
31672
31123
|
"No finished translation job is waiting to be applied.",
|
|
31673
|
-
"Run 'tolkbot translate
|
|
31124
|
+
"Run 'tolkbot translate --all' to translate what is missing, or pass a job id to apply a job again.",
|
|
31674
31125
|
"nothing_to_apply"
|
|
31675
31126
|
);
|
|
31676
31127
|
}
|
|
@@ -31681,7 +31132,7 @@ function ambiguousJobs(unapplied, locales) {
|
|
|
31681
31132
|
);
|
|
31682
31133
|
return new UsageError(
|
|
31683
31134
|
`More than one translation job is waiting to be applied for ${locales.join(", ")}: ${waiting.map(describeJob).join("; ")}.`,
|
|
31684
|
-
`Pass
|
|
31135
|
+
`Pass the job id(s) to apply. The newest is usually the one: 'tolkbot jobs apply ${newest.map((job) => job.jobId).join(" ")}'.`
|
|
31685
31136
|
);
|
|
31686
31137
|
}
|
|
31687
31138
|
function JobPicker({
|
|
@@ -31689,10 +31140,10 @@ function JobPicker({
|
|
|
31689
31140
|
onSubmit
|
|
31690
31141
|
}) {
|
|
31691
31142
|
const [selected, setSelected] = useState2([]);
|
|
31692
|
-
return /* @__PURE__ */
|
|
31693
|
-
/* @__PURE__ */
|
|
31694
|
-
/* @__PURE__ */
|
|
31695
|
-
/* @__PURE__ */
|
|
31143
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
31144
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, children: "Select translation jobs to apply:" }),
|
|
31145
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "Space to select, Enter to confirm, Ctrl+C to cancel" }),
|
|
31146
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(
|
|
31696
31147
|
MultiSelect,
|
|
31697
31148
|
{
|
|
31698
31149
|
options: jobs.map((job) => ({
|
|
@@ -31707,8 +31158,8 @@ function JobPicker({
|
|
|
31707
31158
|
}
|
|
31708
31159
|
async function pickJobs(jobs) {
|
|
31709
31160
|
let chosen = [];
|
|
31710
|
-
const ui =
|
|
31711
|
-
/* @__PURE__ */
|
|
31161
|
+
const ui = render11(
|
|
31162
|
+
/* @__PURE__ */ jsx11(
|
|
31712
31163
|
JobPicker,
|
|
31713
31164
|
{
|
|
31714
31165
|
jobs,
|
|
@@ -31734,15 +31185,15 @@ async function resolveJobIds(ctx, json2) {
|
|
|
31734
31185
|
if (picked.length === 0) {
|
|
31735
31186
|
throw new UsageError(
|
|
31736
31187
|
"No job selected.",
|
|
31737
|
-
"Pass
|
|
31188
|
+
"Pass the job id(s) to apply: 'tolkbot jobs apply <id\u2026>'."
|
|
31738
31189
|
);
|
|
31739
31190
|
}
|
|
31740
31191
|
return picked;
|
|
31741
31192
|
}
|
|
31742
|
-
async function
|
|
31193
|
+
async function runJobsApply(opts) {
|
|
31743
31194
|
const ctx = await loadPlanContext(process.cwd());
|
|
31744
31195
|
const json2 = opts.json ?? false;
|
|
31745
|
-
const jobIds = opts.
|
|
31196
|
+
const jobIds = opts.jobIds.length > 0 ? opts.jobIds : await resolveJobIds(ctx, json2);
|
|
31746
31197
|
if (json2) {
|
|
31747
31198
|
const applied = [];
|
|
31748
31199
|
for (const jobId of jobIds) {
|
|
@@ -31760,16 +31211,19 @@ async function runApply(opts) {
|
|
|
31760
31211
|
console.log(`Applying ${jobIds.length} job(s)...`);
|
|
31761
31212
|
let totalFiles = 0;
|
|
31762
31213
|
let totalKeys = 0;
|
|
31763
|
-
let
|
|
31214
|
+
let totalNotApplied = 0;
|
|
31764
31215
|
const locales = /* @__PURE__ */ new Set();
|
|
31765
31216
|
for (const [i, jobId] of jobIds.entries()) {
|
|
31766
31217
|
console.log(` [${i + 1}/${jobIds.length}] ${jobId}`);
|
|
31767
|
-
const { dstLocale, files, keys,
|
|
31218
|
+
const { dstLocale, files, keys, notApplied } = await applyJob(
|
|
31219
|
+
ctx,
|
|
31220
|
+
jobId
|
|
31221
|
+
);
|
|
31768
31222
|
locales.add(dstLocale);
|
|
31769
31223
|
totalFiles += files;
|
|
31770
31224
|
totalKeys += keys;
|
|
31771
|
-
|
|
31772
|
-
for (const line of
|
|
31225
|
+
totalNotApplied += notApplied.length;
|
|
31226
|
+
for (const line of describeNotApplied(notApplied)) console.log(line);
|
|
31773
31227
|
}
|
|
31774
31228
|
track("cli_translate_completed", {
|
|
31775
31229
|
mode: "apply",
|
|
@@ -31779,187 +31233,89 @@ async function runApply(opts) {
|
|
|
31779
31233
|
json: false
|
|
31780
31234
|
});
|
|
31781
31235
|
console.log(
|
|
31782
|
-
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${jobIds.length} job(s)` + (
|
|
31236
|
+
`\u2713 Applied ${totalKeys} keys to ${totalFiles} files from ${jobIds.length} job(s)` + (totalNotApplied > 0 ? `, ${totalNotApplied} not applied` : "")
|
|
31783
31237
|
);
|
|
31784
31238
|
}
|
|
31785
31239
|
|
|
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) {
|
|
31240
|
+
// src/commands/jobs/cancel.ts
|
|
31241
|
+
async function runJobsCancel(opts) {
|
|
31809
31242
|
const { project } = await loadPlanContext(process.cwd());
|
|
31810
31243
|
const client3 = await getAuthenticatedConvexClient();
|
|
31811
31244
|
await client3.action(api.translations.cancelJob, {
|
|
31812
31245
|
organizationId: project.organizationId,
|
|
31813
|
-
jobId
|
|
31246
|
+
jobId: opts.jobId
|
|
31814
31247
|
});
|
|
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();
|
|
31832
|
-
}
|
|
31833
|
-
|
|
31834
|
-
// src/commands/translate/edit.tsx
|
|
31835
|
-
import path11 from "path";
|
|
31836
|
-
import { Spinner as Spinner15 } from "@inkjs/ui";
|
|
31837
|
-
import { Box as Box18, render as render18, Text as Text18 } from "ink";
|
|
31838
|
-
|
|
31839
|
-
// src/utils/locale-files.ts
|
|
31840
|
-
import path10 from "path";
|
|
31841
|
-
async function findLocaleFiles(baseDir) {
|
|
31842
|
-
const adapter = createAdapter("json");
|
|
31843
|
-
const result = await adapter.discover(baseDir);
|
|
31844
|
-
const localeFiles = /* @__PURE__ */ new Map();
|
|
31845
|
-
for (const [locale, files] of result.filesByLocale) {
|
|
31846
|
-
localeFiles.set(
|
|
31847
|
-
locale,
|
|
31848
|
-
files.map((f) => f.path)
|
|
31849
|
-
);
|
|
31850
|
-
}
|
|
31851
|
-
return localeFiles;
|
|
31852
|
-
}
|
|
31853
|
-
function resolveFileForKey(files, key, namespace) {
|
|
31854
|
-
if (files.length === 0) return null;
|
|
31855
|
-
if (files.length === 1) return files[0] ?? null;
|
|
31856
|
-
const ns = namespace ?? key.split(".")[0];
|
|
31857
|
-
for (const file2 of files) {
|
|
31858
|
-
const fileName = path10.parse(path10.basename(file2)).name;
|
|
31859
|
-
if (fileName === ns) {
|
|
31860
|
-
return file2;
|
|
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;
|
|
31248
|
+
if (opts.json) jsonSuccess({ jobId: opts.jobId, ok: true });
|
|
31249
|
+
console.log(`\u2713 Cancelled job ${opts.jobId}`);
|
|
31872
31250
|
}
|
|
31873
31251
|
|
|
31874
|
-
// src/commands/
|
|
31875
|
-
|
|
31876
|
-
|
|
31877
|
-
|
|
31878
|
-
|
|
31879
|
-
|
|
31880
|
-
|
|
31881
|
-
|
|
31252
|
+
// src/commands/jobs/list.ts
|
|
31253
|
+
async function describeJobs(ctx, jobs) {
|
|
31254
|
+
const pendingOf = pendingCounter(ctx);
|
|
31255
|
+
const listed = [];
|
|
31256
|
+
for (const job of jobs) {
|
|
31257
|
+
let applied = "none";
|
|
31258
|
+
let pending2 = 0;
|
|
31259
|
+
if (!isForWorkingTree(job)) {
|
|
31260
|
+
} else if (job.status === "pending" || job.status === "running") {
|
|
31261
|
+
applied = "running";
|
|
31262
|
+
} else if (isApplicable(job.status)) {
|
|
31263
|
+
pending2 = await pendingOf(job);
|
|
31264
|
+
applied = pending2 > 0 ? "waiting" : "applied";
|
|
31265
|
+
}
|
|
31266
|
+
listed.push({
|
|
31267
|
+
jobId: job._id,
|
|
31268
|
+
srcLocale: job.srcLocale,
|
|
31269
|
+
dstLocale: job.dstLocale,
|
|
31270
|
+
status: job.status,
|
|
31271
|
+
source: job.source ?? null,
|
|
31272
|
+
createdAt: new Date(job._creationTime).toISOString(),
|
|
31273
|
+
total: job.total,
|
|
31274
|
+
completed: job.completed,
|
|
31275
|
+
failed: job.failed,
|
|
31276
|
+
creditsUsed: job.creditsUsed,
|
|
31277
|
+
applied,
|
|
31278
|
+
pending: pending2
|
|
31279
|
+
});
|
|
31882
31280
|
}
|
|
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
|
-
] });
|
|
31281
|
+
return listed;
|
|
31898
31282
|
}
|
|
31899
|
-
|
|
31900
|
-
|
|
31901
|
-
|
|
31902
|
-
|
|
31903
|
-
|
|
31904
|
-
|
|
31905
|
-
|
|
31906
|
-
|
|
31907
|
-
|
|
31908
|
-
|
|
31909
|
-
|
|
31910
|
-
|
|
31911
|
-
|
|
31912
|
-
|
|
31913
|
-
|
|
31914
|
-
|
|
31915
|
-
|
|
31916
|
-
|
|
31283
|
+
var APPLIED_TEXT = {
|
|
31284
|
+
applied: "applied",
|
|
31285
|
+
waiting: "waiting to apply",
|
|
31286
|
+
running: "",
|
|
31287
|
+
none: ""
|
|
31288
|
+
};
|
|
31289
|
+
function formatRow(job) {
|
|
31290
|
+
const when = job.createdAt.slice(0, 16).replace("T", " ");
|
|
31291
|
+
const progress = `${job.completed}/${job.total}`;
|
|
31292
|
+
const applied = job.applied === "waiting" ? `${APPLIED_TEXT.waiting} (${job.pending})` : APPLIED_TEXT[job.applied];
|
|
31293
|
+
return [
|
|
31294
|
+
job.jobId,
|
|
31295
|
+
when,
|
|
31296
|
+
`${job.srcLocale} \u2192 ${job.dstLocale}`.padEnd(14),
|
|
31297
|
+
job.status.padEnd(9),
|
|
31298
|
+
progress.padStart(9),
|
|
31299
|
+
(job.source ?? "cli").padEnd(9),
|
|
31300
|
+
applied
|
|
31301
|
+
].join(" ").trimEnd();
|
|
31302
|
+
}
|
|
31303
|
+
async function runJobsList(opts) {
|
|
31304
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
31305
|
+
const jobs = await describeJobs(ctx, await listRecentJobs(ctx, opts.limit));
|
|
31306
|
+
if (opts.json) jsonSuccess({ jobs });
|
|
31307
|
+
if (jobs.length === 0) {
|
|
31308
|
+
console.log(
|
|
31309
|
+
"No translation jobs yet. Run 'tolkbot translate --all' to start one."
|
|
31917
31310
|
);
|
|
31311
|
+
return;
|
|
31918
31312
|
}
|
|
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();
|
|
31313
|
+
for (const job of jobs) console.log(formatRow(job));
|
|
31314
|
+
if (jobs.some((job) => job.applied === "waiting")) {
|
|
31315
|
+
console.log("\nApply the waiting ones with 'tolkbot jobs apply'.");
|
|
31956
31316
|
}
|
|
31957
|
-
render18(/* @__PURE__ */ jsx18(EditUI, { state: { type: "success", ...result } })).unmount();
|
|
31958
31317
|
}
|
|
31959
31318
|
|
|
31960
|
-
// src/commands/translate/run.tsx
|
|
31961
|
-
import { render as render19 } from "ink";
|
|
31962
|
-
|
|
31963
31319
|
// ../shared/src/conditions/index.ts
|
|
31964
31320
|
var BILLING_BLOCKED = "BILLING_BLOCKED";
|
|
31965
31321
|
|
|
@@ -32061,10 +31417,10 @@ function serverMessage(message) {
|
|
|
32061
31417
|
}
|
|
32062
31418
|
function argumentError(message) {
|
|
32063
31419
|
if (!message.includes("ArgumentValidationError")) return null;
|
|
32064
|
-
const
|
|
31420
|
+
const path11 = /Path: \.?(\S+)/.exec(message)?.[1];
|
|
32065
31421
|
const value = /Value: (.+)/.exec(message)?.[1];
|
|
32066
31422
|
return new UsageError(
|
|
32067
|
-
|
|
31423
|
+
path11 && value ? `TolkBot refused ${value} as ${path11}.` : "TolkBot refused the arguments.",
|
|
32068
31424
|
"Check the id or value passed; list ids with the command's list subcommand."
|
|
32069
31425
|
);
|
|
32070
31426
|
}
|
|
@@ -32082,687 +31438,1413 @@ function toCliError(error62) {
|
|
|
32082
31438
|
"not_authenticated"
|
|
32083
31439
|
);
|
|
32084
31440
|
}
|
|
32085
|
-
if (isNetworkError(error62)) {
|
|
32086
|
-
return new CliError(
|
|
32087
|
-
"Could not reach TolkBot.",
|
|
32088
|
-
"Check the network connection and try again.",
|
|
32089
|
-
"network"
|
|
31441
|
+
if (isNetworkError(error62)) {
|
|
31442
|
+
return new CliError(
|
|
31443
|
+
"Could not reach TolkBot.",
|
|
31444
|
+
"Check the network connection and try again.",
|
|
31445
|
+
"network"
|
|
31446
|
+
);
|
|
31447
|
+
}
|
|
31448
|
+
const message = describeError(error62);
|
|
31449
|
+
const refused = argumentError(message);
|
|
31450
|
+
if (refused) return refused;
|
|
31451
|
+
if (message.includes("Server Error")) {
|
|
31452
|
+
return new CliError(serverMessage(message));
|
|
31453
|
+
}
|
|
31454
|
+
const internal = new CliError(message, void 0, "internal");
|
|
31455
|
+
if (error62 instanceof Error) internal.stack = error62.stack;
|
|
31456
|
+
return internal;
|
|
31457
|
+
}
|
|
31458
|
+
function errorPayload(error62) {
|
|
31459
|
+
return {
|
|
31460
|
+
error: {
|
|
31461
|
+
code: error62.code,
|
|
31462
|
+
message: error62.baseMessage,
|
|
31463
|
+
hint: error62.hint ?? null
|
|
31464
|
+
}
|
|
31465
|
+
};
|
|
31466
|
+
}
|
|
31467
|
+
function reportError(error62) {
|
|
31468
|
+
const cli = toCliError(error62);
|
|
31469
|
+
if (wantsJson()) {
|
|
31470
|
+
console.error(JSON.stringify(errorPayload(cli), null, 2));
|
|
31471
|
+
} else {
|
|
31472
|
+
console.error(`\u2717 ${cli.baseMessage}`);
|
|
31473
|
+
if (cli.hint) console.error(` ${cli.hint}`);
|
|
31474
|
+
if (cli.code === "internal" && cli.stack) {
|
|
31475
|
+
console.error("");
|
|
31476
|
+
console.error(cli.stack);
|
|
31477
|
+
}
|
|
31478
|
+
}
|
|
31479
|
+
const code = exitCodeFor(cli.code);
|
|
31480
|
+
process.exitCode = code;
|
|
31481
|
+
return code;
|
|
31482
|
+
}
|
|
31483
|
+
function fail(error62) {
|
|
31484
|
+
const cli = toCliError(error62);
|
|
31485
|
+
throw requestExit(exitCodeFor(cli.code), () => {
|
|
31486
|
+
reportError(cli);
|
|
31487
|
+
});
|
|
31488
|
+
}
|
|
31489
|
+
|
|
31490
|
+
// src/commands/translate/translate-job.ts
|
|
31491
|
+
var FINISHED_STATUSES = /* @__PURE__ */ new Set([
|
|
31492
|
+
"succeeded",
|
|
31493
|
+
"partial",
|
|
31494
|
+
"failed",
|
|
31495
|
+
"cancelled"
|
|
31496
|
+
]);
|
|
31497
|
+
function isJobFinished(status) {
|
|
31498
|
+
return FINISHED_STATUSES.has(status);
|
|
31499
|
+
}
|
|
31500
|
+
function unsuccessfulJobError(jobId, status) {
|
|
31501
|
+
if (status === "cancelled") {
|
|
31502
|
+
return new CliError(
|
|
31503
|
+
`Translation job ${jobId} was cancelled.`,
|
|
31504
|
+
void 0,
|
|
31505
|
+
"job_cancelled"
|
|
31506
|
+
);
|
|
31507
|
+
}
|
|
31508
|
+
if (status === "failed") {
|
|
31509
|
+
return new CliError(
|
|
31510
|
+
`Translation job ${jobId} failed.`,
|
|
31511
|
+
`See it at ${env.TOLKBOT_WEB_URL}/translation-jobs/${jobId}.`,
|
|
31512
|
+
"job_failed"
|
|
31513
|
+
);
|
|
31514
|
+
}
|
|
31515
|
+
return null;
|
|
31516
|
+
}
|
|
31517
|
+
var JOB_WAIT_TIMEOUT_MS = 60 * 6e4;
|
|
31518
|
+
var JOB_POLL_MS = 500;
|
|
31519
|
+
var JobWaitTimeoutError = class extends CliError {
|
|
31520
|
+
constructor(jobId, timeoutMs) {
|
|
31521
|
+
super(
|
|
31522
|
+
`Translation job ${jobId} was still running after ${Math.round(timeoutMs / 6e4)} minutes, so the CLI stopped waiting.`,
|
|
31523
|
+
`The job keeps running. Check it with 'tolkbot jobs show ${jobId}' and apply it with 'tolkbot jobs apply ${jobId}' once it finishes.`,
|
|
31524
|
+
"job_timed_out"
|
|
31525
|
+
);
|
|
31526
|
+
this.jobId = jobId;
|
|
31527
|
+
this.name = "JobWaitTimeoutError";
|
|
31528
|
+
}
|
|
31529
|
+
jobId;
|
|
31530
|
+
};
|
|
31531
|
+
async function waitForJob(jobId, read, opts = {}) {
|
|
31532
|
+
const timeoutMs = opts.timeoutMs ?? JOB_WAIT_TIMEOUT_MS;
|
|
31533
|
+
const pollMs = opts.pollMs ?? JOB_POLL_MS;
|
|
31534
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
31535
|
+
const now = opts.now ?? Date.now;
|
|
31536
|
+
const deadline = now() + timeoutMs;
|
|
31537
|
+
for (; ; ) {
|
|
31538
|
+
const summary = await read();
|
|
31539
|
+
if (!summary) {
|
|
31540
|
+
throw new CliError(
|
|
31541
|
+
`Translation job ${jobId} was not found.`,
|
|
31542
|
+
void 0,
|
|
31543
|
+
"job_not_found"
|
|
31544
|
+
);
|
|
31545
|
+
}
|
|
31546
|
+
opts.onProgress?.(summary);
|
|
31547
|
+
if (isJobFinished(summary.status)) return summary;
|
|
31548
|
+
if (now() >= deadline) {
|
|
31549
|
+
throw new JobWaitTimeoutError(jobId, timeoutMs);
|
|
31550
|
+
}
|
|
31551
|
+
await sleep(pollMs);
|
|
31552
|
+
}
|
|
31553
|
+
}
|
|
31554
|
+
async function startTranslationJob(params) {
|
|
31555
|
+
const { projectId, organizationId: organizationId2, framework, srcLocale, dstLocale } = params;
|
|
31556
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31557
|
+
return await convexClient.action(api.translations.startJob, {
|
|
31558
|
+
organizationId: organizationId2,
|
|
31559
|
+
projectId,
|
|
31560
|
+
srcLocale,
|
|
31561
|
+
dstLocale,
|
|
31562
|
+
framework,
|
|
31563
|
+
tasks: params.tasks.map((t) => ({
|
|
31564
|
+
key: t.key,
|
|
31565
|
+
namespace: t.namespace,
|
|
31566
|
+
path: t.path,
|
|
31567
|
+
src: t.src,
|
|
31568
|
+
hash: t.hash,
|
|
31569
|
+
context: t.context,
|
|
31570
|
+
// What `jobs apply` checks the target against. Without it every
|
|
31571
|
+
// result was written over whatever the file held by then.
|
|
31572
|
+
expectedTargetHash: t.expectedTargetHash
|
|
31573
|
+
}))
|
|
31574
|
+
});
|
|
31575
|
+
}
|
|
31576
|
+
async function waitForTranslationJob(organizationId2, jobId, onProgress) {
|
|
31577
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31578
|
+
return waitForJob(
|
|
31579
|
+
jobId,
|
|
31580
|
+
() => convexClient.query(api.translations.getJobSummary, {
|
|
31581
|
+
organizationId: organizationId2,
|
|
31582
|
+
jobId
|
|
31583
|
+
}),
|
|
31584
|
+
{ onProgress }
|
|
31585
|
+
);
|
|
31586
|
+
}
|
|
31587
|
+
|
|
31588
|
+
// src/commands/jobs/show.ts
|
|
31589
|
+
var STATUS_TEXT = {
|
|
31590
|
+
pending: "is waiting to start",
|
|
31591
|
+
running: "is running",
|
|
31592
|
+
succeeded: "finished",
|
|
31593
|
+
partial: "finished with some keys failed",
|
|
31594
|
+
failed: "failed",
|
|
31595
|
+
cancelled: "was cancelled"
|
|
31596
|
+
};
|
|
31597
|
+
function describe3(job) {
|
|
31598
|
+
const processed = job.completed + job.failed;
|
|
31599
|
+
const percent = job.total > 0 ? Math.round(processed / job.total * 100) : 0;
|
|
31600
|
+
const lines = [
|
|
31601
|
+
`Job ${job.jobId} ${STATUS_TEXT[job.status]}`,
|
|
31602
|
+
` ${job.srcLocale} \u2192 ${job.dstLocale}, started ${job.createdAt.slice(0, 16).replace("T", " ")} UTC`,
|
|
31603
|
+
` ${job.completed} translated, ${job.failed} failed, ${job.total} in all (${percent}%)`,
|
|
31604
|
+
` ${job.creditsUsed} credits used`
|
|
31605
|
+
];
|
|
31606
|
+
if (job.applied === "applied") lines.push(" Applied to this working tree");
|
|
31607
|
+
if (job.applied === "waiting") {
|
|
31608
|
+
lines.push(
|
|
31609
|
+
` ${job.pending} key(s) waiting: 'tolkbot jobs apply ${job.jobId}'`
|
|
31610
|
+
);
|
|
31611
|
+
}
|
|
31612
|
+
lines.push(` ${env.TOLKBOT_WEB_URL}/translation-jobs/${job.jobId}`);
|
|
31613
|
+
return lines;
|
|
31614
|
+
}
|
|
31615
|
+
async function runJobsShow(opts) {
|
|
31616
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
31617
|
+
const found = await ctx.client.query(api.translations.getJobDetails, {
|
|
31618
|
+
organizationId: ctx.project.organizationId,
|
|
31619
|
+
jobId: opts.jobId
|
|
31620
|
+
});
|
|
31621
|
+
if (found?.projectId !== ctx.project._id) {
|
|
31622
|
+
throw new CliError(
|
|
31623
|
+
`Translation job ${opts.jobId} was not found in this project.`,
|
|
31624
|
+
"List the project's jobs with 'tolkbot jobs list'.",
|
|
31625
|
+
"job_not_found"
|
|
31626
|
+
);
|
|
31627
|
+
}
|
|
31628
|
+
const [job] = await describeJobs(ctx, [found]);
|
|
31629
|
+
if (!job)
|
|
31630
|
+
throw new CliError(`Translation job ${opts.jobId} was not found.`);
|
|
31631
|
+
const failure2 = unsuccessfulJobError(job.jobId, job.status);
|
|
31632
|
+
if (opts.json) {
|
|
31633
|
+
if (!failure2) jsonSuccess(job);
|
|
31634
|
+
jsonResult(job, 1);
|
|
31635
|
+
fail(failure2);
|
|
31636
|
+
}
|
|
31637
|
+
for (const line of describe3(job)) console.log(line);
|
|
31638
|
+
if (failure2) fail(failure2);
|
|
31639
|
+
}
|
|
31640
|
+
|
|
31641
|
+
// src/commands/jobs/index.ts
|
|
31642
|
+
var jobsCommand = new Command3("jobs").description(
|
|
31643
|
+
"List, inspect, apply and cancel translation jobs"
|
|
31644
|
+
);
|
|
31645
|
+
var JSON_HELP = "Output as JSON (machine-readable)";
|
|
31646
|
+
jobsCommand.command("list").description(
|
|
31647
|
+
"Recent translation jobs for the project, with their status and whether their results are in this working tree"
|
|
31648
|
+
).option("--limit <n>", "How many jobs to list", "20").option("--json", JSON_HELP).action(async (opts) => {
|
|
31649
|
+
const schema = z5.object({
|
|
31650
|
+
limit: z5.coerce.number().int().min(1).max(100),
|
|
31651
|
+
json: z5.boolean().optional()
|
|
31652
|
+
});
|
|
31653
|
+
await runJobsList(validateOrExit(schema, opts));
|
|
31654
|
+
});
|
|
31655
|
+
jobsCommand.command("show").description("Show one translation job").argument("<id>", "Job ID").option("--json", JSON_HELP).action(async (jobId, opts) => {
|
|
31656
|
+
const schema = z5.object({
|
|
31657
|
+
jobId: z5.string().min(1),
|
|
31658
|
+
json: z5.boolean().optional()
|
|
31659
|
+
});
|
|
31660
|
+
await runJobsShow(validateOrExit(schema, { ...opts, jobId }));
|
|
31661
|
+
});
|
|
31662
|
+
jobsCommand.command("apply").description(
|
|
31663
|
+
"Write finished jobs' results into the working tree. With no ID: the finished jobs not applied yet, one per language"
|
|
31664
|
+
).argument("[ids...]", "Job ID(s) to apply").option("--json", JSON_HELP).action(async (jobIds, opts) => {
|
|
31665
|
+
const schema = z5.object({
|
|
31666
|
+
jobIds: z5.array(z5.string().min(1)),
|
|
31667
|
+
json: z5.boolean().optional()
|
|
31668
|
+
});
|
|
31669
|
+
await runJobsApply(validateOrExit(schema, { ...opts, jobIds }));
|
|
31670
|
+
});
|
|
31671
|
+
jobsCommand.command("cancel").description("Cancel a running translation job").argument("<id>", "Job ID").option("--json", JSON_HELP).action(async (jobId, opts) => {
|
|
31672
|
+
const schema = z5.object({
|
|
31673
|
+
jobId: z5.string().min(1),
|
|
31674
|
+
json: z5.boolean().optional()
|
|
31675
|
+
});
|
|
31676
|
+
await runJobsCancel(validateOrExit(schema, { ...opts, jobId }));
|
|
31677
|
+
});
|
|
31678
|
+
|
|
31679
|
+
// src/commands/learnings/index.ts
|
|
31680
|
+
import { Command as Command4 } from "commander";
|
|
31681
|
+
import { z as z6 } from "zod/v4";
|
|
31682
|
+
|
|
31683
|
+
// src/commands/learnings/archive.tsx
|
|
31684
|
+
import { Spinner as Spinner9 } from "@inkjs/ui";
|
|
31685
|
+
import { Box as Box12, render as render12, Text as Text12 } from "ink";
|
|
31686
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
31687
|
+
function ArchiveUI({ state }) {
|
|
31688
|
+
if (state.type === "archiving") {
|
|
31689
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
31690
|
+
/* @__PURE__ */ jsx12(Spinner9, {}),
|
|
31691
|
+
/* @__PURE__ */ jsx12(Text12, { children: " Archiving learning\u2026" })
|
|
31692
|
+
] });
|
|
31693
|
+
}
|
|
31694
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
31695
|
+
/* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
|
|
31696
|
+
/* @__PURE__ */ jsx12(Text12, { children: " Learning archived" })
|
|
31697
|
+
] });
|
|
31698
|
+
}
|
|
31699
|
+
async function runLearningsArchive(opts) {
|
|
31700
|
+
const run = async () => {
|
|
31701
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31702
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31703
|
+
await convexClient.mutation(api.learnings.archive, {
|
|
31704
|
+
organizationId: organizationId2,
|
|
31705
|
+
learningId: opts.id
|
|
31706
|
+
});
|
|
31707
|
+
};
|
|
31708
|
+
if (opts.json) {
|
|
31709
|
+
await run();
|
|
31710
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
31711
|
+
}
|
|
31712
|
+
const { unmount } = renderTransient(
|
|
31713
|
+
/* @__PURE__ */ jsx12(ArchiveUI, { state: { type: "archiving" } })
|
|
31714
|
+
);
|
|
31715
|
+
try {
|
|
31716
|
+
await run();
|
|
31717
|
+
} finally {
|
|
31718
|
+
unmount();
|
|
31719
|
+
}
|
|
31720
|
+
render12(/* @__PURE__ */ jsx12(ArchiveUI, { state: { type: "success" } })).unmount();
|
|
31721
|
+
}
|
|
31722
|
+
|
|
31723
|
+
// src/commands/learnings/list.tsx
|
|
31724
|
+
import { Spinner as Spinner10 } from "@inkjs/ui";
|
|
31725
|
+
import { Box as Box13, render as render13, Text as Text13 } from "ink";
|
|
31726
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
31727
|
+
var feedbackLabels = {
|
|
31728
|
+
"incorrect-term": "Incorrect term",
|
|
31729
|
+
"wrong-tone": "Wrong tone",
|
|
31730
|
+
"cultural-context": "Cultural context",
|
|
31731
|
+
"technical-accuracy": "Technical accuracy",
|
|
31732
|
+
other: "Other"
|
|
31733
|
+
};
|
|
31734
|
+
function LearningsListUI({ state }) {
|
|
31735
|
+
if (state.type === "loading") {
|
|
31736
|
+
return /* @__PURE__ */ jsxs13(Box13, { children: [
|
|
31737
|
+
/* @__PURE__ */ jsx13(Spinner10, {}),
|
|
31738
|
+
/* @__PURE__ */ jsx13(Text13, { children: " Loading learnings\u2026" })
|
|
31739
|
+
] });
|
|
31740
|
+
}
|
|
31741
|
+
const { learnings } = state;
|
|
31742
|
+
if (learnings.length === 0) {
|
|
31743
|
+
return /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: "No learnings found." }) });
|
|
31744
|
+
}
|
|
31745
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
31746
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
31747
|
+
learnings.length,
|
|
31748
|
+
" learning",
|
|
31749
|
+
learnings.length === 1 ? "" : "s"
|
|
31750
|
+
] }) }),
|
|
31751
|
+
learnings.map((l) => /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
|
|
31752
|
+
/* @__PURE__ */ jsxs13(Box13, { gap: 1, children: [
|
|
31753
|
+
/* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
|
|
31754
|
+
l.srcLocale,
|
|
31755
|
+
" \u2192 ",
|
|
31756
|
+
l.dstLocale
|
|
31757
|
+
] }),
|
|
31758
|
+
/* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31759
|
+
"[",
|
|
31760
|
+
feedbackLabels[l.feedbackType] ?? l.feedbackType,
|
|
31761
|
+
"]"
|
|
31762
|
+
] }),
|
|
31763
|
+
l.status === "archived" && /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: "[archived]" })
|
|
31764
|
+
] }),
|
|
31765
|
+
/* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { children: [
|
|
31766
|
+
'"',
|
|
31767
|
+
l.sourceText,
|
|
31768
|
+
'" \u2192 "',
|
|
31769
|
+
l.suggestedTranslation,
|
|
31770
|
+
'"'
|
|
31771
|
+
] }) }),
|
|
31772
|
+
l.reasoning && /* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31773
|
+
"Reason: ",
|
|
31774
|
+
l.reasoning
|
|
31775
|
+
] }) }),
|
|
31776
|
+
/* @__PURE__ */ jsx13(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
|
|
31777
|
+
"ID: ",
|
|
31778
|
+
l._id
|
|
31779
|
+
] }) })
|
|
31780
|
+
] }, l._id))
|
|
31781
|
+
] });
|
|
31782
|
+
}
|
|
31783
|
+
async function loadLearnings(opts) {
|
|
31784
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31785
|
+
const { projectId, organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31786
|
+
return convexClient.query(api.learnings.listByProject, {
|
|
31787
|
+
organizationId: organizationId2,
|
|
31788
|
+
projectId,
|
|
31789
|
+
status: opts.status,
|
|
31790
|
+
limit: opts.limit
|
|
31791
|
+
});
|
|
31792
|
+
}
|
|
31793
|
+
async function runLearningsList(opts) {
|
|
31794
|
+
if (opts.json) {
|
|
31795
|
+
const learnings2 = await loadLearnings(opts);
|
|
31796
|
+
jsonSuccess({
|
|
31797
|
+
learnings: learnings2.map((l) => ({
|
|
31798
|
+
id: l._id,
|
|
31799
|
+
sourceText: l.sourceText,
|
|
31800
|
+
suggestedTranslation: l.suggestedTranslation,
|
|
31801
|
+
srcLocale: l.srcLocale,
|
|
31802
|
+
dstLocale: l.dstLocale,
|
|
31803
|
+
feedbackType: l.feedbackType,
|
|
31804
|
+
reasoning: l.reasoning,
|
|
31805
|
+
status: l.status,
|
|
31806
|
+
source: l.source
|
|
31807
|
+
}))
|
|
31808
|
+
});
|
|
31809
|
+
}
|
|
31810
|
+
const { unmount } = renderTransient(
|
|
31811
|
+
/* @__PURE__ */ jsx13(LearningsListUI, { state: { type: "loading" } })
|
|
31812
|
+
);
|
|
31813
|
+
let learnings;
|
|
31814
|
+
try {
|
|
31815
|
+
learnings = await loadLearnings(opts);
|
|
31816
|
+
} finally {
|
|
31817
|
+
unmount();
|
|
31818
|
+
}
|
|
31819
|
+
render13(
|
|
31820
|
+
/* @__PURE__ */ jsx13(LearningsListUI, { state: { type: "success", learnings } })
|
|
31821
|
+
).unmount();
|
|
31822
|
+
}
|
|
31823
|
+
|
|
31824
|
+
// src/commands/learnings/remove.tsx
|
|
31825
|
+
import { Spinner as Spinner11 } from "@inkjs/ui";
|
|
31826
|
+
import { Box as Box14, render as render14, Text as Text14 } from "ink";
|
|
31827
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
31828
|
+
function RemoveUI2({ state }) {
|
|
31829
|
+
if (state.type === "removing") {
|
|
31830
|
+
return /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
31831
|
+
/* @__PURE__ */ jsx14(Spinner11, {}),
|
|
31832
|
+
/* @__PURE__ */ jsx14(Text14, { children: " Removing learning\u2026" })
|
|
31833
|
+
] });
|
|
31834
|
+
}
|
|
31835
|
+
return /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
31836
|
+
/* @__PURE__ */ jsx14(Text14, { color: "green", children: "\u2713" }),
|
|
31837
|
+
/* @__PURE__ */ jsx14(Text14, { children: " Learning removed" })
|
|
31838
|
+
] });
|
|
31839
|
+
}
|
|
31840
|
+
async function runLearningsRemove(opts) {
|
|
31841
|
+
const run = async () => {
|
|
31842
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31843
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31844
|
+
await convexClient.mutation(api.learnings.remove, {
|
|
31845
|
+
organizationId: organizationId2,
|
|
31846
|
+
learningId: opts.id
|
|
31847
|
+
});
|
|
31848
|
+
};
|
|
31849
|
+
if (opts.json) {
|
|
31850
|
+
await run();
|
|
31851
|
+
jsonSuccess({ id: opts.id, ok: true });
|
|
31852
|
+
}
|
|
31853
|
+
const { unmount } = renderTransient(
|
|
31854
|
+
/* @__PURE__ */ jsx14(RemoveUI2, { state: { type: "removing" } })
|
|
31855
|
+
);
|
|
31856
|
+
try {
|
|
31857
|
+
await run();
|
|
31858
|
+
} finally {
|
|
31859
|
+
unmount();
|
|
31860
|
+
}
|
|
31861
|
+
render14(/* @__PURE__ */ jsx14(RemoveUI2, { state: { type: "success" } })).unmount();
|
|
31862
|
+
}
|
|
31863
|
+
|
|
31864
|
+
// src/commands/learnings/show.tsx
|
|
31865
|
+
import { Spinner as Spinner12 } from "@inkjs/ui";
|
|
31866
|
+
import { Box as Box15, render as render15, Text as Text15 } from "ink";
|
|
31867
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
31868
|
+
function LearningShowUI({ state }) {
|
|
31869
|
+
if (state.type === "loading") {
|
|
31870
|
+
return /* @__PURE__ */ jsxs15(Box15, { children: [
|
|
31871
|
+
/* @__PURE__ */ jsx15(Spinner12, {}),
|
|
31872
|
+
/* @__PURE__ */ jsx15(Text15, { children: " Loading learning\u2026" })
|
|
31873
|
+
] });
|
|
31874
|
+
}
|
|
31875
|
+
const { learning } = state;
|
|
31876
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
31877
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31878
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Source text:" }),
|
|
31879
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.sourceText })
|
|
31880
|
+
] }),
|
|
31881
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31882
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Suggested translation:" }),
|
|
31883
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.suggestedTranslation })
|
|
31884
|
+
] }),
|
|
31885
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31886
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Locales:" }),
|
|
31887
|
+
/* @__PURE__ */ jsxs15(Text15, { children: [
|
|
31888
|
+
learning.srcLocale,
|
|
31889
|
+
" \u2192 ",
|
|
31890
|
+
learning.dstLocale
|
|
31891
|
+
] })
|
|
31892
|
+
] }),
|
|
31893
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31894
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Feedback type:" }),
|
|
31895
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.feedbackType })
|
|
31896
|
+
] }),
|
|
31897
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31898
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Reasoning:" }),
|
|
31899
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.reasoning })
|
|
31900
|
+
] }),
|
|
31901
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31902
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Status:" }),
|
|
31903
|
+
/* @__PURE__ */ jsx15(Text15, { color: learning.status === "active" ? "green" : "yellow", children: learning.status })
|
|
31904
|
+
] }),
|
|
31905
|
+
/* @__PURE__ */ jsxs15(Box15, { gap: 1, children: [
|
|
31906
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: "Source:" }),
|
|
31907
|
+
/* @__PURE__ */ jsx15(Text15, { children: learning.source })
|
|
31908
|
+
] }),
|
|
31909
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
|
|
31910
|
+
"ID: ",
|
|
31911
|
+
learning._id
|
|
31912
|
+
] }) })
|
|
31913
|
+
] });
|
|
31914
|
+
}
|
|
31915
|
+
async function loadLearning(id) {
|
|
31916
|
+
const convexClient = await getAuthenticatedConvexClient();
|
|
31917
|
+
const { organizationId: organizationId2 } = await getProjectContext(convexClient);
|
|
31918
|
+
const learning = await convexClient.query(api.learnings.getById, {
|
|
31919
|
+
organizationId: organizationId2,
|
|
31920
|
+
learningId: id
|
|
31921
|
+
});
|
|
31922
|
+
if (!learning) {
|
|
31923
|
+
throw new CliError(
|
|
31924
|
+
`Learning ${id} was not found.`,
|
|
31925
|
+
"List them with 'tolkbot learnings list'.",
|
|
31926
|
+
"not_found"
|
|
31927
|
+
);
|
|
31928
|
+
}
|
|
31929
|
+
return learning;
|
|
31930
|
+
}
|
|
31931
|
+
async function runLearningsShow(opts) {
|
|
31932
|
+
if (opts.json) {
|
|
31933
|
+
const learning2 = await loadLearning(opts.id);
|
|
31934
|
+
jsonSuccess({
|
|
31935
|
+
id: learning2._id,
|
|
31936
|
+
sourceText: learning2.sourceText,
|
|
31937
|
+
suggestedTranslation: learning2.suggestedTranslation,
|
|
31938
|
+
srcLocale: learning2.srcLocale,
|
|
31939
|
+
dstLocale: learning2.dstLocale,
|
|
31940
|
+
feedbackType: learning2.feedbackType,
|
|
31941
|
+
reasoning: learning2.reasoning,
|
|
31942
|
+
status: learning2.status,
|
|
31943
|
+
source: learning2.source
|
|
31944
|
+
});
|
|
31945
|
+
}
|
|
31946
|
+
const { unmount } = renderTransient(
|
|
31947
|
+
/* @__PURE__ */ jsx15(LearningShowUI, { state: { type: "loading" } })
|
|
31948
|
+
);
|
|
31949
|
+
let learning;
|
|
31950
|
+
try {
|
|
31951
|
+
learning = await loadLearning(opts.id);
|
|
31952
|
+
} finally {
|
|
31953
|
+
unmount();
|
|
31954
|
+
}
|
|
31955
|
+
render15(/* @__PURE__ */ jsx15(LearningShowUI, { state: { type: "success", learning } })).unmount();
|
|
31956
|
+
}
|
|
31957
|
+
|
|
31958
|
+
// src/commands/learnings/index.ts
|
|
31959
|
+
var learningsCommand = new Command4("learnings").description(
|
|
31960
|
+
"View and manage translation learnings"
|
|
31961
|
+
);
|
|
31962
|
+
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) => {
|
|
31963
|
+
const schema = z6.object({
|
|
31964
|
+
status: z6.enum(["active", "archived"]).optional(),
|
|
31965
|
+
limit: z6.coerce.number().int().positive().default(100),
|
|
31966
|
+
json: z6.boolean().optional()
|
|
31967
|
+
});
|
|
31968
|
+
const parsed = validateOrExit(schema, opts);
|
|
31969
|
+
await runLearningsList(parsed);
|
|
31970
|
+
});
|
|
31971
|
+
learningsCommand.command("show").description("Show details of a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31972
|
+
const schema = z6.object({
|
|
31973
|
+
id: z6.string().min(1),
|
|
31974
|
+
json: z6.boolean().optional()
|
|
31975
|
+
});
|
|
31976
|
+
const parsed = validateOrExit(schema, opts);
|
|
31977
|
+
await runLearningsShow(parsed);
|
|
31978
|
+
});
|
|
31979
|
+
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) => {
|
|
31980
|
+
const schema = z6.object({
|
|
31981
|
+
id: z6.string().min(1),
|
|
31982
|
+
json: z6.boolean().optional()
|
|
31983
|
+
});
|
|
31984
|
+
const parsed = validateOrExit(schema, opts);
|
|
31985
|
+
await runLearningsArchive(parsed);
|
|
31986
|
+
});
|
|
31987
|
+
learningsCommand.command("remove").description("Permanently remove a learning").requiredOption("--id <learningId>", "Learning ID").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
31988
|
+
const schema = z6.object({
|
|
31989
|
+
id: z6.string().min(1),
|
|
31990
|
+
json: z6.boolean().optional()
|
|
31991
|
+
});
|
|
31992
|
+
const parsed = validateOrExit(schema, opts);
|
|
31993
|
+
await runLearningsRemove(parsed);
|
|
31994
|
+
});
|
|
31995
|
+
|
|
31996
|
+
// src/commands/status/index.ts
|
|
31997
|
+
import { Command as Command5 } from "commander";
|
|
31998
|
+
import { z as z7 } from "zod/v4";
|
|
31999
|
+
|
|
32000
|
+
// src/commands/status/status.tsx
|
|
32001
|
+
import { Spinner as Spinner13 } from "@inkjs/ui";
|
|
32002
|
+
import { Box as Box16, Text as Text16 } from "ink";
|
|
32003
|
+
|
|
32004
|
+
// src/commands/status/status-core.ts
|
|
32005
|
+
function fullKey4(namespace, key) {
|
|
32006
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
32007
|
+
}
|
|
32008
|
+
function isPersonHold(reason) {
|
|
32009
|
+
return reason === "locked" || reason === "pending-draft";
|
|
32010
|
+
}
|
|
32011
|
+
function count(reasons2) {
|
|
32012
|
+
const byReason = {};
|
|
32013
|
+
for (const reason of reasons2)
|
|
32014
|
+
byReason[reason] = (byReason[reason] ?? 0) + 1;
|
|
32015
|
+
return { total: reasons2.length, byReason };
|
|
32016
|
+
}
|
|
32017
|
+
function openPullRequest(rows, locale) {
|
|
32018
|
+
const row = rows.find(
|
|
32019
|
+
(r) => r.prState === "open" && r.prUrl !== void 0 && sameLocale(r.targetLanguage, locale)
|
|
32020
|
+
);
|
|
32021
|
+
if (!row?.prUrl) return null;
|
|
32022
|
+
return { number: row.prNumber ?? null, url: row.prUrl };
|
|
32023
|
+
}
|
|
32024
|
+
function languageStatus(locale, plan, pullRequests, opts = {}) {
|
|
32025
|
+
const { summary } = plan;
|
|
32026
|
+
const held = [
|
|
32027
|
+
...plan.heldKeys,
|
|
32028
|
+
...plan.protectedKeys.flatMap(
|
|
32029
|
+
({ key, reason }) => isPersonHold(reason) ? [{ key, reason }] : []
|
|
32030
|
+
)
|
|
32031
|
+
];
|
|
32032
|
+
const kept = plan.protectedKeys.flatMap(
|
|
32033
|
+
({ key, reason }) => isPersonHold(reason) ? [] : [{ key, reason }]
|
|
32034
|
+
);
|
|
32035
|
+
const tasks = (reason) => plan.tasks.filter((t) => t.reason === reason).map((t) => fullKey4(t.namespace, t.key));
|
|
32036
|
+
return {
|
|
32037
|
+
locale,
|
|
32038
|
+
expected: summary.totalKeys,
|
|
32039
|
+
upToDate: summary.upToDate,
|
|
32040
|
+
missing: summary.missing,
|
|
32041
|
+
changed: summary.changed,
|
|
32042
|
+
heldByPerson: count(held.map((h) => h.reason)),
|
|
32043
|
+
keptForPerson: count(kept.map((k) => k.reason)),
|
|
32044
|
+
untracked: summary.untracked,
|
|
32045
|
+
pullRequest: openPullRequest(pullRequests, locale),
|
|
32046
|
+
...opts.keys ? {
|
|
32047
|
+
keys: {
|
|
32048
|
+
missing: tasks("missing"),
|
|
32049
|
+
changed: tasks("changed"),
|
|
32050
|
+
heldByPerson: held,
|
|
32051
|
+
keptForPerson: kept,
|
|
32052
|
+
untracked: plan.untrackedKeys.map((k) => k.key)
|
|
32053
|
+
}
|
|
32054
|
+
} : {}
|
|
32055
|
+
};
|
|
32056
|
+
}
|
|
32057
|
+
function needsTranslation(language) {
|
|
32058
|
+
return language.missing > 0 || language.changed > 0;
|
|
32059
|
+
}
|
|
32060
|
+
var REASON_WORDS = {
|
|
32061
|
+
locked: "locked",
|
|
32062
|
+
"pending-draft": "pending draft",
|
|
32063
|
+
manual: "written by a person",
|
|
32064
|
+
"target-edited": "edited since TolkBot wrote it",
|
|
32065
|
+
unknown: "no record of who wrote it"
|
|
32066
|
+
};
|
|
32067
|
+
function reasons(counts) {
|
|
32068
|
+
return Object.entries(counts.byReason).map(
|
|
32069
|
+
([reason, n]) => `${n} ${REASON_WORDS[reason]}`
|
|
32070
|
+
).join(", ");
|
|
32071
|
+
}
|
|
32072
|
+
function describeLanguage(language) {
|
|
32073
|
+
const counts = [
|
|
32074
|
+
`${language.expected} expected`,
|
|
32075
|
+
`${language.upToDate} up to date`,
|
|
32076
|
+
`${language.missing} missing`,
|
|
32077
|
+
`${language.changed} changed since translated`
|
|
32078
|
+
].join(", ");
|
|
32079
|
+
const mark = needsTranslation(language) ? "\u2022" : "\u2713";
|
|
32080
|
+
const lines = [`${mark} ${language.locale}: ${counts}`];
|
|
32081
|
+
if (language.heldByPerson.total > 0) {
|
|
32082
|
+
lines.push(
|
|
32083
|
+
` ${language.heldByPerson.total} held by a person (${reasons(language.heldByPerson)})`
|
|
32084
|
+
);
|
|
32085
|
+
}
|
|
32086
|
+
if (language.keptForPerson.total > 0) {
|
|
32087
|
+
lines.push(
|
|
32088
|
+
` ${language.keptForPerson.total} changed but kept for a person (${reasons(language.keptForPerson)})`
|
|
32089
|
+
);
|
|
32090
|
+
}
|
|
32091
|
+
if (language.untracked > 0) {
|
|
32092
|
+
lines.push(
|
|
32093
|
+
` ${language.untracked} untracked: TolkBot has no record of them`
|
|
32094
|
+
);
|
|
32095
|
+
}
|
|
32096
|
+
if (language.pullRequest) {
|
|
32097
|
+
lines.push(` Pull request: ${language.pullRequest.url}`);
|
|
32098
|
+
}
|
|
32099
|
+
const { keys } = language;
|
|
32100
|
+
if (keys) {
|
|
32101
|
+
const list = (label, names) => {
|
|
32102
|
+
if (names.length === 0) return;
|
|
32103
|
+
lines.push(` ${label}:`);
|
|
32104
|
+
for (const name of names) lines.push(` ${name}`);
|
|
32105
|
+
};
|
|
32106
|
+
list("Missing", keys.missing);
|
|
32107
|
+
list("Changed", keys.changed);
|
|
32108
|
+
list(
|
|
32109
|
+
"Held by a person",
|
|
32110
|
+
keys.heldByPerson.map(
|
|
32111
|
+
(k) => `${k.key} (${REASON_WORDS[k.reason]})`
|
|
32112
|
+
)
|
|
32113
|
+
);
|
|
32114
|
+
list(
|
|
32115
|
+
"Kept for a person",
|
|
32116
|
+
keys.keptForPerson.map(
|
|
32117
|
+
(k) => `${k.key} (${REASON_WORDS[k.reason]})`
|
|
32118
|
+
)
|
|
32119
|
+
);
|
|
32120
|
+
list("Untracked", keys.untracked);
|
|
32121
|
+
}
|
|
32122
|
+
return lines;
|
|
32123
|
+
}
|
|
32124
|
+
|
|
32125
|
+
// src/commands/status/status.tsx
|
|
32126
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
32127
|
+
var OFFLINE_HINT = "Without a login, 'tolkbot validate' checks the files on this machine.";
|
|
32128
|
+
async function runStatus(opts) {
|
|
32129
|
+
let ctx;
|
|
32130
|
+
try {
|
|
32131
|
+
ctx = await loadPlanContext(process.cwd());
|
|
32132
|
+
} catch (error62) {
|
|
32133
|
+
if (error62 instanceof AuthError) {
|
|
32134
|
+
throw new AuthError(
|
|
32135
|
+
error62.baseMessage,
|
|
32136
|
+
[error62.hint, OFFLINE_HINT].filter(Boolean).join(" "),
|
|
32137
|
+
error62.code
|
|
32138
|
+
);
|
|
32139
|
+
}
|
|
32140
|
+
throw error62;
|
|
32141
|
+
}
|
|
32142
|
+
const { project } = ctx;
|
|
32143
|
+
const srcLocale = project.baseLanguage;
|
|
32144
|
+
const locales = project.targetLanguages;
|
|
32145
|
+
const progress = renderTransient(
|
|
32146
|
+
/* @__PURE__ */ jsxs16(Box16, { children: [
|
|
32147
|
+
/* @__PURE__ */ jsx16(Spinner13, {}),
|
|
32148
|
+
/* @__PURE__ */ jsxs16(Text16, { children: [
|
|
32149
|
+
" Reading the status of ",
|
|
32150
|
+
project.name,
|
|
32151
|
+
"\u2026"
|
|
32152
|
+
] })
|
|
32153
|
+
] })
|
|
32154
|
+
);
|
|
32155
|
+
const languages = [];
|
|
32156
|
+
let credits;
|
|
32157
|
+
try {
|
|
32158
|
+
const pullRequests = locales.length === 0 ? [] : await ctx.client.query(api.prJobs.listByProject, {
|
|
32159
|
+
organizationId: project.organizationId,
|
|
32160
|
+
projectId: project._id
|
|
32161
|
+
});
|
|
32162
|
+
for (const locale of locales) {
|
|
32163
|
+
const plan = await planForLocale(ctx, srcLocale, locale);
|
|
32164
|
+
languages.push(
|
|
32165
|
+
languageStatus(locale, plan, pullRequests, {
|
|
32166
|
+
keys: opts.keys
|
|
32167
|
+
})
|
|
32168
|
+
);
|
|
32169
|
+
}
|
|
32170
|
+
credits = await readCreditBalance(ctx);
|
|
32171
|
+
} finally {
|
|
32172
|
+
progress.unmount();
|
|
32173
|
+
}
|
|
32174
|
+
const payload = {
|
|
32175
|
+
project: {
|
|
32176
|
+
name: project.name,
|
|
32177
|
+
slug: project.slug,
|
|
32178
|
+
url: `${env.TOLKBOT_WEB_URL}/projects/${project.slug}`,
|
|
32179
|
+
baseLanguage: srcLocale,
|
|
32180
|
+
pattern: fullPattern(scopeOf(project)),
|
|
32181
|
+
autoCreatePR: project.workflow?.autoCreatePR ?? DEFAULT_BEHAVIORAL_SETTINGS.workflow.autoCreatePR
|
|
32182
|
+
},
|
|
32183
|
+
credits: { balance: credits },
|
|
32184
|
+
languages
|
|
32185
|
+
};
|
|
32186
|
+
const outOfDate = languages.some(needsTranslation);
|
|
32187
|
+
const exitCode = opts.exitCode && outOfDate ? 1 : 0;
|
|
32188
|
+
if (opts.json) {
|
|
32189
|
+
if (exitCode === 0) jsonSuccess(payload);
|
|
32190
|
+
jsonResult(payload, exitCode);
|
|
32191
|
+
return;
|
|
32192
|
+
}
|
|
32193
|
+
const { project: p } = payload;
|
|
32194
|
+
console.log(`${p.name} (${p.slug}) ${p.url}`);
|
|
32195
|
+
console.log(
|
|
32196
|
+
`Base language ${p.baseLanguage}, files ${p.pattern}, auto-create pull requests ${p.autoCreatePR ? "on" : "off"}`
|
|
32197
|
+
);
|
|
32198
|
+
console.log(
|
|
32199
|
+
credits === null ? "Credits left: could not be read" : `Credits left: ${credits}`
|
|
32200
|
+
);
|
|
32201
|
+
console.log("");
|
|
32202
|
+
if (languages.length === 0) {
|
|
32203
|
+
console.log(
|
|
32204
|
+
`No target languages configured. Add them in the project settings at ${p.url}/settings.`
|
|
32205
|
+
);
|
|
32206
|
+
}
|
|
32207
|
+
for (const language of languages) {
|
|
32208
|
+
for (const line of describeLanguage(language)) console.log(line);
|
|
32209
|
+
}
|
|
32210
|
+
if (outOfDate) {
|
|
32211
|
+
console.log(
|
|
32212
|
+
"\nRun 'tolkbot translate --all' to translate what is missing or changed."
|
|
32213
|
+
);
|
|
32214
|
+
}
|
|
32215
|
+
process.exitCode = exitCode;
|
|
32216
|
+
}
|
|
32217
|
+
|
|
32218
|
+
// src/commands/status/index.ts
|
|
32219
|
+
var statusCommand = new Command5("status").description(
|
|
32220
|
+
"Show what each language is missing, what waits for a person, the open pull requests and the credits left"
|
|
32221
|
+
).option("--keys", "List the keys by name under each language").option(
|
|
32222
|
+
"--exit-code",
|
|
32223
|
+
"Exit 1 when any language has missing or changed keys"
|
|
32224
|
+
).option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32225
|
+
const schema = z7.object({
|
|
32226
|
+
keys: z7.boolean().optional(),
|
|
32227
|
+
exitCode: z7.boolean().optional(),
|
|
32228
|
+
json: z7.boolean().optional()
|
|
32229
|
+
});
|
|
32230
|
+
await runStatus(validateOrExit(schema, opts));
|
|
32231
|
+
});
|
|
32232
|
+
|
|
32233
|
+
// src/commands/telemetry.ts
|
|
32234
|
+
import { Command as Command6 } from "commander";
|
|
32235
|
+
import { z as z8 } from "zod/v4";
|
|
32236
|
+
var REASONS = {
|
|
32237
|
+
env: `disabled by $${TELEMETRY_ENV_VAR}`,
|
|
32238
|
+
"do-not-track": `disabled by $${DO_NOT_TRACK_ENV_VAR}`,
|
|
32239
|
+
preference: "disabled with `tolkbot telemetry off`",
|
|
32240
|
+
"no-key": "disabled: this build has no telemetry key",
|
|
32241
|
+
default: "enabled"
|
|
32242
|
+
};
|
|
32243
|
+
async function printStatus(json2) {
|
|
32244
|
+
const state = await telemetryState();
|
|
32245
|
+
if (json2) {
|
|
32246
|
+
jsonResult({ ...state, preferenceFile: getPreferencePath() }, 0);
|
|
32247
|
+
return;
|
|
32248
|
+
}
|
|
32249
|
+
console.log(
|
|
32250
|
+
`Telemetry is ${state.enabled ? "on" : "off"} (${REASONS[state.reason]}).`
|
|
32251
|
+
);
|
|
32252
|
+
console.log(
|
|
32253
|
+
`Turn it off with \`tolkbot telemetry off\`, $${TELEMETRY_ENV_VAR}=0 or $${DO_NOT_TRACK_ENV_VAR}=1.`
|
|
32254
|
+
);
|
|
32255
|
+
}
|
|
32256
|
+
var telemetryCommand = new Command6("telemetry").description(
|
|
32257
|
+
"Control anonymous usage reporting"
|
|
32258
|
+
);
|
|
32259
|
+
telemetryCommand.command("status", { isDefault: true }).description("Show whether telemetry is on and why").option("--json", "Output as JSON (machine-readable)").action(async (opts) => {
|
|
32260
|
+
const parsed = validateOrExit(
|
|
32261
|
+
z8.object({ json: z8.boolean().optional() }),
|
|
32262
|
+
opts
|
|
32263
|
+
);
|
|
32264
|
+
await printStatus(parsed.json);
|
|
32265
|
+
});
|
|
32266
|
+
telemetryCommand.command("off").description("Stop reporting usage from this machine").action(async () => {
|
|
32267
|
+
await setTelemetryPreference(false);
|
|
32268
|
+
console.log(
|
|
32269
|
+
`Telemetry is off. Preference saved to ${getPreferencePath()}.`
|
|
32270
|
+
);
|
|
32271
|
+
});
|
|
32272
|
+
telemetryCommand.command("on").description("Resume reporting usage from this machine").action(async () => {
|
|
32273
|
+
await setTelemetryPreference(true);
|
|
32274
|
+
await printStatus(false);
|
|
32275
|
+
});
|
|
32276
|
+
|
|
32277
|
+
// src/commands/translate/index.ts
|
|
32278
|
+
import { Command as Command7 } from "commander";
|
|
32279
|
+
import { z as z9 } from "zod/v4";
|
|
32280
|
+
|
|
32281
|
+
// src/commands/translate/adopt.tsx
|
|
32282
|
+
var STAMP_CHUNK2 = 200;
|
|
32283
|
+
async function adoptLocale(ctx, srcLocale, dstLocale) {
|
|
32284
|
+
const plan = await planForLocale(ctx, srcLocale, dstLocale);
|
|
32285
|
+
const locale = governanceLocale(ctx.project, dstLocale);
|
|
32286
|
+
const byTemplate = /* @__PURE__ */ new Map();
|
|
32287
|
+
for (const key of plan.untrackedKeys) {
|
|
32288
|
+
const group = byTemplate.get(key.pathTemplate) ?? [];
|
|
32289
|
+
group.push(key);
|
|
32290
|
+
byTemplate.set(key.pathTemplate, group);
|
|
32291
|
+
}
|
|
32292
|
+
const outcome = {
|
|
32293
|
+
dstLocale,
|
|
32294
|
+
untracked: plan.untrackedKeys.length,
|
|
32295
|
+
adopted: 0,
|
|
32296
|
+
skipped: 0
|
|
32297
|
+
};
|
|
32298
|
+
for (const [pathTemplate, keys] of byTemplate) {
|
|
32299
|
+
for (let i = 0; i < keys.length; i += STAMP_CHUNK2) {
|
|
32300
|
+
const result = await ctx.client.mutation(
|
|
32301
|
+
api.localeKeyStates.adoptUntrackedForCli,
|
|
32302
|
+
{
|
|
32303
|
+
organizationId: ctx.project.organizationId,
|
|
32304
|
+
projectId: ctx.project._id,
|
|
32305
|
+
locale,
|
|
32306
|
+
pathTemplate,
|
|
32307
|
+
entries: keys.slice(i, i + STAMP_CHUNK2).map((key) => ({
|
|
32308
|
+
key: key.fileKey,
|
|
32309
|
+
namespace: key.namespace,
|
|
32310
|
+
baseValue: key.src,
|
|
32311
|
+
translatedValue: key.dst
|
|
32312
|
+
}))
|
|
32313
|
+
}
|
|
32314
|
+
);
|
|
32315
|
+
outcome.adopted += result.adopted;
|
|
32316
|
+
outcome.skipped += result.skipped;
|
|
32317
|
+
}
|
|
32318
|
+
}
|
|
32319
|
+
return outcome;
|
|
32320
|
+
}
|
|
32321
|
+
async function runAdopt(opts) {
|
|
32322
|
+
const run = async () => {
|
|
32323
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
32324
|
+
const src = opts.src ?? ctx.project.baseLanguage;
|
|
32325
|
+
const locales = opts.all ? ctx.project.targetLanguages : opts.dst ? [opts.dst] : [];
|
|
32326
|
+
if (locales.length === 0) {
|
|
32327
|
+
throw opts.all ? new CliError(
|
|
32328
|
+
"No target languages configured for this project.",
|
|
32329
|
+
void 0,
|
|
32330
|
+
"no_target_languages"
|
|
32331
|
+
) : new UsageError(
|
|
32332
|
+
"Either --dst <locale> or --all must be provided."
|
|
32333
|
+
);
|
|
32334
|
+
}
|
|
32335
|
+
const outcomes2 = [];
|
|
32336
|
+
for (const dst of locales)
|
|
32337
|
+
outcomes2.push(await adoptLocale(ctx, src, dst));
|
|
32338
|
+
track("cli_translate_completed", {
|
|
32339
|
+
mode: "adopt",
|
|
32340
|
+
key_count: outcomes2.reduce((n, o) => n + o.adopted, 0),
|
|
32341
|
+
locale_count: outcomes2.length,
|
|
32342
|
+
job_count: 0,
|
|
32343
|
+
json: opts.json === true
|
|
32344
|
+
});
|
|
32345
|
+
return outcomes2;
|
|
32346
|
+
};
|
|
32347
|
+
if (opts.json) {
|
|
32348
|
+
jsonSuccess({ adopted: await run() });
|
|
32349
|
+
}
|
|
32350
|
+
const outcomes = await run();
|
|
32351
|
+
for (const o of outcomes) {
|
|
32352
|
+
console.log(
|
|
32353
|
+
`\u2713 ${o.dstLocale}: adopted ${o.adopted} untracked translation(s)` + (o.skipped > 0 ? `, ${o.skipped} already tracked` : "")
|
|
32090
32354
|
);
|
|
32091
32355
|
}
|
|
32092
|
-
const message = describeError(error62);
|
|
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
32356
|
}
|
|
32102
|
-
|
|
32103
|
-
|
|
32104
|
-
|
|
32105
|
-
|
|
32106
|
-
|
|
32107
|
-
|
|
32108
|
-
|
|
32109
|
-
|
|
32357
|
+
|
|
32358
|
+
// src/commands/translate/edit.tsx
|
|
32359
|
+
import path10 from "path";
|
|
32360
|
+
import { Spinner as Spinner14 } from "@inkjs/ui";
|
|
32361
|
+
import { Box as Box17, render as render16, Text as Text17 } from "ink";
|
|
32362
|
+
|
|
32363
|
+
// src/utils/locale-files.ts
|
|
32364
|
+
import path9 from "path";
|
|
32365
|
+
async function findLocaleFiles(baseDir) {
|
|
32366
|
+
const adapter = createAdapter("json");
|
|
32367
|
+
const result = await adapter.discover(baseDir);
|
|
32368
|
+
const localeFiles = /* @__PURE__ */ new Map();
|
|
32369
|
+
for (const [locale, files] of result.filesByLocale) {
|
|
32370
|
+
localeFiles.set(
|
|
32371
|
+
locale,
|
|
32372
|
+
files.map((f) => f.path)
|
|
32373
|
+
);
|
|
32374
|
+
}
|
|
32375
|
+
return localeFiles;
|
|
32110
32376
|
}
|
|
32111
|
-
function
|
|
32112
|
-
|
|
32113
|
-
if (
|
|
32114
|
-
|
|
32115
|
-
|
|
32116
|
-
|
|
32117
|
-
if (
|
|
32118
|
-
|
|
32119
|
-
console.error("");
|
|
32120
|
-
console.error(cli.stack);
|
|
32377
|
+
function resolveFileForKey(files, key, namespace) {
|
|
32378
|
+
if (files.length === 0) return null;
|
|
32379
|
+
if (files.length === 1) return files[0] ?? null;
|
|
32380
|
+
const ns = namespace ?? key.split(".")[0];
|
|
32381
|
+
for (const file2 of files) {
|
|
32382
|
+
const fileName = path9.parse(path9.basename(file2)).name;
|
|
32383
|
+
if (fileName === ns) {
|
|
32384
|
+
return file2;
|
|
32121
32385
|
}
|
|
32122
32386
|
}
|
|
32123
|
-
|
|
32124
|
-
process.exitCode = code;
|
|
32125
|
-
return code;
|
|
32387
|
+
return files[0] ?? null;
|
|
32126
32388
|
}
|
|
32127
|
-
function
|
|
32128
|
-
|
|
32129
|
-
|
|
32130
|
-
|
|
32131
|
-
|
|
32389
|
+
function resolveKeyInFile(key, targetFile, fileCount, namespace) {
|
|
32390
|
+
if (fileCount <= 1 || namespace) return key;
|
|
32391
|
+
const fileName = path9.parse(path9.basename(targetFile)).name;
|
|
32392
|
+
if (key.startsWith(`${fileName}.`)) {
|
|
32393
|
+
return key.slice(fileName.length + 1);
|
|
32394
|
+
}
|
|
32395
|
+
return key;
|
|
32132
32396
|
}
|
|
32133
32397
|
|
|
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
|
-
] })
|
|
32398
|
+
// src/commands/translate/edit.tsx
|
|
32399
|
+
import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
32400
|
+
function EditUI({ state }) {
|
|
32401
|
+
if (state.type === "editing") {
|
|
32402
|
+
return /* @__PURE__ */ jsxs17(Box17, { children: [
|
|
32403
|
+
/* @__PURE__ */ jsx17(Spinner14, {}),
|
|
32404
|
+
/* @__PURE__ */ jsx17(Text17, { children: " Editing translation\u2026" })
|
|
32161
32405
|
] });
|
|
32162
32406
|
}
|
|
32163
|
-
|
|
32164
|
-
|
|
32165
|
-
/* @__PURE__ */
|
|
32166
|
-
/* @__PURE__ */
|
|
32407
|
+
return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", children: [
|
|
32408
|
+
/* @__PURE__ */ jsxs17(Box17, { children: [
|
|
32409
|
+
/* @__PURE__ */ jsx17(Text17, { color: "green", children: "\u2713" }),
|
|
32410
|
+
/* @__PURE__ */ jsxs17(Text17, { children: [
|
|
32167
32411
|
" ",
|
|
32168
|
-
|
|
32169
|
-
|
|
32170
|
-
dstLocale,
|
|
32171
|
-
": Generating tasks..."
|
|
32412
|
+
"Updated ",
|
|
32413
|
+
/* @__PURE__ */ jsx17(Text17, { bold: true, children: state.key })
|
|
32172
32414
|
] })
|
|
32173
|
-
] })
|
|
32415
|
+
] }),
|
|
32416
|
+
/* @__PURE__ */ jsx17(Box17, { paddingLeft: 2, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: state.value }) }),
|
|
32417
|
+
/* @__PURE__ */ jsx17(Box17, { marginTop: 1, children: /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
|
|
32418
|
+
"File: ",
|
|
32419
|
+
state.filePath
|
|
32420
|
+
] }) })
|
|
32421
|
+
] });
|
|
32422
|
+
}
|
|
32423
|
+
async function performEdit(opts) {
|
|
32424
|
+
const baseDir = process.cwd();
|
|
32425
|
+
const config3 = readConfig(baseDir);
|
|
32426
|
+
if (!config3) throw noConfigError();
|
|
32427
|
+
const localeFiles = await findLocaleFiles(baseDir);
|
|
32428
|
+
const files = localeFiles.get(opts.locale);
|
|
32429
|
+
if (!files || files.length === 0) {
|
|
32430
|
+
throw new CliError(
|
|
32431
|
+
`No translation files found for locale "${opts.locale}".`,
|
|
32432
|
+
`Available locales: ${[...localeFiles.keys()].join(", ") || "none"}.`,
|
|
32433
|
+
"no_translation_files"
|
|
32434
|
+
);
|
|
32174
32435
|
}
|
|
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
|
-
] });
|
|
32436
|
+
const targetFile = resolveFileForKey(files, opts.key, opts.namespace);
|
|
32437
|
+
if (!targetFile) {
|
|
32438
|
+
throw new CliError(
|
|
32439
|
+
`Could not determine target file for key "${opts.key}" in locale "${opts.locale}".`,
|
|
32440
|
+
"Pass --namespace to say which file the key belongs to."
|
|
32441
|
+
);
|
|
32197
32442
|
}
|
|
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
|
-
] });
|
|
32443
|
+
const keyInFile = resolveKeyInFile(
|
|
32444
|
+
opts.key,
|
|
32445
|
+
targetFile,
|
|
32446
|
+
files.length,
|
|
32447
|
+
opts.namespace
|
|
32448
|
+
);
|
|
32449
|
+
const adapter = createAdapter("json");
|
|
32450
|
+
let existingData = {};
|
|
32451
|
+
try {
|
|
32452
|
+
const parsed = await adapter.parse(targetFile);
|
|
32453
|
+
existingData = parsed.data;
|
|
32454
|
+
} catch {
|
|
32220
32455
|
}
|
|
32221
|
-
|
|
32222
|
-
|
|
32223
|
-
|
|
32224
|
-
|
|
32225
|
-
|
|
32226
|
-
|
|
32227
|
-
|
|
32228
|
-
|
|
32229
|
-
|
|
32230
|
-
|
|
32231
|
-
|
|
32232
|
-
|
|
32233
|
-
|
|
32234
|
-
|
|
32456
|
+
const updatedData = setByPath(
|
|
32457
|
+
existingData,
|
|
32458
|
+
keyInFile,
|
|
32459
|
+
opts.value
|
|
32460
|
+
);
|
|
32461
|
+
await adapter.serialize(targetFile, updatedData, { merge: false });
|
|
32462
|
+
const relativePath = path10.relative(baseDir, targetFile);
|
|
32463
|
+
return { filePath: relativePath, key: opts.key, value: opts.value };
|
|
32464
|
+
}
|
|
32465
|
+
async function runEdit(opts) {
|
|
32466
|
+
if (opts.json) {
|
|
32467
|
+
const result2 = await performEdit(opts);
|
|
32468
|
+
jsonSuccess({
|
|
32469
|
+
file: result2.filePath,
|
|
32470
|
+
key: result2.key,
|
|
32471
|
+
value: result2.value
|
|
32472
|
+
});
|
|
32235
32473
|
}
|
|
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
|
-
] });
|
|
32474
|
+
const { unmount } = renderTransient(/* @__PURE__ */ jsx17(EditUI, { state: { type: "editing" } }));
|
|
32475
|
+
let result;
|
|
32476
|
+
try {
|
|
32477
|
+
result = await performEdit(opts);
|
|
32478
|
+
} finally {
|
|
32479
|
+
unmount();
|
|
32249
32480
|
}
|
|
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
|
-
] });
|
|
32481
|
+
render16(/* @__PURE__ */ jsx17(EditUI, { state: { type: "success", ...result } })).unmount();
|
|
32290
32482
|
}
|
|
32291
32483
|
|
|
32292
|
-
// src/commands/translate/translate
|
|
32293
|
-
|
|
32294
|
-
|
|
32295
|
-
|
|
32296
|
-
|
|
32297
|
-
|
|
32298
|
-
|
|
32299
|
-
|
|
32300
|
-
|
|
32484
|
+
// src/commands/translate/translate.tsx
|
|
32485
|
+
import { Spinner as Spinner15 } from "@inkjs/ui";
|
|
32486
|
+
import { Box as Box18, Text as Text18 } from "ink";
|
|
32487
|
+
import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
32488
|
+
function planOutput(plan) {
|
|
32489
|
+
return {
|
|
32490
|
+
...plan.summary,
|
|
32491
|
+
protectedKeys: plan.protectedKeys,
|
|
32492
|
+
heldKeys: plan.heldKeys,
|
|
32493
|
+
untrackedKeys: plan.untrackedKeys.map((k) => k.key)
|
|
32494
|
+
};
|
|
32301
32495
|
}
|
|
32302
|
-
function
|
|
32303
|
-
return
|
|
32496
|
+
function fullKey5(namespace, key) {
|
|
32497
|
+
return namespace ? `${namespace}.${key}` : key;
|
|
32304
32498
|
}
|
|
32305
|
-
function
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
|
|
32309
|
-
|
|
32310
|
-
|
|
32499
|
+
function characters(text) {
|
|
32500
|
+
return Array.from(text).length;
|
|
32501
|
+
}
|
|
32502
|
+
function targetLocales(ctx, opts) {
|
|
32503
|
+
if (opts.dst) return [opts.dst];
|
|
32504
|
+
const locales = ctx.project.targetLanguages;
|
|
32505
|
+
if (locales.length === 0) {
|
|
32506
|
+
throw new CliError(
|
|
32507
|
+
"No target languages configured.",
|
|
32508
|
+
`Configure them in the project settings at ${env.TOLKBOT_WEB_URL}/projects/${ctx.project.slug}/settings.`,
|
|
32509
|
+
"no_target_languages"
|
|
32311
32510
|
);
|
|
32312
32511
|
}
|
|
32313
|
-
|
|
32314
|
-
|
|
32315
|
-
|
|
32316
|
-
|
|
32317
|
-
|
|
32318
|
-
|
|
32512
|
+
return locales;
|
|
32513
|
+
}
|
|
32514
|
+
async function runTranslate(opts) {
|
|
32515
|
+
const ctx = await loadPlanContext(process.cwd());
|
|
32516
|
+
const srcLocale = opts.src ?? ctx.project.baseLanguage;
|
|
32517
|
+
const dstLocales = targetLocales(ctx, opts);
|
|
32518
|
+
if (opts.dryRun) {
|
|
32519
|
+
await runDryRun(ctx, srcLocale, dstLocales, opts);
|
|
32520
|
+
return;
|
|
32319
32521
|
}
|
|
32320
|
-
|
|
32522
|
+
await runLanguages(ctx, srcLocale, dstLocales, opts);
|
|
32321
32523
|
}
|
|
32322
|
-
var
|
|
32323
|
-
|
|
32324
|
-
|
|
32325
|
-
|
|
32326
|
-
|
|
32327
|
-
|
|
32328
|
-
|
|
32329
|
-
|
|
32330
|
-
|
|
32331
|
-
|
|
32332
|
-
|
|
32524
|
+
var NO_ESTIMATE = "No credit estimate exists for this project yet. Keys and characters are what a run would send.";
|
|
32525
|
+
async function runDryRun(ctx, srcLocale, dstLocales, opts) {
|
|
32526
|
+
const languages = [];
|
|
32527
|
+
const progress = renderTransient(
|
|
32528
|
+
/* @__PURE__ */ jsx18(Working, { text: `Planning ${dstLocales.join(", ")}\u2026` })
|
|
32529
|
+
);
|
|
32530
|
+
try {
|
|
32531
|
+
for (const dstLocale of dstLocales) {
|
|
32532
|
+
const { plan } = await generateTasks(ctx, srcLocale, dstLocale, {
|
|
32533
|
+
retranslateUntracked: opts.retranslateUntracked
|
|
32534
|
+
});
|
|
32535
|
+
languages.push({
|
|
32536
|
+
dstLocale,
|
|
32537
|
+
keys: plan.tasks.length,
|
|
32538
|
+
characters: plan.tasks.reduce(
|
|
32539
|
+
(n, task) => n + characters(task.src),
|
|
32540
|
+
0
|
|
32541
|
+
),
|
|
32542
|
+
tasks: plan.tasks.map((task) => ({
|
|
32543
|
+
key: fullKey5(task.namespace, task.key),
|
|
32544
|
+
path: task.path,
|
|
32545
|
+
reason: task.reason,
|
|
32546
|
+
...opts.verbose ? { src: task.src } : {}
|
|
32547
|
+
})),
|
|
32548
|
+
plan
|
|
32549
|
+
});
|
|
32550
|
+
}
|
|
32551
|
+
} finally {
|
|
32552
|
+
progress.unmount();
|
|
32333
32553
|
}
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32338
|
-
const
|
|
32339
|
-
|
|
32340
|
-
|
|
32341
|
-
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
|
|
32348
|
-
|
|
32554
|
+
const total = {
|
|
32555
|
+
keys: languages.reduce((n, l) => n + l.keys, 0),
|
|
32556
|
+
characters: languages.reduce((n, l) => n + l.characters, 0)
|
|
32557
|
+
};
|
|
32558
|
+
const balance = await readCreditBalance(ctx);
|
|
32559
|
+
track("cli_translate_completed", {
|
|
32560
|
+
mode: "plan",
|
|
32561
|
+
key_count: total.keys,
|
|
32562
|
+
locale_count: languages.length,
|
|
32563
|
+
job_count: 0,
|
|
32564
|
+
json: opts.json ?? false
|
|
32565
|
+
});
|
|
32566
|
+
if (opts.json) {
|
|
32567
|
+
jsonSuccess({
|
|
32568
|
+
dryRun: true,
|
|
32569
|
+
srcLocale,
|
|
32570
|
+
languages: languages.map(({ plan, ...language }) => ({
|
|
32571
|
+
...language,
|
|
32572
|
+
plan: planOutput(plan)
|
|
32573
|
+
})),
|
|
32574
|
+
total,
|
|
32575
|
+
estimate: null,
|
|
32576
|
+
estimateNote: NO_ESTIMATE,
|
|
32577
|
+
credits: { balance }
|
|
32578
|
+
});
|
|
32579
|
+
}
|
|
32580
|
+
for (const language of languages) {
|
|
32581
|
+
const head = `${srcLocale} \u2192 ${language.dstLocale}`;
|
|
32582
|
+
if (language.keys === 0) {
|
|
32583
|
+
console.log(`\u2713 ${head}: up to date`);
|
|
32584
|
+
} else {
|
|
32585
|
+
console.log(
|
|
32586
|
+
`\u2022 ${head}: ${language.keys} key(s) to translate, ${language.characters} characters`
|
|
32349
32587
|
);
|
|
32350
32588
|
}
|
|
32351
|
-
|
|
32352
|
-
|
|
32353
|
-
|
|
32354
|
-
|
|
32589
|
+
for (const line of describePlan(language.plan)) {
|
|
32590
|
+
console.log(` ${line}`);
|
|
32591
|
+
}
|
|
32592
|
+
if (opts.verbose) {
|
|
32593
|
+
for (const task of language.tasks) {
|
|
32594
|
+
console.log(` ${task.key}: ${JSON.stringify(task.src)}`);
|
|
32595
|
+
}
|
|
32355
32596
|
}
|
|
32356
|
-
await sleep(pollMs);
|
|
32357
32597
|
}
|
|
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 }
|
|
32598
|
+
console.log(
|
|
32599
|
+
`
|
|
32600
|
+
In all: ${total.keys} key(s), ${total.characters} characters.` + (balance === null ? "" : ` Credit balance: ${balance}.`)
|
|
32392
32601
|
);
|
|
32393
|
-
|
|
32602
|
+
console.log(NO_ESTIMATE);
|
|
32603
|
+
console.log("Nothing was started. Run without --dry-run to translate.");
|
|
32394
32604
|
}
|
|
32395
|
-
|
|
32396
|
-
|
|
32397
|
-
|
|
32398
|
-
|
|
32399
|
-
|
|
32605
|
+
function Working({ text }) {
|
|
32606
|
+
return /* @__PURE__ */ jsxs18(Box18, { children: [
|
|
32607
|
+
/* @__PURE__ */ jsx18(Spinner15, {}),
|
|
32608
|
+
/* @__PURE__ */ jsxs18(Text18, { children: [
|
|
32609
|
+
" ",
|
|
32610
|
+
text
|
|
32611
|
+
] })
|
|
32612
|
+
] });
|
|
32613
|
+
}
|
|
32614
|
+
function progressText(dstLocale, summary) {
|
|
32615
|
+
if (!summary) return `${dstLocale}: starting\u2026`;
|
|
32616
|
+
const done = summary.completed + summary.failed;
|
|
32617
|
+
return `${dstLocale}: translating ${done}/${summary.total}\u2026`;
|
|
32618
|
+
}
|
|
32619
|
+
function describeOutcome(outcome) {
|
|
32620
|
+
const { dstLocale, status, keys, written } = outcome;
|
|
32621
|
+
const wrote = written ? `, ${written.keys} written to ${written.files} file(s)` : "";
|
|
32622
|
+
const notApplied = outcome.notApplied.length > 0 ? `, ${outcome.notApplied.length} not applied` : "";
|
|
32623
|
+
switch (status) {
|
|
32624
|
+
case "up-to-date":
|
|
32625
|
+
return `\u2713 ${dstLocale}: up to date`;
|
|
32626
|
+
case "started":
|
|
32627
|
+
return `\u2192 ${dstLocale}: started job ${outcome.jobId} with ${keys} key(s)`;
|
|
32628
|
+
case "succeeded":
|
|
32629
|
+
return `\u2713 ${dstLocale}: ${outcome.completed} translated${wrote}${notApplied}`;
|
|
32630
|
+
case "partial":
|
|
32631
|
+
return `\u26A0 ${dstLocale}: ${outcome.completed} of ${keys} translated, ${outcome.failed} failed${wrote}${notApplied}`;
|
|
32632
|
+
case "failed":
|
|
32633
|
+
return `\u2717 ${dstLocale}: the job failed (${outcome.jobId})${wrote}${notApplied}`;
|
|
32634
|
+
case "cancelled":
|
|
32635
|
+
return `\u2717 ${dstLocale}: the job was cancelled (${outcome.jobId})`;
|
|
32636
|
+
case "timed-out":
|
|
32637
|
+
return `\u2717 ${dstLocale}: ${outcome.error}`;
|
|
32638
|
+
case "not-started":
|
|
32639
|
+
return `\u2717 ${dstLocale}: not started. ${outcome.error}`;
|
|
32640
|
+
case "pending":
|
|
32641
|
+
case "running":
|
|
32642
|
+
return `\u2026 ${dstLocale}: ${status}`;
|
|
32643
|
+
}
|
|
32644
|
+
}
|
|
32645
|
+
async function runLanguages(ctx, srcLocale, dstLocales, opts) {
|
|
32400
32646
|
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."
|
|
32647
|
+
const json2 = opts.json ?? false;
|
|
32648
|
+
const outcomes = [];
|
|
32649
|
+
let failure2 = null;
|
|
32650
|
+
let stopped = null;
|
|
32651
|
+
const report = (outcome) => {
|
|
32652
|
+
outcomes.push(outcome);
|
|
32653
|
+
if (json2) return;
|
|
32654
|
+
console.log(describeOutcome(outcome));
|
|
32655
|
+
for (const line of describeNotApplied(outcome.notApplied)) {
|
|
32656
|
+
console.log(` ${line}`);
|
|
32657
|
+
}
|
|
32658
|
+
};
|
|
32659
|
+
for (const dstLocale of dstLocales) {
|
|
32660
|
+
const progress = renderTransient(
|
|
32661
|
+
/* @__PURE__ */ jsx18(Working, { text: `${dstLocale}: planning\u2026` })
|
|
32418
32662
|
);
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
|
|
32423
|
-
|
|
32424
|
-
|
|
32425
|
-
|
|
32426
|
-
const
|
|
32427
|
-
|
|
32663
|
+
try {
|
|
32664
|
+
const { tasks, plan } = await generateTasks(
|
|
32665
|
+
ctx,
|
|
32666
|
+
srcLocale,
|
|
32667
|
+
dstLocale,
|
|
32668
|
+
{ retranslateUntracked: opts.retranslateUntracked }
|
|
32669
|
+
);
|
|
32670
|
+
const base = {
|
|
32671
|
+
dstLocale,
|
|
32672
|
+
status: "up-to-date",
|
|
32673
|
+
jobId: null,
|
|
32674
|
+
keys: tasks.length,
|
|
32675
|
+
completed: null,
|
|
32676
|
+
failed: null,
|
|
32677
|
+
written: null,
|
|
32678
|
+
notApplied: [],
|
|
32679
|
+
error: null,
|
|
32680
|
+
plan: planOutput(plan)
|
|
32681
|
+
};
|
|
32682
|
+
const leftOut = describeLeftOut(plan);
|
|
32683
|
+
if (stopped !== null) {
|
|
32684
|
+
progress.unmount();
|
|
32685
|
+
report({ ...base, status: "not-started", error: stopped });
|
|
32686
|
+
continue;
|
|
32687
|
+
}
|
|
32428
32688
|
if (tasks.length === 0) {
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
plan: summary
|
|
32433
|
-
});
|
|
32689
|
+
progress.unmount();
|
|
32690
|
+
report(base);
|
|
32691
|
+
printNotes(json2, leftOut);
|
|
32434
32692
|
continue;
|
|
32435
32693
|
}
|
|
32694
|
+
progress.rerender(/* @__PURE__ */ jsx18(Working, { text: progressText(dstLocale, null) }));
|
|
32695
|
+
let jobId;
|
|
32436
32696
|
try {
|
|
32437
|
-
|
|
32438
|
-
projectId,
|
|
32697
|
+
jobId = await startTranslationJob({
|
|
32698
|
+
projectId: project._id,
|
|
32439
32699
|
organizationId: project.organizationId,
|
|
32440
32700
|
framework,
|
|
32441
32701
|
srcLocale,
|
|
32442
32702
|
dstLocale,
|
|
32443
32703
|
tasks
|
|
32444
32704
|
});
|
|
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
32705
|
} catch (error62) {
|
|
32457
|
-
|
|
32458
|
-
|
|
32459
|
-
|
|
32460
|
-
|
|
32461
|
-
|
|
32462
|
-
|
|
32463
|
-
|
|
32706
|
+
const refused = toCliError(error62);
|
|
32707
|
+
failure2 ??= refused;
|
|
32708
|
+
progress.unmount();
|
|
32709
|
+
report({
|
|
32710
|
+
...base,
|
|
32711
|
+
status: "not-started",
|
|
32712
|
+
error: refused.baseMessage
|
|
32464
32713
|
});
|
|
32465
|
-
|
|
32466
|
-
|
|
32714
|
+
if (exitCodeFor(refused.code) === EXIT_CODES.billing) {
|
|
32715
|
+
stopped = `TolkBot refused the ${dstLocale} job: ${refused.baseMessage}`;
|
|
32716
|
+
}
|
|
32717
|
+
continue;
|
|
32467
32718
|
}
|
|
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
|
-
});
|
|
32719
|
+
if (!opts.wait) {
|
|
32720
|
+
progress.unmount();
|
|
32721
|
+
report({ ...base, status: "started", jobId });
|
|
32722
|
+
printNotes(json2, leftOut);
|
|
32521
32723
|
continue;
|
|
32522
32724
|
}
|
|
32523
|
-
|
|
32524
|
-
status: "translating",
|
|
32525
|
-
taskCount: tasks.length,
|
|
32526
|
-
notes
|
|
32527
|
-
});
|
|
32528
|
-
let outcome;
|
|
32725
|
+
let summary;
|
|
32529
32726
|
try {
|
|
32530
|
-
|
|
32531
|
-
|
|
32532
|
-
|
|
32533
|
-
|
|
32534
|
-
|
|
32535
|
-
|
|
32536
|
-
|
|
32537
|
-
onProgress: (progress) => {
|
|
32538
|
-
updateJob(dstLocale, { progress });
|
|
32539
|
-
}
|
|
32540
|
-
});
|
|
32727
|
+
summary = await waitForTranslationJob(
|
|
32728
|
+
project.organizationId,
|
|
32729
|
+
jobId,
|
|
32730
|
+
(latest) => progress.rerender(
|
|
32731
|
+
/* @__PURE__ */ jsx18(Working, { text: progressText(dstLocale, latest) })
|
|
32732
|
+
)
|
|
32733
|
+
);
|
|
32541
32734
|
} catch (error62) {
|
|
32542
32735
|
if (!(error62 instanceof JobWaitTimeoutError)) throw error62;
|
|
32543
|
-
updateJob(dstLocale, {
|
|
32544
|
-
status: "error",
|
|
32545
|
-
jobId: error62.jobId,
|
|
32546
|
-
error: error62.message
|
|
32547
|
-
});
|
|
32548
32736
|
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,
|
|
32737
|
+
progress.unmount();
|
|
32738
|
+
report({
|
|
32739
|
+
...base,
|
|
32740
|
+
status: "timed-out",
|
|
32565
32741
|
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
|
|
32742
|
+
error: error62.baseMessage
|
|
32576
32743
|
});
|
|
32744
|
+
stopped = `The CLI stopped waiting for the ${dstLocale} job.`;
|
|
32745
|
+
continue;
|
|
32746
|
+
}
|
|
32747
|
+
const finished = {
|
|
32748
|
+
...base,
|
|
32749
|
+
status: summary.status,
|
|
32750
|
+
jobId,
|
|
32751
|
+
completed: summary.completed,
|
|
32752
|
+
failed: summary.failed
|
|
32753
|
+
};
|
|
32754
|
+
if (summary.status !== "cancelled") {
|
|
32755
|
+
progress.rerender(
|
|
32756
|
+
/* @__PURE__ */ jsx18(Working, { text: `${dstLocale}: writing the files\u2026` })
|
|
32757
|
+
);
|
|
32758
|
+
const applied = await applyJob(ctx, jobId);
|
|
32759
|
+
finished.written = { files: applied.files, keys: applied.keys };
|
|
32760
|
+
finished.notApplied = applied.notApplied;
|
|
32761
|
+
}
|
|
32762
|
+
const unsuccessful = unsuccessfulJobError(jobId, summary.status);
|
|
32763
|
+
if (unsuccessful) {
|
|
32764
|
+
failure2 ??= unsuccessful;
|
|
32765
|
+
finished.error = unsuccessful.baseMessage;
|
|
32577
32766
|
}
|
|
32767
|
+
progress.unmount();
|
|
32768
|
+
report(finished);
|
|
32769
|
+
printNotes(json2, leftOut);
|
|
32770
|
+
if (summary.status === "cancelled") {
|
|
32771
|
+
stopped = `The ${dstLocale} job was cancelled.`;
|
|
32772
|
+
}
|
|
32773
|
+
} finally {
|
|
32774
|
+
progress.unmount();
|
|
32578
32775
|
}
|
|
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
32776
|
}
|
|
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
|
|
32777
|
+
track("cli_translate_completed", {
|
|
32778
|
+
mode: "run",
|
|
32779
|
+
key_count: outcomes.reduce((n, o) => n + o.keys, 0),
|
|
32780
|
+
locale_count: outcomes.length,
|
|
32781
|
+
job_count: outcomes.filter((o) => o.jobId).length,
|
|
32782
|
+
json: json2
|
|
32672
32783
|
});
|
|
32673
|
-
|
|
32674
|
-
|
|
32675
|
-
|
|
32676
|
-
|
|
32677
|
-
|
|
32784
|
+
const payload = { srcLocale, languages: outcomes };
|
|
32785
|
+
if (json2) {
|
|
32786
|
+
if (!failure2) jsonSuccess(payload);
|
|
32787
|
+
jsonResult(payload, 1);
|
|
32788
|
+
fail(failure2);
|
|
32789
|
+
}
|
|
32790
|
+
if (!opts.wait && outcomes.some((o) => o.status === "started")) {
|
|
32791
|
+
console.log(
|
|
32792
|
+
"\nThe jobs keep running. Follow them with 'tolkbot jobs list' and write them into the files with 'tolkbot jobs apply'."
|
|
32678
32793
|
);
|
|
32679
32794
|
}
|
|
32680
|
-
|
|
32795
|
+
if (failure2) fail(failure2);
|
|
32681
32796
|
}
|
|
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);
|
|
32797
|
+
function printNotes(json2, notes) {
|
|
32798
|
+
if (json2) return;
|
|
32799
|
+
for (const note of notes) console.log(` ${note}`);
|
|
32800
|
+
}
|
|
32801
|
+
function checkTranslateOptions(opts) {
|
|
32802
|
+
if (!opts.dst && !opts.all) {
|
|
32803
|
+
throw new UsageError(
|
|
32804
|
+
"Either --dst <locale> or --all must be provided.",
|
|
32805
|
+
"Run 'tolkbot translate --help'."
|
|
32806
|
+
);
|
|
32696
32807
|
}
|
|
32697
|
-
|
|
32698
|
-
|
|
32699
|
-
);
|
|
32700
|
-
let summary;
|
|
32701
|
-
try {
|
|
32702
|
-
summary = await fetchJobSummary(opts.jobId);
|
|
32703
|
-
} finally {
|
|
32704
|
-
unmount();
|
|
32808
|
+
if (opts.dst && opts.all) {
|
|
32809
|
+
throw new UsageError("Cannot use both --dst and --all.");
|
|
32705
32810
|
}
|
|
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);
|
|
32811
|
+
if (opts.dryRun && !opts.wait) {
|
|
32812
|
+
throw new UsageError(
|
|
32813
|
+
"Cannot use --dry-run with --no-wait: a dry run starts nothing."
|
|
32814
|
+
);
|
|
32721
32815
|
}
|
|
32722
32816
|
}
|
|
32723
32817
|
|
|
32724
32818
|
// src/commands/translate/index.ts
|
|
32725
|
-
var translateCommand = new Command5("translate").description(
|
|
32726
|
-
"Translate with TolkBot"
|
|
32727
|
-
);
|
|
32728
32819
|
var RETRANSLATE_UNTRACKED_HELP = "Also retranslate translations TolkBot has no record of (kept by default)";
|
|
32729
|
-
translateCommand
|
|
32730
|
-
"
|
|
32820
|
+
var translateCommand = new Command7("translate").description(
|
|
32821
|
+
"Translate what is missing or changed, and write the results into your locale files"
|
|
32731
32822
|
).option(
|
|
32732
32823
|
"--src <locale>",
|
|
32733
32824
|
"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) {
|
|
32825
|
+
).option("--dst <locale>", "Destination locale").option("--all", "Every target language").option("--dry-run", "Print what a run would translate, and start nothing").option(
|
|
32826
|
+
"--no-wait",
|
|
32827
|
+
"Start the jobs and print their IDs without waiting; apply them later with 'tolkbot jobs apply'"
|
|
32828
|
+
).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) => {
|
|
32829
|
+
const [word] = command.args;
|
|
32830
|
+
if (word !== void 0) {
|
|
32759
32831
|
throw new UsageError(
|
|
32760
|
-
|
|
32832
|
+
`unknown command 'translate ${word}'`,
|
|
32833
|
+
"Run 'tolkbot translate --help'. Jobs are under 'tolkbot jobs'."
|
|
32761
32834
|
);
|
|
32762
32835
|
}
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32836
|
+
const schema = z9.object({
|
|
32837
|
+
src: z9.string().min(1).optional(),
|
|
32838
|
+
dst: z9.string().min(1).optional(),
|
|
32839
|
+
all: z9.boolean().optional(),
|
|
32840
|
+
dryRun: z9.boolean().optional(),
|
|
32841
|
+
wait: z9.boolean(),
|
|
32842
|
+
retranslateUntracked: z9.boolean().optional(),
|
|
32843
|
+
verbose: z9.boolean().optional(),
|
|
32844
|
+
json: z9.boolean().optional()
|
|
32845
|
+
});
|
|
32846
|
+
const parsed = validateOrExit(schema, opts);
|
|
32847
|
+
checkTranslateOptions(parsed);
|
|
32766
32848
|
await runTranslate(parsed);
|
|
32767
32849
|
});
|
|
32768
32850
|
translateCommand.command("adopt").description(
|
|
@@ -32771,11 +32853,11 @@ translateCommand.command("adopt").description(
|
|
|
32771
32853
|
"--src <locale>",
|
|
32772
32854
|
"Source locale (defaults to the project's base language)"
|
|
32773
32855
|
).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:
|
|
32856
|
+
const schema = z9.object({
|
|
32857
|
+
src: z9.string().min(1).optional(),
|
|
32858
|
+
dst: z9.string().min(1).optional(),
|
|
32859
|
+
all: z9.boolean().optional(),
|
|
32860
|
+
json: z9.boolean().optional()
|
|
32779
32861
|
});
|
|
32780
32862
|
const parsed = validateOrExit(schema, opts);
|
|
32781
32863
|
if (parsed.dst && parsed.all) {
|
|
@@ -32783,46 +32865,20 @@ translateCommand.command("adopt").description(
|
|
|
32783
32865
|
}
|
|
32784
32866
|
await runAdopt(parsed);
|
|
32785
32867
|
});
|
|
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
32868
|
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:
|
|
32869
|
+
const schema = z9.object({
|
|
32870
|
+
key: z9.string().min(1),
|
|
32871
|
+
locale: z9.string().min(1),
|
|
32872
|
+
value: z9.string().min(1),
|
|
32873
|
+
namespace: z9.string().optional(),
|
|
32874
|
+
json: z9.boolean().optional()
|
|
32811
32875
|
});
|
|
32812
32876
|
const parsed = validateOrExit(schema, opts);
|
|
32813
32877
|
await runEdit(parsed);
|
|
32814
32878
|
});
|
|
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
32879
|
|
|
32824
32880
|
// src/commands/validate.tsx
|
|
32825
|
-
import { Box as
|
|
32881
|
+
import { Box as Box19, render as render17, Text as Text19 } from "ink";
|
|
32826
32882
|
|
|
32827
32883
|
// ../ai/src/validators/placeholders.ts
|
|
32828
32884
|
var IDENTIFIER = /^[A-Za-z0-9_$][A-Za-z0-9_$.[\]-]*$/;
|
|
@@ -33915,7 +33971,7 @@ function finalize2(issues, stats, strict) {
|
|
|
33915
33971
|
}
|
|
33916
33972
|
|
|
33917
33973
|
// src/commands/validate.tsx
|
|
33918
|
-
import { jsx as
|
|
33974
|
+
import { jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
33919
33975
|
var LEVEL_COLOR = {
|
|
33920
33976
|
error: "red",
|
|
33921
33977
|
warning: "yellow",
|
|
@@ -33924,12 +33980,12 @@ var LEVEL_COLOR = {
|
|
|
33924
33980
|
var GUTTER = 9;
|
|
33925
33981
|
function IssueLine({ issue: issue2 }) {
|
|
33926
33982
|
const location = [issue2.file, issue2.key].filter(Boolean).join(" \xB7 ");
|
|
33927
|
-
return /* @__PURE__ */
|
|
33928
|
-
/* @__PURE__ */
|
|
33929
|
-
/* @__PURE__ */
|
|
33930
|
-
/* @__PURE__ */
|
|
33983
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
33984
|
+
/* @__PURE__ */ jsxs19(Box19, { children: [
|
|
33985
|
+
/* @__PURE__ */ jsx19(Box19, { width: GUTTER, flexShrink: 0, children: /* @__PURE__ */ jsx19(Text19, { color: LEVEL_COLOR[issue2.level], children: issue2.level }) }),
|
|
33986
|
+
/* @__PURE__ */ jsx19(Box19, { flexGrow: 1, children: /* @__PURE__ */ jsx19(Text19, { children: issue2.message }) })
|
|
33931
33987
|
] }),
|
|
33932
|
-
/* @__PURE__ */
|
|
33988
|
+
/* @__PURE__ */ jsx19(Box19, { marginLeft: GUTTER, children: /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
33933
33989
|
issue2.rule,
|
|
33934
33990
|
location ? ` \u2014 ${location}` : ""
|
|
33935
33991
|
] }) })
|
|
@@ -33938,20 +33994,20 @@ function IssueLine({ issue: issue2 }) {
|
|
|
33938
33994
|
function ReportUI({ report }) {
|
|
33939
33995
|
const { counts, stats } = report;
|
|
33940
33996
|
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__ */
|
|
33997
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
|
|
33998
|
+
/* @__PURE__ */ jsxs19(Box19, { children: [
|
|
33999
|
+
/* @__PURE__ */ jsx19(Text19, { color: report.valid ? "green" : "red", children: report.valid ? "\u2713" : "\u2717" }),
|
|
34000
|
+
/* @__PURE__ */ jsxs19(Text19, { children: [
|
|
33945
34001
|
" Validation ",
|
|
33946
34002
|
report.valid ? "passed" : "failed"
|
|
33947
34003
|
] }),
|
|
33948
|
-
summary ? /* @__PURE__ */
|
|
34004
|
+
summary ? /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
33949
34005
|
" \u2014 ",
|
|
33950
34006
|
summary
|
|
33951
34007
|
] }) : null
|
|
33952
34008
|
] }),
|
|
33953
|
-
stats.locales.length > 0 && /* @__PURE__ */
|
|
33954
|
-
/* @__PURE__ */
|
|
34009
|
+
stats.locales.length > 0 && /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, flexDirection: "column", children: [
|
|
34010
|
+
/* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
33955
34011
|
stats.filesChecked,
|
|
33956
34012
|
" ",
|
|
33957
34013
|
stats.format,
|
|
@@ -33960,18 +34016,18 @@ function ReportUI({ report }) {
|
|
|
33960
34016
|
stats.totalKeys,
|
|
33961
34017
|
" keys"
|
|
33962
34018
|
] }),
|
|
33963
|
-
/* @__PURE__ */
|
|
34019
|
+
/* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
33964
34020
|
"Locales: ",
|
|
33965
34021
|
stats.locales.join(", "),
|
|
33966
34022
|
stats.baseLocale ? ` (base: ${stats.baseLocale})` : ""
|
|
33967
34023
|
] }),
|
|
33968
|
-
stats.pattern && /* @__PURE__ */
|
|
34024
|
+
stats.pattern && /* @__PURE__ */ jsxs19(Text19, { dimColor: true, children: [
|
|
33969
34025
|
"Pattern: ",
|
|
33970
34026
|
stats.pattern
|
|
33971
34027
|
] })
|
|
33972
34028
|
] }),
|
|
33973
|
-
report.issues.length > 0 && /* @__PURE__ */
|
|
33974
|
-
!report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */
|
|
34029
|
+
report.issues.length > 0 && /* @__PURE__ */ jsx19(Box19, { marginTop: 1, flexDirection: "column", gap: 1, children: report.issues.map((issue2, idx) => /* @__PURE__ */ jsx19(IssueLine, { issue: issue2 }, idx)) }),
|
|
34030
|
+
!report.strict && counts.warning > 0 && report.valid && /* @__PURE__ */ jsx19(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "Warnings did not fail this run. Use --strict to treat them as errors." }) })
|
|
33975
34031
|
] });
|
|
33976
34032
|
}
|
|
33977
34033
|
function emit(report, json2) {
|
|
@@ -33991,7 +34047,7 @@ function emit(report, json2) {
|
|
|
33991
34047
|
if (!report.valid) fail(validationFailure(report));
|
|
33992
34048
|
return;
|
|
33993
34049
|
}
|
|
33994
|
-
|
|
34050
|
+
render17(/* @__PURE__ */ jsx19(ReportUI, { report })).unmount();
|
|
33995
34051
|
process.exitCode = exitCode;
|
|
33996
34052
|
}
|
|
33997
34053
|
function validationFailure(report) {
|
|
@@ -34050,12 +34106,9 @@ function commandPath(command) {
|
|
|
34050
34106
|
return names.join(" ");
|
|
34051
34107
|
}
|
|
34052
34108
|
function buildProgram() {
|
|
34053
|
-
const program = new
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
);
|
|
34057
|
-
program.hook("preAction", (thisCommand, actionCommand) => {
|
|
34058
|
-
const { token } = thisCommand.opts();
|
|
34109
|
+
const program = new Command8().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();
|
|
34110
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
34111
|
+
const { token } = actionCommand.optsWithGlobals();
|
|
34059
34112
|
setTokenOverride(token);
|
|
34060
34113
|
resolveJsonOption(actionCommand);
|
|
34061
34114
|
beginCommand(commandPath(actionCommand), version);
|
|
@@ -34081,11 +34134,14 @@ function buildProgram() {
|
|
|
34081
34134
|
);
|
|
34082
34135
|
program.addCommand(authCommand);
|
|
34083
34136
|
program.addCommand(glossaryCommand);
|
|
34137
|
+
program.addCommand(jobsCommand);
|
|
34084
34138
|
program.addCommand(learningsCommand);
|
|
34139
|
+
program.addCommand(statusCommand);
|
|
34085
34140
|
program.addCommand(telemetryCommand);
|
|
34086
34141
|
program.addCommand(translateCommand);
|
|
34087
34142
|
program.addHelpText("after", PROGRAM_HELP_FOOTER);
|
|
34088
34143
|
forEachCommand(program, (command) => {
|
|
34144
|
+
if (command !== program) addTokenOption(command);
|
|
34089
34145
|
addNoJsonOption(command);
|
|
34090
34146
|
reportUsageErrors(command);
|
|
34091
34147
|
});
|
|
@@ -34095,6 +34151,11 @@ function forEachCommand(command, visit2) {
|
|
|
34095
34151
|
visit2(command);
|
|
34096
34152
|
for (const sub of command.commands) forEachCommand(sub, visit2);
|
|
34097
34153
|
}
|
|
34154
|
+
var TOKEN_HELP = `Auth token to use for this invocation (overrides $${TOKEN_ENV_VAR} and the stored token)`;
|
|
34155
|
+
function addTokenOption(command) {
|
|
34156
|
+
if (command.options.some((o) => o.long === "--token")) return;
|
|
34157
|
+
command.addOption(new Option("--token <token>", TOKEN_HELP).hideHelp());
|
|
34158
|
+
}
|
|
34098
34159
|
function jsonOptionOf(command) {
|
|
34099
34160
|
return command.options.find((option) => option.long === "--json");
|
|
34100
34161
|
}
|
|
@@ -34133,8 +34194,10 @@ var PROGRAM_HELP_FOOTER = `
|
|
|
34133
34194
|
Examples:
|
|
34134
34195
|
$ tolkbot auth login
|
|
34135
34196
|
$ tolkbot init
|
|
34136
|
-
$ tolkbot
|
|
34137
|
-
$ tolkbot translate
|
|
34197
|
+
$ tolkbot status
|
|
34198
|
+
$ tolkbot translate --all
|
|
34199
|
+
$ tolkbot translate --all --dry-run
|
|
34200
|
+
$ tolkbot jobs list
|
|
34138
34201
|
$ tolkbot validate
|
|
34139
34202
|
|
|
34140
34203
|
Without a terminal (an agent, CI), nothing prompts: commands take their
|