@lang-tag/cli 0.15.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.
Files changed (30) hide show
  1. package/README.md +23 -14
  2. package/algorithms/case-utils.d.ts +12 -0
  3. package/algorithms/collector/dictionary-collector.d.ts +2 -2
  4. package/algorithms/collector/index.d.ts +3 -3
  5. package/algorithms/collector/namespace-collector.d.ts +2 -2
  6. package/algorithms/collector/type.d.ts +2 -2
  7. package/algorithms/config-generation/config-keeper.d.ts +1 -1
  8. package/algorithms/config-generation/index.d.ts +3 -3
  9. package/algorithms/config-generation/path-based-config-generator.d.ts +4 -3
  10. package/algorithms/config-generation/prepend-namespace-to-path.d.ts +1 -1
  11. package/algorithms/import/flexible-import-algorithm.d.ts +232 -0
  12. package/algorithms/import/index.d.ts +2 -1
  13. package/algorithms/import/simple-mapping-import-algorithm.d.ts +120 -0
  14. package/algorithms/index.cjs +418 -26
  15. package/algorithms/index.d.ts +6 -3
  16. package/algorithms/index.js +420 -28
  17. package/chunks/namespace-collector.cjs +75 -0
  18. package/chunks/namespace-collector.js +76 -0
  19. package/index.cjs +1156 -743
  20. package/index.js +1316 -903
  21. package/logger.d.ts +1 -1
  22. package/package.json +1 -1
  23. package/templates/config/init-config.mustache +1 -0
  24. package/templates/import/imported-tag.mustache +14 -0
  25. package/{config.d.ts → type.d.ts} +41 -32
  26. package/namespace-collector-DCruv_PK.js +0 -95
  27. package/namespace-collector-DRnZvkDR.cjs +0 -94
  28. /package/{template → templates/tag}/base-app.mustache +0 -0
  29. /package/{template → templates/tag}/base-library.mustache +0 -0
  30. /package/{template → templates/tag}/placeholder.mustache +0 -0
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const namespaceCollector = require("../namespace-collector-DRnZvkDR.cjs");
4
- const path = require("pathe");
5
- const process = require("node:process");
6
3
  const caseLib = require("case");
