@better-i18n/cli 0.1.7 → 0.2.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 (142) hide show
  1. package/dist/analyzer/dynamic-matcher.d.ts +53 -0
  2. package/dist/analyzer/dynamic-matcher.d.ts.map +1 -0
  3. package/dist/analyzer/dynamic-matcher.js +93 -0
  4. package/dist/analyzer/dynamic-matcher.js.map +1 -0
  5. package/dist/analyzer/index.d.ts +13 -4
  6. package/dist/analyzer/index.d.ts.map +1 -1
  7. package/dist/analyzer/index.js +165 -22
  8. package/dist/analyzer/index.js.map +1 -1
  9. package/dist/analyzer/rules/data-structure.d.ts +13 -0
  10. package/dist/analyzer/rules/data-structure.d.ts.map +1 -0
  11. package/dist/analyzer/rules/data-structure.js +264 -0
  12. package/dist/analyzer/rules/data-structure.js.map +1 -0
  13. package/dist/analyzer/rules/index.d.ts +10 -7
  14. package/dist/analyzer/rules/index.d.ts.map +1 -1
  15. package/dist/analyzer/rules/index.js +10 -7
  16. package/dist/analyzer/rules/index.js.map +1 -1
  17. package/dist/analyzer/rules/jsx-text.js +1 -1
  18. package/dist/analyzer/rules/jsx-text.js.map +1 -1
  19. package/dist/analyzer/rules/translation-function.d.ts.map +1 -1
  20. package/dist/analyzer/rules/translation-function.js +94 -6
  21. package/dist/analyzer/rules/translation-function.js.map +1 -1
  22. package/dist/analyzer/types.d.ts +28 -3
  23. package/dist/analyzer/types.d.ts.map +1 -1
  24. package/dist/commands/check.d.ts +14 -0
  25. package/dist/commands/check.d.ts.map +1 -0
  26. package/dist/commands/check.js +70 -0
  27. package/dist/commands/check.js.map +1 -0
  28. package/dist/commands/doctor.d.ts +20 -0
  29. package/dist/commands/doctor.d.ts.map +1 -0
  30. package/dist/commands/doctor.js +77 -0
  31. package/dist/commands/doctor.js.map +1 -0
  32. package/dist/commands/sync.d.ts +1 -0
  33. package/dist/commands/sync.d.ts.map +1 -1
  34. package/dist/commands/sync.js +158 -272
  35. package/dist/commands/sync.js.map +1 -1
  36. package/dist/context/detector.js +1 -1
  37. package/dist/context/detector.js.map +1 -1
  38. package/dist/context/oidc.d.ts +33 -0
  39. package/dist/context/oidc.d.ts.map +1 -0
  40. package/dist/context/oidc.js +63 -0
  41. package/dist/context/oidc.js.map +1 -0
  42. package/dist/doctor/index.d.ts +65 -0
  43. package/dist/doctor/index.d.ts.map +1 -0
  44. package/dist/doctor/index.js +252 -0
  45. package/dist/doctor/index.js.map +1 -0
  46. package/dist/doctor/project-discovery.d.ts +37 -0
  47. package/dist/doctor/project-discovery.d.ts.map +1 -0
  48. package/dist/doctor/project-discovery.js +140 -0
  49. package/dist/doctor/project-discovery.js.map +1 -0
  50. package/dist/doctor/score.d.ts +41 -0
  51. package/dist/doctor/score.d.ts.map +1 -0
  52. package/dist/doctor/score.js +107 -0
  53. package/dist/doctor/score.js.map +1 -0
  54. package/dist/index.js +35 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/reporters/doctor-eslint.d.ts +17 -0
  57. package/dist/reporters/doctor-eslint.d.ts.map +1 -0
  58. package/dist/reporters/doctor-eslint.js +139 -0
  59. package/dist/reporters/doctor-eslint.js.map +1 -0
  60. package/dist/reporters/doctor-json.d.ts +12 -0
  61. package/dist/reporters/doctor-json.d.ts.map +1 -0
  62. package/dist/reporters/doctor-json.js +21 -0
  63. package/dist/reporters/doctor-json.js.map +1 -0
  64. package/dist/reporters/doctor-report.d.ts +23 -0
  65. package/dist/reporters/doctor-report.d.ts.map +1 -0
  66. package/dist/reporters/doctor-report.js +62 -0
  67. package/dist/reporters/doctor-report.js.map +1 -0
  68. package/dist/reporters/eslint-style.js +4 -4
  69. package/dist/reporters/eslint-style.js.map +1 -1
  70. package/dist/rules/categories.d.ts +36 -0
  71. package/dist/rules/categories.d.ts.map +1 -0
  72. package/dist/rules/categories.js +80 -0
  73. package/dist/rules/categories.js.map +1 -0
  74. package/dist/rules/code/index.d.ts +9 -0
  75. package/dist/rules/code/index.d.ts.map +1 -0
  76. package/dist/rules/code/index.js +9 -0
  77. package/dist/rules/code/index.js.map +1 -0
  78. package/dist/rules/code/jsx-attribute.d.ts +12 -0
  79. package/dist/rules/code/jsx-attribute.d.ts.map +1 -0
  80. package/dist/rules/code/jsx-attribute.js +78 -0
  81. package/dist/rules/code/jsx-attribute.js.map +1 -0
  82. package/dist/rules/code/jsx-text.d.ts +12 -0
  83. package/dist/rules/code/jsx-text.d.ts.map +1 -0
  84. package/dist/rules/code/jsx-text.js +47 -0
  85. package/dist/rules/code/jsx-text.js.map +1 -0
  86. package/dist/rules/code/string-variable.d.ts +13 -0
  87. package/dist/rules/code/string-variable.d.ts.map +1 -0
  88. package/dist/rules/code/string-variable.js +185 -0
  89. package/dist/rules/code/string-variable.js.map +1 -0
  90. package/dist/rules/code/ternary-locale.d.ts +12 -0
  91. package/dist/rules/code/ternary-locale.d.ts.map +1 -0
  92. package/dist/rules/code/ternary-locale.js +53 -0
  93. package/dist/rules/code/ternary-locale.js.map +1 -0
  94. package/dist/rules/code/toast-message.d.ts +13 -0
  95. package/dist/rules/code/toast-message.d.ts.map +1 -0
  96. package/dist/rules/code/toast-message.js +101 -0
  97. package/dist/rules/code/toast-message.js.map +1 -0
  98. package/dist/rules/extraction/data-structure.d.ts +13 -0
  99. package/dist/rules/extraction/data-structure.d.ts.map +1 -0
  100. package/dist/rules/extraction/data-structure.js +212 -0
  101. package/dist/rules/extraction/data-structure.js.map +1 -0
  102. package/dist/rules/extraction/index.d.ts +6 -0
  103. package/dist/rules/extraction/index.d.ts.map +1 -0
  104. package/dist/rules/extraction/index.js +6 -0
  105. package/dist/rules/extraction/index.js.map +1 -0
  106. package/dist/rules/extraction/translation-function.d.ts +12 -0
  107. package/dist/rules/extraction/translation-function.d.ts.map +1 -0
  108. package/dist/rules/extraction/translation-function.js +153 -0
  109. package/dist/rules/extraction/translation-function.js.map +1 -0
  110. package/dist/rules/health/index.d.ts +16 -0
  111. package/dist/rules/health/index.d.ts.map +1 -0
  112. package/dist/rules/health/index.js +22 -0
  113. package/dist/rules/health/index.js.map +1 -0
  114. package/dist/rules/health/missing-translations.d.ts +13 -0
  115. package/dist/rules/health/missing-translations.d.ts.map +1 -0
  116. package/dist/rules/health/missing-translations.js +48 -0
  117. package/dist/rules/health/missing-translations.js.map +1 -0
  118. package/dist/rules/health/orphan-keys.d.ts +16 -0
  119. package/dist/rules/health/orphan-keys.d.ts.map +1 -0
  120. package/dist/rules/health/orphan-keys.js +50 -0
  121. package/dist/rules/health/orphan-keys.js.map +1 -0
  122. package/dist/rules/health/placeholder-mismatch.d.ts +32 -0
  123. package/dist/rules/health/placeholder-mismatch.d.ts.map +1 -0
  124. package/dist/rules/health/placeholder-mismatch.js +120 -0
  125. package/dist/rules/health/placeholder-mismatch.js.map +1 -0
  126. package/dist/rules/registry.d.ts +84 -0
  127. package/dist/rules/registry.d.ts.map +1 -0
  128. package/dist/rules/registry.js +11 -0
  129. package/dist/rules/registry.js.map +1 -0
  130. package/dist/utils/cdn-client.d.ts +30 -0
  131. package/dist/utils/cdn-client.d.ts.map +1 -0
  132. package/dist/utils/cdn-client.js +59 -0
  133. package/dist/utils/cdn-client.js.map +1 -0
  134. package/dist/utils/json-keys.d.ts +60 -0
  135. package/dist/utils/json-keys.d.ts.map +1 -0
  136. package/dist/utils/json-keys.js +103 -0
  137. package/dist/utils/json-keys.js.map +1 -0
  138. package/dist/utils/key-comparison.d.ts +67 -0
  139. package/dist/utils/key-comparison.d.ts.map +1 -0
  140. package/dist/utils/key-comparison.js +299 -0
  141. package/dist/utils/key-comparison.js.map +1 -0
  142. package/package.json +2 -2
