mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
228 lines • 9.37 kB
JavaScript
/**
* Project Environment Detector
* Detects project type, languages, frameworks, build systems, and package managers
*/
import fs from 'fs-extra';
import * as path from 'path';
import { glob } from 'glob';
import * as os from 'os';
import { LanguageConfigManager } from './LanguageConfigManager.js';
export class ProjectEnvironmentDetector {
projectRoot;
languageManager;
environment;
constructor(projectRoot) {
this.projectRoot = projectRoot;
this.languageManager = new LanguageConfigManager();
this.environment = {
languages: [],
frameworks: [],
buildSystems: [],
packageManagers: [],
operatingSystem: os.platform(),
architecture: os.arch(),
projectType: 'single'
};
}
async detectEnvironment() {
await this.detectLanguages();
await this.detectFrameworks();
await this.detectBuildSystems();
this.determineProjectType();
return this.environment;
}
async detectLanguages() {
// Detect languages by file extensions
const allFiles = await glob('**/*', {
cwd: this.projectRoot,
ignore: ['node_modules/**', 'dist/**', 'build/**', '.git/**'],
absolute: false
});
const languageStats = new Map();
let totalLinesOfCode = 0;
// Count files by language
for (const file of allFiles) {
const ext = path.extname(file).toLowerCase().substring(1);
const language = this.languageManager.getLanguageFromExtension(ext);
if (language) {
const stats = languageStats.get(language) || { files: 0, linesOfCode: 0 };
stats.files++;
// Count lines of code
try {
const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8');
const loc = content.split('\n').length;
stats.linesOfCode += loc;
totalLinesOfCode += loc;
}
catch (error) {
// Skip files that can't be read
}
languageStats.set(language, stats);
}
}
// Convert to language info
this.environment.languages = Array.from(languageStats.entries()).map(([language, stats]) => {
const config = this.languageManager.getConfig(language);
return {
name: config.name,
percentage: totalLinesOfCode > 0 ? (stats.linesOfCode / totalLinesOfCode) * 100 : 0,
files: stats.files,
linesOfCode: stats.linesOfCode,
entryPoints: [],
buildFiles: [],
packageFiles: []
};
}).sort((a, b) => b.percentage - a.percentage);
}
async detectFrameworks() {
const frameworks = new Set();
for (const language of this.languageManager.getAllLanguages()) {
const config = this.languageManager.getConfig(language);
if (!config)
continue;
// Check package files for framework dependencies
for (const packageFile of config.packageFiles) {
const packageFiles = await glob(packageFile, {
cwd: this.projectRoot,
ignore: ['node_modules/**'],
absolute: false
});
for (const file of packageFiles) {
try {
const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8');
// Check for framework detection patterns
for (const [framework, patterns] of Object.entries(config.frameworkDetection)) {
for (const pattern of patterns) {
if (content.includes(pattern)) {
frameworks.add(framework);
}
}
}
}
catch (error) {
// Skip files that can't be read
}
}
}
}
this.environment.frameworks = Array.from(frameworks);
}
async detectBuildSystems() {
const buildSystems = new Set();
const packageManagers = new Set();
// Build system detection
const buildFiles = [
{ pattern: 'webpack.config.*', system: 'Webpack' },
{ pattern: 'vite.config.*', system: 'Vite' },
{ pattern: 'rollup.config.*', system: 'Rollup' },
{ pattern: 'gulpfile.*', system: 'Gulp' },
{ pattern: 'Gruntfile.*', system: 'Grunt' },
{ pattern: 'CMakeLists.txt', system: 'CMake' },
{ pattern: 'Makefile', system: 'Make' },
{ pattern: 'build.gradle', system: 'Gradle' },
{ pattern: 'pom.xml', system: 'Maven' },
{ pattern: 'Cargo.toml', system: 'Cargo' },
{ pattern: 'go.mod', system: 'Go Modules' },
{ pattern: 'meson.build', system: 'Meson' },
{ pattern: 'BUILD', system: 'Bazel' }
];
for (const { pattern, system } of buildFiles) {
const files = await glob(pattern, {
cwd: this.projectRoot,
ignore: ['node_modules/**'],
absolute: false
});
if (files.length > 0) {
buildSystems.add(system);
}
}
// Package manager detection
const packageFiles = [
{ pattern: 'package.json', manager: 'npm' },
{ pattern: 'yarn.lock', manager: 'Yarn' },
{ pattern: 'pnpm-lock.yaml', manager: 'pnpm' },
{ pattern: 'composer.json', manager: 'Composer' },
{ pattern: 'Pipfile', manager: 'Pipenv' },
{ pattern: 'poetry.lock', manager: 'Poetry' },
{ pattern: 'requirements.txt', manager: 'pip' },
{ pattern: 'Gemfile', manager: 'Bundler' },
{ pattern: 'Cargo.toml', manager: 'Cargo' },
{ pattern: 'go.mod', manager: 'Go Modules' },
{ pattern: 'pubspec.yaml', manager: 'Pub' }
];
for (const { pattern, manager } of packageFiles) {
const files = await glob(pattern, {
cwd: this.projectRoot,
ignore: ['node_modules/**'],
absolute: false
});
if (files.length > 0) {
packageManagers.add(manager);
}
}
this.environment.buildSystems = Array.from(buildSystems);
this.environment.packageManagers = Array.from(packageManagers);
}
determineProjectType() {
// Check for monorepo indicators
const monorepoIndicators = ['lerna.json', 'rush.json', 'pnpm-workspace.yaml', 'workspace'];
for (const indicator of monorepoIndicators) {
try {
if (fs.existsSync(path.join(this.projectRoot, indicator))) {
this.environment.projectType = 'monorepo';
return;
}
}
catch (error) {
// Continue checking
}
}
// Check for mobile app indicators
const mobileIndicators = ['android/', 'ios/', 'flutter/', 'pubspec.yaml', '*.xcodeproj'];
for (const indicator of mobileIndicators) {
try {
const files = glob.sync(indicator, { cwd: this.projectRoot });
if (files.length > 0) {
this.environment.projectType = 'mobile';
return;
}
}
catch (error) {
// Continue checking
}
}
// Check for desktop app indicators
if (this.environment.frameworks.some(f => ['Electron', 'WPF', 'WinForms', 'Qt', 'GTK'].includes(f))) {
this.environment.projectType = 'desktop';
return;
}
// Check for web app indicators
if (this.environment.frameworks.some(f => ['React', 'Vue', 'Angular', 'Svelte'].includes(f))) {
this.environment.projectType = 'web';
return;
}
// Check for API indicators
if (this.environment.frameworks.some(f => ['Express', 'FastAPI', 'Django', 'Spring', 'ASP.NET Core'].includes(f))) {
this.environment.projectType = 'api';
return;
}
// Check for library indicators
if (fs.existsSync(path.join(this.projectRoot, 'lib')) ||
fs.existsSync(path.join(this.projectRoot, 'src/lib'))) {
this.environment.projectType = 'library';
return;
}
// Check for microservice indicators
if (fs.existsSync(path.join(this.projectRoot, 'Dockerfile')) ||
fs.existsSync(path.join(this.projectRoot, 'docker-compose.yml'))) {
this.environment.projectType = 'microservice';
return;
}
// Default to single project
this.environment.projectType = 'single';
}
getEnvironment() {
return this.environment;
}
}
//# sourceMappingURL=ProjectEnvironmentDetector.js.map