Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ You can see a [Dependency Radar example report](https://www.dependency-radar.com
*Expanded dependency view: scan the key risk signals first, then drill into status, scope, origins, install behaviour, licence, vulnerabilities, and upgrade blockers.*

![Dependency Radar – interactive dependency graph view](./docs/screenshot-02.jpg)
*Graph view: explore the full dependency tree visually, with direct, dev, and transitive relationships at a glance.*
*Graph view: explore the full dependency tree visually, with a docked side panel for search and per-package details — no tooltips covering the graph.*

![Dependency Radar – flame view](./docs/screenshot-04.jpg)
*Flame view: a profiler-style icicle of your dependency tree. Bar width is the share of the tree beneath it, so the heaviest direct dependencies are obvious at a glance.*

![Dependency Radar – balloon view](./docs/screenshot-05.jpg)
*Balloon view: every direct dependency is a system orbiting your project, its sub-dependencies fanning out behind it, coloured by lineage.*

---

Expand Down Expand Up @@ -73,7 +79,7 @@ Security issues should be reported privately; see [SECURITY.md](./SECURITY.md).

- **Vulnerability scanning** — runs `npm audit` / `pnpm audit` / `yarn audit` and surfaces advisories with severity, fix availability, installed-version matching, and direct static-import evidence
- **License analysis** — validates SPDX declarations, infers licences from `LICENSE` files, and flags mismatches, unknown licences, and strong copyleft
- **Interactive dependency graph** — explore your full dependency tree visually, including direct, dev, and transitive relationships
- **Four interactive graph layouts** — the classic dependency graph plus flame (profiler-style icicle), balloon (orbital constellation), and hyperbolic (Poincaré-disk focus+context) views, switchable from the graph toolbar, with a shared docked side panel and package search, plus a hover path trail in the flame, balloon, and hyperbolic views
- **Upgrade friction analysis** — identifies upgrade blockers: peer constraints, engine ranges, native bindings, install scripts, deprecated packages
- **Maintenance signals** — flags deprecated, repo-archived, unmaintained, stale, and slowing dependencies from npm registry metadata (plus repository push activity when available), with a local 7-day cache
- **Replacement suggestions** — matches your dependencies against the community-maintained [e18e](https://e18e.dev) [module-replacements](https://github.com/es-tooling/module-replacements) catalogue and suggests native or lighter alternatives, fully offline
Expand Down Expand Up @@ -196,6 +202,24 @@ It is recomputed after registry enrichment so a registry-discovered deprecation

---

## Graph views

The report's Graph View offers four switchable layouts (toolbar buttons), all sharing the same docked side panel and package search:

- **Graph** — the classic layered dependency graph: pan, zoom, and click a node to focus its ancestors and descendants. Focusing zooms to fit the highlighted subtree.
- **Flame** — a profiler-style icicle plot. One row per depth; bar width is the package's share of the whole tree beneath it (path-expanded, like stacks in a flame graph), children heaviest-first. Click a bar to zoom in — every ancestor stays pinned above you; double-click to reset.
- **Balloon** — an orbital constellation: the project at the centre, direct dependencies orbiting it, each one's sub-dependencies fanning out behind it recursively. Drag to pan, scroll to zoom, click a body to fly to it.
- **Hyperbolic** — a Poincaré-disk focus+context view: the whole tree in one finite circle, sub-dependencies compressing toward the rim. Dragging warps the space (whatever you pull toward the centre grows); scrolling magnifies the disk; clicking a package brings it to the centre.

Shared behaviour:

- **Side panel** — selecting a package in any layout shows its dossier: version, kind, licence, vulnerabilities, subtree size, "appears in N places", and clickable *depends-on* / *required-by* chips that refocus the current view. Search results fly to the package in whichever layout you're using.
- **Status line** — in the flame, balloon, and hyperbolic views, hovering shows the full path trail (`project › a › b › c`) along the bottom of the canvas instead of a tooltip covering the visualization.
- **Colours are lineage** — in Flame and Balloon, each direct dependency's entire subtree keeps one hue, so you can trace which root pulled a package in; red always marks vulnerable packages, and dev-only dependencies render dimmer. The toolbar key explains each layout's colours.
- **Workspaces** — all layouts respect the workspace selector, scoping the tree to that workspace's direct dependencies.

---

## Usage

Run a scan against the current project (writes `dependency-radar.html`):
Expand Down
23 changes: 17 additions & 6 deletions dist/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,7 @@ function parseArgs(argv) {
commandProvided: false,
project: process.cwd(),
quiet: false,
offline: false,
out: "dependency-radar.html",
keepTemp: false,
audit: true,
Expand Down Expand Up @@ -1020,6 +1021,7 @@ function parseArgs(argv) {
else if (arg === "--keep-temp")
opts.keepTemp = true;
else if (arg === "--offline") {
opts.offline = true;
opts.audit = false;
opts.outdated = false;
opts.maintenance = false;
Expand Down Expand Up @@ -1969,16 +1971,25 @@ async function executeAnalysis(opts, options) {
supplyChain: supplyChainCollectorStatus,
};
const scanWarnings = [];
const addCollectorWarning = (collector, label, consequence) => {
const addCollectorWarning = (collector, label, consequence, skippedBecause) => {
const status = collectors[collector];
if (status !== "available")
scanWarnings.push(`${label} is ${status}; ${consequence}`);
if (status === "available")
return;
const cause = status === "skipped" && skippedBecause ? ` (${skippedBecause})` : "";
scanWarnings.push(`${label} is ${status}${cause}; ${consequence}`);
};
// Name the flag responsible for a deliberate skip so the report can say
// why, not just what.
const maintenanceSkipReason = opts.offline
? "scan ran with --offline"
: !opts.maintenance
? "scan ran with --no-maintenance"
: undefined;
addCollectorWarning("dependencyTree", "Dependency tree collection", "dependency coverage and classification may be incomplete.");
addCollectorWarning("audit", "Vulnerability audit", "vulnerability status is unknown.");
addCollectorWarning("audit", "Vulnerability audit", "vulnerability status is unknown.", opts.offline ? "scan ran with --offline" : undefined);
addCollectorWarning("imports", "Static import collection", "direct-import evidence may be incomplete.");
addCollectorWarning("maintenance", "Maintenance collection", "maintenance status may be incomplete.");
addCollectorWarning("registryMetadata", "Targeted registry metadata", "registry risk signals may be incomplete.");
addCollectorWarning("maintenance", "Maintenance collection", "maintenance status may be incomplete.", maintenanceSkipReason);
addCollectorWarning("registryMetadata", "Targeted registry metadata", "registry risk signals may be incomplete.", opts.offline ? "scan ran with --offline" : undefined);
addCollectorWarning("supplyChain", "Lockfile supply-chain collection", "source and integrity signals may be incomplete.");
const scanStatus = {
complete: Object.values(collectors).every((status) => status !== "partial" && status !== "unavailable"),
Expand Down
4 changes: 2 additions & 2 deletions dist/report-assets.js

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions dist/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,19 @@ ${safeCssContent}
<div id="dependency-list" class="dependency-grid"></div>
</section>
<section class="view-panel" id="graph-view" data-view="graph" aria-hidden="true">
<div class="graph-canvas-shell" id="graph-canvas-shell">
<div class="graph-canvas-shell" id="graph-canvas-shell">
<canvas id="graph-canvas"></canvas>
<div class="graph-alt-host" id="graph-alt-host" hidden></div>
<div class="graph-status-line dim" id="graph-status-line"></div>
<div class="graph-overlay-top">
<button type="button" class="graph-back-btn" id="graph-back-btn">Back to List View</button>
<div class="graph-key" aria-label="Graph key">
<div class="graph-mode-switch" id="graph-mode-switch" role="group" aria-label="Graph layout">
<button type="button" class="graph-mode-btn active" data-graph-mode="graph" aria-pressed="true">Graph</button>
<button type="button" class="graph-mode-btn" data-graph-mode="flame" aria-pressed="false">Flame</button>
<button type="button" class="graph-mode-btn" data-graph-mode="balloon" aria-pressed="false">Balloon</button>
<button type="button" class="graph-mode-btn" data-graph-mode="hyperbolic" aria-pressed="false">Hyperbolic</button>
</div>
<div class="graph-key" id="graph-key" aria-label="Graph key">
<span class="graph-workspace-label">Key</span>
<div class="graph-key-items">
<span class="graph-key-item">
Expand Down Expand Up @@ -435,6 +443,13 @@ ${safeCssContent}
<div class="graph-popover-meta" id="graph-popover-amplification"></div>
<button type="button" class="graph-popover-action" id="graph-open-list">Open in List</button>
</div>
<aside class="graph-side-panel" id="graph-side-panel" aria-label="Selection details">
<div class="graph-side-search">
<input id="graph-search" type="search" placeholder="Find a package…" autocomplete="off" aria-label="Find a package" />
<ul id="graph-search-results" class="graph-search-results"></ul>
</div>
<div class="graph-dossier" id="graph-dossier"></div>
</aside>
</div>
</section>
</main>
Expand Down
Binary file modified docs/screenshot-01.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshot-02.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshot-03.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshot-04.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshot-05.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading