dependency-radar 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ Dependency Radar is a local-first CLI tool that inspects a Node.js project’s i
5
5
  ## What it does
6
6
 
7
7
  - Analyses installed dependencies using only local data (no SaaS, no uploads by default)
8
- - Combines multiple tools (npm audit, npm ls, license-checker, depcheck, madge) into a single report
8
+ - Combines multiple tools (npm audit, npm ls, import graph analysis) into a single report
9
9
  - Shows direct vs sub-dependencies, dependency depth, and parent relationships
10
10
  - Highlights licences, known vulnerabilities, install-time scripts, native modules, and package footprint
11
11
  - Produces a single self-contained HTML file you can share or archive
@@ -17,6 +17,18 @@ Dependency Radar is a local-first CLI tool that inspects a Node.js project’s i
17
17
  - Not a bundler or build tool
18
18
  - Not a dependency updater
19
19
 
20
+
21
+ ## License Scanning
22
+
23
+ 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:
24
+
25
+ - Declared SPDX validation (including deprecated IDs and `WITH` exceptions)
26
+ - Inferred SPDX license (with confidence: `high`, `medium`, `low`) based on deterministic text matching
27
+ - A status (`declared-only`, `inferred-only`, `match`, `mismatch`, `invalid-spdx`, `unknown`) to make review decisions easier
28
+
29
+ This logic applies to all dependencies (direct and transitive). Inferred licenses are never treated as authoritative over valid declared SPDX expressions.
30
+
31
+
20
32
  ## Setup
21
33
 
22
34
  ```bash
@@ -26,7 +38,7 @@ npm run build
26
38
 
27
39
  ## Requirements
28
40
 
29
- - Node.js 18+ (required by `madge`, one of the analyzers)
41
+ - Node.js 14.14+
30
42
 
31
43
  ## Usage
32
44
 
@@ -35,25 +47,46 @@ The simplest way to run Dependency Radar is via npx. It runs in the current dire
35
47
  Run a scan against the current project (writes `dependency-radar.html`):
36
48
 
37
49
  ```bash
38
- npx dependency-radar scan
50
+ npx dependency-radar
39
51
  ```
40
52
 
53
+ The `scan` command is the default and can also be run explicitly as `npx dependency-radar scan`.
54
+
55
+
41
56
  Specify a project and output path:
42
57
 
43
58
  ```bash
44
- npx dependency-radar scan --project ./my-app --out ./reports/dependency-radar.html
59
+ npx dependency-radar --project ./my-app --out ./reports/dependency-radar.html
45
60
  ```
46
61
 
47
62
  Keep the temporary `.dependency-radar` folder for debugging raw tool outputs:
48
63
 
49
64
  ```bash
50
- npx dependency-radar scan --keep-temp
65
+ npx dependency-radar --keep-temp
66
+ ```
67
+
68
+ Skip `npm audit` and `npm outdated` (useful for offline scans):
69
+
70
+ ```bash
71
+ npx dependency-radar --offline
72
+ ```
73
+
74
+ Output JSON instead of HTML report:
75
+
76
+ ```bash
77
+ npx dependency-radar --json
78
+ ```
79
+
80
+ Open the generated report using the system default:
81
+
82
+ ```bash
83
+ npx dependency-radar --open
51
84
  ```
52
85
 
53
- Skip `npm audit` (useful for offline scans):
86
+ Show options:
54
87
 
55
88
  ```bash
