1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright (c) Meta Platforms, Inc. and affiliates.
//! Convert Next.js Turbopack bundle analysis data into Unigraph `MapGraph` JSON.
//!
//! # Background
//!
//! Next.js ships a command `next experimental-analyze -o` that runs a production
//! Turbopack build and writes binary analysis data to `.next/diagnostics/analyze/data/`.
//! This data contains the full module dependency graph plus per-route size attribution.
//!
//! This crate reads that data and produces a Unigraph `MapGraph` — a JSON format
//! that Unigraph Explorer can visualize with dominator trees, tiered metrics,
//! force-directed layout, and more.
//!
//! # Data sources
//!
//! The analyze output directory contains three kinds of files:
//!
//! - **`modules.data`** — Global module dependency graph. Contains every module
//! across all routes and RSC layers, plus sync and async dependency edges.
//! This is ONE graph — edges do not vary per route.
//!
//! - **`routes.json`** — JSON array of route path strings (e.g. `["/", "/about"]`).
//!
//! - **Per-route `analyze.data`** — Size attribution data. For each route, which
//! source files contribute how many bytes to the route's output chunks. Contains
//! NO dependency edges — only sizes.
//!
//! See `ANALYZE_DATA_FORMAT.md` in this crate for the full binary format specification.
//!
//! # Key design decisions
//!
//! - **Layers always separate**: The same file compiled in different RSC layers
//! (`app-rsc`, `app-client`, `app-ssr`) has genuinely different dependency edges
//! and produces separate nodes. A `layer` label enables UI filtering.
//!
//! - **Single graph, routes as labels**: Since edges are global, we produce one
//! graph with `route` labels on each node indicating which routes include it.
//! Sizes are summed across routes.
//!
//! - **Fragments collapsed by default**: Tree-shaking fragments (`<exports>`,
//! `<module evaluation>`) are merged back into their parent module unless
//! `--fragments` is passed. Collapsing unions edges, sums sizes, and removes
//! self-edges that arise from inter-fragment references.
//!
//! - **Tiered traversal**: Sync imports map to the "eager" tier, async imports
//! (`import()`) map to the "lazy" tier. This gives Unigraph's tiered metrics:
//! `size#eager` (initial load), `size#lazy` (total), `size#eager~dominated`
//! (unique cost per module in the initial bundle).
//!
//! # Usage
//!
//! ```bash
//! # Generate analyze data
//! cd your-next-app && ./node_modules/.bin/next experimental-analyze -o
//!
//! # Convert to Unigraph (via task runner)
//! ut unigraph_turbopack .next/diagnostics/analyze/data/ -o graph.json --pretty
//!
//! # Visualize
//! ut serve -f graph.json
//! ```
use Path;
use Result;
use MapGraph;
/// Controls how the Turbopack data is mapped to Unigraph nodes.
/// Read Turbopack analyze data from `data_dir` and produce a Unigraph `MapGraph`.
///
/// `data_dir` should point to `.next/diagnostics/analyze/data/` — the directory
/// containing `modules.data`, `routes.json`, and per-route `analyze.data` files.
///
/// The returned `MapGraph` can be serialized to JSON with `serde_json` and
/// loaded into Unigraph Explorer via `ut serve -f graph.json`.