agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
148 lines (132 loc) • 4.98 kB
JavaScript
/**
* @file Frontend-Backend Integration Analysis orchestrator
* @description Single responsibility: Coordinate comprehensive integration mismatch detection
*
* This analyzer identifies integration issues between frontend and backend code by comparing
* API endpoint definitions with frontend API calls. It detects missing endpoints, unused
* endpoints, and route mismatches to prevent integration failures in production.
*
* Design rationale:
* - Cross-layer analysis prevents runtime integration failures
* - Route normalization enables consistent comparison across different coding styles
* - Specialized extractors handle framework-specific patterns (Express, React, etc.)
* - Comprehensive detection covers both unused and missing endpoint scenarios
*/
const fs = require('fs');
const path = require('path');
const { analyzeProjectIntegration } = require('./frontendBackendProjectAnalyzer');
const { extractBackendEndpoints } = require('./backendEndpointExtractor');
const { extractFrontendCalls } = require('./frontendApiCallExtractor');
const { detectMissingEndpoints } = require('./missingEndpointDetector');
const { detectUnusedEndpoints } = require('./unusedEndpointDetector');
/**
* Normalize route paths for consistent comparison across frontend and backend
*
* Technical function: Standardizes route formats for accurate integration analysis
*
* Implementation rationale:
* - Query parameter removal focuses comparison on route structure
* - Trailing slash normalization handles different framework conventions
* - Case normalization prevents false mismatches due to casing differences
* - Parameter replacement enables matching dynamic routes
*
* Normalization strategy:
* - Remove query parameters (?key=value) to focus on route structure
* - Remove trailing slashes except for root route
* - Convert to lowercase for case-insensitive comparison
* - Replace dynamic parameters (/users/:id) with generic placeholder
*
* @param {string} route - Route path to normalize
* @returns {string} Normalized route
*/
function normalizeRoute(route) {
if (!route || typeof route !== 'string') {
return '/';
}
// Remove query parameters
const cleanRoute = route.split('?')[0];
// Remove trailing slash
const noTrailingSlash = cleanRoute.replace(/\/$/, '') || '/';
// Convert to lowercase
const lowercase = noTrailingSlash.toLowerCase();
// Replace dynamic parameters with :param
const normalized = lowercase.replace(/\/:[^\/]+/g, '/:param');
return normalized;
}
/**
* Analyze frontend-backend integration issues
* @param {string} target - Project directory path
* @param {Object} options - Analysis options
* @returns {Object} Integration analysis results
*/
function analyzeFrontendBackend(target, options = {}) {
return analyzeProjectIntegration(target, options);
}
/**
* Extract frontend API calls from file paths
* @param {Array<string>} filePaths - Array of frontend file paths to analyze
* @returns {Array<Object>} Array of frontend API calls
*/
async function extractFrontendCallsFromFiles(filePaths) {
const calls = [];
for (const filePath of filePaths) {
try {
const content = await fs.promises.readFile(filePath, 'utf8');
const lines = content.split('\n');
lines.forEach((line, index) => {
// Look for fetch() calls
const fetchMatch = line.match(/fetch\s*\(\s*['"`]([^'"`]+)['"`]/);
if (fetchMatch) {
calls.push({
type: 'fetch',
route: fetchMatch[1],
url: fetchMatch[1],
method: extractMethodFromFetch(line),
line: index + 1,
file: filePath
});
}
// Look for axios calls
const axiosMatch = line.match(/axios\.(\w+)\s*\(\s*['"`]([^'"`]+)['"`]/);
if (axiosMatch) {
calls.push({
type: 'axios',
method: axiosMatch[1].toUpperCase(),
route: axiosMatch[2],
url: axiosMatch[2],
line: index + 1,
file: filePath
});
}
// Look for XMLHttpRequest calls
const xhrMatch = line.match(/\.open\s*\(\s*['"`](\w+)['"`]\s*,\s*['"`]([^'"`]+)['"`]/);
if (xhrMatch) {
calls.push({
type: 'xhr',
method: xhrMatch[1].toUpperCase(),
route: xhrMatch[2],
url: xhrMatch[2],
line: index + 1,
file: filePath
});
}
});
} catch (error) {
// Skip files that can't be read
}
}
return calls;
}
function extractMethodFromFetch(line) {
const methodMatch = line.match(/method\s*:\s*['"`](\w+)['"`]/);
return methodMatch ? methodMatch[1].toUpperCase() : 'GET';
}
module.exports = {
analyzeFrontendBackend,
analyzeProjectIntegration,
extractBackendEndpoints,
extractFrontendCalls,
detectMissingEndpoints,
detectUnusedEndpoints,
normalizeRoute
};