56
- npx dependency-radar scan --no-audit
89
+ npx dependency-radar --help
57
90
  ```
58
91
 
59
92
  ## Scripts
@@ -66,13 +99,211 @@ npx dependency-radar scan --no-audit
66
99
 
67
100
  - The target project must have node_modules installed (run npm install first).
68
101
  - The scan is local-first and does not upload your code or dependencies anywhere.
69
- - `npm audit` performs registry lookups; use `--no-audit` for offline-only scans.
102
+ - `npm audit` and `npm outdated` perform registry lookups; use `--offline` for offline-only scans.
70
103
  - A temporary `.dependency-radar` folder is created during the scan to store intermediate tool output.
71
104
  - Use `--keep-temp` to retain this folder for debugging; otherwise it is deleted automatically.
72
105
  - If a tool fails, its section is marked as unavailable, but the report is still generated.
73
- - Node.js 18+ is required because `madge` (one of the analyzers) targets Node 18+.
74
106
 
75
107
  ## Output
76
108
 
77
109
  Dependency Radar writes a single HTML file (dependency-radar.html by default).
78
110
  The file is fully self-contained and can be opened locally in a browser, shared with others, or attached to tickets and documentation.
111
+
112
+ ### JSON output
113
+
114
+ Use `--json` to write the aggregated scan data as JSON (defaults to `dependency-radar.json`).
115
+
116
+ The JSON schema matches the `AggregatedData` TypeScript interface in `src/types.ts`. For quick reference:
117
+
118
+ ```ts
119
+ export interface AggregatedData {
120
+ schemaVersion: '1.2'; // Report schema version for compatibility checks
121
+ generatedAt: string; // ISO timestamp when the scan finished
122
+ dependencyRadarVersion: string; // CLI version that produced the report
123
+ git: {
124
+ branch: string; // Git branch name, empty when unavailable/detached
125
+ };
126
+ project: {
127
+ projectDir: string; // Project path relative to the user's home directory (e.g. /Developer/app)
128
+ };
129
+ environment: {
130
+ nodeVersion: string; // Node.js version from process.versions.node
131
+ runtimeVersion: string; // Node.js runtime version from process.version
132
+ minRequiredMajor: number; // Strictest Node major required by dependency engines (0 if unknown)
133
+ platform?: string; // OS platform (process.platform)
134
+ arch?: string; // CPU architecture (process.arch)
135
+ ci?: boolean; // True when running in CI (process.env.CI === 'true')
136
+ packageManagerField?: string; // package.json packageManager field (e.g. pnpm@9.1.0)
137
+ packageManager?: 'npm' | 'pnpm' | 'yarn'; // Package manager used to scan
138
+ packageManagerVersion?: string; // Version of the package manager used to scan
139
+ toolVersions?: {
140
+ npm?: string;
141
+ pnpm?: string;
142
+ yarn?: string;
143
+ };
144
+ };
145
+ workspaces: {
146
+ enabled: boolean; // True when the scan used workspace aggregation
147
+ type?: 'npm' | 'pnpm' | 'yarn' | 'none'; // Workspace type if detected
148
+ packageCount?: number; // Number of workspace packages scanned
149
+ };
150
+ summary: {
151
+ dependencyCount: number; // Total dependencies in the graph
152
+ directCount: number; // Dependencies listed in package.json
153
+ transitiveCount: number; // Dependencies pulled in by other dependencies
154
+ };
155
+ dependencies: Record<string, DependencyRecord>; // Keyed by name@version
156
+ }
157
+
158
+ export interface DependencyRecord {
159
+ package: {
160
+ id: string; // Stable identifier in the form name@version
161
+ name: string; // Package name from npm metadata
162
+ version: string; // Installed version from npm ls
163
+ description?: string; // Description from the installed package.json (if present)
164
+ deprecated: boolean; // True if the package.json has a deprecated flag
165
+ links: {
166
+ npm: string; // npm package page URL
167
+ repository?: string; // Repository URL (if present)
168
+ homepage?: string; // Homepage URL (if present)
169
+ bugs?: string; // Issue tracker URL (if present)
170
+ };
171
+ };
172
+ compliance: {
173
+ license: {
174
+ declared?: {
175
+ spdxId: string; // SPDX ID or expression from package.json
176
+ expression: boolean; // True when SPDX expression (AND/OR/WITH)
177
+ deprecated: boolean; // True if SPDX ID is deprecated
178
+ valid: boolean; // True if SPDX ID/expression is valid
179
+ };
180
+ inferred?: {
181
+ spdxId: string; // SPDX ID inferred from LICENSE text
182
+ confidence: 'high' | 'medium' | 'low'; // Heuristic confidence
183
+ };
184
+ exception?: {
185
+ id: string; // SPDX exception id
186
+ deprecated: boolean; // True if exception is deprecated
187
+ valid: boolean; // True if exception id is valid
188
+ };
189
+ status:
190
+ | 'declared-only'
191
+ | 'inferred-only'
192
+ | 'match'
193
+ | 'mismatch'
194
+ | 'invalid-spdx'
195
+ | 'unknown';
196
+ };
197
+ licenseRisk: 'green' | 'amber' | 'red'; // Risk classification derived from declared/inferred SPDX ids
198
+ };
199
+ security: {
200
+ summary: {
201
+ critical: number; // npm audit counts for critical issues
202
+ high: number; // npm audit counts for high issues
203
+ moderate: number; // npm audit counts for moderate issues
204
+ low: number; // npm audit counts for low issues
205
+ highest: 'low' | 'moderate' | 'high' | 'critical' | 'none'; // Highest severity present
206
+ risk: 'green' | 'amber' | 'red'; // Risk classification derived from audit counts
207
+ };
208
+ advisories?: Array<{
209
+ id: string; // GHSA identifier
210
+ title: string; // Human-readable advisory title
211
+ severity: 'low' | 'moderate' | 'high' | 'critical';
212
+ vulnerableRange: string; // Semver range
213
+ fixAvailable: boolean; // True if npm audit indicates a fix exists
214
+ url: string; // Advisory URL
215
+ }>;
216
+ };
217
+ upgrade: {
218
+ nodeEngine: string | null; // engines.node from the package.json (if present)
219
+ outdatedStatus?: 'current' | 'patch' | 'minor' | 'major' | 'unknown'; // Derived from npm outdated (if present)
220
+ latestVersion?: string; // npm latest version (present only when status is not current)
221
+ blockers?: Array<'nodeEngine' | 'peerDependency' | 'nativeBindings' | 'deprecated'>; // Reasons for upgrade friction
222
+ blocksNodeMajor?: boolean; // True if local signals indicate a node major bump is risky
223
+ };
224
+ usage: {
225
+ direct: boolean; // True if declared in package.json (dependencies/devDependencies/etc.)
226
+ scope: 'runtime' | 'dev' | 'optional' | 'peer'; // Scope inferred from the declaring root package(s)
227
+ depth: number; // Minimum dependency tree depth observed in npm ls
228
+ origins: {
229
+ rootPackageCount: number; // Number of direct roots that introduce this dependency
230
+ topRootPackages: Array<{ name: string; version: string }>; // Up to 10 root packages (name/version)
231
+ parentPackageCount: number; // Number of direct parents
232
+ topParentPackages: string[]; // Up to 5 direct parent ids (name@version)
233
+ workspaces?: string[]; // Workspace packages that declare/use this dependency
234
+ };
235
+ introduction?: 'direct' | 'tooling' | 'framework' | 'testing' | 'transitive' | 'unknown'; // Heuristic for why the dependency exists
236
+ runtimeImpact?: 'runtime' | 'build' | 'testing' | 'tooling' | 'mixed'; // Heuristic based on import locations
237
+ importUsage?: {
238
+ fileCount: number; // Number of project files importing this package (import graph)
239
+ topFiles: string[]; // Top import locations (bounded to 5)
240
+ };
241
+ tsTypes: 'bundled' | 'definitelyTyped' | 'none' | 'unknown'; // TypeScript type availability
242
+ };
243
+ graph: {
244
+ fanIn: number; // Number of packages that depend on this package
245
+ fanOut: number; // Number of packages this package depends on
246
+ subDeps?: {
247
+ // Declared outgoing dependency edges; values are tuples.
248
+ // tuple[0] = declared version range, tuple[1] = resolved dependency id or null if not installed.
249
+ // Only installed dependencies have full dependency records in the top-level list.
250
+ dep?: Record<string, [string, string | null]>; // Declared runtime deps
251
+ dev?: Record<string, [string, string | null]>; // Declared dev deps
252
+ peer?: Record<string, [string, string | null]>; // Declared peer deps
253
+ opt?: Record<string, [string, string | null]>; // Declared optional deps
254
+ };
255
+ };
256
+ execution?: {
257
+ risk: 'amber' | 'red'; // Install-time risk (green implied when absent)
258
+ native?: true; // True if native bindings or build tooling are detected
259
+ scripts?: {
260
+ hooks: Array<'preinstall' | 'install' | 'postinstall' | 'prepare'>; // Lifecycle hooks detected
261
+ complexity?: number; // Heuristic complexity (stored only when high)
262
+ signals?: Array<
263
+ | 'network-access'
264
+ | 'dynamic-exec'
265
+ | 'child-process'
266
+ | 'encoding'
267
+ | 'obfuscated'
268
+ | 'reads-env'
269
+ | 'reads-home'
270
+ | 'uses-ssh'
271
+ >; // Review-worthy install-time signals (sparse)
272
+ };
273
+ };
274
+ }
275
+ ```
276
+
277
+ For full details and any future changes, see `src/types.ts`.
278
+
279
+ Environment data includes Node.js version, OS platform, CPU architecture, and package manager versions.
280
+ No personal information, usernames, paths, or environment variables are collected.
281
+
282
+ ## Development
283
+
284
+ ### Report UI Development
285
+
286
+ 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.
287
+
288
+ **Start the development server:**
289
+
290
+ ```bash
291
+ npm run dev:report
292
+ ```
293
+
294
+ This opens the report UI in your browser with sample data covering all dependency states (various licenses, vulnerability severities, usage statuses, etc.).
295
+
296
+ **Build workflow:**
297
+
298
+ 1. Make changes in `report-ui/` (edit `style.css`, `main.ts`, `index.html`)
299
+ 2. Run `npm run build:report` to compile and inject assets into `src/report-assets.ts`
300
+ 3. Run `npm run build` to compile the full project (this runs `build:report` automatically)
301
+
302
+ **File structure:**
303
+
304
+ - `report-ui/index.html` – HTML template structure
305
+ - `report-ui/style.css` – All CSS styles
306
+ - `report-ui/main.ts` – TypeScript rendering logic
307
+ - `report-ui/sample-data.json` – Sample data for development
308
+ - `report-ui/types.ts` – Client-side TypeScript types
309
+ - `src/report-assets.ts` – Auto-generated file with bundled CSS/JS (do not edit directly)