agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
172 lines (154 loc) • 6.55 kB
JavaScript
/**
* @file Comprehensive file system utilities for robust cross-platform operations
* @description Single responsibility: Provide foundational file system operations with error handling and optimization
*
* This utility module provides essential file system operations that serve as building blocks
* for all AgentSqripts analysis tools. It implements recursive directory traversal, path
* validation, extension filtering, and pattern exclusion with robust error handling and
* performance optimizations for large-scale code analysis workflows.
*
* Design rationale:
* - Centralized file system operations eliminate code duplication across analyzers
* - Robust error handling prevents analysis pipeline failures from file system issues
* - Performance optimizations support analysis of large codebases efficiently
* - Cross-platform compatibility ensures consistent behavior across operating systems
* - Configurable filtering enables targeted analysis of specific file types and patterns
*
* Core capabilities:
* - Recursive directory traversal with intelligent exclusion patterns
* - Extension-based file filtering for targeted analysis workflows
* - Path validation and resolution with comprehensive error checking
* - Performance-optimized file discovery for large project analysis
* - Platform-independent file system operations with consistent behavior
*/
const fs = require('fs'); // built-in file system module
const path = require('path'); // built-in path module
const qerrors = require('qerrors'); // error logging helper
/**
* Get all files recursively from a directory (async version for better scalability)
* @param {string} dir - Directory path
* @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(dir, extensions = [], excludePatterns = []) {
const results = []; // collected file paths
await walkDirectoryAsync(dir, extensions, excludePatterns, results); // traverse directory tree
return results; // return accumulated results
}
/**
* Determine if a file should be excluded
* @param {string} filePath - Path of file to evaluate
* @param {Array<string>} excludePatterns - Patterns to match against
* @returns {boolean} True if file should be skipped
*/
function shouldExclude(filePath, excludePatterns = []) {
return excludePatterns.some(pattern => filePath.includes(pattern)); // check patterns
}
/**
* Walk a directory tree asynchronously for better scalability
* @param {string} currentDir - Directory to walk
* @param {Array<string>} extensions - Extensions to include
* @param {Array<string>} excludePatterns - Patterns to exclude
* @param {Array<string>} results - Accumulator for file paths
* @returns {Promise<void>}
*/
async function walkDirectoryAsync(currentDir, extensions = [], excludePatterns = [], results = []) {
try {
const { promises: fsPromises } = require('fs');
const files = await fsPromises.readdir(currentDir); // read directory contents async
for (const file of files) {
const filePath = path.join(currentDir, file); // build full path
if (shouldExclude(filePath, excludePatterns)) continue; // skip excluded paths
const stat = await fsPromises.stat(filePath); // inspect file stats async
if (stat.isDirectory()) {
await walkDirectoryAsync(filePath, extensions, excludePatterns, results); // recurse into directory
} else if (extensions.length === 0 || extensions.some(ext => file.endsWith(ext))) {
results.push(filePath); // store matching file
}
}
} catch (error) {
qerrors(error, `Failed to walk directory`, { currentDir }); // log walking errors
}
}
/**
* Walk a directory tree synchronously (legacy version for compatibility)
* @deprecated Use walkDirectoryAsync for better performance
* @param {string} currentDir - Directory to walk
* @param {Array<string>} extensions - Extensions to include
* @param {Array<string>} excludePatterns - Patterns to exclude
* @param {Array<string>} results - Accumulator for file paths
* @returns {void}
*/
function walkDirectorySync(currentDir, extensions = [], excludePatterns = [], results = []) {
console.warn(`Warning: walkDirectorySync is deprecated. Use walkDirectoryAsync for better performance.`);
try {
const files = fs.readdirSync(currentDir); // read directory contents
for (const file of files) {
const filePath = path.join(currentDir, file); // build full path
if (shouldExclude(filePath, excludePatterns)) continue; // skip excluded paths
const stat = fs.statSync(filePath); // inspect file stats
if (stat.isDirectory()) {
walkDirectorySync(filePath, extensions, excludePatterns, results); // recurse into directory
} else if (extensions.length === 0 || extensions.some(ext => file.endsWith(ext))) {
results.push(filePath); // store matching file
}
}
} catch (error) {
qerrors(error, `Failed to walk directory`, { currentDir }); // log walking errors
}
}
/**
* Check if path exists (async version for better scalability)
* @param {string} filePath - Path to check
* @returns {Promise<boolean>} True if path exists
*/
async function pathExists(filePath) {
try {
const { promises: fsPromises } = require('fs');
await fsPromises.access(filePath);
return true;
} catch {
return false;
}
}
/**
* Check if path exists synchronously (legacy version for compatibility)
* @deprecated Use pathExists for better performance
* @param {string} filePath - Path to check
* @returns {boolean} True if path exists
*/
function pathExistsSync(filePath) {
console.warn(`Warning: pathExistsSync is deprecated. Use pathExists for better performance.`);
try {
fs.accessSync(filePath);
return true;
} catch {
return false;
}
}
/**
* Read file safely (async version for better scalability)
* @param {string} filePath - File path
* @returns {Promise<string|null>} File content or null if error
*/
async function readFileSafely(filePath) {
try {
const { promises: fsPromises } = require('fs');
return await fsPromises.readFile(filePath, 'utf-8');
} catch (error) {
console.warn(`Warning: Could not read ${filePath}: ${error.message}`);
return null;
}
}
module.exports = {
fs,
path,
getAllFiles,
shouldExclude,
walkDirectoryAsync,
walkDirectorySync,
pathExists,
pathExistsSync,
readFileSafely
};