UNPKG

context-forge

Version:

AI orchestration platform with autonomous teams, enhancement planning, migration tools, 25+ slash commands, checkpoints & hooks. Multi-IDE: Claude, Cursor, Windsurf, Cline, Copilot

424 lines 16.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FrameworkDetector = void 0; const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const glob_1 = require("glob"); class FrameworkDetector { constructor(projectPath) { this.projectPath = projectPath; this.patterns = this.initializePatterns(); } initializePatterns() { return [ // React { framework: 'react', files: ['package.json'], dependencies: ['react', 'react-dom'], content: [ { file: '**/*.{js,jsx,ts,tsx}', pattern: /from ['"]react['"]/, weight: 10 }, { file: '**/*.{js,jsx,ts,tsx}', pattern: /React\.Component/, weight: 5 }, ], structure: ['src'], priority: 100, variants: [ { name: 'next.js', dependencies: ['next'], files: ['next.config.js', 'next.config.mjs', 'next.config.ts'], }, { name: 'gatsby', dependencies: ['gatsby'], files: ['gatsby-config.js'], }, { name: 'create-react-app', files: ['public/index.html'], devDependencies: ['react-scripts'], }, ], }, // Vue { framework: 'vue', files: ['package.json'], dependencies: ['vue'], content: [ { file: '**/*.vue', pattern: /<template>/, weight: 20 }, { file: '**/*.{js,ts}', pattern: /from ['"]vue['"]/, weight: 10 }, { file: '**/*.{js,ts}', pattern: /createApp|Vue\.component/, weight: 15 }, ], priority: 95, variants: [ { name: 'nuxt', dependencies: ['nuxt'], files: ['nuxt.config.js', 'nuxt.config.ts'], }, { name: 'vue-cli', files: ['vue.config.js'], }, { name: 'vite-vue', devDependencies: ['@vitejs/plugin-vue'], files: ['vite.config.js', 'vite.config.ts'], }, ], }, // Angular { framework: 'angular', files: ['angular.json', 'package.json'], dependencies: ['@angular/core', '@angular/common'], content: [ { file: '**/*.ts', pattern: /@Component\({/, weight: 20 }, { file: '**/*.ts', pattern: /from ['"]@angular/, weight: 10 }, ], structure: ['src/app'], priority: 90, }, // Express { framework: 'express', files: ['package.json'], dependencies: ['express'], content: [ { file: '**/*.{js,ts}', pattern: /require\(['"]express['"]\)/, weight: 15 }, { file: '**/*.{js,ts}', pattern: /from ['"]express['"]/, weight: 15 }, { file: '**/*.{js,ts}', pattern: /app\.(get|post|put|delete|use)\(/, weight: 10 }, ], priority: 80, }, // NestJS { framework: 'nestjs', files: ['package.json', 'nest-cli.json'], dependencies: ['@nestjs/core', '@nestjs/common'], content: [ { file: '**/*.ts', pattern: /@Module\({/, weight: 20 }, { file: '**/*.ts', pattern: /@Controller\(/, weight: 15 }, { file: '**/*.ts', pattern: /from ['"]@nestjs/, weight: 10 }, ], priority: 85, }, // Django { framework: 'django', files: ['manage.py', 'requirements.txt'], content: [ { file: 'manage.py', pattern: /django/, weight: 30 }, { file: 'requirements.txt', pattern: /django/i, weight: 20 }, { file: '**/*.py', pattern: /from django/, weight: 10 }, { file: '**/settings.py', pattern: /INSTALLED_APPS/, weight: 15 }, ], structure: ['apps', 'templates'], priority: 85, }, // FastAPI { framework: 'fastapi', files: ['requirements.txt', 'pyproject.toml'], content: [ { file: 'requirements.txt', pattern: /fastapi/i, weight: 25 }, { file: 'pyproject.toml', pattern: /fastapi/, weight: 25 }, { file: '**/*.py', pattern: /from fastapi import/, weight: 20 }, { file: '**/*.py', pattern: /FastAPI\(\)/, weight: 15 }, ], priority: 75, }, // Ruby on Rails { framework: 'rails', files: ['Gemfile', 'config/routes.rb'], content: [ { file: 'Gemfile', pattern: /gem ['"]rails['"]/, weight: 30 }, { file: 'config/routes.rb', pattern: /Rails\.application\.routes/, weight: 20 }, ], structure: ['app/controllers', 'app/models', 'app/views'], priority: 85, }, // Laravel { framework: 'laravel', files: ['composer.json', 'artisan'], content: [ { file: 'composer.json', pattern: /"laravel\/framework"/, weight: 30 }, { file: 'artisan', pattern: /Laravel/, weight: 20 }, ], structure: ['app/Http/Controllers', 'resources/views'], priority: 80, }, // Spring Boot { framework: 'spring-boot', files: ['pom.xml', 'build.gradle'], content: [ { file: 'pom.xml', pattern: /spring-boot-starter/, weight: 30 }, { file: 'build.gradle', pattern: /org\.springframework\.boot/, weight: 30 }, { file: '**/*.java', pattern: /@SpringBootApplication/, weight: 25 }, { file: '**/*.java', pattern: /@RestController/, weight: 15 }, ], structure: ['src/main/java', 'src/main/resources'], priority: 80, }, ].sort((a, b) => b.priority - a.priority); } async detectFrameworks() { const detectedFrameworks = []; for (const pattern of this.patterns) { const confidence = await this.calculateConfidence(pattern); if (confidence > 30) { const version = await this.detectVersion(pattern); const variant = await this.detectVariant(pattern); detectedFrameworks.push({ framework: pattern.framework, version, variant, confidence, }); } } // Sort by confidence detectedFrameworks.sort((a, b) => b.confidence - a.confidence); const primary = detectedFrameworks.find((f) => f.confidence >= 70); const secondary = detectedFrameworks.filter((f) => f.confidence >= 50 && f.framework !== primary?.framework); return { primary, secondary, allDetected: detectedFrameworks, }; } async calculateConfidence(pattern) { let confidence = 0; // Check required files for (const file of pattern.files) { const exists = await this.fileExists(file); if (exists) { confidence += 30 / pattern.files.length; } } // Check dependencies if (pattern.dependencies || pattern.devDependencies) { const packageJson = await this.readPackageJson(); if (packageJson) { const deps = Object.keys(packageJson.dependencies || {}); const devDeps = Object.keys(packageJson.devDependencies || {}); pattern.dependencies?.forEach((dep) => { if (deps.includes(dep)) { confidence += Math.min(20, 40 / (pattern.dependencies?.length || 1)); } }); pattern.devDependencies?.forEach((dep) => { if (devDeps.includes(dep)) { confidence += Math.min(10, 20 / (pattern.devDependencies?.length || 1)); } }); } } // Check content patterns if (pattern.content) { for (const contentPattern of pattern.content) { const matches = await this.checkContentPattern(contentPattern); if (matches) { confidence += contentPattern.weight; } } } // Check directory structure if (pattern.structure) { for (const dir of pattern.structure) { const exists = await this.directoryExists(dir); if (exists) { confidence += 10 / pattern.structure.length; } } } return Math.min(100, Math.round(confidence)); } async detectVersion(pattern) { // Try package.json first const packageJson = await this.readPackageJson(); if (packageJson) { const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies, }; // Check main framework dependency if (pattern.dependencies) { for (const dep of pattern.dependencies) { if (allDeps[dep]) { return this.cleanVersion(allDeps[dep]); } } } } // Try lock files for exact versions const lockFile = await this.readLockFile(); if (lockFile && pattern.dependencies) { for (const dep of pattern.dependencies) { const version = this.extractVersionFromLock(lockFile, dep); if (version) return version; } } return undefined; } async detectVariant(pattern) { if (!pattern.variants) return undefined; for (const variant of pattern.variants) { let isVariant = false; // Check variant-specific files if (variant.files) { for (const file of variant.files) { if (await this.fileExists(file)) { isVariant = true; break; } } } // Check variant dependencies if (!isVariant && variant.dependencies) { const packageJson = await this.readPackageJson(); if (packageJson) { const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies, }; for (const dep of variant.dependencies) { if (allDeps[dep]) { isVariant = true; break; } } } } if (isVariant) { return variant.name; } } return undefined; } async fileExists(filePath) { try { await fs.access(path.join(this.projectPath, filePath)); return true; } catch { return false; } } async directoryExists(dirPath) { try { const stats = await fs.stat(path.join(this.projectPath, dirPath)); return stats.isDirectory(); } catch { return false; } } async readPackageJson() { try { const content = await fs.readFile(path.join(this.projectPath, 'package.json'), 'utf-8'); return JSON.parse(content); } catch { return null; } } async readLockFile() { const lockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']; for (const lockFile of lockFiles) { try { const content = await fs.readFile(path.join(this.projectPath, lockFile), 'utf-8'); return content; } catch { continue; } } return null; } async checkContentPattern(pattern) { try { const files = await (0, glob_1.glob)(pattern.file, { cwd: this.projectPath, ignore: ['node_modules/**', 'dist/**', 'build/**'], nodir: true, }); for (const file of files.slice(0, 10)) { // Check first 10 files try { const content = await fs.readFile(path.join(this.projectPath, file), 'utf-8'); if (pattern.pattern.test(content)) { return true; } } catch { continue; } } } catch { return false; } return false; } cleanVersion(version) { // Remove version prefixes like ^, ~, >=, etc. return version.replace(/^[\^~>=<\s]+/, ''); } extractVersionFromLock(lockContent, packageName) { // Simple extraction for different lock file formats const patterns = [ new RegExp(`"${packageName}"[^"]*"version"[^"]*"([^"]+)"`, 'i'), new RegExp(`${packageName}@([^\\s]+)`, 'i'), new RegExp(`${packageName}:\\s*version[^\\d]*(\\d+\\.\\d+\\.\\d+)`, 'i'), ]; for (const pattern of patterns) { const match = lockContent.match(pattern); if (match && match[1]) { return this.cleanVersion(match[1]); } } return undefined; } } exports.FrameworkDetector = FrameworkDetector; //# sourceMappingURL=frameworkDetector.js.map