UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

231 lines 7.88 kB
import fs from 'fs-extra'; import * as path from 'path'; import { glob } from 'glob'; export class MetricsCollector { projectRoot; ignorePatterns = [ 'node_modules/**', '.git/**', 'dist/**', 'build/**', 'coverage/**', '*.min.js', '*.min.css', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml' ]; constructor(projectRoot) { this.projectRoot = projectRoot; } async collectQuickMetrics() { const files = await this.getProjectFiles(true); const filesByExtension = {}; const filesByType = {}; const languages = {}; let testFiles = 0; for (const file of files) { const ext = path.extname(file) || 'no-extension'; filesByExtension[ext] = (filesByExtension[ext] || 0) + 1; // Count test files if (file.includes('test') || file.includes('spec')) { testFiles++; } // Map extensions to language categories const language = this.getLanguageFromExtension(ext); if (language && language !== 'other') { languages[language] = (languages[language] || 0) + 1; } // Map to file types const fileType = this.getFileType(ext); filesByType[fileType] = (filesByType[fileType] || 0) + 1; } return { fileCount: files.length, totalFiles: files.length, totalLines: 0, // Skip line counting for quick scan codeLines: 0, commentLines: 0, blankLines: 0, filesByExtension, filesByType, largestFiles: [], todoCount: 0, fixmeCount: 0, testFiles, languages }; } async collectFullMetrics() { const files = await this.getProjectFiles(false); const filesByExtension = {}; const filesByType = {}; const fileSizes = []; const languages = {}; let totalLines = 0; let codeLines = 0; let commentLines = 0; let blankLines = 0; let todoCount = 0; let fixmeCount = 0; let testFiles = 0; for (const file of files) { const ext = path.extname(file) || 'no-extension'; filesByExtension[ext] = (filesByExtension[ext] || 0) + 1; // Count test files if (file.includes('test') || file.includes('spec')) { testFiles++; } // Map extensions to language categories const language = this.getLanguageFromExtension(ext); if (language && language !== 'other') { languages[language] = (languages[language] || 0) + 1; } // Map to file types const fileType = this.getFileType(ext); filesByType[fileType] = (filesByType[fileType] || 0) + 1; try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); const lines = content.split('\n'); fileSizes.push({ path: file, lines: lines.length }); totalLines += lines.length; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) { blankLines++; } else if (this.isCommentLine(trimmed, ext)) { commentLines++; } else { codeLines++; } // Count TODOs and FIXMEs if (line.match(/TODO/i)) todoCount++; if (line.match(/FIXME/i)) fixmeCount++; } } catch (error) { // Skip files that can't be read } } // Sort files by size and get top 10 fileSizes.sort((a, b) => b.lines - a.lines); const largestFiles = fileSizes.slice(0, 10); return { fileCount: files.length, totalFiles: files.length, totalLines, codeLines, commentLines, blankLines, filesByExtension, filesByType, largestFiles, todoCount, fixmeCount, testFiles, languages }; } async getProjectFiles(quick) { const gitignorePath = path.join(this.projectRoot, '.gitignore'); let customIgnorePatterns = []; if (await fs.pathExists(gitignorePath)) { const gitignoreContent = await fs.readFile(gitignorePath, 'utf-8'); customIgnorePatterns = gitignoreContent .split('\n') .filter(line => line.trim() && !line.startsWith('#')) .map(line => line.trim()); } const allIgnorePatterns = [...this.ignorePatterns, ...customIgnorePatterns]; // For quick scan, limit to common source file extensions const pattern = quick ? '**/*.{js,ts,jsx,tsx,py,java,c,cpp,go,rs}' : '**/*'; const files = await glob(pattern, { cwd: this.projectRoot, ignore: allIgnorePatterns, nodir: true }); return files; } isCommentLine(line, extension) { // Simple comment detection - can be enhanced const commentPatterns = { '.js': [/^\/\//, /^\/\*/, /^\*/], '.ts': [/^\/\//, /^\/\*/, /^\*/], '.jsx': [/^\/\//, /^\/\*/, /^\*/], '.tsx': [/^\/\//, /^\/\*/, /^\*/], '.py': [/^#/, /^"""/], '.java': [/^\/\//, /^\/\*/, /^\*/], '.c': [/^\/\//, /^\/\*/, /^\*/], '.cpp': [/^\/\//, /^\/\*/, /^\*/], '.go': [/^\/\//, /^\/\*/], '.rs': [/^\/\//, /^\/\*/], '.php': [/^\/\//, /^\/\*/, /^#/], '.rb': [/^#/], '.cs': [/^\/\//, /^\/\*/, /^\*/], }; const patterns = commentPatterns[extension] || []; return patterns.some(pattern => pattern.test(line)); } getLanguageFromExtension(ext) { const languageMap = { '.py': 'python', '.js': 'javascript', '.jsx': 'javascript', '.ts': 'typescript', '.tsx': 'typescript', '.java': 'java', '.cs': 'csharp', '.c': 'cpp', '.cpp': 'cpp', '.cc': 'cpp', '.cxx': 'cpp', '.h': 'cpp', '.hpp': 'cpp', '.php': 'php', '.vue': 'vue', '.rs': 'rust', '.go': 'go', '.rb': 'ruby', '.css': 'css', '.scss': 'scss', '.sass': 'scss', }; return languageMap[ext] || 'other'; } getFileType(ext) { const typeMap = { '.js': 'JavaScript', '.jsx': 'JavaScript', '.ts': 'TypeScript', '.tsx': 'TypeScript', '.py': 'Python', '.java': 'Java', '.cs': 'C#', '.c': 'C/C++', '.cpp': 'C/C++', '.h': 'C/C++', '.hpp': 'C/C++', '.php': 'PHP', '.vue': 'Vue', '.rs': 'Rust', '.go': 'Go', '.rb': 'Ruby', '.css': 'CSS', '.scss': 'SCSS', '.json': 'JSON', '.xml': 'XML', '.yaml': 'YAML', '.yml': 'YAML', '.md': 'Markdown', '.txt': 'Text', '.sh': 'Shell', '.bash': 'Shell', }; return typeMap[ext] || 'Other'; } } //# sourceMappingURL=MetricsCollector.js.map