agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
138 lines (131 loc) • 7.15 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
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 = []) {
console.log(`scanDirectory scanning ${currentPath}`); // log function start
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
}
}
console.log(`scanDirectory processed ${currentPath}`); // log function completion
} 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']) {
console.log(`getAllFiles scanning ${dirPath}`); // log function start
const files = []; // collected file paths
await scanDirectory(dirPath, files, extensions, excludePatterns); // delegate to helper
console.log(`getAllFiles found ${files.length} files`); // log result count
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 = []) {
console.log(`walkDirectoryAsync scanning ${currentPath}`); // log function start
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
}
}
console.log(`walkDirectoryAsync processed ${currentPath}`); // log function completion
} 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 = []) {
console.log(`walkDirectorySync scanning ${currentPath}`); // log function start
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
}
}
console.log(`walkDirectorySync processed ${currentPath}`); // log function completion
} 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']) {
console.log(`getAllFilesSync scanning ${dirPath}`); // log function start
const files = []; // collected file paths
walkDirectorySync(dirPath, files, extensions, excludePatterns); // delegate to helper
console.log(`getAllFilesSync found ${files.length} files`); // log result count
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)
};