import os from pathlib import Path from pathspec import PathSpec from pathspec.patterns import GitWildMatchPattern from ..utils.logger import log class ProjectMap: def __init__(self, project_path: str): self.project_path = project_path self.gitignore_rules = self._load_gitignore() def generate_map(self) -> str: """ Создает карту структуры проекта, игнорируя файлы из .gitignore и директорию .git/. """ log("Generating project structure map...") structure = self._traverse_directory(self.project_path) return structure def _traverse_directory(self, path: str, level: int = 0) -> str: """ Рекурсивно обходит директории и формирует строку структуры, игнорируя файлы и директории из .gitignore и .git/. """ indent = " " * level result = "" for entry in sorted(os.listdir(path)): full_path = os.path.join(path, entry) # Игнорируем директорию .git/ if entry == ".git": continue # Проверяем, игнорируется ли файл или директория по правилам .gitignore relative_path = os.path.relpath(full_path, self.project_path) if self._is_ignored(relative_path): continue if os.path.isdir(full_path): result += f"{indent}- {entry}/\n" result += self._traverse_directory(full_path, level + 1) else: result += f"{indent}- {entry}\n" return result def save_map(self, output_file: str = "react_app_map.txt"): """ Сохраняет карту структуры в файл. """ map_content = self.generate_map() with open(output_file, "w", encoding="utf-8") as f: f.write(map_content) log(f"Project map saved to {output_file}") def _load_gitignore(self) -> list: """ Загружает правила из .gitignore, если файл существует. """ gitignore_path = os.path.join(self.project_path, ".gitignore") rules = [] if os.path.exists(gitignore_path): with open(gitignore_path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if line and not line.startswith("#"): # Игнорируем комментарии и пустые строки rules.append(line) return rules def _is_ignored(self, path: str) -> bool: """ Проверяет, игнорируется ли файл или директория по правилам .gitignore, включая поддержку сложных правил (например, **/logs/*.log). """ # Преобразуем правила .gitignore в PathSpec spec = PathSpec.from_lines(GitWildMatchPattern, self.gitignore_rules) # Проверяем, соответствует ли путь правилам return spec.match_file(path)