( pattern: string, command: string, caseInsensitive = false, )
| 88 | * @returns true if the command matches the pattern |
| 89 | */ |
| 90 | export function matchWildcardPattern( |
| 91 | pattern: string, |
| 92 | command: string, |
| 93 | caseInsensitive = false, |
| 94 | ): boolean { |
| 95 | // Trim leading/trailing whitespace from pattern |
| 96 | const trimmedPattern = pattern.trim() |
| 97 | |
| 98 | // Process the pattern to handle escape sequences: \* and \\ |
| 99 | let processed = '' |
| 100 | let i = 0 |
| 101 | |
| 102 | while (i < trimmedPattern.length) { |
| 103 | const char = trimmedPattern[i] |
| 104 | |
| 105 | // Handle escape sequences |
| 106 | if (char === '\\' && i + 1 < trimmedPattern.length) { |
| 107 | const nextChar = trimmedPattern[i + 1] |
| 108 | if (nextChar === '*') { |
| 109 | // \* -> literal asterisk placeholder |
| 110 | processed += ESCAPED_STAR_PLACEHOLDER |
| 111 | i += 2 |
| 112 | continue |
| 113 | } else if (nextChar === '\\') { |
| 114 | // \\ -> literal backslash placeholder |
| 115 | processed += ESCAPED_BACKSLASH_PLACEHOLDER |
| 116 | i += 2 |
| 117 | continue |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | processed += char |
| 122 | i++ |
| 123 | } |
| 124 | |
| 125 | // Escape regex special characters except * |
| 126 | const escaped = processed.replace(/[.+?^${}()|[\]\\'"]/g, '\\$&') |
| 127 | |
| 128 | // Convert unescaped * to .* for wildcard matching |
| 129 | const withWildcards = escaped.replace(/\*/g, '.*') |
| 130 | |
| 131 | // Convert placeholders back to escaped regex literals |
| 132 | let regexPattern = withWildcards |
| 133 | .replace(ESCAPED_STAR_PLACEHOLDER_RE, '\\*') |
| 134 | .replace(ESCAPED_BACKSLASH_PLACEHOLDER_RE, '\\\\') |
| 135 | |
| 136 | // When a pattern ends with ' *' (space + unescaped wildcard) AND the trailing |
| 137 | // wildcard is the ONLY unescaped wildcard, make the trailing space-and-args |
| 138 | // optional so 'git *' matches both 'git add' and bare 'git'. |
| 139 | // This aligns wildcard matching with prefix rule semantics (git:*). |
| 140 | // Multi-wildcard patterns like '* run *' are excluded — making the last |
| 141 | // wildcard optional would incorrectly match 'npm run' (no trailing arg). |
| 142 | const unescapedStarCount = (processed.match(/\*/g) || []).length |
| 143 | if (regexPattern.endsWith(' .*') && unescapedStarCount === 1) { |
| 144 | regexPattern = regexPattern.slice(0, -3) + '( .*)?' |
| 145 | } |
| 146 | |
| 147 | // Create regex that matches the entire string. |
nothing calls this directly
no outgoing calls
no test coverage detected