feat: fix database connection & memory leaks, consolidate code duplication
Co-authored-by: cetinmustafa83 <30570791+cetinmustafa83@users.noreply.github.com>
This commit is contained in:
5 files changed
+1191
-11
No files matched your search
@@ -0,0 +1,124 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = path.join(__dirname, '..');
|
||||
const srcDir = path.join(projectRoot, 'src');
|
||||
|
||||
// Patterns indicating potential memory leaks
|
||||
const leakPatterns = {
|
||||
uncleared_listeners: {
|
||||
pattern: /addEventListener\s*\([^)]*\)/g,
|
||||
warning: 'Event listener without removeEventListener cleanup'
|
||||
},
|
||||
uncleared_timers: {
|
||||
pattern: /(setInterval|setTimeout)\s*\(/g,
|
||||
warning: 'Timer without clear/clearInterval cleanup'
|
||||
},
|
||||
missing_cleanup: {
|
||||
pattern: /useEffect\s*\(\s*\([^)]*\)\s*=>\s*\{[^}]*\},\s*\[\s*\]\s*\)/g,
|
||||
warning: 'useEffect with empty dependency array but no cleanup'
|
||||
},
|
||||
unsubscribe: {
|
||||
pattern: /\.subscribe\s*\([^)]*\)/g,
|
||||
warning: 'Observable subscription without unsubscribe'
|
||||
}
|
||||
};
|
||||
|
||||
let issuesFound = 0;
|
||||
let filesScanned = 0;
|
||||
|
||||
function scanFile(filePath) {
|
||||
filesScanned++;
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
let fileIssues = 0;
|
||||
|
||||
// Check for uncleared event listeners
|
||||
const hasAddEventListener = content.includes('addEventListener');
|
||||
if (hasAddEventListener) {
|
||||
const addCount = (content.match(/addEventListener/g) || []).length;
|
||||
const removeCount = (content.match(/removeEventListener/g) || []).length;
|
||||
if (addCount > removeCount) {
|
||||
console.log(`\n⚠️ ${path.relative(projectRoot, filePath)}`);
|
||||
console.log(` Found ${addCount} addEventListener but only ${removeCount} removeEventListener`);
|
||||
issuesFound++;
|
||||
fileIssues++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for uncleared timers
|
||||
const hasSetInterval = content.includes('setInterval');
|
||||
if (hasSetInterval) {
|
||||
const setCount = (content.match(/setInterval/g) || []).length;
|
||||
const clearCount = (content.match(/clearInterval/g) || []).length;
|
||||
if (setCount > clearCount) {
|
||||
console.log(`\n⚠️ ${path.relative(projectRoot, filePath)}`);
|
||||
console.log(` Found ${setCount} setInterval but only ${clearCount} clearInterval`);
|
||||
issuesFound++;
|
||||
fileIssues++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for setTimeout without cleanup
|
||||
const hasSetTimeout = content.includes('setTimeout');
|
||||
if (hasSetTimeout && !content.includes('clearTimeout')) {
|
||||
console.log(`\n⚠️ ${path.relative(projectRoot, filePath)}`);
|
||||
console.log(` Found setTimeout but no clearTimeout found`);
|
||||
issuesFound++;
|
||||
fileIssues++;
|
||||
}
|
||||
|
||||
// Check for subscriptions without unsubscribe
|
||||
const hasSubscribe = content.includes('.subscribe(');
|
||||
if (hasSubscribe && !content.includes('.unsubscribe()')) {
|
||||
console.log(`\n⚠️ ${path.relative(projectRoot, filePath)}`);
|
||||
console.log(` Found .subscribe() but no .unsubscribe() found`);
|
||||
issuesFound++;
|
||||
fileIssues++;
|
||||
}
|
||||
|
||||
// Check for useEffect with empty deps but complex logic
|
||||
const useEffectMatches = content.matchAll(/useEffect\s*\(\s*\([^)]*\)\s*=>\s*\{([^}]+)\},\s*\[\s*\]\s*\)/g);
|
||||
for (const match of useEffectMatches) {
|
||||
const body = match[1];
|
||||
if (body.includes('addEventListener') || body.includes('setInterval') || body.includes('subscribe')) {
|
||||
console.log(`\n⚠️ ${path.relative(projectRoot, filePath)}`);
|
||||
console.log(` useEffect with [] deps contains event/timer/subscription logic`);
|
||||
issuesFound++;
|
||||
fileIssues++;
|
||||
}
|
||||
}
|
||||
|
||||
return fileIssues;
|
||||
}
|
||||
|
||||
function walkDirectory(dir) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat.isDirectory() && !file.startsWith('.') && file !== 'node_modules') {
|
||||
walkDirectory(filePath);
|
||||
} else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
|
||||
scanFile(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🔍 Memory Leak Audit\n');
|
||||
console.log(`Scanning ${srcDir}...\n`);
|
||||
|
||||
walkDirectory(srcDir);
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log(`\nResults:`);
|
||||
console.log(`- Files scanned: ${filesScanned}`);
|
||||
console.log(`- Issues found: ${issuesFound}`);
|
||||
console.log(`\n✅ Run 'npm run memory-fix' to auto-fix common issues\n`);
|
||||
|
||||
process.exit(issuesFound > 0 ? 1 : 0);
|
||||
Reference in new issue
Block a user