Files
oh-my-opencode/src/shared/pattern-matcher.ts
T
YeonGyu-Kim c2f7d059d2 perf(shared): optimize hot-path utilities across plugin
- task-list: replace O(n³) blocker resolution with Map lookup (C4)
- logger: buffer log entries and flush periodically to reduce sync I/O (C5)
- plugin-interface: create chatParamsHandler once at init (H3)
- pattern-matcher: cache compiled RegExp for wildcard matchers (H6)
- file-reference-resolver: use replaceAll instead of split/join (M9)
- connected-providers-cache: add in-memory cache for read operations (L4)
2026-03-18 14:19:00 +09:00

47 lines
1.4 KiB
TypeScript

import type { ClaudeHooksConfig, HookMatcher } from "../hooks/claude-code-hooks/types"
/**
* Escape all regex special characters EXCEPT asterisk (*).
* Asterisk is preserved for glob-to-regex conversion.
*/
function escapeRegexExceptAsterisk(str: string): string {
// Escape all regex special chars except * (which we convert to .* for glob matching)
return str.replace(/[.+?^${}()|[\]\\]/g, "\\$&")
}
const regexCache = new Map<string, RegExp>()
export function matchesToolMatcher(toolName: string, matcher: string): boolean {
if (!matcher) {
return true
}
const patterns = matcher.split("|").map((p) => p.trim())
return patterns.some((p) => {
if (p.includes("*")) {
// First escape regex special chars (except *), then convert * to .*
let regex = regexCache.get(p)
if (!regex) {
const escaped = escapeRegexExceptAsterisk(p)
regex = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`, "i")
regexCache.set(p, regex)
}
return regex.test(toolName)
}
return p.toLowerCase() === toolName.toLowerCase()
})
}
export function findMatchingHooks(
config: ClaudeHooksConfig,
eventName: keyof ClaudeHooksConfig,
toolName?: string
): HookMatcher[] {
const hookMatchers = config[eventName]
if (!hookMatchers) return []
return hookMatchers.filter((hookMatcher) => {
if (!toolName) return true
return matchesToolMatcher(toolName, hookMatcher.matcher)
})
}