UNPKG

@morodomi/ait3

Version:

AIT³ Development Platform - AI + Ticket + Test + Tool driven development methodology

271 lines (270 loc) 9.44 kB
import { readdir, stat, readFile, access } from 'fs/promises'; import { join } from 'path'; import { constants } from 'fs'; export class DirectoryStructureAnalyzer { rootPath; constructor(rootPath) { this.rootPath = rootPath; } async analyzeStructure(path) { const targetPath = path || this.rootPath; const directories = await this.scanDirectories(targetPath); const hasGitRepository = await this.checkForGit(targetPath); const hasCICD = await this.checkForCICD(targetPath); const hasDocker = await this.checkForDocker(targetPath); return { rootPath: targetPath, directories, hasGitRepository, hasCICD, hasDocker }; } async detectFramework(path, language) { const targetPath = path || this.rootPath; // TypeScript/JavaScript frameworks if (language === 'TypeScript' || language === 'JavaScript' || !language) { // Next.js if (await this.fileExists(join(targetPath, 'next.config.js')) || await this.fileExists(join(targetPath, 'next.config.mjs'))) { const version = await this.getPackageVersion(targetPath, 'next'); return { name: 'Next.js', version, type: 'fullstack', confidence: 0.95 }; } // Express const packageJson = await this.readPackageJson(targetPath); if (packageJson?.dependencies?.express) { return { name: 'Express', version: this.extractVersion(packageJson.dependencies.express), type: 'backend', confidence: 0.85 }; } // Check for library project if (packageJson?.main || packageJson?.types) { return { name: 'Node.js Library', version: packageJson.version, type: 'library', confidence: 0.7 }; } } // Python frameworks if (language === 'Python' || !language) { // Django if (await this.fileExists(join(targetPath, 'manage.py'))) { const requirementsVersion = await this.getRequirementsVersion(targetPath, 'django'); return { name: 'Django', version: requirementsVersion || '4.2.0', type: 'fullstack', confidence: 0.95 }; } // Flask const requirements = await this.readRequirements(targetPath); if (requirements?.includes('flask')) { const version = await this.getRequirementsVersion(targetPath, 'flask'); return { name: 'Flask', version: version || '2.3.0', type: 'backend', confidence: 0.9 }; } } // PHP frameworks if (language === 'PHP' || !language) { // Laravel if (await this.fileExists(join(targetPath, 'artisan'))) { const composerJson = await this.readComposerJson(targetPath); const version = this.extractVersion(composerJson?.require?.['laravel/framework']); return { name: 'Laravel', version: version || '10.0', type: 'fullstack', confidence: 0.95 }; } } return { name: 'Unknown', type: 'unknown', confidence: 0 }; } async scanDirectories(targetPath) { const directories = []; try { const entries = await readdir(targetPath); for (const entry of entries) { const fullPath = join(targetPath, entry); const stats = await stat(fullPath); if (stats.isDirectory() && !this.shouldIgnoreDirectory(entry)) { const fileCount = await this.countFiles(fullPath); const type = this.detectDirectoryType(entry); directories.push({ path: fullPath, name: entry, type, fileCount }); } } } catch { // Return empty array on error } return directories.sort((a, b) => a.name.localeCompare(b.name)); } async countFiles(dir) { let count = 0; try { const entries = await readdir(dir); for (const entry of entries) { const fullPath = join(dir, entry); const stats = await stat(fullPath); if (stats.isFile()) { count++; } else if (stats.isDirectory() && !this.shouldIgnoreDirectory(entry)) { count += await this.countFiles(fullPath); } } } catch { // Ignore errors } return count; } detectDirectoryType(name) { // Source directories if (['src', 'lib', 'app', 'source'].includes(name)) { return 'source'; } // Test directories if (['test', 'tests', 'spec', 'specs', '__tests__'].includes(name)) { return 'test'; } // Build/output directories if (['dist', 'build', 'out', 'public', 'target'].includes(name)) { return 'build'; } // Configuration directories if (['config', 'configs', '.config'].includes(name)) { return 'config'; } // Documentation if (['docs', 'doc', 'documentation'].includes(name)) { return 'docs'; } return 'other'; } shouldIgnoreDirectory(name) { const ignoreDirs = [ 'node_modules', '.git', '.svn', '.hg', 'coverage', '.nyc_output', '.next', '__pycache__', '.pytest_cache', 'vendor', '.venv', 'venv', 'env', '.env', '.idea', '.vscode', '.DS_Store' ]; return ignoreDirs.includes(name) || name.startsWith('.'); } async checkForGit(targetPath) { return await this.fileExists(join(targetPath, '.git')) || await this.fileExists(join(targetPath, '.gitignore')); } async checkForCICD(targetPath) { return await this.fileExists(join(targetPath, '.github/workflows')) || await this.fileExists(join(targetPath, '.gitlab-ci.yml')) || await this.fileExists(join(targetPath, 'Jenkinsfile')) || await this.fileExists(join(targetPath, '.circleci')) || await this.fileExists(join(targetPath, 'azure-pipelines.yml')); } async checkForDocker(targetPath) { return await this.fileExists(join(targetPath, 'Dockerfile')) || await this.fileExists(join(targetPath, 'docker-compose.yml')) || await this.fileExists(join(targetPath, 'docker-compose.yaml')); } async fileExists(filePath) { try { await access(filePath, constants.F_OK); return true; } catch { return false; } } async readPackageJson(targetPath) { try { const content = await readFile(join(targetPath, 'package.json'), 'utf-8'); return JSON.parse(content); } catch { return null; } } async readComposerJson(targetPath) { try { const content = await readFile(join(targetPath, 'composer.json'), 'utf-8'); return JSON.parse(content); } catch { return null; } } async readRequirements(targetPath) { try { return await readFile(join(targetPath, 'requirements.txt'), 'utf-8'); } catch { return null; } } async getPackageVersion(targetPath, packageName) { const packageJson = await this.readPackageJson(targetPath); if (packageJson?.dependencies?.[packageName]) { return this.extractVersion(packageJson.dependencies[packageName]); } return undefined; } async getRequirementsVersion(targetPath, packageName) { const requirements = await this.readRequirements(targetPath); if (!requirements) return null; const lines = requirements.split('\n'); for (const line of lines) { if (line.toLowerCase().startsWith(packageName)) { const match = line.match(/==(.+)/); if (match) return match[1]; } } return null; } extractVersion(versionString) { if (!versionString) return undefined; // Remove prefixes like ^, ~, >=, etc. const match = versionString.match(/\d+\.\d+(\.\d+)?/); return match ? match[0] : undefined; } }