mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
215 lines • 8.57 kB
JavaScript
/**
* File System Performance Analyzer
* Checks for file system related performance issues
*/
import fs from 'fs-extra';
import * as path from 'path';
import { glob } from 'glob';
export class FileSystemAnalyzer {
projectRoot;
constructor(projectRoot) {
this.projectRoot = projectRoot;
}
async analyze() {
const issues = [];
await Promise.all([
this.checkPackageJsonSize(issues),
this.checkNodeModulesSize(issues),
this.checkFileCount(issues),
this.checkLargeFiles(issues),
this.checkSyncOperations(issues)
]);
return issues;
}
async checkPackageJsonSize(issues) {
const packageJsonPath = path.join(this.projectRoot, 'package.json');
if (await fs.pathExists(packageJsonPath)) {
try {
const packageJson = await fs.readJson(packageJsonPath);
const deps = Object.keys(packageJson.dependencies || {});
const devDeps = Object.keys(packageJson.devDependencies || {});
const totalDeps = deps.length + devDeps.length;
if (totalDeps > 100) {
issues.push({
file: 'package.json',
type: 'dependency_bloat',
message: `High number of dependencies (${totalDeps}). Consider reducing to improve install time.`,
impact: 'medium'
});
}
if (totalDeps > 200) {
issues.push({
file: 'package.json',
type: 'excessive_dependencies',
message: `Excessive dependencies (${totalDeps}). This will significantly slow down npm install.`,
impact: 'high'
});
}
}
catch (error) {
// Ignore JSON parsing errors
}
}
}
async checkNodeModulesSize(issues) {
const nodeModulesPath = path.join(this.projectRoot, 'node_modules');
if (await fs.pathExists(nodeModulesPath)) {
try {
const size = await this.getDirectorySize(nodeModulesPath);
const sizeMB = size / (1024 * 1024);
if (sizeMB > 500) {
issues.push({
file: 'node_modules',
type: 'large_node_modules',
message: `node_modules is very large (${sizeMB.toFixed(0)}MB). Consider using npm ci in production.`,
impact: 'medium'
});
}
if (sizeMB > 1000) {
issues.push({
file: 'node_modules',
type: 'huge_node_modules',
message: `node_modules is extremely large (${sizeMB.toFixed(0)}MB). Review dependencies.`,
impact: 'high'
});
}
}
catch (error) {
// Skip if can't calculate size
}
}
}
async checkFileCount(issues) {
try {
const files = await glob('**/*', {
cwd: this.projectRoot,
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
});
if (files.length > 1000) {
issues.push({
file: 'project',
type: 'high_file_count',
message: `Project has many files (${files.length}). Consider organizing or cleaning up.`,
impact: 'low'
});
}
if (files.length > 5000) {
issues.push({
file: 'project',
type: 'excessive_file_count',
message: `Project has excessive files (${files.length}). This may slow down tools and IDEs.`,
impact: 'medium'
});
}
}
catch (error) {
// Skip if glob fails
}
}
async checkLargeFiles(issues) {
try {
const files = await glob('**/*', {
cwd: this.projectRoot,
ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
});
for (const file of files) {
const filePath = path.join(this.projectRoot, file);
try {
const stats = await fs.stat(filePath);
if (stats.isFile()) {
const sizeMB = stats.size / (1024 * 1024);
if (sizeMB > 10) {
issues.push({
file,
type: 'large_file',
message: `File is large (${sizeMB.toFixed(1)}MB). Consider compression or chunking.`,
impact: 'medium'
});
}
if (sizeMB > 50) {
issues.push({
file,
type: 'huge_file',
message: `File is extremely large (${sizeMB.toFixed(1)}MB). This will impact performance.`,
impact: 'high'
});
}
}
}
catch (error) {
// Skip files that can't be read
}
}
}
catch (error) {
// Skip if glob fails
}
}
async checkSyncOperations(issues) {
// Check for synchronous operations in JS/TS files
const syncPatterns = [
{ pattern: /\.readFileSync\(/, type: 'sync_file_read', message: 'Use async readFile instead of readFileSync' },
{ pattern: /\.writeFileSync\(/, type: 'sync_file_write', message: 'Use async writeFile instead of writeFileSync' },
{ pattern: /\.execSync\(/, type: 'sync_exec', message: 'Use async exec instead of execSync' },
{ pattern: /JSON\.parse.*readFileSync/, type: 'sync_json_read', message: 'Use async JSON file reading' }
];
try {
const files = await glob('**/*.{ts,js}', {
cwd: this.projectRoot,
ignore: ['node_modules/**', '.git/**', 'dist/**']
});
for (const file of files) {
const filePath = path.join(this.projectRoot, file);
try {
const content = await fs.readFile(filePath, 'utf-8');
const lines = content.split('\n');
lines.forEach((line, index) => {
for (const syncPattern of syncPatterns) {
if (syncPattern.pattern.test(line)) {
issues.push({
file: `${file}:${index + 1}`,
type: syncPattern.type,
message: syncPattern.message,
impact: 'medium'
});
}
}
});
}
catch (error) {
// Skip files that can't be read
}
}
}
catch (error) {
// Skip if glob fails
}
}
async getDirectorySize(dirPath, depth = 0) {
let totalSize = 0;
// Limit recursion depth to prevent hanging
if (depth > 3) {
return 0;
}
try {
const items = await fs.readdir(dirPath);
// Limit items processed to prevent hanging on large directories
const itemsToProcess = items.slice(0, 100);
for (const item of itemsToProcess) {
const itemPath = path.join(dirPath, item);
const stats = await fs.stat(itemPath);
if (stats.isDirectory()) {
totalSize += await this.getDirectorySize(itemPath, depth + 1);
}
else {
totalSize += stats.size;
}
}
}
catch (error) {
// Return what we have so far
}
return totalSize;
}
}
//# sourceMappingURL=FileSystemAnalyzer.js.map