@messagevisor/catalog 0.5.0 → 0.7.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/lib/node/index.js CHANGED
@@ -100,6 +100,95 @@ var fs = __importStar(require("fs"));
100
100
  var http = __importStar(require("http"));
101
101
  var path = __importStar(require("path"));
102
102
  var formatExamplePreview_1 = require("./formatExamplePreview");
103
+ var CLI_FORMAT_GREEN = "\x1b[32m%s\x1b[0m";
104
+ var CLI_FORMAT_DIM = "\x1b[2m%s\x1b[0m";
105
+ var CLI_FORMAT_BOLD = "\x1b[1m%s\x1b[0m";
106
+ function colorize(value, colorCode) {
107
+ return "\u001B[".concat(colorCode, "m").concat(value, "\u001B[0m");
108
+ }
109
+ function prettyDuration(diffInMs) {
110
+ var diff = Math.abs(diffInMs);
111
+ if (diff === 0) {
112
+ return "0ms";
113
+ }
114
+ var ms = diff % 1000;
115
+ diff = (diff - ms) / 1000;
116
+ var secs = diff % 60;
117
+ diff = (diff - secs) / 60;
118
+ var mins = diff % 60;
119
+ var hrs = (diff - mins) / 60;
120
+ var result = "";
121
+ if (hrs) {
122
+ result += " ".concat(hrs, "h");
123
+ }
124
+ if (mins) {
125
+ result += " ".concat(mins, "m");
126
+ }
127
+ if (secs) {
128
+ result += " ".concat(secs, "s");
129
+ }
130
+ if (ms) {
131
+ result += " ".concat(ms, "ms");
132
+ }
133
+ return result.trim();
134
+ }
135
+ function pluralize(count, singular, plural) {
136
+ if (plural === void 0) { plural = "".concat(singular, "s"); }
137
+ return "".concat(count, " ").concat(count === 1 ? singular : plural);
138
+ }
139
+ function formatCatalogPath(rootDirectoryPath, filePath) {
140
+ var relativePath = path.relative(rootDirectoryPath, filePath);
141
+ if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
142
+ return relativePath;
143
+ }
144
+ return filePath;
145
+ }
146
+ var CatalogProgressReporter = /** @class */ (function () {
147
+ function CatalogProgressReporter(rootDirectoryPath, outputDirectoryPath) {
148
+ this.rootDirectoryPath = rootDirectoryPath;
149
+ this.outputDirectoryPath = outputDirectoryPath;
150
+ this.startedAt = Date.now();
151
+ }
152
+ CatalogProgressReporter.prototype.start = function (options) {
153
+ console.log("");
154
+ console.log(CLI_FORMAT_BOLD, "Generating Messagevisor catalog");
155
+ console.log(" ".concat(colorize("Output", 36), ": ").concat(formatCatalogPath(this.rootDirectoryPath, this.outputDirectoryPath)));
156
+ console.log(" ".concat(colorize("Router", 36), ": ").concat(options.browserRouter ? "browser" : "hash"));
157
+ console.log(" ".concat(colorize("Sets", 36), ": ").concat(options.sets ? "enabled" : "none"));
158
+ console.log(" ".concat(colorize("Features", 36), ": ").concat(options.features.join(", ") || "none"));
159
+ console.log("");
160
+ };
161
+ CatalogProgressReporter.prototype.step = function (label, detail) {
162
+ var suffix = detail ? ": ".concat(colorize(detail, 2)) : "";
163
+ console.log(" ".concat(colorize("•", 36), " ").concat(label).concat(suffix));
164
+ return Date.now();
165
+ };
166
+ CatalogProgressReporter.prototype.substep = function (label, detail) {
167
+ var suffix = detail ? ": ".concat(colorize(detail, 2)) : "";
168
+ console.log(" ".concat(colorize("•", 36), " ").concat(label).concat(suffix));
169
+ return Date.now();
170
+ };
171
+ CatalogProgressReporter.prototype.done = function (startedAt, detail) {
172
+ var suffix = detail ? " ".concat(detail) : "";
173
+ console.log(CLI_FORMAT_DIM, " done in ".concat(prettyDuration(Date.now() - startedAt)).concat(suffix));
174
+ };
175
+ CatalogProgressReporter.prototype.setStart = function (set) {
176
+ console.log("");
177
+ if (set) {
178
+ console.log(CLI_FORMAT_BOLD, "Set \"".concat(set, "\""));
179
+ }
180
+ else {
181
+ console.log(CLI_FORMAT_BOLD, "Root catalog");
182
+ }
183
+ return Date.now();
184
+ };
185
+ CatalogProgressReporter.prototype.complete = function () {
186
+ console.log("");
187
+ console.log(CLI_FORMAT_GREEN, "Catalog exported to ".concat(formatCatalogPath(this.rootDirectoryPath, this.outputDirectoryPath)));
188
+ console.log(CLI_FORMAT_BOLD, "Time: ".concat(prettyDuration(Date.now() - this.startedAt)));
189
+ };
190
+ return CatalogProgressReporter;
191
+ }());
103
192
  exports.CATALOG_SCHEMA_VERSION = "1";
104
193
  exports.CATALOG_HISTORY_PAGE_SIZE = 50;
105
194
  function toPosixPath(value) {
@@ -354,15 +443,61 @@ function toLocaleDuplicatesFile(localeKey, duplicatesByLocale) {
354
443
  duplicateValues: duplicateValues,
355
444
  };
356
445
  }
