UNPKG

smartui-migration-tool

Version:

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

951 lines (808 loc) • 31 kB
const { Command, Flags } = require('@oclif/core'); const chalk = require('chalk'); const { ASCIILogos } = require('../utils/ascii-logos'); const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); class PatternRecognition extends Command { static description = 'Advanced pattern recognition and analysis for visual testing migration'; static flags = { path: Flags.string({ char: 'p', description: 'Project path to analyze' }), platform: Flags.string({ char: 'f', description: 'Target platform (percy|applitools|sauce|smartui)' }), framework: Flags.string({ char: 'w', description: 'Target framework (cypress|playwright|selenium|jest)' }), language: Flags.string({ char: 'l', description: 'Target language (javascript|typescript|python|java|csharp)' }), 'confidence-threshold': Flags.integer({ description: 'Minimum confidence threshold (0-100)', default: 70 }), 'include-context': Flags.boolean({ description: 'Include context analysis in results' }), 'export-patterns': Flags.boolean({ description: 'Export detected patterns to file' }), output: Flags.string({ char: 'o', description: 'Output file for pattern analysis results' }), format: Flags.string({ description: 'Output format (json|yaml|table)', default: 'table' }) }; async run() { console.log(ASCIILogos.getMinimalLogo()); console.log(chalk.cyan.bold('\nšŸ” Advanced Pattern Recognition Engine\n')); const { flags } = await this.parse(PatternRecognition); const projectPath = flags.path || process.cwd(); console.log(chalk.yellow.bold('šŸ“ Analyzing Project:')); console.log(chalk.white(projectPath)); try { // Initialize pattern recognition engine const engine = this.initializePatternEngine(); // Perform comprehensive pattern analysis const analysis = await this.performPatternAnalysis(projectPath, engine, flags); // Display results this.displayPatternResults(analysis, flags); // Export patterns if requested if (flags['export-patterns']) { await this.exportPatterns(analysis, projectPath); } // Save results if output specified if (flags.output) { await this.saveAnalysisResults(analysis, flags.output, flags.format); } } catch (error) { console.error(chalk.red.bold('\nāŒ Pattern recognition failed:')); console.error(chalk.red(error.message)); this.exit(1); } } initializePatternEngine() { return { visualTestingPatterns: this.getVisualTestingPatterns(), frameworkPatterns: this.getFrameworkPatterns(), configurationPatterns: this.getConfigurationPatterns(), migrationPatterns: this.getMigrationPatterns(), contextAnalyzer: this.createContextAnalyzer() }; } async performPatternAnalysis(projectPath, engine, flags) { const analysis = { projectPath, timestamp: new Date().toISOString(), patterns: { visualTesting: [], frameworks: [], configurations: [], migrations: [] }, context: {}, statistics: {}, recommendations: [] }; console.log(chalk.blue(' šŸ” Scanning for visual testing patterns...')); analysis.patterns.visualTesting = await this.analyzeVisualTestingPatterns(projectPath, engine, flags); console.log(chalk.blue(' šŸ—ļø Detecting framework patterns...')); analysis.patterns.frameworks = await this.analyzeFrameworkPatterns(projectPath, engine, flags); console.log(chalk.blue(' āš™ļø Analyzing configuration patterns...')); analysis.patterns.configurations = await this.analyzeConfigurationPatterns(projectPath, engine, flags); console.log(chalk.blue(' šŸ”„ Identifying migration patterns...')); analysis.patterns.migrations = await this.analyzeMigrationPatterns(projectPath, engine, flags); // Context analysis if requested if (flags['include-context']) { console.log(chalk.blue(' 🧠 Performing context analysis...')); analysis.context = await this.performContextAnalysis(projectPath, analysis.patterns, engine); } // Calculate statistics analysis.statistics = this.calculatePatternStatistics(analysis.patterns); // Generate recommendations analysis.recommendations = this.generatePatternRecommendations(analysis.patterns, flags); return analysis; } async analyzeVisualTestingPatterns(projectPath, engine, flags) { const patterns = []; const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true }); for (const file of files.slice(0, 50)) { // Limit for performance try { const content = await fs.readFile(file, 'utf8'); const filePatterns = this.findVisualTestingPatterns(content, file, engine.visualTestingPatterns); patterns.push(...filePatterns); } catch (error) { // Skip files that can't be read } } return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100); } async analyzeFrameworkPatterns(projectPath, engine, flags) { const patterns = []; const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true }); for (const file of files.slice(0, 50)) { try { const content = await fs.readFile(file, 'utf8'); const filePatterns = this.findFrameworkPatterns(content, file, engine.frameworkPatterns); patterns.push(...filePatterns); } catch (error) { // Skip files that can't be read } } return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100); } async analyzeConfigurationPatterns(projectPath, engine, flags) { const patterns = []; const configFiles = glob.sync(`${projectPath}/**/*.{json,js,ts,yml,yaml,config}`, { nodir: true }); for (const file of configFiles.slice(0, 20)) { try { const content = await fs.readFile(file, 'utf8'); const filePatterns = this.findConfigurationPatterns(content, file, engine.configurationPatterns); patterns.push(...filePatterns); } catch (error) { // Skip files that can't be read } } return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100); } async analyzeMigrationPatterns(projectPath, engine, flags) { const patterns = []; const files = glob.sync(`${projectPath}/**/*.{js,ts,jsx,tsx,py,java,cs}`, { nodir: true }); for (const file of files.slice(0, 50)) { try { const content = await fs.readFile(file, 'utf8'); const filePatterns = this.findMigrationPatterns(content, file, engine.migrationPatterns); patterns.push(...filePatterns); } catch (error) { // Skip files that can't be read } } return this.filterByConfidence(patterns, flags['confidence-threshold'] / 100); } async performContextAnalysis(projectPath, patterns, engine) { return { projectStructure: await this.analyzeProjectStructure(projectPath), codeComplexity: this.analyzeCodeComplexity(patterns), migrationReadiness: this.analyzeMigrationReadiness(patterns), riskAssessment: this.assessMigrationRisks(patterns), dependencies: await this.analyzeDependencies(projectPath) }; } findVisualTestingPatterns(content, filePath, patterns) { const matches = []; for (const pattern of patterns) { const regex = new RegExp(pattern.regex, 'gi'); const matches_found = content.match(regex); if (matches_found) { matches.push({ type: 'visual-testing', pattern: pattern.name, platform: pattern.platform, framework: pattern.framework, file: filePath, matches: matches_found.length, confidence: pattern.confidence, context: this.extractContext(content, matches_found[0], 3), suggestion: pattern.suggestion, migration: pattern.migration }); } } return matches; } findFrameworkPatterns(content, filePath, patterns) { const matches = []; for (const pattern of patterns) { const regex = new RegExp(pattern.regex, 'gi'); const matches_found = content.match(regex); if (matches_found) { matches.push({ type: 'framework', pattern: pattern.name, framework: pattern.framework, language: pattern.language, file: filePath, matches: matches_found.length, confidence: pattern.confidence, context: this.extractContext(content, matches_found[0], 3), suggestion: pattern.suggestion }); } } return matches; } findConfigurationPatterns(content, filePath, patterns) { const matches = []; for (const pattern of patterns) { const regex = new RegExp(pattern.regex, 'gi'); const matches_found = content.match(regex); if (matches_found) { matches.push({ type: 'configuration', pattern: pattern.name, configType: pattern.configType, file: filePath, matches: matches_found.length, confidence: pattern.confidence, context: this.extractContext(content, matches_found[0], 3), suggestion: pattern.suggestion }); } } return matches; } findMigrationPatterns(content, filePath, patterns) { const matches = []; for (const pattern of patterns) { const regex = new RegExp(pattern.regex, 'gi'); const matches_found = content.match(regex); if (matches_found) { matches.push({ type: 'migration', pattern: pattern.name, sourcePlatform: pattern.sourcePlatform, targetPlatform: pattern.targetPlatform, file: filePath, matches: matches_found.length, confidence: pattern.confidence, context: this.extractContext(content, matches_found[0], 3), suggestion: pattern.suggestion, transformation: pattern.transformation }); } } return matches; } extractContext(content, match, lines) { const lines_array = content.split('\n'); const matchLine = lines_array.findIndex(line => line.includes(match)); if (matchLine === -1) return ''; const start = Math.max(0, matchLine - lines); const end = Math.min(lines_array.length, matchLine + lines + 1); return lines_array.slice(start, end).join('\n'); } filterByConfidence(patterns, threshold) { return patterns.filter(pattern => pattern.confidence >= threshold); } calculatePatternStatistics(patterns) { const stats = { total: 0, byType: {}, byPlatform: {}, byFramework: {}, confidenceDistribution: { high: 0, medium: 0, low: 0 }, fileDistribution: {} }; const allPatterns = [ ...patterns.visualTesting, ...patterns.frameworks, ...patterns.configurations, ...patterns.migrations ]; stats.total = allPatterns.length; allPatterns.forEach(pattern => { // By type stats.byType[pattern.type] = (stats.byType[pattern.type] || 0) + 1; // By platform if (pattern.platform) { stats.byPlatform[pattern.platform] = (stats.byPlatform[pattern.platform] || 0) + 1; } // By framework if (pattern.framework) { stats.byFramework[pattern.framework] = (stats.byFramework[pattern.framework] || 0) + 1; } // Confidence distribution if (pattern.confidence >= 0.8) stats.confidenceDistribution.high++; else if (pattern.confidence >= 0.6) stats.confidenceDistribution.medium++; else stats.confidenceDistribution.low++; // File distribution const file = path.basename(pattern.file); stats.fileDistribution[file] = (stats.fileDistribution[file] || 0) + 1; }); return stats; } generatePatternRecommendations(patterns, flags) { const recommendations = []; // Visual testing migration recommendations const visualPatterns = patterns.visualTesting; const outdatedPlatforms = visualPatterns.filter(p => ['percy', 'applitools', 'sauce-labs'].includes(p.platform) ); if (outdatedPlatforms.length > 0) { recommendations.push({ type: 'migration', priority: 'high', message: `Found ${outdatedPlatforms.length} outdated visual testing patterns. Consider migrating to SmartUI.`, patterns: outdatedPlatforms.map(p => p.pattern), confidence: 0.9 }); } // Framework recommendations const frameworkPatterns = patterns.frameworks; if (frameworkPatterns.length === 0) { recommendations.push({ type: 'framework', priority: 'medium', message: 'No test framework patterns detected. Consider adding a testing framework.', confidence: 0.7 }); } // Configuration recommendations const configPatterns = patterns.configurations; const smartuiConfig = configPatterns.find(p => p.pattern.includes('smartui')); if (!smartuiConfig) { recommendations.push({ type: 'configuration', priority: 'high', message: 'No SmartUI configuration found. Add smartui.config.js for optimal setup.', confidence: 0.95 }); } return recommendations; } async analyzeProjectStructure(projectPath) { const structure = { totalFiles: 0, testFiles: 0, configFiles: 0, sourceFiles: 0, languages: new Set(), frameworks: new Set() }; const files = glob.sync(`${projectPath}/**/*`, { nodir: true }); structure.totalFiles = files.length; files.forEach(file => { const ext = path.extname(file).toLowerCase(); const basename = path.basename(file).toLowerCase(); if (basename.includes('test') || basename.includes('spec')) { structure.testFiles++; } if (basename.includes('config') || ext === '.json' || ext === '.yml' || ext === '.yaml') { structure.configFiles++; } if (['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs'].includes(ext)) { structure.sourceFiles++; // Detect language if (['.js', '.jsx'].includes(ext)) structure.languages.add('javascript'); if (['.ts', '.tsx'].includes(ext)) structure.languages.add('typescript'); if (ext === '.py') structure.languages.add('python'); if (ext === '.java') structure.languages.add('java'); if (ext === '.cs') structure.languages.add('csharp'); } }); return { ...structure, languages: Array.from(structure.languages), frameworks: Array.from(structure.frameworks) }; } analyzeCodeComplexity(patterns) { const complexity = { score: 0, factors: [] }; const allPatterns = [ ...patterns.visualTesting, ...patterns.frameworks, ...patterns.configurations, ...patterns.migrations ]; // Calculate complexity based on pattern density and types const patternDensity = allPatterns.length / 100; // patterns per 100 lines complexity.score = Math.min(100, patternDensity * 20); if (patternDensity > 5) { complexity.factors.push('High pattern density detected'); } const migrationPatterns = patterns.migrations.length; if (migrationPatterns > 10) { complexity.factors.push('Complex migration requirements'); } return complexity; } analyzeMigrationReadiness(patterns) { const readiness = { score: 0, factors: [] }; const visualPatterns = patterns.visualTesting; const smartuiPatterns = visualPatterns.filter(p => p.platform === 'smartui'); const outdatedPatterns = visualPatterns.filter(p => ['percy', 'applitools', 'sauce-labs'].includes(p.platform) ); // Calculate readiness score if (smartuiPatterns.length > 0) { readiness.score += 40; readiness.factors.push('SmartUI patterns already present'); } if (outdatedPatterns.length === 0) { readiness.score += 30; readiness.factors.push('No outdated patterns to migrate'); } else { readiness.score += 20; readiness.factors.push(`${outdatedPatterns.length} patterns need migration`); } if (patterns.frameworks.length > 0) { readiness.score += 20; readiness.factors.push('Test framework detected'); } if (patterns.configurations.length > 0) { readiness.score += 10; readiness.factors.push('Configuration files present'); } return readiness; } assessMigrationRisks(patterns) { const risks = { level: 'low', factors: [] }; const allPatterns = [ ...patterns.visualTesting, ...patterns.frameworks, ...patterns.configurations, ...patterns.migrations ]; const highConfidencePatterns = allPatterns.filter(p => p.confidence >= 0.8); const lowConfidencePatterns = allPatterns.filter(p => p.confidence < 0.6); if (lowConfidencePatterns.length > highConfidencePatterns.length) { risks.level = 'high'; risks.factors.push('Many low-confidence pattern matches'); } else if (lowConfidencePatterns.length > 0) { risks.level = 'medium'; risks.factors.push('Some low-confidence pattern matches'); } const migrationPatterns = patterns.migrations.length; if (migrationPatterns > 20) { risks.level = 'high'; risks.factors.push('Complex migration requirements'); } return risks; } async analyzeDependencies(projectPath) { try { const packageJsonPath = path.join(projectPath, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); return { dependencies: Object.keys(packageJson.dependencies || {}), devDependencies: Object.keys(packageJson.devDependencies || {}), hasSmartUI: !!(packageJson.dependencies?.['@lambdatest/smartui-cli'] || packageJson.devDependencies?.['@lambdatest/smartui-cli']) }; } } catch (error) { // Ignore errors } return { dependencies: [], devDependencies: [], hasSmartUI: false }; } displayPatternResults(analysis, flags) { console.log(chalk.yellow.bold('\nšŸ“Š Pattern Recognition Results:')); // Statistics console.log(chalk.blue('\nšŸ“ˆ Statistics:')); console.log(chalk.white(` • Total patterns: ${analysis.statistics.total}`)); console.log(chalk.white(` • High confidence: ${analysis.statistics.confidenceDistribution.high}`)); console.log(chalk.white(` • Medium confidence: ${analysis.statistics.confidenceDistribution.medium}`)); console.log(chalk.white(` • Low confidence: ${analysis.statistics.confidenceDistribution.low}`)); // Pattern breakdown console.log(chalk.blue('\nšŸ” Pattern Breakdown:')); Object.entries(analysis.statistics.byType).forEach(([type, count]) => { console.log(chalk.white(` • ${type}: ${count}`)); }); // Platform breakdown if (Object.keys(analysis.statistics.byPlatform).length > 0) { console.log(chalk.blue('\nšŸ¢ Platform Breakdown:')); Object.entries(analysis.statistics.byPlatform).forEach(([platform, count]) => { console.log(chalk.white(` • ${platform}: ${count}`)); }); } // Framework breakdown if (Object.keys(analysis.statistics.byFramework).length > 0) { console.log(chalk.blue('\nšŸ—ļø Framework Breakdown:')); Object.entries(analysis.statistics.byFramework).forEach(([framework, count]) => { console.log(chalk.white(` • ${framework}: ${count}`)); }); } // Context analysis if (flags['include-context'] && analysis.context) { console.log(chalk.blue('\n🧠 Context Analysis:')); if (analysis.context.projectStructure) { const structure = analysis.context.projectStructure; console.log(chalk.white(` • Total files: ${structure.totalFiles}`)); console.log(chalk.white(` • Test files: ${structure.testFiles}`)); console.log(chalk.white(` • Config files: ${structure.configFiles}`)); console.log(chalk.white(` • Languages: ${structure.languages.join(', ')}`)); } if (analysis.context.codeComplexity) { const complexity = analysis.context.codeComplexity; console.log(chalk.white(` • Complexity score: ${complexity.score.toFixed(1)}/100`)); if (complexity.factors.length > 0) { console.log(chalk.white(` • Factors: ${complexity.factors.join(', ')}`)); } } if (analysis.context.migrationReadiness) { const readiness = analysis.context.migrationReadiness; console.log(chalk.white(` • Migration readiness: ${readiness.score}/100`)); if (readiness.factors.length > 0) { console.log(chalk.white(` • Factors: ${readiness.factors.join(', ')}`)); } } if (analysis.context.riskAssessment) { const risk = analysis.context.riskAssessment; const riskColor = risk.level === 'high' ? chalk.red : risk.level === 'medium' ? chalk.yellow : chalk.green; console.log(riskColor(` • Risk level: ${risk.level.toUpperCase()}`)); if (risk.factors.length > 0) { console.log(chalk.white(` • Factors: ${risk.factors.join(', ')}`)); } } } // Recommendations if (analysis.recommendations.length > 0) { console.log(chalk.blue('\nšŸ’” Recommendations:')); analysis.recommendations.forEach((rec, index) => { const priorityColor = { 'high': chalk.red, 'medium': chalk.yellow, 'low': chalk.green }; console.log(chalk.white(` ${index + 1}. ${rec.message}`)); console.log(priorityColor[rec.priority](` Priority: ${rec.priority.toUpperCase()}`)); console.log(chalk.gray(` Confidence: ${(rec.confidence * 100).toFixed(1)}%`)); }); } } async exportPatterns(analysis, projectPath) { const exportPath = path.join(projectPath, 'smartui-patterns.json'); const exportData = { timestamp: analysis.timestamp, projectPath: analysis.projectPath, patterns: analysis.patterns, statistics: analysis.statistics }; await fs.writeJson(exportPath, exportData, { spaces: 2 }); console.log(chalk.green(`\nāœ… Patterns exported to: ${exportPath}`)); } async saveAnalysisResults(analysis, outputPath, format) { let content; if (format === 'json') { content = JSON.stringify(analysis, null, 2); } else if (format === 'yaml') { const yaml = require('js-yaml'); content = yaml.dump(analysis); } else { content = JSON.stringify(analysis, null, 2); } await fs.writeFile(outputPath, content); console.log(chalk.green(`\nāœ… Analysis results saved to: ${outputPath}`)); } getVisualTestingPatterns() { return [ // Percy patterns { name: 'percy-snapshot', regex: 'percy\\.snapshot', platform: 'percy', framework: 'cypress', confidence: 0.95, suggestion: 'Replace with SmartUI visual check', migration: 'cy.visualCheck()' }, { name: 'percy-visual', regex: 'percy\\.visual', platform: 'percy', framework: 'cypress', confidence: 0.95, suggestion: 'Replace with SmartUI visual check', migration: 'cy.visualCheck()' }, // Applitools patterns { name: 'applitools-eyes', regex: 'eyes\\.', platform: 'applitools', framework: 'cypress', confidence: 0.9, suggestion: 'Replace with SmartUI visual check', migration: 'cy.visualCheck()' }, { name: 'applitools-check', regex: 'checkWindow', platform: 'applitools', framework: 'cypress', confidence: 0.9, suggestion: 'Replace with SmartUI visual check', migration: 'cy.visualCheck()' }, // Sauce Labs patterns { name: 'sauce-visual', regex: 'sauce.*visual', platform: 'sauce-labs', framework: 'selenium', confidence: 0.85, suggestion: 'Replace with SmartUI visual check', migration: 'smartui.visualCheck()' }, // SmartUI patterns { name: 'smartui-visual', regex: 'smartui\\.visual', platform: 'smartui', framework: 'cypress', confidence: 0.95, suggestion: 'SmartUI pattern detected', migration: 'Already migrated' } ]; } getFrameworkPatterns() { return [ // Cypress patterns { name: 'cypress-commands', regex: 'cy\\.', framework: 'cypress', language: 'javascript', confidence: 0.9, suggestion: 'Cypress framework detected' }, { name: 'cypress-describe', regex: 'describe\\(.*cy\\.', framework: 'cypress', language: 'javascript', confidence: 0.95, suggestion: 'Cypress test structure detected' }, // Playwright patterns { name: 'playwright-page', regex: 'page\\.', framework: 'playwright', language: 'javascript', confidence: 0.9, suggestion: 'Playwright framework detected' }, { name: 'playwright-test', regex: 'test\\(.*page\\.', framework: 'playwright', language: 'javascript', confidence: 0.95, suggestion: 'Playwright test structure detected' }, // Selenium patterns { name: 'selenium-webdriver', regex: 'webdriver', framework: 'selenium', language: 'javascript', confidence: 0.8, suggestion: 'Selenium WebDriver detected' } ]; } getConfigurationPatterns() { return [ { name: 'cypress-config', regex: 'cypress\\.config', configType: 'cypress', confidence: 0.9, suggestion: 'Cypress configuration detected' }, { name: 'playwright-config', regex: 'playwright\\.config', configType: 'playwright', confidence: 0.9, suggestion: 'Playwright configuration detected' }, { name: 'smartui-config', regex: 'smartui\\.config', configType: 'smartui', confidence: 0.95, suggestion: 'SmartUI configuration detected' }, { name: 'package-json', regex: 'package\\.json', configType: 'npm', confidence: 0.8, suggestion: 'Package.json configuration detected' } ]; } getMigrationPatterns() { return [ { name: 'percy-to-smartui', regex: 'percy\\.(snapshot|visual)', sourcePlatform: 'percy', targetPlatform: 'smartui', confidence: 0.9, suggestion: 'Migrate from Percy to SmartUI', transformation: 'percy.snapshot() → cy.visualCheck()' }, { name: 'applitools-to-smartui', regex: 'eyes\\.(checkWindow|check)', sourcePlatform: 'applitools', targetPlatform: 'smartui', confidence: 0.9, suggestion: 'Migrate from Applitools to SmartUI', transformation: 'eyes.checkWindow() → cy.visualCheck()' }, { name: 'sauce-to-smartui', regex: 'sauce.*visual', sourcePlatform: 'sauce-labs', targetPlatform: 'smartui', confidence: 0.85, suggestion: 'Migrate from Sauce Labs to SmartUI', transformation: 'sauce.visualCheck() → smartui.visualCheck()' } ]; } createContextAnalyzer() { return { analyzeScope: (content, pattern) => this.analyzeScope(content, pattern), analyzeDependencies: (content, pattern) => this.analyzeDependencies(content, pattern), analyzeComplexity: (content, pattern) => this.analyzeComplexity(content, pattern) }; } analyzeScope(content, pattern) { // Analyze the scope where the pattern appears const lines = content.split('\n'); const patternLine = lines.findIndex(line => line.includes(pattern)); if (patternLine === -1) return { scope: 'unknown', context: '' }; // Determine if it's in a function, class, or global scope let scope = 'global'; let context = ''; for (let i = patternLine; i >= 0; i--) { const line = lines[i]; if (line.includes('function ') || line.includes('=>')) { scope = 'function'; context = line.trim(); break; } if (line.includes('class ')) { scope = 'class'; context = line.trim(); break; } } return { scope, context }; } analyzeDependencies(content, pattern) { // Analyze dependencies related to the pattern const dependencies = []; if (pattern.includes('percy')) { dependencies.push('@percy/cli', '@percy/cypress'); } if (pattern.includes('applitools')) { dependencies.push('@applitools/eyes-cypress'); } if (pattern.includes('sauce')) { dependencies.push('sauce-connect'); } if (pattern.includes('smartui')) { dependencies.push('@lambdatest/smartui-cli'); } return dependencies; } analyzeComplexity(content, pattern) { // Analyze the complexity of the code around the pattern const lines = content.split('\n'); const patternLine = lines.findIndex(line => line.includes(pattern)); if (patternLine === -1) return { complexity: 'low', factors: [] }; const factors = []; let complexity = 'low'; // Check for nested structures const contextLines = lines.slice(Math.max(0, patternLine - 5), patternLine + 6); const nestingLevel = contextLines.reduce((max, line) => { const indent = line.match(/^\s*/)[0].length; return Math.max(max, indent); }, 0); if (nestingLevel > 8) { factors.push('High nesting level'); complexity = 'high'; } else if (nestingLevel > 4) { factors.push('Medium nesting level'); complexity = 'medium'; } // Check for async/await patterns if (contextLines.some(line => line.includes('async') || line.includes('await'))) { factors.push('Async operations present'); if (complexity === 'low') complexity = 'medium'; } return { complexity, factors }; } } module.exports.default = PatternRecognition;