Skip to content

Commit bd71fd3

Browse files
authored
fix(cli): use separator-bounded matching for key pattern filters (#2010)
* fix(cli): use separator-bounded matching for key pattern filters * fix(cli): add dash as recognized separator in key pattern matching
1 parent 2f10ae2 commit bd71fd3

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

.changeset/flat-beans-wonder.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"lingo.dev": patch
3+
---
4+
5+
fix(cli): use exact and separator-bounded matching for lockedKeys, ignoredKeys, preservedKeys, and localizableKeys instead of substring prefix matching

packages/cli/src/cli/utils/key-matching.spec.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,41 @@ import {
66
} from "./key-matching";
77

88
describe("matchesKeyPattern", () => {
9-
it("should match keys with prefix matching", () => {
9+
it("should match keys with separator-bounded prefix matching", () => {
1010
const patterns = ["api", "settings"];
1111

1212
expect(matchesKeyPattern("api/users", patterns)).toBe(true);
1313
expect(matchesKeyPattern("api/posts", patterns)).toBe(true);
14+
expect(matchesKeyPattern("api.users", patterns)).toBe(true);
1415
expect(matchesKeyPattern("settings/theme", patterns)).toBe(true);
16+
expect(matchesKeyPattern("settings.theme", patterns)).toBe(true);
1517
expect(matchesKeyPattern("other/key", patterns)).toBe(false);
1618
});
1719

20+
it("should match exact keys", () => {
21+
const patterns = ["inbox"];
22+
23+
expect(matchesKeyPattern("inbox", patterns)).toBe(true);
24+
});
25+
26+
it("should not match keys that share a prefix but lack a separator", () => {
27+
const patterns = ["inbox"];
28+
29+
expect(matchesKeyPattern("inbox_url", patterns)).toBe(false);
30+
expect(matchesKeyPattern("inbox_empty_title", patterns)).toBe(false);
31+
expect(matchesKeyPattern("inbox_empty_body", patterns)).toBe(false);
32+
expect(matchesKeyPattern("inboxes", patterns)).toBe(false);
33+
});
34+
35+
it("should match keys with dot, slash, or dash separator after pattern", () => {
36+
const patterns = ["inbox"];
37+
38+
expect(matchesKeyPattern("inbox.title", patterns)).toBe(true);
39+
expect(matchesKeyPattern("inbox/details", patterns)).toBe(true);
40+
expect(matchesKeyPattern("inbox-0", patterns)).toBe(true);
41+
expect(matchesKeyPattern("inbox.nested.key", patterns)).toBe(true);
42+
});
43+
1844
it("should match keys with glob patterns", () => {
1945
const patterns = ["api/*/users", "settings/*"];
2046

packages/cli/src/cli/utils/key-matching.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { minimatch } from "minimatch";
22

33
/**
4-
* Checks if a key matches any of the provided patterns using prefix or glob matching
4+
* Checks if a key matches any of the provided patterns using exact, separator-bounded prefix, or glob matching.
5+
* Separator-bounded means the key must equal the pattern exactly, or continue with a ".", "/", or "-" separator.
6+
* This prevents "inbox" from matching "inbox_url" while still matching "inbox.title", "inbox/details", or "heading-0".
57
*/
68
export function matchesKeyPattern(key: string, patterns: string[]): boolean {
79
return patterns.some(
8-
(pattern) => key.startsWith(pattern) || minimatch(key, pattern),
10+
(pattern) =>
11+
key === pattern ||
12+
key.startsWith(pattern + ".") ||
13+
key.startsWith(pattern + "/") ||
14+
key.startsWith(pattern + "-") ||
15+
minimatch(key, pattern),
916
);
1017
}
1118

0 commit comments

Comments
 (0)