UNPKG

ctx-gen

Version:

AI-Enhanced Documentation Generator for Code Understanding

228 lines 7.44 kB
import fs from 'fs-extra'; import path from 'path'; /** * Updates the .gitignore file to include the documentation directory * @param docsDir - The documentation directory to ignore */ export async function updateGitIgnore(docsDir) { const gitignorePath = '.gitignore'; let content = ''; // Read existing .gitignore if it exists if (await fs.pathExists(gitignorePath)) { content = await fs.readFile(gitignorePath, 'utf-8'); } // Normalize the docs directory path for .gitignore const normalizedDocsDir = docsDir.replace(/\\/g, '/'); // Check if the directory is already in .gitignore if (!content.split('\n').some(line => line.trim() === normalizedDocsDir)) { // Add the docs directory to .gitignore const updatedContent = content.endsWith('\n') ? `${content}${normalizedDocsDir}\n` : `${content}\n${normalizedDocsDir}\n`; // Write updated content back to .gitignore await fs.writeFile(gitignorePath, updatedContent); } } /** * Creates a directory if it doesn't exist * @param dirPath - The directory path to create */ export async function ensureDirectory(dirPath) { await fs.ensureDir(dirPath); } /** * Ensures parent directory exists before writing a file * @param filePath - The file path * @param content - The content to write */ export async function writeFileWithDir(filePath, content) { const dir = path.dirname(filePath); await fs.ensureDir(dir); await fs.writeFile(filePath, content); } /** * Gets the relative path from the current working directory * @param absolutePath - The absolute file path * @returns The relative path from cwd */ export function getRelativePath(absolutePath) { return path.relative(process.cwd(), absolutePath); } /** * Read patterns from .gitignore file * @returns Array of gitignore patterns */ export async function readGitignorePatterns() { const gitignorePath = '.gitignore'; const patterns = []; if (await fs.pathExists(gitignorePath)) { const content = await fs.readFile(gitignorePath, 'utf-8'); const lines = content.split('\n'); for (const line of lines) { const trimmedLine = line.trim(); // Skip empty lines and comments if (trimmedLine && !trimmedLine.startsWith('#')) { patterns.push(trimmedLine); } } } return patterns; } /** * Simple glob pattern matcher * @param string - String to test * @param pattern - Glob pattern * @returns Whether the string matches the pattern */ function simpleGlobMatch(string, pattern) { // Handle directory patterns (pattern ending with /) const isDirPattern = pattern.endsWith('/'); let patternToUse = pattern; if (isDirPattern) { // If it's a directory pattern, we need to check if the string contains this directory patternToUse = pattern.slice(0, -1); // If string starts with the directory pattern, it should match if (string === patternToUse || string.startsWith(patternToUse + '/')) { return true; } return false; } // Handle files and other glob patterns // Convert glob pattern to regex const regexPattern = pattern // Escape regex special characters except * and ? .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Replace ** with a special marker for recursive matching .replace(/\*\*/g, '__RECURSIVE__') // Replace * with regex equivalent (excluding /) .replace(/\*/g, '[^/]*') // Replace ? with regex equivalent (excluding /) .replace(/\?/g, '[^/]') // Restore ** as recursive matching pattern .replace(/__RECURSIVE__/g, '.*'); // Create regex with start and end anchors const regex = new RegExp(`^${regexPattern}$`); // Test the string against the pattern return regex.test(string); } /** * Checks if a file should be excluded based on patterns including gitignore * @param filePath - The file path to check * @param excludePatterns - Array of exclude patterns (glob patterns) * @param gitignorePatterns - Array of gitignore patterns * @returns Whether the file should be excluded */ export function shouldExcludeFile(filePath, excludePatterns, gitignorePatterns = []) { const relativePath = getRelativePath(filePath); // Check explicit exclude patterns for (const pattern of excludePatterns) { if (relativePath.includes(pattern)) { return true; } } // Check gitignore patterns for (const pattern of gitignorePatterns) { if (simpleGlobMatch(relativePath, pattern)) { return true; } } return false; } /** * Gets the language of a file based on extension * @param filePath - The file path * @returns The file language if supported, undefined otherwise */ export function getFileLanguage(filePath) { const extension = path.extname(filePath).toLowerCase(); const languageMap = { // JavaScript family '.ts': 'typescript', '.tsx': 'typescript', '.js': 'javascript', '.jsx': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript', '.json': 'json', // Web-related '.html': 'html', '.css': 'css', '.scss': 'scss', '.sass': 'sass', '.less': 'less', '.svg': 'svg', // Backend languages '.py': 'python', '.java': 'java', '.groovy': 'groovy', '.kt': 'kotlin', '.scala': 'scala', '.rb': 'ruby', '.php': 'php', '.pl': 'perl', '.cs': 'csharp', '.go': 'go', '.rs': 'rust', '.swift': 'swift', '.dart': 'dart', // C family '.c': 'c', '.cpp': 'cpp', '.cc': 'cpp', '.h': 'c', '.hpp': 'cpp', '.m': 'objective-c', '.mm': 'objective-cpp', // Functional languages '.hs': 'haskell', '.elm': 'elm', '.fs': 'fsharp', '.fsx': 'fsharp', '.clj': 'clojure', '.ml': 'ocaml', // Shell and scripts '.sh': 'bash', '.bash': 'bash', '.zsh': 'zsh', '.ps1': 'powershell', '.bat': 'batch', '.cmd': 'batch', // Other languages '.lua': 'lua', '.r': 'r', '.sql': 'sql', '.graphql': 'graphql', '.yaml': 'yaml', '.yml': 'yaml', '.toml': 'toml', '.md': 'markdown', '.tex': 'latex' }; return languageMap[extension]; } /** * Detects if a file contains binary content * @param filePath - The file path * @returns True if file is binary, false otherwise */ export async function isBinaryFile(filePath) { try { // Read the first 4KB of the file const buffer = Buffer.alloc(4096); const file = await fs.open(filePath, 'r'); const { bytesRead } = await fs.read(file, buffer, 0, 4096, 0); await fs.close(file); // Check for NUL bytes which typically indicate binary content for (let i = 0; i < bytesRead; i++) { if (buffer[i] === 0) { return true; } } return false; } catch (error) { console.error(`Error checking if file is binary: ${filePath}`, error); return false; } } //# sourceMappingURL=fileUtils.js.map