@nntdgrss/node-modules-cleaner
Version:
Утилита для поиска и удаления неиспользуемых node_modules директорий
131 lines (130 loc) • 5.33 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileUtils = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const glob_1 = require("glob");
const logger_utils_1 = require("./logger.utils");
/**
* Класс для работы с файловой системой
*/
class FileUtils {
/**
* Поиск всех директорий node_modules в указанной директории
* @param config - конфигурация поиска
* @returns Promise<NodeModulesInfo[]> - массив информации о найденных директориях
*/
static async findNodeModules(config) {
try {
const pattern = path_1.default.join(config.startPath, config.maxDepth === -1
? "**/node_modules"
: `${"*/".repeat(config.maxDepth)}node_modules`);
let paths = await (0, glob_1.glob)(pattern, {
ignore: config.exclude,
absolute: true,
});
// Фильтруем вложенные node_modules
paths = paths.filter((p) => {
const relativePath = p.substring(config.startPath.length);
const count = relativePath
.split("/")
.filter((part) => part === "node_modules").length;
return count === 1;
});
logger_utils_1.Logger.info(`Найдено ${paths.length} директорий node_modules`);
const results = [];
let processed = 0;
for (const nodePath of paths) {
try {
const stats = await fs_extra_1.default.stat(nodePath);
const size = await this.getDirectorySize(nodePath);
const lastModified = stats.mtime;
const isUnused = this.isDirectoryUnused(lastModified);
results.push({
path: nodePath,
size,
lastModified,
isUnused,
});
processed++;
logger_utils_1.Logger.progress(processed, paths.length, "Анализ директорий");
}
catch (err) {
logger_utils_1.Logger.warn(`Ошибка при обработке ${nodePath}: ${err}`);
}
}
return results;
}
catch (err) {
logger_utils_1.Logger.error(`Ошибка при поиске node_modules: ${err}`);
return [];
}
}
/**
* Получение размера директории
* @param dirPath - путь к директории
* @returns Promise<number> - размер в байтах
*/
static async getDirectorySize(dirPath) {
try {
const files = await fs_extra_1.default.readdir(dirPath);
const stats = await Promise.all(files.map(async (file) => {
const filePath = path_1.default.join(dirPath, file);
const stat = await fs_extra_1.default.stat(filePath);
if (stat.isDirectory()) {
return this.getDirectorySize(filePath);
}
return stat.size;
}));
return stats.reduce((acc, size) => acc + size, 0);
}
catch (err) {
logger_utils_1.Logger.warn(`Ошибка при подсчете размера ${dirPath}: ${err}`);
return 0;
}
}
/**
* Проверка, является ли директория неиспользуемой (старше 1 месяца)
* @param lastModified - дата последнего изменения
* @returns boolean
*/
static isDirectoryUnused(lastModified) {
const oneMonthAgo = new Date();
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
return lastModified < oneMonthAgo;
}
/**
* Удаление директории node_modules
* @param path - путь к директории
* @returns Promise<boolean> - успешность удаления
*/
static async removeNodeModules(path) {
try {
await fs_extra_1.default.remove(path);
return true;
}
catch (err) {
logger_utils_1.Logger.error(`Ошибка при удалении ${path}: ${err}`);
return false;
}
}
/**
* Форматирование размера в читаемый вид
* @param bytes - размер в байтах
* @returns string - отформатированный размер
*/
static formatSize(bytes) {
const units = ["Б", "КБ", "МБ", "ГБ"];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
}
exports.FileUtils = FileUtils;