357
- function writeJson(filePath, content) {
446
+ var CatalogJsonWriter = /** @class */ (function () {
447
+ function CatalogJsonWriter() {
448
+ this.directories = new Map();
449
+ }
450
+ CatalogJsonWriter.prototype.ensureDirectory = function (directoryPath) {
451
+ var promise = this.directories.get(directoryPath);
452
+ if (!promise) {
453
+ promise = fs.promises.mkdir(directoryPath, { recursive: true }).then(function () { return undefined; });
454
+ this.directories.set(directoryPath, promise);
455
+ }
456
+ return promise;
457
+ };
458
+ CatalogJsonWriter.prototype.write = function (filePath, content) {
459
+ return __awaiter(this, void 0, void 0, function () {
460
+ return __generator(this, function (_a) {
461
+ switch (_a.label) {
462
+ case 0: return [4 /*yield*/, this.ensureDirectory(path.dirname(filePath))];
463
+ case 1:
464
+ _a.sent();
465
+ return [4 /*yield*/, fs.promises.writeFile(filePath, JSON.stringify(content, null, 2))];
466
+ case 2:
467
+ _a.sent();
468
+ return [2 /*return*/];
469
+ }
470
+ });
471
+ });
472
+ };
473
+ return CatalogJsonWriter;
474
+ }());
475
+ function mapWithConcurrency(items, concurrency, callback) {
358
476
  return __awaiter(this, void 0, void 0, function () {
477
+ var nextIndex, workerCount, workers;
478
+ var _this = this;
359
479
  return __generator(this, function (_a) {
360
480
  switch (_a.label) {
361
- case 0: return [4 /*yield*/, fs.promises.mkdir(path.dirname(filePath), { recursive: true })];
481
+ case 0:
482
+ nextIndex = 0;
483
+ workerCount = Math.min(concurrency, items.length);
484
+ workers = Array.from({ length: workerCount }, function () { return __awaiter(_this, void 0, void 0, function () {
485
+ var index;
486
+ return __generator(this, function (_a) {
487
+ switch (_a.label) {
488
+ case 0:
489
+ if (!(nextIndex < items.length)) return [3 /*break*/, 2];
490
+ index = nextIndex++;
491
+ return [4 /*yield*/, callback(items[index], index)];
492
+ case 1:
493
+ _a.sent();
494
+ return [3 /*break*/, 0];
495
+ case 2: return [2 /*return*/];
496
+ }
497
+ });
498
+ }); });
499
+ return [4 /*yield*/, Promise.all(workers)];
362
500
  case 1:
363
- _a.sent();
364
- return [4 /*yield*/, fs.promises.writeFile(filePath, JSON.stringify(content, null, 2))];
365
- case 2:
366
501
  _a.sent();
367
502
  return [2 /*return*/];
368
503
  }
@@ -453,6 +588,14 @@ function addHistoryIndexEntry(target, key, entry) {
453
588
  }
454
589
  target[key].push(entry);
455
590
  }
591
+ function toEntityHistoryEntry(entry, entity) {
592
+ return {
593
+ commit: entry.commit,
594
+ author: entry.author,
595
+ timestamp: entry.timestamp,
596
+ entities: [entity],
597
+ };
598
+ }
456
599
  function buildCatalogHistoryIndex(entries) {
457
600
  var index = createEmptyHistoryIndex();
458
601
  index.entries = entries;
@@ -465,7 +608,7 @@ function buildCatalogHistoryIndex(entries) {
465
608
  continue;
466
609
  }
467
610
  var entityKey = getHistoryEntityKey(entity.type, entity.key, entity.set);
468
- addHistoryIndexEntry(index.byEntity, entityKey, entry);
611
+ addHistoryIndexEntry(index.byEntity, entityKey, toEntityHistoryEntry(entry, entity));
469
612
  if (!index.lastModifiedByEntity[entityKey]) {
470
613
  index.lastModifiedByEntity[entityKey] = toLastModified(entry);
471
614
  }
@@ -674,6 +817,19 @@ function getRepositoryRootDirectoryPath(rootDirectoryPath) {
674
817
  return getRealPath(rootDirectoryPath);
675
818
  }
676
819
  }
820
+ function getRepositorySourceRootDirectoryPath(rootDirectoryPath) {
821
+ try {
822
+ var gitRootDirectoryPath = runGit(rootDirectoryPath, ["rev-parse", "--show-toplevel"]).trim() || rootDirectoryPath;
823
+ var realRootDirectoryPath = getRealPath(rootDirectoryPath);
824
+ if (realRootDirectoryPath !== rootDirectoryPath) {
825
+ return path.resolve(rootDirectoryPath, path.relative(realRootDirectoryPath, gitRootDirectoryPath));
826
+ }
827
+ return gitRootDirectoryPath;
828
+ }
829
+ catch (_error) {
830
+ return rootDirectoryPath;
831
+ }
832
+ }
677
833
  function getOwnerAndRepoFromGitRemote(origin, host) {
678
834
  var escapedHost = host.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
679
835
  var match = origin.match(new RegExp("".concat(escapedHost, "[:/]([^/]+)/(.+?)(?:\\.git)?$")));
@@ -735,18 +891,22 @@ function chunkHistory(history, pageSize) {
735
891
  }
736
892
  return pages.length > 0 ? pages : [[]];
737
893
  }
738
- function writeHistoryPages(directoryPath, history) {
739
- return __awaiter(this, void 0, void 0, function () {
894
+ function writeHistoryPages(writer_1, directoryPath_1, history_1) {
895
+ return __awaiter(this, arguments, void 0, function (writer, directoryPath, history, options) {
740
896
  var pages, index;
897
+ if (options === void 0) { options = {}; }
741
898
  return __generator(this, function (_a) {
742
899
  switch (_a.label) {
743
900
  case 0:
901
+ if (options.skipEmpty && history.length === 0) {
902
+ return [2 /*return*/, 1];
903
+ }
744
904
  pages = chunkHistory(history);
745
905
  index = 0;
746
906
  _a.label = 1;
747
907
  case 1:
748
908
  if (!(index < pages.length)) return [3 /*break*/, 4];
749
- return [4 /*yield*/, writeJson(path.join(directoryPath, "page-".concat(index + 1, ".json")), {
909
+ return [4 /*yield*/, writer.write(path.join(directoryPath, "page-".concat(index + 1, ".json")), {
750
910
  page: index + 1,
751
911
  pageSize: exports.CATALOG_HISTORY_PAGE_SIZE,
752
912
  totalPages: pages.length,
@@ -758,7 +918,7 @@ function writeHistoryPages(directoryPath, history) {
758
918
  case 3:
759
919
  index++;
760
920
  return [3 /*break*/, 1];
761
- case 4: return [2 /*return*/];
921
+ case 4: return [2 /*return*/, 0];
762
922
  }
763
923
  });
764
924
  });
@@ -766,7 +926,8 @@ function writeHistoryPages(directoryPath, history) {
766
926
  function getHistoryForEntity(historyIndex, type, key, set) {
767
927
  return historyIndex.byEntity[getHistoryEntityKey(type, key, set)] || [];
768
928
  }
769
- function getSourceFileInfo(repositoryRootDirectoryPath, rootDirectoryPath, projectConfig, type, key) {
929
+ function getSourceFileInfo(repositorySourceRootDirectoryPath, rootDirectoryPath, projectConfig, type, key, options) {
930
+ if (options === void 0) { options = {}; }
770
931
  var directoryByType = {
771
932
  locale: projectConfig.localesDirectoryPath,
772
933
  message: projectConfig.messagesDirectoryPath,
@@ -777,9 +938,9 @@ function getSourceFileInfo(repositoryRootDirectoryPath, rootDirectoryPath, proje
777
938
  var extension = ".".concat(projectConfig.parser.extension);
778
939
  var filePath = path.resolve(path.resolve.apply(path, __spreadArray([rootDirectoryPath,
779
940
  directoryByType[type]], key.split(projectConfig.namespaceCharacter), false)) + extension);
780
- var absolutePath = getRealPath(filePath);
941
+ var absolutePath = options.resolveAbsolutePath ? getRealPath(filePath) : filePath;
781
942
  return {
782
- sourcePath: toPosixPath(path.relative(repositoryRootDirectoryPath, absolutePath)),
943
+ sourcePath: toPosixPath(path.relative(repositorySourceRootDirectoryPath, filePath)),
783
944
  absolutePath: absolutePath,
784
945
  };
785
946
  }
@@ -815,12 +976,15 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
815
976
  translationShards[filename][msgKey].add(lower);
816
977
  }
817
978
  }
818
- var outputDirectoryPath, _a, localeKeys, messageKeys, attributeKeys, segmentKeys, targetKeys, _b, locales, messages, attributes, segments, targets, messageTargets, targetMessages, localeTargets, attributeTargets, segmentTargets, attributesUsedInSegments, attributesUsedInMessages, segmentsUsedInMessages, _i, targetKeys_1, targetKey, targetLocaleKeys, _c, targetLocaleKeys_1, localeKey, _d, _e, messageKey, _f, segmentKeys_1, segmentKey, usedAttributes, _g, _h, attributeKey, _j, messageKeys_1, messageKey, message, targetsForMessage, _k, _l, override, usedAttributes, usedSegments, _m, _o, attributeKey, _p, targetsForMessage_1, targetKey, _q, _r, segmentKey, _s, targetsForMessage_2, targetKey, _t, _u, attributeKey, _v, _w, segmentKey, _x, _y, targetKey, history, localeDirections, duplicateResult, duplicatesByLocale, index, evaluatedMessageExamplesByKey, evaluatedLocaleExamplesByKey, _loop_1, _z, localeKeys_1, localeKey, translationShards, _loop_2, _0, messageKeys_2, messageKey, _1, _2, _3, prefix, messageMap, shardData, _4, _5, _6, msgKey, valueSet, _7, attributeKeys_1, attributeKey, attribute, sourceFileInfo, detail, _8, segmentKeys_2, segmentKey, segment, usedAttributes, sourceFileInfo, detail, _9, targetKeys_2, targetKey, target, targetLocaleKeys, formatsByLocale, formatRowsByLocale, _10, targetLocaleKeys_2, localeKey, sourceFileInfo, detail, _11, _12, type;
819
- var _13, _14;
820
- return __generator(this, function (_15) {
821
- switch (_15.label) {
979
+ var outputDirectoryPath, setStartedAt, entitiesStartedAt, _a, localeKeys, messageKeys, attributeKeys, segmentKeys, targetKeys, _b, locales, messages, attributes, segments, targets, relationshipsStartedAt, messageTargets, targetMessages, localeTargets, attributeTargets, segmentTargets, attributesUsedInSegments, attributesUsedInMessages, segmentsUsedInMessages, _i, targetKeys_1, targetKey, targetLocaleKeys, _c, targetLocaleKeys_1, localeKey, _d, _e, messageKey, _f, segmentKeys_1, segmentKey, usedAttributes, _g, _h, attributeKey, _j, messageKeys_1, messageKey, message, targetsForMessage, _k, _l, override, usedAttributes, usedSegments, _m, _o, attributeKey, _p, targetsForMessage_1, targetKey, _q, _r, segmentKey, _s, targetsForMessage_2, targetKey, _t, _u, attributeKey, _v, _w, segmentKey, _x, _y, targetKey, history, localeDirections, duplicateResult, duplicatesByLocale, index, historyStartedAt, examplesStartedAt, evaluatedMessageExamplesByKey, evaluatedLocaleExamplesByKey, localesStartedAt, skippedEmptyHistoryCount, duplicatesStartedAt, translationShards, messagesStartedAt, messageDetailsStartedAt, skippedEmptyMessageHistoryCount, messageHistoryStartedAt, translationSearchStartedAt, _z, messageKeys_2, messageKey, message, _0, localeKeys_1, localeKey, row, _1, _2, override, overrideRow, _3, _4, _5, prefix, messageMap, shardData, _6, _7, _8, msgKey, valueSet, attributesStartedAt, segmentsStartedAt, targetsStartedAt, indexStartedAt, _9, _10, type;
980
+ var _this = this;
981
+ var _11;
982
+ return __generator(this, function (_12) {
983
+ switch (_12.label) {
822
984
  case 0:
823
985
  outputDirectoryPath = path.join(context.dataDirectoryPath, outputRelativeDirectory);
986
+ setStartedAt = context.progress.setStart(set);
987
+ entitiesStartedAt = context.progress.step("Processing entities");
824
988
  return [4 /*yield*/, Promise.all([
825
989
  datasource.listLocales(),
826
990
  datasource.listMessages(),
@@ -829,7 +993,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
829
993
  datasource.listTargets(),
830
994
  ])];
831
995
  case 1:
832
- _a = _15.sent(), localeKeys = _a[0], messageKeys = _a[1], attributeKeys = _a[2], segmentKeys = _a[3], targetKeys = _a[4];
996
+ _a = (_12.sent()), localeKeys = _a[0], messageKeys = _a[1], attributeKeys = _a[2], segmentKeys = _a[3], targetKeys = _a[4];
833
997
  return [4 /*yield*/, Promise.all([
834
998
  readAll(localeKeys, function (key) { return datasource.readLocale(key); }),
835
999
  readAll(messageKeys, function (key) { return datasource.readMessage(key); }),
@@ -838,7 +1002,15 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
838
1002
  readAll(targetKeys, function (key) { return datasource.readTarget(key); }),
839
1003
  ])];
840
1004
  case 2:
841
- _b = _15.sent(), locales = _b[0], messages = _b[1], attributes = _b[2], segments = _b[3], targets = _b[4];
1005
+ _b = _12.sent(), locales = _b[0], messages = _b[1], attributes = _b[2], segments = _b[3], targets = _b[4];
1006
+ context.progress.done(entitiesStartedAt, "(".concat([
1007
+ pluralize(localeKeys.length, "locale"),
1008
+ pluralize(messageKeys.length, "message"),
1009
+ pluralize(attributeKeys.length, "attribute"),
1010
+ pluralize(segmentKeys.length, "segment"),
1011
+ pluralize(targetKeys.length, "target"),
1012
+ ].join(", "), ")"));
1013
+ relationshipsStartedAt = context.progress.step("Mapping relationships");
842
1014
  messageTargets = {};
843
1015
  targetMessages = {};
844
1016
  localeTargets = {};
@@ -850,7 +1022,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
850
1022
  for (_i = 0, targetKeys_1 = targetKeys; _i < targetKeys_1.length; _i++) {
851
1023
  targetKey = targetKeys_1[_i];
852
1024
  targetMessages[targetKey] = getTargetMessageKeys(targets[targetKey], messageKeys);
853
- targetLocaleKeys = ((_13 = targets[targetKey].locales) === null || _13 === void 0 ? void 0 : _13.length)
1025
+ targetLocaleKeys = ((_11 = targets[targetKey].locales) === null || _11 === void 0 ? void 0 : _11.length)
854
1026
  ? targets[targetKey].locales
855
1027
  : localeKeys;
856
1028
  for (_c = 0, targetLocaleKeys_1 = targetLocaleKeys; _c < targetLocaleKeys_1.length; _c++) {
@@ -933,6 +1105,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
933
1105
  }
934
1106
  }
935
1107
  }
1108
+ context.progress.done(relationshipsStartedAt);
936
1109
  history = set ? context.historyIndex.bySet[set] || [] : context.historyIndex.entries;
937
1110
  localeDirections = getLocaleDirections(locales);
938
1111
  duplicateResult = context.duplicateResultsBySet[getDuplicateSetKey(set)] || {
@@ -957,14 +1130,17 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
957
1130
  target: [],
958
1131
  },
959
1132
  };
960
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history"), history)];
1133
+ historyStartedAt = context.progress.step("Writing history pages");
1134
+ return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history"), history)];
961
1135
  case 3:
962
- _15.sent();
1136
+ _12.sent();
1137
+ context.progress.done(historyStartedAt, "(".concat(pluralize(history.length, "entry", "entries"), ")"));
1138
+ examplesStartedAt = context.progress.step("Evaluating examples");
963
1139
  return [4 /*yield*/, context.runtime.resolveExamples(projectConfig, datasource, {
964
1140
  onlyMessages: true,
965
1141
  })];
966
1142
  case 4:
967
- evaluatedMessageExamplesByKey = (_15.sent()).messages.reduce(function (accumulator, example) {
1143
+ evaluatedMessageExamplesByKey = (_12.sent()).messages.reduce(function (accumulator, example) {
968
1144
  if (!accumulator[example.message]) {
969
1145
  accumulator[example.message] = [];
970
1146
  }
@@ -975,7 +1151,7 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
975
1151
  onlyLocales: true,
976
1152
  })];
977
1153
  case 5:
978
- evaluatedLocaleExamplesByKey = (_15.sent()).locales.reduce(function (accumulator, example) {
1154
+ evaluatedLocaleExamplesByKey = (_12.sent()).locales.reduce(function (accumulator, example) {
979
1155
  var _a;
980
1156
  var originalTranslation = example.message
981
1157
  ? resolveTranslationRow((_a = messages[example.message]) === null || _a === void 0 ? void 0 : _a.translations, example.locale, locales)
@@ -987,280 +1163,317 @@ function buildSetCatalog(context, set, projectConfig, datasource, outputRelative
987
1163
  accumulator[example.locale].push(__assign(__assign({}, example), { originalTranslation: originalTranslation || undefined }));
988
1164
  return accumulator;
989
1165
  }, {});
990
- _loop_1 = function (localeKey) {
991
- var locale, sourceFileInfo, detail;
992
- return __generator(this, function (_16) {
993
- switch (_16.label) {
994
- case 0:
995
- locale = locales[localeKey];
996
- sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "locale", localeKey);
997
- detail = {
998
- type: "locale",
999
- key: localeKey,
1000
- entity: locale,
1001
- sourcePath: sourceFileInfo.sourcePath,
1002
- editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1003
- baseFormats: locale.formats || {},
1004
- computedFormats: context.runtime.resolveFormats(localeKey, locales),
1005
- formatRows: getFormatRows(context.runtime, localeKey, locales),
1006
- evaluatedExamples: evaluatedLocaleExamplesByKey[localeKey] || [],
1007
- targetFormats: Object.fromEntries(targetKeys.map(function (targetKey) { return [
1008
- targetKey,
1009
- context.runtime.resolveFormats(localeKey, locales, targets[targetKey]),
1010
- ]; })),
1011
- lastModified: getLastModified(context.historyIndex, "locale", localeKey, set || undefined),
1012
- };
1013
- index.entities.locale.push(getEntitySummary(locale, "locale", localeKey, context.historyIndex, set || undefined, {
1014
- targets: sortStrings(Array.from(localeTargets[localeKey] || [])),
1015
- }));
1016
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "locale", "".concat(encodeKey(localeKey), ".json")), detail)];
1017
- case 1:
1018
- _16.sent();
1019
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "duplicates", "locales", "".concat(encodeKey(localeKey), ".json")), toLocaleDuplicatesFile(localeKey, duplicatesByLocale))];
1020
- case 2:
1021
- _16.sent();
1022
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "locale", encodeKey(localeKey)), getHistoryForEntity(context.historyIndex, "locale", localeKey, set || undefined))];
1023
- case 3:
1024
- _16.sent();
1025
- return [2 /*return*/];
1026
- }
1027
- });
1028
- };
1029
- _z = 0, localeKeys_1 = localeKeys;
1030
- _15.label = 6;
1166
+ context.progress.done(examplesStartedAt, "(".concat(pluralize(Object.values(evaluatedMessageExamplesByKey).reduce(function (total, items) { return total + items.length; }, 0), "message example"), ", ").concat(pluralize(Object.values(evaluatedLocaleExamplesByKey).reduce(function (total, items) { return total + items.length; }, 0), "locale example"), ")"));
1167
+ localesStartedAt = context.progress.step("Writing locales");
1168
+ skippedEmptyHistoryCount = 0;
1169
+ return [4 /*yield*/, mapWithConcurrency(localeKeys, 32, function (localeKey) { return __awaiter(_this, void 0, void 0, function () {
1170
+ var locale, sourceFileInfo, detail, _a;
1171
+ return __generator(this, function (_b) {
1172
+ switch (_b.label) {
1173
+ case 0:
1174
+ locale = locales[localeKey];
1175
+ sourceFileInfo = getSourceFileInfo(context.repositorySourceRootDirectoryPath, context.rootDirectoryPath, projectConfig, "locale", localeKey, { resolveAbsolutePath: context.devEditors.length > 0 });
1176
+ detail = {
1177
+ type: "locale",
1178
+ key: localeKey,
1179
+ entity: locale,
1180
+ sourcePath: sourceFileInfo.sourcePath,
1181
+ editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1182
+ baseFormats: locale.formats || {},
1183
+ computedFormats: context.runtime.resolveFormats(localeKey, locales),
1184
+ formatRows: getFormatRows(context.runtime, localeKey, locales),
1185
+ evaluatedExamples: evaluatedLocaleExamplesByKey[localeKey] || [],
1186
+ targetFormats: Object.fromEntries(targetKeys.map(function (targetKey) { return [
1187
+ targetKey,
1188
+ context.runtime.resolveFormats(localeKey, locales, targets[targetKey]),
1189
+ ]; })),
1190
+ lastModified: getLastModified(context.historyIndex, "locale", localeKey, set || undefined),
1191
+ };
1192
+ index.entities.locale.push(getEntitySummary(locale, "locale", localeKey, context.historyIndex, set || undefined, {
1193
+ targets: sortStrings(Array.from(localeTargets[localeKey] || [])),
1194
+ }));
1195
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "entities", "locale", "".concat(encodeKey(localeKey), ".json")), detail)];
1196
+ case 1:
1197
+ _b.sent();
1198
+ _a = skippedEmptyHistoryCount;
1199
+ return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history", "locale", encodeKey(localeKey)), getHistoryForEntity(context.historyIndex, "locale", localeKey, set || undefined), { skipEmpty: true })];
1200
+ case 2:
1201
+ skippedEmptyHistoryCount = _a + _b.sent();
1202
+ return [2 /*return*/];
1203
+ }
1204
+ });
1205
+ }); })];
1031
1206
  case 6:
1032
- if (!(_z < localeKeys_1.length)) return [3 /*break*/, 9];
1033
- localeKey = localeKeys_1[_z];
1034
- return [5 /*yield**/, _loop_1(localeKey)];
1207
+ _12.sent();
1208
+ context.progress.done(localesStartedAt, "(".concat(pluralize(localeKeys.length, "locale"), ")"));
1209
+ if (!context.withDuplicates) return [3 /*break*/, 8];
1210
+ duplicatesStartedAt = context.progress.step("Writing duplicate reports");
1211
+ return [4 /*yield*/, mapWithConcurrency(localeKeys, 32, function (localeKey) { return __awaiter(_this, void 0, void 0, function () {
1212
+ return __generator(this, function (_a) {
1213
+ switch (_a.label) {
1214
+ case 0: return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "duplicates", "locales", "".concat(encodeKey(localeKey), ".json")), toLocaleDuplicatesFile(localeKey, duplicatesByLocale))];
1215
+ case 1:
1216
+ _a.sent();
1217
+ return [2 /*return*/];
1218
+ }
1219
+ });
1220
+ }); })];
1035
1221
  case 7:
1036
- _15.sent();
1037
- _15.label = 8;
1222
+ _12.sent();
1223
+ context.progress.done(duplicatesStartedAt, "(".concat(pluralize(localeKeys.length, "locale"), ")"));
1224
+ _12.label = 8;
1038
1225
  case 8:
1039
- _z++;
1040
- return [3 /*break*/, 6];
1041
- case 9:
1042
1226
  translationShards = {};
1043
- _loop_2 = function (messageKey) {
1044
- var message, overrides, sourceFileInfo, detail, directLocales, overrideLocalesSet, _17, overrides_1, override, _18, _19, lk, overrideLocalesList, _20, localeKeys_2, localeKey, row, _21, overrides_2, override, overrideRow;
1045
- return __generator(this, function (_22) {
1046
- switch (_22.label) {
1047
- case 0:
1048
- message = messages[messageKey];
1049
- overrides = (message.overrides || []).map(function (override) {
1050
- var attributes = new Set();
1051
- var overrideSegments = new Set();
1052
- collectAttributeKeysFromConditions(override.conditions, attributes);
1053
- collectSegmentKeys(override.segments, overrideSegments);
1054
- return __assign(__assign({}, override), { usedAttributes: sortStrings(Array.from(attributes)), usedSegments: sortStrings(Array.from(overrideSegments)) });
1055
- });
1056
- sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "message", messageKey);
1057
- detail = {
1058
- type: "message",
1059
- key: messageKey,
1060
- entity: __assign(__assign({}, message), { overrides: overrides }),
1061
- sourcePath: sourceFileInfo.sourcePath,
1062
- editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1063
- targets: sortStrings(messageTargets[messageKey] || []),
1064
- localeKeys: localeKeys,
1065
- localeDirections: localeDirections,
1066
- translations: localeKeys.map(function (localeKey) {
1067
- return resolveTranslationRow(message.translations, localeKey, locales);
1068
- }),
1069
- evaluatedExamples: evaluatedMessageExamplesByKey[messageKey] || [],
1070
- overrideTranslations: overrides.map(function (override) { return ({
1071
- key: override.key,
1072
- rows: localeKeys.map(function (localeKey) {
1073
- return resolveTranslationRow(override.translations, localeKey, locales);
1227
+ messagesStartedAt = context.progress.step("Writing messages");
1228
+ messageDetailsStartedAt = context.progress.substep("Writing message details");
1229
+ skippedEmptyMessageHistoryCount = 0;
1230
+ return [4 /*yield*/, mapWithConcurrency(messageKeys, 32, function (messageKey) { return __awaiter(_this, void 0, void 0, function () {
1231
+ var message, overrides, sourceFileInfo, detail, directLocales, overrideLocalesSet, _i, overrides_1, override, _a, _b, lk, overrideLocalesList;
1232
+ return __generator(this, function (_c) {
1233
+ switch (_c.label) {
1234
+ case 0:
1235
+ message = messages[messageKey];
1236
+ overrides = (message.overrides || []).map(function (override) {
1237
+ var attributes = new Set();
1238
+ var overrideSegments = new Set();
1239
+ collectAttributeKeysFromConditions(override.conditions, attributes);
1240
+ collectSegmentKeys(override.segments, overrideSegments);
1241
+ return __assign(__assign({}, override), { usedAttributes: sortStrings(Array.from(attributes)), usedSegments: sortStrings(Array.from(overrideSegments)) });
1242
+ });
1243
+ sourceFileInfo = getSourceFileInfo(context.repositorySourceRootDirectoryPath, context.rootDirectoryPath, projectConfig, "message", messageKey, { resolveAbsolutePath: context.devEditors.length > 0 });
1244
+ detail = {
1245
+ type: "message",
1246
+ key: messageKey,
1247
+ entity: __assign(__assign({}, message), { overrides: overrides }),
1248
+ sourcePath: sourceFileInfo.sourcePath,
1249
+ editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1250
+ targets: sortStrings(messageTargets[messageKey] || []),
1251
+ localeKeys: localeKeys,
1252
+ localeDirections: localeDirections,
1253
+ translations: localeKeys.map(function (localeKey) {
1254
+ return resolveTranslationRow(message.translations, localeKey, locales);
1074
1255
  }),
1075
- }); }),
1076
- lastModified: getLastModified(context.historyIndex, "message", messageKey, set || undefined),
1077
- };
1078
- directLocales = localeKeys.filter(function (lk) { return message.translations && typeof message.translations[lk] === "string"; });
1079
- overrideLocalesSet = new Set();
1080
- for (_17 = 0, overrides_1 = overrides; _17 < overrides_1.length; _17++) {
1081
- override = overrides_1[_17];
1082
- for (_18 = 0, _19 = Object.keys(override.translations || {}); _18 < _19.length; _18++) {
1083
- lk = _19[_18];
1084
- overrideLocalesSet.add(lk);
1085
- }
1086
- }
1087
- overrideLocalesList = sortStrings(Array.from(overrideLocalesSet));
1088
- if (context.withTranslationSearch) {
1089
- // Build translation shards (direct + inherited + override, all locales combined)
1090
- for (_20 = 0, localeKeys_2 = localeKeys; _20 < localeKeys_2.length; _20++) {
1091
- localeKey = localeKeys_2[_20];
1092
- row = resolveTranslationRow(message.translations, localeKey, locales);
1093
- if (row.source !== "missing" && row.value) {
1094
- addToTranslationShard(messageKey, row.value);
1095
- }
1096
- for (_21 = 0, overrides_2 = overrides; _21 < overrides_2.length; _21++) {
1097
- override = overrides_2[_21];
1098
- overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
1099
- if (overrideRow.source !== "missing" && overrideRow.value) {
1100
- addToTranslationShard(messageKey, overrideRow.value);
1101
- }
1256
+ evaluatedExamples: evaluatedMessageExamplesByKey[messageKey] || [],
1257
+ overrideTranslations: overrides.map(function (override) { return ({
1258
+ key: override.key,
1259
+ rows: localeKeys.map(function (localeKey) {
1260
+ return resolveTranslationRow(override.translations, localeKey, locales);
1261
+ }),
1262
+ }); }),
1263
+ lastModified: getLastModified(context.historyIndex, "message", messageKey, set || undefined),
1264
+ };
1265
+ directLocales = localeKeys.filter(function (lk) { return message.translations && typeof message.translations[lk] === "string"; });
1266
+ overrideLocalesSet = new Set();
1267
+ for (_i = 0, overrides_1 = overrides; _i < overrides_1.length; _i++) {
1268
+ override = overrides_1[_i];
1269
+ for (_a = 0, _b = Object.keys(override.translations || {}); _a < _b.length; _a++) {
1270
+ lk = _b[_a];
1271
+ overrideLocalesSet.add(lk);
1102
1272
  }
1103
1273
  }
1104
- }
1105
- index.entities.message.push(getEntitySummary(message, "message", messageKey, context.historyIndex, set || undefined, __assign(__assign({ targets: sortStrings(messageTargets[messageKey] || []) }, (directLocales.length > 0 ? { locales: sortStrings(directLocales) } : {})), (overrideLocalesList.length > 0 ? { overrideLocales: overrideLocalesList } : {}))));
1106
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "message", "".concat(encodeKey(messageKey), ".json")), detail)];
1107
- case 1:
1108
- _22.sent();
1109
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "message", encodeKey(messageKey)), getHistoryForEntity(context.historyIndex, "message", messageKey, set || undefined))];
1110
- case 2:
1111
- _22.sent();
1112
- return [2 /*return*/];
1113
- }
1114
- });
1115
- };
1116
- _0 = 0, messageKeys_2 = messageKeys;
1117
- _15.label = 10;
1274
+ overrideLocalesList = sortStrings(Array.from(overrideLocalesSet));
1275
+ index.entities.message.push(getEntitySummary(message, "message", messageKey, context.historyIndex, set || undefined, __assign(__assign({ targets: sortStrings(messageTargets[messageKey] || []) }, (directLocales.length > 0 ? { locales: sortStrings(directLocales) } : {})), (overrideLocalesList.length > 0 ? { overrideLocales: overrideLocalesList } : {}))));
1276
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "entities", "message", "".concat(encodeKey(messageKey), ".json")), detail)];
1277
+ case 1:
1278
+ _c.sent();
1279
+ return [2 /*return*/];
1280
+ }
1281
+ });
1282
+ }); })];
1283
+ case 9:
1284
+ _12.sent();
1285
+ context.progress.done(messageDetailsStartedAt, "(".concat(pluralize(messageKeys.length, "message"), ")"));
1286
+ messageHistoryStartedAt = context.progress.substep("Writing message history pages");
1287
+ return [4 /*yield*/, mapWithConcurrency(messageKeys, 32, function (messageKey) { return __awaiter(_this, void 0, void 0, function () {
1288
+ var skippedHistory;
1289
+ return __generator(this, function (_a) {
1290
+ switch (_a.label) {
1291
+ case 0: return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history", "message", encodeKey(messageKey)), getHistoryForEntity(context.historyIndex, "message", messageKey, set || undefined), { skipEmpty: true })];
1292
+ case 1:
1293
+ skippedHistory = _a.sent();
1294
+ skippedEmptyMessageHistoryCount += skippedHistory;
1295
+ skippedEmptyHistoryCount += skippedHistory;
1296
+ return [2 /*return*/];
1297
+ }
1298
+ });
1299
+ }); })];
1118
1300
  case 10:
1119
- if (!(_0 < messageKeys_2.length)) return [3 /*break*/, 13];
1120
- messageKey = messageKeys_2[_0];
1121
- return [5 /*yield**/, _loop_2(messageKey)];
1301
+ _12.sent();
1302
+ context.progress.done(messageHistoryStartedAt, "(".concat(pluralize(messageKeys.length, "message"), ", ").concat(pluralize(skippedEmptyMessageHistoryCount, "empty history", "empty histories"), " skipped)"));
1303
+ context.progress.done(messagesStartedAt, "(".concat(pluralize(messageKeys.length, "message"), ", ").concat(pluralize(skippedEmptyMessageHistoryCount, "empty history", "empty histories"), " skipped)"));
1304
+ if (!context.withTranslationSearch) return [3 /*break*/, 15];
1305
+ translationSearchStartedAt = context.progress.step("Building translation search shards");
1306
+ for (_z = 0, messageKeys_2 = messageKeys; _z < messageKeys_2.length; _z++) {
1307
+ messageKey = messageKeys_2[_z];
1308
+ message = messages[messageKey];
1309
+ for (_0 = 0, localeKeys_1 = localeKeys; _0 < localeKeys_1.length; _0++) {
1310
+ localeKey = localeKeys_1[_0];
1311
+ row = resolveTranslationRow(message.translations, localeKey, locales);
1312
+ if (row.source !== "missing" && row.value) {
1313
+ addToTranslationShard(messageKey, row.value);
1314
+ }
1315
+ for (_1 = 0, _2 = message.overrides || []; _1 < _2.length; _1++) {
1316
+ override = _2[_1];
1317
+ overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
1318
+ if (overrideRow.source !== "missing" && overrideRow.value) {
1319
+ addToTranslationShard(messageKey, overrideRow.value);
1320
+ }
1321
+ }
1322
+ }
1323
+ }
1324
+ _3 = 0, _4 = Object.entries(translationShards);
1325
+ _12.label = 11;
1122
1326
  case 11:
1123
- _15.sent();
1124
- _15.label = 12;
1125
- case 12:
1126
- _0++;
1127
- return [3 /*break*/, 10];
1128
- case 13:
1129
- if (!context.withTranslationSearch) return [3 /*break*/, 17];
1130
- _1 = 0, _2 = Object.entries(translationShards);
1131
- _15.label = 14;
1132
- case 14:
1133
- if (!(_1 < _2.length)) return [3 /*break*/, 17];
1134
- _3 = _2[_1], prefix = _3[0], messageMap = _3[1];
1327
+ if (!(_3 < _4.length)) return [3 /*break*/, 14];
1328
+ _5 = _4[_3], prefix = _5[0], messageMap = _5[1];
1135
1329
  shardData = {};
1136
- for (_4 = 0, _5 = Object.entries(messageMap); _4 < _5.length; _4++) {
1137
- _6 = _5[_4], msgKey = _6[0], valueSet = _6[1];
1330
+ for (_6 = 0, _7 = Object.entries(messageMap); _6 < _7.length; _6++) {
1331
+ _8 = _7[_6], msgKey = _8[0], valueSet = _8[1];
1138
1332
  shardData[msgKey] = Array.from(valueSet);
1139
1333
  }
1140
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "translations", "".concat(prefix, ".json")), shardData)];
1334
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "translations", "".concat(prefix, ".json")), shardData)];
1335
+ case 12:
1336
+ _12.sent();
1337
+ _12.label = 13;
1338
+ case 13:
1339
+ _3++;
1340
+ return [3 /*break*/, 11];
1341
+ case 14:
1342
+ context.progress.done(translationSearchStartedAt, "(".concat(pluralize(Object.keys(translationShards).length, "shard"), ")"));
1343
+ _12.label = 15;
1141
1344
  case 15:
