122 lines
5.0 KiB
Python
122 lines
5.0 KiB
Python
import os
|
|
import re
|
|
import pathspec
|
|
|
|
def scan_for_reusability(start_path='.', output_file='docs/05_Reusabilidade_Ativos.md', gitignore_file='.gitignore'):
|
|
"""
|
|
Varre o projeto em busca de Classes, Funções Exportadas e Web Components.
|
|
Gera um relatório Markdown com JSDocs extraídos.
|
|
"""
|
|
|
|
# Configurações de Filtro
|
|
ignore_patterns = ['.git', '.agent', 'node_modules', 'venv', 'dist', 'build', '*.min.js']
|
|
if os.path.exists(gitignore_file):
|
|
with open(gitignore_file, 'r', encoding='utf-8') as f:
|
|
ignore_patterns.extend(f.read().splitlines())
|
|
spec = pathspec.PathSpec.from_lines('gitwildmatch', ignore_patterns)
|
|
|
|
# Regex patterns para identificar componentes reutilizáveis (JS/TS)
|
|
patterns = {
|
|
'Class': r'export\s+class\s+(\w+)',
|
|
'Function': r'export\s+function\s+(\w+)',
|
|
'WebComponent': r'customElements\.define\([\'"](.*?)[\'"]',
|
|
'Vue/React': r'export\s+default\s+\{\s*name:\s*[\'"](\w+)[\'"]'
|
|
}
|
|
|
|
# Regex para capturar comentários JSDoc imediatamente antes
|
|
doc_pattern = r'/\*\*(.*?)\*/\s*'
|
|
|
|
inventory = []
|
|
|
|
print("🕵️ Iniciando varredura de reusabilidade...")
|
|
|
|
for root, dirs, files in os.walk(start_path):
|
|
# Filtra pastas ignoradas
|
|
dirs[:] = [d for d in dirs if not spec.match_file(os.path.join(root, d))]
|
|
|
|
for file in files:
|
|
file_path = os.path.join(root, file)
|
|
|
|
# Analisa apenas arquivos .js, .ts, .jsx, .vue
|
|
if not file.endswith(('.js', '.ts', '.jsx', '.vue')) or spec.match_file(file_path):
|
|
continue
|
|
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
content = f.read()
|
|
|
|
# Procura por definições
|
|
for tipo, regex in patterns.items():
|
|
matches = re.finditer(regex, content)
|
|
for match in matches:
|
|
name = match.group(1)
|
|
# Tenta pegar o JSDoc antes da definição
|
|
start_index = match.start()
|
|
preceding_text = content[:start_index].strip()
|
|
|
|
description = "Sem documentação."
|
|
# Verifica se termina com um bloco de comentário
|
|
# Usamos rfind para garantir que pegamos apenas o ÚLTIMO bloco de comentário antes da definição
|
|
last_doc_start = preceding_text.rfind('/**')
|
|
if last_doc_start != -1:
|
|
potential_doc = preceding_text[last_doc_start:]
|
|
doc_match = re.search(r'/\*\*(.*?)\*/$', potential_doc, re.DOTALL)
|
|
if doc_match:
|
|
# Limpa os asteriscos do JSDoc
|
|
raw_doc = doc_match.group(1)
|
|
clean_doc = '\n'.join([line.strip().lstrip('*').strip() for line in raw_doc.splitlines() if line.strip()])
|
|
description = clean_doc if clean_doc else "Documentação vazia."
|
|
|
|
inventory.append({
|
|
'file': file_path,
|
|
'type': tipo,
|
|
'name': name,
|
|
'desc': description,
|
|
'size': os.path.getsize(file_path)
|
|
})
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Erro ao ler {file_path}: {e}")
|
|
|
|
# Gera o Relatório Markdown
|
|
return generate_markdown_report(inventory, output_file)
|
|
|
|
def generate_markdown_report(inventory, output_file):
|
|
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
|
|
|
md = [
|
|
"# ♻️ Catálogo de Reusabilidade",
|
|
f"> Gerado automaticamente. Total de ativos encontrados: **{len(inventory)}**",
|
|
"",
|
|
"## 🧩 Componentes e Módulos",
|
|
""
|
|
]
|
|
|
|
# Agrupa por arquivo para ficar organizado
|
|
files_map = {}
|
|
for item in inventory:
|
|
if item['file'] not in files_map: files_map[item['file']] = []
|
|
files_map[item['file']].append(item)
|
|
|
|
for file_path, items in files_map.items():
|
|
file_size_kb = os.path.getsize(file_path) / 1024
|
|
md.append(f"### 📄 `{file_path}` ({file_size_kb:.2f} KB)")
|
|
md.append("| Tipo | Nome | Descrição (JSDoc) |")
|
|
md.append("| :--- | :--- | :--- |")
|
|
|
|
for item in items:
|
|
desc_short = item['desc'].replace('\n', ' <br> ')
|
|
md.append(f"| `{item['type']}` | **{item['name']}** | {desc_short} |")
|
|
|
|
md.append("")
|
|
|
|
with open(output_file, 'w', encoding='utf-8') as f:
|
|
f.write('\n'.join(md))
|
|
|
|
return f"✅ Relatório salvo em: {output_file}"
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
# Se passar argumento, muda o arquivo de saída
|
|
out = sys.argv[1] if len(sys.argv) > 1 else 'docs/05_Reusabilidade_Ativos.md'
|
|
print(scan_for_reusability(output_file=out)) |