UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

149 lines (129 loc) 4.68 kB
/** * @file AST-based promise in loop detector * @description Detects promise creation in loops using AST analysis */ const { parseToAST, isInsideLoop, getNodeLine, getNodeSource, walk } = require('./astParser'); const { PERFORMANCE_PATTERNS } = require('../performancePatterns'); /** * Detect promise creation in loops using AST * @param {string} content - File content * @param {string} filePath - File path * @returns {Array} Array of promise in loop issues */ function detectPromiseInLoopsAST(content, filePath) { const issues = []; const ast = parseToAST(content, filePath); if (!ast) return []; const pattern = PERFORMANCE_PATTERNS['promise_in_loop']; // Track if we're already using Promise.all or similar const promiseAllUsage = new Set(); walk.simple(ast, { CallExpression(node) { // Check for Promise.all, Promise.allSettled, etc. if (node.callee.type === 'MemberExpression' && node.callee.object.name === 'Promise' && ['all', 'allSettled', 'race'].includes(node.callee.property.name)) { // Mark this location as using Promise.all promiseAllUsage.add(getNodeLine(node)); } } }); // Find promise creation patterns walk.simple(ast, { NewExpression(node) { // new Promise(...) if (node.callee.name === 'Promise') { checkPromiseInLoop(node); } }, CallExpression(node) { // Promise.resolve/reject if (node.callee.type === 'MemberExpression' && node.callee.object.name === 'Promise' && ['resolve', 'reject'].includes(node.callee.property.name)) { checkPromiseInLoop(node); } // fetch() calls if (node.callee.name === 'fetch') { checkPromiseInLoop(node); } // axios calls if (node.callee.type === 'MemberExpression' && node.callee.object.name === 'axios' && ['get', 'post', 'put', 'delete', 'patch', 'request'].includes(node.callee.property.name)) { checkPromiseInLoop(node); } // .then() chains if (node.callee.type === 'MemberExpression' && node.callee.property.name === 'then') { checkPromiseInLoop(node); } } }); function checkPromiseInLoop(node) { const loop = isInsideLoop(ast, node); if (!loop) return; const line = getNodeLine(node); // Check if this line is near a Promise.all usage const hasPromiseAll = Array.from(promiseAllUsage).some( promiseAllLine => Math.abs(promiseAllLine - line) <= 5 ); if (!hasPromiseAll) { const loopType = getLoopType(loop); const promiseType = getPromiseType(node); issues.push({ type: 'promise_in_loop', severity: pattern.severity, category: pattern.category, location: `${filePath}:${line}`, line: line, code: getNodeSource(content, node).trim(), loopType: loopType, promiseType: promiseType, description: `Creating ${promiseType} in ${loopType} can cause memory and performance issues`, summary: `${promiseType} creation inside ${loopType}`, recommendation: 'Collect promises in array and use Promise.all() for concurrent execution', effort: pattern.effort, impact: pattern.impact, estimatedSavings: '30-50% memory and performance improvement' }); } } function getLoopType(loopNode) { if (loopNode.type === 'ForStatement') return 'for loop'; if (loopNode.type === 'WhileStatement') return 'while loop'; if (loopNode.type === 'DoWhileStatement') return 'do-while loop'; if (loopNode.type === 'ForInStatement') return 'for-in loop'; if (loopNode.type === 'ForOfStatement') return 'for-of loop'; if (loopNode.type === 'CallExpression') { return `${loopNode.callee.property.name} loop`; } return 'loop'; } function getPromiseType(node) { if (node.type === 'NewExpression' && node.callee.name === 'Promise') { return 'new Promise'; } if (node.type === 'CallExpression') { if (node.callee.type === 'MemberExpression') { if (node.callee.object.name === 'Promise') { return `Promise.${node.callee.property.name}`; } if (node.callee.object.name === 'axios') { return `axios.${node.callee.property.name}`; } if (node.callee.property.name === 'then') { return 'promise chain'; } } if (node.callee.name === 'fetch') { return 'fetch'; } } return 'promise'; } return issues; } module.exports = { detectPromiseInLoopsAST };