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
JavaScript
/**
* @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)
};