agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
140 lines (119 loc) • 4.4 kB
JavaScript
/**
* @file AST-based code analyzer
* @description Uses AST parsing for accurate code analysis
*/
/**
* Count real exports using AST or fallback to regex
* @param {string} content - Code content
* @returns {number} Number of real exports
*/
function countRealExports(content) {
try {
// Try using acorn for JavaScript parsing
const { parseAST } = require('../../utils/astHelpers');
const ast = parseAST(content, {
allowImportExportEverywhere: true
});
let exportCount = 0;
// Walk the AST to count exports
function walk(node) {
if (!node) return;
if (node.type === 'ExportNamedDeclaration' ||
node.type === 'ExportDefaultDeclaration' ||
node.type === 'ExportAllDeclaration') {
exportCount++;
}
// Recursively walk child nodes
for (const key in node) {
if (node[key] && typeof node[key] === 'object') {
if (Array.isArray(node[key])) {
node[key].forEach(walk);
} else if (node[key].type) {
walk(node[key]);
}
}
}
}
walk(ast);
return exportCount;
} catch (error) {
// Fallback to regex if parsing fails - count all types of exports
const exportMatches = content.match(/export\s+(default\s+|const\s+|let\s+|var\s+|function\s+|class\s+|async\s+function\s+|\{)/g) || [];
return exportMatches.length;
}
}
/**
* Count real functions using AST or fallback to regex
* @param {string} content - Code content
* @returns {number} Number of real functions
*/
function countRealFunctions(content) {
try {
const acorn = require('acorn');
const ast = acorn.parse(content, {
sourceType: 'module',
ecmaVersion: 'latest',
allowReturnOutsideFunction: true,
allowImportExportEverywhere: true
});
let functionCount = 0;
let inCallExpression = false;
function walk(node, parent) {
if (!node) return;
// Track if we're inside a call expression (like forEach, map, filter)
const wasInCallExpression = inCallExpression;
if (node.type === 'CallExpression') {
inCallExpression = true;
}
// Count top-level and meaningful functions, not simple callbacks
if (node.type === 'FunctionDeclaration') {
functionCount++;
} else if ((node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression')) {
// Skip counting if this is a callback to common array methods
const isArrayCallback = parent && parent.type === 'CallExpression' &&
parent.callee && parent.callee.property &&
['forEach', 'map', 'filter', 'reduce', 'find', 'some', 'every', 'sort'].includes(parent.callee.property.name);
// Skip counting if this is a simple one-liner arrow function in array methods
const isSimpleCallback = node.type === 'ArrowFunctionExpression' &&
!node.body.body && // No block body (single expression)
inCallExpression;
if (!isArrayCallback && !isSimpleCallback) {
functionCount++;
}
}
// Count method definitions (e.g., in classes)
if (node.type === 'MethodDefinition') {
functionCount++;
}
// Count function properties in object expressions (like module.exports object)
if (node.type === 'Property' && node.value &&
(node.value.type === 'FunctionExpression' ||
node.value.type === 'ArrowFunctionExpression')) {
// Only count if not in a call expression context
if (!inCallExpression) {
functionCount++;
}
}
for (const key in node) {
if (node[key] && typeof node[key] === 'object') {
if (Array.isArray(node[key])) {
node[key].forEach(child => walk(child, node));
} else if (node[key].type) {
walk(node[key], node);
}
}
}
inCallExpression = wasInCallExpression;
}
walk(ast, null);
return functionCount;
} catch (error) {
// Fallback to regex
const functionMatches = content.match(/function\s+\w+|const\s+\w+\s*=\s*\(|let\s+\w+\s*=\s*\(|var\s+\w+\s*=\s*\(/g) || [];
return functionMatches.length;
}
}
module.exports = {
countRealExports,
countRealFunctions
};