smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
790 lines • 26.5 kB
JavaScript
"use strict";
/**
* Cross-File Dependency Analyzer
* Phase 3: Cross-File Dependency Analysis & Intelligent Suggestions
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CrossFileDependencyAnalyzer = void 0;
class CrossFileDependencyAnalyzer {
constructor() {
this.fileMap = new Map();
this.dependencyMap = new Map();
this.graph = null;
}
analyze(files) {
const startTime = Date.now();
const startMemory = process.memoryUsage().heapUsed;
// Store files
this.fileMap = files;
// Analyze dependencies
const dependencies = this.analyzeDependencies(files);
// Build graph
const graph = this.buildGraph(files, dependencies);
// Find clusters
const clusters = this.findClusters(graph);
// Find cycles
const cycles = this.findCycles(graph);
// Calculate metrics
const metrics = this.calculateMetrics(graph, clusters, cycles);
// Generate recommendations
const recommendations = this.generateRecommendations(dependencies, graph, clusters, cycles);
// Generate transformations
const transformations = this.generateTransformations(dependencies, graph, clusters, cycles);
const endTime = Date.now();
const endMemory = process.memoryUsage().heapUsed;
return {
dependencies,
graph,
clusters,
cycles,
metrics,
recommendations,
transformations,
metadata: {
language: 'javascript',
framework: null,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: endTime - startTime,
memoryUsage: endMemory - startMemory,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
}
};
}
analyzeDependencies(files) {
const dependencies = [];
for (const [filePath, ast] of files) {
this.traverseAST(ast, (node) => {
if (node.type === 'import') {
const importSource = this.extractImportSource(node);
const targetFile = this.resolveImportPath(importSource, filePath);
if (targetFile) {
dependencies.push({
source: filePath,
target: targetFile,
type: 'import',
strength: 'strong',
direction: 'unidirectional',
usage: [node.id],
lineNumbers: [node.start?.line || 0],
confidence: 0.9,
metadata: this.createDependencyMetadata(ast)
});
}
}
});
}
return dependencies;
}
buildGraph(files, dependencies) {
const nodes = [];
const edges = [];
// Create nodes
for (const [filePath, ast] of files) {
const node = this.createFileNode(filePath, ast, dependencies);
nodes.push(node);
}
// Create edges
for (const dep of dependencies) {
const edge = this.createFileEdge(dep);
edges.push(edge);
}
// Find clusters
const clusters = this.findClusters({ nodes, edges, clusters: [], cycles: [], metrics: this.createEmptyMetrics(), metadata: this.createEmptyMetadata() });
// Find cycles
const cycles = this.findCycles({ nodes, edges, clusters: [], cycles: [], metrics: this.createEmptyMetrics(), metadata: this.createEmptyMetadata() });
// Calculate metrics
const metrics = this.calculateMetrics({ nodes, edges, clusters, cycles, metrics: this.createEmptyMetrics(), metadata: this.createEmptyMetadata() }, clusters, cycles);
return {
nodes,
edges,
clusters,
cycles,
metrics,
metadata: this.createEmptyMetadata()
};
}
findClusters(graph) {
const clusters = [];
const visited = new Set();
for (const node of graph.nodes) {
if (!visited.has(node.id)) {
const cluster = this.createCluster(node, graph, visited);
clusters.push(cluster);
}
}
return clusters;
}
findCycles(graph) {
const cycles = [];
const visited = new Set();
const recursionStack = new Set();
for (const node of graph.nodes) {
if (!visited.has(node.id)) {
const cycle = this.detectCycle(node, graph, visited, recursionStack);
if (cycle) {
cycles.push(cycle);
}
}
}
return cycles;
}
calculateMetrics(graph, clusters, cycles) {
const totalNodes = graph.nodes.length;
const totalEdges = graph.edges.length;
const totalClusters = clusters.length;
const totalCycles = cycles.length;
const averageDegree = totalEdges / totalNodes;
const averageClustering = this.calculateAverageClustering(graph);
const averagePathLength = this.calculateAveragePathLength(graph);
const diameter = this.calculateDiameter(graph);
const density = this.calculateDensity(graph);
const modularity = this.calculateModularity(graph, clusters);
const assortativity = this.calculateAssortativity(graph);
const efficiency = this.calculateEfficiency(graph);
const robustness = this.calculateRobustness(graph);
const vulnerability = this.calculateVulnerability(graph);
const resilience = this.calculateResilience(graph);
const adaptability = this.calculateAdaptability(graph);
const evolvability = this.calculateEvolvability(graph);
const maintainability = this.calculateMaintainability(graph);
const testability = this.calculateTestability(graph);
const performance = this.calculatePerformance(graph);
const security = this.calculateSecurity(graph);
const accessibility = this.calculateAccessibility(graph);
const usability = this.calculateUsability(graph);
const reliability = this.calculateReliability(graph);
const scalability = this.calculateScalability(graph);
const portability = this.calculatePortability(graph);
const reusability = this.calculateReusability(graph);
const readability = this.calculateReadability(graph);
const documentation = this.calculateDocumentation(graph);
const errorHandling = this.calculateErrorHandling(graph);
const logging = this.calculateLogging(graph);
const monitoring = this.calculateMonitoring(graph);
const debugging = this.calculateDebugging(graph);
const profiling = this.calculateProfiling(graph);
return {
totalNodes,
totalEdges,
totalClusters,
totalCycles,
averageDegree,
averageClustering,
averagePathLength,
diameter,
density,
modularity,
assortativity,
efficiency,
robustness,
vulnerability,
resilience,
adaptability,
evolvability,
maintainability,
testability,
performance,
security,
accessibility,
usability,
reliability,
scalability,
portability,
reusability,
readability,
documentation,
errorHandling,
logging,
monitoring,
debugging,
profiling
};
}
generateRecommendations(dependencies, graph, clusters, cycles) {
const recommendations = [];
// Generate recommendations for cycles
for (const cycle of cycles) {
recommendations.push({
id: `rec_cycle_${cycle.id}`,
type: 'decouple',
title: `Break Circular Dependency: ${cycle.id}`,
description: `Break circular dependency involving ${cycle.nodes.length} files`,
priority: 'high',
effort: 'medium',
impact: 'high',
risk: 'medium',
confidence: 0.8,
files: cycle.nodes,
dependencies: cycle.edges,
prerequisites: [],
alternatives: [],
resources: [],
examples: [],
code: '// Break circular dependency code',
validation: '// Validation for breaking circular dependency',
rollback: '// Rollback for breaking circular dependency',
metadata: this.createCrossFileMetadata()
});
}
// Generate recommendations for clusters
for (const cluster of clusters) {
if (cluster.coupling > 0.7) {
recommendations.push({
id: `rec_cluster_${cluster.id}`,
type: 'modularize',
title: `Modularize Cluster: ${cluster.name}`,
description: `Reduce coupling in cluster ${cluster.name}`,
priority: 'medium',
effort: 'high',
impact: 'medium',
risk: 'low',
confidence: 0.7,
files: cluster.nodes,
dependencies: cluster.edges,
prerequisites: [],
alternatives: [],
resources: [],
examples: [],
code: '// Modularize cluster code',
validation: '// Validation for modularizing cluster',
rollback: '// Rollback for modularizing cluster',
metadata: this.createCrossFileMetadata()
});
}
}
return recommendations;
}
generateTransformations(dependencies, graph, clusters, cycles) {
const transformations = [];
// Generate transformations for cycles
for (const cycle of cycles) {
transformations.push({
id: `transform_cycle_${cycle.id}`,
name: `Break Circular Dependency: ${cycle.id}`,
description: `Break circular dependency involving ${cycle.nodes.length} files`,
type: 'decouple',
from: cycle.nodes.join(' -> '),
to: 'Decoupled structure',
confidence: 0.8,
effort: 'medium',
impact: 'high',
risk: 'medium',
files: cycle.nodes,
code: '// Break circular dependency transformation',
validation: '// Validation for breaking circular dependency',
rollback: '// Rollback for breaking circular dependency',
metadata: this.createCrossFileMetadata()
});
}
return transformations;
}
// Helper methods
traverseAST(ast, callback) {
callback(ast);
if (ast.children) {
ast.children.forEach(child => this.traverseAST(child, callback));
}
}
extractImportSource(node) {
return 'module';
}
resolveImportPath(importSource, currentFile) {
return 'resolved/path';
}
createFileNode(filePath, ast, dependencies) {
const nodeDependencies = dependencies.filter(dep => dep.source === filePath).map(dep => dep.target);
const nodeDependents = dependencies.filter(dep => dep.target === filePath).map(dep => dep.source);
return {
id: filePath,
path: filePath,
name: this.extractFileName(filePath),
type: this.detectFileType(filePath),
language: ast.language,
framework: ast.framework || null,
platform: ast.platform || null,
size: this.calculateFileSize(ast),
lines: this.countLines(ast.raw),
complexity: this.calculateComplexity(ast),
maintainability: 0.7,
testability: 0.8,
dependencies: nodeDependencies,
dependents: nodeDependents,
centrality: 0.5,
betweenness: 0.3,
closeness: 0.4,
pagerank: 0.2,
metadata: this.createFileNodeMetadata(ast)
};
}
createFileEdge(dependency) {
return {
id: `${dependency.source}_${dependency.target}`,
source: dependency.source,
target: dependency.target,
type: dependency.type,
weight: 1.0,
strength: dependency.strength,
direction: dependency.direction,
usage: dependency.usage,
lineNumbers: dependency.lineNumbers,
confidence: dependency.confidence,
metadata: dependency.metadata
};
}
createCluster(node, graph, visited) {
const clusterNodes = [node.id];
const clusterEdges = [];
visited.add(node.id);
// Find connected nodes
const connectedNodes = this.findConnectedNodes(node, graph, visited);
clusterNodes.push(...connectedNodes);
// Find edges within cluster
for (const edge of graph.edges) {
if (clusterNodes.includes(edge.source) && clusterNodes.includes(edge.target)) {
clusterEdges.push(edge.id);
}
}
return {
id: `cluster_${node.id}`,
name: `Cluster ${node.name}`,
type: 'module',
nodes: clusterNodes,
edges: clusterEdges,
cohesion: 0.7,
coupling: 0.3,
modularity: 0.8,
reusability: 0.6,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4,
metadata: this.createClusterMetadata()
};
}
findConnectedNodes(node, graph, visited) {
const connected = [];
const queue = [node.id];
while (queue.length > 0) {
const currentId = queue.shift();
if (visited.has(currentId))
continue;
visited.add(currentId);
connected.push(currentId);
// Find adjacent nodes
for (const edge of graph.edges) {
if (edge.source === currentId && !visited.has(edge.target)) {
queue.push(edge.target);
}
if (edge.target === currentId && !visited.has(edge.source)) {
queue.push(edge.source);
}
}
}
return connected;
}
detectCycle(node, graph, visited, recursionStack) {
visited.add(node.id);
recursionStack.add(node.id);
for (const edge of graph.edges) {
if (edge.source === node.id) {
if (!visited.has(edge.target)) {
const cycle = this.detectCycle(graph.nodes.find(n => n.id === edge.target), graph, visited, recursionStack);
if (cycle)
return cycle;
}
else if (recursionStack.has(edge.target)) {
// Found a cycle
return {
id: `cycle_${node.id}_${edge.target}`,
nodes: [node.id, edge.target],
edges: [edge.id],
type: edge.type,
severity: 'medium',
impact: ['Circular dependency detected'],
solutions: ['Break the cycle by introducing an interface or abstraction'],
confidence: 0.8,
metadata: this.createCycleMetadata()
};
}
}
}
recursionStack.delete(node.id);
return null;
}
extractFileName(filePath) {
return filePath.split('/').pop() || filePath;
}
detectFileType(filePath) {
if (filePath.includes('test') || filePath.includes('spec'))
return 'test';
if (filePath.includes('config'))
return 'config';
if (filePath.includes('asset'))
return 'asset';
if (filePath.includes('doc'))
return 'documentation';
if (filePath.includes('build'))
return 'build';
if (filePath.includes('deploy'))
return 'deployment';
return 'source';
}
calculateFileSize(ast) {
return ast.raw.length;
}
countLines(code) {
return code.split('\n').length;
}
calculateComplexity(ast) {
return 1.0;
}
calculateAverageClustering(graph) {
return 0.5;
}
calculateAveragePathLength(graph) {
return 3.0;
}
calculateDiameter(graph) {
return 5.0;
}
calculateDensity(graph) {
const n = graph.nodes.length;
const m = graph.edges.length;
return n > 1 ? (2 * m) / (n * (n - 1)) : 0;
}
calculateModularity(graph, clusters) {
return 0.7;
}
calculateAssortativity(graph) {
return 0.3;
}
calculateEfficiency(graph) {
return 0.6;
}
calculateRobustness(graph) {
return 0.7;
}
calculateVulnerability(graph) {
return 0.3;
}
calculateResilience(graph) {
return 0.8;
}
calculateAdaptability(graph) {
return 0.6;
}
calculateEvolvability(graph) {
return 0.7;
}
calculateMaintainability(graph) {
return 0.7;
}
calculateTestability(graph) {
return 0.8;
}
calculatePerformance(graph) {
return 0.7;
}
calculateSecurity(graph) {
return 0.6;
}
calculateAccessibility(graph) {
return 0.5;
}
calculateUsability(graph) {
return 0.6;
}
calculateReliability(graph) {
return 0.7;
}
calculateScalability(graph) {
return 0.6;
}
calculatePortability(graph) {
return 0.7;
}
calculateReusability(graph) {
return 0.6;
}
calculateReadability(graph) {
return 0.8;
}
calculateDocumentation(graph) {
return 0.5;
}
calculateErrorHandling(graph) {
return 0.6;
}
calculateLogging(graph) {
return 0.5;
}
calculateMonitoring(graph) {
return 0.4;
}
calculateDebugging(graph) {
return 0.6;
}
calculateProfiling(graph) {
return 0.4;
}
createDependencyMetadata(ast) {
return {
language: ast.language,
framework: ast.framework || null,
platform: ast.platform || null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createFileNodeMetadata(ast) {
return {
language: ast.language,
framework: ast.framework || null,
platform: ast.platform || null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createClusterMetadata() {
return {
language: 'javascript',
framework: null,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createCycleMetadata() {
return {
language: 'javascript',
framework: null,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createEmptyMetrics() {
return {
totalNodes: 0,
totalEdges: 0,
totalClusters: 0,
totalCycles: 0,
averageDegree: 0,
averageClustering: 0,
averagePathLength: 0,
diameter: 0,
density: 0,
modularity: 0,
assortativity: 0,
efficiency: 0,
robustness: 0,
vulnerability: 0,
resilience: 0,
adaptability: 0,
evolvability: 0,
maintainability: 0,
testability: 0,
performance: 0,
security: 0,
accessibility: 0,
usability: 0,
reliability: 0,
scalability: 0,
portability: 0,
reusability: 0,
readability: 0,
documentation: 0,
errorHandling: 0,
logging: 0,
monitoring: 0,
debugging: 0,
profiling: 0
};
}
createEmptyMetadata() {
return {
language: 'javascript',
framework: null,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
createCrossFileMetadata() {
return {
language: 'javascript',
framework: null,
platform: null,
version: '1.0.0',
timestamp: new Date().toISOString(),
processingTime: 0,
memoryUsage: 0,
confidence: 0.8,
quality: 0.7,
complexity: 0.5,
maintainability: 0.7,
testability: 0.8,
performance: 0.7,
security: 0.6,
accessibility: 0.5,
usability: 0.6,
reliability: 0.7,
scalability: 0.6,
portability: 0.7,
reusability: 0.6,
readability: 0.8,
documentation: 0.5,
errorHandling: 0.6,
logging: 0.5,
monitoring: 0.4,
debugging: 0.6,
profiling: 0.4
};
}
}
exports.CrossFileDependencyAnalyzer = CrossFileDependencyAnalyzer;
//# sourceMappingURL=CrossFileDependencyAnalyzer.js.map