@nntdgrss/node-modules-cleaner
Version:
Утилита для поиска и удаления неиспользуемых node_modules директорий
226 lines (225 loc) • 9.91 kB
JavaScript
;
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;