@lang-tag/cli 0.13.1 → 0.15.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/index.cjs CHANGED
@@ -9,6 +9,7 @@ const path = require("path");
9
9
  const globby = require("globby");
10
10
  const path$1 = require("pathe");
11
11
  const url = require("url");
12
+ const namespaceCollector = require("./namespace-collector-DRnZvkDR.cjs");
12
13
  const process$1 = require("node:process");
13
14
  const acorn = require("acorn");
14
15
  const micromatch = require("micromatch");
@@ -80,31 +81,33 @@ class $LT_TagProcessor {
80
81
  while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
81
82
  i++;
82
83
  }
83
- if (i >= fileContent.length || fileContent[i] !== "{") {
84
- currentIndex = matchStartIndex + 1;
85
- continue;
86
- }
87
- braceCount = 1;
88
- const secondParamStart = i;
89
- i++;
90
- while (i < fileContent.length && braceCount > 0) {
91
- if (fileContent[i] === "{") braceCount++;
92
- if (fileContent[i] === "}") braceCount--;
93
- i++;
94
- }
95
- if (braceCount !== 0) {
96
- currentIndex = matchStartIndex + 1;
97
- continue;
98
- }
99
- parameter2Text = fileContent.substring(secondParamStart, i);
100
- while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
101
- i++;
102
- }
103
- if (i < fileContent.length && fileContent[i] === ",") {
84
+ if (i >= fileContent.length || fileContent[i] === ")") ;
85
+ else if (fileContent[i] === "{") {
86
+ braceCount = 1;
87
+ const secondParamStart = i;
104
88
  i++;
89
+ while (i < fileContent.length && braceCount > 0) {
90
+ if (fileContent[i] === "{") braceCount++;
91
+ if (fileContent[i] === "}") braceCount--;
92
+ i++;
93
+ }
94
+ if (braceCount !== 0) {
95
+ currentIndex = matchStartIndex + 1;
96
+ continue;
97
+ }
98
+ parameter2Text = fileContent.substring(secondParamStart, i);
105
99
  while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
106
100
  i++;
107
101
  }
102
+ if (i < fileContent.length && fileContent[i] === ",") {
103
+ i++;
104
+ while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
105
+ i++;
106
+ }
107
+ }
108
+ } else {
109
+ currentIndex = matchStartIndex + 1;
110
+ continue;
108
111
  }
