UNPKG

@nntdgrss/node-modules-cleaner

Version:

Утилита для поиска и удаления неиспользуемых node_modules директорий

226 lines (225 loc) 9.91 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RemoveCommand = void 0; const inquirer_1 = __importDefault(require("inquirer")); const archiver_1 = __importDefault(require("archiver")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const types_1 = require("../types/types"); const file_utils_1 = require("../utils/file.utils"); const find_command_1 = require("./find.command"); const progress_1 = require("../ui/progress"); const tree_view_1 = require("../ui/tree.view"); const spinner_1 = require("../ui/spinner"); /** * Класс для удаления директорий node_modules */ class RemoveCommand { /** * Выполнение команды удаления * @param options - опции удаления * @returns Promise<RemoveResult> */ static async execute(options) { const nodeModules = await find_command_1.FindCommand.execute(options.path); const result = { removed: 0, totalSize: 0, errors: [], }; if (nodeModules.length === 0) { return result; } switch (options.mode) { case types_1.RemoveMode.ALL: return this.removeAll(nodeModules, options); case types_1.RemoveMode.UNUSED: return this.removeUnused(nodeModules, options); case types_1.RemoveMode.INTERACTIVE: return this.removeInteractive(nodeModules, options); default: spinner_1.Spinner.error("Неизвестный режим удаления"); return result; } } /** * Создание бэкапа директорий * @param paths - пути к директориям для бэкапа * @returns Promise<string> - путь к файлу бэкапа */ static async createBackup(paths) { spinner_1.Spinner.start("Создание резервной копии..."); const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); const backupPath = path_1.default.join(process.cwd(), `node_modules_backup_${timestamp}.zip`); const output = fs_1.default.createWriteStream(backupPath); const archive = (0, archiver_1.default)("zip", { zlib: { level: 9 } }); archive.pipe(output); for (const dirPath of paths) { archive.directory(dirPath, path_1.default.basename(dirPath)); } await archive.finalize(); spinner_1.Spinner.success(`Резервная копия создана: ${backupPath}`); return backupPath; } /** * Удаление всех найденных директорий node_modules * @param nodeModules - массив информации о директориях * @param options - опции удаления */ static async removeAll(nodeModules, options) { const result = { removed: 0, totalSize: 0, errors: [] }; const totalSize = nodeModules.reduce((acc, info) => acc + info.size, 0); if (options.dryRun) { spinner_1.Spinner.info(`Тестовый режим: будет удалено ${nodeModules.length} директорий`); tree_view_1.TreeView.display(nodeModules); return result; } if (options.backup) { result.backupPath = await this.createBackup(nodeModules.map((info) => info.path)); } progress_1.Progress.start(nodeModules.length, "Удаление директорий"); for (const info of nodeModules) { try { if (!options.dryRun) { await file_utils_1.FileUtils.removeNodeModules(info.path); } result.removed++; result.totalSize += info.size; progress_1.Progress.update(result.removed, result.totalSize); } catch (err) { result.errors.push({ path: info.path, error: String(err) }); } } progress_1.Progress.stop(); this.displayResults(result); return result; } /** * Удаление неиспользуемых директорий node_modules * @param nodeModules - массив информации о директориях * @param options - опции удаления */ static async removeUnused(nodeModules, options) { const unusedModules = nodeModules.filter((info) => info.isUnused); const result = { removed: 0, totalSize: 0, errors: [] }; if (unusedModules.length === 0) { spinner_1.Spinner.info("Неиспользуемые директории не найдены"); return result; } if (options.dryRun) { spinner_1.Spinner.info(`Тестовый режим: будет удалено ${unusedModules.length} неиспользуемых директорий`); tree_view_1.TreeView.display(unusedModules); return result; } if (options.backup) { result.backupPath = await this.createBackup(unusedModules.map((info) => info.path)); } progress_1.Progress.start(unusedModules.length, "Удаление неиспользуемых директорий"); for (const info of unusedModules) { try { if (!options.dryRun) { await file_utils_1.FileUtils.removeNodeModules(info.path); } result.removed++; result.totalSize += info.size; progress_1.Progress.update(result.removed, result.totalSize); } catch (err) { result.errors.push({ path: info.path, error: String(err) }); } } progress_1.Progress.stop(); this.displayResults(result); return result; } /** * Интерактивное удаление выбранных директорий * @param nodeModules - массив информации о директориях * @param options - опции удаления */ static async removeInteractive(nodeModules, options) { const result = { removed: 0, totalSize: 0, errors: [] }; const choices = nodeModules.map((info) => ({ name: tree_view_1.TreeView.formatNodeInfo(info, ""), value: info, checked: info.isUnused, })); // Очищаем консоль перед отображением списка console.clear(); const { selected } = await inquirer_1.default.prompt([ { type: "checkbox", name: "selected", message: "Выберите директории для удаления (пробел - выбрать, enter - подтвердить):", choices, pageSize: 15, loop: false, }, ]); if (selected.length === 0) { spinner_1.Spinner.warn("Директории не выбраны"); return result; } const totalSize = selected.reduce((acc, info) => acc + info.size, 0); if (options.dryRun) { spinner_1.Spinner.info(`Тестовый режим: будет удалено ${selected.length} директорий`); tree_view_1.TreeView.display(selected); return result; } const { confirm } = await inquirer_1.default.prompt([ { type: "confirm", name: "confirm", message: `Подтвердите удаление ${selected.length} директорий (${file_utils_1.FileUtils.formatSize(totalSize)}):`, default: false, }, ]); if (!confirm) { spinner_1.Spinner.warn("Операция отменена"); return result; } if (options.backup) { result.backupPath = await this.createBackup(selected.map((info) => info.path)); } progress_1.Progress.start(selected.length, "Удаление выбранных директорий"); for (const info of selected) { try { if (!options.dryRun) { await file_utils_1.FileUtils.removeNodeModules(info.path); } result.removed++; result.totalSize += info.size; progress_1.Progress.update(result.removed, result.totalSize); } catch (err) { result.errors.push({ path: info.path, error: String(err) }); } } progress_1.Progress.stop(); this.displayResults(result); return result; } /** * Отображение результатов операции удаления * @param result - результаты удаления */ static displayResults(result) { console.clear(); spinner_1.Spinner.success(`\nУдалено ${result.removed} директорий\n` + `Освобождено: ${file_utils_1.FileUtils.formatSize(result.totalSize)}`); if (result.backupPath) { spinner_1.Spinner.info(`Резервная копия: ${result.backupPath}`); } if (result.errors.length > 0) { spinner_1.Spinner.warn("\nПроизошли ошибки при удалении:"); result.errors.forEach(({ path, error }) => { console.log(` ${path}: ${error}`); }); } } } exports.RemoveCommand = RemoveCommand;