1142
- _15.sent();
1143
- _15.label = 16;
1345
+ attributesStartedAt = context.progress.step("Writing attributes");
1346
+ return [4 /*yield*/, mapWithConcurrency(attributeKeys, 32, function (attributeKey) { return __awaiter(_this, void 0, void 0, function () {
1347
+ var attribute, sourceFileInfo, detail, _a;
1348
+ return __generator(this, function (_b) {
1349
+ switch (_b.label) {
1350
+ case 0:
1351
+ attribute = attributes[attributeKey];
1352
+ sourceFileInfo = getSourceFileInfo(context.repositorySourceRootDirectoryPath, context.rootDirectoryPath, projectConfig, "attribute", attributeKey, { resolveAbsolutePath: context.devEditors.length > 0 });
1353
+ detail = {
1354
+ type: "attribute",
1355
+ key: attributeKey,
1356
+ entity: attribute,
1357
+ sourcePath: sourceFileInfo.sourcePath,
1358
+ editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1359
+ usage: {
1360
+ segments: sortStrings(Array.from(attributesUsedInSegments[attributeKey] || [])),
1361
+ messages: sortStrings(Array.from(attributesUsedInMessages[attributeKey] || [])),
1362
+ },
1363
+ lastModified: getLastModified(context.historyIndex, "attribute", attributeKey, set || undefined),
1364
+ };
1365
+ index.entities.attribute.push(getEntitySummary(attribute, "attribute", attributeKey, context.historyIndex, set || undefined, {
1366
+ targets: sortStrings(Array.from(attributeTargets[attributeKey] || [])),
1367
+ }));
1368
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "entities", "attribute", "".concat(encodeKey(attributeKey), ".json")), detail)];
1369
+ case 1:
1370
+ _b.sent();
1371
+ _a = skippedEmptyHistoryCount;
1372
+ return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history", "attribute", encodeKey(attributeKey)), getHistoryForEntity(context.historyIndex, "attribute", attributeKey, set || undefined), { skipEmpty: true })];
1373
+ case 2:
1374
+ skippedEmptyHistoryCount = _a + _b.sent();
1375
+ return [2 /*return*/];
1376
+ }
1377
+ });
1378
+ }); })];
1144
1379
  case 16:
