@lang-tag/cli 0.16.0 → 0.17.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/README.md +23 -14
- package/algorithms/collector/dictionary-collector.d.ts +2 -2
- package/algorithms/collector/index.d.ts +3 -3
- package/algorithms/collector/namespace-collector.d.ts +2 -2
- package/algorithms/collector/type.d.ts +2 -2
- package/algorithms/config-generation/config-keeper.d.ts +1 -1
- package/algorithms/config-generation/index.d.ts +3 -3
- package/algorithms/config-generation/path-based-config-generator.d.ts +1 -1
- package/algorithms/config-generation/prepend-namespace-to-path.d.ts +1 -1
- package/algorithms/import/flexible-import-algorithm.d.ts +1 -1
- package/algorithms/import/index.d.ts +2 -2
- package/algorithms/import/simple-mapping-import-algorithm.d.ts +1 -1
- package/algorithms/index.cjs +380 -27
- package/algorithms/index.d.ts +3 -3
- package/algorithms/index.js +361 -25
- package/chunks/namespace-collector.cjs +75 -0
- package/chunks/namespace-collector.js +76 -0
- package/index.cjs +1172 -830
- package/index.js +1171 -829
- package/logger.d.ts +1 -1
- package/package.json +1 -1
- package/{config.d.ts → type.d.ts} +3 -3
- package/flexible-import-algorithm-C-S1c742.js +0 -311
- package/flexible-import-algorithm-Fa-l4jWj.cjs +0 -327
package/index.cjs
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
const commander = require("commander");
|
|
5
|
+
const process$1 = require("node:process");
|
|
5
6
|
const fs = require("fs");
|
|
6
|
-
const promises = require("fs/promises");
|
|
7
|
-
const JSON5 = require("json5");
|
|
8
|
-
const path = require("path");
|
|
9
7
|
const globby = require("globby");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const JSON5 = require("json5");
|
|
10
|
+
const promises = require("fs/promises");
|
|
10
11
|
const path$1 = require("pathe");
|
|
11
12
|
const url = require("url");
|
|
12
13
|
require("case");
|
|
13
|
-
const
|
|
14
|
-
const flexibleImportAlgorithm = require("./flexible-import-algorithm-Fa-l4jWj.cjs");
|
|
15
|
-
const acorn = require("acorn");
|
|
14
|
+
const namespaceCollector = require("./chunks/namespace-collector.cjs");
|
|
16
15
|
const micromatch = require("micromatch");
|
|
17
|
-
const
|
|
16
|
+
const acorn = require("acorn");
|
|
18
17
|
const mustache = require("mustache");
|
|
18
|
+
const chokidar = require("chokidar");
|
|
19
19
|
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
20
20
|
function _interopNamespaceDefault(e) {
|
|
21
21
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -33,9 +33,61 @@ function _interopNamespaceDefault(e) {
|
|
|
33
33
|
n.default = e;
|
|
34
34
|
return Object.freeze(n);
|
|
35
35
|
}
|
|
36
|
-
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
37
36
|
const process__namespace = /* @__PURE__ */ _interopNamespaceDefault(process$1);
|
|
37
|
+
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
38
38
|
const acorn__namespace = /* @__PURE__ */ _interopNamespaceDefault(acorn);
|
|
39
|
+
function $LT_FilterInvalidTags(tags, config, logger) {
|
|
40
|
+
return tags.filter((tag) => {
|
|
41
|
+
if (tag.validity === "invalid-param-1")
|
|
42
|
+
logger.debug(
|
|
43
|
+
'Skipping tag "{fullMatch}". Invalid JSON: "{invalid}"',
|
|
44
|
+
{
|
|
45
|
+
fullMatch: tag.fullMatch.trim(),
|
|
46
|
+
invalid: tag.parameter1Text
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
if (tag.validity === "invalid-param-2")
|
|
50
|
+
logger.debug(
|
|
51
|
+
'Skipping tag "{fullMatch}". Invalid JSON: "{invalid}"',
|
|
52
|
+
{
|
|
53
|
+
fullMatch: tag.fullMatch.trim(),
|
|
54
|
+
invalid: tag.parameter2Text
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
if (tag.validity === "translations-not-found")
|
|
58
|
+
logger.debug(
|
|
59
|
+
'Skipping tag "{fullMatch}". Translations not found at parameter position: {pos}',
|
|
60
|
+
{
|
|
61
|
+
fullMatch: tag.fullMatch.trim(),
|
|
62
|
+
pos: config.translationArgPosition
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
return tag.validity === "ok";
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function $LT_FilterEmptyNamespaceTags(tags, logger) {
|
|
69
|
+
return tags.filter((tag) => {
|
|
70
|
+
if (!tag.parameterConfig) {
|
|
71
|
+
logger.warn(
|
|
72
|
+
'Skipping tag "{fullMatch}". Tag configuration not defined. (Check lang-tag config at collect.onCollectConfigFix)',
|
|
73
|
+
{
|
|
74
|
+
fullMatch: tag.fullMatch.trim()
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (!tag.parameterConfig.namespace) {
|
|
80
|
+
logger.warn(
|
|
81
|
+
'Skipping tag "{fullMatch}". Tag configuration namespace not defined. (Check lang-tag config at collect.onCollectConfigFix)',
|
|
82
|
+
{
|
|
83
|
+
fullMatch: tag.fullMatch.trim()
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
39
91
|
class $LT_TagProcessor {
|
|
40
92
|
constructor(config) {
|
|
41
93
|
this.config = config;
|
|
@@ -46,7 +98,10 @@ class $LT_TagProcessor {
|
|
|
46
98
|
const matches = [];
|
|
47
99
|
let currentIndex = 0;
|
|
48
100
|
const skipRanges = this.buildSkipRanges(fileContent);
|
|
49
|
-
const startPattern = new RegExp(
|
|
101
|
+
const startPattern = new RegExp(
|
|
102
|
+
`${optionalVariableAssignment}${tagName}\\(\\s*\\{`,
|
|
103
|
+
"g"
|
|
104
|
+
);
|
|
50
105
|
while (true) {
|
|
51
106
|
startPattern.lastIndex = currentIndex;
|
|
52
107
|
const startMatch = startPattern.exec(fileContent);
|
|
@@ -68,7 +123,10 @@ class $LT_TagProcessor {
|
|
|
68
123
|
currentIndex = matchStartIndex + 1;
|
|
69
124
|
continue;
|
|
70
125
|
}
|
|
71
|
-
let parameter1Text = fileContent.substring(
|
|
126
|
+
let parameter1Text = fileContent.substring(
|
|
127
|
+
matchStartIndex + startMatch[0].length - 1,
|
|
128
|
+
i
|
|
129
|
+
);
|
|
72
130
|
let parameter2Text;
|
|
73
131
|
while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
|
|
74
132
|
i++;
|
|
@@ -120,7 +178,10 @@ class $LT_TagProcessor {
|
|
|
120
178
|
}
|
|
121
179
|
i++;
|
|
122
180
|
const fullMatch = fileContent.substring(matchStartIndex, i);
|
|
123
|
-
const { line, column } = getLineAndColumn(
|
|
181
|
+
const { line, column } = getLineAndColumn(
|
|
182
|
+
fileContent,
|
|
183
|
+
matchStartIndex
|
|
184
|
+
);
|
|
124
185
|
let validity = "ok";
|
|
125
186
|
let parameter1 = void 0;
|
|
126
187
|
let parameter2 = void 0;
|
|
@@ -135,7 +196,9 @@ class $LT_TagProcessor {
|
|
|
135
196
|
parameter2 = JSON5.parse(parameter2Text);
|
|
136
197
|
} catch (error) {
|
|
137
198
|
try {
|
|
138
|
-
parameter2 = JSON5.parse(
|
|
199
|
+
parameter2 = JSON5.parse(
|
|
200
|
+
this.escapeNewlinesInStrings(parameter2Text)
|
|
201
|
+
);
|
|
139
202
|
} catch {
|
|
140
203
|
validity = "invalid-param-2";
|
|
141
204
|
}
|
|
@@ -143,7 +206,9 @@ class $LT_TagProcessor {
|
|
|
143
206
|
}
|
|
144
207
|
} catch (error) {
|
|
145
208
|
try {
|
|
146
|
-
parameter1 = JSON5.parse(
|
|
209
|
+
parameter1 = JSON5.parse(
|
|
210
|
+
this.escapeNewlinesInStrings(parameter1Text)
|
|
211
|
+
);
|
|
147
212
|
} catch {
|
|
148
213
|
validity = "invalid-param-1";
|
|
149
214
|
}
|
|
@@ -177,22 +242,31 @@ class $LT_TagProcessor {
|
|
|
177
242
|
}
|
|
178
243
|
const tag = R.tag;
|
|
179
244
|
let newTranslationsString = R.translations;
|
|
180
|
-
if (!newTranslationsString)
|
|
181
|
-
|
|
182
|
-
if (
|
|
245
|
+
if (!newTranslationsString)
|
|
246
|
+
newTranslationsString = this.config.translationArgPosition === 1 ? tag.parameter1Text : tag.parameter2Text || "{}";
|
|
247
|
+
else if (typeof newTranslationsString !== "string")
|
|
248
|
+
newTranslationsString = JSON5.stringify(newTranslationsString);
|
|
249
|
+
if (!newTranslationsString)
|
|
250
|
+
throw new Error("Tag must have translations provided!");
|
|
183
251
|
try {
|
|
184
252
|
JSON5.parse(newTranslationsString);
|
|
185
253
|
} catch (error) {
|
|
186
|
-
throw new Error(
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Tag translations are invalid object! Translations: ${newTranslationsString}`
|
|
256
|
+
);
|
|
187
257
|
}
|
|
188
258
|
let newConfigString = R.config;
|
|
189
|
-
if (!newConfigString && newConfigString !== null)
|
|
259
|
+
if (!newConfigString && newConfigString !== null)
|
|
260
|
+
newConfigString = tag.parameterConfig;
|
|
190
261
|
if (newConfigString) {
|
|
191
262
|
try {
|
|
192
|
-
if (typeof newConfigString === "string")
|
|
263
|
+
if (typeof newConfigString === "string")
|
|
264
|
+
JSON5.parse(newConfigString);
|
|
193
265
|
else newConfigString = JSON5.stringify(newConfigString);
|
|
194
266
|
} catch (error) {
|
|
195
|
-
throw new Error(
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Tag config is invalid object! Config: ${newConfigString}`
|
|
269
|
+
);
|
|
196
270
|
}
|
|
197
271
|
}
|
|
198
272
|
if (newConfigString === null && this.config.translationArgPosition === 2) {
|
|
@@ -203,7 +277,8 @@ class $LT_TagProcessor {
|
|
|
203
277
|
let tagFunction = `${this.config.tagName}(${arg1}`;
|
|
204
278
|
if (arg2) tagFunction += `, ${arg2}`;
|
|
205
279
|
tagFunction += ")";
|
|
206
|
-
if (tag.variableName)
|
|
280
|
+
if (tag.variableName)
|
|
281
|
+
replaceMap.set(tag, ` ${tag.variableName} = ${tagFunction}`);
|
|
207
282
|
else replaceMap.set(tag, tagFunction);
|
|
208
283
|
});
|
|
209
284
|
let offset = 0;
|
|
@@ -396,127 +471,397 @@ function getLineAndColumn(text, matchIndex) {
|
|
|
396
471
|
const column = lines[lines.length - 1].length + 1;
|
|
397
472
|
return { line, column };
|
|
398
473
|
}
|
|
399
|
-
function $
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
function $LT_FilterEmptyNamespaceTags(tags, logger) {
|
|
420
|
-
return tags.filter((tag) => {
|
|
421
|
-
if (!tag.parameterConfig) {
|
|
422
|
-
logger.warn('Skipping tag "{fullMatch}". Tag configuration not defined. (Check lang-tag config at collect.onCollectConfigFix)', {
|
|
423
|
-
fullMatch: tag.fullMatch.trim()
|
|
424
|
-
});
|
|
425
|
-
return false;
|
|
474
|
+
async function $LT_CollectCandidateFilesWithTags(props) {
|
|
475
|
+
const { config, logger } = props;
|
|
476
|
+
const processor = new $LT_TagProcessor(config);
|
|
477
|
+
const cwd = process$1.cwd();
|
|
478
|
+
let filesToScan = props.filesToScan;
|
|
479
|
+
if (!filesToScan) {
|
|
480
|
+
filesToScan = await globby.globby(config.includes, {
|
|
481
|
+
cwd,
|
|
482
|
+
ignore: config.excludes,
|
|
483
|
+
absolute: true
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
const candidates = [];
|
|
487
|
+
for (const filePath of filesToScan) {
|
|
488
|
+
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
489
|
+
let tags = processor.extractTags(fileContent);
|
|
490
|
+
if (!tags.length) {
|
|
491
|
+
continue;
|
|
426
492
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
});
|
|
431
|
-
return false;
|
|
493
|
+
tags = $LT_FilterInvalidTags(tags, config, logger);
|
|
494
|
+
if (!tags.length) {
|
|
495
|
+
continue;
|
|
432
496
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
for (const name of propNames) {
|
|
439
|
-
const value = obj[name];
|
|
440
|
-
if (value && typeof value === "object") {
|
|
441
|
-
deepFreezeObject(value);
|
|
497
|
+
for (let tag of tags) {
|
|
498
|
+
tag.parameterConfig = config.collect.onCollectConfigFix({
|
|
499
|
+
config: tag.parameterConfig,
|
|
500
|
+
langTagConfig: config
|
|
501
|
+
});
|
|
442
502
|
}
|
|
503
|
+
tags = $LT_FilterEmptyNamespaceTags(tags, logger);
|
|
504
|
+
const relativeFilePath = path.relative(cwd, filePath);
|
|
505
|
+
candidates.push({ relativeFilePath, tags });
|
|
443
506
|
}
|
|
444
|
-
return
|
|
507
|
+
return candidates;
|
|
445
508
|
}
|
|
446
|
-
async function
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
let
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
509
|
+
async function $LT_GroupTagsToCollections({
|
|
510
|
+
logger,
|
|
511
|
+
files,
|
|
512
|
+
config
|
|
513
|
+
}) {
|
|
514
|
+
let totalTags = 0;
|
|
515
|
+
const collections = {};
|
|
516
|
+
function getTranslationsCollection(namespace) {
|
|
517
|
+
const collectionName = config.collect.collector.aggregateCollection(namespace);
|
|
518
|
+
const collection = collections[collectionName] || {};
|
|
519
|
+
if (!(collectionName in collections)) {
|
|
520
|
+
collections[collectionName] = collection;
|
|
521
|
+
}
|
|
522
|
+
return collection;
|
|
455
523
|
}
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
for (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
shouldUpdate = true;
|
|
474
|
-
event.isSaved = true;
|
|
475
|
-
event.savedConfig = updatedConfig;
|
|
476
|
-
logger.debug('Called save for "{path}" with config "{config}" triggered by: ("{trigger}")', { path: path$12, config: JSON.stringify(updatedConfig), trigger: triggerName || "-" });
|
|
524
|
+
const allConflicts = [];
|
|
525
|
+
const existingValuesByNamespace = /* @__PURE__ */ new Map();
|
|
526
|
+
for (const file of files) {
|
|
527
|
+
totalTags += file.tags.length;
|
|
528
|
+
for (const _tag of file.tags) {
|
|
529
|
+
const tag = config.collect.collector.transformTag(_tag);
|
|
530
|
+
const tagConfig = tag.parameterConfig;
|
|
531
|
+
const collection = getTranslationsCollection(tagConfig.namespace);
|
|
532
|
+
let existingValues = existingValuesByNamespace.get(
|
|
533
|
+
tagConfig.namespace
|
|
534
|
+
);
|
|
535
|
+
if (!existingValues) {
|
|
536
|
+
existingValues = /* @__PURE__ */ new Map();
|
|
537
|
+
existingValuesByNamespace.set(
|
|
538
|
+
tagConfig.namespace,
|
|
539
|
+
existingValues
|
|
540
|
+
);
|
|
477
541
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
542
|
+
const valueTracker = {
|
|
543
|
+
get: (path2) => existingValues.get(path2),
|
|
544
|
+
trackValue: (path2, value) => {
|
|
545
|
+
existingValues.set(path2, {
|
|
546
|
+
tag,
|
|
547
|
+
relativeFilePath: file.relativeFilePath,
|
|
548
|
+
value
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
const addConflict = async (path2, tagA, tagBValue, conflictType) => {
|
|
553
|
+
if (conflictType === "path_overwrite" && config.collect?.ignoreConflictsWithMatchingValues !== false && tagA.value === tagBValue) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const conflict = {
|
|
557
|
+
path: path2,
|
|
558
|
+
tagA,
|
|
559
|
+
tagB: {
|
|
560
|
+
tag,
|
|
561
|
+
relativeFilePath: file.relativeFilePath,
|
|
562
|
+
value: tagBValue
|
|
563
|
+
},
|
|
564
|
+
conflictType
|
|
565
|
+
};
|
|
566
|
+
if (config.collect?.onConflictResolution) {
|
|
567
|
+
let shouldContinue = true;
|
|
568
|
+
await config.collect.onConflictResolution({
|
|
569
|
+
conflict,
|
|
570
|
+
logger,
|
|
571
|
+
exit() {
|
|
572
|
+
shouldContinue = false;
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
if (!shouldContinue) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`LangTagConflictResolution:Processing stopped due to conflict resolution: ${conflict.tagA.tag.parameterConfig.namespace}|${conflict.path}`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
allConflicts.push(conflict);
|
|
582
|
+
};
|
|
583
|
+
const target = await ensureNestedObject(
|
|
584
|
+
tagConfig.path,
|
|
585
|
+
collection,
|
|
586
|
+
valueTracker,
|
|
587
|
+
addConflict
|
|
588
|
+
);
|
|
589
|
+
await mergeWithConflictDetection(
|
|
590
|
+
target,
|
|
591
|
+
tag.parameterTranslations,
|
|
592
|
+
tagConfig.path || "",
|
|
593
|
+
valueTracker,
|
|
594
|
+
addConflict
|
|
595
|
+
);
|
|
486
596
|
}
|
|
487
597
|
}
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
await promises.writeFile(file, newContent, "utf-8");
|
|
491
|
-
const encodedFile = encodeURI(file);
|
|
492
|
-
logger.info('Lang tag configurations written for file "{path}" (file://{file}:{line})', { path: path$12, file: encodedFile, line: lastUpdatedLine });
|
|
493
|
-
return true;
|
|
598
|
+
if (allConflicts.length > 0) {
|
|
599
|
+
logger.warn(`Found ${allConflicts.length} conflicts.`);
|
|
494
600
|
}
|
|
495
|
-
|
|
601
|
+
if (config.collect?.onCollectFinish) {
|
|
602
|
+
let shouldContinue = true;
|
|
603
|
+
config.collect.onCollectFinish({
|
|
604
|
+
totalTags,
|
|
605
|
+
namespaces: collections,
|
|
606
|
+
conflicts: allConflicts,
|
|
607
|
+
logger,
|
|
608
|
+
exit() {
|
|
609
|
+
shouldContinue = false;
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
if (!shouldContinue) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
`LangTagConflictResolution:Processing stopped due to collect finish handler`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return collections;
|
|
496
619
|
}
|
|
497
|
-
function
|
|
498
|
-
if (!
|
|
499
|
-
|
|
500
|
-
|
|
620
|
+
async function ensureNestedObject(path2, rootCollection, valueTracker, addConflict) {
|
|
621
|
+
if (!path2 || !path2.trim()) return rootCollection;
|
|
622
|
+
let current = rootCollection;
|
|
623
|
+
let currentPath = "";
|
|
624
|
+
for (const key of path2.split(".")) {
|
|
625
|
+
currentPath = currentPath ? `${currentPath}.${key}` : key;
|
|
626
|
+
if (current[key] !== void 0 && typeof current[key] !== "object") {
|
|
627
|
+
const existingInfo = valueTracker.get(currentPath);
|
|
628
|
+
if (existingInfo) {
|
|
629
|
+
await addConflict(
|
|
630
|
+
currentPath,
|
|
631
|
+
existingInfo,
|
|
632
|
+
{},
|
|
633
|
+
"type_mismatch"
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
return current;
|
|
637
|
+
}
|
|
638
|
+
current[key] = current[key] || {};
|
|
639
|
+
current = current[key];
|
|
640
|
+
}
|
|
641
|
+
return current;
|
|
501
642
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
643
|
+
async function mergeWithConflictDetection(target, source, basePath = "", valueTracker, addConflict) {
|
|
644
|
+
if (typeof target !== "object" || typeof source !== "object") {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
for (const key in source) {
|
|
648
|
+
if (!source.hasOwnProperty(key)) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const currentPath = basePath ? `${basePath}.${key}` : key;
|
|
652
|
+
let targetValue = target[key];
|
|
653
|
+
const sourceValue = source[key];
|
|
654
|
+
if (Array.isArray(sourceValue)) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (targetValue !== void 0) {
|
|
658
|
+
const targetType = typeof targetValue;
|
|
659
|
+
const sourceType = typeof sourceValue;
|
|
660
|
+
let existingInfo = valueTracker.get(currentPath);
|
|
661
|
+
if (!existingInfo && targetType === "object" && targetValue !== null && !Array.isArray(targetValue)) {
|
|
662
|
+
const findNestedInfo = (obj, prefix) => {
|
|
663
|
+
for (const key2 in obj) {
|
|
664
|
+
const path2 = prefix ? `${prefix}.${key2}` : key2;
|
|
665
|
+
const info = valueTracker.get(path2);
|
|
666
|
+
if (info) {
|
|
667
|
+
return info;
|
|
668
|
+
}
|
|
669
|
+
if (typeof obj[key2] === "object" && obj[key2] !== null && !Array.isArray(obj[key2])) {
|
|
670
|
+
const nestedInfo = findNestedInfo(obj[key2], path2);
|
|
671
|
+
if (nestedInfo) {
|
|
672
|
+
return nestedInfo;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return void 0;
|
|
677
|
+
};
|
|
678
|
+
existingInfo = findNestedInfo(targetValue, currentPath);
|
|
679
|
+
}
|
|
680
|
+
if (targetType !== sourceType) {
|
|
681
|
+
if (existingInfo) {
|
|
682
|
+
await addConflict(
|
|
683
|
+
currentPath,
|
|
684
|
+
existingInfo,
|
|
685
|
+
sourceValue,
|
|
686
|
+
"type_mismatch"
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (targetType !== "object") {
|
|
692
|
+
if (existingInfo) {
|
|
693
|
+
await addConflict(
|
|
694
|
+
currentPath,
|
|
695
|
+
existingInfo,
|
|
696
|
+
sourceValue,
|
|
697
|
+
"path_overwrite"
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
if (targetValue !== sourceValue) {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue)) {
|
|
706
|
+
if (!targetValue) {
|
|
707
|
+
targetValue = {};
|
|
708
|
+
target[key] = targetValue;
|
|
709
|
+
}
|
|
710
|
+
await mergeWithConflictDetection(
|
|
711
|
+
targetValue,
|
|
712
|
+
sourceValue,
|
|
713
|
+
currentPath,
|
|
714
|
+
valueTracker,
|
|
715
|
+
addConflict
|
|
716
|
+
);
|
|
717
|
+
} else {
|
|
718
|
+
target[key] = sourceValue;
|
|
719
|
+
valueTracker.trackValue(currentPath, sourceValue);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const CONFIG_FILE_NAME = "lang-tag.config.js";
|
|
724
|
+
const EXPORTS_FILE_NAME = "lang-tags.json";
|
|
725
|
+
async function $LT_EnsureDirectoryExists(filePath) {
|
|
726
|
+
await promises.mkdir(filePath, { recursive: true });
|
|
727
|
+
}
|
|
728
|
+
async function $LT_WriteJSON(filePath, data) {
|
|
729
|
+
await promises.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
730
|
+
}
|
|
731
|
+
async function $LT_ReadJSON(filePath) {
|
|
732
|
+
const content = await promises.readFile(filePath, "utf-8");
|
|
733
|
+
return JSON.parse(content);
|
|
734
|
+
}
|
|
735
|
+
async function $LT_WriteFileWithDirs(filePath, content) {
|
|
736
|
+
const dir = path.dirname(filePath);
|
|
737
|
+
try {
|
|
738
|
+
await promises.mkdir(dir, { recursive: true });
|
|
739
|
+
} catch (error) {
|
|
740
|
+
}
|
|
741
|
+
await promises.writeFile(filePath, content, "utf-8");
|
|
742
|
+
}
|
|
743
|
+
async function $LT_ReadFileContent(relativeFilePath) {
|
|
744
|
+
const cwd = process.cwd();
|
|
745
|
+
const absolutePath = path.resolve(cwd, relativeFilePath);
|
|
746
|
+
return await promises.readFile(absolutePath, "utf-8");
|
|
747
|
+
}
|
|
748
|
+
async function $LT_WriteAsExportFile({
|
|
749
|
+
config,
|
|
750
|
+
logger,
|
|
751
|
+
files
|
|
752
|
+
}) {
|
|
753
|
+
const packageJson = await $LT_ReadJSON(
|
|
754
|
+
path.resolve(process.cwd(), "package.json")
|
|
755
|
+
);
|
|
756
|
+
if (!packageJson) {
|
|
757
|
+
throw new Error("package.json not found");
|
|
758
|
+
}
|
|
759
|
+
const exportData = {
|
|
760
|
+
baseLanguageCode: config.baseLanguageCode,
|
|
761
|
+
files: files.map(({ relativeFilePath, tags }) => ({
|
|
762
|
+
relativeFilePath,
|
|
763
|
+
tags: tags.map((tag) => ({
|
|
764
|
+
variableName: tag.variableName,
|
|
765
|
+
config: tag.parameterConfig,
|
|
766
|
+
translations: tag.parameterTranslations
|
|
767
|
+
}))
|
|
768
|
+
}))
|
|
769
|
+
};
|
|
770
|
+
await promises.writeFile(EXPORTS_FILE_NAME, JSON.stringify(exportData), "utf-8");
|
|
771
|
+
logger.success(`Written {file}`, { file: EXPORTS_FILE_NAME });
|
|
772
|
+
}
|
|
773
|
+
function deepMergeTranslations(target, source) {
|
|
774
|
+
if (typeof target !== "object") {
|
|
775
|
+
throw new Error("Target must be an object");
|
|
776
|
+
}
|
|
777
|
+
if (typeof source !== "object") {
|
|
778
|
+
throw new Error("Source must be an object");
|
|
779
|
+
}
|
|
780
|
+
let changed = false;
|
|
781
|
+
for (const key in source) {
|
|
782
|
+
if (!source.hasOwnProperty(key)) {
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
785
|
+
let targetValue = target[key];
|
|
786
|
+
const sourceValue = source[key];
|
|
787
|
+
if (typeof targetValue === "string" && typeof sourceValue === "object") {
|
|
788
|
+
throw new Error(
|
|
789
|
+
`Trying to write object into target key "${key}" which is translation already`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
if (Array.isArray(sourceValue)) {
|
|
793
|
+
throw new Error(
|
|
794
|
+
`Trying to write array into target key "${key}", we do not allow arrays inside translations`
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
if (typeof sourceValue === "object") {
|
|
798
|
+
if (!targetValue) {
|
|
799
|
+
targetValue = {};
|
|
800
|
+
target[key] = targetValue;
|
|
801
|
+
}
|
|
802
|
+
if (deepMergeTranslations(targetValue, sourceValue)) {
|
|
803
|
+
changed = true;
|
|
804
|
+
}
|
|
805
|
+
} else {
|
|
806
|
+
if (target[key] !== sourceValue) {
|
|
807
|
+
changed = true;
|
|
808
|
+
}
|
|
809
|
+
target[key] = sourceValue;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return changed;
|
|
813
|
+
}
|
|
814
|
+
async function $LT_WriteToCollections({
|
|
815
|
+
config,
|
|
816
|
+
collections,
|
|
817
|
+
logger,
|
|
818
|
+
clean
|
|
819
|
+
}) {
|
|
820
|
+
await config.collect.collector.preWrite(clean);
|
|
821
|
+
const changedCollections = [];
|
|
822
|
+
for (let collectionName of Object.keys(collections)) {
|
|
823
|
+
if (!collectionName) {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
const filePath = await config.collect.collector.resolveCollectionFilePath(
|
|
827
|
+
collectionName
|
|
828
|
+
);
|
|
829
|
+
let originalJSON = {};
|
|
830
|
+
try {
|
|
831
|
+
originalJSON = await $LT_ReadJSON(filePath);
|
|
832
|
+
} catch (e) {
|
|
833
|
+
await config.collect.collector.onMissingCollection(
|
|
834
|
+
collectionName
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
if (deepMergeTranslations(originalJSON, collections[collectionName])) {
|
|
838
|
+
changedCollections.push(collectionName);
|
|
839
|
+
await $LT_WriteJSON(filePath, originalJSON);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
await config.collect.collector.postWrite(changedCollections);
|
|
843
|
+
}
|
|
844
|
+
const LANG_TAG_DEFAULT_CONFIG = {
|
|
845
|
+
tagName: "lang",
|
|
846
|
+
isLibrary: false,
|
|
847
|
+
includes: ["src/**/*.{js,ts,jsx,tsx}"],
|
|
848
|
+
excludes: ["node_modules", "dist", "build"],
|
|
849
|
+
localesDirectory: "locales",
|
|
850
|
+
baseLanguageCode: "en",
|
|
851
|
+
collect: {
|
|
852
|
+
collector: new namespaceCollector.NamespaceCollector(),
|
|
513
853
|
defaultNamespace: "common",
|
|
514
854
|
ignoreConflictsWithMatchingValues: true,
|
|
515
855
|
onCollectConfigFix: ({ config, langTagConfig }) => {
|
|
516
856
|
if (langTagConfig.isLibrary) return config;
|
|
517
|
-
if (!config)
|
|
857
|
+
if (!config)
|
|
858
|
+
return {
|
|
859
|
+
path: "",
|
|
860
|
+
namespace: langTagConfig.collect.defaultNamespace
|
|
861
|
+
};
|
|
518
862
|
if (!config.path) config.path = "";
|
|
519
|
-
if (!config.namespace)
|
|
863
|
+
if (!config.namespace)
|
|
864
|
+
config.namespace = langTagConfig.collect.defaultNamespace;
|
|
520
865
|
return config;
|
|
521
866
|
},
|
|
522
867
|
onConflictResolution: async (event) => {
|
|
@@ -529,21 +874,40 @@ const LANG_TAG_DEFAULT_CONFIG = {
|
|
|
529
874
|
import: {
|
|
530
875
|
dir: "src/lang-libraries",
|
|
531
876
|
tagImportPath: 'import { lang } from "@/my-lang-tag-path"',
|
|
532
|
-
onImport:
|
|
533
|
-
|
|
534
|
-
|
|
877
|
+
onImport: (event) => {
|
|
878
|
+
for (let e of event.exports) {
|
|
879
|
+
event.logger.info(
|
|
880
|
+
"Detected lang tag exports at package {packageName}",
|
|
881
|
+
{ packageName: e.packageJSON.name }
|
|
882
|
+
);
|
|
535
883
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
884
|
+
event.logger.warn(
|
|
885
|
+
`
|
|
886
|
+
Import Algorithm Not Configured
|
|
887
|
+
|
|
888
|
+
To import external language tags, you need to configure an import algorithm.
|
|
889
|
+
|
|
890
|
+
Setup Instructions:
|
|
891
|
+
1. Add this import at the top of your configuration file:
|
|
892
|
+
{importStr}
|
|
893
|
+
|
|
894
|
+
2. Replace import.onImport function with:
|
|
895
|
+
{onImport}
|
|
896
|
+
|
|
897
|
+
This will enable import of language tags from external packages.
|
|
898
|
+
`.trim(),
|
|
899
|
+
{
|
|
900
|
+
importStr: "const { flexibleImportAlgorithm } = require('@lang-tag/cli/algorithms');",
|
|
901
|
+
onImport: "onImport: flexibleImportAlgorithm({ filePath: { includePackageInPath: true } })"
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
}
|
|
544
905
|
},
|
|
545
906
|
translationArgPosition: 1,
|
|
546
907
|
onConfigGeneration: async (event) => {
|
|
908
|
+
event.logger.info(
|
|
909
|
+
"Config generation event is not configured. Add onConfigGeneration handler to customize config generation."
|
|
910
|
+
);
|
|
547
911
|
}
|
|
548
912
|
};
|
|
549
913
|
async function $LT_ReadConfig(projectPath) {
|
|
@@ -559,7 +923,9 @@ async function $LT_ReadConfig(projectPath) {
|
|
|
559
923
|
const userConfig = configModule.default || {};
|
|
560
924
|
const tn = (userConfig.tagName || "").toLowerCase().replace(/[-_\s]/g, "");
|
|
561
925
|
if (tn === "langtag" || tn === "lang-tag") {
|
|
562
|
-
throw new Error(
|
|
926
|
+
throw new Error(
|
|
927
|
+
'Custom tagName cannot be "lang-tag" or "langtag"! (It is not recommended for use with libraries)\n'
|
|
928
|
+
);
|
|
563
929
|
}
|
|
564
930
|
const config = {
|
|
565
931
|
...LANG_TAG_DEFAULT_CONFIG,
|
|
@@ -581,6 +947,75 @@ async function $LT_ReadConfig(projectPath) {
|
|
|
581
947
|
throw error;
|
|
582
948
|
}
|
|
583
949
|
}
|
|
950
|
+
const ANSI$1 = {
|
|
951
|
+
reset: "\x1B[0m",
|
|
952
|
+
white: "\x1B[97m",
|
|
953
|
+
brightCyan: "\x1B[96m",
|
|
954
|
+
green: "\x1B[92m",
|
|
955
|
+
gray: "\x1B[90m",
|
|
956
|
+
redBg: "\x1B[41m\x1B[97m",
|
|
957
|
+
bold: "\x1B[1m"
|
|
958
|
+
};
|
|
959
|
+
function colorizeFromAST(code, nodes) {
|
|
960
|
+
const ranges = [];
|
|
961
|
+
for (const node of nodes) {
|
|
962
|
+
const color = getColorForNodeType(node.type);
|
|
963
|
+
const priority = getPriorityForNodeType(node.type);
|
|
964
|
+
ranges.push({
|
|
965
|
+
start: node.start,
|
|
966
|
+
end: node.end,
|
|
967
|
+
color,
|
|
968
|
+
priority
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
ranges.sort((a, b) => {
|
|
972
|
+
if (b.start !== a.start) return b.start - a.start;
|
|
973
|
+
return b.priority - a.priority;
|
|
974
|
+
});
|
|
975
|
+
let colorized = code;
|
|
976
|
+
for (const range of ranges) {
|
|
977
|
+
const before = colorized.substring(0, range.start);
|
|
978
|
+
const text = colorized.substring(range.start, range.end);
|
|
979
|
+
const after = colorized.substring(range.end);
|
|
980
|
+
colorized = before + range.color + text + ANSI$1.reset + after;
|
|
981
|
+
}
|
|
982
|
+
return colorized;
|
|
983
|
+
}
|
|
984
|
+
function getColorForNodeType(type) {
|
|
985
|
+
switch (type) {
|
|
986
|
+
case "key":
|
|
987
|
+
return ANSI$1.brightCyan;
|
|
988
|
+
case "bracket":
|
|
989
|
+
case "colon":
|
|
990
|
+
return ANSI$1.gray;
|
|
991
|
+
case "value":
|
|
992
|
+
return ANSI$1.green;
|
|
993
|
+
case "comment":
|
|
994
|
+
return ANSI$1.gray;
|
|
995
|
+
case "error":
|
|
996
|
+
return ANSI$1.bold + ANSI$1.redBg;
|
|
997
|
+
default:
|
|
998
|
+
return ANSI$1.white;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
function getPriorityForNodeType(type) {
|
|
1002
|
+
switch (type) {
|
|
1003
|
+
case "error":
|
|
1004
|
+
return 3;
|
|
1005
|
+
// Highest priority
|
|
1006
|
+
case "key":
|
|
1007
|
+
return 2;
|
|
1008
|
+
case "value":
|
|
1009
|
+
return 1;
|
|
1010
|
+
case "bracket":
|
|
1011
|
+
case "colon":
|
|
1012
|
+
case "comment":
|
|
1013
|
+
return 0;
|
|
1014
|
+
// Lowest priority
|
|
1015
|
+
default:
|
|
1016
|
+
return 0;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
584
1019
|
function parseObjectAST(code) {
|
|
585
1020
|
const nodes = [];
|
|
586
1021
|
try {
|
|
@@ -674,81 +1109,12 @@ function markConflictNodes(nodes, conflictPath) {
|
|
|
674
1109
|
return node;
|
|
675
1110
|
});
|
|
676
1111
|
}
|
|
677
|
-
const ANSI
|
|
1112
|
+
const ANSI = {
|
|
678
1113
|
reset: "\x1B[0m",
|
|
679
1114
|
white: "\x1B[97m",
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
redBg: "\x1B[41m\x1B[97m",
|
|
684
|
-
bold: "\x1B[1m"
|
|
685
|
-
};
|
|
686
|
-
function colorizeFromAST(code, nodes) {
|
|
687
|
-
const ranges = [];
|
|
688
|
-
for (const node of nodes) {
|
|
689
|
-
const color = getColorForNodeType(node.type);
|
|
690
|
-
const priority = getPriorityForNodeType(node.type);
|
|
691
|
-
ranges.push({
|
|
692
|
-
start: node.start,
|
|
693
|
-
end: node.end,
|
|
694
|
-
color,
|
|
695
|
-
priority
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
ranges.sort((a, b) => {
|
|
699
|
-
if (b.start !== a.start) return b.start - a.start;
|
|
700
|
-
return b.priority - a.priority;
|
|
701
|
-
});
|
|
702
|
-
let colorized = code;
|
|
703
|
-
for (const range of ranges) {
|
|
704
|
-
const before = colorized.substring(0, range.start);
|
|
705
|
-
const text = colorized.substring(range.start, range.end);
|
|
706
|
-
const after = colorized.substring(range.end);
|
|
707
|
-
colorized = before + range.color + text + ANSI$1.reset + after;
|
|
708
|
-
}
|
|
709
|
-
return colorized;
|
|
710
|
-
}
|
|
711
|
-
function getColorForNodeType(type) {
|
|
712
|
-
switch (type) {
|
|
713
|
-
case "key":
|
|
714
|
-
return ANSI$1.brightCyan;
|
|
715
|
-
case "bracket":
|
|
716
|
-
case "colon":
|
|
717
|
-
return ANSI$1.gray;
|
|
718
|
-
case "value":
|
|
719
|
-
return ANSI$1.green;
|
|
720
|
-
case "comment":
|
|
721
|
-
return ANSI$1.gray;
|
|
722
|
-
case "error":
|
|
723
|
-
return ANSI$1.bold + ANSI$1.redBg;
|
|
724
|
-
default:
|
|
725
|
-
return ANSI$1.white;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
function getPriorityForNodeType(type) {
|
|
729
|
-
switch (type) {
|
|
730
|
-
case "error":
|
|
731
|
-
return 3;
|
|
732
|
-
// Highest priority
|
|
733
|
-
case "key":
|
|
734
|
-
return 2;
|
|
735
|
-
case "value":
|
|
736
|
-
return 1;
|
|
737
|
-
case "bracket":
|
|
738
|
-
case "colon":
|
|
739
|
-
case "comment":
|
|
740
|
-
return 0;
|
|
741
|
-
// Lowest priority
|
|
742
|
-
default:
|
|
743
|
-
return 0;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
const ANSI = {
|
|
747
|
-
reset: "\x1B[0m",
|
|
748
|
-
white: "\x1B[97m",
|
|
749
|
-
cyan: "\x1B[96m",
|
|
750
|
-
gray: "\x1B[37m",
|
|
751
|
-
darkGray: "\x1B[90m",
|
|
1115
|
+
cyan: "\x1B[96m",
|
|
1116
|
+
gray: "\x1B[37m",
|
|
1117
|
+
darkGray: "\x1B[90m",
|
|
752
1118
|
bold: "\x1B[1m"
|
|
753
1119
|
};
|
|
754
1120
|
function getVisibleLines(totalLines, errorLines, threshold = 10) {
|
|
@@ -774,18 +1140,24 @@ function printLines(lines, startLineNumber, errorLines = /* @__PURE__ */ new Set
|
|
|
774
1140
|
lines.forEach((line, i) => {
|
|
775
1141
|
const lineNumber = startLineNumber + i;
|
|
776
1142
|
const lineNumStr = String(lineNumber).padStart(3, " ");
|
|
777
|
-
console.log(
|
|
1143
|
+
console.log(
|
|
1144
|
+
`${ANSI.gray}${lineNumStr}${ANSI.reset} ${ANSI.darkGray}│${ANSI.reset} ${line}`
|
|
1145
|
+
);
|
|
778
1146
|
});
|
|
779
1147
|
} else {
|
|
780
1148
|
let lastPrinted = -2;
|
|
781
1149
|
lines.forEach((line, i) => {
|
|
782
1150
|
if (visibleLines.has(i)) {
|
|
783
1151
|
if (i > lastPrinted + 1 && lastPrinted >= 0) {
|
|
784
|
-
console.log(
|
|
1152
|
+
console.log(
|
|
1153
|
+
`${ANSI.gray} -${ANSI.reset} ${ANSI.darkGray}│${ANSI.reset} ${ANSI.gray}...${ANSI.reset}`
|
|
1154
|
+
);
|
|
785
1155
|
}
|
|
786
1156
|
const lineNumber = startLineNumber + i;
|
|
787
1157
|
const lineNumStr = String(lineNumber).padStart(3, " ");
|
|
788
|
-
console.log(
|
|
1158
|
+
console.log(
|
|
1159
|
+
`${ANSI.gray}${lineNumStr}${ANSI.reset} ${ANSI.darkGray}│${ANSI.reset} ${line}`
|
|
1160
|
+
);
|
|
789
1161
|
lastPrinted = i;
|
|
790
1162
|
}
|
|
791
1163
|
});
|
|
@@ -793,7 +1165,7 @@ function printLines(lines, startLineNumber, errorLines = /* @__PURE__ */ new Set
|
|
|
793
1165
|
}
|
|
794
1166
|
async function getLangTagCodeSection(tagInfo) {
|
|
795
1167
|
const { tag, relativeFilePath } = tagInfo;
|
|
796
|
-
const fileContent = await
|
|
1168
|
+
const fileContent = await $LT_ReadFileContent(relativeFilePath);
|
|
797
1169
|
const fileLines = fileContent.split("\n");
|
|
798
1170
|
const startLine = tag.line;
|
|
799
1171
|
const endLine = tag.line + tag.fullMatch.split("\n").length - 1;
|
|
@@ -835,14 +1207,20 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
835
1207
|
const wholeTagCode = await getLangTagCodeSection(tagInfo);
|
|
836
1208
|
const translationTagCode = translationArgPosition === 1 ? tag.parameter1Text : tag.parameter2Text;
|
|
837
1209
|
const configTagCode = translationArgPosition === 1 ? tag.parameter2Text : tag.parameter1Text;
|
|
838
|
-
const translationErrorPath = stripPrefix(
|
|
1210
|
+
const translationErrorPath = stripPrefix(
|
|
1211
|
+
conflictPath,
|
|
1212
|
+
tag.parameterConfig?.path
|
|
1213
|
+
);
|
|
839
1214
|
let colorizedWhole = wholeTagCode;
|
|
840
1215
|
let errorLines = /* @__PURE__ */ new Set();
|
|
841
1216
|
if (translationTagCode) {
|
|
842
1217
|
try {
|
|
843
1218
|
const translationNodes = parseObjectAST(translationTagCode);
|
|
844
1219
|
const markedTranslationNodes = translationErrorPath ? markConflictNodes(translationNodes, translationErrorPath) : translationNodes;
|
|
845
|
-
const translationErrorLines = getErrorLineNumbers(
|
|
1220
|
+
const translationErrorLines = getErrorLineNumbers(
|
|
1221
|
+
translationTagCode,
|
|
1222
|
+
markedTranslationNodes
|
|
1223
|
+
);
|
|
846
1224
|
const translationStartInWhole = wholeTagCode.indexOf(translationTagCode);
|
|
847
1225
|
if (translationStartInWhole >= 0) {
|
|
848
1226
|
const linesBeforeTranslation = wholeTagCode.substring(0, translationStartInWhole).split("\n").length - 1;
|
|
@@ -850,12 +1228,20 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
850
1228
|
errorLines.add(linesBeforeTranslation + lineNum2);
|
|
851
1229
|
});
|
|
852
1230
|
if (translationErrorLines.size > 0) {
|
|
853
|
-
const lastTranslationErrorLine = Math.max(
|
|
1231
|
+
const lastTranslationErrorLine = Math.max(
|
|
1232
|
+
...Array.from(translationErrorLines)
|
|
1233
|
+
);
|
|
854
1234
|
lineNum = startLine + linesBeforeTranslation + lastTranslationErrorLine;
|
|
855
1235
|
}
|
|
856
1236
|
}
|
|
857
|
-
const colorizedTranslation = colorizeFromAST(
|
|
858
|
-
|
|
1237
|
+
const colorizedTranslation = colorizeFromAST(
|
|
1238
|
+
translationTagCode,
|
|
1239
|
+
markedTranslationNodes
|
|
1240
|
+
);
|
|
1241
|
+
colorizedWhole = colorizedWhole.replace(
|
|
1242
|
+
translationTagCode,
|
|
1243
|
+
colorizedTranslation
|
|
1244
|
+
);
|
|
859
1245
|
} catch (error) {
|
|
860
1246
|
console.error("Failed to colorize translation:", error);
|
|
861
1247
|
}
|
|
@@ -877,7 +1263,10 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
877
1263
|
}
|
|
878
1264
|
return node;
|
|
879
1265
|
});
|
|
880
|
-
const configErrorLines = getErrorLineNumbers(
|
|
1266
|
+
const configErrorLines = getErrorLineNumbers(
|
|
1267
|
+
configTagCode,
|
|
1268
|
+
markedConfigNodes
|
|
1269
|
+
);
|
|
881
1270
|
const configStartInWhole = wholeTagCode.indexOf(configTagCode);
|
|
882
1271
|
if (configStartInWhole >= 0) {
|
|
883
1272
|
const linesBeforeConfig = wholeTagCode.substring(0, configStartInWhole).split("\n").length - 1;
|
|
@@ -885,14 +1274,22 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
885
1274
|
errorLines.add(linesBeforeConfig + lineNum2);
|
|
886
1275
|
});
|
|
887
1276
|
}
|
|
888
|
-
const colorizedConfig = colorizeFromAST(
|
|
889
|
-
|
|
1277
|
+
const colorizedConfig = colorizeFromAST(
|
|
1278
|
+
configTagCode,
|
|
1279
|
+
markedConfigNodes
|
|
1280
|
+
);
|
|
1281
|
+
colorizedWhole = colorizedWhole.replace(
|
|
1282
|
+
configTagCode,
|
|
1283
|
+
colorizedConfig
|
|
1284
|
+
);
|
|
890
1285
|
} catch (error) {
|
|
891
1286
|
console.error("Failed to colorize config:", error);
|
|
892
1287
|
}
|
|
893
1288
|
}
|
|
894
1289
|
const encodedPath = encodeURI(filePath);
|
|
895
|
-
console.log(
|
|
1290
|
+
console.log(
|
|
1291
|
+
`${ANSI.gray}${prefix}${ANSI.reset} ${ANSI.cyan}file://${encodedPath}${ANSI.reset}${ANSI.gray}:${lineNum}${ANSI.reset}`
|
|
1292
|
+
);
|
|
896
1293
|
printLines(colorizedWhole.split("\n"), startLine, errorLines, condense);
|
|
897
1294
|
} catch (error) {
|
|
898
1295
|
console.error("Error displaying conflict:", error);
|
|
@@ -900,8 +1297,20 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
900
1297
|
}
|
|
901
1298
|
async function $LT_LogConflict(conflict, translationArgPosition, condense) {
|
|
902
1299
|
const { path: conflictPath, tagA, tagB } = conflict;
|
|
903
|
-
await logTagConflictInfo(
|
|
904
|
-
|
|
1300
|
+
await logTagConflictInfo(
|
|
1301
|
+
tagA,
|
|
1302
|
+
"between",
|
|
1303
|
+
conflictPath,
|
|
1304
|
+
translationArgPosition,
|
|
1305
|
+
condense
|
|
1306
|
+
);
|
|
1307
|
+
await logTagConflictInfo(
|
|
1308
|
+
tagB,
|
|
1309
|
+
"and",
|
|
1310
|
+
conflictPath,
|
|
1311
|
+
translationArgPosition,
|
|
1312
|
+
condense
|
|
1313
|
+
);
|
|
905
1314
|
}
|
|
906
1315
|
const ANSI_COLORS = {
|
|
907
1316
|
reset: "\x1B[0m",
|
|
@@ -915,14 +1324,18 @@ const ANSI_COLORS = {
|
|
|
915
1324
|
cyan: "\x1B[36m"
|
|
916
1325
|
};
|
|
917
1326
|
function validateAndInterpolate(message, params) {
|
|
918
|
-
const placeholders = Array.from(message.matchAll(/\{(\w+)\}/g)).map(
|
|
1327
|
+
const placeholders = Array.from(message.matchAll(/\{(\w+)\}/g)).map(
|
|
1328
|
+
(m) => m[1]
|
|
1329
|
+
);
|
|
919
1330
|
const missing = placeholders.filter((p) => !(p in (params || {})));
|
|
920
1331
|
if (missing.length) {
|
|
921
1332
|
throw new Error(`Missing variables in message: ${missing.join(", ")}`);
|
|
922
1333
|
}
|
|
923
1334
|
const extra = params ? Object.keys(params).filter((k) => !placeholders.includes(k)) : [];
|
|
924
1335
|
if (extra.length) {
|
|
925
|
-
throw new Error(
|
|
1336
|
+
throw new Error(
|
|
1337
|
+
`Extra variables provided not used in message: ${extra.join(", ")}`
|
|
1338
|
+
);
|
|
926
1339
|
}
|
|
927
1340
|
const parts = [];
|
|
928
1341
|
let lastIndex = 0;
|
|
@@ -968,12 +1381,24 @@ function $LT_CreateDefaultLogger(debugMode, translationArgPosition = 1) {
|
|
|
968
1381
|
conflict: async (conflict, condense) => {
|
|
969
1382
|
const { path: path2, conflictType, tagA } = conflict;
|
|
970
1383
|
console.log();
|
|
971
|
-
console.log(
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
console.log(
|
|
975
|
-
|
|
976
|
-
|
|
1384
|
+
console.log(
|
|
1385
|
+
`${ANSI_COLORS.bold}${ANSI_COLORS.red}⚠ Translation Conflict Detected${ANSI_COLORS.reset}`
|
|
1386
|
+
);
|
|
1387
|
+
console.log(
|
|
1388
|
+
`${ANSI_COLORS.gray}${"─".repeat(60)}${ANSI_COLORS.reset}`
|
|
1389
|
+
);
|
|
1390
|
+
console.log(
|
|
1391
|
+
` ${ANSI_COLORS.cyan}Conflict Type:${ANSI_COLORS.reset} ${ANSI_COLORS.white}${conflictType}${ANSI_COLORS.reset}`
|
|
1392
|
+
);
|
|
1393
|
+
console.log(
|
|
1394
|
+
` ${ANSI_COLORS.cyan}Translation Key:${ANSI_COLORS.reset} ${ANSI_COLORS.white}${path2}${ANSI_COLORS.reset}`
|
|
1395
|
+
);
|
|
1396
|
+
console.log(
|
|
1397
|
+
` ${ANSI_COLORS.cyan}Namespace:${ANSI_COLORS.reset} ${ANSI_COLORS.white}${tagA.tag.parameterConfig.namespace}${ANSI_COLORS.reset}`
|
|
1398
|
+
);
|
|
1399
|
+
console.log(
|
|
1400
|
+
`${ANSI_COLORS.gray}${"─".repeat(60)}${ANSI_COLORS.reset}`
|
|
1401
|
+
);
|
|
977
1402
|
await $LT_LogConflict(conflict, translationArgPosition, condense);
|
|
978
1403
|
console.log();
|
|
979
1404
|
}
|
|
@@ -981,7 +1406,10 @@ function $LT_CreateDefaultLogger(debugMode, translationArgPosition = 1) {
|
|
|
981
1406
|
}
|
|
982
1407
|
async function $LT_GetCommandEssentials() {
|
|
983
1408
|
const config = await $LT_ReadConfig(process$1.cwd());
|
|
984
|
-
const logger = $LT_CreateDefaultLogger(
|
|
1409
|
+
const logger = $LT_CreateDefaultLogger(
|
|
1410
|
+
config.debug,
|
|
1411
|
+
config.translationArgPosition
|
|
1412
|
+
);
|
|
985
1413
|
config.collect.collector.config = config;
|
|
986
1414
|
config.collect.collector.logger = logger;
|
|
987
1415
|
return {
|
|
@@ -989,398 +1417,255 @@ async function $LT_GetCommandEssentials() {
|
|
|
989
1417
|
logger
|
|
990
1418
|
};
|
|
991
1419
|
}
|
|
992
|
-
async function $
|
|
1420
|
+
async function $LT_CMD_Collect(options) {
|
|
993
1421
|
const { config, logger } = await $LT_GetCommandEssentials();
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const path2 = file.substring(charactersToSkip);
|
|
1003
|
-
const localDirty = await checkAndRegenerateFileLangTags(config, logger, file, path2);
|
|
1004
|
-
if (localDirty) {
|
|
1005
|
-
dirty = true;
|
|
1422
|
+
logger.info("Collecting translations from source files...");
|
|
1423
|
+
const files = await $LT_CollectCandidateFilesWithTags({ config, logger });
|
|
1424
|
+
if (config.debug) {
|
|
1425
|
+
for (let file of files) {
|
|
1426
|
+
logger.debug("Found {count} translations tags inside: {file}", {
|
|
1427
|
+
count: file.tags.length,
|
|
1428
|
+
file: file.relativeFilePath
|
|
1429
|
+
});
|
|
1006
1430
|
}
|
|
1007
1431
|
}
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1432
|
+
if (config.isLibrary) {
|
|
1433
|
+
await $LT_WriteAsExportFile({ config, logger, files });
|
|
1434
|
+
return;
|
|
1010
1435
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1436
|
+
try {
|
|
1437
|
+
const collections = await $LT_GroupTagsToCollections({
|
|
1438
|
+
logger,
|
|
1439
|
+
files,
|
|
1440
|
+
config
|
|
1441
|
+
});
|
|
1442
|
+
const totalTags = files.reduce(
|
|
1443
|
+
(sum, file) => sum + file.tags.length,
|
|
1444
|
+
0
|
|
1445
|
+
);
|
|
1446
|
+
logger.debug("Found {totalTags} translation tags", { totalTags });
|
|
1447
|
+
await $LT_WriteToCollections({
|
|
1448
|
+
config,
|
|
1449
|
+
collections,
|
|
1450
|
+
logger,
|
|
1451
|
+
clean: options?.clean
|
|
1452
|
+
});
|
|
1453
|
+
} catch (e) {
|
|
1454
|
+
const prefix = "LangTagConflictResolution:";
|
|
1455
|
+
if (e.message.startsWith(prefix)) {
|
|
1456
|
+
logger.error(e.message.substring(prefix.length));
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
throw e;
|
|
1015
1460
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1461
|
+
}
|
|
1462
|
+
async function $LT_CollectExportFiles(logger) {
|
|
1463
|
+
const nodeModulesPath = path$1.join(process$1.cwd(), "node_modules");
|
|
1464
|
+
if (!fs.existsSync(nodeModulesPath)) {
|
|
1465
|
+
logger.error('"node_modules" directory not found');
|
|
1466
|
+
return [];
|
|
1018
1467
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
if (Array.isArray(sourceValue)) {
|
|
1030
|
-
throw new Error(`Trying to write array into target key "${key}", we do not allow arrays inside translations`);
|
|
1031
|
-
}
|
|
1032
|
-
if (typeof sourceValue === "object") {
|
|
1033
|
-
if (!targetValue) {
|
|
1034
|
-
targetValue = {};
|
|
1035
|
-
target[key] = targetValue;
|
|
1036
|
-
}
|
|
1037
|
-
if (deepMergeTranslations(targetValue, sourceValue)) {
|
|
1038
|
-
changed = true;
|
|
1468
|
+
const results = [];
|
|
1469
|
+
try {
|
|
1470
|
+
const exportFiles = await globby.globby(
|
|
1471
|
+
[`node_modules/**/${EXPORTS_FILE_NAME}`],
|
|
1472
|
+
{
|
|
1473
|
+
cwd: process$1.cwd(),
|
|
1474
|
+
onlyFiles: true,
|
|
1475
|
+
ignore: ["**/node_modules/**/node_modules/**"],
|
|
1476
|
+
deep: 4
|
|
1039
1477
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1478
|
+
);
|
|
1479
|
+
for (const exportFile of exportFiles) {
|
|
1480
|
+
const fullExportPath = path$1.resolve(exportFile);
|
|
1481
|
+
const packageJsonPath = findPackageJsonForExport(
|
|
1482
|
+
fullExportPath,
|
|
1483
|
+
nodeModulesPath
|
|
1484
|
+
);
|
|
1485
|
+
if (packageJsonPath) {
|
|
1486
|
+
results.push({
|
|
1487
|
+
exportPath: fullExportPath,
|
|
1488
|
+
packageJsonPath
|
|
1489
|
+
});
|
|
1490
|
+
} else {
|
|
1491
|
+
logger.warn(
|
|
1492
|
+
"Found export file but could not match package.json: {exportPath}",
|
|
1493
|
+
{
|
|
1494
|
+
exportPath: fullExportPath
|
|
1495
|
+
}
|
|
1496
|
+
);
|
|
1043
1497
|
}
|
|
1044
|
-
target[key] = sourceValue;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
return changed;
|
|
1048
|
-
}
|
|
1049
|
-
async function $LT_WriteToCollections({ config, collections, logger, clean }) {
|
|
1050
|
-
await config.collect.collector.preWrite(clean);
|
|
1051
|
-
const changedCollections = [];
|
|
1052
|
-
for (let collectionName of Object.keys(collections)) {
|
|
1053
|
-
if (!collectionName) {
|
|
1054
|
-
continue;
|
|
1055
|
-
}
|
|
1056
|
-
const filePath = await config.collect.collector.resolveCollectionFilePath(collectionName);
|
|
1057
|
-
let originalJSON = {};
|
|
1058
|
-
try {
|
|
1059
|
-
originalJSON = await flexibleImportAlgorithm.$LT_ReadJSON(filePath);
|
|
1060
|
-
} catch (e) {
|
|
1061
|
-
await config.collect.collector.onMissingCollection(collectionName);
|
|
1062
|
-
}
|
|
1063
|
-
if (deepMergeTranslations(originalJSON, collections[collectionName])) {
|
|
1064
|
-
changedCollections.push(collectionName);
|
|
1065
|
-
await flexibleImportAlgorithm.$LT_WriteJSON(filePath, originalJSON);
|
|
1066
1498
|
}
|
|
1499
|
+
logger.debug(
|
|
1500
|
+
"Found {count} export files with matching package.json in node_modules",
|
|
1501
|
+
{
|
|
1502
|
+
count: results.length
|
|
1503
|
+
}
|
|
1504
|
+
);
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
logger.error("Error scanning node_modules with globby: {error}", {
|
|
1507
|
+
error: String(error)
|
|
1508
|
+
});
|
|
1067
1509
|
}
|
|
1068
|
-
|
|
1510
|
+
return results;
|
|
1069
1511
|
}
|
|
1070
|
-
|
|
1071
|
-
const
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
if (!filesToScan) {
|
|
1076
|
-
filesToScan = await globby.globby(config.includes, { cwd, ignore: config.excludes, absolute: true });
|
|
1512
|
+
function findPackageJsonForExport(exportPath, nodeModulesPath) {
|
|
1513
|
+
const relativePath = path$1.relative(nodeModulesPath, exportPath);
|
|
1514
|
+
const pathParts = relativePath.split(path$1.sep);
|
|
1515
|
+
if (pathParts.length < 2) {
|
|
1516
|
+
return null;
|
|
1077
1517
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1518
|
+
if (pathParts[0].startsWith("@")) {
|
|
1519
|
+
if (pathParts.length >= 3) {
|
|
1520
|
+
const packageDir = path$1.join(
|
|
1521
|
+
nodeModulesPath,
|
|
1522
|
+
pathParts[0],
|
|
1523
|
+
pathParts[1]
|
|
1524
|
+
);
|
|
1525
|
+
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
1526
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
1527
|
+
return packageJsonPath;
|
|
1528
|
+
}
|
|
1088
1529
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1530
|
+
} else {
|
|
1531
|
+
const packageDir = path$1.join(nodeModulesPath, pathParts[0]);
|
|
1532
|
+
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
1533
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
1534
|
+
return packageJsonPath;
|
|
1091
1535
|
}
|
|
1092
|
-
tags = $LT_FilterEmptyNamespaceTags(tags, logger);
|
|
1093
|
-
const relativeFilePath = path.relative(cwd, filePath);
|
|
1094
|
-
candidates.push({ relativeFilePath, tags });
|
|
1095
1536
|
}
|
|
1096
|
-
return
|
|
1537
|
+
return null;
|
|
1097
1538
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
config: tag.parameterConfig,
|
|
1110
|
-
translations: tag.parameterTranslations
|
|
1111
|
-
}))
|
|
1112
|
-
}))
|
|
1113
|
-
};
|
|
1114
|
-
await promises.writeFile(EXPORTS_FILE_NAME, JSON.stringify(exportData), "utf-8");
|
|
1115
|
-
logger.success(`Written {file}`, { file: EXPORTS_FILE_NAME });
|
|
1539
|
+
const __filename$1 = url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
1540
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
1541
|
+
const templatePath = path.join(
|
|
1542
|
+
__dirname$1,
|
|
1543
|
+
"templates",
|
|
1544
|
+
"import",
|
|
1545
|
+
"imported-tag.mustache"
|
|
1546
|
+
);
|
|
1547
|
+
const template = fs.readFileSync(templatePath, "utf-8");
|
|
1548
|
+
function renderTemplate$1(data) {
|
|
1549
|
+
return mustache.render(template, data, {}, { escape: (text) => text });
|
|
1116
1550
|
}
|
|
1117
|
-
async function
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
let existingValues = existingValuesByNamespace.get(tagConfig.namespace);
|
|
1137
|
-
if (!existingValues) {
|
|
1138
|
-
existingValues = /* @__PURE__ */ new Map();
|
|
1139
|
-
existingValuesByNamespace.set(tagConfig.namespace, existingValues);
|
|
1140
|
-
}
|
|
1141
|
-
const valueTracker = {
|
|
1142
|
-
get: (path2) => existingValues.get(path2),
|
|
1143
|
-
trackValue: (path2, value) => {
|
|
1144
|
-
existingValues.set(path2, { tag, relativeFilePath: file.relativeFilePath, value });
|
|
1145
|
-
}
|
|
1146
|
-
};
|
|
1147
|
-
const addConflict = async (path2, tagA, tagBValue, conflictType) => {
|
|
1148
|
-
if (conflictType === "path_overwrite" && config.collect?.ignoreConflictsWithMatchingValues !== false && tagA.value === tagBValue) {
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
|
-
const conflict = {
|
|
1152
|
-
path: path2,
|
|
1153
|
-
tagA,
|
|
1154
|
-
tagB: {
|
|
1155
|
-
tag,
|
|
1156
|
-
relativeFilePath: file.relativeFilePath,
|
|
1157
|
-
value: tagBValue
|
|
1158
|
-
},
|
|
1159
|
-
conflictType
|
|
1160
|
-
};
|
|
1161
|
-
if (config.collect?.onConflictResolution) {
|
|
1162
|
-
let shouldContinue = true;
|
|
1163
|
-
await config.collect.onConflictResolution({
|
|
1164
|
-
conflict,
|
|
1165
|
-
logger,
|
|
1166
|
-
exit() {
|
|
1167
|
-
shouldContinue = false;
|
|
1168
|
-
}
|
|
1169
|
-
});
|
|
1170
|
-
if (!shouldContinue) {
|
|
1171
|
-
throw new Error(`LangTagConflictResolution:Processing stopped due to conflict resolution: ${conflict.tagA.tag.parameterConfig.namespace}|${conflict.path}`);
|
|
1172
|
-
}
|
|
1551
|
+
async function generateImportFiles(config, logger, importManager) {
|
|
1552
|
+
const importedFiles = importManager.getImportedFiles();
|
|
1553
|
+
for (const importedFile of importedFiles) {
|
|
1554
|
+
const filePath = path$1.resolve(
|
|
1555
|
+
process__namespace.cwd(),
|
|
1556
|
+
config.import.dir,
|
|
1557
|
+
importedFile.pathRelativeToImportDir
|
|
1558
|
+
);
|
|
1559
|
+
const processedExports = importedFile.tags.map((tag) => {
|
|
1560
|
+
const parameter1 = config.translationArgPosition === 1 ? tag.translations : tag.config;
|
|
1561
|
+
const parameter2 = config.translationArgPosition === 1 ? tag.config : tag.translations;
|
|
1562
|
+
const hasParameter2 = parameter2 !== null && parameter2 !== void 0 && (typeof parameter2 !== "object" || Object.keys(parameter2).length > 0);
|
|
1563
|
+
return {
|
|
1564
|
+
name: tag.variableName,
|
|
1565
|
+
parameter1: JSON5.stringify(parameter1, void 0, 4),
|
|
1566
|
+
parameter2: hasParameter2 ? JSON5.stringify(parameter2, void 0, 4) : null,
|
|
1567
|
+
hasParameter2,
|
|
1568
|
+
config: {
|
|
1569
|
+
tagName: config.tagName
|
|
1173
1570
|
}
|
|
1174
|
-
allConflicts.push(conflict);
|
|
1175
1571
|
};
|
|
1176
|
-
const target = await ensureNestedObject(
|
|
1177
|
-
tagConfig.path,
|
|
1178
|
-
collection,
|
|
1179
|
-
valueTracker,
|
|
1180
|
-
addConflict
|
|
1181
|
-
);
|
|
1182
|
-
await mergeWithConflictDetection(
|
|
1183
|
-
target,
|
|
1184
|
-
tag.parameterTranslations,
|
|
1185
|
-
tagConfig.path || "",
|
|
1186
|
-
valueTracker,
|
|
1187
|
-
addConflict
|
|
1188
|
-
);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
if (allConflicts.length > 0) {
|
|
1192
|
-
logger.warn(`Found ${allConflicts.length} conflicts.`);
|
|
1193
|
-
}
|
|
1194
|
-
if (config.collect?.onCollectFinish) {
|
|
1195
|
-
let shouldContinue = true;
|
|
1196
|
-
config.collect.onCollectFinish({
|
|
1197
|
-
totalTags,
|
|
1198
|
-
namespaces: collections,
|
|
1199
|
-
conflicts: allConflicts,
|
|
1200
|
-
logger,
|
|
1201
|
-
exit() {
|
|
1202
|
-
shouldContinue = false;
|
|
1203
|
-
}
|
|
1204
1572
|
});
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
for (const key of path2.split(".")) {
|
|
1216
|
-
currentPath = currentPath ? `${currentPath}.${key}` : key;
|
|
1217
|
-
if (current[key] !== void 0 && typeof current[key] !== "object") {
|
|
1218
|
-
const existingInfo = valueTracker.get(currentPath);
|
|
1219
|
-
if (existingInfo) {
|
|
1220
|
-
await addConflict(currentPath, existingInfo, {}, "type_mismatch");
|
|
1221
|
-
}
|
|
1222
|
-
return current;
|
|
1223
|
-
}
|
|
1224
|
-
current[key] = current[key] || {};
|
|
1225
|
-
current = current[key];
|
|
1573
|
+
const templateData = {
|
|
1574
|
+
tagImportPath: config.import.tagImportPath,
|
|
1575
|
+
exports: processedExports
|
|
1576
|
+
};
|
|
1577
|
+
const content = renderTemplate$1(templateData);
|
|
1578
|
+
await $LT_EnsureDirectoryExists(path.dirname(filePath));
|
|
1579
|
+
await promises.writeFile(filePath, content, "utf-8");
|
|
1580
|
+
logger.success('Created tag file: "{file}"', {
|
|
1581
|
+
file: importedFile.pathRelativeToImportDir
|
|
1582
|
+
});
|
|
1226
1583
|
}
|
|
1227
|
-
return current;
|
|
1228
1584
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1585
|
+
class ImportManager {
|
|
1586
|
+
importedFiles = [];
|
|
1587
|
+
constructor() {
|
|
1588
|
+
this.importedFiles = [];
|
|
1232
1589
|
}
|
|
1233
|
-
|
|
1234
|
-
if (!
|
|
1235
|
-
|
|
1590
|
+
importTag(pathRelativeToImportDir, tag) {
|
|
1591
|
+
if (!pathRelativeToImportDir) {
|
|
1592
|
+
throw new Error(
|
|
1593
|
+
`pathRelativeToImportDir required, got: ${pathRelativeToImportDir}`
|
|
1594
|
+
);
|
|
1236
1595
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
continue;
|
|
1596
|
+
if (!tag?.variableName) {
|
|
1597
|
+
throw new Error(
|
|
1598
|
+
`tag.variableName required, got: ${tag?.variableName}`
|
|
1599
|
+
);
|
|
1242
1600
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
if (!existingInfo && targetType === "object" && targetValue !== null && !Array.isArray(targetValue)) {
|
|
1248
|
-
const findNestedInfo = (obj, prefix) => {
|
|
1249
|
-
for (const key2 in obj) {
|
|
1250
|
-
const path2 = prefix ? `${prefix}.${key2}` : key2;
|
|
1251
|
-
const info = valueTracker.get(path2);
|
|
1252
|
-
if (info) {
|
|
1253
|
-
return info;
|
|
1254
|
-
}
|
|
1255
|
-
if (typeof obj[key2] === "object" && obj[key2] !== null && !Array.isArray(obj[key2])) {
|
|
1256
|
-
const nestedInfo = findNestedInfo(obj[key2], path2);
|
|
1257
|
-
if (nestedInfo) {
|
|
1258
|
-
return nestedInfo;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
return void 0;
|
|
1263
|
-
};
|
|
1264
|
-
existingInfo = findNestedInfo(targetValue, currentPath);
|
|
1265
|
-
}
|
|
1266
|
-
if (targetType !== sourceType) {
|
|
1267
|
-
if (existingInfo) {
|
|
1268
|
-
await addConflict(currentPath, existingInfo, sourceValue, "type_mismatch");
|
|
1269
|
-
}
|
|
1270
|
-
continue;
|
|
1271
|
-
}
|
|
1272
|
-
if (targetType !== "object") {
|
|
1273
|
-
if (existingInfo) {
|
|
1274
|
-
await addConflict(currentPath, existingInfo, sourceValue, "path_overwrite");
|
|
1275
|
-
}
|
|
1276
|
-
if (targetValue !== sourceValue) {
|
|
1277
|
-
continue;
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1601
|
+
if (!this.isValidJavaScriptIdentifier(tag.variableName)) {
|
|
1602
|
+
throw new Error(
|
|
1603
|
+
`Invalid JavaScript identifier: "${tag.variableName}". Variable names must start with a letter, underscore, or dollar sign, and contain only letters, digits, underscores, and dollar signs.`
|
|
1604
|
+
);
|
|
1280
1605
|
}
|
|
1281
|
-
if (
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
valueTracker,
|
|
1291
|
-
addConflict
|
|
1606
|
+
if (tag.translations == null) {
|
|
1607
|
+
throw new Error(`tag.translations required`);
|
|
1608
|
+
}
|
|
1609
|
+
let importedFile = this.importedFiles.find(
|
|
1610
|
+
(file) => file.pathRelativeToImportDir === pathRelativeToImportDir
|
|
1611
|
+
);
|
|
1612
|
+
if (importedFile) {
|
|
1613
|
+
const duplicateTag = importedFile.tags.find(
|
|
1614
|
+
(existingTag) => existingTag.variableName === tag.variableName
|
|
1292
1615
|
);
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1616
|
+
if (duplicateTag) {
|
|
1617
|
+
throw new Error(
|
|
1618
|
+
`Duplicate variable name "${tag.variableName}" in file "${pathRelativeToImportDir}". Variable names must be unique within the same file.`
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1296
1621
|
}
|
|
1297
|
-
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1301
|
-
logger.info("Collecting translations from source files...");
|
|
1302
|
-
const files = await $LT_CollectCandidateFilesWithTags({ config, logger });
|
|
1303
|
-
if (config.debug) {
|
|
1304
|
-
for (let file of files) {
|
|
1305
|
-
logger.debug("Found {count} translations tags inside: {file}", { count: file.tags.length, file: file.relativeFilePath });
|
|
1622
|
+
if (!importedFile) {
|
|
1623
|
+
importedFile = { pathRelativeToImportDir, tags: [] };
|
|
1624
|
+
this.importedFiles.push(importedFile);
|
|
1306
1625
|
}
|
|
1626
|
+
importedFile.tags.push(tag);
|
|
1307
1627
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return;
|
|
1628
|
+
getImportedFiles() {
|
|
1629
|
+
return [...this.importedFiles];
|
|
1311
1630
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
const totalTags = files.reduce((sum, file) => sum + file.tags.length, 0);
|
|
1315
|
-
logger.debug("Found {totalTags} translation tags", { totalTags });
|
|
1316
|
-
await $LT_WriteToCollections({ config, collections, logger, clean: options?.clean });
|
|
1317
|
-
} catch (e) {
|
|
1318
|
-
const prefix = "LangTagConflictResolution:";
|
|
1319
|
-
if (e.message.startsWith(prefix)) {
|
|
1320
|
-
logger.error(e.message.substring(prefix.length));
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
throw e;
|
|
1631
|
+
getImportedFilesCount() {
|
|
1632
|
+
return this.importedFiles.length;
|
|
1324
1633
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const globStartIndex = pattern.indexOf("*");
|
|
1328
|
-
const braceStartIndex = pattern.indexOf("{");
|
|
1329
|
-
let endIndex = -1;
|
|
1330
|
-
if (globStartIndex !== -1 && braceStartIndex !== -1) {
|
|
1331
|
-
endIndex = Math.min(globStartIndex, braceStartIndex);
|
|
1332
|
-
} else if (globStartIndex !== -1) {
|
|
1333
|
-
endIndex = globStartIndex;
|
|
1334
|
-
} else if (braceStartIndex !== -1) {
|
|
1335
|
-
endIndex = braceStartIndex;
|
|
1634
|
+
hasImportedFiles() {
|
|
1635
|
+
return this.importedFiles.length > 0;
|
|
1336
1636
|
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
return lastSlashIndex !== -1 ? pattern.substring(0, lastSlashIndex) : ".";
|
|
1637
|
+
isValidJavaScriptIdentifier(name) {
|
|
1638
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
1340
1639
|
}
|
|
1341
|
-
const lastSeparatorIndex = pattern.lastIndexOf("/", endIndex);
|
|
1342
|
-
return lastSeparatorIndex === -1 ? "." : pattern.substring(0, lastSeparatorIndex);
|
|
1343
|
-
}
|
|
1344
|
-
function $LT_CreateChokidarWatcher(config) {
|
|
1345
|
-
const cwd = process.cwd();
|
|
1346
|
-
const baseDirsToWatch = [
|
|
1347
|
-
...new Set(config.includes.map((pattern) => getBasePath(pattern)))
|
|
1348
|
-
];
|
|
1349
|
-
const finalDirsToWatch = baseDirsToWatch.map((dir) => dir === "." ? cwd : dir);
|
|
1350
|
-
const ignored = [...config.excludes, "**/.git/**"];
|
|
1351
|
-
return chokidar.watch(finalDirsToWatch, {
|
|
1352
|
-
// Watch base directories
|
|
1353
|
-
cwd,
|
|
1354
|
-
ignored,
|
|
1355
|
-
persistent: true,
|
|
1356
|
-
ignoreInitial: true,
|
|
1357
|
-
awaitWriteFinish: {
|
|
1358
|
-
stabilityThreshold: 300,
|
|
1359
|
-
pollInterval: 100
|
|
1360
|
-
}
|
|
1361
|
-
});
|
|
1362
1640
|
}
|
|
1363
|
-
async function $
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1641
|
+
async function $LT_ImportLibraries(config, logger) {
|
|
1642
|
+
const exportFiles = await $LT_CollectExportFiles(logger);
|
|
1643
|
+
const importManager = new ImportManager();
|
|
1644
|
+
let exports2 = [];
|
|
1645
|
+
for (const { exportPath, packageJsonPath } of exportFiles) {
|
|
1646
|
+
const exportData = await $LT_ReadJSON(exportPath);
|
|
1647
|
+
const packageJSON = await $LT_ReadJSON(packageJsonPath);
|
|
1648
|
+
exports2.push({ packageJSON, exportData });
|
|
1649
|
+
}
|
|
1650
|
+
config.import.onImport({
|
|
1651
|
+
exports: exports2,
|
|
1652
|
+
importManager,
|
|
1653
|
+
logger,
|
|
1654
|
+
langTagConfig: config
|
|
1372
1655
|
});
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
if (!micromatch.isMatch(cwdRelativeFilePath, config.includes)) {
|
|
1656
|
+
if (!importManager.hasImportedFiles()) {
|
|
1657
|
+
logger.warn("No tags were imported from any library files");
|
|
1376
1658
|
return;
|
|
1377
1659
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
const
|
|
1383
|
-
await $
|
|
1660
|
+
await generateImportFiles(config, logger, importManager);
|
|
1661
|
+
if (config.import.onImportFinish) config.import.onImportFinish();
|
|
1662
|
+
}
|
|
1663
|
+
async function $LT_ImportTranslations() {
|
|
1664
|
+
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1665
|
+
await $LT_EnsureDirectoryExists(config.import.dir);
|
|
1666
|
+
logger.info("Importing translations from libraries...");
|
|
1667
|
+
await $LT_ImportLibraries(config, logger);
|
|
1668
|
+
logger.success("Successfully imported translations from libraries.");
|
|
1384
1669
|
}
|
|
1385
1670
|
async function detectModuleSystem() {
|
|
1386
1671
|
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
@@ -1433,7 +1718,7 @@ const generationAlgorithm = pathBasedConfigGenerator({
|
|
|
1433
1718
|
});
|
|
1434
1719
|
const keeper = configKeeper({ propertyName: 'keep' });
|
|
1435
1720
|
|
|
1436
|
-
/** @type {import('@lang-tag/cli/
|
|
1721
|
+
/** @type {import('@lang-tag/cli/type').LangTagCLIConfig} */
|
|
1437
1722
|
const config = {
|
|
1438
1723
|
tagName: 'lang',
|
|
1439
1724
|
isLibrary: false,
|
|
@@ -1452,242 +1737,38 @@ const config = {
|
|
|
1452
1737
|
await keeper(event);
|
|
1453
1738
|
},
|
|
1454
1739
|
collect: {
|
|
1455
|
-
defaultNamespace: 'common',
|
|
1456
|
-
onConflictResolution: async event => {
|
|
1457
|
-
await event.logger.conflict(event.conflict, true);
|
|
1458
|
-
// By default, continue processing even if conflicts occur
|
|
1459
|
-
// Call event.exit(); to terminate the process upon the first conflict
|
|
1460
|
-
},
|
|
1461
|
-
onCollectFinish: event => {
|
|
1462
|
-
if (event.conflicts.length) event.exit(); // Stop the process to avoid merging on conflict
|
|
1463
|
-
}
|
|
1464
|
-
},
|
|
1465
|
-
translationArgPosition: 1,
|
|
1466
|
-
debug: false,
|
|
1467
|
-
};
|
|
1468
|
-
|
|
1469
|
-
${exportStatement}`;
|
|
1470
|
-
}
|
|
1471
|
-
async function $LT_CMD_InitConfig() {
|
|
1472
|
-
const logger = $LT_CreateDefaultLogger();
|
|
1473
|
-
if (fs.existsSync(CONFIG_FILE_NAME)) {
|
|
1474
|
-
logger.success("Configuration file already exists. Please remove the existing configuration file before creating a new default one");
|
|
1475
|
-
return;
|
|
1476
|
-
}
|
|
1477
|
-
try {
|
|
1478
|
-
const configContent = await generateDefaultConfig();
|
|
1479
|
-
await promises.writeFile(CONFIG_FILE_NAME, configContent, "utf-8");
|
|
1480
|
-
logger.success("Configuration file created successfully");
|
|
1481
|
-
} catch (error) {
|
|
1482
|
-
logger.error(error?.message);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
async function $LT_CollectExportFiles(logger) {
|
|
1486
|
-
const nodeModulesPath = path$1.join(process$1.cwd(), "node_modules");
|
|
1487
|
-
if (!fs.existsSync(nodeModulesPath)) {
|
|
1488
|
-
logger.error('"node_modules" directory not found');
|
|
1489
|
-
return [];
|
|
1490
|
-
}
|
|
1491
|
-
const results = [];
|
|
1492
|
-
try {
|
|
1493
|
-
const exportFiles = await globby.globby([
|
|
1494
|
-
`node_modules/**/${EXPORTS_FILE_NAME}`
|
|
1495
|
-
], {
|
|
1496
|
-
cwd: process$1.cwd(),
|
|
1497
|
-
onlyFiles: true,
|
|
1498
|
-
ignore: ["**/node_modules/**/node_modules/**"],
|
|
1499
|
-
deep: 4
|
|
1500
|
-
});
|
|
1501
|
-
for (const exportFile of exportFiles) {
|
|
1502
|
-
const fullExportPath = path$1.resolve(exportFile);
|
|
1503
|
-
const packageJsonPath = findPackageJsonForExport(fullExportPath, nodeModulesPath);
|
|
1504
|
-
if (packageJsonPath) {
|
|
1505
|
-
results.push({
|
|
1506
|
-
exportPath: fullExportPath,
|
|
1507
|
-
packageJsonPath
|
|
1508
|
-
});
|
|
1509
|
-
} else {
|
|
1510
|
-
logger.warn("Found export file but could not match package.json: {exportPath}", {
|
|
1511
|
-
exportPath: fullExportPath
|
|
1512
|
-
});
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
logger.debug("Found {count} export files with matching package.json in node_modules", {
|
|
1516
|
-
count: results.length
|
|
1517
|
-
});
|
|
1518
|
-
} catch (error) {
|
|
1519
|
-
logger.error("Error scanning node_modules with globby: {error}", {
|
|
1520
|
-
error: String(error)
|
|
1521
|
-
});
|
|
1522
|
-
}
|
|
1523
|
-
return results;
|
|
1524
|
-
}
|
|
1525
|
-
function findPackageJsonForExport(exportPath, nodeModulesPath) {
|
|
1526
|
-
const relativePath = path$1.relative(nodeModulesPath, exportPath);
|
|
1527
|
-
const pathParts = relativePath.split(path$1.sep);
|
|
1528
|
-
if (pathParts.length < 2) {
|
|
1529
|
-
return null;
|
|
1530
|
-
}
|
|
1531
|
-
if (pathParts[0].startsWith("@")) {
|
|
1532
|
-
if (pathParts.length >= 3) {
|
|
1533
|
-
const packageDir = path$1.join(nodeModulesPath, pathParts[0], pathParts[1]);
|
|
1534
|
-
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
1535
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
1536
|
-
return packageJsonPath;
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
} else {
|
|
1540
|
-
const packageDir = path$1.join(nodeModulesPath, pathParts[0]);
|
|
1541
|
-
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
1542
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
1543
|
-
return packageJsonPath;
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
return null;
|
|
1547
|
-
}
|
|
1548
|
-
const __filename$1 = url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
1549
|
-
const __dirname$1 = path.dirname(__filename$1);
|
|
1550
|
-
const templatePath = path.join(__dirname$1, "templates", "import", "imported-tag.mustache");
|
|
1551
|
-
const template = fs.readFileSync(templatePath, "utf-8");
|
|
1552
|
-
function renderTemplate$1(data) {
|
|
1553
|
-
return mustache.render(template, data, {}, { escape: (text) => text });
|
|
1554
|
-
}
|
|
1555
|
-
async function generateImportFiles(config, logger, importManager) {
|
|
1556
|
-
const importedFiles = importManager.getImportedFiles();
|
|
1557
|
-
for (const importedFile of importedFiles) {
|
|
1558
|
-
const filePath = path$1.resolve(
|
|
1559
|
-
process__namespace.cwd(),
|
|
1560
|
-
config.import.dir,
|
|
1561
|
-
importedFile.pathRelativeToImportDir
|
|
1562
|
-
);
|
|
1563
|
-
const processedExports = importedFile.tags.map((tag) => {
|
|
1564
|
-
const parameter1 = config.translationArgPosition === 1 ? tag.translations : tag.config;
|
|
1565
|
-
const parameter2 = config.translationArgPosition === 1 ? tag.config : tag.translations;
|
|
1566
|
-
const hasParameter2 = parameter2 !== null && parameter2 !== void 0 && (typeof parameter2 !== "object" || Object.keys(parameter2).length > 0);
|
|
1567
|
-
return {
|
|
1568
|
-
name: tag.variableName,
|
|
1569
|
-
parameter1: JSON5.stringify(parameter1, void 0, 4),
|
|
1570
|
-
parameter2: hasParameter2 ? JSON5.stringify(parameter2, void 0, 4) : null,
|
|
1571
|
-
hasParameter2,
|
|
1572
|
-
config: {
|
|
1573
|
-
tagName: config.tagName
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
});
|
|
1577
|
-
const templateData = {
|
|
1578
|
-
tagImportPath: config.import.tagImportPath,
|
|
1579
|
-
exports: processedExports
|
|
1580
|
-
};
|
|
1581
|
-
const content = renderTemplate$1(templateData);
|
|
1582
|
-
await flexibleImportAlgorithm.$LT_EnsureDirectoryExists(path.dirname(filePath));
|
|
1583
|
-
await promises.writeFile(filePath, content, "utf-8");
|
|
1584
|
-
logger.success('Created tag file: "{file}"', { file: importedFile.pathRelativeToImportDir });
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
class ImportManager {
|
|
1588
|
-
importedFiles = [];
|
|
1589
|
-
constructor() {
|
|
1590
|
-
this.importedFiles = [];
|
|
1591
|
-
}
|
|
1592
|
-
importTag(pathRelativeToImportDir, tag) {
|
|
1593
|
-
if (!pathRelativeToImportDir) {
|
|
1594
|
-
throw new Error(`pathRelativeToImportDir required, got: ${pathRelativeToImportDir}`);
|
|
1595
|
-
}
|
|
1596
|
-
if (!tag?.variableName) {
|
|
1597
|
-
throw new Error(`tag.variableName required, got: ${tag?.variableName}`);
|
|
1598
|
-
}
|
|
1599
|
-
if (!this.isValidJavaScriptIdentifier(tag.variableName)) {
|
|
1600
|
-
throw new Error(`Invalid JavaScript identifier: "${tag.variableName}". Variable names must start with a letter, underscore, or dollar sign, and contain only letters, digits, underscores, and dollar signs.`);
|
|
1601
|
-
}
|
|
1602
|
-
if (tag.translations == null) {
|
|
1603
|
-
throw new Error(`tag.translations required`);
|
|
1604
|
-
}
|
|
1605
|
-
let importedFile = this.importedFiles.find(
|
|
1606
|
-
(file) => file.pathRelativeToImportDir === pathRelativeToImportDir
|
|
1607
|
-
);
|
|
1608
|
-
if (importedFile) {
|
|
1609
|
-
const duplicateTag = importedFile.tags.find(
|
|
1610
|
-
(existingTag) => existingTag.variableName === tag.variableName
|
|
1611
|
-
);
|
|
1612
|
-
if (duplicateTag) {
|
|
1613
|
-
throw new Error(`Duplicate variable name "${tag.variableName}" in file "${pathRelativeToImportDir}". Variable names must be unique within the same file.`);
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
if (!importedFile) {
|
|
1617
|
-
importedFile = { pathRelativeToImportDir, tags: [] };
|
|
1618
|
-
this.importedFiles.push(importedFile);
|
|
1619
|
-
}
|
|
1620
|
-
importedFile.tags.push(tag);
|
|
1621
|
-
}
|
|
1622
|
-
getImportedFiles() {
|
|
1623
|
-
return [...this.importedFiles];
|
|
1624
|
-
}
|
|
1625
|
-
getImportedFilesCount() {
|
|
1626
|
-
return this.importedFiles.length;
|
|
1627
|
-
}
|
|
1628
|
-
hasImportedFiles() {
|
|
1629
|
-
return this.importedFiles.length > 0;
|
|
1630
|
-
}
|
|
1631
|
-
isValidJavaScriptIdentifier(name) {
|
|
1632
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
1633
|
-
}
|
|
1740
|
+
defaultNamespace: 'common',
|
|
1741
|
+
onConflictResolution: async event => {
|
|
1742
|
+
await event.logger.conflict(event.conflict, true);
|
|
1743
|
+
// By default, continue processing even if conflicts occur
|
|
1744
|
+
// Call event.exit(); to terminate the process upon the first conflict
|
|
1745
|
+
},
|
|
1746
|
+
onCollectFinish: event => {
|
|
1747
|
+
if (event.conflicts.length) event.exit(); // Stop the process to avoid merging on conflict
|
|
1748
|
+
}
|
|
1749
|
+
},
|
|
1750
|
+
translationArgPosition: 1,
|
|
1751
|
+
debug: false,
|
|
1752
|
+
};
|
|
1753
|
+
|
|
1754
|
+
${exportStatement}`;
|
|
1634
1755
|
}
|
|
1635
|
-
async function $
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
const packageJSON = await flexibleImportAlgorithm.$LT_ReadJSON(packageJsonPath);
|
|
1642
|
-
exports2.push({ packageJSON, exportData });
|
|
1643
|
-
}
|
|
1644
|
-
config.import.onImport({ exports: exports2, importManager, logger, langTagConfig: config });
|
|
1645
|
-
if (!importManager.hasImportedFiles()) {
|
|
1646
|
-
logger.warn("No tags were imported from any library files");
|
|
1756
|
+
async function $LT_CMD_InitConfig() {
|
|
1757
|
+
const logger = $LT_CreateDefaultLogger();
|
|
1758
|
+
if (fs.existsSync(CONFIG_FILE_NAME)) {
|
|
1759
|
+
logger.success(
|
|
1760
|
+
"Configuration file already exists. Please remove the existing configuration file before creating a new default one"
|
|
1761
|
+
);
|
|
1647
1762
|
return;
|
|
1648
1763
|
}
|
|
1649
|
-
await generateImportFiles(config, logger, importManager);
|
|
1650
|
-
if (config.import.onImportFinish) config.import.onImportFinish();
|
|
1651
|
-
}
|
|
1652
|
-
async function $LT_ImportTranslations() {
|
|
1653
|
-
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1654
|
-
await flexibleImportAlgorithm.$LT_EnsureDirectoryExists(config.import.dir);
|
|
1655
|
-
logger.info("Importing translations from libraries...");
|
|
1656
|
-
await $LT_ImportLibraries(config, logger);
|
|
1657
|
-
logger.success("Successfully imported translations from libraries.");
|
|
1658
|
-
}
|
|
1659
|
-
function renderTemplate(template2, data) {
|
|
1660
|
-
return mustache.render(template2, data, {}, { escape: (text) => text });
|
|
1661
|
-
}
|
|
1662
|
-
function loadTemplate(templateName) {
|
|
1663
|
-
const __filename2 = url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename2).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
1664
|
-
const __dirname = path.dirname(__filename2);
|
|
1665
|
-
const templatePath2 = path.join(__dirname, "templates", "tag", `${templateName}.mustache`);
|
|
1666
1764
|
try {
|
|
1667
|
-
|
|
1765
|
+
const configContent = await generateDefaultConfig();
|
|
1766
|
+
await promises.writeFile(CONFIG_FILE_NAME, configContent, "utf-8");
|
|
1767
|
+
logger.success("Configuration file created successfully");
|
|
1668
1768
|
} catch (error) {
|
|
1669
|
-
|
|
1769
|
+
logger.error(error?.message);
|
|
1670
1770
|
}
|
|
1671
1771
|
}
|
|
1672
|
-
function prepareTemplateData(options) {
|
|
1673
|
-
return {
|
|
1674
|
-
...options,
|
|
1675
|
-
tmpVariables: {
|
|
1676
|
-
key: "{{key}}",
|
|
1677
|
-
username: "{{username}}",
|
|
1678
|
-
processRegex: "{{(.*?)}}"
|
|
1679
|
-
}
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
|
-
function renderInitTagTemplates(options) {
|
|
1683
|
-
const baseTemplateName = options.isLibrary ? "base-library" : "base-app";
|
|
1684
|
-
const baseTemplate = loadTemplate(baseTemplateName);
|
|
1685
|
-
const placeholderTemplate = loadTemplate("placeholder");
|
|
1686
|
-
const templateData = prepareTemplateData(options);
|
|
1687
|
-
const renderedBase = renderTemplate(baseTemplate, templateData);
|
|
1688
|
-
const renderedPlaceholders = renderTemplate(placeholderTemplate, templateData);
|
|
1689
|
-
return renderedBase + "\n\n" + renderedPlaceholders;
|
|
1690
|
-
}
|
|
1691
1772
|
async function readPackageJson() {
|
|
1692
1773
|
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
1693
1774
|
if (!fs.existsSync(packageJsonPath)) {
|
|
@@ -1729,51 +1810,312 @@ async function detectInitTagOptions(options, config) {
|
|
|
1729
1810
|
packageVersion: packageJson?.version || "1.0.0"
|
|
1730
1811
|
};
|
|
1731
1812
|
}
|
|
1813
|
+
function renderTemplate(template2, data) {
|
|
1814
|
+
return mustache.render(template2, data, {}, { escape: (text) => text });
|
|
1815
|
+
}
|
|
1816
|
+
function loadTemplate(templateName) {
|
|
1817
|
+
const __filename2 = url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename2).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
1818
|
+
const __dirname = path.dirname(__filename2);
|
|
1819
|
+
const templatePath2 = path.join(
|
|
1820
|
+
__dirname,
|
|
1821
|
+
"templates",
|
|
1822
|
+
"tag",
|
|
1823
|
+
`${templateName}.mustache`
|
|
1824
|
+
);
|
|
1825
|
+
try {
|
|
1826
|
+
return fs.readFileSync(templatePath2, "utf-8");
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
throw new Error(`Failed to load template ${templateName}: ${error}`);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
function prepareTemplateData(options) {
|
|
1832
|
+
return {
|
|
1833
|
+
...options,
|
|
1834
|
+
tmpVariables: {
|
|
1835
|
+
key: "{{key}}",
|
|
1836
|
+
username: "{{username}}",
|
|
1837
|
+
processRegex: "{{(.*?)}}"
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
function renderInitTagTemplates(options) {
|
|
1842
|
+
const baseTemplateName = options.isLibrary ? "base-library" : "base-app";
|
|
1843
|
+
const baseTemplate = loadTemplate(baseTemplateName);
|
|
1844
|
+
const placeholderTemplate = loadTemplate("placeholder");
|
|
1845
|
+
const templateData = prepareTemplateData(options);
|
|
1846
|
+
const renderedBase = renderTemplate(baseTemplate, templateData);
|
|
1847
|
+
const renderedPlaceholders = renderTemplate(
|
|
1848
|
+
placeholderTemplate,
|
|
1849
|
+
templateData
|
|
1850
|
+
);
|
|
1851
|
+
return renderedBase + "\n\n" + renderedPlaceholders;
|
|
1852
|
+
}
|
|
1732
1853
|
async function $LT_CMD_InitTagFile(options = {}) {
|
|
1733
1854
|
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1734
1855
|
const renderOptions = await detectInitTagOptions(options, config);
|
|
1735
1856
|
const outputPath = options.output || `${renderOptions.tagName}.${renderOptions.fileExtension}`;
|
|
1736
1857
|
logger.info("Initializing lang-tag with the following options:");
|
|
1737
1858
|
logger.info(" Tag name: {tagName}", { tagName: renderOptions.tagName });
|
|
1738
|
-
logger.info(" Library mode: {isLibrary}", {
|
|
1739
|
-
|
|
1740
|
-
|
|
1859
|
+
logger.info(" Library mode: {isLibrary}", {
|
|
1860
|
+
isLibrary: renderOptions.isLibrary ? "Yes" : "No"
|
|
1861
|
+
});
|
|
1862
|
+
logger.info(" React: {isReact}", {
|
|
1863
|
+
isReact: renderOptions.isReact ? "Yes" : "No"
|
|
1864
|
+
});
|
|
1865
|
+
logger.info(" TypeScript: {isTypeScript}", {
|
|
1866
|
+
isTypeScript: renderOptions.isTypeScript ? "Yes" : "No"
|
|
1867
|
+
});
|
|
1741
1868
|
logger.info(" Output path: {outputPath}", { outputPath });
|
|
1742
1869
|
let renderedContent;
|
|
1743
1870
|
try {
|
|
1744
1871
|
renderedContent = renderInitTagTemplates(renderOptions);
|
|
1745
1872
|
} catch (error) {
|
|
1746
|
-
logger.error("Failed to render templates: {error}", {
|
|
1873
|
+
logger.error("Failed to render templates: {error}", {
|
|
1874
|
+
error: error?.message
|
|
1875
|
+
});
|
|
1747
1876
|
return;
|
|
1748
1877
|
}
|
|
1749
1878
|
if (fs.existsSync(outputPath)) {
|
|
1750
1879
|
logger.warn("File already exists: {outputPath}", { outputPath });
|
|
1751
|
-
logger.info(
|
|
1880
|
+
logger.info(
|
|
1881
|
+
"Use --output to specify a different path or remove the existing file"
|
|
1882
|
+
);
|
|
1752
1883
|
return;
|
|
1753
1884
|
}
|
|
1754
1885
|
try {
|
|
1755
|
-
await
|
|
1756
|
-
logger.success("Lang-tag file created successfully: {outputPath}", {
|
|
1886
|
+
await $LT_WriteFileWithDirs(outputPath, renderedContent);
|
|
1887
|
+
logger.success("Lang-tag file created successfully: {outputPath}", {
|
|
1888
|
+
outputPath
|
|
1889
|
+
});
|
|
1757
1890
|
logger.info("Next steps:");
|
|
1758
|
-
logger.info("1. Import the {tagName} function in your files:", {
|
|
1891
|
+
logger.info("1. Import the {tagName} function in your files:", {
|
|
1892
|
+
tagName: renderOptions.tagName
|
|
1893
|
+
});
|
|
1759
1894
|
logger.info(" import { {tagName} } from './{importPath}';", {
|
|
1760
1895
|
tagName: renderOptions.tagName,
|
|
1761
1896
|
importPath: outputPath.replace(/^src\//, "")
|
|
1762
1897
|
});
|
|
1763
|
-
logger.info(
|
|
1898
|
+
logger.info(
|
|
1899
|
+
"2. Create your translation objects and use the tag function"
|
|
1900
|
+
);
|
|
1764
1901
|
logger.info('3. Run "lang-tag collect" to extract translations');
|
|
1765
1902
|
} catch (error) {
|
|
1766
|
-
logger.error("Failed to write file: {error}", {
|
|
1903
|
+
logger.error("Failed to write file: {error}", {
|
|
1904
|
+
error: error?.message
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
function deepFreezeObject(obj) {
|
|
1909
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
1910
|
+
for (const name of propNames) {
|
|
1911
|
+
const value = obj[name];
|
|
1912
|
+
if (value && typeof value === "object") {
|
|
1913
|
+
deepFreezeObject(value);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return Object.freeze(obj);
|
|
1917
|
+
}
|
|
1918
|
+
async function checkAndRegenerateFileLangTags(config, logger, file, path$12) {
|
|
1919
|
+
let libraryImportsDir = config.import.dir;
|
|
1920
|
+
if (!libraryImportsDir.endsWith(path.sep)) libraryImportsDir += path.sep;
|
|
1921
|
+
const fileContent = fs.readFileSync(file, "utf-8");
|
|
1922
|
+
const processor = new $LT_TagProcessor(config);
|
|
1923
|
+
let tags = processor.extractTags(fileContent);
|
|
1924
|
+
tags = $LT_FilterInvalidTags(tags, config, logger);
|
|
1925
|
+
if (!tags.length) {
|
|
1926
|
+
return false;
|
|
1927
|
+
}
|
|
1928
|
+
const replacements = [];
|
|
1929
|
+
let lastUpdatedLine = 0;
|
|
1930
|
+
for (let tag of tags) {
|
|
1931
|
+
let newConfig = void 0;
|
|
1932
|
+
let shouldUpdate = false;
|
|
1933
|
+
const frozenConfig = tag.parameterConfig ? deepFreezeObject(tag.parameterConfig) : tag.parameterConfig;
|
|
1934
|
+
const event = {
|
|
1935
|
+
langTagConfig: config,
|
|
1936
|
+
logger,
|
|
1937
|
+
config: frozenConfig,
|
|
1938
|
+
absolutePath: file,
|
|
1939
|
+
relativePath: path$12,
|
|
1940
|
+
isImportedLibrary: path$12.startsWith(libraryImportsDir),
|
|
1941
|
+
isSaved: false,
|
|
1942
|
+
savedConfig: void 0,
|
|
1943
|
+
save: (updatedConfig, triggerName) => {
|
|
1944
|
+
if (!updatedConfig && updatedConfig !== null)
|
|
1945
|
+
throw new Error("Wrong config data");
|
|
1946
|
+
newConfig = updatedConfig;
|
|
1947
|
+
shouldUpdate = true;
|
|
1948
|
+
event.isSaved = true;
|
|
1949
|
+
event.savedConfig = updatedConfig;
|
|
1950
|
+
logger.debug(
|
|
1951
|
+
'Called save for "{path}" with config "{config}" triggered by: ("{trigger}")',
|
|
1952
|
+
{
|
|
1953
|
+
path: path$12,
|
|
1954
|
+
config: JSON.stringify(updatedConfig),
|
|
1955
|
+
trigger: triggerName || "-"
|
|
1956
|
+
}
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
await config.onConfigGeneration(event);
|
|
1961
|
+
if (!shouldUpdate) {
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
lastUpdatedLine = tag.line;
|
|
1965
|
+
if (!isConfigSame(tag.parameterConfig, newConfig)) {
|
|
1966
|
+
replacements.push({ tag, config: newConfig });
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
if (replacements.length) {
|
|
1970
|
+
const newContent = processor.replaceTags(fileContent, replacements);
|
|
1971
|
+
await promises.writeFile(file, newContent, "utf-8");
|
|
1972
|
+
const encodedFile = encodeURI(file);
|
|
1973
|
+
logger.info(
|
|
1974
|
+
'Lang tag configurations written for file "{path}" (file://{file}:{line})',
|
|
1975
|
+
{ path: path$12, file: encodedFile, line: lastUpdatedLine }
|
|
1976
|
+
);
|
|
1977
|
+
return true;
|
|
1978
|
+
}
|
|
1979
|
+
return false;
|
|
1980
|
+
}
|
|
1981
|
+
function isConfigSame(c1, c2) {
|
|
1982
|
+
if (!c1 && !c2) return true;
|
|
1983
|
+
if (c1 && typeof c1 === "object" && c2 && typeof c2 === "object" && JSON5.stringify(c1) === JSON5.stringify(c2))
|
|
1984
|
+
return true;
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1987
|
+
async function $LT_CMD_RegenerateTags() {
|
|
1988
|
+
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1989
|
+
const files = await globby.globby(config.includes, {
|
|
1990
|
+
cwd: process.cwd(),
|
|
1991
|
+
ignore: config.excludes,
|
|
1992
|
+
absolute: true
|
|
1993
|
+
});
|
|
1994
|
+
const charactersToSkip = process.cwd().length + 1;
|
|
1995
|
+
let dirty = false;
|
|
1996
|
+
for (const file of files) {
|
|
1997
|
+
const path2 = file.substring(charactersToSkip);
|
|
1998
|
+
const localDirty = await checkAndRegenerateFileLangTags(
|
|
1999
|
+
config,
|
|
2000
|
+
logger,
|
|
2001
|
+
file,
|
|
2002
|
+
path2
|
|
2003
|
+
);
|
|
2004
|
+
if (localDirty) {
|
|
2005
|
+
dirty = true;
|
|
2006
|
+
}
|
|
1767
2007
|
}
|
|
2008
|
+
if (!dirty) {
|
|
2009
|
+
logger.info(
|
|
2010
|
+
"No changes were made based on the current configuration and files"
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
function getBasePath(pattern) {
|
|
2015
|
+
const globStartIndex = pattern.indexOf("*");
|
|
2016
|
+
const braceStartIndex = pattern.indexOf("{");
|
|
2017
|
+
let endIndex = -1;
|
|
2018
|
+
if (globStartIndex !== -1 && braceStartIndex !== -1) {
|
|
2019
|
+
endIndex = Math.min(globStartIndex, braceStartIndex);
|
|
2020
|
+
} else if (globStartIndex !== -1) {
|
|
2021
|
+
endIndex = globStartIndex;
|
|
2022
|
+
} else if (braceStartIndex !== -1) {
|
|
2023
|
+
endIndex = braceStartIndex;
|
|
2024
|
+
}
|
|
2025
|
+
if (endIndex === -1) {
|
|
2026
|
+
const lastSlashIndex = pattern.lastIndexOf("/");
|
|
2027
|
+
return lastSlashIndex !== -1 ? pattern.substring(0, lastSlashIndex) : ".";
|
|
2028
|
+
}
|
|
2029
|
+
const lastSeparatorIndex = pattern.lastIndexOf("/", endIndex);
|
|
2030
|
+
return lastSeparatorIndex === -1 ? "." : pattern.substring(0, lastSeparatorIndex);
|
|
2031
|
+
}
|
|
2032
|
+
function $LT_CreateChokidarWatcher(config) {
|
|
2033
|
+
const cwd = process.cwd();
|
|
2034
|
+
const baseDirsToWatch = [
|
|
2035
|
+
...new Set(config.includes.map((pattern) => getBasePath(pattern)))
|
|
2036
|
+
];
|
|
2037
|
+
const finalDirsToWatch = baseDirsToWatch.map(
|
|
2038
|
+
(dir) => dir === "." ? cwd : dir
|
|
2039
|
+
);
|
|
2040
|
+
const ignored = [...config.excludes, "**/.git/**"];
|
|
2041
|
+
return chokidar.watch(finalDirsToWatch, {
|
|
2042
|
+
// Watch base directories
|
|
2043
|
+
cwd,
|
|
2044
|
+
ignored,
|
|
2045
|
+
persistent: true,
|
|
2046
|
+
ignoreInitial: true,
|
|
2047
|
+
awaitWriteFinish: {
|
|
2048
|
+
stabilityThreshold: 300,
|
|
2049
|
+
pollInterval: 100
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
async function $LT_WatchTranslations() {
|
|
2054
|
+
const { config, logger } = await $LT_GetCommandEssentials();
|
|
2055
|
+
await $LT_CMD_Collect();
|
|
2056
|
+
const watcher = $LT_CreateChokidarWatcher(config);
|
|
2057
|
+
logger.info("Starting watch mode for translations...");
|
|
2058
|
+
logger.info("Watching for changes...");
|
|
2059
|
+
logger.info("Press Ctrl+C to stop watching");
|
|
2060
|
+
watcher.on(
|
|
2061
|
+
"change",
|
|
2062
|
+
async (filePath) => await handleFile(config, logger, filePath)
|
|
2063
|
+
).on(
|
|
2064
|
+
"add",
|
|
2065
|
+
async (filePath) => await handleFile(config, logger, filePath)
|
|
2066
|
+
).on("error", (error) => {
|
|
2067
|
+
logger.error("Error in file watcher: {error}", { error });
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
async function handleFile(config, logger, cwdRelativeFilePath, event) {
|
|
2071
|
+
if (!micromatch.isMatch(cwdRelativeFilePath, config.includes)) {
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
const cwd = process.cwd();
|
|
2075
|
+
const absoluteFilePath = path.join(cwd, cwdRelativeFilePath);
|
|
2076
|
+
await checkAndRegenerateFileLangTags(
|
|
2077
|
+
config,
|
|
2078
|
+
logger,
|
|
2079
|
+
absoluteFilePath,
|
|
2080
|
+
cwdRelativeFilePath
|
|
2081
|
+
);
|
|
2082
|
+
const files = await $LT_CollectCandidateFilesWithTags({
|
|
2083
|
+
filesToScan: [cwdRelativeFilePath],
|
|
2084
|
+
config,
|
|
2085
|
+
logger
|
|
2086
|
+
});
|
|
2087
|
+
const namespaces = await $LT_GroupTagsToCollections({
|
|
2088
|
+
logger,
|
|
2089
|
+
files,
|
|
2090
|
+
config
|
|
2091
|
+
});
|
|
2092
|
+
await $LT_WriteToCollections({ config, collections: namespaces, logger });
|
|
1768
2093
|
}
|
|
1769
2094
|
function createCli() {
|
|
1770
2095
|
commander.program.name("lang-tag").description("CLI to manage language translations").version("0.1.0");
|
|
1771
2096
|
commander.program.command("collect").alias("c").description("Collect translations from source files").option("-c, --clean", "Remove output directory before collecting").action($LT_CMD_Collect);
|
|
1772
2097
|
commander.program.command("import").alias("i").description("Import translations from libraries in node_modules").action($LT_ImportTranslations);
|
|
1773
2098
|
commander.program.command("regenerate-tags").alias("rt").description("Regenerate configuration for language tags").action($LT_CMD_RegenerateTags);
|
|
1774
|
-
commander.program.command("watch").alias("w").description(
|
|
2099
|
+
commander.program.command("watch").alias("w").description(
|
|
2100
|
+
"Watch for changes in source files and automatically collect translations"
|
|
2101
|
+
).action($LT_WatchTranslations);
|
|
1775
2102
|
commander.program.command("init").description("Initialize project with default configuration").action($LT_CMD_InitConfig);
|
|
1776
|
-
commander.program.command("init-tag").description("Initialize a new lang-tag function file").option(
|
|
2103
|
+
commander.program.command("init-tag").description("Initialize a new lang-tag function file").option(
|
|
2104
|
+
"-n, --name <name>",
|
|
2105
|
+
"Name of the tag function (default: from config)"
|
|
2106
|
+
).option(
|
|
2107
|
+
"-l, --library",
|
|
2108
|
+
"Generate library-style tag (default: from config)"
|
|
2109
|
+
).option(
|
|
2110
|
+
"-r, --react",
|
|
2111
|
+
"Include React-specific optimizations (default: auto-detect)"
|
|
2112
|
+
).option(
|
|
2113
|
+
"-t, --typescript",
|
|
2114
|
+
"Force TypeScript output (default: auto-detect)"
|
|
2115
|
+
).option(
|
|
2116
|
+
"-o, --output <path>",
|
|
2117
|
+
"Output file path (default: auto-generated)"
|
|
2118
|
+
).action(async (options) => {
|
|
1777
2119
|
await $LT_CMD_InitTagFile(options);
|
|
1778
2120
|
});
|
|
1779
2121
|
return commander.program;
|