smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
1,101 lines (930 loc) • 35.3 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 SuggestionEngine extends Command {
static description = 'Generate intelligent suggestions for code improvement and migration';
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'
}),
output: Flags.string({
char: 'o',
description: 'Output file for suggestions',
default: 'suggestions.json'
}),
type: Flags.string({
char: 't',
description: 'Type of suggestions to generate',
default: 'all',
options: ['all', 'quality', 'performance', 'security', 'migration', 'architecture']
}),
priority: Flags.string({
char: 'r',
description: 'Minimum priority level',
default: 'medium',
options: ['low', 'medium', 'high', 'critical']
}),
verbose: Flags.boolean({
char: 'v',
description: 'Enable verbose output',
default: false
})
};
async run() {
const { flags } = await this.parse(SuggestionEngine);
console.log(chalk.blue.bold('\n💡 Intelligent Suggestion Engine'));
console.log(chalk.gray('Generating smart suggestions for code improvement...\n'));
try {
// Create suggestion engine
const engine = this.createSuggestionEngine();
// Generate suggestions
const results = await this.generateSuggestions(flags, engine);
// Display results
this.displayResults(results, flags.verbose);
// Save results
if (flags.output) {
await fs.writeJson(flags.output, results, { spaces: 2 });
console.log(chalk.green(`\n✅ Suggestions saved to: ${flags.output}`));
}
} catch (error) {
console.error(chalk.red(`\n❌ Error generating suggestions: ${error.message}`));
this.exit(1);
}
}
createSuggestionEngine() {
return {
// Code Quality Suggestions
generateQualitySuggestions: (files) => {
const suggestions = [];
files.forEach(file => {
if (file.content) {
// Complexity suggestions
const complexitySuggestions = this.analyzeComplexity(file);
suggestions.push(...complexitySuggestions);
// Maintainability suggestions
const maintainabilitySuggestions = this.analyzeMaintainability(file);
suggestions.push(...maintainabilitySuggestions);
// Readability suggestions
const readabilitySuggestions = this.analyzeReadability(file);
suggestions.push(...readabilitySuggestions);
// Code style suggestions
const styleSuggestions = this.analyzeCodeStyle(file);
suggestions.push(...styleSuggestions);
}
});
return suggestions;
},
// Performance Suggestions
generatePerformanceSuggestions: (files) => {
const suggestions = [];
files.forEach(file => {
if (file.content) {
// Performance anti-patterns
const performanceSuggestions = this.analyzePerformance(file);
suggestions.push(...performanceSuggestions);
// Memory usage suggestions
const memorySuggestions = this.analyzeMemoryUsage(file);
suggestions.push(...memorySuggestions);
// Algorithm efficiency suggestions
const algorithmSuggestions = this.analyzeAlgorithms(file);
suggestions.push(...algorithmSuggestions);
}
});
return suggestions;
},
// Security Suggestions
generateSecuritySuggestions: (files) => {
const suggestions = [];
files.forEach(file => {
if (file.content) {
// Security vulnerabilities
const securitySuggestions = this.analyzeSecurity(file);
suggestions.push(...securitySuggestions);
// Input validation suggestions
const validationSuggestions = this.analyzeInputValidation(file);
suggestions.push(...validationSuggestions);
// Authentication suggestions
const authSuggestions = this.analyzeAuthentication(file);
suggestions.push(...authSuggestions);
}
});
return suggestions;
},
// Migration Suggestions
generateMigrationSuggestions: (files) => {
const suggestions = [];
files.forEach(file => {
if (file.content) {
// Visual testing migration
const visualTestingSuggestions = this.analyzeVisualTesting(file);
suggestions.push(...visualTestingSuggestions);
// Framework migration
const frameworkSuggestions = this.analyzeFrameworkMigration(file);
suggestions.push(...frameworkSuggestions);
// API migration
const apiSuggestions = this.analyzeAPIMigration(file);
suggestions.push(...apiSuggestions);
}
});
return suggestions;
},
// Architecture Suggestions
generateArchitectureSuggestions: (files) => {
const suggestions = [];
// Design pattern suggestions
const patternSuggestions = this.analyzeDesignPatterns(files);
suggestions.push(...patternSuggestions);
// Modularity suggestions
const modularitySuggestions = this.analyzeModularity(files);
suggestions.push(...modularitySuggestions);
// Scalability suggestions
const scalabilitySuggestions = this.analyzeScalability(files);
suggestions.push(...scalabilitySuggestions);
return suggestions;
},
// Confidence Scoring
calculateConfidence: (suggestion) => {
let confidence = 0.5; // Base confidence
// Increase confidence based on evidence
if (suggestion.evidence && suggestion.evidence.length > 0) {
confidence += 0.2;
}
if (suggestion.severity === 'high' || suggestion.severity === 'critical') {
confidence += 0.1;
}
if (suggestion.impact === 'high') {
confidence += 0.1;
}
return Math.min(confidence, 1.0);
},
// Priority Ranking
calculatePriority: (suggestion) => {
let priority = 0;
// Base priority from severity
const severityMap = { low: 1, medium: 2, high: 3, critical: 4 };
priority += severityMap[suggestion.severity] || 1;
// Increase priority based on impact
const impactMap = { low: 1, medium: 2, high: 3 };
priority += impactMap[suggestion.impact] || 1;
// Increase priority based on confidence
priority += suggestion.confidence * 2;
return priority;
},
// Transformation Suggestions
generateTransformations: (suggestion) => {
const transformations = [];
if (suggestion.type === 'complexity') {
transformations.push({
type: 'refactor',
description: 'Break down complex function into smaller functions',
code: this.generateRefactoringCode(suggestion)
});
} else if (suggestion.type === 'performance') {
transformations.push({
type: 'optimize',
description: 'Optimize performance bottleneck',
code: this.generateOptimizationCode(suggestion)
});
} else if (suggestion.type === 'security') {
transformations.push({
type: 'secure',
description: 'Implement security best practices',
code: this.generateSecurityCode(suggestion)
});
} else if (suggestion.type === 'migration') {
transformations.push({
type: 'migrate',
description: 'Migrate to recommended solution',
code: this.generateMigrationCode(suggestion)
});
}
return transformations;
}
};
}
async generateSuggestions(flags, engine) {
const results = {
timestamp: new Date().toISOString(),
path: flags.path,
type: flags.type,
priority: flags.priority,
files: [],
suggestions: [],
summary: {}
};
// Find files to analyze
const files = await this.findFiles(flags);
results.files = files;
// Generate suggestions based on type
let allSuggestions = [];
if (flags.type === 'all' || flags.type === 'quality') {
const qualitySuggestions = engine.generateQualitySuggestions(files);
allSuggestions.push(...qualitySuggestions);
}
if (flags.type === 'all' || flags.type === 'performance') {
const performanceSuggestions = engine.generatePerformanceSuggestions(files);
allSuggestions.push(...performanceSuggestions);
}
if (flags.type === 'all' || flags.type === 'security') {
const securitySuggestions = engine.generateSecuritySuggestions(files);
allSuggestions.push(...securitySuggestions);
}
if (flags.type === 'all' || flags.type === 'migration') {
const migrationSuggestions = engine.generateMigrationSuggestions(files);
allSuggestions.push(...migrationSuggestions);
}
if (flags.type === 'all' || flags.type === 'architecture') {
const architectureSuggestions = engine.generateArchitectureSuggestions(files);
allSuggestions.push(...architectureSuggestions);
}
// Calculate confidence and priority for each suggestion
allSuggestions.forEach(suggestion => {
suggestion.confidence = engine.calculateConfidence(suggestion);
suggestion.priority = engine.calculatePriority(suggestion);
suggestion.transformations = engine.generateTransformations(suggestion);
});
// Filter by priority
const priorityMap = { low: 1, medium: 2, high: 3, critical: 4 };
const minPriority = priorityMap[flags.priority] || 2;
results.suggestions = allSuggestions.filter(s => s.priority >= minPriority);
// Sort by priority (descending)
results.suggestions.sort((a, b) => b.priority - a.priority);
// Generate summary
results.summary = this.generateSummary(results.suggestions);
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 })));
}
// Read file contents
for (const file of files) {
try {
const content = await fs.readFile(file.path, 'utf8');
const language = this.detectLanguage(file.path);
file.content = content;
file.language = language;
file.size = content.length;
file.lines = content.split('\n').length;
} catch (error) {
if (flags.verbose) {
console.warn(chalk.yellow(`⚠️ Could not read file: ${file.path}`));
}
}
}
return files;
}
detectLanguage(filePath) {
const ext = path.extname(filePath).toLowerCase();
const languageMap = {
'.js': 'javascript',
'.jsx': 'javascript',
'.ts': 'typescript',
'.tsx': 'typescript',
'.py': 'python',
'.java': 'java',
'.cs': 'csharp'
};
return languageMap[ext] || 'unknown';
}
analyzeComplexity(file) {
const suggestions = [];
const content = file.content;
const lines = content.split('\n');
// Check for long functions
const functionRegex = /function\s+(\w+)\s*\([^)]*\)\s*\{/g;
let match;
while ((match = functionRegex.exec(content)) !== null) {
const functionName = match[1];
const functionStart = match.index;
const functionEnd = this.findFunctionEnd(content, functionStart);
const functionLines = content.substring(functionStart, functionEnd).split('\n').length;
if (functionLines > 50) {
suggestions.push({
type: 'complexity',
severity: 'medium',
impact: 'medium',
title: 'Long Function Detected',
description: `Function '${functionName}' is ${functionLines} lines long`,
file: file.path,
line: this.getLineNumber(content, functionStart),
evidence: [`Function length: ${functionLines} lines`],
recommendation: 'Consider breaking this function into smaller, more focused functions'
});
}
}
// Check for deep nesting
const nestingLevel = this.calculateNestingLevel(content);
if (nestingLevel > 4) {
suggestions.push({
type: 'complexity',
severity: 'high',
impact: 'high',
title: 'Deep Nesting Detected',
description: `Maximum nesting level: ${nestingLevel}`,
file: file.path,
evidence: [`Nesting level: ${nestingLevel}`],
recommendation: 'Refactor to reduce nesting depth and improve readability'
});
}
return suggestions;
}
analyzeMaintainability(file) {
const suggestions = [];
const content = file.content;
// Check for magic numbers
const magicNumberRegex = /\b\d{3,}\b/g;
const magicNumbers = content.match(magicNumberRegex);
if (magicNumbers && magicNumbers.length > 5) {
suggestions.push({
type: 'maintainability',
severity: 'medium',
impact: 'medium',
title: 'Magic Numbers Detected',
description: `${magicNumbers.length} magic numbers found`,
file: file.path,
evidence: magicNumbers.slice(0, 5),
recommendation: 'Replace magic numbers with named constants'
});
}
// Check for commented code
const commentedCodeRegex = /\/\/.*\w+.*\(/g;
const commentedCode = content.match(commentedCodeRegex);
if (commentedCode && commentedCode.length > 3) {
suggestions.push({
type: 'maintainability',
severity: 'low',
impact: 'low',
title: 'Commented Code Detected',
description: `${commentedCode.length} lines of commented code found`,
file: file.path,
evidence: commentedCode.slice(0, 3),
recommendation: 'Remove commented code or convert to documentation'
});
}
return suggestions;
}
analyzeReadability(file) {
const suggestions = [];
const content = file.content;
const lines = content.split('\n');
// Check for long lines
lines.forEach((line, index) => {
if (line.length > 120) {
suggestions.push({
type: 'readability',
severity: 'low',
impact: 'low',
title: 'Long Line Detected',
description: `Line ${index + 1} is ${line.length} characters long`,
file: file.path,
line: index + 1,
evidence: [line.substring(0, 50) + '...'],
recommendation: 'Break long lines to improve readability'
});
}
});
// Check for inconsistent naming
const variableRegex = /(?:let|const|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g;
const variables = [];
let match;
while ((match = variableRegex.exec(content)) !== null) {
variables.push(match[1]);
}
const namingIssues = this.checkNamingConsistency(variables);
if (namingIssues.length > 0) {
suggestions.push({
type: 'readability',
severity: 'low',
impact: 'low',
title: 'Inconsistent Naming Detected',
description: `${namingIssues.length} naming inconsistencies found`,
file: file.path,
evidence: namingIssues.slice(0, 3),
recommendation: 'Use consistent naming conventions throughout the codebase'
});
}
return suggestions;
}
analyzeCodeStyle(file) {
const suggestions = [];
const content = file.content;
// Check for missing semicolons (JavaScript/TypeScript)
if (file.language === 'javascript' || file.language === 'typescript') {
const lines = content.split('\n');
const missingSemicolons = [];
lines.forEach((line, index) => {
const trimmedLine = line.trim();
if (trimmedLine &&
!trimmedLine.endsWith(';') &&
!trimmedLine.endsWith('{') &&
!trimmedLine.endsWith('}') &&
!trimmedLine.startsWith('//') &&
!trimmedLine.startsWith('*') &&
!trimmedLine.startsWith('if') &&
!trimmedLine.startsWith('for') &&
!trimmedLine.startsWith('while') &&
!trimmedLine.startsWith('switch') &&
!trimmedLine.startsWith('function') &&
!trimmedLine.startsWith('class')) {
missingSemicolons.push(index + 1);
}
});
if (missingSemicolons.length > 5) {
suggestions.push({
type: 'style',
severity: 'low',
impact: 'low',
title: 'Missing Semicolons',
description: `${missingSemicolons.length} lines missing semicolons`,
file: file.path,
evidence: missingSemicolons.slice(0, 5),
recommendation: 'Add semicolons for consistency and to avoid potential issues'
});
}
}
return suggestions;
}
analyzePerformance(file) {
const suggestions = [];
const content = file.content;
// Check for inefficient loops
const forLoopRegex = /for\s*\(\s*var\s+(\w+)\s*=\s*0\s*;\s*\1\s*<\s*(\w+)\.length\s*;\s*\1\+\+\)/g;
let match;
while ((match = forLoopRegex.exec(content)) !== null) {
suggestions.push({
type: 'performance',
severity: 'medium',
impact: 'medium',
title: 'Inefficient Loop Detected',
description: 'Consider caching array length or using forEach',
file: file.path,
line: this.getLineNumber(content, match.index),
evidence: [match[0]],
recommendation: 'Cache array length or use forEach for better performance'
});
}
// Check for string concatenation in loops
const stringConcatRegex = /(\w+)\s*\+=\s*['"][^'"]*['"]/g;
const stringConcats = content.match(stringConcatRegex);
if (stringConcats && stringConcats.length > 3) {
suggestions.push({
type: 'performance',
severity: 'medium',
impact: 'medium',
title: 'String Concatenation in Loop',
description: 'Consider using array.join() or template literals',
file: file.path,
evidence: stringConcats.slice(0, 3),
recommendation: 'Use array.join() or template literals for better performance'
});
}
return suggestions;
}
analyzeMemoryUsage(file) {
const suggestions = [];
const content = file.content;
// Check for potential memory leaks
const eventListenerRegex = /addEventListener\s*\([^,]+,\s*[^)]+\)/g;
const eventListeners = content.match(eventListenerRegex);
if (eventListeners && eventListeners.length > 5) {
suggestions.push({
type: 'performance',
severity: 'medium',
impact: 'medium',
title: 'Multiple Event Listeners',
description: `${eventListeners.length} event listeners found`,
file: file.path,
evidence: eventListeners.slice(0, 3),
recommendation: 'Ensure event listeners are properly removed to prevent memory leaks'
});
}
return suggestions;
}
analyzeAlgorithms(file) {
const suggestions = [];
const content = file.content;
// Check for O(n²) algorithms
const nestedLoopRegex = /for\s*\([^)]+\)\s*\{[^}]*for\s*\([^)]+\)\s*\{/g;
if (nestedLoopRegex.test(content)) {
suggestions.push({
type: 'performance',
severity: 'medium',
impact: 'medium',
title: 'Nested Loops Detected',
description: 'Consider optimizing nested loops for better performance',
file: file.path,
evidence: ['Nested loop structure detected'],
recommendation: 'Consider using more efficient algorithms or data structures'
});
}
return suggestions;
}
analyzeSecurity(file) {
const suggestions = [];
const content = file.content;
// Check for eval usage
if (content.includes('eval(')) {
suggestions.push({
type: 'security',
severity: 'critical',
impact: 'high',
title: 'eval() Usage Detected',
description: 'eval() can be dangerous and should be avoided',
file: file.path,
evidence: ['eval() function found'],
recommendation: 'Replace eval() with safer alternatives like JSON.parse() or Function constructor'
});
}
// Check for innerHTML usage
if (content.includes('innerHTML')) {
suggestions.push({
type: 'security',
severity: 'high',
impact: 'high',
title: 'innerHTML Usage Detected',
description: 'innerHTML can lead to XSS vulnerabilities',
file: file.path,
evidence: ['innerHTML property found'],
recommendation: 'Use textContent or createElement for safer DOM manipulation'
});
}
return suggestions;
}
analyzeInputValidation(file) {
const suggestions = [];
const content = file.content;
// Check for missing input validation
const inputRegex = /input\s*\[[^\]]+\]/g;
const inputs = content.match(inputRegex);
if (inputs && inputs.length > 0) {
const hasValidation = content.includes('validate') || content.includes('check') || content.includes('test');
if (!hasValidation) {
suggestions.push({
type: 'security',
severity: 'high',
impact: 'high',
title: 'Missing Input Validation',
description: 'Input validation not detected',
file: file.path,
evidence: ['Input access without validation'],
recommendation: 'Add proper input validation and sanitization'
});
}
}
return suggestions;
}
analyzeAuthentication(file) {
const suggestions = [];
const content = file.content;
// Check for hardcoded credentials
const credentialRegex = /(?:password|secret|key|token)\s*[:=]\s*['"][^'"]+['"]/gi;
const credentials = content.match(credentialRegex);
if (credentials && credentials.length > 0) {
suggestions.push({
type: 'security',
severity: 'critical',
impact: 'high',
title: 'Hardcoded Credentials Detected',
description: 'Hardcoded credentials found in code',
file: file.path,
evidence: credentials.slice(0, 3),
recommendation: 'Move credentials to environment variables or secure configuration'
});
}
return suggestions;
}
analyzeVisualTesting(file) {
const suggestions = [];
const content = file.content;
// Check for Percy usage
if (content.includes('percy') || content.includes('Percy')) {
suggestions.push({
type: 'migration',
severity: 'high',
impact: 'high',
title: 'Percy Visual Testing Detected',
description: 'Percy visual testing code found',
file: file.path,
evidence: ['Percy API usage detected'],
recommendation: 'Consider migrating to SmartUI for better performance and features'
});
}
// Check for Applitools usage
if (content.includes('applitools') || content.includes('eyes')) {
suggestions.push({
type: 'migration',
severity: 'high',
impact: 'high',
title: 'Applitools Visual Testing Detected',
description: 'Applitools visual testing code found',
file: file.path,
evidence: ['Applitools API usage detected'],
recommendation: 'Consider migrating to SmartUI for better performance and features'
});
}
return suggestions;
}
analyzeFrameworkMigration(file) {
const suggestions = [];
const content = file.content;
// Check for outdated framework usage
if (content.includes('jQuery') && !content.includes('$')) {
suggestions.push({
type: 'migration',
severity: 'medium',
impact: 'medium',
title: 'jQuery Usage Detected',
description: 'Consider migrating to modern frameworks',
file: file.path,
evidence: ['jQuery library usage'],
recommendation: 'Consider migrating to React, Vue, or Angular for better maintainability'
});
}
return suggestions;
}
analyzeAPIMigration(file) {
const suggestions = [];
const content = file.content;
// Check for deprecated API usage
if (content.includes('XMLHttpRequest') && !content.includes('fetch')) {
suggestions.push({
type: 'migration',
severity: 'medium',
impact: 'medium',
title: 'XMLHttpRequest Usage Detected',
description: 'Consider using modern fetch API',
file: file.path,
evidence: ['XMLHttpRequest usage'],
recommendation: 'Migrate to fetch API for better promise support and cleaner code'
});
}
return suggestions;
}
analyzeDesignPatterns(files) {
const suggestions = [];
// Check for singleton pattern usage
const hasSingleton = files.some(f =>
f.content && f.content.includes('getInstance') && f.content.includes('static')
);
if (hasSingleton) {
suggestions.push({
type: 'architecture',
severity: 'low',
impact: 'low',
title: 'Singleton Pattern Detected',
description: 'Consider if singleton is the right pattern for your use case',
evidence: ['Singleton pattern implementation found'],
recommendation: 'Evaluate if singleton is necessary or if dependency injection would be better'
});
}
return suggestions;
}
analyzeModularity(files) {
const suggestions = [];
// Check for large files
const largeFiles = files.filter(f => f.content && f.content.length > 10000);
if (largeFiles.length > 0) {
suggestions.push({
type: 'architecture',
severity: 'medium',
impact: 'medium',
title: 'Large Files Detected',
description: `${largeFiles.length} files are larger than 10KB`,
evidence: largeFiles.map(f => `${path.basename(f.path)}: ${f.content.length} characters`),
recommendation: 'Consider breaking large files into smaller, more focused modules'
});
}
return suggestions;
}
analyzeScalability(files) {
const suggestions = [];
// Check for synchronous operations
const syncOps = files.filter(f =>
f.content && (f.content.includes('sync') || f.content.includes('Sync'))
);
if (syncOps.length > 0) {
suggestions.push({
type: 'architecture',
severity: 'medium',
impact: 'medium',
title: 'Synchronous Operations Detected',
description: 'Consider using asynchronous operations for better scalability',
evidence: ['Synchronous operations found'],
recommendation: 'Replace synchronous operations with async/await or promises where possible'
});
}
return suggestions;
}
checkNamingConsistency(variables) {
const issues = [];
const camelCase = /^[a-z][a-zA-Z0-9]*$/;
const snakeCase = /^[a-z][a-z0-9_]*$/;
const pascalCase = /^[A-Z][a-zA-Z0-9]*$/;
const namingStyles = {
camelCase: variables.filter(v => camelCase.test(v)).length,
snakeCase: variables.filter(v => snakeCase.test(v)).length,
pascalCase: variables.filter(v => pascalCase.test(v)).length
};
const total = variables.length;
const dominantStyle = Object.entries(namingStyles).reduce((a, b) =>
namingStyles[a[0]] > namingStyles[b[0]] ? a : b
)[0];
variables.forEach(variable => {
if (dominantStyle === 'camelCase' && !camelCase.test(variable)) {
issues.push(variable);
} else if (dominantStyle === 'snakeCase' && !snakeCase.test(variable)) {
issues.push(variable);
} else if (dominantStyle === 'pascalCase' && !pascalCase.test(variable)) {
issues.push(variable);
}
});
return issues;
}
calculateNestingLevel(content) {
let maxNesting = 0;
let currentNesting = 0;
for (let i = 0; i < content.length; i++) {
if (content[i] === '{') {
currentNesting++;
maxNesting = Math.max(maxNesting, currentNesting);
} else if (content[i] === '}') {
currentNesting--;
}
}
return maxNesting;
}
findFunctionEnd(content, start) {
let braceCount = 0;
let i = start;
while (i < content.length) {
if (content[i] === '{') {
braceCount++;
} else if (content[i] === '}') {
braceCount--;
if (braceCount === 0) {
return i + 1;
}
}
i++;
}
return content.length;
}
getLineNumber(content, position) {
return content.substring(0, position).split('\n').length;
}
generateRefactoringCode(suggestion) {
return `// Refactored version of ${suggestion.title}
// Break down complex function into smaller, focused functions
function processData(data) {
const validatedData = validateData(data);
const processedData = transformData(validatedData);
return processedData;
}
function validateData(data) {
// Validation logic here
return data;
}
function transformData(data) {
// Transformation logic here
return data;
}`;
}
generateOptimizationCode(suggestion) {
return `// Optimized version for better performance
// Cache array length and use efficient algorithms
// Before: for (var i = 0; i < array.length; i++)
// After:
const length = array.length;
for (let i = 0; i < length; i++) {
// Process array[i]
}
// Or use forEach for better readability
array.forEach((item, index) => {
// Process item
});`;
}
generateSecurityCode(suggestion) {
return `// Secure implementation
// Replace dangerous functions with safe alternatives
// Instead of eval():
const result = JSON.parse(jsonString);
// Instead of innerHTML:
element.textContent = userInput;
// Or use createElement for safe DOM manipulation
const div = document.createElement('div');
div.textContent = userInput;`;
}
generateMigrationCode(suggestion) {
return `// Migration to SmartUI
// Replace Percy/Applitools with SmartUI
// Before (Percy):
// percy.snapshot('Homepage');
// After (SmartUI):
await smartui.snapshot('Homepage', {
name: 'Homepage',
fullPage: true
});`;
}
generateSummary(suggestions) {
const summary = {
total: suggestions.length,
byType: {},
bySeverity: {},
byImpact: {},
topSuggestions: suggestions.slice(0, 5)
};
suggestions.forEach(suggestion => {
// Count by type
summary.byType[suggestion.type] = (summary.byType[suggestion.type] || 0) + 1;
// Count by severity
summary.bySeverity[suggestion.severity] = (summary.bySeverity[suggestion.severity] || 0) + 1;
// Count by impact
summary.byImpact[suggestion.impact] = (summary.byImpact[suggestion.impact] || 0) + 1;
});
return summary;
}
displayResults(results, verbose) {
console.log(chalk.green.bold('\n💡 Suggestion Engine Results'));
console.log(chalk.gray('=' * 50));
// Summary
console.log(chalk.blue.bold('\n📊 Summary:'));
console.log(` Total suggestions: ${results.summary.total}`);
console.log(` Files analyzed: ${results.files.length}`);
// By Type
console.log(chalk.blue.bold('\n📋 By Type:'));
Object.entries(results.summary.byType).forEach(([type, count]) => {
console.log(` ${type}: ${count}`);
});
// By Severity
console.log(chalk.blue.bold('\n⚠️ By Severity:'));
Object.entries(results.summary.bySeverity).forEach(([severity, count]) => {
const color = severity === 'critical' ? chalk.red :
severity === 'high' ? chalk.yellow :
severity === 'medium' ? chalk.blue : chalk.green;
console.log(` ${color(severity)}: ${count}`);
});
// Top Suggestions
console.log(chalk.blue.bold('\n🎯 Top Suggestions:'));
results.summary.topSuggestions.forEach((suggestion, index) => {
const severityColor = suggestion.severity === 'critical' ? chalk.red :
suggestion.severity === 'high' ? chalk.yellow :
suggestion.severity === 'medium' ? chalk.blue : chalk.green;
console.log(` ${index + 1}. ${severityColor(suggestion.title)} (${suggestion.severity})`);
console.log(` ${suggestion.description}`);
console.log(` File: ${path.basename(suggestion.file)}`);
console.log(` Confidence: ${(suggestion.confidence * 100).toFixed(1)}%`);
console.log(` Priority: ${suggestion.priority.toFixed(1)}`);
console.log('');
});
if (verbose) {
console.log(chalk.blue.bold('\n🔍 Detailed Suggestions:'));
results.suggestions.forEach((suggestion, index) => {
console.log(`\n${index + 1}. ${suggestion.title}`);
console.log(` Type: ${suggestion.type}`);
console.log(` Severity: ${suggestion.severity}`);
console.log(` Impact: ${suggestion.impact}`);
console.log(` Confidence: ${(suggestion.confidence * 100).toFixed(1)}%`);
console.log(` Priority: ${suggestion.priority.toFixed(1)}`);
console.log(` File: ${suggestion.file}`);
if (suggestion.line) {
console.log(` Line: ${suggestion.line}`);
}
console.log(` Description: ${suggestion.description}`);
console.log(` Recommendation: ${suggestion.recommendation}`);
if (suggestion.evidence && suggestion.evidence.length > 0) {
console.log(` Evidence: ${suggestion.evidence.join(', ')}`);
}
if (suggestion.transformations && suggestion.transformations.length > 0) {
console.log(` Transformations:`);
suggestion.transformations.forEach((transformation, tIndex) => {
console.log(` ${tIndex + 1}. ${transformation.description}`);
});
}
});
}
}
}
module.exports.default = SuggestionEngine;