@@ -0,0 +1,103 @@
1
+ /**
2
+ * JSON key utilities
3
+ *
4
+ * Functions for flattening, counting, and working with nested translation JSON files.
5
+ * Extracted from sync.ts for reuse in doctor, check, and sync commands.
6
+ */
7
+ /**
8
+ * Flatten nested JSON translation file into a namespace-grouped key map.
9
+ *
10
+ * Only first-level key becomes the namespace (Better i18n convention).
11
+ * Returns Record<namespace, fullKey[]>.
12
+ *
13
+ * @example
14
+ * ```
15
+ * flattenKeys({ common: { title: "Hello", actions: { save: "Save" } } })
16
+ * // => { common: ["common.title", "common.actions.save"] }
17
+ * ```
18
+ */
19
+ export function flattenKeys(remoteKeys) {
20
+ const result = {};
21
+ function traverse(obj, prefix = "") {
22
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj))
23
+ return;
24
+ for (const [key, value] of Object.entries(obj)) {
25
+ const fullKey = prefix ? `${prefix}.${key}` : key;
26
+ if (typeof value === "object" &&
27
+ value !== null &&
28
+ !Array.isArray(value)) {
29
+ traverse(value, fullKey);
30
+ }
31
+ else {
32
+ // Leaf (string, number, array, boolean, or null)
33
+ const namespace = fullKey.split(".")[0] || "default";
34
+ if (!result[namespace])
35
+ result[namespace] = [];
36
+ result[namespace].push(fullKey);
37
+ }
38
+ }
39
+ }
40
+ traverse(remoteKeys);
41
+ return result;
42
+ }
43
+ /**
44
+ * Flatten nested JSON to a flat key→value map (all namespaces merged).
45
+ *
46
+ * Unlike flattenKeys() which groups by namespace, this returns a single
47
+ * flat map of dotted-path → leaf-value. Useful for health rules that
48
+ * need to compare source and target translation values.
49
+ */
50
+ export function flattenToRecord(translations) {
51
+ const result = {};
52
+ function traverse(obj, prefix = "") {
53
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj))
54
+ return;
55
+ for (const [key, value] of Object.entries(obj)) {
56
+ const fullKey = prefix ? `${prefix}.${key}` : key;
57
+ if (typeof value === "object" &&
58
+ value !== null &&
59
+ !Array.isArray(value)) {
60
+ traverse(value, fullKey);
61
+ }
62
+ else if (typeof value === "string") {
63
+ result[fullKey] = value;
64
+ }
65
+ }
66
+ }
67
+ traverse(translations);
68
+ return result;
69
+ }
70
+ /**
71
+ * Count total leaf keys in nested translation structure.
72
+ * Only counts string values (not numbers, arrays, booleans).
73
+ */
74
+ export function countTotalKeys(remoteKeys) {
75
+ let count = 0;
76
+ function traverse(obj) {
77
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj))
78
+ return;
79
+ for (const value of Object.values(obj)) {
80
+ if (typeof value === "string") {
81
+ count++;
82
+ }
83
+ else if (typeof value === "object" && value !== null) {
84
+ traverse(value);
85
+ }
86
+ }
87
+ }
88
+ traverse(remoteKeys);
89
+ return count;
90
+ }
91
+ /**
92
+ * Count total keys from a namespace-grouped string[] map.
93
+ */
94
+ export function countMissingKeys(keys) {
95
+ return Object.values(keys).reduce((sum, arr) => sum + arr.length, 0);
96
+ }
97
+ /**
98
+ * Count total keys from a namespace-grouped MissingKeyEntry[] map.
99
+ */
100
+ export function countMissingKeysFromEntries(keys) {
101
+ return Object.values(keys).reduce((sum, arr) => sum + arr.length, 0);
102
+ }
103
+ //# sourceMappingURL=json-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-keys.js","sourceRoot":"","sources":["../../src/utils/json-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Key comparison utilities
3
+ *
4
+ * Core logic for comparing local code keys with remote translation keys.
5
+ * Handles namespace binding resolution, container detection, fuzzy matching,
6
+ * and dynamic pattern matching. Extracted from sync.ts for reuse.
7
+ */
8
+ import type { Issue } from "../analyzer/types.js";
9
+ import { type MissingKeyEntry, type RemoteTranslations } from "./json-keys.js";
10
+ /** Dynamic key pattern match information */
11
+ export interface DynamicPatternMatch {
12
+ pattern: string;
13
+ file: string;
14
+ line: number;
15
+ matchedKeys: string[];
16
+ }
17
+ /** Scan statistics from code analysis */
18
+ export interface ScanStats {
19
+ dynamicKeys: number;
20
+ dynamicNamespaces: number;
21
+ unboundTranslators: number;
22
+ rootScopedTranslators: number;
23
+ }
24
+ /** Full sync metrics from key comparison */
25
+ export interface SyncMetrics {
26
+ localTotal: number;
27
+ remoteTotal: number;
28
+ intersectionCount: number;
29
+ allLocal: Set<string>;
30
+ allRemoteLeaves: Set<string>;
31
+ missing: Record<string, MissingKeyEntry[]>;
32
+ unused: Record<string, string[]>;
33
+ dynamicReviewRequired: Record<string, string[]>;
34
+ dynamicPatternMatches: DynamicPatternMatch[];
35
+ classification: Record<string, number>;
36
+ filteredKeys: Array<{
37
+ key: string;
38
+ reason: string;
39
+ }>;
40
+ localCoverage: number;
41
+ remoteCoverage: number;
42
+ invariants: {
43
+ local: boolean;
44
+ remote: boolean;
45
+ };
46
+ scanStats?: ScanStats;
47
+ }
48
+ /**
49
+ * Unify all comparison logic to ensure consistent metrics.
50
+ *
51
+ * This is the core comparison engine that:
52
+ * 1. Flattens remote nested JSON to leaf keys
53
+ * 2. Builds container set (paths with children but no direct value)
54
+ * 3. Handles bound-scoped namespace bindings
55
+ * 4. Resolves unknown-scoped partial keys via fuzzy suffix matching
56
+ * 5. Handles container-access keys by expanding to all children
57
+ * 6. Matches dynamic patterns via matchDynamicPattern
58
+ *
59
+ * Returns comprehensive SyncMetrics with missing, unused, coverage percentages.
60
+ */
61
+ export declare function getSummary(allIssues: Issue[], remoteKeys: RemoteTranslations, verbose?: boolean): SyncMetrics;
62
+ /**
63
+ * Group issues by namespace (first dot-segment).
64
+ * Single-segment keys go to "default" namespace.
65
+ */
66
+ export declare function groupKeysByNamespace(issues: Issue[]): Record<string, string[]>;
67
+ //# sourceMappingURL=key-comparison.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-comparison.d.ts","sourceRoot":"","sources":["../../src/utils/key-comparison.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAExB,MAAM,gBAAgB,CAAC;AAExB,4CAA4C;AAC5C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAChD,qBAAqB,EAAE,mBAAmB,EAAE,CAAC;IAC7C,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,YAAY,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAChD,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,KAAK,EAAE,EAClB,UAAU,EAAE,kBAAkB,EAC9B,OAAO,CAAC,EAAE,OAAO,GAChB,WAAW,CA4Sb;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,KAAK,EAAE,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAsB1B"}
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Key comparison utilities
3
+ *
4
+ * Core logic for comparing local code keys with remote translation keys.
5
+ * Handles namespace binding resolution, container detection, fuzzy matching,
6
+ * and dynamic pattern matching. Extracted from sync.ts for reuse.
7
+ */
8
+ import { matchDynamicPattern } from "../analyzer/dynamic-matcher.js";
9
+ import { countMissingKeys, countMissingKeysFromEntries, flattenKeys, } from "./json-keys.js";
10
+ /**
11
+ * Build a set of all dotted paths in a nested JSON object.
12
+ * Includes both leaf paths and container (branch) paths.
13
+ */
14
+ function buildPathSet(obj, prefix = "", paths) {
15
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj))
16
+ return;
17
+ for (const [key, value] of Object.entries(obj)) {
18
+ const fullPath = prefix ? `${prefix}.${key}` : key;
19
+ paths.add(fullPath);
20
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
21
+ buildPathSet(value, fullPath, paths);
22
+ }
23
+ }
24
+ }
25
+ /**
26
+ * Unify all comparison logic to ensure consistent metrics.
27
+ *
28
+ * This is the core comparison engine that:
29
+ * 1. Flattens remote nested JSON to leaf keys
30
+ * 2. Builds container set (paths with children but no direct value)
31
+ * 3. Handles bound-scoped namespace bindings
32
+ * 4. Resolves unknown-scoped partial keys via fuzzy suffix matching
33
+ * 5. Handles container-access keys by expanding to all children
34
+ * 6. Matches dynamic patterns via matchDynamicPattern
35
+ *
36
+ * Returns comprehensive SyncMetrics with missing, unused, coverage percentages.
37
+ */
38
+ export function getSummary(allIssues, remoteKeys, verbose) {
39
+ const remoteFlattened = flattenKeys(remoteKeys);
40
+ // Use unique key sets for all calculations
41
+ const allLocal = new Set();
42
+ const classification = {
43
+ "bound-scoped": 0,
44
+ "root-scoped": 0,
45
+ "unknown-scoped": 0,
46
+ "unbound": 0,
47
+ };
48
+ // Build fast lookup for all remote keys (including paths)
49
+ const remoteKeyPaths = new Set();
50
+ buildPathSet(remoteKeys, "", remoteKeyPaths);
51
+ // Flatten all remote leaves for intersection
52
+ const allRemoteLeaves = new Set();
53
+ for (const keys of Object.values(remoteFlattened)) {
54
+ for (const k of keys)
55
+ allRemoteLeaves.add(k);
56
+ }
57
+ // Build a set of all remote containers (paths that exist but aren't leaves)
58
+ const remoteContainers = new Set();
59
+ for (const path of remoteKeyPaths) {
60
+ if (!allRemoteLeaves.has(path)) {
61
+ remoteContainers.add(path);
62
+ }
63
+ }
64
+ // Handle namespace-scoped translators
65
+ // When code has: const t = useTranslations("teacher.courses.course_modules")
66
+ // ALL keys under that namespace are accessible, so mark them as used
67
+ const boundNamespaces = new Set();
68
+ for (const issue of allIssues) {
69
+ if (issue.bindingType === "bound-scoped" && issue.namespace) {
70
+ boundNamespaces.add(issue.namespace);
71
+ }
72
+ }
73
+ // For each bound namespace, mark all its children as used
74
+ for (const namespace of boundNamespaces) {
75
+ if (remoteContainers.has(namespace) || remoteKeyPaths.has(namespace)) {
76
+ const namespacePrefix = namespace + ".";
77
+ const childrenKeys = Array.from(allRemoteLeaves).filter((k) => k.startsWith(namespacePrefix));
78
+ if (verbose && childrenKeys.length > 0) {
79
+ console.log(`[VERBOSE] Namespace binding detected: "${namespace}" (${childrenKeys.length} children accessible)`);
80
+ console.log(`[VERBOSE] → Marking all children as used:`, childrenKeys.slice(0, 3).join(", "), childrenKeys.length > 3 ? "..." : "");
81
+ }
82
+ childrenKeys.forEach((child) => allLocal.add(child));
83
+ classification["bound-scoped"] =
84
+ (classification["bound-scoped"] || 0) + 1;
85
+ }
86
+ }
87
+ // Filter local keys: strictly skip if they refer to a container object in remote
88
+ const rawLocalKeys = Array.from(new Set(allIssues.map((i) => i.key).filter(Boolean)));
89
+ const filteredKeys = [];
90
+ // Separate partial keys (unknown-scoped) for fuzzy matching
91
+ const partialKeys = [];
92
+ const fullyQualifiedKeys = [];
93
+ for (const issue of allIssues) {
94
+ if (!issue.key)
95
+ continue;
96
+ // Check if this local key is accessing a remote container
97
+ if (remoteContainers.has(issue.key)) {
98
+ const containerPrefix = issue.key + ".";
99
+ const childrenKeys = Array.from(allRemoteLeaves).filter((k) => k.startsWith(containerPrefix));
100
+ if (verbose) {
101
+ console.log(`[VERBOSE] Container access detected: "${issue.key}" at ${issue.file}:${issue.line}`);
102
+ console.log(`[VERBOSE] → Marking ${childrenKeys.length} children as used:`, childrenKeys.slice(0, 3).join(", "), childrenKeys.length > 3 ? "..." : "");
103
+ }
104
+ childrenKeys.forEach((child) => allLocal.add(child));
105
+ if (issue.bindingType) {
106
+ classification[issue.bindingType] =
107
+ (classification[issue.bindingType] || 0) + 1;
108
+ }
109
+ continue;
110
+ }
111
+ // Check if this key is a container in remote (has children but no leaf value)
112
+ const isRemoteContainer = remoteKeyPaths.has(issue.key) && !allRemoteLeaves.has(issue.key);
113
+ if (isRemoteContainer) {
114
+ const hasChildrenInLocal = rawLocalKeys.some((other) => other !== issue.key && other.startsWith(issue.key + "."));
115
+ if (hasChildrenInLocal) {
116
+ if (verbose) {
117
+ filteredKeys.push({
118
+ key: issue.key,
119
+ reason: "container with extracted children",
120
+ });
121
+ }
122
+ continue;
123
+ }
124
+ }
125
+ // Check if this is a partial key (pattern-matched translator without namespace)
126
+ if (issue.bindingType === "unknown-scoped") {
127
+ partialKeys.push({ key: issue.key, issue });
128
+ }
129
+ else {
130
+ fullyQualifiedKeys.push(issue);
131
+ allLocal.add(issue.key);
132
+ if (issue.bindingType) {
133
+ classification[issue.bindingType] =
134
+ (classification[issue.bindingType] || 0) + 1;
135
+ }
136
+ }
137
+ }
138
+ // Fuzzy match partial keys against remote keys
139
+ for (const { key: partialKey, issue } of partialKeys) {
140
+ const matches = Array.from(allRemoteLeaves).filter((remoteKey) => {
141
+ if (remoteKey === partialKey)
142
+ return true;
143
+ if (remoteKey.endsWith(`.${partialKey}`))
144
+ return true;
145
+ if (!partialKey.includes(".") && remoteKey.endsWith(partialKey)) {
146
+ const beforePartial = remoteKey.slice(0, -partialKey.length);
147
+ if (beforePartial.endsWith("."))
148
+ return true;
149
+ }
150
+ return false;
151
+ });
152
+ if (matches.length === 1) {
153
+ allLocal.add(matches[0]);
154
+ if (verbose) {
155
+ console.log(`[VERBOSE] Fuzzy matched partial key: "${partialKey}" → "${matches[0]}"`);
156
+ }
157
+ }
158
+ else if (matches.length > 1) {
159
+ if (verbose) {
160
+ console.log(`[VERBOSE] Ambiguous partial key: "${partialKey}" (${matches.length} matches: ${matches.slice(0, 3).join(", ")}${matches.length > 3 ? ", ..." : ""})`);
161
+ }
162
+ matches.forEach((m) => allLocal.add(m));
163
+ }
164
+ else {
165
+ allLocal.add(partialKey);
166
+ if (verbose) {
167
+ console.log(`[VERBOSE] No fuzzy match for partial key: "${partialKey}"`);
168
+ }
169
+ }
170
+ if (issue.bindingType) {
171
+ classification[issue.bindingType] =
172
+ (classification[issue.bindingType] || 0) + 1;
173
+ }
174
+ }
175
+ // Calculate intersection once
176
+ const intersection = new Set();
177
+ for (const k of allLocal) {
178
+ if (allRemoteLeaves.has(k))
179
+ intersection.add(k);
180
+ }
181
+ // Missing: In Local Set but not in Remote Set
182
+ const missing = {};
183
+ for (const issue of allIssues) {
184
+ if (!issue.key || !allLocal.has(issue.key))
185
+ continue;
186
+ if (!allRemoteLeaves.has(issue.key)) {
187
+ const ns = issue.key.split(".")[0] || "default";
188
+ if (!missing[ns])
189
+ missing[ns] = [];
190
+ if (!missing[ns].some((m) => m.key === issue.key)) {
191
+ missing[ns].push({
192
+ key: issue.key,
193
+ value: issue.text || "",
194
+ });
195
+ }
196
+ }
197
+ }
198
+ // Extract dynamic patterns from issues
199
+ const dynamicPatterns = allIssues
200
+ .filter((i) => i.isDynamic && i.pattern)
201
+ .map((i) => ({ pattern: i.pattern, file: i.file, line: i.line }));
202
+ if (verbose && dynamicPatterns.length > 0) {
203
+ console.log(`[VERBOSE] Found ${dynamicPatterns.length} dynamic patterns`);
204
+ }
205
+ // Match dynamic patterns against remote keys
206
+ const keysMatchedByDynamicPatterns = new Set();
207
+ const dynamicPatternMatches = [];
208
+ for (const { pattern, file, line } of dynamicPatterns) {
209
+ const matches = matchDynamicPattern(pattern, Array.from(allRemoteLeaves));
210
+ if (matches.length > 0) {
211
+ dynamicPatternMatches.push({ pattern, file, line, matchedKeys: matches });
212
+ matches.forEach((k) => {
213
+ keysMatchedByDynamicPatterns.add(k);
214
+ allLocal.add(k);
215
+ });
216
+ if (verbose) {
217
+ console.log(`[VERBOSE] Pattern "${pattern}" matched ${matches.length} keys`);
218
+ }
219
+ }
220
+ }
221
+ // Recalculate intersection after adding dynamic matches
222
+ intersection.clear();
223
+ for (const k of allLocal) {
224
+ if (allRemoteLeaves.has(k))
225
+ intersection.add(k);
226
+ }
227
+ // Unused: In Remote Set but not in Local Set
228
+ const staticUnused = {};
229
+ const dynamicReviewRequired = {};
230
+ for (const k of allRemoteLeaves) {
231
+ if (!allLocal.has(k)) {
232
+ const ns = k.split(".")[0] || "default";
233
+ if (keysMatchedByDynamicPatterns.has(k)) {
234
+ if (!dynamicReviewRequired[ns])
235
+ dynamicReviewRequired[ns] = [];
236
+ dynamicReviewRequired[ns].push(k);
237
+ }
238
+ else {
239
+ if (!staticUnused[ns])
240
+ staticUnused[ns] = [];
241
+ staticUnused[ns].push(k);
242
+ }
243
+ }
244
+ }
245
+ const localTotal = allLocal.size;
246
+ const remoteTotal = allRemoteLeaves.size;
247
+ const intersectionCount = intersection.size;
248
+ return {
249
+ localTotal,
250
+ remoteTotal,
251
+ intersectionCount,
252
+ allLocal,
253
+ allRemoteLeaves,
254
+ missing,
255
+ unused: staticUnused,
256
+ dynamicReviewRequired,
257
+ dynamicPatternMatches,
258
+ classification,
259
+ filteredKeys,
260
+ localCoverage: localTotal > 0
261
+ ? Math.round((intersectionCount / localTotal) * 100)
262
+ : 100,
263
+ remoteCoverage: remoteTotal > 0
264
+ ? Math.round((intersectionCount / remoteTotal) * 100)
265
+ : 100,
266
+ invariants: {
267
+ local: localTotal ===
268
+ intersectionCount + countMissingKeysFromEntries(missing),
269
+ remote: remoteTotal ===
270
+ intersectionCount +
271
+ countMissingKeys(staticUnused) +
272
+ countMissingKeys(dynamicReviewRequired),
273
+ },
274
+ };
275
+ }
276
+ /**
277
+ * Group issues by namespace (first dot-segment).
278
+ * Single-segment keys go to "default" namespace.
279
+ */
280
+ export function groupKeysByNamespace(issues) {
281
+ const result = {};
282
+ for (const issue of issues) {
283
+ if (!issue.key)
284
+ continue;
285
+ const parts = issue.key.split(".");
286
+ const namespace = parts.length > 1 ? parts[0] : "default";
287
+ const fullKey = issue.key;
288
+ if (!result[namespace]) {
289
+ result[namespace] = new Set();
290
+ }
291
+ result[namespace].add(fullKey);
292
+ }
293
+ const output = {};
294
+ for (const [namespace, keys] of Object.entries(result)) {
295
+ output[namespace] = Array.from(keys).sort();
296
+ }
297
+ return output;
298
+ }
299
+ //# sourceMappingURL=key-comparison.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-comparison.js","sourceRoot":"","sources":["../../src/utils/key-comparison.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-i18n/cli",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "CLI tool for detecting hardcoded strings in React/Next.js apps",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,13 +44,13 @@
44
44
  "prepublishOnly": "npm run clean && npm run build"
45
45
  },
46
46
  "dependencies": {
47
+ "@inquirer/prompts": "^7.2.0",
47
48
  "commander": "^12.1.0",
48
49
  "glob": "^11.0.0",
49
50
  "ora": "^8.1.1",
50
51
  "typescript": "~5.9.2"
51
52
  },
52
53
  "devDependencies": {
53
- "@better-i18n/typescript-config": "workspace:*",
54
54
  "@types/node": "^20.0.0"
55
55
  },
56
56
  "engines": {