Skip to content

ArchMAP — Architecture Overview

Pipeline summary

Every archmap command runs the same core pipeline:

Source files on disk
┌─────────────────────────────────────┐
│  Parser   (archmap.core.parser)     │  Reads source, extracts imports
└────────────────┬────────────────────┘
                 │  ParsedProject
┌─────────────────────────────────────┐
│  Graph Builder (archmap.core.graph) │  Builds directed node/edge graph
└────────────────┬────────────────────┘
                 │  Graph dict
┌─────────────────────────────────────┐
│  Analyzer  (archmap.core.analyzer)  │  Cycles, complexity, risks
└────────────────┬────────────────────┘
                 │  Report dict
┌─────────────────────────────────────┐
│  Exporters (archmap.exporters)      │  JSON / Mermaid / Cytoscape
└─────────────────────────────────────┘

Module reference

archmap.core.parser

Entry point: parse_project(project_path, virtual_files=None) → ParsedProject

Discovers all source files under project_path and calls the language-specific sub-parser for each:

Sub-parser Language Strategy
python_parser.py Python AST walk — captures import X, from X import Y
js_parser.py JavaScript Regex — import, require, export ... from, dynamic import()
ts_parser.py TypeScript Same as JS
rust_parser.py Rust Regex — use, mod, extern crate

Dependency resolution (_resolve_python_dependency, _resolve_js_ts_dependency, etc.) maps each import string to: - An internal file ID (type: "file") when the path exists in the project - An external package (type: "package", id prefixed with pkg:) otherwise

For Python absolute from pkg import name imports, each imported name is checked as a potential submodule (pkg/name.py, pkg/name/__init__.py) before falling back to the package root.


archmap.core.graph

Entry point: build_graph(parsed_project) → Graph

Converts a ParsedProject into a directed graph:

  • Nodes — one per file (plus synthetic package nodes for external deps)
  • Fields: id, label, type, language, folder, outgoing, incoming, isCircular
  • Edges — one per resolved dependency
  • Fields: id ("source->target"), source, target, isCircular

Edge deduplication is handled at this stage.


archmap.core.analyzer

Entry point: analyze_graph(graph) → Report

Three sub-analyzers run in sequence:

1. cycle_detector.py

Finds strongly connected components using a DFS variant. Returns cycles: list[list[str]] — each inner list is a set of file IDs forming a cycle.

2. complexity_analyzer.py

Annotates each node with a complexity score [0, 1] (normalized outgoing edge count). Produces metrics.complexity (top files by score) and metrics.criticalFiles (top files by incoming count).

3. risk_analyzer.py

Detects three architecture smell categories:

Risk Detection Threshold
God module outgoing ≥ p90 or 8 Dynamic, per-project
Dependency explosion incoming + outgoing ≥ p90 or 12 Dynamic, per-project
Layer violation Lower-rank layer imports higher-rank layer Hardcoded LAYER_ORDER map

Built-in layer ranks (higher = closer to user):

cli / web-ui / api / interface  →  5 (entry)
app / application               →  4
core / domain                   →  3
exporters / adapters            →  2
utils / shared                  →  1 (foundation)

A violation fires when a layer with rank < N imports from a layer with rank > N.

Every file receives a composite riskScore:

score = incoming×2 + outgoing
      + 10 (if in a cycle)
      + 8  (if god module)
      + 6  (if dependency explosion)
      + 4× (layer violations count)

archmap.exporters

Exporter Output
json_exporter.py Structured JSON (see docs/api.md)
mermaid_exporter.py Mermaid graph TD diagram
cytoscape_exporter.py Cytoscape.js elements format

archmap.cli

main.py is the CLI entry point (registered as archmap and code-arch scripts).

Commands: - analyze — parse + export, print summary - serve — analyze + start an HTTP server serving the Web UI and /api/graph - diff — analyze two git refs, print delta metrics - version — print version

Static file resolution order for serve: 1. PyInstaller _MEIPASS bundle 2. importlib.resources.files("archmap") / "web-ui" / "static" (installed wheel) 3. src/archmap/../../../web-ui/static (source checkout)


archmap.utils

  • file_utils.py — filesystem helpers: discover_source_files, normalize_file_id, to_file_id, extension sets, first_segment, percentile

Data flow types (simplified)

ParsedProject = {
    "projectRoot": str,
    "parsedFiles": list[ParsedFile],
}

ParsedFile = {
    "id": str,           # relative posix path e.g. "src/archmap/cli/main.py"
    "label": str,
    "type": "file",
    "language": str,
    "dependencies": list[Dependency],
}

Dependency = {
    "id": str,           # e.g. "src/archmap/core/__init__.py" or "pkg:requests"
    "label": str,
    "type": "file" | "package",
}