/** * Grep helpers: glob match + output modes. No Bare imports. */ function globToRegExp(glob) { const g = String(glob || '').replace(/\\/g, '/'); if (!g) return null; let out = '^'; for (let i = 0; i < g.length; i++) { const c = g[i]; if (c === '*' && g[i + 1] === '*') { out += '.*'; i += 1; if (g[i + 1] === '/') i += 1; } else if (c === '*') out += '[^/]*'; else if (c === '?') out += '[^/]'; else if ('\\.()+^$[]{}|'.indexOf(c) >= 0) out += '\\' + c; else out += c; } out += '$'; return new RegExp(out, 'i'); } function matchGlob(relPath, glob) { if (!glob) return true; const rel = String(relPath || '').replace(/\\/g, '/'); const re = globToRegExp(glob); if (!re) return true; if (re.test(rel)) return true; const base = rel.split('/').pop(); return re.test(base); } function formatHits(hits, mode, truncated) { const list = hits || []; const m = String(mode || 'content').toLowerCase(); if (m === 'count') { const by = {}; for (const h of list) { const p = h.path || ''; by[p] = (by[p] || 0) + 1; } return { mode: 'count', files: by, truncated: !!truncated }; } if (m === 'files_with_matches' || m === 'files') { const seen = []; const set = new Set(); for (const h of list) { if (!set.has(h.path)) { set.add(h.path); seen.push(h.path); } } return { mode: 'files_with_matches', files: seen, truncated: !!truncated }; } return { mode: 'content', hits: list, truncated: !!truncated }; } module.exports = { globToRegExp, matchGlob, formatHits };