UNPKG

dir3

Version:

Generate directory tree files with customizable ignore patterns

184 lines (183 loc) 6.53 kB
#!/usr/bin/env node import { promises as fs } from 'fs'; import path from 'path'; import inquirer from 'inquirer'; class DirTreeGenerator { constructor() { this.ignoreFile = '.dirtree.ignore'; this.outputFile = 'directory-tree.txt'; this.ignorePatterns = []; } async init() { try { await this.checkAndCreateIgnoreFile(); await this.loadIgnorePatterns(); const tree = await this.generateTree('.'); await this.writeTreeToFile(tree); console.log(`Directory tree generated successfully in ${this.outputFile}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('Error generating directory tree:', errorMessage); process.exit(1); } } async checkAndCreateIgnoreFile() { try { await fs.access(this.ignoreFile); console.log(`Using existing ${this.ignoreFile}`); } catch { const answers = await inquirer.prompt([ { type: 'confirm', name: 'createIgnoreFile', message: `${this.ignoreFile} not found. Create with default ignore patterns?`, default: true } ]); if (answers.createIgnoreFile) { await this.createDefaultIgnoreFile(); console.log(`Created ${this.ignoreFile} with default patterns`); } else { const customAnswers = await inquirer.prompt([ { type: 'input', name: 'customPatterns', message: 'Enter custom ignore patterns (comma-separated, or leave empty):\n Alternatively close this process and create a .dirtree.ignore file manually.', default: '' } ]); const patterns = customAnswers.customPatterns ? customAnswers.customPatterns.split(',').map(p => p.trim()).filter(p => p) : []; await this.createIgnoreFile(patterns); console.log(`Created ${this.ignoreFile} with custom patterns`); } } } async createDefaultIgnoreFile() { const content = DirTreeGenerator.defaultIgnores.join('\n') + '\n'; await fs.writeFile(this.ignoreFile, content, 'utf8'); } async createIgnoreFile(patterns) { const content = patterns.length > 0 ? patterns.join('\n') + '\n' : ''; await fs.writeFile(this.ignoreFile, content, 'utf8'); } async loadIgnorePatterns() { try { const content = await fs.readFile(this.ignoreFile, 'utf8'); this.ignorePatterns = content .split('\n') .map(line => line.trim()) .filter(line => line && !line.startsWith('#')) .map(pattern => this.createRegexFromPattern(pattern)); } catch { this.ignorePatterns = []; } } createRegexFromPattern(pattern) { const escaped = pattern .replace(/\./g, '\\.') .replace(/\*/g, '.*') .replace(/\?/g, '.'); return new RegExp(`^${escaped}$`); } shouldIgnore(itemName, itemPath) { return this.ignorePatterns.some(regex => { return regex.test(itemName) || regex.test(path.basename(itemPath)); }); } async generateTree(dirPath, prefix = '', isLast = true, depth = 0) { if (depth > 50) { return prefix + '└── [MAX DEPTH REACHED]\n'; } let result = ''; try { const items = await fs.readdir(dirPath, { withFileTypes: true }); const filteredItems = []; for (const item of items) { const itemPath = path.join(dirPath, item.name); if (this.shouldIgnore(item.name, itemPath)) { continue; } filteredItems.push({ name: item.name, path: itemPath, isDirectory: item.isDirectory() }); } filteredItems.sort((a, b) => { if (a.isDirectory && !b.isDirectory) return -1; if (!a.isDirectory && b.isDirectory) return 1; return a.name.localeCompare(b.name); }); for (let i = 0; i < filteredItems.length; i++) { const item = filteredItems[i]; if (!item) continue; const isLastItem = i === filteredItems.length - 1; const connector = isLastItem ? '└── ' : '├── '; const icon = item.isDirectory ? '📁 ' : '📄 '; result += prefix + connector + icon + item.name + '\n'; if (item.isDirectory) { const nextPrefix = prefix + (isLastItem ? ' ' : '│ '); result += await this.generateTree(item.path, nextPrefix, isLastItem, depth + 1); } } } catch { result += prefix + '└── [ERROR: Cannot read directory]\n'; } return result; } async writeTreeToFile(tree) { const header = `Directory Tree - Generated with dirtree on ${new Date().toISOString()}\n` + `Root: ${path.resolve('.')}\n` + `Ignore file: ${this.ignoreFile}\n` + '─'.repeat(80) + '\n\n'; const content = header + '📁 .\n' + tree; await fs.writeFile(this.outputFile, content, 'utf8'); } } DirTreeGenerator.defaultIgnores = [ 'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'out', 'target', '.next', '.nuxt', '__pycache__', '.pytest_cache', 'venv', 'env', '.venv', '.env', 'vendor', '.idea', '.vscode', '*.log', '.DS_Store', 'Thumbs.db', 'coverage', '.nyc_output', 'tmp', 'temp', '.cache', '.parcel-cache' ]; const generator = new DirTreeGenerator(); generator.init().catch(error => { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('Fatal error:', errorMessage); process.exit(1); }); export default DirTreeGenerator;