This commit is contained in:
171
app/src/analyzers/projectTypeDetector.ts
Normal file
171
app/src/analyzers/projectTypeDetector.ts
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { ProjectInfo, ProjectType, Framework } from '../types';
|
||||||
|
import { parseJSONFile, parseTOMLFile, getFileExtension, isFileExists } from '../utils/fileUtils';
|
||||||
|
|
||||||
|
const LANGUAGE_FILE_EXTENSIONS: Record<ProjectType, string[]> = {
|
||||||
|
node: ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'],
|
||||||
|
python: ['.py', '.pyw'],
|
||||||
|
go: ['.go'],
|
||||||
|
rust: ['.rs'],
|
||||||
|
java: ['.java', '.kt', '.scala'],
|
||||||
|
unknown: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEPENDENCY_FILES: Record<string, ProjectType> = {
|
||||||
|
'package.json': 'node',
|
||||||
|
'requirements.txt': 'python',
|
||||||
|
'pyproject.toml': 'python',
|
||||||
|
'setup.py': 'python',
|
||||||
|
'go.mod': 'go',
|
||||||
|
'Cargo.toml': 'rust',
|
||||||
|
'pom.xml': 'java',
|
||||||
|
'build.gradle': 'java',
|
||||||
|
'build.gradle.kts': 'java'
|
||||||
|
};
|
||||||
|
|
||||||
|
export function detectProjectType(directory: string): ProjectType {
|
||||||
|
const files = fs.readdirSync(directory);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (DEPENDENCY_FILES[file]) {
|
||||||
|
return DEPENDENCY_FILES[file];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileExtensions = new Set<ProjectType>();
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const fullPath = path.join(directory, file);
|
||||||
|
if (fs.statSync(fullPath).isFile()) {
|
||||||
|
const ext = getFileExtension(file);
|
||||||
|
|
||||||
|
for (const [type, extensions] of Object.entries(LANGUAGE_FILE_EXTENSIONS)) {
|
||||||
|
if (extensions.includes(ext)) {
|
||||||
|
fileExtensions.add(type as ProjectType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExtensions.size === 1) {
|
||||||
|
return fileExtensions.values().next().value as ProjectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExtensions.has('node') && fileExtensions.has('python')) {
|
||||||
|
return 'node';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectFramework(directory: string, projectType: ProjectType): Framework {
|
||||||
|
if (projectType === 'unknown') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJsonPath = path.join(directory, 'package.json');
|
||||||
|
const packageJson = parseJSONFile<{ dependencies?: Record<string, string>; devDependencies?: Record<string, string> }>(packageJsonPath);
|
||||||
|
const deps = {
|
||||||
|
...packageJson?.dependencies,
|
||||||
|
...packageJson?.devDependencies
|
||||||
|
};
|
||||||
|
|
||||||
|
const frameworkPatterns: Record<string, string[]> = {
|
||||||
|
react: ['react', 'react-dom'],
|
||||||
|
vue: ['vue'],
|
||||||
|
angular: ['@angular/core'],
|
||||||
|
nextjs: ['next'],
|
||||||
|
nuxt: ['nuxt'],
|
||||||
|
express: ['express'],
|
||||||
|
fastify: ['fastify'],
|
||||||
|
nestjs: ['@nestjs/core'],
|
||||||
|
django: ['django'],
|
||||||
|
flask: ['flask'],
|
||||||
|
fastapi: ['fastapi'],
|
||||||
|
gin: ['github.com/gin-gonic/gin'],
|
||||||
|
fiber: ['github.com/gofiber/fiber'],
|
||||||
|
spring: ['spring-boot']
|
||||||
|
};
|
||||||
|
|
||||||
|
if (projectType === 'node' && packageJson) {
|
||||||
|
for (const [framework, packages] of Object.entries(frameworkPatterns)) {
|
||||||
|
if (['react', 'vue', 'angular', 'nextjs', 'nuxt', 'express', 'fastify', 'nestjs'].includes(framework)) {
|
||||||
|
for (const pkg of packages) {
|
||||||
|
if (deps[pkg]) {
|
||||||
|
return framework as Framework;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectType === 'python') {
|
||||||
|
const pyprojectPath = path.join(directory, 'pyproject.toml');
|
||||||
|
const pyproject = parseTOMLFile(pyprojectPath);
|
||||||
|
|
||||||
|
if (pyproject?.tool) {
|
||||||
|
const tool = pyproject.tool as Record<string, unknown>;
|
||||||
|
if (tool['fastapi']) return 'fastapi';
|
||||||
|
if (tool['flask']) return 'flask';
|
||||||
|
if (tool['django']) return 'django';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectType === 'go') {
|
||||||
|
const goModPath = path.join(directory, 'go.mod');
|
||||||
|
const goMod = parseTOMLFile(goModPath);
|
||||||
|
|
||||||
|
if (goMod?.require) {
|
||||||
|
const require = goMod.require as Array<{ path: string }>;
|
||||||
|
for (const dep of require) {
|
||||||
|
if (dep.path === 'github.com/gin-gonic/gin') return 'gin';
|
||||||
|
if (dep.path === 'github.com/gofiber/fiber') return 'fiber';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectType === 'java') {
|
||||||
|
if (isFileExists(path.join(directory, 'pom.xml'))) {
|
||||||
|
return 'maven';
|
||||||
|
}
|
||||||
|
if (isFileExists(path.join(directory, 'build.gradle')) ||
|
||||||
|
isFileExists(path.join(directory, 'build.gradle.kts'))) {
|
||||||
|
return 'gradle';
|
||||||
|
}
|
||||||
|
if (isFileExists(path.join(directory, 'pom.xml'))) {
|
||||||
|
const pom = parseJSONFile<{ project?: { packaging?: string } }>(path.join(directory, 'pom.xml'));
|
||||||
|
if (pom?.project?.packaging === 'jar') {
|
||||||
|
return 'spring';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getProjectInfo(directory: string): Promise<ProjectInfo> {
|
||||||
|
const projectType = detectProjectType(directory);
|
||||||
|
const framework = detectFramework(directory, projectType);
|
||||||
|
|
||||||
|
let languageVersion: string | undefined;
|
||||||
|
|
||||||
|
if (projectType === 'node') {
|
||||||
|
const packageJson = parseJSONFile<{ engines?: { node?: string } }>(path.join(directory, 'package.json'));
|
||||||
|
languageVersion = packageJson?.engines?.node;
|
||||||
|
} else if (projectType === 'python') {
|
||||||
|
const pyproject = parseJSONFile<{ 'requires-python'?: string }>(path.join(directory, 'pyproject.toml'));
|
||||||
|
languageVersion = pyproject?.['requires-python'];
|
||||||
|
} else if (projectType === 'go') {
|
||||||
|
const goMod = parseJSONFile<{ go?: string }>(path.join(directory, 'go.mod'));
|
||||||
|
languageVersion = goMod?.go;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: projectType,
|
||||||
|
framework: framework,
|
||||||
|
language: projectType.charAt(0).toUpperCase() + projectType.slice(1),
|
||||||
|
languageVersion,
|
||||||
|
frameworkVersion: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user