109
112
  } else if (fileContent[i] !== ")") {
110
113
  currentIndex = matchStartIndex + 1;
@@ -499,10 +502,13 @@ const CONFIG_FILE_NAME = ".lang-tag.config.js";
499
502
  const EXPORTS_FILE_NAME = ".lang-tag.exports.json";
500
503
  const LANG_TAG_DEFAULT_CONFIG = {
501
504
  tagName: "lang",
505
+ isLibrary: false,
502
506
  includes: ["src/**/*.{js,ts,jsx,tsx}"],
503
507
  excludes: ["node_modules", "dist", "build"],
504
- outputDir: "locales/en",
508
+ localesDirectory: "locales",
509
+ baseLanguageCode: "en",
505
510
  collect: {
511
+ collector: new namespaceCollector.NamespaceCollector(),
506
512
  defaultNamespace: "common",
507
513
  ignoreConflictsWithMatchingValues: true,
508
514
  onCollectConfigFix: ({ config, langTagConfig }) => {
@@ -529,8 +535,6 @@ const LANG_TAG_DEFAULT_CONFIG = {
529
535
  actions.setExportName(`translations${exportIndex}`);
530
536
  }
531
537
  },
532
- isLibrary: false,
533
- language: "en",
534
538
  translationArgPosition: 1,
535
539
  onConfigGeneration: async (event) => {
536
540
  }
@@ -547,10 +551,10 @@ async function $LT_ReadConfig(projectPath) {
547
551
  }
548
552
  const userConfig = configModule.default || {};
549
553
  const tn = (userConfig.tagName || "").toLowerCase().replace(/[-_\s]/g, "");
550
- if (tn.includes("langtag")) {
551
- throw new Error('Custom tagName cannot include "langtag"! (It is not recommended for use with libraries)\n');
554
+ if (tn === "langtag" || tn === "lang-tag") {
555
+ throw new Error('Custom tagName cannot be "lang-tag" or "langtag"! (It is not recommended for use with libraries)\n');
552
556
  }
553
- return {
557
+ const config = {
554
558
  ...LANG_TAG_DEFAULT_CONFIG,
555
559
  ...userConfig,
556
560
  import: {
@@ -562,39 +566,14 @@ async function $LT_ReadConfig(projectPath) {
562
566
  ...userConfig.collect
563
567
  }
564
568
  };
569
+ if (!config.collect.collector) {
570
+ throw new Error("Collector not found! (config.collect.collector)");
571
+ }
572
+ return config;
565
573
  } catch (error) {
566
574
  throw error;
567
575
  }
568
576
  }
569
- async function $LT_EnsureDirectoryExists(filePath) {
570
- await promises.mkdir(filePath, { recursive: true });
571
- }
572
- async function $LT_RemoveDirectory(dirPath) {
573
- try {
574
- await promises.rm(dirPath, { recursive: true, force: true });
575
- } catch (error) {
576
- }
577
- }
578
- async function $LT_WriteJSON(filePath, data) {
579
- await promises.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
580
- }
581
- async function $LT_ReadJSON(filePath) {
582
- const content = await promises.readFile(filePath, "utf-8");
583
- return JSON.parse(content);
584
- }
585
- async function $LT_WriteFileWithDirs(filePath, content) {
586
- const dir = path.dirname(filePath);
587
- try {
588
- await promises.mkdir(dir, { recursive: true });
589
- } catch (error) {
590
- }
591
- await promises.writeFile(filePath, content, "utf-8");
592
- }
593
- async function $LT_ReadFileContent(relativeFilePath) {
594
- const cwd = process.cwd();
595
- const absolutePath = path.resolve(cwd, relativeFilePath);
596
- return await promises.readFile(absolutePath, "utf-8");
597
- }
598
577
  function parseObjectAST(code) {
599
578
  const nodes = [];
600
579
  try {
@@ -807,7 +786,7 @@ function printLines(lines, startLineNumber, errorLines = /* @__PURE__ */ new Set
807
786
  }
808
787
  async function getLangTagCodeSection(tagInfo) {
809
788
  const { tag, relativeFilePath } = tagInfo;
810
- const fileContent = await $LT_ReadFileContent(relativeFilePath);
789
+ const fileContent = await namespaceCollector.$LT_ReadFileContent(relativeFilePath);
811
790
  const fileLines = fileContent.split("\n");
812
791
  const startLine = tag.line;
813
792
  const endLine = tag.line + tag.fullMatch.split("\n").length - 1;
@@ -996,6 +975,8 @@ function $LT_CreateDefaultLogger(debugMode, translationArgPosition = 1) {
996
975
  async function $LT_GetCommandEssentials() {
997
976
  const config = await $LT_ReadConfig(process$1.cwd());
998
977
  const logger = $LT_CreateDefaultLogger(config.debug, config.translationArgPosition);
978
+ config.collect.collector.config = config;
979
+ config.collect.collector.logger = logger;
999
980
  return {
1000
981
  config,
1001
982
  logger
@@ -1058,36 +1039,26 @@ function deepMergeTranslations(target, source) {
1058
1039
  }
1059
1040
  return changed;
1060
1041
  }
1061
- async function $LT_WriteToNamespaces({ config, namespaces, logger, clean }) {
1062
- const changedNamespaces = [];
1063
- if (clean) {
1064
- logger.info("Cleaning output directory...");
1065
- await $LT_RemoveDirectory(config.outputDir);
1066
- }
1067
- await $LT_EnsureDirectoryExists(config.outputDir);
1068
- for (let namespace of Object.keys(namespaces)) {
1042
+ async function $LT_WriteToCollections({ config, collections, logger, clean }) {
1043
+ await config.collect.collector.preWrite(clean);
1044
+ const changedCollections = [];
1045
+ for (let namespace of Object.keys(collections)) {
1069
1046
  if (!namespace) {
1070
1047
  continue;
1071
1048
  }
1072
- const filePath = path$1.resolve(
1073
- process$1.cwd(),
1074
- config.outputDir,
1075
- namespace + ".json"
1076
- );
1049
+ const filePath = await config.collect.collector.resolveCollectionFilePath(namespace);
1077
1050
  let originalJSON = {};
1078
1051
  try {
1079
- originalJSON = await $LT_ReadJSON(filePath);
1052
+ originalJSON = await namespaceCollector.$LT_ReadJSON(filePath);
1080
1053
  } catch (e) {
1081
- if (!clean) {
1082
- logger.warn(`Original namespace file "{namespace}.json" not found. A new one will be created.`, { namespace });
1083
- }
1054
+ await config.collect.collector.onMissingCollection(namespace);
1084
1055
  }
1085
- if (deepMergeTranslations(originalJSON, namespaces[namespace])) {
1086
- changedNamespaces.push(namespace);
1087
- await $LT_WriteJSON(filePath, originalJSON);
1056
+ if (deepMergeTranslations(originalJSON, collections[namespace])) {
1057
+ changedCollections.push(namespace);
1058
+ await namespaceCollector.$LT_WriteJSON(filePath, originalJSON);
1088
1059
  }
1089
1060
  }
1090
- return changedNamespaces;
1061
+ await config.collect.collector.postWrite(changedCollections);
1091
1062
  }
1092
1063
  async function $LT_CollectCandidateFilesWithTags(props) {
1093
1064
  const { config, logger } = props;
@@ -1118,7 +1089,7 @@ async function $LT_CollectCandidateFilesWithTags(props) {
1118
1089
  return candidates;
1119
1090
  }
1120
1091
  async function $LT_WriteAsExportFile({ config, logger, files }) {
1121
- const packageJson = await $LT_ReadJSON(path.resolve(process.cwd(), "package.json"));
1092
+ const packageJson = await namespaceCollector.$LT_ReadJSON(path.resolve(process.cwd(), "package.json"));
1122
1093
  if (!packageJson) {
1123
1094
  throw new Error("package.json not found");
1124
1095
  }
@@ -1139,30 +1110,32 @@ async function $LT_WriteAsExportFile({ config, logger, files }) {
1139
1110
  };
1140
1111
  }
1141
1112
  const data = {
1142
- language: config.language,
1113
+ language: config.baseLanguageCode,
1143
1114
  packageName: packageJson.name || "",
1144
1115
  files: langTagFiles
1145
1116
  };
1146
- await $LT_WriteJSON(EXPORTS_FILE_NAME, data);
1117
+ await namespaceCollector.$LT_WriteJSON(EXPORTS_FILE_NAME, data);
1147
1118
  logger.success(`Written {file}`, { file: EXPORTS_FILE_NAME });
1148
1119
  }
1149
- async function $LT_GroupTagsToNamespaces({ logger, files, config }) {
1120
+ async function $LT_GroupTagsToCollections({ logger, files, config }) {
1150
1121
  let totalTags = 0;
1151
- const namespaces = {};
1152
- function getTranslations(namespace) {
1153
- const namespaceTranslations = namespaces[namespace] || {};
1154
- if (!(namespace in namespaces)) {
1155
- namespaces[namespace] = namespaceTranslations;
1122
+ const collections = {};
1123
+ function getTranslationsCollection(namespace) {
1124
+ const collectionName = config.collect.collector.aggregateCollection(namespace);
1125
+ const collection = collections[collectionName] || {};
1126
+ if (!(collectionName in collections)) {
1127
+ collections[collectionName] = collection;
1156
1128
  }
1157
- return namespaceTranslations;
1129
+ return collection;
1158
1130
  }
1159
1131
  const allConflicts = [];
1160
1132
  const existingValuesByNamespace = /* @__PURE__ */ new Map();
1161
1133
  for (const file of files) {
1162
1134
  totalTags += file.tags.length;
1163
- for (const tag of file.tags) {
1135
+ for (const _tag of file.tags) {
1136
+ const tag = config.collect.collector.transformTag(_tag);
1164
1137
  const tagConfig = tag.parameterConfig;
1165
- const namespaceTranslations = getTranslations(tagConfig.namespace);
1138
+ const collection = getTranslationsCollection(tagConfig.namespace);
1166
1139
  let existingValues = existingValuesByNamespace.get(tagConfig.namespace);
1167
1140
  if (!existingValues) {
1168
1141
  existingValues = /* @__PURE__ */ new Map();
@@ -1205,7 +1178,7 @@ async function $LT_GroupTagsToNamespaces({ logger, files, config }) {
1205
1178
  };
1206
1179
  const target = await ensureNestedObject(
1207
1180
  tagConfig.path,
1208
- namespaceTranslations,
1181
+ collection,
1209
1182
  valueTracker,
1210
1183
  addConflict
1211
1184
  );
@@ -1225,7 +1198,7 @@ async function $LT_GroupTagsToNamespaces({ logger, files, config }) {
1225
1198
  let shouldContinue = true;
1226
1199
  config.collect.onCollectFinish({
1227
1200
  totalTags,
1228
- namespaces,
1201
+ namespaces: collections,
1229
1202
  conflicts: allConflicts,
1230
1203
  logger,
1231
1204
  exit() {
@@ -1236,11 +1209,11 @@ async function $LT_GroupTagsToNamespaces({ logger, files, config }) {
1236
1209
  throw new Error(`LangTagConflictResolution:Processing stopped due to collect finish handler`);
1237
1210
  }
1238
1211
  }
1239
- return namespaces;
1212
+ return collections;
1240
1213
  }
1241
- async function ensureNestedObject(path2, root, valueTracker, addConflict) {
1242
- if (!path2 || !path2.trim()) return root;
1243
- let current = root;
1214
+ async function ensureNestedObject(path2, rootCollection, valueTracker, addConflict) {
1215
+ if (!path2 || !path2.trim()) return rootCollection;
1216
+ let current = rootCollection;
1244
1217
  let currentPath = "";
1245
1218
  for (const key of path2.split(".")) {
1246
1219
  currentPath = currentPath ? `${currentPath}.${key}` : key;
@@ -1340,19 +1313,10 @@ async function $LT_CMD_Collect(options) {
1340
1313
  return;
1341
1314
  }
1342
1315
  try {
1343
- const namespaces = await $LT_GroupTagsToNamespaces({ logger, files, config });
1316
+ const collections = await $LT_GroupTagsToCollections({ logger, files, config });
1344
1317
  const totalTags = files.reduce((sum, file) => sum + file.tags.length, 0);
1345
1318
  logger.debug("Found {totalTags} translation tags", { totalTags });
1346
- const changedNamespaces = await $LT_WriteToNamespaces({ config, namespaces, logger, clean: options?.clean });
1347
- if (!changedNamespaces?.length) {
1348
- logger.info("No changes were made based on the current configuration and files");
1349
- return;
1350
- }
1351
- const n = changedNamespaces.map((n2) => `"${n2}.json"`).join(", ");
1352
- logger.success("Updated namespaces {outputDir} ({namespaces})", {
1353
- outputDir: config.outputDir,
1354
- namespaces: n
1355
- });
1319
+ await $LT_WriteToCollections({ config, collections, logger, clean: options?.clean });
1356
1320
  } catch (e) {
1357
1321
  const prefix = "LangTagConflictResolution:";
1358
1322
  if (e.message.startsWith(prefix)) {
@@ -1418,15 +1382,8 @@ async function handleFile(config, logger, cwdRelativeFilePath, event) {
1418
1382
  const absoluteFilePath = path.join(cwd, cwdRelativeFilePath);
1419
1383
  await checkAndRegenerateFileLangTags(config, logger, absoluteFilePath, cwdRelativeFilePath);
1420
1384
  const files = await $LT_CollectCandidateFilesWithTags({ filesToScan: [cwdRelativeFilePath], config, logger });
1421
- const namespaces = await $LT_GroupTagsToNamespaces({ logger, files, config });
1422
- const changedNamespaces = await $LT_WriteToNamespaces({ config, namespaces, logger });
1423
- if (changedNamespaces.length > 0) {
1424
- const n = changedNamespaces.map((n2) => `"${n2}.json"`).join(", ");
1425
- logger.success("Updated namespaces {outputDir} ({namespaces})", {
1426
- outputDir: config.outputDir,
1427
- namespaces: n
1428
- });
1429
- }
1385
+ const namespaces = await $LT_GroupTagsToCollections({ logger, files, config });
1386
+ await $LT_WriteToCollections({ config, collections: namespaces, logger });
1430
1387
  }
1431
1388
  async function detectModuleSystem() {
1432
1389
  const packageJsonPath = path.join(process.cwd(), "package.json");
@@ -1466,7 +1423,13 @@ const generationAlgorithm = pathBasedConfigGenerator({
1466
1423
  // },
1467
1424
  // admin: {
1468
1425
  // '>': 'management', // rename "admin" to "management"
1469
- // users: false // ignore "users"
1426
+ // users: false // ignore "users",
1427
+ // ui: {
1428
+ // '>>': { // 'redirect' - ignore everything, jump to 'ui' namespace and prefix all paths with 'admin'
1429
+ // namespace: 'ui',
1430
+ // pathPrefix: 'admin'
1431
+ // }
1432
+ // }
1470
1433
  // }
1471
1434
  // }
1472
1435
  // }
@@ -1479,7 +1442,8 @@ const config = {
1479
1442
  isLibrary: false,
1480
1443
  includes: ['src/**/*.{js,ts,jsx,tsx}'],
1481
1444
  excludes: ['node_modules', 'dist', 'build', '**/*.test.ts'],
1482
- outputDir: 'public/locales/en',
1445
+ localesDirectory: 'public/locales',
1446
+ baseLanguageCode: 'en',
1483
1447
  onConfigGeneration: async event => {
1484
1448
  // We do not modify imported configurations
1485
1449
  if (event.isImportedLibrary) return;
@@ -1555,7 +1519,7 @@ async function $LT_ImportLibraries(config, logger) {
1555
1519
  const files = $LT_CollectNodeModulesExportFilePaths(logger);
1556
1520
  const generationFiles = {};
1557
1521
  for (const filePath of files) {
1558
- const exportData = await $LT_ReadJSON(filePath);
1522
+ const exportData = await namespaceCollector.$LT_ReadJSON(filePath);
1559
1523
  for (let langTagFilePath in exportData.files) {
1560
1524
  const fileGenerationData = {};
1561
1525
  const matches = exportData.files[langTagFilePath].matches;
@@ -1608,7 +1572,7 @@ async function $LT_ImportLibraries(config, logger) {
1608
1572
  const content = `${config.import.tagImportPath}
1609
1573
 
1610
1574
  ${exports2}`;
1611
- await $LT_EnsureDirectoryExists(path$1.dirname(filePath));
1575
+ await namespaceCollector.$LT_EnsureDirectoryExists(path$1.dirname(filePath));
1612
1576
  await promises.writeFile(filePath, content, "utf-8");
1613
1577
  logger.success('Imported node_modules file: "{fileName}"', { fileName });
1614
1578
  }
@@ -1616,7 +1580,7 @@ ${exports2}`;
1616
1580
  }
1617
1581
  async function $LT_ImportTranslations() {
1618
1582
  const { config, logger } = await $LT_GetCommandEssentials();
1619
- await $LT_EnsureDirectoryExists(config.import.dir);
1583
+ await namespaceCollector.$LT_EnsureDirectoryExists(config.import.dir);
1620
1584
  logger.info("Importing translations from libraries...");
1621
1585
  await $LT_ImportLibraries(config, logger);
1622
1586
  logger.success("Successfully imported translations from libraries.");
@@ -1717,7 +1681,7 @@ async function $LT_CMD_InitTagFile(options = {}) {
1717
1681
  return;
1718
1682
  }
1719
1683
  try {
1720
- await $LT_WriteFileWithDirs(outputPath, renderedContent);
1684
+ await namespaceCollector.$LT_WriteFileWithDirs(outputPath, renderedContent);
1721
1685
  logger.success("Lang-tag file created successfully: {outputPath}", { outputPath });
1722
1686
  logger.info("Next steps:");
1723
1687
  logger.info("1. Import the {tagName} function in your files:", { tagName: renderOptions.tagName });