UNPKG

treecat

Version:

View folder structure like tree and display file contents like cat — all in one CLI.

151 lines (129 loc) 5.11 kB
import { fs, path, chalk } from './deps.js'; import STATE from './state.js'; import { TreecatError } from './errors.js'; import { getTimestamp } from './getTimestamp.js'; import { clearLine } from './clearLine.js'; import { saveConfig } from './saveConfig.js'; import { readGitignore } from './readGitignore.js'; import { collectMatchedFiles } from './collectMatchedFiles.js'; import { generateFileTree } from './generateFileTree.js'; import { renderTree } from './renderTree.js'; import { processFileContents } from './processFileContents.js'; import { createOutputHandler } from './outputHandler.js'; import { createArchive } from './createArchive.js'; /** * Executes the full scan process with debug logs. * @param {Object} options - Options selected via CLI or interactive prompt. * @returns {Promise<boolean>} - True if scan succeeded, false otherwise. */ export async function executeScan(options) { console.debug('[DEBUG] executeScan() called with options:', options); STATE.stats.startTime = Date.now(); try { STATE.targetDir = path.resolve(options.targetDir); console.debug('[DEBUG] targetDir resolved to:', STATE.targetDir); STATE.config = { ...STATE.config, targetDir: STATE.targetDir, excludeGitignore: options.excludeGitignore, extensions: options.extensions, patterns: options.patterns, keyword: options.keyword, outputChoice: options.outputFormat }; console.debug('[DEBUG] STATE.config updated:', STATE.config); if (options.outputFormat !== 'terminal') { await fs.mkdir(STATE.outputDir, { recursive: true }); STATE.outputFile = path.join( STATE.outputDir, `treecat-${getTimestamp()}.${options.outputFormat}` ); STATE.saveTo = options.outputFormat; console.debug('[DEBUG] outputFile created at:', STATE.outputFile); } const excludeList = options.excludeGitignore ? await readGitignore(path.join(STATE.targetDir, '.gitignore')) : []; console.debug('[DEBUG] Exclude list:', excludeList); const outputHandler = createOutputHandler(STATE.saveTo, STATE.outputFile); outputHandler.write(`# Treecat Scan Report - ${new Date().toISOString()}`); outputHandler.write(`# Target: ${STATE.targetDir}\n`); console.log(chalk.blue('\nScanning directory structure...')); const tree = await generateFileTree( STATE.targetDir, excludeList, options.extensions, options.patterns ); outputHandler.write('\n## Directory Structure\n'); outputHandler.write('```'); outputHandler.write(tree.name); renderTree(tree, '', outputHandler.write); outputHandler.write('```\n'); const files = await collectMatchedFiles( STATE.targetDir, excludeList, options.extensions, options.patterns ); console.debug('[DEBUG] Files matched:', files.length); if (files.length > 0) { console.log(chalk.blue(`\nProcessing ${files.length} files...`)); outputHandler.write('\n## File Contents\n'); for (const file of files) { clearLine(); process.stdout.write(`Processing: ${path.basename(file)}`); await processFileContents(file, outputHandler.write); } clearLine(); console.log(chalk.green(`Processed ${files.length} files successfully`)); } else { outputHandler.write('\nNo matching files found\n'); console.log(chalk.yellow('No matching files found')); } STATE.stats.endTime = Date.now(); const duration = (STATE.stats.endTime - STATE.stats.startTime) / 1000; outputHandler.write('\n## Scan Statistics\n'); outputHandler.write('```'); outputHandler.write(`Directories scanned: ${STATE.stats.directoriesScanned}`); outputHandler.write(`Files processed: ${STATE.stats.filesProcessed}`); outputHandler.write(`Keyword matches: ${STATE.stats.matchesFound}`); outputHandler.write(`Errors encountered: ${STATE.errorLog.length}`); outputHandler.write(`Processing time: ${duration.toFixed(2)} seconds`); outputHandler.write('```\n'); if (STATE.errorLog.length > 0) { const errorLogPath = path.join( STATE.outputDir, `treecat-errors-${getTimestamp()}.log` ); await fs.writeFile( errorLogPath, STATE.errorLog.map(e => `${e.timestamp} | ${e.file}: ${e.error}`).join('\n') ); console.log(chalk.yellow(`\nErrors logged to: ${errorLogPath}`)); } if (STATE.saveTo !== 'terminal') { outputHandler.end(); console.log(chalk.green(`\nOutput saved to: ${STATE.outputFile}`)); if (files.length > 10) { const archivePath = await createArchive(files); console.log(chalk.green(`Files archived to: ${archivePath}`)); } } if (options.saveConfig) { const configPath = path.join(STATE.targetDir, '.treecatrc.json'); console.debug('[DEBUG] Saving config to:', configPath); await saveConfig(configPath, STATE.config, TreecatError); } console.debug('[DEBUG] executeScan() completed successfully'); return true; } catch (error) { console.debug('[DEBUG] executeScan() threw error:', error); if (error instanceof TreecatError) { console.error(error.toString()); } else { console.error(chalk.red.bold('[FATAL]'), error?.message || error); } return false; } }