agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
111 lines (106 loc) • 4.12 kB
JavaScript
/**
* @file Cyclomatic complexity calculation for code quality analysis
* @description Single responsibility: Calculate McCabe cyclomatic complexity metrics
*
* This module implements McCabe's cyclomatic complexity calculation using pattern-based
* detection of decision points in JavaScript code. Cyclomatic complexity measures the
* number of linearly independent paths through a program, serving as a key indicator
* of code maintainability, testability, and potential defect density.
*
* Design rationale:
* - Pattern-based approach handles JavaScript syntax variations efficiently
* - Comprehensive decision point coverage captures all complexity contributors
* - Simple implementation maintains performance for large codebases
* - Conservative counting avoids over-penalizing modern JavaScript patterns
*/
/**
* Calculate McCabe cyclomatic complexity using decision point enumeration
*
* Technical function: Pattern-based complexity calculation with comprehensive decision point detection
*
* Implementation rationale:
* - Base complexity of 1 represents single linear execution path
* - Each decision point adds one additional independent path
* - Regex patterns efficiently identify all JavaScript decision constructs
* - Global matching ensures all occurrences contribute to complexity score
*
* Cyclomatic complexity theory:
* - M = E - N + 2P (where E=edges, N=nodes, P=connected components)
* - Simplified: M = decision_points + 1 for single function analysis
* - Each branching statement creates additional execution paths
* - Complexity correlates with testing effort and defect probability
*
* Decision point coverage:
* - Conditional statements: if, else if (each adds 1)
* - Loop constructs: while, for (each adds 1)
* - Switch cases: each case label adds 1
* - Exception handling: catch blocks add 1
* - Logical operators: && and || in conditions add 1 each
* - Ternary operators: ? : conditional expressions add 1
*
* JavaScript-specific considerations:
* - Logical operators (&&, ||) create implicit branching
* - Ternary operators provide compact conditional logic
* - Switch statements: each case increases complexity
* - Try-catch: exception paths increase complexity
* - Arrow functions and modern syntax handled through pattern matching
*
* Complexity interpretation guidelines:
* - 1-10: Simple, low risk
* - 11-20: Moderate complexity, some risk
* - 21-50: Complex, high risk
* - >50: Very complex, very high risk, consider refactoring
*
* Alternative approaches considered:
* - AST-based analysis: More accurate but higher overhead
* - Control flow graph construction: Precise but complex implementation
* - Static analysis tools: External dependencies and integration complexity
* - Pattern matching chosen for balance of accuracy and performance
*
* @param {string} content - JavaScript code content to analyze for complexity
* @returns {number} Cyclomatic complexity score (minimum 1)
* @example
* const complexity = calculateCyclomaticComplexity(`
* function process(x) {
* if (x > 0) { // +1
* while (x > 5) { // +1
* x--;
* }
* } else if (x < 0) { // +1
* x = -x;
* }
* return x;
* }
* `);
* // Returns: 4 (base 1 + 3 decision points)
*/
// Precompiled regex patterns for better performance - avoids recompiling on every function call
const DECISION_PATTERNS = [
/\bif\b/g,
/\belse\s+if\b/g,
/\bwhile\b/g,
/\bfor\b/g,
/\bcase\b/g,
/\bcatch\b/g,
/\b&&\b/g,
/\b\|\|\b/g,
/\?\s*[^:]*:/g, // ternary operator
/\bswitch\b/g
];
function calculateCyclomaticComplexity(content) {
// Start with base complexity of 1
let complexity = 1;
// Count decision points using precompiled patterns
DECISION_PATTERNS.forEach(pattern => {
// Reset regex lastIndex for global patterns
pattern.lastIndex = 0;
const matches = content.match(pattern);
if (matches) {
complexity += matches.length;
}
});
return complexity;
}
module.exports = {
calculateCyclomaticComplexity
};