Skip to content

Commit b2106a4

Browse files
committed
fix(cli): use separator-bounded matching for key pattern filters
1 parent b81fcce commit b2106a4

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-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: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,40 @@ 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 or slash 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.nested.key", patterns)).toBe(true);
41+
});
42+
1843
it("should match keys with glob patterns", () => {
1944
const patterns = ["api/*/users", "settings/*"];
2045

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
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" or "inbox/details".
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+
minimatch(key, pattern),
915
);
1016
}
1117

0 commit comments

Comments
 (0)