what-is-my-tech-stack
Version:
Analyze project dependencies and generate a human-readable tech stack description
213 lines (177 loc) • 6.26 kB
text/typescript
import { FileReader } from '../utils/fileReader.js';
import { NodeAnalyzer } from './nodeAnalyzer.js';
import { PythonAnalyzer } from './pythonAnalyzer.js';
interface ProjectTechStack {
type: 'node' | 'python' | 'both' | 'unknown';
node?: {
dependencies: {
name: string;
version: string;
type: 'dependency' | 'devDependency';
}[];
categories: Record<string, string[]>;
description?: string;
};
python?: {
dependencies: {
name: string;
version: string;
constraint?: string;
}[];
categories: Record<string, string[]>;
description?: string;
};
}
export class DependencyAnalyzer {
private projectPath: string;
constructor(projectPath: string) {
this.projectPath = projectPath;
}
/**
* Analyzes the project dependencies and returns a structured tech stack
*/
async analyze(): Promise<ProjectTechStack> {
const projectType = FileReader.detectProjectType(this.projectPath);
const result: ProjectTechStack = { type: projectType };
if (projectType === 'node' || projectType === 'both') {
const nodeAnalyzer = new NodeAnalyzer(`${this.projectPath}/package.json`);
const nodeDependencies = await nodeAnalyzer.analyze();
const nodeCategories = nodeAnalyzer.categorizeDependencies(nodeDependencies);
// Get AI-generated description
let nodeDescription: string | undefined;
try {
// Generate a minimal list of technologies
const techList = nodeDependencies.map((dep) => `• ${dep.name}`).join('\n');
nodeDescription = techList;
} catch (error) {
process.stderr.write(
`Failed to generate Node.js tech stack description: ${(error as Error).message}\n`
);
}
result.node = {
dependencies: nodeDependencies,
categories: nodeCategories,
description: nodeDescription,
};
}
if (projectType === 'python' || projectType === 'both') {
const pythonAnalyzer = new PythonAnalyzer(`${this.projectPath}/requirements.txt`);
const pythonDependencies = await pythonAnalyzer.analyze();
const pythonCategories = pythonAnalyzer.categorizeDependencies(pythonDependencies);
// Get AI-generated description
let pythonDescription: string | undefined;
try {
// Generate a minimal list of technologies
const techList = pythonDependencies.map((dep) => `• ${dep.name}`).join('\n');
pythonDescription = techList;
} catch (error) {
process.stderr.write(
`Failed to generate Python tech stack description: ${(error as Error).message}\n`
);
}
result.python = {
dependencies: pythonDependencies,
categories: pythonCategories,
description: pythonDescription,
};
}
return result;
}
/**
* Generates a human-readable summary of the tech stack
*/
generateSummary(techStack: ProjectTechStack): string {
const summary: string[] = [];
summary.push('# Project Tech Stack Analysis\n');
if (techStack.type === 'unknown') {
summary.push('No recognized dependency files found (package.json or requirements.txt).');
return summary.join('\n');
}
if (techStack.node) {
summary.push('## Node.js Dependencies\n');
this.appendNodeSummary(summary, techStack.node);
}
if (techStack.python) {
if (techStack.node) summary.push('\n'); // Add spacing between sections
summary.push('## Python Dependencies\n');
this.appendPythonSummary(summary, techStack.python);
}
return summary.join('\n');
}
private appendNodeSummary(
summary: string[],
nodeStack: NonNullable<ProjectTechStack['node']>
): void {
const { categories, description } = nodeStack;
if (description) {
summary.push('### Overview');
summary.push(description + '\n');
}
if (categories.framework?.length) {
summary.push('### Frameworks');
summary.push(categories.framework.join(', ') + '\n');
}
if (categories.testing?.length) {
summary.push('### Testing Tools');
summary.push(categories.testing.join(', ') + '\n');
}
if (categories.bundler?.length) {
summary.push('### Build Tools & Bundlers');
summary.push(categories.bundler.join(', ') + '\n');
}
if (categories.linter?.length) {
summary.push('### Linting & Code Style');
summary.push(categories.linter.join(', ') + '\n');
}
if (categories.typescript?.length) {
summary.push('### TypeScript');
summary.push(categories.typescript.join(', ') + '\n');
}
if (categories.utilities?.length) {
summary.push('### Utilities');
summary.push(categories.utilities.join(', ') + '\n');
}
if (categories.other?.length) {
summary.push('### Other Dependencies');
summary.push(categories.other.join(', ') + '\n');
}
}
private appendPythonSummary(
summary: string[],
pythonStack: NonNullable<ProjectTechStack['python']>
): void {
const { categories, description } = pythonStack;
if (description) {
summary.push('### Overview');
summary.push(description + '\n');
}
if (categories.web_framework?.length) {
summary.push('### Web Frameworks');
summary.push(categories.web_framework.join(', ') + '\n');
}
if (categories.testing?.length) {
summary.push('### Testing Tools');
summary.push(categories.testing.join(', ') + '\n');
}
if (categories.database?.length) {
summary.push('### Database & ORM');
summary.push(categories.database.join(', ') + '\n');
}
if (categories.async?.length) {
summary.push('### Async & Task Queue');
summary.push(categories.async.join(', ') + '\n');
}
if (categories.data_science?.length) {
summary.push('### Data Science & ML');
summary.push(categories.data_science.join(', ') + '\n');
}
if (categories.utilities?.length) {
summary.push('### Utilities');
summary.push(categories.utilities.join(', ') + '\n');
}
if (categories.other?.length) {
summary.push('### Other Dependencies');
summary.push(categories.other.join(', ') + '\n');
}
}
}