UNPKG

gitguide

Version:

AI-powered README generator for any project directory

297 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DirectoryAnalyzer = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); class DirectoryAnalyzer { constructor() { this.maxFileSize = 100 * 1024; // 100KB this.maxFiles = 200; this.excludePatterns = [ /node_modules/, /\.git/, /dist/, /build/, /coverage/, /\.next/, /\.nuxt/, /\.cache/, /\.vscode/, /\.idea/, /\.DS_Store/, /\.env\.local/, /\.env\.production/, /package-lock\.json/, /yarn\.lock/, /pnpm-lock\.yaml/, /composer\.lock/, /Pipfile\.lock/, /poetry\.lock/, /\.pyc$/, /\.log$/, /\.tmp$/, /\.temp$/ ]; this.importantFiles = [ 'package.json', 'tsconfig.json', 'next.config.js', 'vite.config.js', 'webpack.config.js', 'tailwind.config.js', 'requirements.txt', 'Pipfile', 'pyproject.toml', 'setup.py', 'Cargo.toml', 'go.mod', 'pom.xml', 'build.gradle', 'Gemfile', 'composer.json', 'Dockerfile', 'docker-compose.yml', '.env.example', 'config.js', 'config.json' ]; } async analyzeDirectory(dirPath) { const structure = await this.getDirectoryStructure(dirPath); const files = await this.getProjectFiles(dirPath); const projectInfo = this.extractProjectInfo(dirPath, files); return { path: dirPath, files, structure, ...projectInfo }; } async getDirectoryStructure(dirPath) { let fileCount = 0; let directoryCount = 0; const languages = {}; const scanDirectory = async (currentPath, depth = 0) => { if (depth > 10) return; // Prevent infinite recursion try { const entries = await fs_extra_1.default.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path_1.default.join(currentPath, entry.name); const relativePath = path_1.default.relative(dirPath, fullPath); if (this.shouldExclude(relativePath)) { continue; } if (entry.isDirectory()) { directoryCount++; await scanDirectory(fullPath, depth + 1); } else if (entry.isFile()) { fileCount++; const ext = path_1.default.extname(entry.name).toLowerCase(); if (ext) { languages[ext] = (languages[ext] || 0) + 1; } } } } catch (error) { // Skip directories we can't read } }; await scanDirectory(dirPath); return { files: fileCount, directories: directoryCount, languages }; } async getProjectFiles(dirPath) { const files = []; let processedFiles = 0; const scanDirectory = async (currentPath, depth = 0) => { if (depth > 8 || processedFiles >= this.maxFiles) return; try { const entries = await fs_extra_1.default.readdir(currentPath, { withFileTypes: true }); // Process important files first const sortedEntries = entries.sort((a, b) => { const aImportant = this.importantFiles.includes(a.name); const bImportant = this.importantFiles.includes(b.name); if (aImportant && !bImportant) return -1; if (!aImportant && bImportant) return 1; return 0; }); for (const entry of sortedEntries) { if (processedFiles >= this.maxFiles) break; const fullPath = path_1.default.join(currentPath, entry.name); const relativePath = path_1.default.relative(dirPath, fullPath); if (this.shouldExclude(relativePath)) { continue; } if (entry.isFile()) { const file = await this.processFile(fullPath, relativePath); if (file) { files.push(file); processedFiles++; } } else if (entry.isDirectory()) { files.push({ name: entry.name, path: relativePath, type: 'directory', size: 0 }); await scanDirectory(fullPath, depth + 1); } } } catch (error) { // Skip directories we can't read } }; await scanDirectory(dirPath); return files; } async processFile(fullPath, relativePath) { try { const stats = await fs_extra_1.default.stat(fullPath); const fileName = path_1.default.basename(relativePath); if (stats.size > this.maxFileSize) { return { name: fileName, path: relativePath, type: 'file', size: stats.size, content: `[File too large: ${Math.round(stats.size / 1024)}KB]` }; } const shouldReadContent = this.shouldReadFileContent(fileName, relativePath); let content = ''; if (shouldReadContent) { try { const buffer = await fs_extra_1.default.readFile(fullPath); // Check if file is binary if (this.isBinaryFile(buffer)) { content = `[Binary file: ${path_1.default.extname(fileName)}]`; } else { content = buffer.toString('utf8').slice(0, 5000); // Limit content size } } catch (error) { content = '[Unable to read file content]'; } } return { name: fileName, path: relativePath, type: 'file', size: stats.size, content }; } catch (error) { return null; } } shouldExclude(relativePath) { return this.excludePatterns.some(pattern => pattern.test(relativePath)); } shouldReadFileContent(fileName, relativePath) { // Always read important configuration files if (this.importantFiles.includes(fileName)) { return true; } // Read text files and common source files const ext = path_1.default.extname(fileName).toLowerCase(); const textExtensions = [ '.js', '.ts', '.jsx', '.tsx', '.vue', '.svelte', '.py', '.rb', '.php', '.go', '.rs', '.java', '.c', '.cpp', '.h', '.hpp', '.css', '.scss', '.sass', '.less', '.styl', '.html', '.htm', '.xml', '.svg', '.json', '.yaml', '.yml', '.toml', '.ini', '.conf', '.md', '.txt', '.rst', '.adoc', '.sh', '.bash', '.zsh', '.fish', '.sql', '.graphql', '.gql', '.dockerfile', '.gitignore', '.gitattributes' ]; return textExtensions.includes(ext) || fileName.startsWith('.env'); } isBinaryFile(buffer) { // Simple binary detection: check for null bytes in first 1KB const sample = buffer.slice(0, 1024); for (let i = 0; i < sample.length; i++) { if (sample[i] === 0) { return true; } } return false; } extractProjectInfo(dirPath, files) { const dirName = path_1.default.basename(dirPath); let name = dirName; let description = ''; let language = 'Unknown'; let version = '1.0.0'; // Extract info from package.json const packageJson = files.find(f => f.name === 'package.json'); if (packageJson?.content && packageJson.content !== '[Binary file: .json]') { try { const pkg = JSON.parse(packageJson.content); name = pkg.name || name; description = pkg.description || description; version = pkg.version || version; if (pkg.dependencies?.react || pkg.devDependencies?.react) { language = 'JavaScript/React'; } else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) { language = 'JavaScript/Vue'; } else if (pkg.dependencies?.next || pkg.devDependencies?.next) { language = 'JavaScript/Next.js'; } else { language = 'JavaScript/Node.js'; } } catch (error) { // Ignore JSON parse errors } } // Detect other languages if (files.some(f => f.name === 'requirements.txt' || f.name === 'setup.py')) { language = 'Python'; } else if (files.some(f => f.name === 'Cargo.toml')) { language = 'Rust'; } else if (files.some(f => f.name === 'go.mod')) { language = 'Go'; } else if (files.some(f => f.name === 'pom.xml' || f.name === 'build.gradle')) { language = 'Java'; } else if (files.some(f => f.name === 'Gemfile')) { language = 'Ruby'; } else if (files.some(f => f.name === 'composer.json')) { language = 'PHP'; } return { name, description, language, version }; } } exports.DirectoryAnalyzer = DirectoryAnalyzer; //# sourceMappingURL=directoryAnalyzer.js.map