UNPKG

@opichi/smartcode

Version:

Universal code intelligence MCP server - analyze any codebase with TypeScript excellence and multi-language support

284 lines 11.3 kB
export class PatternDetector { graph; projectStructure = null; constructor(graph) { this.graph = graph; } setProjectStructure(structure) { this.projectStructure = structure; } detectPatterns() { const patterns = []; // Detect different architectural patterns patterns.push(...this.detectMVCPattern()); patterns.push(...this.detectAPIPattern()); patterns.push(...this.detectServicePattern()); patterns.push(...this.detectRepositoryPattern()); patterns.push(...this.detectFactoryPattern()); patterns.push(...this.detectObserverPattern()); patterns.push(...this.detectSingletonPattern()); return patterns.filter(p => p.confidence > 0.3); // Only return confident patterns } detectMVCPattern() { const patterns = []; const components = this.graph.getArchitecturalComponents(); const models = components.get('models') || []; const controllers = components.get('controllers') || []; const views = components.get('views') || []; if (models.length > 0 && controllers.length > 0) { // Group related MVC triads const mvcGroups = this.groupMVCComponents(models, controllers, views); mvcGroups.forEach(group => { patterns.push({ name: `${group.entity} MVC Pattern`, type: 'mvc', components: [...group.models, ...group.controllers, ...group.views], description: `Model-View-Controller pattern for ${group.entity} entity`, confidence: this.calculateMVCConfidence(group) }); }); } return patterns; } detectAPIPattern() { const patterns = []; const allNodes = this.graph.getNodesByType('function'); // Look for API endpoint patterns const apiEndpoints = allNodes.filter(node => this.isAPIEndpoint(node)); if (apiEndpoints.length > 0) { // Group by resource const resourceGroups = this.groupAPIEndpoints(apiEndpoints); resourceGroups.forEach((endpoints, resource) => { patterns.push({ name: `${resource} REST API`, type: 'api', components: endpoints, description: `RESTful API endpoints for ${resource} resource`, confidence: this.calculateAPIConfidence(endpoints) }); }); } return patterns; } detectServicePattern() { const patterns = []; const services = this.graph.getArchitecturalComponents().get('services') || []; if (services.length > 0) { // Group services by domain const serviceGroups = this.groupServicesByDomain(services); serviceGroups.forEach((serviceList, domain) => { patterns.push({ name: `${domain} Service Layer`, type: 'service', components: serviceList, description: `Service layer pattern for ${domain} domain logic`, confidence: this.calculateServiceConfidence(serviceList) }); }); } return patterns; } detectRepositoryPattern() { const patterns = []; const allNodes = this.graph.getNodesByType('class'); const repositories = allNodes.filter(node => node.name.toLowerCase().includes('repository') || node.name.toLowerCase().includes('dao') || this.hasRepositoryMethods(node)); if (repositories.length > 0) { patterns.push({ name: 'Repository Pattern', type: 'repository', components: repositories, description: 'Data access abstraction using repository pattern', confidence: this.calculateRepositoryConfidence(repositories) }); } return patterns; } detectFactoryPattern() { const patterns = []; const allNodes = this.graph.getNodesByType('class'); const factories = allNodes.filter(node => node.name.toLowerCase().includes('factory') || node.name.toLowerCase().includes('builder') || this.hasFactoryMethods(node)); if (factories.length > 0) { patterns.push({ name: 'Factory Pattern', type: 'factory', components: factories, description: 'Object creation using factory pattern', confidence: this.calculateFactoryConfidence(factories) }); } return patterns; } detectObserverPattern() { const patterns = []; const allNodes = this.graph.getNodesByType('class'); const observers = allNodes.filter(node => this.hasObserverMethods(node) || node.content.includes('addEventListener') || node.content.includes('subscribe') || node.content.includes('emit')); if (observers.length > 0) { patterns.push({ name: 'Observer Pattern', type: 'observer', components: observers, description: 'Event-driven communication using observer pattern', confidence: this.calculateObserverConfidence(observers) }); } return patterns; } detectSingletonPattern() { const patterns = []; const allNodes = this.graph.getNodesByType('class'); const singletons = allNodes.filter(node => this.hasSingletonPattern(node)); if (singletons.length > 0) { patterns.push({ name: 'Singleton Pattern', type: 'singleton', components: singletons, description: 'Single instance classes using singleton pattern', confidence: this.calculateSingletonConfidence(singletons) }); } return patterns; } // Helper methods for pattern detection groupMVCComponents(models, controllers, views) { const groups = []; const entities = new Set(); // Extract entity names from models and controllers [...models, ...controllers].forEach(node => { const entity = this.extractEntityName(node.name); if (entity) entities.add(entity); }); entities.forEach(entity => { const group = { entity, models: models.filter(m => this.extractEntityName(m.name) === entity), controllers: controllers.filter(c => this.extractEntityName(c.name) === entity), views: views.filter(v => this.extractEntityName(v.name) === entity) }; if (group.models.length > 0 || group.controllers.length > 0) { groups.push(group); } }); return groups; } extractEntityName(name) { // Extract entity name from class/file names const cleaned = name.toLowerCase() .replace(/controller|model|view|service|repository/g, '') .replace(/s$/, ''); // Remove plural return cleaned.length > 1 ? cleaned : null; } isAPIEndpoint(node) { const content = node.content.toLowerCase(); return content.includes('app.get') || content.includes('app.post') || content.includes('app.put') || content.includes('app.delete') || content.includes('router.') || content.includes('@route') || content.includes('route::'); } groupAPIEndpoints(endpoints) { const groups = new Map(); endpoints.forEach(endpoint => { const resource = this.extractAPIResource(endpoint); if (!groups.has(resource)) { groups.set(resource, []); } groups.get(resource).push(endpoint); }); return groups; } extractAPIResource(endpoint) { // Extract resource name from API endpoint const content = endpoint.content; const routeMatch = content.match(/['"`]\/(\w+)/); if (routeMatch) { return routeMatch[1]; } return this.extractEntityName(endpoint.name) || 'unknown'; } groupServicesByDomain(services) { const groups = new Map(); services.forEach(service => { const domain = this.extractEntityName(service.name) || 'general'; if (!groups.has(domain)) { groups.set(domain, []); } groups.get(domain).push(service); }); return groups; } hasRepositoryMethods(node) { const content = node.content.toLowerCase(); const repoMethods = ['find', 'save', 'delete', 'update', 'create', 'get', 'query']; return repoMethods.some(method => content.includes(method)); } hasFactoryMethods(node) { const content = node.content.toLowerCase(); return content.includes('create') || content.includes('build') || content.includes('make') || content.includes('new '); } hasObserverMethods(node) { const content = node.content.toLowerCase(); return content.includes('notify') || content.includes('observe') || content.includes('subscribe') || content.includes('unsubscribe') || content.includes('emit') || content.includes('trigger'); } hasSingletonPattern(node) { const content = node.content.toLowerCase(); return content.includes('instance') && (content.includes('static') || content.includes('private constructor')); } // Confidence calculation methods calculateMVCConfidence(group) { let confidence = 0.3; // Base confidence if (group.models.length > 0) confidence += 0.3; if (group.controllers.length > 0) confidence += 0.3; if (group.views.length > 0) confidence += 0.1; return Math.min(confidence, 1.0); } calculateAPIConfidence(endpoints) { const httpMethods = ['get', 'post', 'put', 'delete']; const foundMethods = new Set(); endpoints.forEach(endpoint => { httpMethods.forEach(method => { if (endpoint.content.toLowerCase().includes(method)) { foundMethods.add(method); } }); }); return Math.min(0.4 + (foundMethods.size * 0.15), 1.0); } calculateServiceConfidence(services) { return Math.min(0.5 + (services.length * 0.1), 1.0); } calculateRepositoryConfidence(repositories) { return Math.min(0.6 + (repositories.length * 0.1), 1.0); } calculateFactoryConfidence(factories) { return Math.min(0.5 + (factories.length * 0.1), 1.0); } calculateObserverConfidence(observers) { return Math.min(0.4 + (observers.length * 0.1), 1.0); } calculateSingletonConfidence(singletons) { return Math.min(0.7 + (singletons.length * 0.1), 1.0); } } //# sourceMappingURL=patterns.js.map