Initial commit: CodeMap v0.1.0 - CLI tool for code analysis and diagram generation
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-01-30 12:19:05 +00:00
parent db7afb5ce8
commit c95103dd33

144
codemap/cli/analyze.py Normal file
View File

@@ -0,0 +1,144 @@
import os
import sys
from pathlib import Path
from typing import Optional, List
import typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from codemap.parsers import PythonParser, JavaScriptParser, GoParser
from codemap.core import GraphBuilder, MermaidGenerator
from codemap.templates import render_html
console = Console()
def analyze(
path: str = typer.Argument(..., help="Directory or file to analyze"),
output: Optional[str] = typer.Option(None, "--output", "-o", help="Output file path"),
format: str = typer.Option("mermaid", "--format", "-f", help="Output format (mermaid, html, markdown)"),
max_depth: int = typer.Option(3, "--max-depth", "-d", help="Maximum dependency depth"),
include_packages: bool = typer.Option(True, "--include-packages/--no-packages", help="Include package groupings"),
) -> None:
target_path = Path(path)
if not target_path.exists():
console.print(f"[red]Error: Path '{path}' does not exist[/red]")
sys.exit(1)
if not target_path.is_dir() and not target_path.suffix in [".py", ".js", ".jsx", ".ts", ".tsx", ".go", ".mjs"]:
console.print(f"[red]Error: Unsupported file type[/red]")
sys.exit(1)
with console.status("[bold green]Analyzing codebase...") as status:
files = _collect_files(target_path)
if not files:
console.print("[yellow]No supported files found[/yellow]")
return
status.update("[bold green]Parsing files...")
parsers = [PythonParser(), JavaScriptParser(), GoParser()]
parsed_files = []
errors = []
for file_path in files:
for parser in parsers:
if parser.can_parse(file_path):
try:
parsed = parser.parse(file_path)
parsed_files.append(parsed)
except Exception as e:
errors.append((file_path, str(e)))
break
status.update("[bold green]Building dependency graph...")
builder = GraphBuilder()
builder.build_from_files(parsed_files)
status.update("[bold green]Generating diagram...")
if max_depth > 0:
start_nodes = [str(f.file_path.absolute()) for f in parsed_files]
filtered_graph = builder.filter_by_depth(start_nodes, max_depth)
graph_data = builder._build_graph_data_from_filtered(filtered_graph)
else:
graph_data = builder.get_graph_data()
mermaid_gen = MermaidGenerator(graph_data)
if format == "mermaid":
output_content = mermaid_gen.generate_flowchart(include_packages)
elif format == "markdown":
output_content = _generate_markdown(graph_data, mermaid_gen, include_packages)
elif format == "html":
output_content = render_html(mermaid_gen.generate_flowchart(include_packages))
else:
output_content = mermaid_gen.generate_flowchart(include_packages)
_display_results(builder, errors, path)
if output:
Path(output).write_text(output_content, encoding="utf-8")
console.print(f"[green]Output written to {output}[/green]")
else:
console.print(output_content)
def _collect_files(target_path: Path) -> List[Path]:
files = []
extensions = [".py", ".pyi", ".js", ".jsx", ".ts", ".tsx", ".go", ".mjs"]
if target_path.is_file():
if target_path.suffix in extensions:
return [target_path]
return []
for ext in extensions:
files.extend(target_path.rglob(f"*{ext}"))
return sorted(set(files))
def _display_results(builder: GraphBuilder, errors: List, path: str) -> None:
stats = builder.get_stats()
table = Table(title="Analysis Summary")
table.add_column("Metric", style="cyan")
table.add_column("Value", style="magenta")
table.add_row("Files Analyzed", str(stats["file_count"]))
table.add_row("Nodes", str(stats["node_count"]))
table.add_row("Dependencies", str(stats["edge_count"]))
table.add_row("Is DAG", str(stats["is_dag"]))
console.print(Panel(table, title=f"Analysis Results for {path}", expand=False))
if errors:
error_table = Table(title="Parse Errors")
error_table.add_column("File", style="red")
error_table.add_column("Error", style="yellow")
for file_path, error in errors[:10]:
error_table.add_row(str(file_path), error)
console.print(Panel(error_table, title="Errors", expand=False))
def _generate_markdown(graph_data, mermaid_gen: MermaidGenerator, include_packages: bool) -> str:
md = ["# Code Map Analysis\n"]
md.append(f"## Summary\n")
md.append(f"- **Nodes**: {len(graph_data.nodes)}")
md.append(f"- **Edges**: {len(graph_data.edges)}")
md.append(f"- **Packages**: {len(graph_data.packages)}\n")
md.append("## Dependency Graph\n")
md.append("```mermaid")
md.append(mermaid_gen.generate_flowchart(include_packages))
md.append("```\n")
if graph_data.packages:
md.append("## Packages\n")
for package, nodes in graph_data.packages.items():
md.append(f"### {package}")
for node_id in nodes:
node = next((n for n in graph_data.nodes if n.id == node_id), None)
if node:
md.append(f"- {node.name}")
return "\n".join(md)