dependency-radar
v0.3.1
Published
Local-first dependency analysis tool that generates a single HTML report showing risk, size, usage, and structure of your project's dependencies.
Maintainers
Readme
Dependency Radar
Dependency Radar is a CLI tool that inspects a Node.js project’s installed dependencies and generates a single, human-readable HTML report. The report highlights dependency structure, usage, licences, vulnerabilities, and other signals that help you understand risk and complexity hidden in your node_modules folder.
The simplest way to get started is:
npx dependency-radarThis runs a scan against the current project and writes a self-contained dependency-radar.html report you can open locally, share with teammates, or attach to tickets and documentation.
What it does
- Analyses installed dependencies by running standard package manager tooling (npm, pnpm, or yarn)
- Combines multiple signals (audit results, dependency graph data, import usage, and heuristics) into a single report
- Shows direct vs transitive dependencies, dependency depth, and parent relationships
- Highlights licences, known vulnerabilities, install-time scripts, native modules, and package footprint
- Produces a single self-contained HTML file with no external assets, which you can easily share
What it is not
- Not a CI service or hosted scanning platform
- Not a replacement for dedicated security scanners
- Not a bundler or build tool
- Not a dependency updater
For teams that want deeper analysis, long-term tracking, and additional enrichment (such as ecosystem and maintenance signals), Dependency Radar also offers an optional premium service.
See https://dependency-radar.com for details.
License Scanning
Dependency Radar validates SPDX licenses declared in package.json and can infer licenses from LICENSE files when declarations are missing or invalid. It works offline and uses a bundled SPDX identifier list (generated at build time) with no runtime network access. Each dependency gets a structured license record with:
- Declared SPDX validation (including deprecated IDs and
WITHexceptions) - Inferred SPDX license (with confidence:
high,medium,low) based on deterministic text matching - A status (
declared-only,inferred-only,match,mismatch,invalid-spdx,unknown) to make review decisions easier
This logic applies to all dependencies (direct and transitive). Inferred licenses are never treated as authoritative over valid declared SPDX expressions.
Setup
npm install
npm run buildRequirements
- Node.js 14.14+
Usage
The simplest way to run Dependency Radar is via npx. It runs in the current directory and writes an HTML report to disk.
Run a scan against the current project (writes dependency-radar.html):
npx dependency-radarThe scan command is the default and can also be run explicitly as npx dependency-radar scan.
Specify a project and output path:
npx dependency-radar --project ./my-app --out ./reports/dependency-radar.htmlKeep the temporary .dependency-radar folder for debugging raw tool outputs:
npx dependency-radar --keep-tempSkip npm audit and npm outdated (useful for offline scans):
npx dependency-radar --offlineOutput JSON instead of HTML report:
npx dependency-radar --jsonOpen the generated report using the system default:
npx dependency-radar --openShow options:
npx dependency-radar --helpScripts
npm run build– generate SPDX/report assets and compile TypeScript todist/npm run dev– run a scan from source (ts-node src/cli.ts scan)npm run scan– run a scan from the built output (node dist/cli.js scan)npm run dev:report– run the report UI dev servernpm run build:spdx– rebuild bundled SPDX identifiersnpm run build:report-ui– build report UI assetsnpm run build:report– rebuild report assets used by the CLI
Fixture scripts:
npm run fixtures:install– install core fixture dependenciesnpm run fixtures:install:all– install all fixture dependenciesnpm run fixtures:scan– scan the core fixture setnpm run fixtures:install:npmnpm run fixtures:install:npm-heavynpm run fixtures:install:pnpmnpm run fixtures:install:pnpm-hoistednpm run fixtures:install:yarnnpm run fixtures:install:yarn-berrynpm run fixtures:install:optionalnpm run fixtures:scan:npmnpm run fixtures:scan:npm-heavynpm run fixtures:scan:pnpmnpm run fixtures:scan:pnpm-hoistednpm run fixtures:scan:yarnnpm run fixtures:scan:yarn-berrynpm run fixtures:scan:optionalnpm run fixtures:scan:no-node-modules
Notes
- The target project must have dependencies installed (run
npm install,pnpm install, oryarn installfirst). - The scan runs on your machine and does not upload your code or dependencies anywhere.
npm audit/pnpm audit/yarn npm auditandnpm outdated/pnpm outdatedperform registry lookups; use--offlinefor offline-only scans.- A temporary
.dependency-radarfolder is created during the scan to store intermediate tool output. - Use
--keep-tempto retain this folder for debugging; otherwise it is deleted automatically. - If some per-package tools fail (common in large workspaces), the scan continues and reports warnings; missing sections are marked unavailable where applicable.
Output
Dependency Radar writes a single HTML file (dependency-radar.html by default).
The file is fully self-contained and can be opened locally in a browser, shared with others, or attached to tickets and documentation.
JSON output
Use --json to write the aggregated scan data as JSON (defaults to dependency-radar.json).
The JSON schema matches the AggregatedData TypeScript interface in src/types.ts. For quick reference:
export interface AggregatedData {
schemaVersion: '1.2'; // Report schema version for compatibility checks
generatedAt: string; // ISO timestamp when the scan finished
dependencyRadarVersion: string; // CLI version that produced the report
git: {
branch: string; // Git branch name, empty when unavailable/detached
};
project: {
projectDir: string; // Project path relative to the user's home directory (e.g. /Developer/app)
};
environment: {
nodeVersion: string; // Node.js version from process.versions.node
runtimeVersion: string; // Node.js runtime version from process.version
minRequiredMajor: number; // Strictest Node major required by dependency engines (0 if unknown)
platform?: string; // OS platform (process.platform)
arch?: string; // CPU architecture (process.arch)
ci?: boolean; // True when running in CI (process.env.CI === 'true')
packageManagerField?: string; // package.json packageManager field (e.g. [email protected])
packageManager?: 'npm' | 'pnpm' | 'yarn'; // Package manager used to scan
packageManagerVersion?: string; // Version of the package manager used to scan
toolVersions?: {
npm?: string;
pnpm?: string;
yarn?: string;
};
};
workspaces: {
enabled: boolean; // True when the scan used workspace aggregation
type?: 'npm' | 'pnpm' | 'yarn' | 'none'; // Workspace type if detected
packageCount?: number; // Number of workspace packages scanned
};
summary: {
dependencyCount: number; // Total dependencies in the graph
directCount: number; // Dependencies listed in package.json
transitiveCount: number; // Dependencies pulled in by other dependencies
};
dependencies: Record<string, DependencyRecord>; // Keyed by name@version
}
export interface DependencyRecord {
package: {
id: string; // Stable identifier in the form name@version
name: string; // Package name from npm metadata
version: string; // Installed version from npm ls
description?: string; // Description from the installed package.json (if present)
deprecated: boolean; // True if the package.json has a deprecated flag
links: {
npm: string; // npm package page URL
repository?: string; // Repository URL (if present)
homepage?: string; // Homepage URL (if present)
bugs?: string; // Issue tracker URL (if present)
};
};
compliance: {
license: {
declared?: {
spdxId: string; // SPDX ID or expression from package.json
expression: boolean; // True when SPDX expression (AND/OR/WITH)
deprecated: boolean; // True if SPDX ID is deprecated
valid: boolean; // True if SPDX ID/expression is valid
};
inferred?: {
spdxId: string; // SPDX ID inferred from LICENSE text
confidence: 'high' | 'medium' | 'low'; // Heuristic confidence
};
exception?: {
id: string; // SPDX exception id
deprecated: boolean; // True if exception is deprecated
valid: boolean; // True if exception id is valid
};
status:
| 'declared-only'
| 'inferred-only'
| 'match'
| 'mismatch'
| 'invalid-spdx'
| 'unknown';
};
licenseRisk: 'green' | 'amber' | 'red'; // Risk classification derived from declared/inferred SPDX ids
};
security: {
summary: {
critical: number; // npm audit counts for critical issues
high: number; // npm audit counts for high issues
moderate: number; // npm audit counts for moderate issues
low: number; // npm audit counts for low issues
highest: 'low' | 'moderate' | 'high' | 'critical' | 'none'; // Highest severity present
risk: 'green' | 'amber' | 'red'; // Risk classification derived from audit counts
};
advisories?: Array<{
id: string; // GHSA identifier
title: string; // Human-readable advisory title
severity: 'low' | 'moderate' | 'high' | 'critical';
vulnerableRange: string; // Semver range
fixAvailable: boolean; // True if npm audit indicates a fix exists
url: string; // Advisory URL
}>;
};
upgrade: {
nodeEngine: string | null; // engines.node from the package.json (if present)
outdatedStatus?: 'current' | 'patch' | 'minor' | 'major' | 'unknown'; // Derived from npm outdated (if present)
latestVersion?: string; // npm latest version (present only when status is not current)
blockers?: Array<'nodeEngine' | 'peerDependency' | 'nativeBindings' | 'deprecated'>; // Reasons for upgrade friction
blocksNodeMajor?: boolean; // True if local signals indicate a node major bump is risky
};
usage: {
direct: boolean; // True if declared in package.json (dependencies/devDependencies/etc.)
scope: 'runtime' | 'dev' | 'optional' | 'peer'; // Scope inferred from the declaring root package(s)
depth: number; // Minimum dependency tree depth observed in npm ls
origins: {
rootPackageCount: number; // Number of direct roots that introduce this dependency
topRootPackages: Array<{ name: string; version: string }>; // Up to 10 root packages (name/version)
parentPackageCount: number; // Number of direct parents
topParentPackages: string[]; // Up to 5 direct parent ids (name@version)
workspaces?: string[]; // Workspace packages that declare/use this dependency
};
introduction?: 'direct' | 'tooling' | 'framework' | 'testing' | 'transitive' | 'unknown'; // Heuristic for why the dependency exists
runtimeImpact?: 'runtime' | 'build' | 'testing' | 'tooling' | 'mixed'; // Heuristic based on import locations
importUsage?: {
fileCount: number; // Number of project files importing this package (import graph)
topFiles: string[]; // Top import locations (bounded to 5)
};
tsTypes: 'bundled' | 'definitelyTyped' | 'none' | 'unknown'; // TypeScript type availability
};
graph: {
fanIn: number; // Number of packages that depend on this package
fanOut: number; // Number of packages this package depends on
subDeps?: {
// Declared outgoing dependency edges; values are tuples.
// tuple[0] = declared version range, tuple[1] = resolved dependency id or null if not installed.
// Only installed dependencies have full dependency records in the top-level list.
dep?: Record<string, [string, string | null]>; // Declared runtime deps
dev?: Record<string, [string, string | null]>; // Declared dev deps
peer?: Record<string, [string, string | null]>; // Declared peer deps
opt?: Record<string, [string, string | null]>; // Declared optional deps
};
};
execution?: {
risk: 'amber' | 'red'; // Install-time risk (green implied when absent)
native?: true; // True if native bindings or build tooling are detected
scripts?: {
hooks: Array<'preinstall' | 'install' | 'postinstall' | 'prepare'>; // Lifecycle hooks detected
complexity?: number; // Heuristic complexity (stored only when high)
signals?: Array<
| 'network-access'
| 'dynamic-exec'
| 'child-process'
| 'encoding'
| 'obfuscated'
| 'reads-env'
| 'reads-home'
| 'uses-ssh'
>; // Review-worthy install-time signals (sparse)
};
};
}For full details and any future changes, see src/types.ts.
Environment data includes Node.js version, OS platform, CPU architecture, and package manager versions. No personal information, usernames, paths, or environment variables are collected.
Development
Report UI Development
The HTML report UI is developed in a separate Vite project located in report-ui/. This provides a proper development environment with hot reload, TypeScript support, and sample data.
Start the development server:
npm run dev:reportThis opens the report UI in your browser with sample data covering all dependency states (various licenses, vulnerability severities, usage statuses, etc.).
Build workflow:
- Make changes in
report-ui/(editstyle.css,main.ts,index.html) - Run
npm run build:reportto compile and inject assets intosrc/report-assets.ts - Run
npm run buildto compile the full project (this runsbuild:reportautomatically)
File structure:
report-ui/index.html– HTML template structurereport-ui/style.css– All CSS stylesreport-ui/main.ts– TypeScript rendering logicreport-ui/sample-data.json– Sample data for developmentreport-ui/types.ts– Client-side TypeScript typessrc/report-assets.ts– Auto-generated file with bundled CSS/JS (do not edit directly)
