UNPKG

ccguard

Version:

Automated enforcement of net-negative LOC, complexity constraints, and quality standards for Claude code

150 lines 5.27 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GitIgnoreParser = void 0; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); class GitIgnoreParser { patterns; rootDir; constructor(rootDir) { this.rootDir = rootDir; this.patterns = []; this.loadPatterns(); } loadPatterns() { // Add default patterns first this.addDefaultPatterns(); // Then load .gitignore (so it can override defaults) this.loadGitignoreFile(path_1.default.join(this.rootDir, '.gitignore')); } loadGitignoreFile(gitignorePath) { if (!fs_1.default.existsSync(gitignorePath)) { return; } try { const content = fs_1.default.readFileSync(gitignorePath, 'utf-8'); const lines = content.split('\n'); for (const line of lines) { const trimmed = line.trim(); // Skip empty lines and comments if (!trimmed || trimmed.startsWith('#')) { continue; } this.addPattern(trimmed); } } catch (error) { console.error(`Error reading .gitignore at ${gitignorePath}:`, error); } } addDefaultPatterns() { // Always ignore these const defaults = [ 'node_modules', '.git', 'dist', 'build', '.DS_Store', '.env', '.env.local', 'coverage', '.nyc_output', '.vscode', '.idea', ]; for (const pattern of defaults) { this.addPattern(pattern); } } addPattern(pattern) { let negated = false; let directory = false; let workingPattern = pattern; // Handle negation if (workingPattern.startsWith('!')) { negated = true; workingPattern = workingPattern.slice(1); } // Handle directory-only patterns if (workingPattern.endsWith('/')) { directory = true; workingPattern = workingPattern.slice(0, -1); } // Convert gitignore pattern to regex const regex = this.gitignoreToRegex(workingPattern); this.patterns.push({ pattern: regex, negated, directory }); } gitignoreToRegex(pattern) { // Escape regex special characters except * and ? let regex = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // Handle ** (matches any number of directories) regex = regex.replace(/\*\*/g, '___DOUBLE_STAR___'); // Handle * (matches anything except /) regex = regex.replace(/\*/g, '[^/]*'); // Handle ? (matches any single character except /) regex = regex.replace(/\?/g, '[^/]'); // Replace back ** placeholder regex = regex.replace(/___DOUBLE_STAR___/g, '.*'); // If pattern doesn't start with /, it can match anywhere if (!pattern.startsWith('/')) { regex = `(^|/)${regex}`; } else { // Remove leading / and anchor to start regex = `^${regex.slice(1)}`; } // Add end anchor if pattern doesn't end with * if (!pattern.endsWith('*')) { regex = `${regex}($|/)`; } return new RegExp(regex); } isIgnored(filePath) { // Convert to relative path from root const relativePath = path_1.default.relative(this.rootDir, filePath); // Never include files outside the root directory if (relativePath.startsWith('..')) { return true; } let ignored = false; // Check each pattern in order (later patterns can override earlier ones) for (const { pattern, negated } of this.patterns) { if (pattern.test(relativePath)) { ignored = !negated; } } return ignored; } // Get all files in a directory that are not ignored getAllFiles(dir = this.rootDir) { const files = []; const walkDir = (currentDir) => { try { const entries = fs_1.default.readdirSync(currentDir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path_1.default.join(currentDir, entry.name); if (this.isIgnored(fullPath)) { continue; } if (entry.isDirectory()) { walkDir(fullPath); } else if (entry.isFile()) { files.push(fullPath); } } } catch (error) { // Skip directories we can't read console.debug(`Skipping unreadable directory: ${currentDir}`); } }; walkDir(dir); return files; } } exports.GitIgnoreParser = GitIgnoreParser; //# sourceMappingURL=GitIgnoreParser.js.map