UNPKG

ctx-gen

Version:

AI-Enhanced Documentation Generator for Code Understanding

161 lines 6.79 kB
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