1145
- _1++;
1146
- return [3 /*break*/, 14];
1380
+ _12.sent();
1381
+ context.progress.done(attributesStartedAt, "(".concat(pluralize(attributeKeys.length, "attribute"), ")"));
1382
+ segmentsStartedAt = context.progress.step("Writing segments");
1383
+ return [4 /*yield*/, mapWithConcurrency(segmentKeys, 32, function (segmentKey) { return __awaiter(_this, void 0, void 0, function () {
1384
+ var segment, usedAttributes, sourceFileInfo, detail, _a;
1385
+ return __generator(this, function (_b) {
1386
+ switch (_b.label) {
1387
+ case 0:
1388
+ segment = segments[segmentKey];
1389
+ usedAttributes = new Set();
1390
+ collectAttributeKeysFromConditions(segment.conditions, usedAttributes);
1391
+ sourceFileInfo = getSourceFileInfo(context.repositorySourceRootDirectoryPath, context.rootDirectoryPath, projectConfig, "segment", segmentKey, { resolveAbsolutePath: context.devEditors.length > 0 });
1392
+ detail = {
1393
+ type: "segment",
1394
+ key: segmentKey,
1395
+ entity: segment,
1396
+ sourcePath: sourceFileInfo.sourcePath,
1397
+ editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1398
+ usage: {
1399
+ attributes: sortStrings(Array.from(usedAttributes)),
1400
+ messages: sortStrings(Array.from(segmentsUsedInMessages[segmentKey] || [])),
1401
+ },
1402
+ lastModified: getLastModified(context.historyIndex, "segment", segmentKey, set || undefined),
1403
+ };
1404
+ index.entities.segment.push(getEntitySummary(segment, "segment", segmentKey, context.historyIndex, set || undefined, {
1405
+ targets: sortStrings(Array.from(segmentTargets[segmentKey] || [])),
1406
+ }));
1407
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "entities", "segment", "".concat(encodeKey(segmentKey), ".json")), detail)];
1408
+ case 1:
1409
+ _b.sent();
1410
+ _a = skippedEmptyHistoryCount;
1411
+ return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history", "segment", encodeKey(segmentKey)), getHistoryForEntity(context.historyIndex, "segment", segmentKey, set || undefined), { skipEmpty: true })];
1412
+ case 2:
1413
+ skippedEmptyHistoryCount = _a + _b.sent();
1414
+ return [2 /*return*/];
1415
+ }
1416
+ });
1417
+ }); })];
1147
1418
  case 17:
