smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
695 lines (600 loc) ⢠24.6 kB
JavaScript
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;