userpravah
Version:
UserPravah is an extensible, framework-agnostic tool for analyzing user flows and navigation patterns in web applications. It supports multiple frameworks (Angular, React) and output formats (DOT/Graphviz, JSON) with a plugin-based architecture for easy e
73 lines (72 loc) • 4.1 kB
JavaScript
import { BasicPatternCollector } from './core/pattern-collector.interface.js';
import * as path from 'path'; // For potential output path construction
export class MainAnalyzer {
constructor(adapters, formatters, patternCollector // Allow injecting a shared collector
) {
this.frameworkAdapters = [];
this.outputFormatters = [];
this.frameworkAdapters = adapters;
this.outputFormatters = formatters;
this.patternCollector = patternCollector || new BasicPatternCollector();
}
registerAdapter(adapter) {
if (!this.frameworkAdapters.find(a => a.frameworkName.toLowerCase() === adapter.frameworkName.toLowerCase())) {
this.frameworkAdapters.push(adapter);
}
else {
console.warn(`[MainAnalyzer] Adapter for framework '${adapter.frameworkName}' already registered.`);
}
}
registerFormatter(formatter) {
if (!this.outputFormatters.find(f => f.formatName.toLowerCase() === formatter.formatName.toLowerCase())) {
this.outputFormatters.push(formatter);
}
else {
console.warn(`[MainAnalyzer] Formatter for format '${formatter.formatName}' already registered.`);
}
}
async analyzeAndGenerateOutput(projectPath, targetFrameworkName, targetOutputFormat, outputDirectory) {
const adapter = this.frameworkAdapters.find(a => a.frameworkName.toLowerCase() === targetFrameworkName.toLowerCase());
if (!adapter) {
throw new Error(`[MainAnalyzer] Framework adapter for "${targetFrameworkName}" not found.`);
}
const formatter = this.outputFormatters.find(f => f.formatName.toLowerCase() === targetOutputFormat.toLowerCase());
if (!formatter) {
throw new Error(`[MainAnalyzer] Output formatter for "${targetOutputFormat}" not found.`);
}
console.log(`🚀 [MainAnalyzer] Starting analysis for ${targetFrameworkName} project at: ${projectPath} using ${adapter.constructor.name}`);
// Clear patterns from the central collector before each full analysis run
this.patternCollector.clearPatterns();
// The adapter's analyzeProject method should use its *own* pattern collector instance internally
// or be passed the main one if designed that way. Here, we assume it might have its own,
// and we retrieve patterns afterwards.
const analysisResult = await adapter.analyzeProject(projectPath); // The adapter manages its own project instance
// After analysis, if the adapter supports getDiscoveredPatterns, merge them into the main collector.
// This is useful if adapters have their own collectors or if we want to ensure patterns are sourced correctly.
if (adapter.getDiscoveredPatterns) {
const adapterPatterns = adapter.getDiscoveredPatterns();
for (const type in adapterPatterns) {
adapterPatterns[type].forEach(p => this.patternCollector.addPattern(p)); // Add to central collector
}
}
console.log(`📊 [MainAnalyzer] Generating ${targetOutputFormat} output using ${formatter.constructor.name}...`);
// Ensure output directory exists (formatter could also do this)
// import * as fs from 'fs';
// if (!fs.existsSync(outputDirectory)) {
// fs.mkdirSync(outputDirectory, { recursive: true });
// }
await formatter.generateOutput(analysisResult, outputDirectory);
console.log(`✨ [MainAnalyzer] Analysis and ${targetOutputFormat} generation complete!`);
console.log(` Output should be in directory: ${path.resolve(outputDirectory)}`);
console.log(` Total patterns collected: ${this.patternCollector.getAllPatterns().length}`);
}
getCollectedPatterns() {
return this.patternCollector.getAllPatterns();
}
getPatternsByFramework(framework) {
return this.patternCollector.getPatternsByFramework(framework);
}
getPatternsByType(type) {
return this.patternCollector.getPatternsByType(type);
}
}