@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/dist/assets/index-Cx9rnVIG.js +73 -0
- package/dist/index.html +1 -1
- package/lib/node/index.d.ts +3 -0
- package/lib/node/index.js +573 -311
- package/lib/node/index.js.map +1 -1
- package/package.json +2 -2
- package/src/api.spec.ts +46 -1
- package/src/api.ts +29 -1
- package/src/node/index.spec.ts +369 -50
- package/src/node/index.ts +443 -66
- package/src/pages/EntityDetailPage.tsx +15 -4
- package/src/types.ts +1 -0
- package/dist/assets/index-BJS9aW0t.js +0 -73
package/src/node/index.ts
CHANGED
|
@@ -18,6 +18,123 @@ import type {
|
|
|
18
18
|
|
|
19
19
|
import { attachFormatExamplePreviews } from "./formatExamplePreview";
|
|
20
20
|
|
|
21
|
+
const CLI_FORMAT_GREEN = "\x1b[32m%s\x1b[0m";
|
|
22
|
+
const CLI_FORMAT_DIM = "\x1b[2m%s\x1b[0m";
|
|
23
|
+
const CLI_FORMAT_BOLD = "\x1b[1m%s\x1b[0m";
|
|
24
|
+
|
|
25
|
+
function colorize(value: string, colorCode: number) {
|
|
26
|
+
return `\x1b[${colorCode}m${value}\x1b[0m`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function prettyDuration(diffInMs: number) {
|
|
30
|
+
let diff = Math.abs(diffInMs);
|
|
31
|
+
|
|
32
|
+
if (diff === 0) {
|
|
33
|
+
return "0ms";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ms = diff % 1000;
|
|
37
|
+
diff = (diff - ms) / 1000;
|
|
38
|
+
const secs = diff % 60;
|
|
39
|
+
diff = (diff - secs) / 60;
|
|
40
|
+
const mins = diff % 60;
|
|
41
|
+
const hrs = (diff - mins) / 60;
|
|
42
|
+
|
|
43
|
+
let result = "";
|
|
44
|
+
|
|
45
|
+
if (hrs) {
|
|
46
|
+
result += ` ${hrs}h`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (mins) {
|
|
50
|
+
result += ` ${mins}m`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (secs) {
|
|
54
|
+
result += ` ${secs}s`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (ms) {
|
|
58
|
+
result += ` ${ms}ms`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result.trim();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function pluralize(count: number, singular: string, plural = `${singular}s`) {
|
|
65
|
+
return `${count} ${count === 1 ? singular : plural}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatCatalogPath(rootDirectoryPath: string, filePath: string) {
|
|
69
|
+
const relativePath = path.relative(rootDirectoryPath, filePath);
|
|
70
|
+
|
|
71
|
+
if (relativePath && !relativePath.startsWith("..") && !path.isAbsolute(relativePath)) {
|
|
72
|
+
return relativePath;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return filePath;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class CatalogProgressReporter {
|
|
79
|
+
private readonly startedAt = Date.now();
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
private readonly rootDirectoryPath: string,
|
|
83
|
+
private readonly outputDirectoryPath: string,
|
|
84
|
+
) {}
|
|
85
|
+
|
|
86
|
+
start(options: { browserRouter: boolean; sets: boolean; features: string[] }) {
|
|
87
|
+
console.log("");
|
|
88
|
+
console.log(CLI_FORMAT_BOLD, "Generating Messagevisor catalog");
|
|
89
|
+
console.log(
|
|
90
|
+
` ${colorize("Output", 36)}: ${formatCatalogPath(
|
|
91
|
+
this.rootDirectoryPath,
|
|
92
|
+
this.outputDirectoryPath,
|
|
93
|
+
)}`,
|
|
94
|
+
);
|
|
95
|
+
console.log(` ${colorize("Router", 36)}: ${options.browserRouter ? "browser" : "hash"}`);
|
|
96
|
+
console.log(` ${colorize("Sets", 36)}: ${options.sets ? "enabled" : "none"}`);
|
|
97
|
+
console.log(` ${colorize("Features", 36)}: ${options.features.join(", ") || "none"}`);
|
|
98
|
+
console.log("");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
step(label: string, detail?: string) {
|
|
102
|
+
const suffix = detail ? `: ${colorize(detail, 2)}` : "";
|
|
103
|
+
console.log(` ${colorize("•", 36)} ${label}${suffix}`);
|
|
104
|
+
return Date.now();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
substep(label: string, detail?: string) {
|
|
108
|
+
const suffix = detail ? `: ${colorize(detail, 2)}` : "";
|
|
109
|
+
console.log(` ${colorize("•", 36)} ${label}${suffix}`);
|
|
110
|
+
return Date.now();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
done(startedAt: number, detail?: string) {
|
|
114
|
+
const suffix = detail ? ` ${detail}` : "";
|
|
115
|
+
console.log(CLI_FORMAT_DIM, ` done in ${prettyDuration(Date.now() - startedAt)}${suffix}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setStart(set: string | undefined) {
|
|
119
|
+
console.log("");
|
|
120
|
+
if (set) {
|
|
121
|
+
console.log(CLI_FORMAT_BOLD, `Set "${set}"`);
|
|
122
|
+
} else {
|
|
123
|
+
console.log(CLI_FORMAT_BOLD, "Root catalog");
|
|
124
|
+
}
|
|
125
|
+
return Date.now();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
complete() {
|
|
129
|
+
console.log("");
|
|
130
|
+
console.log(
|
|
131
|
+
CLI_FORMAT_GREEN,
|
|
132
|
+
`Catalog exported to ${formatCatalogPath(this.rootDirectoryPath, this.outputDirectoryPath)}`,
|
|
133
|
+
);
|
|
134
|
+
console.log(CLI_FORMAT_BOLD, `Time: ${prettyDuration(Date.now() - this.startedAt)}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
21
138
|
export interface CatalogPluginParsedOptions {
|
|
22
139
|
_: string[];
|
|
23
140
|
[key: string]: any;
|
|
@@ -223,6 +340,7 @@ export interface CatalogExportOptions {
|
|
|
223
340
|
dev?: boolean;
|
|
224
341
|
devEditors?: CatalogDevEditor[];
|
|
225
342
|
withTranslationSearch?: boolean;
|
|
343
|
+
withDuplicates?: boolean;
|
|
226
344
|
}
|
|
227
345
|
|
|
228
346
|
export interface CatalogServeOptions {
|
|
@@ -240,6 +358,7 @@ export interface CatalogServerHandle {
|
|
|
240
358
|
interface CatalogBuildContext {
|
|
241
359
|
rootDirectoryPath: string;
|
|
242
360
|
repositoryRootDirectoryPath: string;
|
|
361
|
+
repositorySourceRootDirectoryPath: string;
|
|
243
362
|
outputDirectoryPath: string;
|
|
244
363
|
dataDirectoryPath: string;
|
|
245
364
|
historyIndex: CatalogHistoryIndex;
|
|
@@ -247,6 +366,9 @@ interface CatalogBuildContext {
|
|
|
247
366
|
devEditors: CatalogDevEditor[];
|
|
248
367
|
duplicateResultsBySet: Record<string, CatalogDuplicateTranslationsSetResult>;
|
|
249
368
|
withTranslationSearch: boolean;
|
|
369
|
+
withDuplicates: boolean;
|
|
370
|
+
progress: CatalogProgressReporter;
|
|
371
|
+
writer: CatalogJsonWriter;
|
|
250
372
|
}
|
|
251
373
|
|
|
252
374
|
interface SourceFileInfo {
|
|
@@ -609,9 +731,41 @@ function toLocaleDuplicatesFile(
|
|
|
609
731
|
};
|
|
610
732
|
}
|
|
611
733
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
734
|
+
class CatalogJsonWriter {
|
|
735
|
+
private readonly directories = new Map<string, Promise<void>>();
|
|
736
|
+
|
|
737
|
+
private ensureDirectory(directoryPath: string) {
|
|
738
|
+
let promise = this.directories.get(directoryPath);
|
|
739
|
+
|
|
740
|
+
if (!promise) {
|
|
741
|
+
promise = fs.promises.mkdir(directoryPath, { recursive: true }).then(() => undefined);
|
|
742
|
+
this.directories.set(directoryPath, promise);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return promise;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async write(filePath: string, content: unknown) {
|
|
749
|
+
await this.ensureDirectory(path.dirname(filePath));
|
|
750
|
+
await fs.promises.writeFile(filePath, JSON.stringify(content, null, 2));
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async function mapWithConcurrency<T>(
|
|
755
|
+
items: T[],
|
|
756
|
+
concurrency: number,
|
|
757
|
+
callback: (item: T, index: number) => Promise<void>,
|
|
758
|
+
) {
|
|
759
|
+
let nextIndex = 0;
|
|
760
|
+
const workerCount = Math.min(concurrency, items.length);
|
|
761
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
762
|
+
while (nextIndex < items.length) {
|
|
763
|
+
const index = nextIndex++;
|
|
764
|
+
await callback(items[index], index);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
await Promise.all(workers);
|
|
615
769
|
}
|
|
616
770
|
|
|
617
771
|
function getEntityDirectoryPaths(config: any): Record<CatalogEntityType | "test", string> {
|
|
@@ -756,6 +910,18 @@ function addHistoryIndexEntry(
|
|
|
756
910
|
target[key].push(entry);
|
|
757
911
|
}
|
|
758
912
|
|
|
913
|
+
function toEntityHistoryEntry(
|
|
914
|
+
entry: CatalogHistoryEntry,
|
|
915
|
+
entity: CatalogHistoryEntity,
|
|
916
|
+
): CatalogHistoryEntry {
|
|
917
|
+
return {
|
|
918
|
+
commit: entry.commit,
|
|
919
|
+
author: entry.author,
|
|
920
|
+
timestamp: entry.timestamp,
|
|
921
|
+
entities: [entity],
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
|
|
759
925
|
function buildCatalogHistoryIndex(entries: CatalogHistoryEntry[]): CatalogHistoryIndex {
|
|
760
926
|
const index = createEmptyHistoryIndex();
|
|
761
927
|
index.entries = entries;
|
|
@@ -769,7 +935,7 @@ function buildCatalogHistoryIndex(entries: CatalogHistoryEntry[]): CatalogHistor
|
|
|
769
935
|
}
|
|
770
936
|
|
|
771
937
|
const entityKey = getHistoryEntityKey(entity.type, entity.key, entity.set);
|
|
772
|
-
addHistoryIndexEntry(index.byEntity, entityKey, entry);
|
|
938
|
+
addHistoryIndexEntry(index.byEntity, entityKey, toEntityHistoryEntry(entry, entity));
|
|
773
939
|
|
|
774
940
|
if (!index.lastModifiedByEntity[entityKey]) {
|
|
775
941
|
index.lastModifiedByEntity[entityKey] = toLastModified(entry);
|
|
@@ -1015,6 +1181,25 @@ function getRepositoryRootDirectoryPath(rootDirectoryPath: string) {
|
|
|
1015
1181
|
}
|
|
1016
1182
|
}
|
|
1017
1183
|
|
|
1184
|
+
function getRepositorySourceRootDirectoryPath(rootDirectoryPath: string) {
|
|
1185
|
+
try {
|
|
1186
|
+
const gitRootDirectoryPath =
|
|
1187
|
+
runGit(rootDirectoryPath, ["rev-parse", "--show-toplevel"]).trim() || rootDirectoryPath;
|
|
1188
|
+
const realRootDirectoryPath = getRealPath(rootDirectoryPath);
|
|
1189
|
+
|
|
1190
|
+
if (realRootDirectoryPath !== rootDirectoryPath) {
|
|
1191
|
+
return path.resolve(
|
|
1192
|
+
rootDirectoryPath,
|
|
1193
|
+
path.relative(realRootDirectoryPath, gitRootDirectoryPath),
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
return gitRootDirectoryPath;
|
|
1198
|
+
} catch (_error) {
|
|
1199
|
+
return rootDirectoryPath;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1018
1203
|
function getOwnerAndRepoFromGitRemote(origin: string, host: string) {
|
|
1019
1204
|
const escapedHost = host.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1020
1205
|
const match = origin.match(new RegExp(`${escapedHost}[:/]([^/]+)/(.+?)(?:\\.git)?$`));
|
|
@@ -1090,17 +1275,28 @@ function chunkHistory(history: CatalogHistoryEntry[], pageSize = CATALOG_HISTORY
|
|
|
1090
1275
|
return pages.length > 0 ? pages : [[]];
|
|
1091
1276
|
}
|
|
1092
1277
|
|
|
1093
|
-
async function writeHistoryPages(
|
|
1278
|
+
async function writeHistoryPages(
|
|
1279
|
+
writer: CatalogJsonWriter,
|
|
1280
|
+
directoryPath: string,
|
|
1281
|
+
history: CatalogHistoryEntry[],
|
|
1282
|
+
options: { skipEmpty?: boolean } = {},
|
|
1283
|
+
) {
|
|
1284
|
+
if (options.skipEmpty && history.length === 0) {
|
|
1285
|
+
return 1;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1094
1288
|
const pages = chunkHistory(history);
|
|
1095
1289
|
|
|
1096
1290
|
for (let index = 0; index < pages.length; index++) {
|
|
1097
|
-
await
|
|
1291
|
+
await writer.write(path.join(directoryPath, `page-${index + 1}.json`), {
|
|
1098
1292
|
page: index + 1,
|
|
1099
1293
|
pageSize: CATALOG_HISTORY_PAGE_SIZE,
|
|
1100
1294
|
totalPages: pages.length,
|
|
1101
1295
|
entries: pages[index],
|
|
1102
1296
|
});
|
|
1103
1297
|
}
|
|
1298
|
+
|
|
1299
|
+
return 0;
|
|
1104
1300
|
}
|
|
1105
1301
|
|
|
1106
1302
|
function getHistoryForEntity(
|
|
@@ -1113,11 +1309,12 @@ function getHistoryForEntity(
|
|
|
1113
1309
|
}
|
|
1114
1310
|
|
|
1115
1311
|
function getSourceFileInfo(
|
|
1116
|
-
|
|
1312
|
+
repositorySourceRootDirectoryPath: string,
|
|
1117
1313
|
rootDirectoryPath: string,
|
|
1118
1314
|
projectConfig: any,
|
|
1119
1315
|
type: CatalogEntityType,
|
|
1120
1316
|
key: string,
|
|
1317
|
+
options: { resolveAbsolutePath?: boolean } = {},
|
|
1121
1318
|
): SourceFileInfo {
|
|
1122
1319
|
const directoryByType: Record<CatalogEntityType, string> = {
|
|
1123
1320
|
locale: projectConfig.localesDirectoryPath,
|
|
@@ -1134,10 +1331,10 @@ function getSourceFileInfo(
|
|
|
1134
1331
|
...key.split(projectConfig.namespaceCharacter),
|
|
1135
1332
|
) + extension,
|
|
1136
1333
|
);
|
|
1137
|
-
const absolutePath = getRealPath(filePath);
|
|
1334
|
+
const absolutePath = options.resolveAbsolutePath ? getRealPath(filePath) : filePath;
|
|
1138
1335
|
|
|
1139
1336
|
return {
|
|
1140
|
-
sourcePath: toPosixPath(path.relative(
|
|
1337
|
+
sourcePath: toPosixPath(path.relative(repositorySourceRootDirectoryPath, filePath)),
|
|
1141
1338
|
absolutePath,
|
|
1142
1339
|
};
|
|
1143
1340
|
}
|
|
@@ -1168,13 +1365,15 @@ async function buildSetCatalog(
|
|
|
1168
1365
|
outputRelativeDirectory: string,
|
|
1169
1366
|
) {
|
|
1170
1367
|
const outputDirectoryPath = path.join(context.dataDirectoryPath, outputRelativeDirectory);
|
|
1171
|
-
const
|
|
1368
|
+
const setStartedAt = context.progress.setStart(set);
|
|
1369
|
+
const entitiesStartedAt = context.progress.step("Processing entities");
|
|
1370
|
+
const [localeKeys, messageKeys, attributeKeys, segmentKeys, targetKeys] = (await Promise.all([
|
|
1172
1371
|
datasource.listLocales(),
|
|
1173
1372
|
datasource.listMessages(),
|
|
1174
1373
|
datasource.listAttributes(),
|
|
1175
1374
|
datasource.listSegments(),
|
|
1176
1375
|
datasource.listTargets(),
|
|
1177
|
-
]);
|
|
1376
|
+
])) as [string[], string[], string[], string[], string[]];
|
|
1178
1377
|
const [locales, messages, attributes, segments, targets] = await Promise.all([
|
|
1179
1378
|
readAll<Locale>(localeKeys, (key) => datasource.readLocale(key)),
|
|
1180
1379
|
readAll<Message>(messageKeys, (key) => datasource.readMessage(key)),
|
|
@@ -1182,6 +1381,18 @@ async function buildSetCatalog(
|
|
|
1182
1381
|
readAll<Segment>(segmentKeys, (key) => datasource.readSegment(key)),
|
|
1183
1382
|
readAll<Target>(targetKeys, (key) => datasource.readTarget(key)),
|
|
1184
1383
|
]);
|
|
1384
|
+
context.progress.done(
|
|
1385
|
+
entitiesStartedAt,
|
|
1386
|
+
`(${[
|
|
1387
|
+
pluralize(localeKeys.length, "locale"),
|
|
1388
|
+
pluralize(messageKeys.length, "message"),
|
|
1389
|
+
pluralize(attributeKeys.length, "attribute"),
|
|
1390
|
+
pluralize(segmentKeys.length, "segment"),
|
|
1391
|
+
pluralize(targetKeys.length, "target"),
|
|
1392
|
+
].join(", ")})`,
|
|
1393
|
+
);
|
|
1394
|
+
|
|
1395
|
+
const relationshipsStartedAt = context.progress.step("Mapping relationships");
|
|
1185
1396
|
const messageTargets: Record<string, string[]> = {};
|
|
1186
1397
|
const targetMessages: Record<string, string[]> = {};
|
|
1187
1398
|
const localeTargets: Record<string, Set<string>> = {};
|
|
@@ -1282,6 +1493,7 @@ async function buildSetCatalog(
|
|
|
1282
1493
|
}
|
|
1283
1494
|
}
|
|
1284
1495
|
}
|
|
1496
|
+
context.progress.done(relationshipsStartedAt);
|
|
1285
1497
|
|
|
1286
1498
|
const history = set ? context.historyIndex.bySet[set] || [] : context.historyIndex.entries;
|
|
1287
1499
|
const localeDirections = getLocaleDirections(locales);
|
|
@@ -1310,8 +1522,11 @@ async function buildSetCatalog(
|
|
|
1310
1522
|
},
|
|
1311
1523
|
};
|
|
1312
1524
|
|
|
1313
|
-
|
|
1525
|
+
const historyStartedAt = context.progress.step("Writing history pages");
|
|
1526
|
+
await writeHistoryPages(context.writer, path.join(outputDirectoryPath, "history"), history);
|
|
1527
|
+
context.progress.done(historyStartedAt, `(${pluralize(history.length, "entry", "entries")})`);
|
|
1314
1528
|
|
|
1529
|
+
const examplesStartedAt = context.progress.step("Evaluating examples");
|
|
1315
1530
|
const evaluatedMessageExamplesByKey = (
|
|
1316
1531
|
await context.runtime.resolveExamples(projectConfig, datasource, {
|
|
1317
1532
|
onlyMessages: true,
|
|
@@ -1345,15 +1560,31 @@ async function buildSetCatalog(
|
|
|
1345
1560
|
});
|
|
1346
1561
|
return accumulator;
|
|
1347
1562
|
}, {});
|
|
1563
|
+
context.progress.done(
|
|
1564
|
+
examplesStartedAt,
|
|
1565
|
+
`(${pluralize(
|
|
1566
|
+
Object.values(evaluatedMessageExamplesByKey).reduce(
|
|
1567
|
+
(total, items) => total + items.length,
|
|
1568
|
+
0,
|
|
1569
|
+
),
|
|
1570
|
+
"message example",
|
|
1571
|
+
)}, ${pluralize(
|
|
1572
|
+
Object.values(evaluatedLocaleExamplesByKey).reduce((total, items) => total + items.length, 0),
|
|
1573
|
+
"locale example",
|
|
1574
|
+
)})`,
|
|
1575
|
+
);
|
|
1348
1576
|
|
|
1349
|
-
|
|
1577
|
+
const localesStartedAt = context.progress.step("Writing locales");
|
|
1578
|
+
let skippedEmptyHistoryCount = 0;
|
|
1579
|
+
await mapWithConcurrency(localeKeys, 32, async (localeKey) => {
|
|
1350
1580
|
const locale = locales[localeKey];
|
|
1351
1581
|
const sourceFileInfo = getSourceFileInfo(
|
|
1352
|
-
context.
|
|
1582
|
+
context.repositorySourceRootDirectoryPath,
|
|
1353
1583
|
context.rootDirectoryPath,
|
|
1354
1584
|
projectConfig,
|
|
1355
1585
|
"locale",
|
|
1356
1586
|
localeKey,
|
|
1587
|
+
{ resolveAbsolutePath: context.devEditors.length > 0 },
|
|
1357
1588
|
);
|
|
1358
1589
|
const detail = {
|
|
1359
1590
|
type: "locale",
|
|
@@ -1379,18 +1610,30 @@ async function buildSetCatalog(
|
|
|
1379
1610
|
targets: sortStrings(Array.from(localeTargets[localeKey] || [])),
|
|
1380
1611
|
}),
|
|
1381
1612
|
);
|
|
1382
|
-
await
|
|
1613
|
+
await context.writer.write(
|
|
1383
1614
|
path.join(outputDirectoryPath, "entities", "locale", `${encodeKey(localeKey)}.json`),
|
|
1384
1615
|
detail,
|
|
1385
1616
|
);
|
|
1386
|
-
await
|
|
1387
|
-
|
|
1388
|
-
toLocaleDuplicatesFile(localeKey, duplicatesByLocale),
|
|
1389
|
-
);
|
|
1390
|
-
await writeHistoryPages(
|
|
1617
|
+
skippedEmptyHistoryCount += await writeHistoryPages(
|
|
1618
|
+
context.writer,
|
|
1391
1619
|
path.join(outputDirectoryPath, "history", "locale", encodeKey(localeKey)),
|
|
1392
1620
|
getHistoryForEntity(context.historyIndex, "locale", localeKey, set || undefined),
|
|
1621
|
+
{ skipEmpty: true },
|
|
1393
1622
|
);
|
|
1623
|
+
});
|
|
1624
|
+
context.progress.done(localesStartedAt, `(${pluralize(localeKeys.length, "locale")})`);
|
|
1625
|
+
|
|
1626
|
+
if (context.withDuplicates) {
|
|
1627
|
+
const duplicatesStartedAt = context.progress.step("Writing duplicate reports");
|
|
1628
|
+
|
|
1629
|
+
await mapWithConcurrency(localeKeys, 32, async (localeKey) => {
|
|
1630
|
+
await context.writer.write(
|
|
1631
|
+
path.join(outputDirectoryPath, "duplicates", "locales", `${encodeKey(localeKey)}.json`),
|
|
1632
|
+
toLocaleDuplicatesFile(localeKey, duplicatesByLocale),
|
|
1633
|
+
);
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
context.progress.done(duplicatesStartedAt, `(${pluralize(localeKeys.length, "locale")})`);
|
|
1394
1637
|
}
|
|
1395
1638
|
|
|
1396
1639
|
// translationShards[3charPrefix][messageKey] = Set<lowercased value>
|
|
@@ -1411,7 +1654,10 @@ async function buildSetCatalog(
|
|
|
1411
1654
|
}
|
|
1412
1655
|
}
|
|
1413
1656
|
|
|
1414
|
-
|
|
1657
|
+
const messagesStartedAt = context.progress.step("Writing messages");
|
|
1658
|
+
const messageDetailsStartedAt = context.progress.substep("Writing message details");
|
|
1659
|
+
let skippedEmptyMessageHistoryCount = 0;
|
|
1660
|
+
await mapWithConcurrency(messageKeys, 32, async (messageKey) => {
|
|
1415
1661
|
const message = messages[messageKey];
|
|
1416
1662
|
const overrides = (message.overrides || []).map((override: Override) => {
|
|
1417
1663
|
const attributes = new Set<string>();
|
|
@@ -1426,11 +1672,12 @@ async function buildSetCatalog(
|
|
|
1426
1672
|
};
|
|
1427
1673
|
});
|
|
1428
1674
|
const sourceFileInfo = getSourceFileInfo(
|
|
1429
|
-
context.
|
|
1675
|
+
context.repositorySourceRootDirectoryPath,
|
|
1430
1676
|
context.rootDirectoryPath,
|
|
1431
1677
|
projectConfig,
|
|
1432
1678
|
"message",
|
|
1433
1679
|
messageKey,
|
|
1680
|
+
{ resolveAbsolutePath: context.devEditors.length > 0 },
|
|
1434
1681
|
);
|
|
1435
1682
|
const detail = {
|
|
1436
1683
|
type: "message",
|
|
@@ -1465,22 +1712,6 @@ async function buildSetCatalog(
|
|
|
1465
1712
|
}
|
|
1466
1713
|
const overrideLocalesList = sortStrings(Array.from(overrideLocalesSet));
|
|
1467
1714
|
|
|
1468
|
-
if (context.withTranslationSearch) {
|
|
1469
|
-
// Build translation shards (direct + inherited + override, all locales combined)
|
|
1470
|
-
for (const localeKey of localeKeys) {
|
|
1471
|
-
const row = resolveTranslationRow(message.translations, localeKey, locales);
|
|
1472
|
-
if (row.source !== "missing" && row.value) {
|
|
1473
|
-
addToTranslationShard(messageKey, row.value);
|
|
1474
|
-
}
|
|
1475
|
-
for (const override of overrides) {
|
|
1476
|
-
const overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
|
|
1477
|
-
if (overrideRow.source !== "missing" && overrideRow.value) {
|
|
1478
|
-
addToTranslationShard(messageKey, overrideRow.value);
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
1715
|
index.entities.message.push(
|
|
1485
1716
|
getEntitySummary(message, "message", messageKey, context.historyIndex, set || undefined, {
|
|
1486
1717
|
targets: sortStrings(messageTargets[messageKey] || []),
|
|
@@ -1488,34 +1719,88 @@ async function buildSetCatalog(
|
|
|
1488
1719
|
...(overrideLocalesList.length > 0 ? { overrideLocales: overrideLocalesList } : {}),
|
|
1489
1720
|
}),
|
|
1490
1721
|
);
|
|
1491
|
-
await
|
|
1722
|
+
await context.writer.write(
|
|
1492
1723
|
path.join(outputDirectoryPath, "entities", "message", `${encodeKey(messageKey)}.json`),
|
|
1493
1724
|
detail,
|
|
1494
1725
|
);
|
|
1495
|
-
|
|
1726
|
+
});
|
|
1727
|
+
context.progress.done(messageDetailsStartedAt, `(${pluralize(messageKeys.length, "message")})`);
|
|
1728
|
+
|
|
1729
|
+
const messageHistoryStartedAt = context.progress.substep("Writing message history pages");
|
|
1730
|
+
await mapWithConcurrency(messageKeys, 32, async (messageKey) => {
|
|
1731
|
+
const skippedHistory = await writeHistoryPages(
|
|
1732
|
+
context.writer,
|
|
1496
1733
|
path.join(outputDirectoryPath, "history", "message", encodeKey(messageKey)),
|
|
1497
1734
|
getHistoryForEntity(context.historyIndex, "message", messageKey, set || undefined),
|
|
1735
|
+
{ skipEmpty: true },
|
|
1498
1736
|
);
|
|
1499
|
-
|
|
1737
|
+
skippedEmptyMessageHistoryCount += skippedHistory;
|
|
1738
|
+
skippedEmptyHistoryCount += skippedHistory;
|
|
1739
|
+
});
|
|
1740
|
+
context.progress.done(
|
|
1741
|
+
messageHistoryStartedAt,
|
|
1742
|
+
`(${pluralize(messageKeys.length, "message")}, ${pluralize(
|
|
1743
|
+
skippedEmptyMessageHistoryCount,
|
|
1744
|
+
"empty history",
|
|
1745
|
+
"empty histories",
|
|
1746
|
+
)} skipped)`,
|
|
1747
|
+
);
|
|
1748
|
+
context.progress.done(
|
|
1749
|
+
messagesStartedAt,
|
|
1750
|
+
`(${pluralize(messageKeys.length, "message")}, ${pluralize(
|
|
1751
|
+
skippedEmptyMessageHistoryCount,
|
|
1752
|
+
"empty history",
|
|
1753
|
+
"empty histories",
|
|
1754
|
+
)} skipped)`,
|
|
1755
|
+
);
|
|
1500
1756
|
|
|
1501
1757
|
if (context.withTranslationSearch) {
|
|
1758
|
+
const translationSearchStartedAt = context.progress.step("Building translation search shards");
|
|
1759
|
+
|
|
1760
|
+
for (const messageKey of messageKeys) {
|
|
1761
|
+
const message = messages[messageKey];
|
|
1762
|
+
|
|
1763
|
+
for (const localeKey of localeKeys) {
|
|
1764
|
+
const row = resolveTranslationRow(message.translations, localeKey, locales);
|
|
1765
|
+
if (row.source !== "missing" && row.value) {
|
|
1766
|
+
addToTranslationShard(messageKey, row.value);
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
for (const override of message.overrides || []) {
|
|
1770
|
+
const overrideRow = resolveTranslationRow(override.translations, localeKey, locales);
|
|
1771
|
+
if (overrideRow.source !== "missing" && overrideRow.value) {
|
|
1772
|
+
addToTranslationShard(messageKey, overrideRow.value);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1502
1778
|
for (const [prefix, messageMap] of Object.entries(translationShards)) {
|
|
1503
1779
|
const shardData: Record<string, string[]> = {};
|
|
1504
1780
|
for (const [msgKey, valueSet] of Object.entries(messageMap)) {
|
|
1505
1781
|
shardData[msgKey] = Array.from(valueSet);
|
|
1506
1782
|
}
|
|
1507
|
-
await
|
|
1783
|
+
await context.writer.write(
|
|
1784
|
+
path.join(outputDirectoryPath, "translations", `${prefix}.json`),
|
|
1785
|
+
shardData,
|
|
1786
|
+
);
|
|
1508
1787
|
}
|
|
1788
|
+
context.progress.done(
|
|
1789
|
+
translationSearchStartedAt,
|
|
1790
|
+
`(${pluralize(Object.keys(translationShards).length, "shard")})`,
|
|
1791
|
+
);
|
|
1509
1792
|
}
|
|
1510
1793
|
|
|
1511
|
-
|
|
1794
|
+
const attributesStartedAt = context.progress.step("Writing attributes");
|
|
1795
|
+
await mapWithConcurrency(attributeKeys, 32, async (attributeKey) => {
|
|
1512
1796
|
const attribute = attributes[attributeKey];
|
|
1513
1797
|
const sourceFileInfo = getSourceFileInfo(
|
|
1514
|
-
context.
|
|
1798
|
+
context.repositorySourceRootDirectoryPath,
|
|
1515
1799
|
context.rootDirectoryPath,
|
|
1516
1800
|
projectConfig,
|
|
1517
1801
|
"attribute",
|
|
1518
1802
|
attributeKey,
|
|
1803
|
+
{ resolveAbsolutePath: context.devEditors.length > 0 },
|
|
1519
1804
|
);
|
|
1520
1805
|
const detail = {
|
|
1521
1806
|
type: "attribute",
|
|
@@ -1547,26 +1832,31 @@ async function buildSetCatalog(
|
|
|
1547
1832
|
},
|
|
1548
1833
|
),
|
|
1549
1834
|
);
|
|
1550
|
-
await
|
|
1835
|
+
await context.writer.write(
|
|
1551
1836
|
path.join(outputDirectoryPath, "entities", "attribute", `${encodeKey(attributeKey)}.json`),
|
|
1552
1837
|
detail,
|
|
1553
1838
|
);
|
|
1554
|
-
await writeHistoryPages(
|
|
1839
|
+
skippedEmptyHistoryCount += await writeHistoryPages(
|
|
1840
|
+
context.writer,
|
|
1555
1841
|
path.join(outputDirectoryPath, "history", "attribute", encodeKey(attributeKey)),
|
|
1556
1842
|
getHistoryForEntity(context.historyIndex, "attribute", attributeKey, set || undefined),
|
|
1843
|
+
{ skipEmpty: true },
|
|
1557
1844
|
);
|
|
1558
|
-
}
|
|
1845
|
+
});
|
|
1846
|
+
context.progress.done(attributesStartedAt, `(${pluralize(attributeKeys.length, "attribute")})`);
|
|
1559
1847
|
|
|
1560
|
-
|
|
1848
|
+
const segmentsStartedAt = context.progress.step("Writing segments");
|
|
1849
|
+
await mapWithConcurrency(segmentKeys, 32, async (segmentKey) => {
|
|
1561
1850
|
const segment = segments[segmentKey];
|
|
1562
1851
|
const usedAttributes = new Set<string>();
|
|
1563
1852
|
collectAttributeKeysFromConditions(segment.conditions, usedAttributes);
|
|
1564
1853
|
const sourceFileInfo = getSourceFileInfo(
|
|
1565
|
-
context.
|
|
1854
|
+
context.repositorySourceRootDirectoryPath,
|
|
1566
1855
|
context.rootDirectoryPath,
|
|
1567
1856
|
projectConfig,
|
|
1568
1857
|
"segment",
|
|
1569
1858
|
segmentKey,
|
|
1859
|
+
{ resolveAbsolutePath: context.devEditors.length > 0 },
|
|
1570
1860
|
);
|
|
1571
1861
|
const detail = {
|
|
1572
1862
|
type: "segment",
|
|
@@ -1586,17 +1876,21 @@ async function buildSetCatalog(
|
|
|
1586
1876
|
targets: sortStrings(Array.from(segmentTargets[segmentKey] || [])),
|
|
1587
1877
|
}),
|
|
1588
1878
|
);
|
|
1589
|
-
await
|
|
1879
|
+
await context.writer.write(
|
|
1590
1880
|
path.join(outputDirectoryPath, "entities", "segment", `${encodeKey(segmentKey)}.json`),
|
|
1591
1881
|
detail,
|
|
1592
1882
|
);
|
|
1593
|
-
await writeHistoryPages(
|
|
1883
|
+
skippedEmptyHistoryCount += await writeHistoryPages(
|
|
1884
|
+
context.writer,
|
|
1594
1885
|
path.join(outputDirectoryPath, "history", "segment", encodeKey(segmentKey)),
|
|
1595
1886
|
getHistoryForEntity(context.historyIndex, "segment", segmentKey, set || undefined),
|
|
1887
|
+
{ skipEmpty: true },
|
|
1596
1888
|
);
|
|
1597
|
-
}
|
|
1889
|
+
});
|
|
1890
|
+
context.progress.done(segmentsStartedAt, `(${pluralize(segmentKeys.length, "segment")})`);
|
|
1598
1891
|
|
|
1599
|
-
|
|
1892
|
+
const targetsStartedAt = context.progress.step("Writing targets");
|
|
1893
|
+
await mapWithConcurrency(targetKeys, 32, async (targetKey) => {
|
|
1600
1894
|
const target = targets[targetKey];
|
|
1601
1895
|
const targetLocaleKeys = target.locales?.length ? target.locales : localeKeys;
|
|
1602
1896
|
const formatsByLocale: Record<string, FormatPresets | undefined> = {};
|
|
@@ -1608,11 +1902,12 @@ async function buildSetCatalog(
|
|
|
1608
1902
|
}
|
|
1609
1903
|
|
|
1610
1904
|
const sourceFileInfo = getSourceFileInfo(
|
|
1611
|
-
context.
|
|
1905
|
+
context.repositorySourceRootDirectoryPath,
|
|
1612
1906
|
context.rootDirectoryPath,
|
|
1613
1907
|
projectConfig,
|
|
1614
1908
|
"target",
|
|
1615
1909
|
targetKey,
|
|
1910
|
+
{ resolveAbsolutePath: context.devEditors.length > 0 },
|
|
1616
1911
|
);
|
|
1617
1912
|
const detail = {
|
|
1618
1913
|
type: "target",
|
|
@@ -1632,21 +1927,30 @@ async function buildSetCatalog(
|
|
|
1632
1927
|
messageCount: targetMessages[targetKey].length,
|
|
1633
1928
|
}),
|
|
1634
1929
|
);
|
|
1635
|
-
await
|
|
1930
|
+
await context.writer.write(
|
|
1636
1931
|
path.join(outputDirectoryPath, "entities", "target", `${encodeKey(targetKey)}.json`),
|
|
1637
1932
|
detail,
|
|
1638
1933
|
);
|
|
1639
|
-
await writeHistoryPages(
|
|
1934
|
+
skippedEmptyHistoryCount += await writeHistoryPages(
|
|
1935
|
+
context.writer,
|
|
1640
1936
|
path.join(outputDirectoryPath, "history", "target", encodeKey(targetKey)),
|
|
1641
1937
|
getHistoryForEntity(context.historyIndex, "target", targetKey, set || undefined),
|
|
1938
|
+
{ skipEmpty: true },
|
|
1642
1939
|
);
|
|
1643
|
-
}
|
|
1940
|
+
});
|
|
1941
|
+
context.progress.done(targetsStartedAt, `(${pluralize(targetKeys.length, "target")})`);
|
|
1644
1942
|
|
|
1943
|
+
const indexStartedAt = context.progress.step("Writing catalog index");
|
|
1645
1944
|
for (const type of Object.keys(index.entities) as CatalogEntityType[]) {
|
|
1646
1945
|
index.entities[type].sort((a, b) => a.key.localeCompare(b.key));
|
|
1647
1946
|
}
|
|
1648
1947
|
|
|
1649
|
-
await
|
|
1948
|
+
await context.writer.write(path.join(outputDirectoryPath, "index.json"), index);
|
|
1949
|
+
context.progress.done(indexStartedAt);
|
|
1950
|
+
context.progress.done(
|
|
1951
|
+
setStartedAt,
|
|
1952
|
+
`total (${pluralize(skippedEmptyHistoryCount, "empty history", "empty histories")} skipped)`,
|
|
1953
|
+
);
|
|
1650
1954
|
|
|
1651
1955
|
return index;
|
|
1652
1956
|
}
|
|
@@ -1685,23 +1989,69 @@ export async function exportCatalog(
|
|
|
1685
1989
|
: projectConfig.catalogDirectoryPath;
|
|
1686
1990
|
const dataDirectoryPath = path.join(outputDirectoryPath, "data");
|
|
1687
1991
|
const withTranslationSearch = options.withTranslationSearch === true;
|
|
1992
|
+
const withDuplicates = options.withDuplicates === true;
|
|
1993
|
+
const progress = new CatalogProgressReporter(rootDirectoryPath, outputDirectoryPath);
|
|
1994
|
+
const writer = new CatalogJsonWriter();
|
|
1995
|
+
|
|
1996
|
+
progress.start({
|
|
1997
|
+
browserRouter: options.browserRouter !== false,
|
|
1998
|
+
sets: projectConfig.sets === true,
|
|
1999
|
+
features: [
|
|
2000
|
+
...(withTranslationSearch ? ["translation search"] : []),
|
|
2001
|
+
...(withDuplicates ? ["duplicates"] : []),
|
|
2002
|
+
],
|
|
2003
|
+
});
|
|
1688
2004
|
|
|
2005
|
+
let stepStartedAt = progress.step("Preparing output directory");
|
|
1689
2006
|
await fs.promises.rm(outputDirectoryPath, { recursive: true, force: true });
|
|
1690
2007
|
await fs.promises.mkdir(dataDirectoryPath, { recursive: true });
|
|
2008
|
+
progress.done(stepStartedAt);
|
|
1691
2009
|
|
|
1692
2010
|
if (options.copyAssets !== false) {
|
|
2011
|
+
stepStartedAt = progress.step("Copying Catalog UI assets");
|
|
1693
2012
|
await copyCatalogAssets(outputDirectoryPath);
|
|
2013
|
+
progress.done(stepStartedAt);
|
|
1694
2014
|
}
|
|
1695
2015
|
|
|
1696
2016
|
const devEditors = options.dev ? options.devEditors || detectDevEditors() : [];
|
|
2017
|
+
stepStartedAt = progress.step("Reading Git history");
|
|
1697
2018
|
const historyIndex = await getGitHistoryIndex(rootDirectoryPath, projectConfig);
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
);
|
|
2019
|
+
progress.done(stepStartedAt, `(${pluralize(historyIndex.entries.length, "commit")})`);
|
|
2020
|
+
|
|
2021
|
+
stepStartedAt = progress.step("Resolving repository links");
|
|
2022
|
+
const links = getRepoLinks(rootDirectoryPath);
|
|
2023
|
+
progress.done(stepStartedAt);
|
|
2024
|
+
|
|
2025
|
+
let duplicateResultsBySet: Record<string, CatalogDuplicateTranslationsSetResult> = {};
|
|
2026
|
+
if (withDuplicates) {
|
|
2027
|
+
stepStartedAt = progress.step("Scanning duplicate translations");
|
|
2028
|
+
duplicateResultsBySet = Object.fromEntries(
|
|
2029
|
+
(await runtime.findDuplicateTranslations(projectConfig, datasource)).results.map((result) => [
|
|
2030
|
+
getDuplicateSetKey(result.set),
|
|
2031
|
+
result,
|
|
2032
|
+
]),
|
|
2033
|
+
);
|
|
2034
|
+
progress.done(
|
|
2035
|
+
stepStartedAt,
|
|
2036
|
+
`(${pluralize(
|
|
2037
|
+
Object.values(duplicateResultsBySet).reduce(
|
|
2038
|
+
(total, result) =>
|
|
2039
|
+
total +
|
|
2040
|
+
result.locales.reduce(
|
|
2041
|
+
(localeTotal, localeResult) => localeTotal + localeResult.duplicateValues.length,
|
|
2042
|
+
0,
|
|
2043
|
+
),
|
|
2044
|
+
0,
|
|
2045
|
+
),
|
|
2046
|
+
"duplicate value",
|
|
2047
|
+
)})`,
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
1702
2051
|
const context: CatalogBuildContext = {
|
|
1703
2052
|
rootDirectoryPath,
|
|
1704
2053
|
repositoryRootDirectoryPath: getRepositoryRootDirectoryPath(rootDirectoryPath),
|
|
2054
|
+
repositorySourceRootDirectoryPath: getRepositorySourceRootDirectoryPath(rootDirectoryPath),
|
|
1705
2055
|
outputDirectoryPath,
|
|
1706
2056
|
dataDirectoryPath,
|
|
1707
2057
|
historyIndex,
|
|
@@ -1709,11 +2059,27 @@ export async function exportCatalog(
|
|
|
1709
2059
|
devEditors,
|
|
1710
2060
|
duplicateResultsBySet,
|
|
1711
2061
|
withTranslationSearch,
|
|
2062
|
+
withDuplicates,
|
|
2063
|
+
progress,
|
|
2064
|
+
writer,
|
|
1712
2065
|
};
|
|
2066
|
+
stepStartedAt = progress.step("Discovering project sets");
|
|
1713
2067
|
const executions = await runtime.getProjectSetExecutions(projectConfig, datasource);
|
|
2068
|
+
progress.done(
|
|
2069
|
+
stepStartedAt,
|
|
2070
|
+
projectConfig.sets
|
|
2071
|
+
? `(${executions.map((execution) => execution.set).join(", ") || "none"})`
|
|
2072
|
+
: "(root)",
|
|
2073
|
+
);
|
|
1714
2074
|
const setIndexes: Record<string, CatalogSetIndex> = {};
|
|
1715
2075
|
|
|
1716
|
-
|
|
2076
|
+
stepStartedAt = progress.step("Writing project history");
|
|
2077
|
+
await writeHistoryPages(
|
|
2078
|
+
writer,
|
|
2079
|
+
path.join(dataDirectoryPath, "project", "history"),
|
|
2080
|
+
historyIndex.entries,
|
|
2081
|
+
);
|
|
2082
|
+
progress.done(stepStartedAt, `(${pluralize(historyIndex.entries.length, "entry", "entries")})`);
|
|
1717
2083
|
|
|
1718
2084
|
for (const execution of executions) {
|
|
1719
2085
|
const outputRelativeDirectory = projectConfig.sets ? path.join("sets", execution.set) : "root";
|
|
@@ -1726,6 +2092,7 @@ export async function exportCatalog(
|
|
|
1726
2092
|
);
|
|
1727
2093
|
}
|
|
1728
2094
|
|
|
2095
|
+
stepStartedAt = progress.step("Writing manifest");
|
|
1729
2096
|
const manifest = {
|
|
1730
2097
|
schemaVersion: CATALOG_SCHEMA_VERSION,
|
|
1731
2098
|
generatedAt: new Date().toISOString(),
|
|
@@ -1735,8 +2102,9 @@ export async function exportCatalog(
|
|
|
1735
2102
|
dev: options.dev ? { editors: devEditors } : undefined,
|
|
1736
2103
|
features: {
|
|
1737
2104
|
translationSearch: withTranslationSearch,
|
|
2105
|
+
duplicates: withDuplicates,
|
|
1738
2106
|
},
|
|
1739
|
-
links
|
|
2107
|
+
links,
|
|
1740
2108
|
paths: {
|
|
1741
2109
|
projectHistory: "data/project/history/page-1.json",
|
|
1742
2110
|
root: projectConfig.sets ? undefined : "data/root/index.json",
|
|
@@ -1752,9 +2120,10 @@ export async function exportCatalog(
|
|
|
1752
2120
|
counts: Object.fromEntries(Object.keys(setIndexes).map((key) => [key, setIndexes[key].counts])),
|
|
1753
2121
|
};
|
|
1754
2122
|
|
|
1755
|
-
await
|
|
2123
|
+
await writer.write(path.join(dataDirectoryPath, "manifest.json"), manifest);
|
|
2124
|
+
progress.done(stepStartedAt);
|
|
1756
2125
|
|
|
1757
|
-
|
|
2126
|
+
progress.complete();
|
|
1758
2127
|
|
|
1759
2128
|
return {
|
|
1760
2129
|
outputDirectoryPath,
|
|
@@ -2032,6 +2401,10 @@ function isWithTranslationSearchEnabled(parsed: CatalogPluginParsedOptions) {
|
|
|
2032
2401
|
return parsed.withTranslationSearch === true || parsed["with-translation-search"] === true;
|
|
2033
2402
|
}
|
|
2034
2403
|
|
|
2404
|
+
function isWithDuplicatesEnabled(parsed: CatalogPluginParsedOptions) {
|
|
2405
|
+
return parsed.withDuplicates === true || parsed["with-duplicates"] === true;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2035
2408
|
export function createCatalogPlugin(
|
|
2036
2409
|
runtime: CatalogRuntime,
|
|
2037
2410
|
api: ReturnType<typeof createCatalogApi> = createCatalogApi(runtime),
|
|
@@ -2042,6 +2415,7 @@ export function createCatalogPlugin(
|
|
|
2042
2415
|
const allowedSubcommands = ["export", "serve"];
|
|
2043
2416
|
const browserRouter = !(parsed.hashRouter || parsed["hash-router"]);
|
|
2044
2417
|
const withTranslationSearch = isWithTranslationSearchEnabled(parsed);
|
|
2418
|
+
const withDuplicates = isWithDuplicatesEnabled(parsed);
|
|
2045
2419
|
|
|
2046
2420
|
if (!parsed.subcommand) {
|
|
2047
2421
|
await api.exportCatalog(rootDirectoryPath, projectConfig, datasource, {
|
|
@@ -2050,6 +2424,7 @@ export function createCatalogPlugin(
|
|
|
2050
2424
|
browserRouter,
|
|
2051
2425
|
dev: true,
|
|
2052
2426
|
withTranslationSearch,
|
|
2427
|
+
withDuplicates,
|
|
2053
2428
|
});
|
|
2054
2429
|
const server = await api.serveCatalog(rootDirectoryPath, projectConfig, datasource, {
|
|
2055
2430
|
outDir: parsed.outDir,
|
|
@@ -2093,6 +2468,7 @@ export function createCatalogPlugin(
|
|
|
2093
2468
|
browserRouter,
|
|
2094
2469
|
dev: true,
|
|
2095
2470
|
withTranslationSearch,
|
|
2471
|
+
withDuplicates,
|
|
2096
2472
|
});
|
|
2097
2473
|
server.triggerReload();
|
|
2098
2474
|
} catch (error) {
|
|
@@ -2141,6 +2517,7 @@ export function createCatalogPlugin(
|
|
|
2141
2517
|
copyAssets: !parsed.noAssets,
|
|
2142
2518
|
browserRouter,
|
|
2143
2519
|
withTranslationSearch,
|
|
2520
|
+
withDuplicates,
|
|
2144
2521
|
});
|
|
2145
2522
|
}
|
|
2146
2523
|
|