@morodomi/ait3
Version:
AIT³ Development Platform - AI + Ticket + Test + Tool driven development methodology
149 lines (148 loc) • 5.1 kB
JavaScript
import { readFile, writeFile, mkdir, access } from 'fs/promises';
import { join } from 'path';
import { constants } from 'fs';
export class DefaultProjectAnalyzer {
rootPath;
languageDetector;
commandDetector;
structureAnalyzer;
cacheDir;
cacheFile;
cache = null;
cacheTimestamp = 0;
CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
constructor(rootPath, languageDetector, commandDetector, structureAnalyzer) {
this.rootPath = rootPath;
this.languageDetector = languageDetector;
this.commandDetector = commandDetector;
this.structureAnalyzer = structureAnalyzer;
this.cacheDir = join(this.rootPath, '.ait3/cache');
this.cacheFile = join(this.cacheDir, 'project-analysis.json');
}
async analyzeProject(path) {
const targetPath = path || this.rootPath;
// Check cache first
const cached = await this.getCachedAnalysis(targetPath);
if (cached) {
return cached;
}
// Perform analysis
const analysis = await this.performAnalysis(targetPath);
// Cache the result
await this.cacheAnalysis(analysis);
return analysis;
}
async performAnalysis(targetPath) {
// Initialize result object
const analysis = {
root: targetPath,
languages: [],
framework: {
name: 'Unknown',
type: 'unknown',
confidence: 0
},
commands: {
test: undefined,
lint: undefined,
format: undefined,
build: undefined
},
structure: {
rootPath: targetPath,
directories: [],
hasGitRepository: false,
hasCICD: false,
hasDocker: false
},
timestamp: new Date().toISOString()
};
// Detect languages with error handling
try {
analysis.languages = await this.languageDetector.detectLanguages(targetPath);
}
catch {
// Continue with empty languages on error
analysis.languages = [];
}
// Get primary language for command detection
let primaryLanguage;
try {
const primary = await this.languageDetector.getPrimaryLanguage(targetPath);
primaryLanguage = primary?.name;
}
catch {
// Continue without primary language
}
// Detect commands
try {
const [test, lint, format, build] = await Promise.all([
this.commandDetector.detectTestCommand(targetPath, primaryLanguage),
this.commandDetector.detectLintCommand(targetPath, primaryLanguage),
this.commandDetector.detectFormatCommand(targetPath, primaryLanguage),
this.commandDetector.detectBuildCommand(targetPath, primaryLanguage)
]);
analysis.commands = { test, lint, format, build };
}
catch {
// Continue with partial command detection
}
// Analyze structure
try {
analysis.structure = await this.structureAnalyzer.analyzeStructure(targetPath);
}
catch {
// Continue with default structure
}
// Detect framework
try {
analysis.framework = await this.structureAnalyzer.detectFramework(targetPath, primaryLanguage);
}
catch {
// Continue with unknown framework
}
return analysis;
}
async getCachedAnalysis(targetPath) {
// Check in-memory cache first
if (this.cache && this.cache.root === targetPath) {
const now = Date.now();
if (now - this.cacheTimestamp < this.CACHE_DURATION) {
return this.cache;
}
}
// Check file cache
try {
await access(this.cacheFile, constants.F_OK);
const content = await readFile(this.cacheFile, 'utf-8');
const cached = JSON.parse(content);
if (cached.root === targetPath) {
const cacheTime = new Date(cached.timestamp).getTime();
const now = Date.now();
if (now - cacheTime < this.CACHE_DURATION) {
// Update in-memory cache
this.cache = cached;
this.cacheTimestamp = cacheTime;
return cached;
}
}
}
catch {
// No cache or invalid cache
}
return null;
}
async cacheAnalysis(analysis) {
// Update in-memory cache
this.cache = analysis;
this.cacheTimestamp = Date.now();
// Save to file
try {
await mkdir(this.cacheDir, { recursive: true });
await writeFile(this.cacheFile, JSON.stringify(analysis, null, 2));
}
catch {
// Ignore cache write errors
}
}
}