82 lines
3.2 KiB
Python
82 lines
3.2 KiB
Python
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") 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") 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)
|