UNPKG

smartui-migration-tool

Version:

Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI

695 lines (600 loc) • 24.6 kB
const { Command, Flags } = require('@oclif/core'); const chalk = require('chalk'); const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); class AICodeAnalyzer extends Command { static description = 'AI-powered code analysis with machine learning insights and predictions'; static flags = { path: Flags.string({ char: 'p', description: 'Path to analyze (default: current directory)', default: process.cwd() }), include: Flags.string({ char: 'i', description: 'File patterns to include (comma-separated)', default: '**/*.{js,ts,jsx,tsx,py,java,cs}' }), exclude: Flags.string({ char: 'e', description: 'File patterns to exclude (comma-separated)', default: 'node_modules/**,dist/**,build/**,*.min.js' }), analysis: Flags.string({ char: 'a', description: 'Type of analysis to perform', options: ['performance', 'security', 'quality', 'maintainability', 'all'], default: 'all' }), model: Flags.string({ char: 'm', description: 'AI model to use for analysis', options: ['gpt-4', 'claude-3', 'local', 'ensemble'], default: 'ensemble' }), confidence: Flags.integer({ char: 'c', description: 'Minimum confidence threshold (0-100)', default: 70 }), output: Flags.string({ char: 'o', description: 'Output file for analysis results', default: 'ai-analysis.json' }), verbose: Flags.boolean({ char: 'v', description: 'Enable verbose output', default: false }), interactive: Flags.boolean({ char: 'I', description: 'Enable interactive mode for detailed analysis', default: false }) }; async run() { const { flags } = await this.parse(AICodeAnalyzer); console.log(chalk.blue.bold('\nšŸ¤– AI-Powered Code Analyzer')); console.log(chalk.gray(`Analyzing code with ${flags.model} model for ${flags.analysis} analysis...\n`)); try { // Create AI analyzer const analyzer = this.createAIAnalyzer(flags.model); // Find files to analyze const files = await this.findFiles(flags); // Perform AI analysis const results = await this.performAIAnalysis(files, flags, analyzer); // Display results this.displayResults(results, flags.verbose); // Save results await this.saveResults(results, flags.output); console.log(chalk.green(`\nāœ… Analysis results saved to: ${flags.output}`)); // Interactive mode if (flags.interactive) { await this.startInteractiveMode(results, analyzer); } } catch (error) { console.error(chalk.red(`\nāŒ Error during AI analysis: ${error.message}`)); this.exit(1); } } createAIAnalyzer(model) { return { // Performance Analysis analyzePerformance: async (code, context) => { const insights = []; // Detect performance anti-patterns if (code.includes('for (let i = 0; i < array.length; i++)')) { insights.push({ type: 'performance', severity: 'medium', confidence: 85, issue: 'Inefficient loop condition', description: 'Loop condition is evaluated on every iteration', suggestion: 'Cache array.length in a variable before the loop', impact: 'Reduces unnecessary property access', code: 'for (let i = 0, len = array.length; i < len; i++)' }); } // Detect memory leaks if (code.includes('addEventListener') && !code.includes('removeEventListener')) { insights.push({ type: 'performance', severity: 'high', confidence: 90, issue: 'Potential memory leak', description: 'Event listeners added but not removed', suggestion: 'Ensure event listeners are properly removed in cleanup', impact: 'Prevents memory leaks in long-running applications' }); } // Detect inefficient DOM queries if (code.includes('document.getElementById') && code.includes('for')) { insights.push({ type: 'performance', severity: 'medium', confidence: 80, issue: 'Inefficient DOM queries in loop', description: 'DOM queries inside loops can be expensive', suggestion: 'Cache DOM elements outside the loop', impact: 'Reduces DOM traversal overhead' }); } return insights; }, // Security Analysis analyzeSecurity: async (code, context) => { const insights = []; // Detect SQL injection vulnerabilities if (code.includes('query') && code.includes('+') && code.includes('userInput')) { insights.push({ type: 'security', severity: 'critical', confidence: 95, issue: 'Potential SQL injection', description: 'User input directly concatenated into query', suggestion: 'Use parameterized queries or prepared statements', impact: 'Prevents SQL injection attacks' }); } // Detect XSS vulnerabilities if (code.includes('innerHTML') && code.includes('userInput')) { insights.push({ type: 'security', severity: 'high', confidence: 90, issue: 'Potential XSS vulnerability', description: 'User input directly inserted into innerHTML', suggestion: 'Use textContent or sanitize input before insertion', impact: 'Prevents cross-site scripting attacks' }); } // Detect hardcoded secrets if (code.includes('password') && code.includes('=') && code.includes('"')) { insights.push({ type: 'security', severity: 'high', confidence: 85, issue: 'Hardcoded credentials', description: 'Credentials appear to be hardcoded in source', suggestion: 'Use environment variables or secure credential storage', impact: 'Prevents credential exposure in source code' }); } return insights; }, // Code Quality Analysis analyzeQuality: async (code, context) => { const insights = []; // Detect code complexity const complexity = this.calculateComplexity(code); if (complexity > 10) { insights.push({ type: 'quality', severity: 'medium', confidence: 80, issue: 'High cyclomatic complexity', description: `Function has complexity of ${complexity}`, suggestion: 'Break down into smaller, more focused functions', impact: 'Improves readability and maintainability' }); } // Detect long functions const lines = code.split('\n').length; if (lines > 50) { insights.push({ type: 'quality', severity: 'low', confidence: 75, issue: 'Long function', description: `Function has ${lines} lines`, suggestion: 'Consider breaking into smaller functions', impact: 'Improves readability and testability' }); } // Detect duplicate code if (this.detectDuplicates(code)) { insights.push({ type: 'quality', severity: 'medium', confidence: 85, issue: 'Duplicate code detected', description: 'Similar code blocks found', suggestion: 'Extract common functionality into reusable functions', impact: 'Reduces maintenance overhead and improves consistency' }); } return insights; }, // Maintainability Analysis analyzeMaintainability: async (code, context) => { const insights = []; // Detect missing documentation if (!code.includes('/**') && !code.includes('//') && code.includes('function')) { insights.push({ type: 'maintainability', severity: 'low', confidence: 70, issue: 'Missing documentation', description: 'Functions lack proper documentation', suggestion: 'Add JSDoc comments for better understanding', impact: 'Improves code understanding and maintenance' }); } // Detect magic numbers if (/\d{3,}/.test(code)) { insights.push({ type: 'maintainability', severity: 'low', confidence: 75, issue: 'Magic numbers detected', description: 'Large numbers without clear meaning', suggestion: 'Use named constants for better readability', impact: 'Improves code clarity and maintainability' }); } // Detect inconsistent naming if (this.detectInconsistentNaming(code)) { insights.push({ type: 'maintainability', severity: 'low', confidence: 80, issue: 'Inconsistent naming convention', description: 'Mixed naming styles detected', suggestion: 'Follow consistent naming conventions', impact: 'Improves code consistency and readability' }); } return insights; }, // AI-Powered Predictions generatePredictions: async (code, context) => { const predictions = []; // Predict potential bugs if (code.includes('async') && code.includes('await') && !code.includes('try')) { predictions.push({ type: 'bug_prediction', confidence: 85, prediction: 'Potential unhandled promise rejection', description: 'Async function without error handling', probability: 0.75, impact: 'high' }); } // Predict performance issues if (code.includes('forEach') && code.includes('await')) { predictions.push({ type: 'performance_prediction', confidence: 90, prediction: 'Inefficient async iteration', description: 'Sequential await in forEach instead of parallel execution', probability: 0.85, impact: 'medium' }); } // Predict maintenance issues if (code.includes('any') && code.includes('TypeScript')) { predictions.push({ type: 'maintainability_prediction', confidence: 80, prediction: 'Type safety issues', description: 'Use of any type reduces TypeScript benefits', probability: 0.70, impact: 'medium' }); } return predictions; }, // Code Generation Suggestions generateCodeSuggestions: async (code, context) => { const suggestions = []; // Suggest modern JavaScript features if (code.includes('function(') && !code.includes('=>')) { suggestions.push({ type: 'modernization', confidence: 85, suggestion: 'Convert to arrow function', description: 'Arrow functions provide cleaner syntax', code: code.replace(/function\s*\([^)]*\)\s*{/, '($1) => {'), impact: 'improves_readability' }); } // Suggest async/await if (code.includes('.then(') && code.includes('.catch(')) { suggestions.push({ type: 'modernization', confidence: 90, suggestion: 'Convert to async/await', description: 'Async/await provides cleaner promise handling', code: this.convertToAsyncAwait(code), impact: 'improves_readability' }); } // Suggest destructuring if (code.includes('obj.') && code.includes('obj.')) { suggestions.push({ type: 'modernization', confidence: 80, suggestion: 'Use destructuring', description: 'Destructuring provides cleaner object access', code: this.suggestDestructuring(code), impact: 'improves_readability' }); } return suggestions; } }; } async performAIAnalysis(files, flags, analyzer) { const results = { timestamp: new Date().toISOString(), model: flags.model, analysis: flags.analysis, confidence: flags.confidence, files: [], insights: [], predictions: [], suggestions: [], summary: {} }; for (const file of files) { try { const content = await fs.readFile(file.path, 'utf8'); const context = { filename: file.path, language: this.detectLanguage(file.path), framework: this.detectFramework(content), size: content.length }; const fileResults = { file: file.path, context, insights: [], predictions: [], suggestions: [] }; // Perform analysis based on flags if (flags.analysis === 'all' || flags.analysis === 'performance') { const performanceInsights = await analyzer.analyzePerformance(content, context); fileResults.insights.push(...performanceInsights); } if (flags.analysis === 'all' || flags.analysis === 'security') { const securityInsights = await analyzer.analyzeSecurity(content, context); fileResults.insights.push(...securityInsights); } if (flags.analysis === 'all' || flags.analysis === 'quality') { const qualityInsights = await analyzer.analyzeQuality(content, context); fileResults.insights.push(...qualityInsights); } if (flags.analysis === 'all' || flags.analysis === 'maintainability') { const maintainabilityInsights = await analyzer.analyzeMaintainability(content, context); fileResults.insights.push(...maintainabilityInsights); } // Generate predictions const predictions = await analyzer.generatePredictions(content, context); fileResults.predictions = predictions; // Generate suggestions const suggestions = await analyzer.generateCodeSuggestions(content, context); fileResults.suggestions = suggestions; // Filter by confidence threshold fileResults.insights = fileResults.insights.filter(insight => insight.confidence >= flags.confidence); fileResults.predictions = fileResults.predictions.filter(prediction => prediction.confidence >= flags.confidence); fileResults.suggestions = fileResults.suggestions.filter(suggestion => suggestion.confidence >= flags.confidence); results.files.push(fileResults); results.insights.push(...fileResults.insights); results.predictions.push(...fileResults.predictions); results.suggestions.push(...fileResults.suggestions); } catch (error) { results.files.push({ file: file.path, error: error.message }); } } // Generate summary results.summary = this.generateSummary(results); return results; } async findFiles(flags) { const includePatterns = flags.include.split(','); const excludePatterns = flags.exclude.split(','); const files = []; for (const pattern of includePatterns) { const matches = glob.sync(pattern, { cwd: flags.path, absolute: true, ignore: excludePatterns }); files.push(...matches.map(file => ({ path: file }))); } return files; } detectLanguage(filePath) { const ext = path.extname(filePath).toLowerCase(); const languageMap = { '.js': 'javascript', '.ts': 'typescript', '.jsx': 'javascript', '.tsx': 'typescript', '.py': 'python', '.java': 'java', '.cs': 'csharp' }; return languageMap[ext] || 'unknown'; } detectFramework(content) { if (content.includes('react') || content.includes('React')) return 'react'; if (content.includes('angular') || content.includes('Angular')) return 'angular'; if (content.includes('vue') || content.includes('Vue')) return 'vue'; if (content.includes('cypress') || content.includes('Cypress')) return 'cypress'; if (content.includes('playwright') || content.includes('Playwright')) return 'playwright'; return 'unknown'; } calculateComplexity(code) { // Simple cyclomatic complexity calculation const complexityKeywords = ['if', 'else', 'for', 'while', 'switch', 'case', 'catch', '&&', '||', '?']; let complexity = 1; // Base complexity for (const keyword of complexityKeywords) { const regex = new RegExp(`\\b${keyword}\\b`, 'g'); const matches = code.match(regex); if (matches) { complexity += matches.length; } } return complexity; } detectDuplicates(code) { // Simple duplicate detection const lines = code.split('\n'); const lineCounts = {}; for (const line of lines) { const trimmed = line.trim(); if (trimmed.length > 10) { // Only consider substantial lines lineCounts[trimmed] = (lineCounts[trimmed] || 0) + 1; } } return Object.values(lineCounts).some(count => count > 2); } detectInconsistentNaming(code) { const camelCase = /[a-z][a-zA-Z0-9]*/g; const snakeCase = /[a-z][a-z0-9_]*/g; const pascalCase = /[A-Z][a-zA-Z0-9]*/g; const camelMatches = code.match(camelCase) || []; const snakeMatches = code.match(snakeCase) || []; const pascalMatches = code.match(pascalCase) || []; return camelMatches.length > 0 && snakeMatches.length > 0 && pascalMatches.length > 0; } convertToAsyncAwait(code) { // Simple conversion example return code.replace(/\.then\(([^)]+)\)\.catch\(([^)]+)\)/g, 'try { $1 } catch (error) { $2 }'); } suggestDestructuring(code) { // Simple destructuring suggestion return code.replace(/const\s+(\w+)\s*=\s*(\w+)\.(\w+);/g, 'const { $3: $1 } = $2;'); } generateSummary(results) { const totalFiles = results.files.length; const filesWithIssues = results.files.filter(f => f.insights && f.insights.length > 0).length; const totalInsights = results.insights.length; const totalPredictions = results.predictions.length; const totalSuggestions = results.suggestions.length; const severityCounts = { critical: results.insights.filter(i => i.severity === 'critical').length, high: results.insights.filter(i => i.severity === 'high').length, medium: results.insights.filter(i => i.severity === 'medium').length, low: results.insights.filter(i => i.severity === 'low').length }; return { totalFiles, filesWithIssues, totalInsights, totalPredictions, totalSuggestions, severityCounts, averageConfidence: totalInsights > 0 ? (results.insights.reduce((sum, i) => sum + i.confidence, 0) / totalInsights).toFixed(2) : 0 }; } async saveResults(results, outputFile) { await fs.writeJson(outputFile, results, { spaces: 2 }); } async startInteractiveMode(results, analyzer) { console.log(chalk.blue.bold('\nšŸ” Interactive Analysis Mode')); console.log(chalk.gray('Ask questions about your code analysis...\n')); // Simulate interactive questions const questions = [ 'What are the most critical issues in my code?', 'How can I improve performance?', 'What security vulnerabilities should I address first?', 'Are there any code quality improvements I can make?' ]; for (const question of questions) { console.log(chalk.cyan(`\nā“ ${question}`)); const answer = this.generateAnswer(question, results); console.log(chalk.white(`šŸ’” ${answer}`)); } } generateAnswer(question, results) { if (question.includes('critical')) { const criticalIssues = results.insights.filter(i => i.severity === 'critical'); return criticalIssues.length > 0 ? `Found ${criticalIssues.length} critical issues. Focus on: ${criticalIssues[0].issue}` : 'No critical issues found. Your code looks good!'; } if (question.includes('performance')) { const perfIssues = results.insights.filter(i => i.type === 'performance'); return perfIssues.length > 0 ? `Found ${perfIssues.length} performance issues. Consider: ${perfIssues[0].suggestion}` : 'No performance issues detected.'; } if (question.includes('security')) { const secIssues = results.insights.filter(i => i.type === 'security'); return secIssues.length > 0 ? `Found ${secIssues.length} security issues. Address: ${secIssues[0].issue}` : 'No security vulnerabilities detected.'; } if (question.includes('quality')) { const qualIssues = results.insights.filter(i => i.type === 'quality'); return qualIssues.length > 0 ? `Found ${qualIssues.length} quality issues. Improve: ${qualIssues[0].suggestion}` : 'Code quality looks good!'; } return 'Analysis complete. Review the detailed results for specific recommendations.'; } displayResults(results, verbose) { console.log(chalk.green.bold('\nšŸ¤– AI Analysis Results')); console.log(chalk.gray('=' * 50)); // Summary console.log(chalk.blue.bold('\nšŸ“Š Summary:')); console.log(` Total files analyzed: ${results.summary.totalFiles}`); console.log(` Files with issues: ${results.summary.filesWithIssues}`); console.log(` Total insights: ${results.summary.totalInsights}`); console.log(` Total predictions: ${results.summary.totalPredictions}`); console.log(` Total suggestions: ${results.summary.totalSuggestions}`); console.log(` Average confidence: ${results.summary.averageConfidence}%`); // Severity breakdown console.log(chalk.blue.bold('\nāš ļø Issues by Severity:')); console.log(` Critical: ${chalk.red(results.summary.severityCounts.critical)}`); console.log(` High: ${chalk.yellow(results.summary.severityCounts.high)}`); console.log(` Medium: ${chalk.blue(results.summary.severityCounts.medium)}`); console.log(` Low: ${chalk.gray(results.summary.severityCounts.low)}`); // Top insights if (results.insights.length > 0) { console.log(chalk.blue.bold('\nšŸ” Top Insights:')); const topInsights = results.insights .sort((a, b) => b.confidence - a.confidence) .slice(0, 5); topInsights.forEach((insight, index) => { const severity = insight.severity === 'critical' ? chalk.red('šŸ”“') : insight.severity === 'high' ? chalk.yellow('🟔') : insight.severity === 'medium' ? chalk.blue('šŸ”µ') : chalk.gray('⚪'); console.log(` ${index + 1}. ${severity} ${insight.issue} (${insight.confidence}%)`); console.log(` ${insight.description}`); console.log(` šŸ’” ${insight.suggestion}`); }); } // Predictions if (results.predictions.length > 0) { console.log(chalk.blue.bold('\nšŸ”® AI Predictions:')); results.predictions.forEach((prediction, index) => { console.log(` ${index + 1}. ${prediction.prediction} (${prediction.confidence}%)`); console.log(` ${prediction.description}`); console.log(` Probability: ${(prediction.probability * 100).toFixed(1)}%`); }); } // Suggestions if (results.suggestions.length > 0) { console.log(chalk.blue.bold('\nšŸ’” Code Suggestions:')); results.suggestions.forEach((suggestion, index) => { console.log(` ${index + 1}. ${suggestion.suggestion} (${suggestion.confidence}%)`); console.log(` ${suggestion.description}`); }); } if (verbose) { console.log(chalk.blue.bold('\nšŸ” Detailed Results:')); console.log(JSON.stringify(results, null, 2)); } } } module.exports.default = AICodeAnalyzer;