1148
- _7 = 0, attributeKeys_1 = attributeKeys;
1149
- _15.label = 18;
1419
+ _12.sent();
1420
+ context.progress.done(segmentsStartedAt, "(".concat(pluralize(segmentKeys.length, "segment"), ")"));
1421
+ targetsStartedAt = context.progress.step("Writing targets");
1422
+ return [4 /*yield*/, mapWithConcurrency(targetKeys, 32, function (targetKey) { return __awaiter(_this, void 0, void 0, function () {
1423
+ var target, targetLocaleKeys, formatsByLocale, formatRowsByLocale, _i, targetLocaleKeys_2, localeKey, sourceFileInfo, detail, _a;
1424
+ var _b;
1425
+ return __generator(this, function (_c) {
1426
+ switch (_c.label) {
1427
+ case 0:
1428
+ target = targets[targetKey];
1429
+ targetLocaleKeys = ((_b = target.locales) === null || _b === void 0 ? void 0 : _b.length) ? target.locales : localeKeys;
1430
+ formatsByLocale = {};
1431
+ formatRowsByLocale = {};
1432
+ for (_i = 0, targetLocaleKeys_2 = targetLocaleKeys; _i < targetLocaleKeys_2.length; _i++) {
1433
+ localeKey = targetLocaleKeys_2[_i];
1434
+ formatsByLocale[localeKey] = context.runtime.resolveFormats(localeKey, locales, target);
1435
+ formatRowsByLocale[localeKey] = getFormatRows(context.runtime, localeKey, locales, target);
1436
+ }
1437
+ sourceFileInfo = getSourceFileInfo(context.repositorySourceRootDirectoryPath, context.rootDirectoryPath, projectConfig, "target", targetKey, { resolveAbsolutePath: context.devEditors.length > 0 });
1438
+ detail = {
1439
+ type: "target",
1440
+ key: targetKey,
1441
+ entity: target,
1442
+ sourcePath: sourceFileInfo.sourcePath,
1443
+ editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1444
+ locales: targetLocaleKeys,
1445
+ formatsByLocale: formatsByLocale,
1446
+ formatRowsByLocale: formatRowsByLocale,
1447
+ messages: targetMessages[targetKey],
1448
+ lastModified: getLastModified(context.historyIndex, "target", targetKey, set || undefined),
1449
+ };
1450
+ index.entities.target.push(getEntitySummary(target, "target", targetKey, context.historyIndex, set || undefined, {
1451
+ messageCount: targetMessages[targetKey].length,
1452
+ }));
1453
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "entities", "target", "".concat(encodeKey(targetKey), ".json")), detail)];
1454
+ case 1:
1455
+ _c.sent();
1456
+ _a = skippedEmptyHistoryCount;
1457
+ return [4 /*yield*/, writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history", "target", encodeKey(targetKey)), getHistoryForEntity(context.historyIndex, "target", targetKey, set || undefined), { skipEmpty: true })];
1458
+ case 2:
1459
+ skippedEmptyHistoryCount = _a + _c.sent();
1460
+ return [2 /*return*/];
1461
+ }
1462
+ });
1463
+ }); })];
1150
1464
  case 18:
