Skip to content

Commit 4c91c5b

Browse files
jayhackcursoragent
andauthored
Add interactive Next.js dependency-graph explorer (/explore) (#154)
## Summary Adds a single new page — **`/explore`** — on top of `develop`. It renders the Next.js framework core (`packages/next/src`) as a force-directed module dependency graph, parsed with the **graph-sitter Rust backend** (~4.9s, 1,680 files / 16k symbols). This is the focused, net-new slice of the earlier docs branch — the docs-site UI itself already lives on `develop`, so this PR only adds what's missing. ## What's included - `site/app/explore/page.tsx` — the showcase page (graph + top import hubs + the `uvx graph-sitter parse` recipe). - `site/components/visualizations/dependency-graph.tsx` — `d3-force` layout + hand-rolled HTML5 canvas renderer (Aura-themed, hover/drag/zoom, dark & light); no heavy charting deps. - `site/lib/data/nextjs-depgraph.json` — generated graph data (91 modules, 710 edges). - `site/scripts/gen-nextjs-depgraph.py` — reproducible generator (re-run to refresh the data). - `site/app/page.tsx` — adds an "Explore" link to the homepage nav. - `d3-force` added to `site/package.json`. ## Test plan - [x] `npm --prefix site run build` (Node 22) — clean, 305 static pages incl. `/explore`. - [ ] Review `/explore` in dark and light; confirm graph renders and hover/drag/zoom work. Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: jayhack <2548876+jayhack@users.noreply.github.com>
1 parent b17289f commit 4c91c5b

8 files changed

Lines changed: 9727 additions & 4504 deletions

File tree

site/app/explore/page.tsx

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import type { Metadata } from "next";
2+
import Link from "next/link";
3+
4+
import { Wordmark } from "@/components/logo";
5+
import { ThemeToggle } from "@/components/theme-toggle";
6+
import { Button } from "@/components/ui/button";
7+
import {
8+
type DepGraphData,
9+
DependencyGraph,
10+
} from "@/components/visualizations/dependency-graph";
11+
import depGraph from "@/lib/data/nextjs-depgraph.json";
12+
13+
const data = depGraph as DepGraphData;
14+
const githubUrl = "https://github.com/codegen-sh/graph-sitter";
15+
16+
export const metadata: Metadata = {
17+
title: "The shape of Next.js | Graph-sitter",
18+
description:
19+
"An interactive dependency graph of the Next.js framework core, parsed with graph-sitter.",
20+
};
21+
22+
const hubs = [...data.nodes].sort((a, b) => b.inbound - a.inbound).slice(0, 6);
23+
24+
export default function ExplorePage() {
25+
return (
26+
<div className="flex min-h-screen flex-col">
27+
<header className="sticky top-0 z-50 border-b border-border/60 bg-background/80 backdrop-blur-xl">
28+
<div className="mx-auto flex h-16 w-full max-w-6xl items-center justify-between px-6 lg:px-8">
29+
<Link href="/" aria-label="Graph-sitter home">
30+
<Wordmark />
31+
</Link>
32+
<nav className="flex items-center gap-1">
33+
<Button
34+
asChild
35+
variant="ghost"
36+
size="sm"
37+
className="text-muted-foreground hover:text-foreground"
38+
>
39+
<Link href="/docs">Docs</Link>
40+
</Button>
41+
<Button
42+
asChild
43+
variant="ghost"
44+
size="sm"
45+
className="hidden text-muted-foreground hover:text-foreground sm:inline-flex"
46+
>
47+
<a href={githubUrl} target="_blank" rel="noreferrer">
48+
GitHub
49+
</a>
50+
</Button>
51+
<ThemeToggle />
52+
</nav>
53+
</div>
54+
</header>
55+
56+
<main className="mx-auto w-full max-w-6xl flex-1 px-6 py-12 lg:px-8 lg:py-16">
57+
<div className="max-w-2xl">
58+
<h1 className="text-balance text-3xl font-semibold tracking-tight sm:text-4xl">
59+
The shape of Next.js
60+
</h1>
61+
<p className="mt-3 text-base leading-relaxed text-muted-foreground sm:text-lg">
62+
Every module in the Next.js framework core, wired by its real import
63+
graph. Graph-sitter parsed{" "}
64+
<code className="rounded-md border border-border bg-muted/60 px-1.5 py-0.5 font-mono text-[0.85em] text-foreground">
65+
packages/next/src
66+
</code>{" "}
67+
{data.meta.files.toLocaleString()} files,{" "}
68+
{data.meta.symbols.toLocaleString()} symbols — in{" "}
69+
{data.meta.parse_seconds}s.
70+
</p>
71+
</div>
72+
73+
<div className="mt-8">
74+
<DependencyGraph data={data} />
75+
</div>
76+
77+
<section className="mt-10 grid gap-8 lg:grid-cols-[1.1fr_0.9fr]">
78+
<div>
79+
<h2 className="text-lg font-semibold tracking-tight">
80+
Most-depended-on modules
81+
</h2>
82+
<p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">
83+
Ranked by inbound imports — the modules that the rest of the
84+
framework leans on hardest.
85+
</p>
86+
<ul className="mt-4 overflow-hidden rounded-xl border border-border">
87+
{hubs.map((n, i) => (
88+
<li
89+
key={n.id}
90+
className="flex items-center gap-3 border-b border-border bg-card px-4 py-2.5 text-sm last:border-b-0"
91+
>
92+
<span className="w-4 text-right font-mono text-xs text-muted-foreground">
93+
{i + 1}
94+
</span>
95+
<span className="font-mono text-foreground">{n.id}</span>
96+
<span className="ml-auto font-medium text-foreground">
97+
{n.inbound}
98+
</span>
99+
<span className="text-xs text-muted-foreground">imports</span>
100+
</li>
101+
))}
102+
</ul>
103+
</div>
104+
105+
<div>
106+
<h2 className="text-lg font-semibold tracking-tight">
107+
How this was built
108+
</h2>
109+
<p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">
110+
No language server, no IDE — just the graph-sitter Rust backend
111+
resolving imports across the whole tree.
112+
</p>
113+
<pre className="code-surface mt-4 overflow-x-auto px-4 py-3.5 font-mono text-[0.8rem] leading-relaxed">
114+
<code>
115+
<span className="block">
116+
<span className="text-aura-green">uvx</span> graph-sitter
117+
parse packages/next/src \
118+
</span>
119+
<span className="block pl-[2ch] text-muted-foreground">
120+
--backend rust --format json
121+
</span>
122+
</code>
123+
</pre>
124+
<p className="mt-4 text-sm leading-relaxed text-muted-foreground">
125+
The resulting graph — {data.meta.modules} modules and{" "}
126+
{data.meta.edges} aggregated import edges — is laid out live in
127+
your browser with a force simulation.
128+
</p>
129+
</div>
130+
</section>
131+
</main>
132+
133+
<footer className="border-t border-border/60">
134+
<div className="mx-auto flex w-full max-w-6xl items-center justify-between px-6 py-8 text-sm text-muted-foreground lg:px-8">
135+
<span>Codebase graphs for codemods.</span>
136+
<Link href="/docs" className="hover:text-foreground">
137+
Read the docs
138+
</Link>
139+
</div>
140+
</footer>
141+
</div>
142+
);
143+
}

site/app/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ export default function Home() {
111111
>
112112
<Link href={docsUrl}>Docs</Link>
113113
</Button>
114+
<Button
115+
asChild
116+
variant="ghost"
117+
size="sm"
118+
className="text-muted-foreground hover:text-foreground"
119+
>
120+
<Link href="/explore">Explore</Link>
121+
</Button>
114122
<Button
115123
asChild
116124
variant="ghost"

0 commit comments

Comments
 (0)