Add framework detector and setup generator
This commit is contained in:
231
src/contextgen/analyzers/framework_detector.py
Normal file
231
src/contextgen/analyzers/framework_detector.py
Normal file
@@ -0,0 +1,231 @@
|
||||
"""Framework detector by analyzing dependencies and file patterns."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from contextgen.analyzers.base import BaseAnalyzer
|
||||
|
||||
|
||||
FRAMEWORK_INDICATORS: dict[str, dict[str, list[str]]] = {
|
||||
"Django": {
|
||||
"files": ["manage.py", "django-admin.py", "urls.py", "settings.py", "wsgi.py", "asgi.py"],
|
||||
"dependencies": ["django", "djangorestframework", "django-filter"],
|
||||
"imports": ["from django", "import django"],
|
||||
},
|
||||
"Flask": {
|
||||
"files": ["app.py", "wsgi.py"],
|
||||
"dependencies": ["flask"],
|
||||
"imports": ["from flask", "import flask"],
|
||||
},
|
||||
"FastAPI": {
|
||||
"files": ["main.py", "app.py"],
|
||||
"dependencies": ["fastapi", "uvicorn"],
|
||||
"imports": ["from fastapi", "import fastapi"],
|
||||
},
|
||||
"React": {
|
||||
"files": ["package.json", "tsconfig.json", "webpack.config.js"],
|
||||
"dependencies": ["react", "react-dom", "react-scripts"],
|
||||
"imports": ["from 'react'", 'from "react"', "import React"],
|
||||
},
|
||||
"Vue": {
|
||||
"files": ["package.json", "vue.config.js", "vue.config.ts"],
|
||||
"dependencies": ["vue", "@vue/cli", "vue-loader"],
|
||||
"imports": ["from 'vue'", 'from "vue"', "import Vue"],
|
||||
},
|
||||
"Next.js": {
|
||||
"files": ["next.config.js", "next.config.ts", "pages/", "app/"],
|
||||
"dependencies": ["next", "react", "react-dom"],
|
||||
"imports": ["from 'next'", 'from "next"', "next/router"],
|
||||
},
|
||||
"Express": {
|
||||
"files": ["app.js", "server.js", "index.js"],
|
||||
"dependencies": ["express"],
|
||||
"imports": ["from 'express'", 'from "express"', "import express"],
|
||||
},
|
||||
"Spring": {
|
||||
"files": ["pom.xml", "build.gradle", "application.properties", "application.yml"],
|
||||
"dependencies": ["spring-boot", "springframework"],
|
||||
"imports": ["import org.springframework", "@SpringBootApplication"],
|
||||
},
|
||||
"Gin": {
|
||||
"files": ["main.go", "go.mod"],
|
||||
"dependencies": ["github.com/gin-gonic/gin"],
|
||||
"imports": ['"github.com/gin-gonic/gin"'],
|
||||
},
|
||||
"Rails": {
|
||||
"files": ["Gemfile", "config.ru", "app/controllers/", "config/routes.rb"],
|
||||
"dependencies": ["rails"],
|
||||
"imports": ["class ApplicationController < ActionController"],
|
||||
},
|
||||
"Laravel": {
|
||||
"files": ["composer.json", "artisan", "config/app.php"],
|
||||
"dependencies": ["laravel/framework"],
|
||||
"imports": ["use Illuminate", "Route::"],
|
||||
},
|
||||
"Angular": {
|
||||
"files": ["angular.json", "package.json", "tsconfig.json"],
|
||||
"dependencies": ["@angular/core", "@angular/cli"],
|
||||
"imports": ["@Component", "@Injectable", "@angular/core"],
|
||||
},
|
||||
"Svelte": {
|
||||
"files": ["svelte.config.js", "package.json"],
|
||||
"dependencies": ["svelte"],
|
||||
"imports": ["from 'svelte'", 'from "svelte"', "import Svelte"],
|
||||
},
|
||||
"Phoenix": {
|
||||
"files": ["mix.exs", "config/config.exs"],
|
||||
"dependencies": ["phoenix"],
|
||||
"imports": ["import Phoenix", "use Phoenix"],
|
||||
},
|
||||
"NestJS": {
|
||||
"files": ["nest-cli.json", "tsconfig.json"],
|
||||
"dependencies": ["@nestjs/core", "@nestjs/common"],
|
||||
"imports": ["@nestjs/common", "@Controller", "@Injectable"],
|
||||
},
|
||||
}
|
||||
|
||||
BUILD_TOOLS: dict[str, dict[str, list[str]]] = {
|
||||
"npm": {
|
||||
"files": ["package.json", "package-lock.json", "npm-shrinkwrap.json"],
|
||||
"commands": ["npm install", "npm run", "npm build"],
|
||||
},
|
||||
"yarn": {
|
||||
"files": ["yarn.lock", "package.json"],
|
||||
"commands": ["yarn install", "yarn run", "yarn build"],
|
||||
},
|
||||
"pnpm": {
|
||||
"files": ["pnpm-lock.yaml", "package.json"],
|
||||
"commands": ["pnpm install", "pnpm run", "pnpm build"],
|
||||
},
|
||||
"pip": {
|
||||
"files": ["requirements.txt", "Pipfile", "pyproject.toml", "setup.py"],
|
||||
"commands": ["pip install", "pipenv install"],
|
||||
},
|
||||
"poetry": {
|
||||
"files": ["pyproject.toml", "poetry.lock"],
|
||||
"commands": ["poetry install", "poetry run"],
|
||||
},
|
||||
"cargo": {
|
||||
"files": ["Cargo.toml", "Cargo.lock"],
|
||||
"commands": ["cargo build", "cargo run", "cargo test"],
|
||||
},
|
||||
"maven": {
|
||||
"files": ["pom.xml"],
|
||||
"commands": ["mvn install", "mvn compile", "mvn test"],
|
||||
},
|
||||
"gradle": {
|
||||
"files": ["build.gradle", "build.gradle.kts", "gradlew"],
|
||||
"commands": ["./gradlew build", "gradle build", "./gradlew test"],
|
||||
},
|
||||
"go_modules": {
|
||||
"files": ["go.mod", "go.sum"],
|
||||
"commands": ["go mod download", "go build", "go run"],
|
||||
},
|
||||
"bundler": {
|
||||
"files": ["Gemfile", "Gemfile.lock"],
|
||||
"commands": ["bundle install", "bundle exec"],
|
||||
},
|
||||
"composer": {
|
||||
"files": ["composer.json", "composer.lock"],
|
||||
"commands": ["composer install", "composer update"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class FrameworkDetector(BaseAnalyzer):
|
||||
"""Detects frameworks and build tools used in the project."""
|
||||
|
||||
def analyze(self) -> dict[str, Any]:
|
||||
"""Detect frameworks and build tools."""
|
||||
frameworks = self._detect_frameworks()
|
||||
build_tools = self._detect_build_tools()
|
||||
|
||||
return {
|
||||
"frameworks": frameworks,
|
||||
"build_tools": build_tools,
|
||||
"full_stack": self._detect_full_stack(frameworks),
|
||||
}
|
||||
|
||||
def _detect_frameworks(self) -> list[dict[str, Any]]:
|
||||
"""Detect frameworks from file patterns and dependencies."""
|
||||
detected: list[dict[str, Any]] = []
|
||||
|
||||
for framework, indicators in FRAMEWORK_INDICATORS.items():
|
||||
score = 0
|
||||
|
||||
for filename in indicators["files"]:
|
||||
if (self.project_path / filename).exists():
|
||||
score += 2
|
||||
|
||||
if self._has_dependency(indicators["dependencies"]):
|
||||
score += 3
|
||||
|
||||
if score >= 3:
|
||||
detected.append({
|
||||
"name": framework,
|
||||
"confidence": min(score / 5, 1.0),
|
||||
"indicators_found": score,
|
||||
})
|
||||
|
||||
detected.sort(key=lambda x: x["confidence"], reverse=True)
|
||||
return detected
|
||||
|
||||
def _detect_build_tools(self) -> list[dict[str, Any]]:
|
||||
"""Detect build tools from file patterns."""
|
||||
detected: list[dict[str, Any]] = []
|
||||
|
||||
for tool, indicators in BUILD_TOOLS.items():
|
||||
score = 0
|
||||
|
||||
for filename in indicators["files"]:
|
||||
if (self.project_path / filename).exists():
|
||||
score += 2
|
||||
|
||||
if score >= 2:
|
||||
detected.append({
|
||||
"name": tool,
|
||||
"confidence": min(score / 3, 1.0),
|
||||
})
|
||||
|
||||
detected.sort(key=lambda x: x["confidence"], reverse=True)
|
||||
return detected
|
||||
|
||||
def _has_dependency(self, dependencies: list[str]) -> bool:
|
||||
"""Check if any dependency is found in project files."""
|
||||
config_files = [
|
||||
self.project_path / "package.json",
|
||||
self.project_path / "pyproject.toml",
|
||||
self.project_path / "requirements.txt",
|
||||
self.project_path / "Cargo.toml",
|
||||
self.project_path / "pom.xml",
|
||||
self.project_path / "build.gradle",
|
||||
self.project_path / "go.mod",
|
||||
self.project_path / "Gemfile",
|
||||
self.project_path / "composer.json",
|
||||
]
|
||||
|
||||
for config_file in config_files:
|
||||
if config_file.exists():
|
||||
content = self._safe_read_file(config_file)
|
||||
if content:
|
||||
for dep in dependencies:
|
||||
if dep in content:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _detect_full_stack(self, frameworks: list[dict[str, Any]]) -> dict[str, str | None]:
|
||||
"""Detect full stack technologies."""
|
||||
frontend = None
|
||||
backend = None
|
||||
|
||||
frontend_frameworks = ["React", "Vue", "Next.js", "Angular", "Svelte"]
|
||||
backend_frameworks = ["Django", "Flask", "FastAPI", "Express", "Spring", "Gin", "Rails", "Laravel", "Phoenix", "NestJS"]
|
||||
|
||||
for fw in frameworks:
|
||||
name = fw["name"]
|
||||
if name in frontend_frameworks:
|
||||
frontend = name
|
||||
elif name in backend_frameworks:
|
||||
backend = name
|
||||
|
||||
return {"frontend": frontend, "backend": backend}
|
||||
Reference in New Issue
Block a user