Skip to content

Commit 75da91b

Browse files
committed
fix(cli): decode URL-encoded keys before minimatch comparison in --key filtering
1 parent de1d172 commit 75da91b

File tree

4 files changed

+17
-6
lines changed

4 files changed

+17
-6
lines changed

.changeset/salty-sails-sell.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+
Fixed --key pattern matching in run and purge commands by decoding URL-encoded keys before minimatch comparison

packages/cli/src/cli/cmd/purge.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ export default new Command()
123123
if (options.key) {
124124
// minimatch for key patterns
125125
keysToRemove = Object.keys(newData).filter((k) =>
126-
minimatch(k, options.key!),
126+
minimatch(
127+
decodeURIComponent(k),
128+
decodeURIComponent(options.key!),
129+
),
127130
);
128131
} else {
129132
// No key specified: remove all keys
@@ -133,7 +136,7 @@ export default new Command()
133136
// Show what will be deleted
134137
if (options.key) {
135138
bucketOra.info(
136-
`About to delete ${keysToRemove.length} key(s) matching '${options.key}' from ${bucketPath.pathPattern} [${targetLocale}]:\n ${keysToRemove.slice(0, 10).join(", ")}${keysToRemove.length > 10 ? ", ..." : ""}`,
139+
`About to delete ${keysToRemove.length} key(s) matching '${decodeURIComponent(options.key)}' from ${bucketPath.pathPattern} [${targetLocale}]:\n ${keysToRemove.slice(0, 10).join(", ")}${keysToRemove.length > 10 ? ", ..." : ""}`,
137140
);
138141
} else {
139142
bucketOra.info(
@@ -161,7 +164,7 @@ export default new Command()
161164
await bucketLoader.push(targetLocale, newData);
162165
if (options.key) {
163166
bucketOra.succeed(
164-
`Removed ${keysToRemove.length} key(s) matching '${options.key}' from ${bucketPath.pathPattern} [${targetLocale}]`,
167+
`Removed ${keysToRemove.length} key(s) matching '${decodeURIComponent(options.key)}' from ${bucketPath.pathPattern} [${targetLocale}]`,
165168
);
166169
} else {
167170
bucketOra.succeed(
@@ -170,7 +173,7 @@ export default new Command()
170173
}
171174
} else if (options.key) {
172175
bucketOra.info(
173-
`No keys matching '${options.key}' found in ${bucketPath.pathPattern} [${targetLocale}]`,
176+
`No keys matching '${decodeURIComponent(options.key)}' found in ${bucketPath.pathPattern} [${targetLocale}]`,
174177
);
175178
} else {
176179
bucketOra.info("No keys to remove.");

packages/cli/src/cli/cmd/run/execute.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,10 @@ function createWorkerTask(args: {
231231
([key]) =>
232232
!assignedTask.onlyKeys.length ||
233233
assignedTask.onlyKeys?.some((pattern) =>
234-
minimatch(key, pattern),
234+
minimatch(
235+
decodeURIComponent(key),
236+
decodeURIComponent(pattern),
237+
),
235238
),
236239
)
237240
.fromPairs()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { minimatch } from "minimatch";
33
/**
44
* Checks if a key matches any of the provided patterns using exact, separator-bounded prefix, or glob matching.
55
* 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".
6+
* This prevents "inbox" from matching "inbox_url" while still matching "inbox.title", "inbox/details", or "inbox-0".
77
*/
88
export function matchesKeyPattern(key: string, patterns: string[]): boolean {
99
return patterns.some(

0 commit comments

Comments
 (0)