stack-performance
Version:
A comprehensive application stack analyzer that evaluates MEAN, MERN, and other Node.js-based applications across 15 performance criteria
389 lines (312 loc) • 11.9 kB
JavaScript
const BaseAnalyzer = require('./BaseAnalyzer');
/**
* Analyzes Application Performance based on multiple algorithmic factors
* Not random - uses concrete metrics and patterns
*/
class ApplicationPerformanceAnalyzer extends BaseAnalyzer {
constructor(stackInfo, projectPath) {
super(stackInfo, projectPath, 'Application Performance');
}
async analyze() {
const factors = [];
// Factor 1: Framework Performance Characteristics (30% weight)
const frameworkScore = this.analyzeFrameworkPerformance();
factors.push({ score: frameworkScore, weight: 0.3 });
// Factor 2: Code Structure and Patterns (25% weight)
const codeStructureScore = await this.analyzeCodeStructure();
factors.push({ score: codeStructureScore, weight: 0.25 });
// Factor 3: Database Integration Efficiency (20% weight)
const dbScore = await this.analyzeDatabaseIntegration();
factors.push({ score: dbScore, weight: 0.2 });
// Factor 4: Asset and Resource Management (15% weight)
const assetScore = await this.analyzeAssetManagement();
factors.push({ score: assetScore, weight: 0.15 });
// Factor 5: Caching Implementation (10% weight)
const cachingScore = await this.analyzeCaching();
factors.push({ score: cachingScore, weight: 0.1 });
const finalScore = this.calculateWeightedScore(factors);
const details = {
frameworkPerformance: frameworkScore,
codeStructure: codeStructureScore,
databaseIntegration: dbScore,
assetManagement: assetScore,
caching: cachingScore,
stackType: this.stackInfo.type,
optimizationOpportunities: this.getOptimizationOpportunities(factors)
};
const recommendations = this.generateRecommendations(factors);
return this.createResult(finalScore, details, recommendations);
}
/**
* Analyze framework performance characteristics
* @returns {number} Performance score based on framework choice
*/
analyzeFrameworkPerformance() {
const { type, technologies } = this.stackInfo;
let score = 70; // Base score
// Framework-specific performance characteristics
if (technologies.includes('Express.js')) {
score += 15; // Express is performant and lightweight
}
if (technologies.includes('Fastify')) {
score += 20; // Fastify is highly optimized for performance
}
if (technologies.includes('Koa')) {
score += 12; // Koa is lightweight but slightly less ecosystem
}
// Frontend framework impact
if (technologies.includes('React')) {
score += 10; // Good performance with proper optimization
if (this.hasDependency('react-dom')) {
score += 5; // Server-side rendering capability
}
}
if (technologies.includes('Angular')) {
score += 8; // Good but heavier than React
}
if (technologies.includes('Vue.js')) {
score += 12; // Excellent performance characteristics
}
// Performance-oriented dependencies
if (this.hasDependency('compression')) score += 3;
if (this.hasDependency('helmet')) score += 2;
if (this.hasDependency('cors')) score += 1;
return Math.min(100, score);
}
/**
* Analyze code structure for performance patterns
* @returns {number} Score based on code structure analysis
*/
async analyzeCodeStructure() {
let score = 60; // Base score
// Find JavaScript/TypeScript files
const codeFiles = this.findFiles('**/*.{js,ts,jsx,tsx}');
if (codeFiles.length === 0) {
return 50; // No code files found
}
// Analyze code complexity
const complexity = await this.analyzeCodeComplexity(codeFiles);
// Score based on complexity metrics
if (complexity.avgLinesPerFile < 100) {
score += 15; // Good file size management
} else if (complexity.avgLinesPerFile > 500) {
score -= 10; // Large files may indicate poor structure
}
if (complexity.complexityScore < 5) {
score += 10; // Low complexity is good
} else if (complexity.complexityScore > 20) {
score -= 15; // High complexity affects performance
}
// Check for performance anti-patterns
const antiPatternDeductions = await this.checkPerformanceAntiPatterns(codeFiles);
score -= antiPatternDeductions;
// Check for performance optimization patterns
const optimizationBonus = await this.checkOptimizationPatterns(codeFiles);
score += optimizationBonus;
return Math.max(0, Math.min(100, score));
}
/**
* Check for performance anti-patterns in code
* @param {Array<string>} files - Code files to analyze
* @returns {number} Deduction points for anti-patterns
*/
async checkPerformanceAntiPatterns(files) {
let deductions = 0;
for (const file of files.slice(0, 20)) { // Limit to first 20 files for performance
const content = await this.readFile(file);
if (!content) continue;
// Synchronous operations in Node.js
if (content.includes('readFileSync') || content.includes('writeFileSync')) {
deductions += 5;
}
// Inefficient loops
if (content.match(/for\s*\([^)]*\.length[^)]*\)/g)) {
deductions += 3;
}
// Memory leaks patterns
if (content.includes('setInterval') && !content.includes('clearInterval')) {
deductions += 4;
}
// Blocking operations
if (content.includes('process.exit') || content.includes('while(true)')) {
deductions += 8;
}
}
return Math.min(30, deductions); // Cap at 30 points
}
/**
* Check for performance optimization patterns
* @param {Array<string>} files - Code files to analyze
* @returns {number} Bonus points for optimizations
*/
async checkOptimizationPatterns(files) {
let bonus = 0;
for (const file of files.slice(0, 20)) {
const content = await this.readFile(file);
if (!content) continue;
// Async/await usage (modern, performant)
if (content.includes('async') && content.includes('await')) {
bonus += 3;
}
// Promise usage
if (content.includes('Promise')) {
bonus += 2;
}
// Efficient array methods
if (content.includes('.map(') || content.includes('.filter(') || content.includes('.reduce(')) {
bonus += 2;
}
// Error handling
if (content.includes('try') && content.includes('catch')) {
bonus += 1;
}
}
return Math.min(15, bonus); // Cap at 15 points
}
/**
* Analyze database integration efficiency
* @returns {number} Database integration score
*/
async analyzeDatabaseIntegration() {
let score = 70; // Base score
if (this.hasDependency('mongoose')) {
score += 15; // Good MongoDB integration
// Check for connection pooling
const configFiles = this.findFiles('**/config/**/*.{js,ts,json}');
for (const file of configFiles) {
const content = await this.readFile(file);
if (content && content.includes('maxPoolSize')) {
score += 5;
break;
}
}
}
if (this.hasDependency('mongodb')) {
score += 12; // Native MongoDB driver
}
// Check for query optimization patterns
const modelFiles = this.findFiles('**/models/**/*.{js,ts}');
for (const file of modelFiles) {
const content = await this.readFile(file);
if (content) {
if (content.includes('index') || content.includes('Index')) {
score += 3; // Database indexing
}
if (content.includes('populate')) {
score += 2; // Efficient data population
}
}
}
return Math.min(100, score);
}
/**
* Analyze asset and resource management
* @returns {number} Asset management score
*/
async analyzeAssetManagement() {
let score = 60; // Base score
// Check for build tools
if (this.hasDependency('webpack')) {
score += 15;
// Check for webpack optimization config
if (await this.fileExists('webpack.config.js')) {
const config = await this.readFile('webpack.config.js');
if (config && config.includes('optimization')) {
score += 10;
}
}
}
if (this.hasDependency('vite')) {
score += 18; // Vite is highly optimized
}
if (this.hasDependency('parcel')) {
score += 12;
}
// Check for minification
if (this.hasDependency('terser') || this.hasDependency('uglify-js')) {
score += 8;
}
// Check for CSS optimization
if (this.hasDependency('css-loader') || this.hasDependency('mini-css-extract-plugin')) {
score += 5;
}
return Math.min(100, score);
}
/**
* Analyze caching implementation
* @returns {number} Caching score
*/
async analyzeCaching() {
let score = 50; // Base score
// Server-side caching
if (this.hasDependency('redis')) {
score += 20;
}
if (this.hasDependency('memcached')) {
score += 15;
}
if (this.hasDependency('node-cache')) {
score += 10;
}
// HTTP caching headers
const serverFiles = this.findFiles('**/server.{js,ts}');
const appFiles = this.findFiles('**/app.{js,ts}');
const routeFiles = this.findFiles('**/routes/**/*.{js,ts}');
const allServerFiles = [...serverFiles, ...appFiles, ...routeFiles];
for (const file of allServerFiles) {
const content = await this.readFile(file);
if (content) {
if (content.includes('Cache-Control') || content.includes('ETag')) {
score += 10;
break;
}
}
}
return Math.min(100, score);
}
/**
* Get optimization opportunities based on analysis
* @param {Array<Object>} factors - Analysis factors
* @returns {Array<string>} List of opportunities
*/
getOptimizationOpportunities(factors) {
const opportunities = [];
factors.forEach(factor => {
if (factor.score < 70) {
switch (factors.indexOf(factor)) {
case 0: opportunities.push('Framework optimization needed'); break;
case 1: opportunities.push('Code structure improvements required'); break;
case 2: opportunities.push('Database queries can be optimized'); break;
case 3: opportunities.push('Asset bundling and minification needed'); break;
case 4: opportunities.push('Caching strategy implementation required'); break;
}
}
});
return opportunities;
}
/**
* Generate performance improvement recommendations
* @param {Array<Object>} factors - Analysis factors
* @returns {Array<string>} Recommendations
*/
generateRecommendations(factors) {
const recommendations = [];
if (factors[0].score < 80) {
recommendations.push('Consider using a more performant framework like Fastify for better throughput');
}
if (factors[1].score < 70) {
recommendations.push('Refactor large files and reduce code complexity for better performance');
}
if (factors[2].score < 75) {
recommendations.push('Implement database connection pooling and query optimization');
}
if (factors[3].score < 70) {
recommendations.push('Set up proper build optimization with webpack or vite');
}
if (factors[4].score < 60) {
recommendations.push('Implement Redis or in-memory caching for frequently accessed data');
}
return recommendations;
}
}
module.exports = ApplicationPerformanceAnalyzer;