@lang-tag/cli 0.18.1 → 0.20.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/algorithms/index.cjs +3 -6
- package/algorithms/index.js +3 -6
- package/index.cjs +402 -39
- package/index.js +403 -40
- package/package.json +3 -2
- package/templates/tag/base-app.mustache +4 -4
- package/templates/tag/base-library.mustache +5 -5
- package/templates/tag/placeholder.mustache +15 -8
- package/type.d.ts +36 -0
package/index.cjs
CHANGED
|
@@ -8,12 +8,13 @@ const globby = require("globby");
|
|
|
8
8
|
const path = require("path");
|
|
9
9
|
const JSON5 = require("json5");
|
|
10
10
|
const promises = require("fs/promises");
|
|
11
|
-
const path$1 = require("pathe");
|
|
12
11
|
const url = require("url");
|
|
12
|
+
const path$1 = require("pathe");
|
|
13
13
|
require("case");
|
|
14
14
|
const namespaceCollector = require("./chunks/namespace-collector.cjs");
|
|
15
15
|
const micromatch = require("micromatch");
|
|
16
16
|
const acorn = require("acorn");
|
|
17
|
+
const tsMorph = require("ts-morph");
|
|
17
18
|
const mustache = require("mustache");
|
|
18
19
|
const checkbox = require("@inquirer/checkbox");
|
|
19
20
|
const confirm = require("@inquirer/confirm");
|
|
@@ -102,22 +103,83 @@ class $LT_TagProcessor {
|
|
|
102
103
|
const matches = [];
|
|
103
104
|
let currentIndex = 0;
|
|
104
105
|
const skipRanges = this.buildSkipRanges(fileContent);
|
|
105
|
-
const
|
|
106
|
-
`${optionalVariableAssignment}${tagName}
|
|
106
|
+
const tagNamePattern = new RegExp(
|
|
107
|
+
`${optionalVariableAssignment}${tagName}`,
|
|
107
108
|
"g"
|
|
108
109
|
);
|
|
109
110
|
while (true) {
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
if (!
|
|
113
|
-
const
|
|
114
|
-
const variableName =
|
|
111
|
+
tagNamePattern.lastIndex = currentIndex;
|
|
112
|
+
const tagNameMatch = tagNamePattern.exec(fileContent);
|
|
113
|
+
if (!tagNameMatch) break;
|
|
114
|
+
const tagNameStartIndex = tagNameMatch.index;
|
|
115
|
+
const variableName = tagNameMatch[1] || void 0;
|
|
116
|
+
if (this.isInSkipRange(tagNameStartIndex, skipRanges)) {
|
|
117
|
+
currentIndex = tagNameStartIndex + 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
let i = tagNameStartIndex + tagNameMatch[0].length;
|
|
121
|
+
let genericType = void 0;
|
|
122
|
+
let matchStartIndex = tagNameStartIndex;
|
|
123
|
+
while (i < fileContent.length && /\s/.test(fileContent[i])) {
|
|
124
|
+
i++;
|
|
125
|
+
}
|
|
126
|
+
if (i < fileContent.length && fileContent[i] === "<") {
|
|
127
|
+
const genericStart = i;
|
|
128
|
+
let angleCount = 1;
|
|
129
|
+
i++;
|
|
130
|
+
while (i < fileContent.length && angleCount > 0) {
|
|
131
|
+
const char = fileContent[i];
|
|
132
|
+
if (char === "<") angleCount++;
|
|
133
|
+
else if (char === ">") angleCount--;
|
|
134
|
+
else if ((char === '"' || char === "'" || char === "`") && !this.isInSkipRange(i, skipRanges)) {
|
|
135
|
+
i++;
|
|
136
|
+
while (i < fileContent.length) {
|
|
137
|
+
if (fileContent[i] === "\\") {
|
|
138
|
+
i += 2;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (fileContent[i] === char) {
|
|
142
|
+
i++;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
i++;
|
|
146
|
+
}
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
i++;
|
|
150
|
+
}
|
|
151
|
+
if (angleCount === 0) {
|
|
152
|
+
genericType = fileContent.substring(genericStart + 1, i - 1).trim();
|
|
153
|
+
matchStartIndex = tagNameStartIndex;
|
|
154
|
+
} else {
|
|
155
|
+
currentIndex = tagNameStartIndex + 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
matchStartIndex = tagNameStartIndex;
|
|
160
|
+
}
|
|
161
|
+
while (i < fileContent.length && /\s/.test(fileContent[i])) {
|
|
162
|
+
i++;
|
|
163
|
+
}
|
|
164
|
+
if (i >= fileContent.length || fileContent[i] !== "(" || i + 1 >= fileContent.length) {
|
|
165
|
+
currentIndex = tagNameStartIndex + 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
i++;
|
|
169
|
+
while (i < fileContent.length && /\s/.test(fileContent[i])) {
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
if (i >= fileContent.length || fileContent[i] !== "{") {
|
|
173
|
+
currentIndex = tagNameStartIndex + 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const braceStartIndex = i;
|
|
115
177
|
if (this.isInSkipRange(matchStartIndex, skipRanges)) {
|
|
116
178
|
currentIndex = matchStartIndex + 1;
|
|
117
179
|
continue;
|
|
118
180
|
}
|
|
119
181
|
let braceCount = 1;
|
|
120
|
-
|
|
182
|
+
i++;
|
|
121
183
|
while (i < fileContent.length && braceCount > 0) {
|
|
122
184
|
if (fileContent[i] === "{") braceCount++;
|
|
123
185
|
if (fileContent[i] === "}") braceCount--;
|
|
@@ -127,10 +189,7 @@ class $LT_TagProcessor {
|
|
|
127
189
|
currentIndex = matchStartIndex + 1;
|
|
128
190
|
continue;
|
|
129
191
|
}
|
|
130
|
-
let parameter1Text = fileContent.substring(
|
|
131
|
-
matchStartIndex + startMatch[0].length - 1,
|
|
132
|
-
i
|
|
133
|
-
);
|
|
192
|
+
let parameter1Text = fileContent.substring(braceStartIndex, i);
|
|
134
193
|
let parameter2Text;
|
|
135
194
|
while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
|
|
136
195
|
i++;
|
|
@@ -225,6 +284,7 @@ class $LT_TagProcessor {
|
|
|
225
284
|
matches.push({
|
|
226
285
|
fullMatch,
|
|
227
286
|
variableName,
|
|
287
|
+
genericType,
|
|
228
288
|
parameter1Text,
|
|
229
289
|
parameter2Text,
|
|
230
290
|
parameterTranslations,
|
|
@@ -278,7 +338,8 @@ class $LT_TagProcessor {
|
|
|
278
338
|
}
|
|
279
339
|
const arg1 = this.config.translationArgPosition === 1 ? newTranslationsString : newConfigString;
|
|
280
340
|
const arg2 = this.config.translationArgPosition === 1 ? newConfigString : newTranslationsString;
|
|
281
|
-
|
|
341
|
+
const genericTypePart = tag.genericType ? `<${tag.genericType}>` : "";
|
|
342
|
+
let tagFunction = `${this.config.tagName}${genericTypePart}(${arg1}`;
|
|
282
343
|
if (arg2) tagFunction += `, ${arg2}`;
|
|
283
344
|
tagFunction += ")";
|
|
284
345
|
if (tag.variableName)
|
|
@@ -504,7 +565,9 @@ async function $LT_CollectCandidateFilesWithTags(props) {
|
|
|
504
565
|
langTagConfig: config
|
|
505
566
|
});
|
|
506
567
|
}
|
|
507
|
-
|
|
568
|
+
if (!props.skipEmptyNamespaceCheck) {
|
|
569
|
+
tags = $LT_FilterEmptyNamespaceTags(tags, logger);
|
|
570
|
+
}
|
|
508
571
|
const relativeFilePath = path.relative(cwd, filePath);
|
|
509
572
|
candidates.push({ relativeFilePath, tags });
|
|
510
573
|
}
|
|
@@ -845,9 +908,30 @@ async function $LT_WriteToCollections({
|
|
|
845
908
|
}
|
|
846
909
|
await config.collect.collector.postWrite(changedCollections);
|
|
847
910
|
}
|
|
911
|
+
function deepFreezeObject(obj) {
|
|
912
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
913
|
+
for (const name of propNames) {
|
|
914
|
+
const value = obj[name];
|
|
915
|
+
if (value && typeof value === "object") {
|
|
916
|
+
deepFreezeObject(value);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return Object.freeze(obj);
|
|
920
|
+
}
|
|
921
|
+
function formatFileUrlForDisplay(filePath) {
|
|
922
|
+
return url.pathToFileURL(filePath).href.replace(/\[/g, "%5B").replace(/\]/g, "%5D").replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
923
|
+
}
|
|
924
|
+
function formatExecutionTime(milliseconds) {
|
|
925
|
+
if (milliseconds >= 1e3) {
|
|
926
|
+
const seconds = milliseconds / 1e3;
|
|
927
|
+
return `${seconds.toFixed(1)}s`;
|
|
928
|
+
}
|
|
929
|
+
return `${Math.round(milliseconds)}ms`;
|
|
930
|
+
}
|
|
848
931
|
const LANG_TAG_DEFAULT_CONFIG = {
|
|
849
932
|
tagName: "lang",
|
|
850
933
|
isLibrary: false,
|
|
934
|
+
enforceLibraryTagPrefix: true,
|
|
851
935
|
includes: ["src/**/*.{js,ts,jsx,tsx}"],
|
|
852
936
|
excludes: ["node_modules", "dist", "build"],
|
|
853
937
|
localesDirectory: "locales",
|
|
@@ -908,6 +992,7 @@ This will enable import of language tags from external packages.
|
|
|
908
992
|
}
|
|
909
993
|
},
|
|
910
994
|
translationArgPosition: 1,
|
|
995
|
+
hideDistDir: "dist",
|
|
911
996
|
onConfigGeneration: async (event) => {
|
|
912
997
|
event.logger.info(
|
|
913
998
|
"Config generation event is not configured. Add onConfigGeneration handler to customize config generation."
|
|
@@ -946,6 +1031,9 @@ async function $LT_ReadConfig(projectPath) {
|
|
|
946
1031
|
if (!config.collect.collector) {
|
|
947
1032
|
throw new Error("Collector not found! (config.collect.collector)");
|
|
948
1033
|
}
|
|
1034
|
+
if (config.isLibrary && (config.enforceLibraryTagPrefix ?? true) && config.tagName && !config.tagName.startsWith("_")) {
|
|
1035
|
+
config.tagName = `_${config.tagName}`;
|
|
1036
|
+
}
|
|
949
1037
|
return config;
|
|
950
1038
|
} catch (error) {
|
|
951
1039
|
throw error;
|
|
@@ -1290,9 +1378,9 @@ async function logTagConflictInfo(tagInfo, prefix, conflictPath, translationArgP
|
|
|
1290
1378
|
console.error("Failed to colorize config:", error);
|
|
1291
1379
|
}
|
|
1292
1380
|
}
|
|
1293
|
-
const
|
|
1381
|
+
const fileUrl = formatFileUrlForDisplay(filePath);
|
|
1294
1382
|
console.log(
|
|
1295
|
-
`${ANSI.gray}${prefix}${ANSI.reset} ${ANSI.cyan}
|
|
1383
|
+
`${ANSI.gray}${prefix}${ANSI.reset} ${ANSI.cyan}${fileUrl}${ANSI.reset}${ANSI.gray}:${lineNum}${ANSI.reset}`
|
|
1296
1384
|
);
|
|
1297
1385
|
printLines(colorizedWhole.split("\n"), startLine, errorLines, condense);
|
|
1298
1386
|
} catch (error) {
|
|
@@ -1422,9 +1510,14 @@ async function $LT_GetCommandEssentials() {
|
|
|
1422
1510
|
};
|
|
1423
1511
|
}
|
|
1424
1512
|
async function $LT_CMD_Collect(options) {
|
|
1513
|
+
const startTime = Date.now();
|
|
1425
1514
|
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1426
1515
|
logger.info("Collecting translations from source files...");
|
|
1427
|
-
const files = await $LT_CollectCandidateFilesWithTags({
|
|
1516
|
+
const files = await $LT_CollectCandidateFilesWithTags({
|
|
1517
|
+
config,
|
|
1518
|
+
logger,
|
|
1519
|
+
skipEmptyNamespaceCheck: config.isLibrary
|
|
1520
|
+
});
|
|
1428
1521
|
if (config.debug) {
|
|
1429
1522
|
for (let file of files) {
|
|
1430
1523
|
logger.debug("Found {count} translations tags inside: {file}", {
|
|
@@ -1433,8 +1526,19 @@ async function $LT_CMD_Collect(options) {
|
|
|
1433
1526
|
});
|
|
1434
1527
|
}
|
|
1435
1528
|
}
|
|
1529
|
+
const totalTags = files.reduce((sum, file) => sum + file.tags.length, 0);
|
|
1436
1530
|
if (config.isLibrary) {
|
|
1531
|
+
if (totalTags === 0 && (config.enforceLibraryTagPrefix ?? true) && config.tagName) {
|
|
1532
|
+
const baseTagName = config.tagName.startsWith("_") ? config.tagName.substring(1) : config.tagName;
|
|
1533
|
+
console.log("");
|
|
1534
|
+
logger.warn(
|
|
1535
|
+
'⚠️ No translation tags found in your library code.\n This might be because enforceLibraryTagPrefix is enabled.\n Remember: your tag function must be named {prefixedBaseTagName} (with "_" prefix), not {baseTagName}.\n Example: export function {prefixedBaseTagName}(...) instead of export function {baseTagName}(...)\n The prefix prevents the tag from appearing in TypeScript autocomplete after compilation.',
|
|
1536
|
+
{ prefixedBaseTagName: `_${baseTagName}`, baseTagName }
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1437
1539
|
await $LT_WriteAsExportFile({ config, logger, files });
|
|
1540
|
+
const executionTime = formatExecutionTime(Date.now() - startTime);
|
|
1541
|
+
logger.debug("Collection completed ({time})", { time: executionTime });
|
|
1438
1542
|
return;
|
|
1439
1543
|
}
|
|
1440
1544
|
try {
|
|
@@ -1443,10 +1547,6 @@ async function $LT_CMD_Collect(options) {
|
|
|
1443
1547
|
files,
|
|
1444
1548
|
config
|
|
1445
1549
|
});
|
|
1446
|
-
const totalTags = files.reduce(
|
|
1447
|
-
(sum, file) => sum + file.tags.length,
|
|
1448
|
-
0
|
|
1449
|
-
);
|
|
1450
1550
|
logger.debug("Found {totalTags} translation tags", { totalTags });
|
|
1451
1551
|
await $LT_WriteToCollections({
|
|
1452
1552
|
config,
|
|
@@ -1454,15 +1554,252 @@ async function $LT_CMD_Collect(options) {
|
|
|
1454
1554
|
logger,
|
|
1455
1555
|
clean: options?.clean
|
|
1456
1556
|
});
|
|
1557
|
+
const executionTime = formatExecutionTime(Date.now() - startTime);
|
|
1558
|
+
logger.debug("Collection completed ({time})", { time: executionTime });
|
|
1457
1559
|
} catch (e) {
|
|
1458
1560
|
const prefix = "LangTagConflictResolution:";
|
|
1459
1561
|
if (e.message.startsWith(prefix)) {
|
|
1460
1562
|
logger.error(e.message.substring(prefix.length));
|
|
1563
|
+
const executionTime = formatExecutionTime(Date.now() - startTime);
|
|
1564
|
+
logger.debug("Collection completed ({time})", {
|
|
1565
|
+
time: executionTime
|
|
1566
|
+
});
|
|
1461
1567
|
return;
|
|
1462
1568
|
}
|
|
1463
1569
|
throw e;
|
|
1464
1570
|
}
|
|
1465
1571
|
}
|
|
1572
|
+
function $LT_HideExportsInDtsFile(dtsFilePath, variableNames) {
|
|
1573
|
+
const originalContent = fs.readFileSync(dtsFilePath, "utf-8");
|
|
1574
|
+
const project = new tsMorph.Project({
|
|
1575
|
+
skipAddingFilesFromTsConfig: true,
|
|
1576
|
+
skipFileDependencyResolution: true,
|
|
1577
|
+
skipLoadingLibFiles: true
|
|
1578
|
+
});
|
|
1579
|
+
const sourceFile = project.addSourceFileAtPath(dtsFilePath);
|
|
1580
|
+
let hasChanges = false;
|
|
1581
|
+
const exportsToHide = [];
|
|
1582
|
+
const processedStatements = /* @__PURE__ */ new Set();
|
|
1583
|
+
for (const declaration of sourceFile.getVariableDeclarations()) {
|
|
1584
|
+
const name = declaration.getName();
|
|
1585
|
+
if (variableNames.has(name)) {
|
|
1586
|
+
const parent = declaration.getParent();
|
|
1587
|
+
if (parent && parent.getKindName() === "VariableDeclarationList") {
|
|
1588
|
+
const varList = parent;
|
|
1589
|
+
const grandParent = varList.getParent();
|
|
1590
|
+
if (grandParent && grandParent.getKindName() === "VariableStatement") {
|
|
1591
|
+
const varStatement = grandParent;
|
|
1592
|
+
if (varStatement.hasExportKeyword()) {
|
|
1593
|
+
if (!processedStatements.has(varStatement)) {
|
|
1594
|
+
processedStatements.add(varStatement);
|
|
1595
|
+
exportsToHide.push(name);
|
|
1596
|
+
varStatement.toggleModifier("export", false);
|
|
1597
|
+
hasChanges = true;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
if (!hasChanges) {
|
|
1605
|
+
return {
|
|
1606
|
+
hiddenCount: 0,
|
|
1607
|
+
modifiedContent: originalContent,
|
|
1608
|
+
originalContent
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
const modifiedContent = sourceFile.getFullText();
|
|
1612
|
+
return {
|
|
1613
|
+
hiddenCount: exportsToHide.length,
|
|
1614
|
+
modifiedContent,
|
|
1615
|
+
originalContent
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
function extractPrefixesFromIncludes(includes) {
|
|
1619
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
1620
|
+
for (const pattern of includes) {
|
|
1621
|
+
const groupMatch = pattern.match(/^\(([^)]+)\)\/\*\*/);
|
|
1622
|
+
if (groupMatch) {
|
|
1623
|
+
const options = groupMatch[1].split("|").map((s) => s.trim());
|
|
1624
|
+
options.forEach((opt) => prefixes.add(opt));
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const simpleMatch = pattern.match(/^([^/]+)\/\*\*/);
|
|
1628
|
+
if (simpleMatch) {
|
|
1629
|
+
prefixes.add(simpleMatch[1]);
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
const singleMatch = pattern.match(/^([^/]+)\//);
|
|
1633
|
+
if (singleMatch) {
|
|
1634
|
+
prefixes.add(singleMatch[1]);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return Array.from(prefixes);
|
|
1638
|
+
}
|
|
1639
|
+
function findMatchingDtsFile(sourceFilePath, sourceRelativePath, dtsFileMap, config) {
|
|
1640
|
+
const sourceBaseName = path.basename(
|
|
1641
|
+
sourceFilePath,
|
|
1642
|
+
path.extname(sourceFilePath)
|
|
1643
|
+
);
|
|
1644
|
+
const relativePathKey = sourceRelativePath.replace(
|
|
1645
|
+
/\.(ts|tsx|js|jsx)$/,
|
|
1646
|
+
""
|
|
1647
|
+
);
|
|
1648
|
+
let dtsFilePath = dtsFileMap.get(relativePathKey);
|
|
1649
|
+
if (dtsFilePath) {
|
|
1650
|
+
return { dtsFilePath, strategy: "relative-path" };
|
|
1651
|
+
}
|
|
1652
|
+
if (config?.includes) {
|
|
1653
|
+
const prefixes = extractPrefixesFromIncludes(config.includes);
|
|
1654
|
+
for (const prefix of prefixes) {
|
|
1655
|
+
const prefixPattern = new RegExp(
|
|
1656
|
+
`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/`
|
|
1657
|
+
);
|
|
1658
|
+
if (prefixPattern.test(sourceRelativePath)) {
|
|
1659
|
+
const strippedPath = sourceRelativePath.replace(
|
|
1660
|
+
prefixPattern,
|
|
1661
|
+
""
|
|
1662
|
+
);
|
|
1663
|
+
const strippedPathKey = strippedPath.replace(
|
|
1664
|
+
/\.(ts|tsx|js|jsx)$/,
|
|
1665
|
+
""
|
|
1666
|
+
);
|
|
1667
|
+
dtsFilePath = dtsFileMap.get(strippedPathKey);
|
|
1668
|
+
if (dtsFilePath) {
|
|
1669
|
+
return {
|
|
1670
|
+
dtsFilePath,
|
|
1671
|
+
strategy: "includes-prefix-stripped"
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
dtsFilePath = dtsFileMap.get(sourceBaseName);
|
|
1678
|
+
if (dtsFilePath) {
|
|
1679
|
+
return { dtsFilePath, strategy: "base-name" };
|
|
1680
|
+
}
|
|
1681
|
+
return { dtsFilePath: null, strategy: null };
|
|
1682
|
+
}
|
|
1683
|
+
async function $LT_MatchSourceToDtsFiles(files, distPath, cwd, config) {
|
|
1684
|
+
const sourceFileToVariables = /* @__PURE__ */ new Map();
|
|
1685
|
+
for (const file of files) {
|
|
1686
|
+
const sourceFilePath = path.resolve(cwd, file.relativeFilePath);
|
|
1687
|
+
const variableNames = /* @__PURE__ */ new Set();
|
|
1688
|
+
for (const tag of file.tags) {
|
|
1689
|
+
if (tag.variableName) {
|
|
1690
|
+
variableNames.add(tag.variableName);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
if (variableNames.size > 0) {
|
|
1694
|
+
sourceFileToVariables.set(sourceFilePath, variableNames);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (sourceFileToVariables.size === 0) {
|
|
1698
|
+
return [];
|
|
1699
|
+
}
|
|
1700
|
+
const dtsFiles = await globby.globby("**/*.d.ts", {
|
|
1701
|
+
cwd: distPath,
|
|
1702
|
+
absolute: true
|
|
1703
|
+
});
|
|
1704
|
+
if (dtsFiles.length === 0) {
|
|
1705
|
+
return [];
|
|
1706
|
+
}
|
|
1707
|
+
const dtsFileMap = /* @__PURE__ */ new Map();
|
|
1708
|
+
for (const dtsFile of dtsFiles) {
|
|
1709
|
+
const relativeDtsPath = path.relative(distPath, dtsFile);
|
|
1710
|
+
const baseName = path.basename(dtsFile, ".d.ts");
|
|
1711
|
+
const relativePathWithoutExt = relativeDtsPath.replace(/\.d\.ts$/, "");
|
|
1712
|
+
dtsFileMap.set(relativePathWithoutExt, dtsFile);
|
|
1713
|
+
dtsFileMap.set(baseName, dtsFile);
|
|
1714
|
+
}
|
|
1715
|
+
const matches = [];
|
|
1716
|
+
const processedDtsFiles = /* @__PURE__ */ new Set();
|
|
1717
|
+
for (const [sourceFilePath, variableNames] of sourceFileToVariables) {
|
|
1718
|
+
const sourceRelativePath = path.relative(cwd, sourceFilePath);
|
|
1719
|
+
const match = findMatchingDtsFile(
|
|
1720
|
+
sourceFilePath,
|
|
1721
|
+
sourceRelativePath,
|
|
1722
|
+
dtsFileMap,
|
|
1723
|
+
config
|
|
1724
|
+
);
|
|
1725
|
+
if (!match.dtsFilePath) {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
if (processedDtsFiles.has(match.dtsFilePath)) {
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
processedDtsFiles.add(match.dtsFilePath);
|
|
1732
|
+
matches.push({
|
|
1733
|
+
sourceFilePath,
|
|
1734
|
+
sourceRelativePath,
|
|
1735
|
+
dtsFilePath: match.dtsFilePath,
|
|
1736
|
+
variableNames
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
return matches;
|
|
1740
|
+
}
|
|
1741
|
+
async function $LT_CMD_HideCompiledExports(options) {
|
|
1742
|
+
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1743
|
+
const distDir = options?.distDir || config.hideDistDir || "dist";
|
|
1744
|
+
const distPath = path.resolve(process$1.cwd(), distDir);
|
|
1745
|
+
if (!fs.existsSync(distPath)) {
|
|
1746
|
+
logger.warn("Dist directory does not exist: {distPath}", { distPath });
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
logger.info("Scanning source files for lang-tag variables...");
|
|
1750
|
+
const files = await $LT_CollectCandidateFilesWithTags({
|
|
1751
|
+
config,
|
|
1752
|
+
logger,
|
|
1753
|
+
skipEmptyNamespaceCheck: true
|
|
1754
|
+
});
|
|
1755
|
+
const matches = await $LT_MatchSourceToDtsFiles(
|
|
1756
|
+
files,
|
|
1757
|
+
distPath,
|
|
1758
|
+
process$1.cwd(),
|
|
1759
|
+
config
|
|
1760
|
+
);
|
|
1761
|
+
if (matches.length === 0) {
|
|
1762
|
+
logger.info(
|
|
1763
|
+
"No lang-tag variables found in source files or no matching .d.ts files found."
|
|
1764
|
+
);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
logger.info("Found {count} .d.ts files to process", {
|
|
1768
|
+
count: matches.length
|
|
1769
|
+
});
|
|
1770
|
+
let hiddenCount = 0;
|
|
1771
|
+
for (const match of matches) {
|
|
1772
|
+
try {
|
|
1773
|
+
const result = $LT_HideExportsInDtsFile(
|
|
1774
|
+
match.dtsFilePath,
|
|
1775
|
+
match.variableNames
|
|
1776
|
+
);
|
|
1777
|
+
if (!result.hiddenCount) {
|
|
1778
|
+
continue;
|
|
1779
|
+
}
|
|
1780
|
+
fs.writeFileSync(match.dtsFilePath, result.modifiedContent, "utf-8");
|
|
1781
|
+
hiddenCount += result.hiddenCount;
|
|
1782
|
+
const hiddenVariables = Array.from(match.variableNames).join(", ");
|
|
1783
|
+
logger.debug(
|
|
1784
|
+
"Hidden exports from {file}: {variables} (from {sourceFile})",
|
|
1785
|
+
{
|
|
1786
|
+
file: path.relative(process$1.cwd(), match.dtsFilePath),
|
|
1787
|
+
variables: hiddenVariables,
|
|
1788
|
+
sourceFile: match.sourceRelativePath
|
|
1789
|
+
}
|
|
1790
|
+
);
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
logger.warn("Error processing file {file}: {error}", {
|
|
1793
|
+
file: match.sourceRelativePath,
|
|
1794
|
+
error: error.message || String(error)
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
logger.success("Hidden {hiddenCount} exports from {fileCount} files.", {
|
|
1799
|
+
hiddenCount,
|
|
1800
|
+
fileCount: matches.length
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1466
1803
|
async function $LT_CollectExportFiles(logger) {
|
|
1467
1804
|
const nodeModulesPath = path$1.join(process$1.cwd(), "node_modules");
|
|
1468
1805
|
if (!fs.existsSync(nodeModulesPath)) {
|
|
@@ -1581,9 +1918,11 @@ async function generateImportFiles(config, logger, importManager) {
|
|
|
1581
1918
|
const content = renderTemplate$2(templateData);
|
|
1582
1919
|
await $LT_EnsureDirectoryExists(path.dirname(filePath));
|
|
1583
1920
|
await promises.writeFile(filePath, content, "utf-8");
|
|
1921
|
+
const fileUrl = formatFileUrlForDisplay(filePath);
|
|
1584
1922
|
logger.success('Created tag file: "{file}"', {
|
|
1585
1923
|
file: importedFile.pathRelativeToImportDir
|
|
1586
1924
|
});
|
|
1925
|
+
logger.debug(" └── link: {url}", { url: fileUrl });
|
|
1587
1926
|
}
|
|
1588
1927
|
}
|
|
1589
1928
|
class ImportManager {
|
|
@@ -1661,12 +2000,12 @@ async function $LT_ImportLibraries(config, logger) {
|
|
|
1661
2000
|
logger.warn("No tags were imported from any library files");
|
|
1662
2001
|
return;
|
|
1663
2002
|
}
|
|
2003
|
+
await $LT_EnsureDirectoryExists(config.import.dir);
|
|
1664
2004
|
await generateImportFiles(config, logger, importManager);
|
|
1665
2005
|
if (config.import.onImportFinish) config.import.onImportFinish();
|
|
1666
2006
|
}
|
|
1667
2007
|
async function $LT_ImportTranslations() {
|
|
1668
2008
|
const { config, logger } = await $LT_GetCommandEssentials();
|
|
1669
|
-
await $LT_EnsureDirectoryExists(config.import.dir);
|
|
1670
2009
|
logger.info("Importing translations from libraries...");
|
|
1671
2010
|
await $LT_ImportLibraries(config, logger);
|
|
1672
2011
|
logger.success("Successfully imported translations from libraries.");
|
|
@@ -2082,7 +2421,10 @@ async function detectInitTagOptions(options, config) {
|
|
|
2082
2421
|
const isTypeScript = options.typescript !== void 0 ? options.typescript : detectTypeScript(packageJson);
|
|
2083
2422
|
const isReact = options.react !== void 0 ? options.react : detectReact(packageJson);
|
|
2084
2423
|
const isLibrary = options.library !== void 0 ? options.library : config.isLibrary;
|
|
2085
|
-
|
|
2424
|
+
let tagName = options.name || config.tagName || "lang";
|
|
2425
|
+
if (isLibrary && (config.enforceLibraryTagPrefix ?? true) && !tagName.startsWith("_")) {
|
|
2426
|
+
tagName = `_${tagName}`;
|
|
2427
|
+
}
|
|
2086
2428
|
const fileExtension = isLibrary && isReact ? isTypeScript ? "tsx" : "jsx" : isTypeScript ? "ts" : "js";
|
|
2087
2429
|
return {
|
|
2088
2430
|
tagName,
|
|
@@ -2183,22 +2525,22 @@ async function $LT_CMD_InitTagFile(options = {}) {
|
|
|
2183
2525
|
"2. Create your translation objects and use the tag function"
|
|
2184
2526
|
);
|
|
2185
2527
|
logger.info('3. Run "lang-tag collect" to extract translations');
|
|
2528
|
+
if (renderOptions.isLibrary && renderOptions.tagName.startsWith("_") && (config.enforceLibraryTagPrefix ?? true)) {
|
|
2529
|
+
console.log("");
|
|
2530
|
+
logger.info(
|
|
2531
|
+
'📌 Important: Library tag prefix enforcement is enabled\n Your tag uses "_" prefix: {tagName} (instead of {baseTagName})\n This prevents the tag from appearing in TypeScript autocomplete after compilation\n Always use {tagName} (with prefix) in your library code\n This is a best practice for library internals - it keeps your API clean\n The prefix is automatically added by the enforceLibraryTagPrefix option\n To disable this behavior, set enforceLibraryTagPrefix: false in your config',
|
|
2532
|
+
{
|
|
2533
|
+
tagName: renderOptions.tagName,
|
|
2534
|
+
baseTagName: renderOptions.tagName.substring(1)
|
|
2535
|
+
}
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
2186
2538
|
} catch (error) {
|
|
2187
2539
|
logger.error("Failed to write file: {error}", {
|
|
2188
2540
|
error: error?.message
|
|
2189
2541
|
});
|
|
2190
2542
|
}
|
|
2191
2543
|
}
|
|
2192
|
-
function deepFreezeObject(obj) {
|
|
2193
|
-
const propNames = Object.getOwnPropertyNames(obj);
|
|
2194
|
-
for (const name of propNames) {
|
|
2195
|
-
const value = obj[name];
|
|
2196
|
-
if (value && typeof value === "object") {
|
|
2197
|
-
deepFreezeObject(value);
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
return Object.freeze(obj);
|
|
2201
|
-
}
|
|
2202
2544
|
async function checkAndRegenerateFileLangTags(config, logger, file, path$12) {
|
|
2203
2545
|
let libraryImportsDir = config.import.dir;
|
|
2204
2546
|
if (!libraryImportsDir.endsWith(path.sep)) libraryImportsDir += path.sep;
|
|
@@ -2232,13 +2574,23 @@ async function checkAndRegenerateFileLangTags(config, logger, file, path$12) {
|
|
|
2232
2574
|
event.isSaved = true;
|
|
2233
2575
|
event.savedConfig = updatedConfig;
|
|
2234
2576
|
logger.debug(
|
|
2235
|
-
'Called save for "{path}" with config "{config}" triggered by: ("{trigger}")',
|
|
2577
|
+
'Called save for "{path}"{varName} with config "{config}" triggered by: ("{trigger}")',
|
|
2236
2578
|
{
|
|
2237
2579
|
path: path$12,
|
|
2238
2580
|
config: JSON.stringify(updatedConfig),
|
|
2239
|
-
trigger: triggerName || "-"
|
|
2581
|
+
trigger: triggerName || "-",
|
|
2582
|
+
varName: tag.variableName ? `(${tag.variableName})` : ""
|
|
2240
2583
|
}
|
|
2241
2584
|
);
|
|
2585
|
+
},
|
|
2586
|
+
getCurrentConfig: () => {
|
|
2587
|
+
if (event.savedConfig !== void 0 && event.savedConfig !== null) {
|
|
2588
|
+
return { ...event.savedConfig };
|
|
2589
|
+
}
|
|
2590
|
+
if (event.config) {
|
|
2591
|
+
return { ...event.config };
|
|
2592
|
+
}
|
|
2593
|
+
return {};
|
|
2242
2594
|
}
|
|
2243
2595
|
};
|
|
2244
2596
|
await config.onConfigGeneration(event);
|
|
@@ -2253,10 +2605,10 @@ async function checkAndRegenerateFileLangTags(config, logger, file, path$12) {
|
|
|
2253
2605
|
if (replacements.length) {
|
|
2254
2606
|
const newContent = processor.replaceTags(fileContent, replacements);
|
|
2255
2607
|
await promises.writeFile(file, newContent, "utf-8");
|
|
2256
|
-
const
|
|
2608
|
+
const fileUrl = formatFileUrlForDisplay(file);
|
|
2257
2609
|
logger.info(
|
|
2258
|
-
'Lang tag configurations written for file "{path}" (
|
|
2259
|
-
{ path: path$12,
|
|
2610
|
+
'Lang tag configurations written for file "{path}" ({url}:{line})',
|
|
2611
|
+
{ path: path$12, url: fileUrl, line: lastUpdatedLine }
|
|
2260
2612
|
);
|
|
2261
2613
|
return true;
|
|
2262
2614
|
}
|
|
@@ -2269,6 +2621,7 @@ function isConfigSame(c1, c2) {
|
|
|
2269
2621
|
return false;
|
|
2270
2622
|
}
|
|
2271
2623
|
async function $LT_CMD_RegenerateTags() {
|
|
2624
|
+
const startTime = Date.now();
|
|
2272
2625
|
const { config, logger } = await $LT_GetCommandEssentials();
|
|
2273
2626
|
const files = await globby.globby(config.includes, {
|
|
2274
2627
|
cwd: process.cwd(),
|
|
@@ -2289,11 +2642,13 @@ async function $LT_CMD_RegenerateTags() {
|
|
|
2289
2642
|
dirty = true;
|
|
2290
2643
|
}
|
|
2291
2644
|
}
|
|
2645
|
+
const executionTime = formatExecutionTime(Date.now() - startTime);
|
|
2292
2646
|
if (!dirty) {
|
|
2293
2647
|
logger.info(
|
|
2294
2648
|
"No changes were made based on the current configuration and files"
|
|
2295
2649
|
);
|
|
2296
2650
|
}
|
|
2651
|
+
logger.debug("Regeneration completed ({time})", { time: executionTime });
|
|
2297
2652
|
}
|
|
2298
2653
|
function getBasePath(pattern) {
|
|
2299
2654
|
const globStartIndex = pattern.indexOf("*");
|
|
@@ -2402,6 +2757,14 @@ function createCli() {
|
|
|
2402
2757
|
).action(async (options) => {
|
|
2403
2758
|
await $LT_CMD_InitTagFile(options);
|
|
2404
2759
|
});
|
|
2760
|
+
commander.program.command("hide-compiled-exports").alias("hce").description(
|
|
2761
|
+
"Hide compiled .d.ts exports of lang-tag variables (remove export modifier, keep type)"
|
|
2762
|
+
).option(
|
|
2763
|
+
"-d, --dist-dir <dir>",
|
|
2764
|
+
'Dist directory to process (default: from config or "dist")'
|
|
2765
|
+
).action(async (options) => {
|
|
2766
|
+
await $LT_CMD_HideCompiledExports({ distDir: options.distDir });
|
|
2767
|
+
});
|
|
2405
2768
|
return commander.program;
|
|
2406
2769
|
}
|
|
2407
2770
|
createCli().parse();
|