UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

145 lines (137 loc) 7.19 kB
/** * @file Comprehensive directory scanning utilities for file discovery and traversal * @description Single responsibility: Provide robust directory traversal across AgentSqripts analyzers * * This utility module provides both synchronous and asynchronous directory scanning * capabilities with extension filtering, pattern exclusion, and comprehensive error handling. * It serves as the foundation for all file discovery operations in the analysis platform, * handling cross-platform directory traversal complexity while maintaining performance. * * Design rationale: * - Dual sync/async interfaces support different analysis workflow requirements * - Extension filtering enables targeted analysis of specific file types * - Pattern exclusion prevents analysis of build artifacts and dependencies * - Comprehensive logging aids in debugging directory traversal issues */ const fs = require('fs'); // built-in file system module const { promises: fsPromises } = require('fs'); // promise-based fs methods const path = require('path'); // path utility for building file paths // Removed qerrors dependency for qtests compatibility // const qerrors = require('qerrors'); // error logging helper /** * Recursively scan a directory and collect file paths * @param {string} currentPath - directory path to scan * @param {Array<string>} files - accumulator for discovered file paths * @param {Array<string>} extensions - file extensions to include * @param {Array<string>} excludePatterns - patterns to exclude from results * @returns {Promise<void>} resolves when scan completes * @throws {Error} when directory access fails */ async function scanDirectory(currentPath, files = [], extensions = [], excludePatterns = []) { // Removed verbose logging for cleaner output try { const entries = await fsPromises.readdir(currentPath, { withFileTypes: true }); // read directory entries for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); // build full path for entry if (excludePatterns.some(pattern => fullPath.includes(pattern))) continue; // skip excluded patterns if (entry.isDirectory()) { await scanDirectory(fullPath, files, extensions, excludePatterns); // recurse into subdirectory } else if (entry.isFile()) { const ext = path.extname(entry.name); // get file extension if (extensions.includes(ext)) files.push(fullPath); // store matching file } } } catch (error) { console.warn(`scanDirectory error in ${currentPath}:`, error.message); // log scanning errors } } /** * Recursively get all files from a directory * @param {string} dirPath - Directory to scan * @param {Array<string>} extensions - File extensions to include * @param {Array<string>} excludePatterns - Patterns to exclude * @returns {Promise<Array<string>>} Array of file paths */ async function getAllFiles(dirPath, extensions = ['.js', '.ts', '.jsx', '.tsx'], excludePatterns = ['node_modules', '.git', 'dist', 'build', '.cache', '.local', '.config', '.upm', 'tempAgentFiles', 'attached_assets', 'logs', '.clinerules', '.kilocode', '.roo']) { // Enhanced exclusion patterns for better filtering const files = []; // collected file paths await scanDirectory(dirPath, files, extensions, excludePatterns); // delegate to helper const quiet = process.env.QUIET_LOGS === '1' || process.env.ANALYSIS_OUTPUT === 'json'; if (!quiet) { console.log(`📁 Found ${files.length} files for analysis`); // single concise log } return files; // return collected files } /** * Walk a directory tree synchronously and collect file paths * @param {string} currentPath - directory path to walk * @param {Array<string>} files - accumulator for discovered file paths * @param {Array<string>} extensions - file extensions to include * @param {Array<string>} excludePatterns - patterns to exclude from results * @returns {void} * @throws {Error} when directory access fails */ async function walkDirectoryAsync(currentPath, files = [], extensions = [], excludePatterns = []) { // Removed verbose logging for cleaner output try { const entries = await fsPromises.readdir(currentPath, { withFileTypes: true }); // read directory contents async for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); // build full path for entry if (excludePatterns.some(pattern => fullPath.includes(pattern))) continue; // skip excluded patterns if (entry.isDirectory()) { await walkDirectoryAsync(fullPath, files, extensions, excludePatterns); // recurse into subdirectory } else if (entry.isFile()) { const ext = path.extname(entry.name); // get file extension if (extensions.includes(ext)) files.push(fullPath); // store matching file } } // Removed excessive logging } catch (error) { console.warn(`walkDirectoryAsync error in ${currentPath}:`, error.message); // log walking errors } } // Keep sync version for backward compatibility but mark as legacy function walkDirectorySync(currentPath, files = [], extensions = [], excludePatterns = []) { // Removed excessive logging try { const entries = fs.readdirSync(currentPath); // read directory contents for (const entry of entries) { const fullPath = path.join(currentPath, entry); // build full path for entry if (excludePatterns.some(pattern => fullPath.includes(pattern))) continue; // skip excluded patterns const stat = fs.statSync(fullPath); // fetch file stats if (stat.isDirectory()) { walkDirectorySync(fullPath, files, extensions, excludePatterns); // recurse into subdirectory } else if (stat.isFile()) { const ext = path.extname(entry); // get file extension if (extensions.includes(ext)) files.push(fullPath); // store matching file } } // Removed excessive logging } catch (error) { console.warn(`walkDirectorySync error in ${currentPath}:`, error.message); // log walking errors } } /** * Synchronous version of getAllFiles * @param {string} dirPath - Directory to scan * @param {Array<string>} extensions - File extensions to include * @param {Array<string>} excludePatterns - Patterns to exclude * @returns {Array<string>} Array of file paths */ function getAllFilesSync(dirPath, extensions = ['.js', '.ts', '.jsx', '.tsx'], excludePatterns = ['node_modules', '.git', 'dist', 'build']) { // Removed excessive logging const files = []; // collected file paths walkDirectorySync(dirPath, files, extensions, excludePatterns); // delegate to helper const quiet = process.env.QUIET_LOGS === '1' || process.env.ANALYSIS_OUTPUT === 'json'; if (!quiet) { console.log(`📁 Found ${files.length} files for analysis`); // single concise log } return files; // return collected files } module.exports = { scanDirectory, // async directory scanner walkDirectoryAsync, // preferred async directory walker walkDirectorySync, // legacy sync directory walker (compatibility only) getAllFiles, // async interface for scanning getAllFilesSync // sync interface for scanning (compatibility only) };