1151
- if (!(_7 < attributeKeys_1.length)) return [3 /*break*/, 22];
1152
- attributeKey = attributeKeys_1[_7];
1153
- attribute = attributes[attributeKey];
1154
- sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "attribute", attributeKey);
1155
- detail = {
1156
- type: "attribute",
1157
- key: attributeKey,
1158
- entity: attribute,
1159
- sourcePath: sourceFileInfo.sourcePath,
1160
- editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1161
- usage: {
1162
- segments: sortStrings(Array.from(attributesUsedInSegments[attributeKey] || [])),
1163
- messages: sortStrings(Array.from(attributesUsedInMessages[attributeKey] || [])),
1164
- },
1165
- lastModified: getLastModified(context.historyIndex, "attribute", attributeKey, set || undefined),
1166
- };
1167
- index.entities.attribute.push(getEntitySummary(attribute, "attribute", attributeKey, context.historyIndex, set || undefined, {
1168
- targets: sortStrings(Array.from(attributeTargets[attributeKey] || [])),
1169
- }));
1170
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "attribute", "".concat(encodeKey(attributeKey), ".json")), detail)];
1171
- case 19:
1172
- _15.sent();
1173
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "attribute", encodeKey(attributeKey)), getHistoryForEntity(context.historyIndex, "attribute", attributeKey, set || undefined))];
1174
- case 20:
1175
- _15.sent();
1176
- _15.label = 21;
1177
- case 21:
1178
- _7++;
1179
- return [3 /*break*/, 18];
1180
- case 22:
1181
- _8 = 0, segmentKeys_2 = segmentKeys;
1182
- _15.label = 23;
1183
- case 23:
1184
- if (!(_8 < segmentKeys_2.length)) return [3 /*break*/, 27];
1185
- segmentKey = segmentKeys_2[_8];
1186
- segment = segments[segmentKey];
1187
- usedAttributes = new Set();
1188
- collectAttributeKeysFromConditions(segment.conditions, usedAttributes);
1189
- sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "segment", segmentKey);
1190
- detail = {
1191
- type: "segment",
1192
- key: segmentKey,
1193
- entity: segment,
1194
- sourcePath: sourceFileInfo.sourcePath,
1195
- editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1196
- usage: {
1197
- attributes: sortStrings(Array.from(usedAttributes)),
1198
- messages: sortStrings(Array.from(segmentsUsedInMessages[segmentKey] || [])),
1199
- },
1200
- lastModified: getLastModified(context.historyIndex, "segment", segmentKey, set || undefined),
1201
- };
1202
- index.entities.segment.push(getEntitySummary(segment, "segment", segmentKey, context.historyIndex, set || undefined, {
1203
- targets: sortStrings(Array.from(segmentTargets[segmentKey] || [])),
1204
- }));
1205
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "segment", "".concat(encodeKey(segmentKey), ".json")), detail)];
1206
- case 24:
1207
- _15.sent();
1208
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "segment", encodeKey(segmentKey)), getHistoryForEntity(context.historyIndex, "segment", segmentKey, set || undefined))];
1209
- case 25:
1210
- _15.sent();
1211
- _15.label = 26;
1212
- case 26:
1213
- _8++;
1214
- return [3 /*break*/, 23];
1215
- case 27:
1216
- _9 = 0, targetKeys_2 = targetKeys;
1217
- _15.label = 28;
1218
- case 28:
1219
- if (!(_9 < targetKeys_2.length)) return [3 /*break*/, 32];
1220
- targetKey = targetKeys_2[_9];
1221
- target = targets[targetKey];
1222
- targetLocaleKeys = ((_14 = target.locales) === null || _14 === void 0 ? void 0 : _14.length) ? target.locales : localeKeys;
1223
- formatsByLocale = {};
1224
- formatRowsByLocale = {};
1225
- for (_10 = 0, targetLocaleKeys_2 = targetLocaleKeys; _10 < targetLocaleKeys_2.length; _10++) {
1226
- localeKey = targetLocaleKeys_2[_10];
1227
- formatsByLocale[localeKey] = context.runtime.resolveFormats(localeKey, locales, target);
1228
- formatRowsByLocale[localeKey] = getFormatRows(context.runtime, localeKey, locales, target);
1229
- }
1230
- sourceFileInfo = getSourceFileInfo(context.repositoryRootDirectoryPath, context.rootDirectoryPath, projectConfig, "target", targetKey);
1231
- detail = {
1232
- type: "target",
1233
- key: targetKey,
1234
- entity: target,
1235
- sourcePath: sourceFileInfo.sourcePath,
1236
- editLinks: getEditorLinks(context.devEditors, sourceFileInfo),
1237
- locales: targetLocaleKeys,
1238
- formatsByLocale: formatsByLocale,
1239
- formatRowsByLocale: formatRowsByLocale,
1240
- messages: targetMessages[targetKey],
1241
- lastModified: getLastModified(context.historyIndex, "target", targetKey, set || undefined),
1242
- };
1243
- index.entities.target.push(getEntitySummary(target, "target", targetKey, context.historyIndex, set || undefined, {
1244
- messageCount: targetMessages[targetKey].length,
1245
- }));
1246
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "entities", "target", "".concat(encodeKey(targetKey), ".json")), detail)];
1247
- case 29:
1248
- _15.sent();
1249
- return [4 /*yield*/, writeHistoryPages(path.join(outputDirectoryPath, "history", "target", encodeKey(targetKey)), getHistoryForEntity(context.historyIndex, "target", targetKey, set || undefined))];
1250
- case 30:
1251
- _15.sent();
1252
- _15.label = 31;
1253
- case 31:
1254
- _9++;
1255
- return [3 /*break*/, 28];
1256
- case 32:
1257
- for (_11 = 0, _12 = Object.keys(index.entities); _11 < _12.length; _11++) {
1258
- type = _12[_11];
1465
+ _12.sent();
1466
+ context.progress.done(targetsStartedAt, "(".concat(pluralize(targetKeys.length, "target"), ")"));
1467
+ indexStartedAt = context.progress.step("Writing catalog index");
1468
+ for (_9 = 0, _10 = Object.keys(index.entities); _9 < _10.length; _9++) {
1469
+ type = _10[_9];
1259
1470
  index.entities[type].sort(function (a, b) { return a.key.localeCompare(b.key); });
1260
1471
  }
1261
- return [4 /*yield*/, writeJson(path.join(outputDirectoryPath, "index.json"), index)];
1262
- case 33:
1263
- _15.sent();
1472
+ return [4 /*yield*/, context.writer.write(path.join(outputDirectoryPath, "index.json"), index)];
1473
+ case 19:
1474
+ _12.sent();
1475
+ context.progress.done(indexStartedAt);
1476
+ context.progress.done(setStartedAt, "total (".concat(pluralize(skippedEmptyHistoryCount, "empty history", "empty histories"), " skipped)"));
1264
1477
  return [2 /*return*/, index];
1265
1478
  }
