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 +240 -9
- package/dist/aggregator.js +1093 -344
- package/dist/cli.js +871 -61
- package/dist/generated/spdx.js +855 -0
- package/dist/license.js +324 -0
- package/dist/report-assets.js +18 -0
- package/dist/report.js +91 -1363
- package/dist/runners/importGraphRunner.js +178 -0
- package/dist/runners/npmAudit.js +81 -16
- package/dist/runners/npmLs.js +216 -15
- package/dist/runners/npmOutdated.js +115 -0
- package/dist/utils.js +175 -24
- package/package.json +31 -11
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
86
|
+
Show options:
|
|
54
87
|
|
|
55
88
|
```bash
|
|
56
|
-
npx dependency-radar
|
|
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`
|
|
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)
|