dir-analysis-tool
Version:
A comprehensive cross-platform CLI tool for advanced directory analysis with file classification, duplicate detection, large file identification, interactive mode, HTML reports, and multiple export formats. Perfect for disk cleanup, storage audits, and pr
199 lines • 8.82 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DirectoryAnalyzer = void 0;
const fs_1 = require("fs");
const path = __importStar(require("path"));
const classifier_1 = require("./classifier");
const utils_1 = require("./utils");
const advanced_analysis_1 = require("./advanced-analysis");
const tree_view_1 = require("./tree-view");
class DirectoryAnalyzer {
classifier;
totalSize = 0;
folderCount = 0;
fileCount = 0;
allFiles = [];
processedFiles = 0;
currentDepth = 0;
constructor() {
this.classifier = new classifier_1.FileClassifier();
}
async analyze(options) {
this.totalSize = 0;
this.folderCount = 0;
this.fileCount = 0;
this.allFiles = [];
this.processedFiles = 0;
this.currentDepth = 0;
this.classifier.reset();
try {
const stats = await fs_1.promises.stat(options.path);
if (!stats.isDirectory()) {
throw new Error(`Path '${options.path}' is not a directory`);
}
}
catch (error) {
throw new Error(`Unable to access path '${options.path}': ${error}`);
}
if (options.progressCallback) {
options.progressCallback(0, 1, 'Scanning directories...');
}
const totalFiles = await this.countFiles(options.path, options.recursive, options.excludePatterns, options.maxDepth || -1);
await this.analyzeDirectory(options.path, options.recursive, options.excludePatterns, options.progressCallback, totalFiles, options.maxDepth || -1, 0);
const basicResult = {
path: options.path,
totalSizeBytes: this.totalSize,
totalSizeMB: (0, utils_1.formatSizeInMB)(this.totalSize),
folders: this.folderCount,
files: this.fileCount,
types: this.classifier.getClassification()
};
const extendedResult = { ...basicResult };
if (options.largeSizeThreshold) {
extendedResult.largeFiles = advanced_analysis_1.LargeFileDetector.detectLargeFiles(this.allFiles, options.largeSizeThreshold);
}
if (options.enableDuplicateDetection) {
const duplicateDetector = new advanced_analysis_1.DuplicateDetector();
const filePaths = this.allFiles.map(f => f.path);
if (options.progressCallback) {
options.progressCallback(0, filePaths.length, 'Detecting duplicates...');
}
extendedResult.duplicateGroups = await duplicateDetector.detectDuplicates(filePaths, options.progressCallback);
if (extendedResult.duplicateGroups.length > 0) {
const totalWastedSpace = extendedResult.duplicateGroups.reduce((sum, group) => sum + group.totalWastedSpace, 0);
extendedResult.duplicateStats = {
totalGroups: extendedResult.duplicateGroups.length,
totalWastedSpace,
totalWastedSpaceFormatted: require('./utils').formatSize(totalWastedSpace)
};
}
}
let filteredFiles = this.allFiles;
if (options.minSize !== undefined || options.maxSize !== undefined) {
filteredFiles = advanced_analysis_1.FileFilter.filterBySize(filteredFiles, options.minSize, options.maxSize);
}
if (options.dateFrom || options.dateTo) {
filteredFiles = await advanced_analysis_1.FileFilter.filterByDate(filteredFiles, options.dateFrom, options.dateTo);
}
if (options.topN) {
extendedResult.topLargestFiles = advanced_analysis_1.LargeFileDetector.getTopLargestFiles(filteredFiles, options.topN);
}
if (options.showEmptyFiles) {
extendedResult.emptyFiles = await advanced_analysis_1.EmptyFileDetector.detectEmptyFiles(this.allFiles);
}
if (filteredFiles.length <= 1000) {
extendedResult.treeView = tree_view_1.TreeViewGenerator.generateCompactTreeView(filteredFiles, options.path, 50);
}
return extendedResult;
}
async countFiles(dirPath, recursive, excludePatterns, maxDepth, currentDepth = 0) {
if (maxDepth >= 0 && currentDepth > maxDepth) {
return 0;
}
try {
const entries = await fs_1.promises.readdir(dirPath, { withFileTypes: true });
let count = 0;
for (const entry of entries) {
if (entry.isDirectory()) {
if ((0, utils_1.shouldExcludeDirectory)(entry.name, excludePatterns)) {
continue;
}
if (recursive) {
const fullPath = path.join(dirPath, entry.name);
count += await this.countFiles(fullPath, recursive, excludePatterns, maxDepth, currentDepth + 1);
}
}
else if (entry.isFile()) {
if (!(0, utils_1.shouldExcludeFile)(entry.name, excludePatterns)) {
count++;
}
}
}
return count;
}
catch (error) {
return 0;
}
}
async analyzeDirectory(dirPath, recursive, excludePatterns, progressCallback, totalFiles, maxDepth = -1, currentDepth = 0) {
if (maxDepth >= 0 && currentDepth > maxDepth) {
return;
}
try {
const entries = await fs_1.promises.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
if ((0, utils_1.shouldExcludeDirectory)(entry.name, excludePatterns)) {
continue;
}
this.folderCount++;
if (recursive) {
await this.analyzeDirectory(fullPath, recursive, excludePatterns, progressCallback, totalFiles, maxDepth, currentDepth + 1);
}
}
else if (entry.isFile()) {
if ((0, utils_1.shouldExcludeFile)(entry.name, excludePatterns)) {
continue;
}
try {
const stats = await fs_1.promises.stat(fullPath);
this.totalSize += stats.size;
this.fileCount++;
this.classifier.classifyFile(entry.name);
this.allFiles.push({
path: fullPath,
size: stats.size
});
this.processedFiles++;
if (progressCallback && totalFiles) {
progressCallback(this.processedFiles, totalFiles, fullPath);
}
}
catch (error) {
// Skip files that can't be accessed
console.warn(`Warning: Unable to access file '${fullPath}': ${error}`);
}
}
}
}
catch (error) {
console.warn(`Warning: Unable to read directory '${dirPath}': ${error}`);
}
}
}
exports.DirectoryAnalyzer = DirectoryAnalyzer;
//# sourceMappingURL=analyzer.js.map