This commit is contained in:
238
.ai-context-generator-cli/src/templates/templateLoader.ts
Normal file
238
.ai-context-generator-cli/src/templates/templateLoader.ts
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { TemplateData } from '../types';
|
||||||
|
|
||||||
|
export class TemplateLoader {
|
||||||
|
private templates: Map<string, (data: TemplateData) => string>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.templates = new Map();
|
||||||
|
this.registerDefaultTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerDefaultTemplates(): void {
|
||||||
|
this.templates.set('default', this.renderDefaultTemplate.bind(this));
|
||||||
|
this.templates.set('cursor', this.renderCursorTemplate.bind(this));
|
||||||
|
this.templates.set('copilot', this.renderCopilotTemplate.bind(this));
|
||||||
|
this.templates.set('generic', this.renderGenericTemplate.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadTemplate(name: string): Promise<(data: TemplateData) => string> {
|
||||||
|
const builtInTemplates = ['default', 'cursor', 'copilot', 'generic'];
|
||||||
|
|
||||||
|
if (builtInTemplates.includes(name)) {
|
||||||
|
return this.templates.get(name)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const templatePath = path.resolve(name);
|
||||||
|
if (await this.fileExists(templatePath)) {
|
||||||
|
const content = await fs.promises.readFile(templatePath, 'utf-8');
|
||||||
|
return this.compileTemplate(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
const customTemplateDir = path.join(process.cwd(), 'templates');
|
||||||
|
const customTemplatePath = path.join(customTemplateDir, `${name}.template`);
|
||||||
|
if (await this.fileExists(customTemplatePath)) {
|
||||||
|
const content = await fs.promises.readFile(customTemplatePath, 'utf-8');
|
||||||
|
return this.compileTemplate(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Template not found: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTemplate(templateContent: string): (data: TemplateData) => string {
|
||||||
|
return (data: TemplateData): string => {
|
||||||
|
let result = templateContent;
|
||||||
|
|
||||||
|
result = result.replace(/\{\{\s*project\.type\s*\}\}/g,
|
||||||
|
data.projectInfo.projectType.primaryLanguage);
|
||||||
|
result = result.replace(/\{\{\s*project\.languages\s*\}\}/g,
|
||||||
|
data.projectInfo.projectType.languages.join(', '));
|
||||||
|
result = result.replace(/\{\{\s*project\.frameworks\s*\}\}/g,
|
||||||
|
data.projectInfo.projectType.frameworks.join(', '));
|
||||||
|
result = result.replace(/\{\{\s*project\.fileCount\s*\}\}/g,
|
||||||
|
String(data.projectInfo.fileCount));
|
||||||
|
result = result.replace(/\{\{\s*dependencies\.total\s*\}\}/g,
|
||||||
|
String(data.projectInfo.dependencies.total));
|
||||||
|
result = result.replace(/\{\{\s*generatedAt\s*\}\}/g,
|
||||||
|
data.generatedAt);
|
||||||
|
|
||||||
|
result = this.renderDependencies(result, data);
|
||||||
|
result = this.renderConventions(result, data);
|
||||||
|
result = this.renderFileList(result, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderDefaultTemplate(data: TemplateData): string {
|
||||||
|
const jsonOutput = JSON.stringify({
|
||||||
|
project: data.projectInfo,
|
||||||
|
files: data.files,
|
||||||
|
generatedAt: data.generatedAt,
|
||||||
|
}, null, 2);
|
||||||
|
|
||||||
|
return `## AI Context
|
||||||
|
|
||||||
|
\`\`\`json
|
||||||
|
${jsonOutput}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- **Language**: ${data.projectInfo.projectType.primaryLanguage}
|
||||||
|
- **Frameworks**: ${data.projectInfo.projectType.frameworks.join(', ') || 'None detected'}
|
||||||
|
- **Dependencies**: ${data.projectInfo.dependencies.total}
|
||||||
|
- **Files Analyzed**: ${data.projectInfo.fileCount}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderCursorTemplate(data: TemplateData): string {
|
||||||
|
const topDeps = data.projectInfo.dependencies.direct
|
||||||
|
.slice(0, 15)
|
||||||
|
.map(d => ` - ${d.name}@${d.version}`)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return `## Project Context
|
||||||
|
|
||||||
|
**Language**: ${data.projectInfo.projectType.primaryLanguage}
|
||||||
|
**Frameworks**: ${data.projectInfo.projectType.frameworks.join(', ') || 'None'}
|
||||||
|
**Build Tools**: ${data.projectInfo.projectType.buildTools.join(', ') || 'None'}
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
${topDeps || ' No dependencies detected'}
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
${data.projectInfo.conventions ? `
|
||||||
|
- **File Naming**: ${data.projectInfo.conventions.namingConvention.files}
|
||||||
|
- **Import Style**: ${data.projectInfo.conventions.importStyle.style}
|
||||||
|
- **Testing Framework**: ${data.projectInfo.conventions.testingFramework || 'None'}
|
||||||
|
- **Code Style**:
|
||||||
|
- Indent: ${data.projectInfo.conventions.codeStyle.indentSize} ${data.projectInfo.conventions.codeStyle.indentType}
|
||||||
|
- Quotes: ${data.projectInfo.conventions.codeStyle.quoteStyle}
|
||||||
|
` : ' Not analyzed'}
|
||||||
|
|
||||||
|
### Key Files
|
||||||
|
${data.files.slice(0, 10).map(f => `- \`${f.path}\``).join('\n')}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderCopilotTemplate(data: TemplateData): string {
|
||||||
|
const deps = data.projectInfo.dependencies.direct
|
||||||
|
.map(d => ` "${d.name}": "${d.version}"`)
|
||||||
|
.join(',\n');
|
||||||
|
|
||||||
|
return `/* Project Context */
|
||||||
|
Language: ${data.projectInfo.projectType.primaryLanguage}
|
||||||
|
Frameworks: ${data.projectInfo.projectType.frameworks.join(', ') || 'None'}
|
||||||
|
Dependencies: ${data.projectInfo.dependencies.total}
|
||||||
|
|
||||||
|
/* Dependencies */
|
||||||
|
{
|
||||||
|
${deps}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conventions */
|
||||||
|
File Naming: ${data.projectInfo.conventions?.namingConvention.files || 'Unknown'}
|
||||||
|
Import Style: ${data.projectInfo.conventions?.importStyle.style || 'Unknown'}
|
||||||
|
Testing: ${data.projectInfo.conventions?.testingFramework || 'None'}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderGenericTemplate(data: TemplateData): string {
|
||||||
|
return `=== PROJECT CONTEXT ===
|
||||||
|
|
||||||
|
Project Type: ${data.projectInfo.projectType.primaryLanguage}
|
||||||
|
Languages: ${data.projectInfo.projectType.languages.join(', ')}
|
||||||
|
Frameworks: ${data.projectInfo.projectType.frameworks.join(', ') || 'None'}
|
||||||
|
Build Tools: ${data.projectInfo.projectType.buildTools.join(', ') || 'None'}
|
||||||
|
|
||||||
|
=== DEPENDENCIES ===
|
||||||
|
Total: ${data.projectInfo.dependencies.total}
|
||||||
|
Production: ${data.projectInfo.dependencies.direct.length}
|
||||||
|
Development: ${data.projectInfo.dependencies.dev.length}
|
||||||
|
|
||||||
|
Top Dependencies:
|
||||||
|
${data.projectInfo.dependencies.direct.slice(0, 10).map(d => ` - ${d.name} (${d.version})`).join('\n')}
|
||||||
|
|
||||||
|
=== CONVENTIONS ===
|
||||||
|
${data.projectInfo.conventions ? `
|
||||||
|
Naming:
|
||||||
|
Files: ${data.projectInfo.conventions.namingConvention.files}
|
||||||
|
Variables: ${data.projectInfo.conventions.namingConvention.variables}
|
||||||
|
Functions: ${data.projectInfo.conventions.namingConvention.functions}
|
||||||
|
Classes: ${data.projectInfo.conventions.namingConvention.classes}
|
||||||
|
|
||||||
|
Import Style: ${data.projectInfo.conventions.importStyle.style}
|
||||||
|
${data.projectInfo.conventions.importStyle.aliasPrefix ? `Alias Prefix: ${data.projectInfo.conventions.importStyle.aliasPrefix}` : ''}
|
||||||
|
Testing Framework: ${data.projectInfo.conventions.testingFramework || 'None'}
|
||||||
|
|
||||||
|
Code Style:
|
||||||
|
Indent: ${data.projectInfo.conventions.codeStyle.indentSize} ${data.projectInfo.conventions.codeStyle.indentType}
|
||||||
|
Line Endings: ${data.projectInfo.conventions.codeStyle.lineEndings}
|
||||||
|
Quote Style: ${data.projectInfo.conventions.codeStyle.quoteStyle}
|
||||||
|
` : ' Not analyzed'}
|
||||||
|
|
||||||
|
=== FILES ===
|
||||||
|
Total Files: ${data.projectInfo.fileCount}
|
||||||
|
${data.files.slice(0, 20).map(f => ` - ${f.path}`).join('\n')}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderDependencies(
|
||||||
|
template: string,
|
||||||
|
data: TemplateData
|
||||||
|
): string {
|
||||||
|
const deps = data.projectInfo.dependencies.direct
|
||||||
|
.map(d => ` - ${d.name}@${d.version}`)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return template
|
||||||
|
.replace(/\{\{\s*dependencies\s*\}\}/g, deps)
|
||||||
|
.replace(/\{\{\s*dependencies\.count\s*\}\}/g,
|
||||||
|
String(data.projectInfo.dependencies.total));
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderConventions(
|
||||||
|
template: string,
|
||||||
|
data: TemplateData
|
||||||
|
): string {
|
||||||
|
if (!data.projectInfo.conventions) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conventions = data.projectInfo.conventions;
|
||||||
|
|
||||||
|
return template
|
||||||
|
.replace(/\{\{\s*conventions\.naming\s*\}\}/g,
|
||||||
|
conventions.namingConvention.files)
|
||||||
|
.replace(/\{\{\s*conventions\.importStyle\s*\}\}/g,
|
||||||
|
conventions.importStyle.style)
|
||||||
|
.replace(/\{\{\s*conventions\.testing\s*\}\}/g,
|
||||||
|
conventions.testingFramework || 'None');
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderFileList(
|
||||||
|
template: string,
|
||||||
|
data: TemplateData
|
||||||
|
): string {
|
||||||
|
const fileList = data.files
|
||||||
|
.slice(0, 30)
|
||||||
|
.map(f => ` - ${f.path}`)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
return template
|
||||||
|
.replace(/\{\{\s*files\s*\}\}/g, fileList)
|
||||||
|
.replace(/\{\{\s*files\.count\s*\}\}/g,
|
||||||
|
String(data.projectInfo.fileCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fileExists(filePath: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await fs.promises.access(filePath, fs.constants.F_OK);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user