UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

377 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StructureAnalyzer = void 0; class StructureAnalyzer { constructor() { this.targetPatterns = [ // Framework-based suggestions { pattern: /^(pages|app)\//, suggestion: 'web/nextjs', reason: 'Next.js application structure detected', category: 'framework', confidence: 0.9 }, { pattern: /^src\/components/, suggestion: 'web/react', reason: 'React component structure detected', category: 'framework', confidence: 0.8 }, { pattern: /^(lib|libs)\//, suggestion: 'libraries', reason: 'Library structure detected', category: 'structure', confidence: 0.7 }, { pattern: /^packages$/, suggestion: 'monorepo', reason: 'Monorepo structure detected', category: 'structure', confidence: 0.9 }, { pattern: /^apps$/, suggestion: 'applications', reason: 'Multi-application structure detected', category: 'structure', confidence: 0.8 }, // Language-based suggestions { pattern: /\.py$/, suggestion: 'python', reason: 'Python project detected', category: 'language', confidence: 0.8 }, { pattern: /\.(ts|tsx)$/, suggestion: 'typescript', reason: 'TypeScript project detected', category: 'language', confidence: 0.8 }, { pattern: /\.rs$/, suggestion: 'rust', reason: 'Rust project detected', category: 'language', confidence: 0.9 }, { pattern: /\.go$/, suggestion: 'go', reason: 'Go project detected', category: 'language', confidence: 0.9 }, // Purpose-based suggestions { pattern: /^(api|server|backend)\//, suggestion: 'backend/api', reason: 'Backend/API structure detected', category: 'purpose', confidence: 0.8 }, { pattern: /^(frontend|client|ui)\//, suggestion: 'frontend', reason: 'Frontend structure detected', category: 'purpose', confidence: 0.8 }, { pattern: /^(scripts|tools|utils)\//, suggestion: 'utilities/scripts', reason: 'Utility scripts detected', category: 'purpose', confidence: 0.7 }, { pattern: /^(docs|documentation)\//, suggestion: 'documentation', reason: 'Documentation structure detected', category: 'purpose', confidence: 0.9 }, { pattern: /^(config|configs)\//, suggestion: 'configuration', reason: 'Configuration files detected', category: 'purpose', confidence: 0.7 }, { pattern: /^(test|tests|__tests__)\//, suggestion: 'testing', reason: 'Test structure detected', category: 'purpose', confidence: 0.8 }, // Infrastructure suggestions { pattern: /^(docker|containers)\//, suggestion: 'infrastructure/docker', reason: 'Docker infrastructure detected', category: 'purpose', confidence: 0.8 }, { pattern: /^(k8s|kubernetes)$/, suggestion: 'infrastructure/kubernetes', reason: 'Kubernetes configuration detected', category: 'purpose', confidence: 0.9 }, { pattern: /^(terraform|tf)\//, suggestion: 'infrastructure/terraform', reason: 'Terraform configuration detected', category: 'purpose', confidence: 0.9 }, { pattern: /^\.github\/workflows\//, suggestion: 'automation/github-actions', reason: 'GitHub Actions workflows detected', category: 'purpose', confidence: 0.9 } ]; } analyzeStructure(structure, tags) { const analysis = { projectType: this.determineProjectType(structure, tags), primaryLanguage: this.determinePrimaryLanguage(structure, tags), framework: this.determineFramework(tags), targetSuggestions: this.generateTargetSuggestions(structure, tags), isMonorepo: this.detectMonorepo(structure), hasTests: this.detectTests(structure), hasDocumentation: this.detectDocumentation(structure), complexity: this.assessComplexity(structure) }; return analysis; } determineProjectType(structure, tags) { // Check for specific project types based on tags and structure if (tags.frameworkTags.includes('nextjs')) return 'Next.js Application'; if (tags.frameworkTags.includes('react')) return 'React Application'; if (tags.frameworkTags.includes('vue')) return 'Vue.js Application'; if (tags.frameworkTags.includes('angular')) return 'Angular Application'; if (tags.frameworkTags.includes('express')) return 'Express.js API'; if (tags.semanticTags.includes('api')) return 'API Service'; if (tags.semanticTags.includes('ai')) return 'AI/ML Project'; if (tags.semanticTags.includes('documentation')) return 'Documentation'; if (this.detectMonorepo(structure)) return 'Monorepo'; if (tags.toolingTags.includes('docker')) return 'Containerized Application'; // Fallback based on primary language const primaryLang = this.determinePrimaryLanguage(structure, tags); return `${primaryLang} Project`; } determinePrimaryLanguage(structure, _tags) { // Count files by language const languageCounts = {}; for (const file of structure.files) { const ext = file.extension.toLowerCase(); if (ext === '.ts' || ext === '.tsx') { languageCounts.typescript = (languageCounts.typescript || 0) + 1; } else if (ext === '.js' || ext === '.jsx') { languageCounts.javascript = (languageCounts.javascript || 0) + 1; } else if (ext === '.py') { languageCounts.python = (languageCounts.python || 0) + 1; } else if (ext === '.rs') { languageCounts.rust = (languageCounts.rust || 0) + 1; } else if (ext === '.go') { languageCounts.go = (languageCounts.go || 0) + 1; } else if (ext === '.java') { languageCounts.java = (languageCounts.java || 0) + 1; } else if (ext === '.cs') { languageCounts.csharp = (languageCounts.csharp || 0) + 1; } } // Find the language with the most files const primaryLang = Object.entries(languageCounts) .sort(([, a], [, b]) => b - a)[0]?.[0]; return primaryLang ? primaryLang.charAt(0).toUpperCase() + primaryLang.slice(1) : 'Unknown'; } determineFramework(tags) { // Return the highest confidence framework if (tags.frameworkTags.length > 0) { return tags.frameworkTags[0]; // Already sorted by confidence } return undefined; } generateTargetSuggestions(structure, tags) { const suggestions = []; const seenSuggestions = new Set(); // Analyze directory structure for (const dir of structure.directories) { for (const pattern of this.targetPatterns) { if (pattern.pattern.test(dir.relativePath)) { if (!seenSuggestions.has(pattern.suggestion)) { suggestions.push({ path: pattern.suggestion, confidence: pattern.confidence, reason: pattern.reason, category: pattern.category }); seenSuggestions.add(pattern.suggestion); } } } } // Analyze file patterns for (const file of structure.files) { for (const pattern of this.targetPatterns) { if (pattern.pattern.test(file.relativePath)) { if (!seenSuggestions.has(pattern.suggestion)) { suggestions.push({ path: pattern.suggestion, confidence: pattern.confidence * 0.8, // Slightly lower confidence for files reason: pattern.reason, category: pattern.category }); seenSuggestions.add(pattern.suggestion); } } } } // Add tag-based suggestions this.addTagBasedSuggestions(tags, suggestions, seenSuggestions); // Sort by confidence and return top suggestions return suggestions .sort((a, b) => b.confidence - a.confidence) .slice(0, 5); // Return top 5 suggestions } addTagBasedSuggestions(tags, suggestions, seenSuggestions) { // Framework-based suggestions if (tags.frameworkTags.includes('nextjs') && !seenSuggestions.has('web/nextjs')) { suggestions.push({ path: 'web/nextjs', confidence: 0.9, reason: 'Next.js framework detected', category: 'framework' }); } if (tags.frameworkTags.includes('react') && !seenSuggestions.has('web/react')) { suggestions.push({ path: 'web/react', confidence: 0.8, reason: 'React framework detected', category: 'framework' }); } // Semantic-based suggestions if (tags.semanticTags.includes('ai') && !seenSuggestions.has('ai-ml')) { suggestions.push({ path: 'ai-ml', confidence: 0.8, reason: 'AI/ML project detected', category: 'purpose' }); } if (tags.semanticTags.includes('api') && !seenSuggestions.has('backend/api')) { suggestions.push({ path: 'backend/api', confidence: 0.7, reason: 'API project detected', category: 'purpose' }); } // Language-based suggestions if (tags.fileTypeTags.includes('python') && !seenSuggestions.has('python')) { suggestions.push({ path: 'python', confidence: 0.6, reason: 'Python project detected', category: 'language' }); } } detectMonorepo(structure) { const monorepoIndicators = [ 'packages', 'apps', 'libs', 'modules', 'workspaces' ]; return structure.directories.some(dir => monorepoIndicators.includes(dir.name.toLowerCase())); } detectTests(structure) { const testIndicators = [ /test/i, /__tests__/, /spec/i, /\.test\./, /\.spec\./ ]; return structure.files.some(file => testIndicators.some(pattern => pattern.test(file.name))) || structure.directories.some(dir => testIndicators.some(pattern => pattern.test(dir.name))); } detectDocumentation(structure) { const docIndicators = [ 'README.md', 'CHANGELOG.md', 'CONTRIBUTING.md', 'docs', 'documentation', 'wiki' ]; return structure.files.some(file => docIndicators.includes(file.name)) || structure.directories.some(dir => docIndicators.includes(dir.name.toLowerCase())); } assessComplexity(structure) { const { totalFiles, totalDirectories, depth } = structure; // Simple heuristics for complexity assessment if (totalFiles <= 10 && totalDirectories <= 3 && depth <= 2) { return 'simple'; } else if (totalFiles <= 50 && totalDirectories <= 15 && depth <= 4) { return 'moderate'; } else { return 'complex'; } } // Get the best target suggestion getBestTargetSuggestion(analysis) { if (analysis.targetSuggestions.length === 0) { return 'general'; } return analysis.targetSuggestions[0].path; } // Get suggestions by category getSuggestionsByCategory(analysis) { const categorized = { framework: [], language: [], purpose: [], structure: [] }; for (const suggestion of analysis.targetSuggestions) { categorized[suggestion.category].push(suggestion); } return categorized; } } exports.StructureAnalyzer = StructureAnalyzer; //# sourceMappingURL=structureAnalyzer.js.map