4
+ const process = require("node:process");
5
+ const promises = require("fs/promises");
6
+ const path = require("pathe");
7
+ const namespaceCollector = require("../chunks/namespace-collector.cjs");
8
+ const micromatch = require("micromatch");
7
9
  function _interopNamespaceDefault(e) {
8
10
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
9
11
  if (e) {
@@ -21,6 +23,16 @@ function _interopNamespaceDefault(e) {
21
23
  return Object.freeze(n);
22
24
  }
23
25
  const caseLib__namespace = /* @__PURE__ */ _interopNamespaceDefault(caseLib);
26
+ function applyCaseTransform(str, caseType) {
27
+ if (caseType === "no") {
28
+ return str;
29
+ }
30
+ const caseFunction = caseLib__namespace[caseType];
31
+ if (typeof caseFunction === "function") {
32
+ return caseFunction(str);
33
+ }
34
+ return str;
35
+ }
24
36
  class DictionaryCollector extends namespaceCollector.TranslationsCollector {
25
37
  constructor(options = {
26
38
  appendNamespaceToPath: false
@@ -54,12 +66,15 @@ class DictionaryCollector extends namespaceCollector.TranslationsCollector {
54
66
  }
55
67
  async preWrite(clean) {
56
68
  this.clean = clean;
57
- const baseDictionaryFile = path.join(this.config.localesDirectory, `${this.config.baseLanguageCode}.json`);
69
+ const baseDictionaryFile = path.join(
70
+ this.config.localesDirectory,
71
+ `${this.config.baseLanguageCode}.json`
72
+ );
58
73
  if (clean) {
59
74
  this.logger.info("Removing {file}", { file: baseDictionaryFile });
60
- await namespaceCollector.$LT_RemoveFile(baseDictionaryFile);
75
+ await removeFile(baseDictionaryFile);
61
76
  }
62
- await namespaceCollector.$LT_EnsureDirectoryExists(this.config.localesDirectory);
77
+ await ensureDirectoryExists(this.config.localesDirectory);
63
78
  }
64
79
  async resolveCollectionFilePath(baseLanguageCode) {
65
80
  return path.resolve(
@@ -70,16 +85,23 @@ class DictionaryCollector extends namespaceCollector.TranslationsCollector {
70
85
  }
71
86
  async onMissingCollection(baseLanguageCode) {
72
87
  if (!this.clean) {
73
- this.logger.warn(`Original dictionary file "{namespace}.json" not found. A new one will be created.`, { namespace: baseLanguageCode });
88
+ this.logger.warn(
89
+ `Original dictionary file "{namespace}.json" not found. A new one will be created.`,
90
+ { namespace: baseLanguageCode }
91
+ );
74
92
  }
75
93
  }
76
94
  async postWrite(changedCollections) {
77
95
  if (!changedCollections?.length) {
78
- this.logger.info("No changes were made based on the current configuration and files");
96
+ this.logger.info(
97
+ "No changes were made based on the current configuration and files"
98
+ );
79
99
  return;
80
100
  }
81
101
  if (changedCollections.length > 1) {
82
- throw new Error("Should not write more than 1 collection! Only 1 base language dictionary expected!");
102
+ throw new Error(
103
+ "Should not write more than 1 collection! Only 1 base language dictionary expected!"
104
+ );
83
105
  }
84
106
  const dict = path.resolve(
85
107
  this.config.localesDirectory,
@@ -88,6 +110,21 @@ class DictionaryCollector extends namespaceCollector.TranslationsCollector {
88
110
  this.logger.success("Updated dictionary {dict}", { dict });
89
111
  }
90
112
  }
113
+ async function ensureDirectoryExists(filePath) {
114
+ try {
115
+ await promises.mkdir(filePath, { recursive: true });
116
+ } catch (error) {
117
+ if (error.code !== "EEXIST") {
118
+ throw error;
119
+ }
120
+ }
121
+ }
122
+ async function removeFile(filePath) {
123
+ try {
124
+ await promises.rm(filePath, { force: true });
125
+ } catch (error) {
126
+ }
127
+ }
91
128
  const TRIGGER_NAME$2 = "path-based-config-generator";
92
129
  function pathBasedConfigGenerator(options = {}) {
93
130
  const {
@@ -134,15 +171,22 @@ function pathBasedConfigGenerator(options = {}) {
134
171
  if (hasPathRules) {
135
172
  pathSegments = applyPathRules(pathSegments, pathRules);
136
173
  } else {
137
- pathSegments = applyStructuredIgnore(pathSegments, ignoreStructured);
174
+ pathSegments = applyStructuredIgnore(
175
+ pathSegments,
176
+ ignoreStructured
177
+ );
138
178
  }
139
179
  if (ignoreIncludesRootDirectories && langTagConfig.includes && pathSegments.length > 0) {
140
- const extractedDirectories = extractRootDirectoriesFromIncludes(langTagConfig.includes);
180
+ const extractedDirectories = extractRootDirectoriesFromIncludes(
181
+ langTagConfig.includes
182
+ );
141
183
  if (extractedDirectories.includes(pathSegments[0])) {
142
184
  pathSegments = pathSegments.slice(1);
143
185
  }
144
186
  }
145
- pathSegments = pathSegments.filter((seg) => !ignoreDirectories.includes(seg));
187
+ pathSegments = pathSegments.filter(
188
+ (seg) => !ignoreDirectories.includes(seg)
189
+ );
146
190
  let namespace;
147
191
  let path$1;
148
192
  if (pathSegments.length >= 1) {
@@ -165,7 +209,9 @@ function pathBasedConfigGenerator(options = {}) {
165
209
  }
166
210
  if (path$1 && pathCase) {
167
211
  const pathParts = path$1.split(".");
168
- const transformedParts = pathParts.map((part) => applyCaseTransform(part, pathCase));
212
+ const transformedParts = pathParts.map(
213
+ (part) => applyCaseTransform(part, pathCase)
214
+ );
169
215
  path$1 = transformedParts.join(".");
170
216
  }
171
217
  const newConfig = event.config ? { ...event.config } : {};
@@ -324,7 +370,10 @@ function applyPathRules(segments, structure) {
324
370
  const remainingSegments = segments.slice(i + 1);
325
371
  const ruleWithoutRedirect = { ...rule };
326
372
  delete ruleWithoutRedirect[">>"];
327
- const processedRemaining = applyPathRules(remainingSegments, ruleWithoutRedirect);
373
+ const processedRemaining = applyPathRules(
374
+ remainingSegments,
375
+ ruleWithoutRedirect
376
+ );
328
377
  deepestRedirect = {
329
378
  rule: redirectRule,
330
379
  remainingSegments: processedRemaining,
@@ -358,13 +407,6 @@ function applyPathRules(segments, structure) {
358
407
  }
359
408
  return result;
360
409
  }
361
- function applyCaseTransform(str, caseType) {
362
- const caseFunction = caseLib__namespace[caseType];
363
- if (typeof caseFunction === "function") {
364
- return caseFunction(str);
365
- }
366
- return str;
367
- }
368
410
  function extractRootDirectoriesFromIncludes(includes) {
369
411
  const directories = /* @__PURE__ */ new Set();
370
412
  for (const pattern of includes) {
@@ -457,15 +499,365 @@ function prependNamespaceToPath(options = {}) {
457
499
  } else {
458
500
  newPath = actualNamespace;
459
501
  }
460
- event.save({
461
- ...currentConfig || {},
462
- path: newPath,
463
- namespace: void 0
464
- }, TRIGGER_NAME);
502
+ event.save(
503
+ {
504
+ ...currentConfig || {},
505
+ path: newPath,
506
+ namespace: void 0
507
+ },
508
+ TRIGGER_NAME
509
+ );
510
+ };
511
+ }
512
+ function flexibleImportAlgorithm(options = {}) {
513
+ const {
514
+ variableName = {},
515
+ filePath = {},
516
+ include,
517
+ exclude = {},
518
+ configRemap
519
+ } = options;
520
+ const { packages: includePackages, namespaces: includeNamespaces } = include || {};
521
+ const {
522
+ packages: excludePackages = [],
523
+ namespaces: excludeNamespaces = []
524
+ } = exclude;
525
+ return (event) => {
526
+ const { exports: exports2, importManager, logger } = event;
527
+ for (const { packageJSON, exportData } of exports2) {
528
+ const packageName = packageJSON.name || "unknown-package";
529
+ if (includePackages && !matchesAnyPattern(packageName, includePackages)) {
530
+ logger.debug(
531
+ `Skipping package not in include list: ${packageName}`
532
+ );
533
+ continue;
534
+ }
535
+ if (matchesAnyPattern(packageName, excludePackages)) {
536
+ logger.debug(`Skipping excluded package: ${packageName}`);
537
+ continue;
538
+ }
539
+ logger.debug(`Processing library: ${packageName}`);
540
+ for (const file of exportData.files) {
541
+ const originalFileName = file.relativeFilePath;
542
+ const targetFilePath = generateFilePath(
543
+ packageName,
544
+ originalFileName,
545
+ filePath
546
+ );
547
+ for (let i = 0; i < file.tags.length; i++) {
548
+ const tag = file.tags[i];
549
+ const tagNamespace = tag.config?.namespace;
550
+ if (includeNamespaces && tagNamespace && !matchesAnyPattern(tagNamespace, includeNamespaces)) {
551
+ logger.debug(
552
+ `Skipping namespace not in include list: ${tagNamespace}`
553
+ );
554
+ continue;
555
+ }
556
+ if (tagNamespace && matchesAnyPattern(tagNamespace, excludeNamespaces)) {
557
+ logger.debug(
558
+ `Skipping excluded namespace: ${tagNamespace}`
559
+ );
560
+ continue;
561
+ }
562
+ const finalVariableName = generateVariableName(
563
+ tag.variableName,
564
+ packageName,
565
+ originalFileName,
566
+ i,
567
+ variableName,
568
+ tag
569
+ );
570
+ if (finalVariableName === null) {
571
+ logger.debug(
572
+ `Skipping tag without variableName in ${path.join(packageName, originalFileName)}`
573
+ );
574
+ continue;
575
+ }
576
+ let finalConfig = tag.config;
577
+ if (configRemap) {
578
+ const remappedConfig = configRemap(tag.config, {
579
+ packageName,
580
+ fileName: originalFileName,
581
+ variableName: finalVariableName,
582
+ tagIndex: i
583
+ });
584
+ if (remappedConfig === null) {
585
+ logger.debug(
586
+ `Removing config due to configRemap returning null in ${path.join(packageName, originalFileName)}`
587
+ );
588
+ finalConfig = null;
589
+ } else {
590
+ finalConfig = remappedConfig;
591
+ }
592
+ }
593
+ importManager.importTag(targetFilePath, {
594
+ variableName: finalVariableName,
595
+ translations: tag.translations,
596
+ config: finalConfig
597
+ });
598
+ logger.debug(
599
+ `Imported: ${finalVariableName} -> ${targetFilePath}`
600
+ );
601
+ }
602
+ }
603
+ }
604
+ };
605
+ }
606
+ function sanitizeVariableName(name) {
607
+ let sanitized = name.replace(/[^a-zA-Z0-9_$]/g, "$");
608
+ if (/^[0-9]/.test(sanitized)) {
609
+ sanitized = "$" + sanitized;
610
+ }
611
+ if (sanitized === "") {
612
+ sanitized = "$";
613
+ }
614
+ return sanitized;
615
+ }
616
+ function applyCaseTransformToPath(filePath, caseType) {
617
+ if (typeof caseType === "string") {
618
+ const segments = filePath.split("/");
619
+ const fileName = segments[segments.length - 1];
620
+ const directorySegments = segments.slice(0, -1);
621
+ const transformedDirectories = directorySegments.map(
622
+ (dir) => applyCaseTransform(dir, caseType)
623
+ );
624
+ const transformedFileName = applyCaseTransformToFileName(
625
+ fileName,
626
+ caseType
627
+ );
628
+ if (transformedDirectories.length === 0) {
629
+ return transformedFileName;
630
+ }
631
+ return [...transformedDirectories, transformedFileName].join("/");
632
+ }
633
+ if (typeof caseType === "object") {
634
+ const { directories = "no", files = "no" } = caseType;
635
+ const segments = filePath.split("/");
636
+ const fileName = segments[segments.length - 1];
637
+ const directorySegments = segments.slice(0, -1);
638
+ const transformedDirectories = directorySegments.map(
639
+ (dir) => applyCaseTransform(dir, directories)
640
+ );
641
+ const transformedFileName = applyCaseTransformToFileName(
642
+ fileName,
643
+ files
644
+ );
645
+ if (transformedDirectories.length === 0) {
646
+ return transformedFileName;
647
+ }
648
+ return [...transformedDirectories, transformedFileName].join("/");
649
+ }
650
+ return filePath;
651
+ }
652
+ function normalizePackageName(packageName, scopedPackageHandling = "replace", context = "variableName") {
653
+ switch (scopedPackageHandling) {
654
+ case "remove-scope":
655
+ let result = packageName.replace(/^@[^/]+\//, "");
656
+ if (context === "variableName") {
657
+ result = result.replace(/[^a-zA-Z0-9_$]/g, "_");
658
+ }
659
+ return result;
660
+ case "replace":
661
+ default:
662
+ let normalized = packageName.replace(/@/g, "").replace(/\//g, context === "variableName" ? "_" : "-");
663
+ if (context === "variableName") {
664
+ normalized = normalized.replace(/[^a-zA-Z0-9_$]/g, "_");
665
+ }
666
+ return normalized;
667
+ }
668
+ }
669
+ function generateVariableName(originalVariableName, packageName, fileName, index, options, tag) {
670
+ const {
671
+ prefixWithPackageName = false,
672
+ scopedPackageHandling = "replace",
673
+ case: caseType = "no",
674
+ sanitizeVariableName: shouldSanitize = true,
675
+ handleMissingVariableName = "auto-generate",
676
+ customVariableName
677
+ } = options;
678
+ if (customVariableName) {
679
+ const customName = customVariableName({
680
+ packageName,
681
+ fileName,
682
+ originalVariableName,
683
+ tagIndex: index,
684
+ tag
685
+ });
686
+ if (customName !== null) {
687
+ originalVariableName = customName;
688
+ }
689
+ }
690
+ if (!originalVariableName) {
691
+ switch (handleMissingVariableName) {
692
+ case "skip":
693
+ return null;
694
+ case "auto-generate":
695
+ originalVariableName = `translations${index + 1}`;
696
+ break;
697
+ default:
698
+ if (typeof handleMissingVariableName === "function") {
699
+ originalVariableName = handleMissingVariableName(
700
+ {},
701
+ packageName,
702
+ fileName,
703
+ index
704
+ );
705
+ } else {
706
+ return null;
707
+ }
708
+ }
709
+ }
710
+ let finalName = originalVariableName;
711
+ if (prefixWithPackageName) {
712
+ const normalizedPackageName = normalizePackageName(
713
+ packageName,
714
+ scopedPackageHandling,
715
+ "variableName"
716
+ );
717
+ finalName = `${normalizedPackageName}_${originalVariableName}`;
718
+ }
719
+ const transformedName = applyCaseTransform(finalName, caseType);
720
+ return shouldSanitize ? sanitizeVariableName(transformedName) : transformedName;
721
+ }
722
+ function generateFilePath(packageName, originalFileName, options) {
723
+ const {
724
+ groupByPackage = false,
725
+ includePackageInPath = false,
726
+ scopedPackageHandling = "replace",
727
+ case: caseType = "no"
728
+ } = options;
729
+ if (groupByPackage) {
730
+ const normalizedPackageName = normalizePackageName(
731
+ packageName,
732
+ scopedPackageHandling,
733
+ "filePath"
734
+ );
735
+ const fileName = `${normalizedPackageName}.ts`;
736
+ return applyCaseTransformToFileName(
737
+ fileName,
738
+ typeof caseType === "string" ? caseType : caseType.files || "no"
739
+ );
740
+ } else if (includePackageInPath) {
741
+ const normalizedPackageName = normalizePackageName(
742
+ packageName,
743
+ scopedPackageHandling,
744
+ "filePath"
745
+ );
746
+ if (typeof caseType === "string") {
747
+ const transformedPackageName = applyCaseTransform(
748
+ normalizedPackageName,
749
+ caseType
750
+ );
751
+ const transformedFilePath = applyCaseTransformToPath(
752
+ originalFileName,
753
+ caseType
754
+ );
755
+ return path.join(transformedPackageName, transformedFilePath);
756
+ } else {
757
+ const transformedPackageName = applyCaseTransform(
758
+ normalizedPackageName,
759
+ caseType.directories || "no"
760
+ );
761
+ const transformedFilePath = applyCaseTransformToPath(
762
+ originalFileName,
763
+ caseType
764
+ );
765
+ return path.join(transformedPackageName, transformedFilePath);
766
+ }
767
+ } else {
768
+ return applyCaseTransformToPath(originalFileName, caseType);
769
+ }
770
+ }
771
+ function applyCaseTransformToFileName(fileName, caseType) {
772
+ if (caseType === "no") {
773
+ return fileName;
774
+ }
775
+ const lastDotIndex = fileName.lastIndexOf(".");
776
+ if (lastDotIndex === -1) {
777
+ return applyCaseTransform(fileName, caseType);
778
+ }
779
+ const nameWithoutExt = fileName.substring(0, lastDotIndex);
780
+ const extension = fileName.substring(lastDotIndex);
781
+ const transformedName = applyCaseTransform(nameWithoutExt, caseType);
782
+ return transformedName + extension;
783
+ }
784
+ function matchesAnyPattern(str, patterns) {
785
+ return micromatch.isMatch(str, patterns);
786
+ }
787
+ function simpleMappingImportAlgorithm(options) {
788
+ const { mappings, configRemap } = options;
789
+ const packageMap = /* @__PURE__ */ new Map();
790
+ const fileMap = /* @__PURE__ */ new Map();
791
+ for (const mapping of mappings) {
792
+ packageMap.set(mapping.packageName, mapping);
793
+ const files = /* @__PURE__ */ new Map();
794
+ for (const file of mapping.files) {
795
+ files.set(file.sourceFile, file);
796
+ }
797
+ fileMap.set(mapping.packageName, files);
798
+ }
799
+ return (event) => {
800
+ const { exports: exports2, importManager, logger } = event;
801
+ for (const { packageJSON, exportData } of exports2) {
802
+ const packageName = packageJSON.name || "unknown-package";
803
+ const packageMapping = packageMap.get(packageName);
804
+ if (!packageMapping) {
805
+ logger.debug(`Skipping unmapped package: ${packageName}`);
806
+ continue;
807
+ }
808
+ logger.debug(`Processing mapped package: ${packageName}`);
809
+ for (const file of exportData.files) {
810
+ const sourceFile = file.relativeFilePath;
811
+ const packageFiles = fileMap.get(packageName);
812
+ if (!packageFiles) continue;
813
+ const fileMapping = packageFiles.get(sourceFile);
814
+ if (!fileMapping) {
815
+ logger.debug(
816
+ `Skipping unmapped file: ${packageName}/${sourceFile}`
817
+ );
818
+ continue;
819
+ }
820
+ logger.debug(
821
+ `Processing mapped file: ${packageName}/${sourceFile} -> ${fileMapping.targetFile}`
822
+ );
823
+ for (const tag of file.tags) {
824
+ const originalVariableName = tag.variableName;
825
+ if (!originalVariableName || !(originalVariableName in fileMapping.variables)) {
826
+ logger.debug(
827
+ `Skipping unmapped variable: ${originalVariableName} in ${packageName}/${sourceFile}`
828
+ );
829
+ continue;
830
+ }
831
+ const newVariableName = fileMapping.variables[originalVariableName] || originalVariableName;
832
+ const targetFilePath = fileMapping.targetFile;
833
+ let finalConfig = tag.config;
834
+ if (configRemap) {
835
+ finalConfig = configRemap(tag.config, {
836
+ packageName,
837
+ sourceFile,
838
+ targetFile: targetFilePath,
839
+ variableName: newVariableName,
840
+ originalVariableName
841
+ });
842
+ }
843
+ importManager.importTag(targetFilePath, {
844
+ variableName: newVariableName,
845
+ translations: tag.translations,
846
+ config: finalConfig
847
+ });
848
+ logger.debug(
849
+ `Imported: ${originalVariableName} -> ${newVariableName} in ${targetFilePath}`
850
+ );
851
+ }
852
+ }
853
+ }
465
854
  };
466
855
  }
467
856
  exports.NamespaceCollector = namespaceCollector.NamespaceCollector;
468
857
  exports.DictionaryCollector = DictionaryCollector;
858
+ exports.applyCaseTransform = applyCaseTransform;
469
859
  exports.configKeeper = configKeeper;
860
+ exports.flexibleImportAlgorithm = flexibleImportAlgorithm;
470
861
  exports.pathBasedConfigGenerator = pathBasedConfigGenerator;
471
862
  exports.prependNamespaceToPath = prependNamespaceToPath;
863
+ exports.simpleMappingImportAlgorithm = simpleMappingImportAlgorithm;
@@ -4,7 +4,10 @@
4
4
  * This module provides access to all available algorithms organized by category:
5
5
  * - Collectors: Define how translation tags are organized into output files
6
6
  * - Config Generation: Customize tag configuration generation
7
- * - Import: Handle importing translation libraries (future)
7
+ * - Import: Handle importing translation libraries
8
+ * - Case Utils: Common case transformation utilities
8
9
  */
9
- export * from './collector/index.ts';
10
- export * from './config-generation/index.ts';
10
+ export * from './case-utils';
11
+ export * from './collector/index';
12
+ export * from './config-generation/index';
13
+ export * from './import/index';