UNPKG

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
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;