ctx-gen
Version:
AI-Enhanced Documentation Generator for Code Understanding
161 lines • 6.79 kB
JavaScript
import fs from 'fs-extra';
import path from 'path';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
import { writeFileWithDir, getRelativePath, getFileLanguage } from '../utils/fileUtils.js';
/**
* Mock implementation of analyzeFilesWithAI for when aiAnalyzer module isn't available
* This will be replaced by the real implementation when available
*/
async function analyzeFilesWithAI(filesToAnalyze, _options) {
const results = new Map();
// Just return the basic documentation
for (const file of filesToAnalyze) {
results.set(file.filePath, file.basicDocumentation);
}
return results;
}
/**
* Generate documentation for a batch of files
* @param filePaths - Array of file paths to document
* @param options - CtxGen options
*/
export async function generateBatchDocumentation(filePaths, options) {
console.log(chalk.blue(`Analyzing ${filePaths.length} files...`));
// Filter files with supported languages
const validFiles = [];
const filesForAI = [];
// Create a progress bar for file analysis
const analysisBar = new cliProgress.SingleBar({
format: 'File Analysis |' + chalk.cyan('{bar}') + '| {percentage}% | {value}/{total} Files',
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true,
});
// Start the progress bar
analysisBar.start(filePaths.length, 0);
// First pass: generate basic documentation for all files
for (let i = 0; i < filePaths.length; i++) {
const filePath = filePaths[i];
const language = getFileLanguage(filePath);
// Update progress bar
analysisBar.update(i + 1);
if (!language) {
// Skip files with unsupported languages
continue;
}
validFiles.push(filePath);
// Read the file content
const content = await fs.readFile(filePath, 'utf-8');
// Generate basic documentation
const basicDoc = await generateBasicDocumentation(filePath, content, language);
// Prepare for AI analysis if enabled
if (options.ai && options.openaiKey) {
filesForAI.push({
filePath,
content,
language,
basicDocumentation: basicDoc,
});
}
else {
// If AI is disabled, write basic documentation immediately
const docPath = getDocPath(filePath, options.docsDir);
await writeFileWithDir(docPath, basicDoc);
}
}
// Stop the first progress bar
analysisBar.stop();
// Second pass: if AI is enabled, process files concurrently
if (options.ai && options.openaiKey && filesForAI.length > 0) {
console.log(chalk.blue(`Enhancing ${filesForAI.length} files with AI analysis...`));
// Create a progress bar for AI analysis
const aiBar = new cliProgress.SingleBar({
format: 'AI Enhancement |' + chalk.green('{bar}') + '| {percentage}% | {value}/{total} Files',
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true,
});
// Start the progress bar
aiBar.start(filesForAI.length, 0);
let processedFiles = 0;
// Try to dynamically import the analyzer
try {
const { analyzeFilesWithAI: importedAnalyzer } = await import('../core/aiAnalyzer.js');
// Process in batches
for (let i = 0; i < filesForAI.length; i += options.concurrentAiAnalyses || 3) {
const batch = filesForAI.slice(i, i + (options.concurrentAiAnalyses || 3));
// Process all files with AI concurrently
const aiResults = await importedAnalyzer(batch, options);
// Write the enhanced documentation to files
for (const [filePath, documentation] of aiResults.entries()) {
const docPath = getDocPath(filePath, options.docsDir);
await writeFileWithDir(docPath, documentation);
// Update progress bar
processedFiles++;
aiBar.update(processedFiles);
}
}
}
catch (error) {
console.error(chalk.red('Error importing AI analyzer:'), error);
console.log(chalk.yellow('Falling back to basic documentation...'));
// Use the local function as fallback
const basicResults = await analyzeFilesWithAI(filesForAI, options);
// Write the documentation to files
for (const [filePath, documentation] of basicResults.entries()) {
const docPath = getDocPath(filePath, options.docsDir);
await writeFileWithDir(docPath, documentation);
// Update progress bar
processedFiles++;
aiBar.update(processedFiles);
}
}
// Stop the AI progress bar
aiBar.stop();
}
console.log(chalk.green(`Documentation generated for ${validFiles.length} files.`));
}
/**
* Generate documentation for a single file
* @param filePath - The path to the file
* @param options - CtxGen options
*/
export async function generateFileDocumentation(filePath, options) {
// For single file, use batch processing with an array of one
await generateBatchDocumentation([filePath], options);
}
/**
* Get the documentation file path
* @param filePath - The source file path
* @param docsDir - The documentation directory
* @returns The documentation file path
*/
function getDocPath(filePath, docsDir) {
const relativePath = getRelativePath(filePath);
return path.join(docsDir, 'modules', relativePath.replace(/\.\w+$/, '.md'));
}
/**
* Generate basic documentation for a file without AI assistance
* @param filePath - The path to the file
* @param content - The file content
* @param language - The file language
* @returns Basic documentation string
*/
async function generateBasicDocumentation(filePath, content, language) {
const relativePath = getRelativePath(filePath);
const moduleName = path.basename(filePath, path.extname(filePath));
let documentation = `# ${moduleName}\n\n`;
documentation += `**File Path:** \`${relativePath}\`\n\n`;
documentation += `**Language:** ${language}\n\n`;
// Add basic file information
documentation += '## Overview\n\n';
documentation += `This file contains the \`${moduleName}\` module.\n\n`;
// Add a code section with the file content
documentation += '## Source Code\n\n';
documentation += '```' + language + '\n';
documentation += content;
documentation += '\n```\n\n';
return documentation;
}
//# sourceMappingURL=fileDocGenerator.js.map