agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
100 lines (93 loc) • 4.32 kB
JavaScript
/**
* @file Calculate cyclomatic complexity score for code blocks
* @description Single responsibility: Compute complexity metric for WET code analysis
*
* This utility calculates cyclomatic complexity for code blocks using a weighted
* approach that considers control flow statements, nested functions, and modern
* JavaScript patterns like callbacks and promises. The complexity score helps
* identify code blocks that may be difficult to maintain or test.
*
* Design rationale:
* - Base complexity of 1 follows standard cyclomatic complexity principles
* - Pattern-based approach handles various JavaScript control flow constructs
* - Weighted scoring for callbacks reflects their impact on cognitive complexity
* - Regex matching provides efficient detection across formatted code styles
*/
/**
* Calculate weighted cyclomatic complexity score for a code block
*
* Technical function: Analyzes code content to compute complexity metric based on control flow
*
* Implementation rationale:
* - Base complexity of 1 represents linear execution path (standard practice)
* - Regex patterns efficiently detect control flow keywords across various formatting
* - Incremental complexity addition follows traditional cyclomatic complexity principles
* - Modern JavaScript patterns (callbacks, promises) receive weighted scoring
*
* Complexity calculation algorithm:
* - Base complexity: 1 (single execution path)
* - Control flow statements: +1 each (if, for, while, switch, case, catch)
* - Nested functions: +1 each (beyond first function)
* - Callbacks/promises: +0.5 each (reflect cognitive overhead, not branching)
*
* Control flow pattern detection:
* - if statements: Conditional branching increases execution paths
* - else if: Additional conditional branches
* - Loops (for/while): Iterative complexity and potential branching
* - Switch/case: Multiple execution paths based on value
* - Exception handling (catch): Error handling complexity
*
* Modern JavaScript considerations:
* - Nested functions add cognitive complexity for understanding scope
* - Callbacks and arrow functions increase maintenance complexity
* - Promise chains represent asynchronous complexity without traditional branching
* - Weighted scoring (0.5) acknowledges cognitive load without full branching complexity
*
* Regex pattern analysis:
* - Word boundaries (\b) ensure matching actual keywords, not substrings
* - Whitespace patterns (\s*) accommodate various formatting styles
* - Global flags (g) count all occurrences within the code block
* - Escaped parentheses match function call syntax specifically
*
* Alternative approaches considered:
* - AST-based complexity: Rejected for performance in pattern detection context
* - Simple line counting: Rejected as inadequate for true complexity measurement
* - McCabe complexity only: Rejected as missing modern JavaScript patterns
*
* @param {Object} node - AST node representing the code block (used for future enhancements)
* @param {string} content - String content of the code block to analyze
* @returns {number} Complexity score (integer, minimum 1)
* @example
* // For: if (x) { for (let i = 0; i < arr.length; i++) { ... } }
* // Returns: 3 (base=1 + if=1 + for=1)
*
* // For: items.map(x => x.process()).filter(x => x.valid)
* // Returns: 2 (base=1 + 2 callbacks * 0.5 = rounded to 2)
*/
function calculateComplexity(node, content) {
let complexity = 1; // Base complexity
// Count control flow statements
const controlFlowPatterns = [
/\bif\s*\(/g,
/\belse\s+if\s*\(/g,
/\bfor\s*\(/g,
/\bwhile\s*\(/g,
/\bswitch\s*\(/g,
/\bcase\s+/g,
/\bcatch\s*\(/g
];
controlFlowPatterns.forEach(pattern => {
const matches = content.match(pattern);
if (matches) complexity += matches.length;
});
// Add complexity for nested functions
const nestedFunctions = content.match(/function\s*\w*\s*\(/g);
if (nestedFunctions && nestedFunctions.length > 1) {
complexity += nestedFunctions.length - 1;
}
// Add complexity for callbacks and promises
const callbacks = content.match(/\.\w+\s*\([^)]*=>/g);
if (callbacks) complexity += callbacks.length * 0.5;
return Math.round(complexity);
}
module.exports = calculateComplexity;