agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
134 lines (123 loc) • 4.99 kB
JavaScript
/**
* @file React/Vue render issue detector
* @description Detects inefficient rendering patterns in React/Vue components
*/
/**
* Detects React/Vue rendering issues
* @param {string} content - File content
* @param {string} filePath - Path to the file
* @returns {Array} Array of rendering issues
*/
function detectReactRenderIssues(content, filePath) {
const issues = [];
const lines = content.split('\n');
const { PERFORMANCE_PATTERNS } = require('./performancePatterns');
// Check if this is a React/Vue file
const isReact = content.includes('import React') || content.includes('from \'react\'') ||
content.includes('from "react"') || /\.jsx$/.test(filePath);
const isVue = content.includes('from \'vue\'') || content.includes('from "vue"') ||
/\.vue$/.test(filePath);
if (!isReact && !isVue) return issues;
lines.forEach((line, index) => {
const lineNumber = index + 1;
const trimmedLine = line.trim();
// React-specific patterns
if (isReact) {
// Inline function props (creates new function every render)
if (/onClick\s*=\s*\{(?!\w+\s*\})\s*\(/.test(line) ||
/onChange\s*=\s*\{(?!\w+\s*\})\s*\(/.test(line) ||
/onSubmit\s*=\s*\{(?!\w+\s*\})\s*\(/.test(line)) {
const pattern = PERFORMANCE_PATTERNS['react_render_issue'];
issues.push({
type: 'react_render_issue',
severity: pattern.severity,
category: pattern.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
issue: 'inline_function_prop',
description: 'Inline function in props causes unnecessary re-renders',
summary: 'Inline function prop creates new function every render',
recommendation: 'Use useCallback hook or define function outside render',
effort: pattern.effort,
impact: pattern.impact,
estimatedSavings: '40-70% reduction in unnecessary renders'
});
}
// Object/Array literals in props
if (/\w+\s*=\s*\{\s*\{[^}]+\}\s*\}/.test(line) && !line.includes('style=')) {
const pattern = PERFORMANCE_PATTERNS['react_render_issue'];
issues.push({
type: 'react_render_issue',
severity: pattern.severity,
category: pattern.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
issue: 'inline_object_prop',
description: 'Inline object in props causes unnecessary re-renders',
summary: 'Inline object prop creates new object every render',
recommendation: 'Use useMemo hook or define object outside render',
effort: pattern.effort,
impact: pattern.impact,
estimatedSavings: '40-70% reduction in unnecessary renders'
});
}
// Missing key prop in lists
if (/\.map\s*\([^)]*\)\s*(?:=>|{)/.test(line)) {
// Check next few lines for missing key prop
let hasKey = false;
for (let j = index; j < Math.min(index + 5, lines.length); j++) {
if (/key\s*=/.test(lines[j])) {
hasKey = true;
break;
}
}
if (!hasKey && (line.includes('<') || lines[index + 1]?.includes('<'))) {
const pattern = PERFORMANCE_PATTERNS['react_render_issue'];
issues.push({
type: 'react_render_issue',
severity: 'HIGH',
category: pattern.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
issue: 'missing_key_prop',
description: 'Missing key prop in list rendering causes reconciliation issues',
summary: 'Missing key prop in mapped elements',
recommendation: 'Add unique key prop to list items',
effort: 1,
impact: pattern.impact,
estimatedSavings: 'Prevents unnecessary DOM updates'
});
}
}
}
// Vue-specific patterns
if (isVue) {
// v-for without key
if (/v-for\s*=/.test(line) && !/:key\s*=/.test(line)) {
const pattern = PERFORMANCE_PATTERNS['react_render_issue'];
issues.push({
type: 'react_render_issue',
severity: 'HIGH',
category: pattern.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
issue: 'missing_vue_key',
description: 'v-for without :key causes inefficient updates',
summary: 'Missing :key in v-for directive',
recommendation: 'Add :key with unique value to v-for elements',
effort: 1,
impact: pattern.impact,
estimatedSavings: 'Prevents unnecessary DOM updates'
});
}
}
});
return issues;
}
module.exports = {
detectReactRenderIssues
};