UNPKG

devibe

Version:

Intelligent repository cleanup with auto mode, AI learning, markdown consolidation, auto-consolidate workflow, context-aware classification, and cost optimization

146 lines 6.2 kB
import * as fs from 'fs/promises'; import * as path from 'path'; /** * Detects if a file is still being referenced/used in the codebase. * This helps prevent deletion of files that are actively used. */ export class UsageDetector { excludePatterns = [ 'node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt', 'out', 'target', ]; async checkFileUsage(filePath, searchPaths) { const fileName = path.basename(filePath); const fileNameWithoutExt = path.basename(filePath, path.extname(filePath)); const references = []; for (const searchPath of searchPaths) { try { const foundRefs = await this.searchForReferences(searchPath, fileName, fileNameWithoutExt, filePath); references.push(...foundRefs); } catch (error) { // Continue searching other paths even if one fails } } return { isReferenced: references.length > 0, references, recommendKeep: references.length > 0, }; } async searchForReferences(searchPath, fileName, fileNameWithoutExt, originalPath) { const references = []; try { const entries = await fs.readdir(searchPath, { withFileTypes: true }); for (const entry of entries) { // Skip excluded directories if (this.excludePatterns.includes(entry.name)) { continue; } const fullPath = path.join(searchPath, entry.name); // Don't check the file against itself if (fullPath === originalPath) { continue; } if (entry.isDirectory()) { // Recursively search subdirectories const subRefs = await this.searchForReferences(fullPath, fileName, fileNameWithoutExt, originalPath); references.push(...subRefs); } else if (entry.isFile()) { // Search in files const fileRefs = await this.searchInFile(fullPath, fileName, fileNameWithoutExt); references.push(...fileRefs); } } } catch (error) { // Permission errors or other issues - continue } return references; } async searchInFile(filePath, targetFileName, targetFileNameWithoutExt) { const references = []; // Only search in text files const ext = path.extname(filePath); const textExtensions = [ '.js', '.ts', '.jsx', '.tsx', '.vue', '.py', '.go', '.java', '.rb', '.php', '.json', '.yaml', '.yml', '.toml', '.ini', '.env', '.md', '.txt', '.sh', '.bash', '.zsh', '.html', '.css', '.scss', '.sass', '.less', '.dockerfile', '.gitignore', '.npmrc', ]; if (!textExtensions.includes(ext.toLowerCase()) && !filePath.includes('Dockerfile')) { return references; } try { const stats = await fs.stat(filePath); // Skip very large files if (stats.size > 1024 * 1024) { // 1MB return references; } const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Check for various reference patterns if (line.includes(targetFileName) || line.includes(targetFileNameWithoutExt) || this.hasImportReference(line, targetFileName, targetFileNameWithoutExt) || this.hasRequireReference(line, targetFileName, targetFileNameWithoutExt) || this.hasScriptReference(line, targetFileName, targetFileNameWithoutExt)) { references.push({ file: filePath, line: i + 1, context: line.trim().substring(0, 100), // Limit context length }); } } } catch (error) { // File not readable or binary, skip } return references; } hasImportReference(line, fileName, fileNameWithoutExt) { // ES6 import statements const importPatterns = [ new RegExp(`import\\s+.*from\\s+['"].*${this.escapeRegex(fileNameWithoutExt)}['"]`), new RegExp(`import\\s+['"].*${this.escapeRegex(fileName)}['"]`), new RegExp(`import\\s+.*from\\s+['"].*/${this.escapeRegex(fileNameWithoutExt)}['"]`), ]; return importPatterns.some(pattern => pattern.test(line)); } hasRequireReference(line, fileName, fileNameWithoutExt) { // CommonJS require statements const requirePatterns = [ new RegExp(`require\\s*\\(\\s*['"].*${this.escapeRegex(fileNameWithoutExt)}['"]\\s*\\)`), new RegExp(`require\\s*\\(\\s*['"].*${this.escapeRegex(fileName)}['"]\\s*\\)`), new RegExp(`require\\s*\\(\\s*['"].*/${this.escapeRegex(fileNameWithoutExt)}['"]\\s*\\)`), ]; return requirePatterns.some(pattern => pattern.test(line)); } hasScriptReference(line, fileName, fileNameWithoutExt) { // Script execution references (bash, package.json, etc.) const scriptPatterns = [ new RegExp(`node\\s+.*${this.escapeRegex(fileName)}`), new RegExp(`node\\s+.*${this.escapeRegex(fileNameWithoutExt)}`), new RegExp(`\\./${this.escapeRegex(fileName)}`), new RegExp(`bash\\s+.*${this.escapeRegex(fileName)}`), new RegExp(`sh\\s+.*${this.escapeRegex(fileName)}`), new RegExp(`["'].*${this.escapeRegex(fileName)}["']`), ]; return scriptPatterns.some(pattern => pattern.test(line)); } escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } } //# sourceMappingURL=usage-detector.js.map