mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
231 lines • 7.88 kB
JavaScript
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