1266
1479
  });
@@ -1292,39 +1505,69 @@ function copyCatalogAssets(outputDirectoryPath) {
1292
1505
  }
1293
1506
  function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasource_1) {
1294
1507
  return __awaiter(this, arguments, void 0, function (runtime, rootDirectoryPath, projectConfig, datasource, options) {
1295
- var outputDirectoryPath, dataDirectoryPath, withTranslationSearch, devEditors, historyIndex, duplicateTranslations, duplicateResultsBySet, context, executions, setIndexes, _i, executions_1, execution, outputRelativeDirectory, _a, _b, manifest;
1508
+ var outputDirectoryPath, dataDirectoryPath, withTranslationSearch, withDuplicates, progress, writer, stepStartedAt, devEditors, historyIndex, links, duplicateResultsBySet, _a, _b, context, executions, setIndexes, _i, executions_1, execution, outputRelativeDirectory, _c, _d, manifest;
1296
1509
  if (options === void 0) { options = {}; }
1297
- return __generator(this, function (_c) {
1298
- switch (_c.label) {
1510
+ return __generator(this, function (_e) {
1511
+ switch (_e.label) {
1299
1512
  case 0:
1300
1513
  outputDirectoryPath = options.outDir
1301
1514
  ? path.resolve(rootDirectoryPath, options.outDir)
1302
1515
  : projectConfig.catalogDirectoryPath;
1303
1516
  dataDirectoryPath = path.join(outputDirectoryPath, "data");
1304
1517
  withTranslationSearch = options.withTranslationSearch === true;
1518
+ withDuplicates = options.withDuplicates === true;
1519
+ progress = new CatalogProgressReporter(rootDirectoryPath, outputDirectoryPath);
1520
+ writer = new CatalogJsonWriter();
1521
+ progress.start({
1522
+ browserRouter: options.browserRouter !== false,
1523
+ sets: projectConfig.sets === true,
1524
+ features: __spreadArray(__spreadArray([], (withTranslationSearch ? ["translation search"] : []), true), (withDuplicates ? ["duplicates"] : []), true),
1525
+ });
1526
+ stepStartedAt = progress.step("Preparing output directory");
1305
1527
  return [4 /*yield*/, fs.promises.rm(outputDirectoryPath, { recursive: true, force: true })];
1306
1528
  case 1:
1307
- _c.sent();
1529
+ _e.sent();
1308
1530
  return [4 /*yield*/, fs.promises.mkdir(dataDirectoryPath, { recursive: true })];
1309
1531
  case 2:
1310
- _c.sent();
1532
+ _e.sent();
1533
+ progress.done(stepStartedAt);
1311
1534
  if (!(options.copyAssets !== false)) return [3 /*break*/, 4];
1535
+ stepStartedAt = progress.step("Copying Catalog UI assets");
1312
1536
  return [4 /*yield*/, copyCatalogAssets(outputDirectoryPath)];
1313
1537
  case 3:
1314
- _c.sent();
1315
- _c.label = 4;
1538
+ _e.sent();
1539
+ progress.done(stepStartedAt);
1540
+ _e.label = 4;
1316
1541
  case 4:
1317
1542
  devEditors = options.dev ? options.devEditors || detectDevEditors() : [];
1543
+ stepStartedAt = progress.step("Reading Git history");
1318
1544
  return [4 /*yield*/, getGitHistoryIndex(rootDirectoryPath, projectConfig)];
1319
1545
  case 5:
1320
- historyIndex = _c.sent();
1546
+ historyIndex = _e.sent();
1547
+ progress.done(stepStartedAt, "(".concat(pluralize(historyIndex.entries.length, "commit"), ")"));
1548
+ stepStartedAt = progress.step("Resolving repository links");
1549
+ links = getRepoLinks(rootDirectoryPath);
1550
+ progress.done(stepStartedAt);
1551
+ duplicateResultsBySet = {};
1552
+ if (!withDuplicates) return [3 /*break*/, 7];
1553
+ stepStartedAt = progress.step("Scanning duplicate translations");
1554
+ _b = (_a = Object).fromEntries;
1321
1555
  return [4 /*yield*/, runtime.findDuplicateTranslations(projectConfig, datasource)];
1322
1556
  case 6:
1323
- duplicateTranslations = _c.sent();
1324
- duplicateResultsBySet = Object.fromEntries(duplicateTranslations.results.map(function (result) { return [getDuplicateSetKey(result.set), result]; }));
1557
+ duplicateResultsBySet = _b.apply(_a, [(_e.sent()).results.map(function (result) { return [
1558
+ getDuplicateSetKey(result.set),
1559
+ result,
1560
+ ]; })]);
1561
+ progress.done(stepStartedAt, "(".concat(pluralize(Object.values(duplicateResultsBySet).reduce(function (total, result) {
1562
+ return total +
1563
+ result.locales.reduce(function (localeTotal, localeResult) { return localeTotal + localeResult.duplicateValues.length; }, 0);
1564
+ }, 0), "duplicate value"), ")"));
1565
+ _e.label = 7;
1566
+ case 7:
1325
1567
  context = {
1326
1568
  rootDirectoryPath: rootDirectoryPath,
1327
1569
  repositoryRootDirectoryPath: getRepositoryRootDirectoryPath(rootDirectoryPath),
1570
+ repositorySourceRootDirectoryPath: getRepositorySourceRootDirectoryPath(rootDirectoryPath),
1328
1571
  outputDirectoryPath: outputDirectoryPath,
1329
1572
  dataDirectoryPath: dataDirectoryPath,
1330
1573
  historyIndex: historyIndex,
@@ -1332,30 +1575,40 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1332
1575
  devEditors: devEditors,
1333
1576
  duplicateResultsBySet: duplicateResultsBySet,
1334
1577
  withTranslationSearch: withTranslationSearch,
1578
+ withDuplicates: withDuplicates,
1579
+ progress: progress,
1580
+ writer: writer,
1335
1581
  };
1582
+ stepStartedAt = progress.step("Discovering project sets");
1336
1583
  return [4 /*yield*/, runtime.getProjectSetExecutions(projectConfig, datasource)];
1337
- case 7:
1338
- executions = _c.sent();
1339
- setIndexes = {};
1340
- return [4 /*yield*/, writeHistoryPages(path.join(dataDirectoryPath, "project", "history"), historyIndex.entries)];
1341
1584
  case 8:
1342
- _c.sent();
1343
- _i = 0, executions_1 = executions;
1344
- _c.label = 9;
1585
+ executions = _e.sent();
1586
+ progress.done(stepStartedAt, projectConfig.sets
1587
+ ? "(".concat(executions.map(function (execution) { return execution.set; }).join(", ") || "none", ")")
1588
+ : "(root)");
1589
+ setIndexes = {};
1590
+ stepStartedAt = progress.step("Writing project history");
1591
+ return [4 /*yield*/, writeHistoryPages(writer, path.join(dataDirectoryPath, "project", "history"), historyIndex.entries)];
1345
1592
  case 9:
1346
- if (!(_i < executions_1.length)) return [3 /*break*/, 12];
1593
+ _e.sent();
1594
+ progress.done(stepStartedAt, "(".concat(pluralize(historyIndex.entries.length, "entry", "entries"), ")"));
1595
+ _i = 0, executions_1 = executions;
1596
+ _e.label = 10;
1597
+ case 10:
1598
+ if (!(_i < executions_1.length)) return [3 /*break*/, 13];
1347
1599
  execution = executions_1[_i];
1348
1600
  outputRelativeDirectory = projectConfig.sets ? path.join("sets", execution.set) : "root";
1349
- _a = setIndexes;
1350
- _b = execution.set || "root";
1601
+ _c = setIndexes;
1602
+ _d = execution.set || "root";
1351
1603
  return [4 /*yield*/, buildSetCatalog(context, execution.set, execution.projectConfig, execution.datasource, outputRelativeDirectory)];
1352
- case 10:
1353
- _a[_b] = _c.sent();
1354
- _c.label = 11;
1355
1604
  case 11:
1356
- _i++;
1357
- return [3 /*break*/, 9];
1605
+ _c[_d] = _e.sent();
1606
+ _e.label = 12;
1358
1607
  case 12:
1608
+ _i++;
1609
+ return [3 /*break*/, 10];
1610
+ case 13:
1611
+ stepStartedAt = progress.step("Writing manifest");
1359
1612
  manifest = {
1360
1613
  schemaVersion: exports.CATALOG_SCHEMA_VERSION,
1361
1614
  generatedAt: new Date().toISOString(),
@@ -1365,8 +1618,9 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1365
1618
  dev: options.dev ? { editors: devEditors } : undefined,
1366
1619
  features: {
1367
1620
  translationSearch: withTranslationSearch,
1621
+ duplicates: withDuplicates,
1368
1622
  },
1369
- links: getRepoLinks(rootDirectoryPath),
1623
+ links: links,
1370
1624
  paths: {
1371
1625
  projectHistory: "data/project/history/page-1.json",
1372
1626
  root: projectConfig.sets ? undefined : "data/root/index.json",
@@ -1379,10 +1633,11 @@ function exportCatalog(runtime_1, rootDirectoryPath_1, projectConfig_1, datasour
1379
1633
  },
1380
1634
  counts: Object.fromEntries(Object.keys(setIndexes).map(function (key) { return [key, setIndexes[key].counts]; })),
1381
1635
  };
1382
- return [4 /*yield*/, writeJson(path.join(dataDirectoryPath, "manifest.json"), manifest)];
1383
- case 13:
1384
- _c.sent();
1385
- console.log("Catalog exported to ".concat(outputDirectoryPath));
1636
+ return [4 /*yield*/, writer.write(path.join(dataDirectoryPath, "manifest.json"), manifest)];
1637
+ case 14:
1638
+ _e.sent();
1639
+ progress.done(stepStartedAt);
1640
+ progress.complete();
1386
1641
  return [2 /*return*/, {
1387
1642
  outputDirectoryPath: outputDirectoryPath,
1388
1643
  manifest: manifest,
@@ -1616,13 +1871,16 @@ function createCatalogApi(runtime) {
1616
1871
  function isWithTranslationSearchEnabled(parsed) {
1617
1872
  return parsed.withTranslationSearch === true || parsed["with-translation-search"] === true;
1618
1873
  }
1874
+ function isWithDuplicatesEnabled(parsed) {
1875
+ return parsed.withDuplicates === true || parsed["with-duplicates"] === true;
1876
+ }
1619
1877
  function createCatalogPlugin(runtime, api) {
1620
1878
  var _this = this;
1621
1879
  if (api === void 0) { api = createCatalogApi(runtime); }
1622
1880
  return {
1623
1881
  command: "catalog [subcommand]",
1624
1882
  handler: function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
1625
- var allowedSubcommands, browserRouter, withTranslationSearch, server_1, outputDirectoryPath, ignoredDirectoryPaths, exportInFlight_1, exportQueued_1, queuedReason_1, debounceTimer_1, runExportAndReload_1, stopWatchingProject;
1883
+ var allowedSubcommands, browserRouter, withTranslationSearch, withDuplicates, server_1, outputDirectoryPath, ignoredDirectoryPaths, exportInFlight_1, exportQueued_1, queuedReason_1, debounceTimer_1, runExportAndReload_1, stopWatchingProject;
1626
1884
  var _this = this;
1627
1885
  var rootDirectoryPath = _b.rootDirectoryPath, projectConfig = _b.projectConfig, datasource = _b.datasource, parsed = _b.parsed;
1628
1886
  return __generator(this, function (_c) {
@@ -1631,6 +1889,7 @@ function createCatalogPlugin(runtime, api) {
1631
1889
  allowedSubcommands = ["export", "serve"];
1632
1890
  browserRouter = !(parsed.hashRouter || parsed["hash-router"]);
1633
1891
  withTranslationSearch = isWithTranslationSearchEnabled(parsed);
1892
+ withDuplicates = isWithDuplicatesEnabled(parsed);
1634
1893
  if (!!parsed.subcommand) return [3 /*break*/, 3];
1635
1894
  return [4 /*yield*/, api.exportCatalog(rootDirectoryPath, projectConfig, datasource, {
1636
1895
  outDir: parsed.outDir,
@@ -1638,6 +1897,7 @@ function createCatalogPlugin(runtime, api) {
1638
1897
  browserRouter: browserRouter,
1639
1898
  dev: true,
1640
1899
  withTranslationSearch: withTranslationSearch,
1900
+ withDuplicates: withDuplicates,
1641
1901
  })];
1642
1902
  case 1:
1643
1903
  _c.sent();
@@ -1687,6 +1947,7 @@ function createCatalogPlugin(runtime, api) {
1687
1947
  browserRouter: browserRouter,
1688
1948
  dev: true,
1689
1949
  withTranslationSearch: withTranslationSearch,
1950
+ withDuplicates: withDuplicates,
1690
1951
  })];
1691
1952
  case 2:
1692
1953
  _a.sent();
@@ -1733,6 +1994,7 @@ function createCatalogPlugin(runtime, api) {
1733
1994
  copyAssets: !parsed.noAssets,
1734
1995
  browserRouter: browserRouter,
1735
1996
  withTranslationSearch: withTranslationSearch,
1997
+ withDuplicates: withDuplicates,
1736
1998
  })];
1737
1999
  case 4:
1738
2000
  _c.sent();