dependency-radar 0.3.1 → 0.4.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 +197 -39
- package/dist/aggregator.js +386 -45
- package/dist/cli.js +399 -78
- package/dist/cta.js +11 -0
- package/dist/report-assets.js +2 -2
- package/dist/report.js +203 -151
- package/dist/runners/npmLs.js +120 -12
- package/dist/runners/npmOutdated.js +34 -18
- package/dist/utils.js +15 -1
- package/package.json +13 -22
package/dist/report.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.renderReport = renderReport;
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const cta_1 = require("./cta");
|
|
9
10
|
const report_assets_1 = require("./report-assets");
|
|
10
11
|
async function renderReport(data, outputPath) {
|
|
11
12
|
const html = buildHtml(data);
|
|
@@ -13,8 +14,8 @@ async function renderReport(data, outputPath) {
|
|
|
13
14
|
await promises_1.default.writeFile(outputPath, html, 'utf8');
|
|
14
15
|
}
|
|
15
16
|
function buildHtml(data) {
|
|
16
|
-
var _a, _b, _c;
|
|
17
17
|
const json = JSON.stringify(data).replace(/</g, '\\u003c');
|
|
18
|
+
const ctaUrl = (0, cta_1.buildCtaUrl)(data.dependencyRadarVersion);
|
|
18
19
|
// Format the generated date
|
|
19
20
|
let formattedDate = data.generatedAt;
|
|
20
21
|
try {
|
|
@@ -35,23 +36,55 @@ function buildHtml(data) {
|
|
|
35
36
|
catch {
|
|
36
37
|
// Keep the original if parsing fails
|
|
37
38
|
}
|
|
38
|
-
// Build conditional meta items
|
|
39
|
-
const runtimeVersion = ((_a = data.environment) === null || _a === void 0 ? void 0 : _a.runtimeVersion)
|
|
40
|
-
? data.environment.runtimeVersion.replace(/^v/, '')
|
|
41
|
-
: null;
|
|
42
|
-
const minRequiredMajor = (_b = data.environment) === null || _b === void 0 ? void 0 : _b.minRequiredMajor;
|
|
43
|
-
const nodeVersionText = runtimeVersion
|
|
44
|
-
? `${runtimeVersion}${minRequiredMajor && minRequiredMajor > 0 ? ` (requires ≥${minRequiredMajor})` : ''}`
|
|
45
|
-
: null;
|
|
46
|
-
const nodeDisclaimer = minRequiredMajor && minRequiredMajor > 0
|
|
47
|
-
? 'Node requirement derived from dependency engine ranges.'
|
|
48
|
-
: null;
|
|
49
39
|
return `<!doctype html>
|
|
50
40
|
<html lang="en">
|
|
51
41
|
<head>
|
|
52
42
|
<meta charset="UTF-8" />
|
|
53
43
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
54
44
|
<title>Dependency Radar</title>
|
|
45
|
+
<link
|
|
46
|
+
rel="icon"
|
|
47
|
+
type="image/svg+xml"
|
|
48
|
+
href="data:image/svg+xml;utf8,
|
|
49
|
+
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1' viewBox='0 0 1024 1024'>
|
|
50
|
+
<defs>
|
|
51
|
+
<style>
|
|
52
|
+
.st0, .st1 {fill: %23ff8000;}
|
|
53
|
+
.st2, .st3 {fill: %2340ff40;}
|
|
54
|
+
.st3, .st4, .st1 {opacity: .4;}
|
|
55
|
+
.st5 {fill: url(%23linear-gradient);}
|
|
56
|
+
.st4, .st6 {fill: red;}
|
|
57
|
+
.st7 {stroke-width: 16px;}
|
|
58
|
+
.st7, .st8 {fill: %23171772;}
|
|
59
|
+
.st7, .st8, .st9 {stroke: %2355fffa; stroke-miterlimit: 10;}
|
|
60
|
+
.st8 {stroke-width: 10px;}
|
|
61
|
+
.st10 {fill: %2314145e;}
|
|
62
|
+
.st9 {fill: none; opacity: .3; stroke-width: 6px;}
|
|
63
|
+
.st11 {fill: %2355fffa;}
|
|
64
|
+
</style>
|
|
65
|
+
<linearGradient id='linear-gradient' x1='239.3' y1='298.2' x2='815.3' y2='298.2' gradientTransform='translate(150 -115.1) rotate(15)' gradientUnits='userSpaceOnUse'>
|
|
66
|
+
<stop offset='.4' stop-color='%2355fffa' stop-opacity='0'/>
|
|
67
|
+
<stop offset='1' stop-color='%2355fffa' stop-opacity='.5'/>
|
|
68
|
+
</linearGradient>
|
|
69
|
+
</defs>
|
|
70
|
+
<circle class='st10' cx='512' cy='512' r='512'/>
|
|
71
|
+
<circle class='st7' cx='512' cy='512' r='430'/>
|
|
72
|
+
<circle class='st8' cx='512' cy='512' r='256'/>
|
|
73
|
+
<circle class='st9' cx='512' cy='512' r='160'/>
|
|
74
|
+
<circle class='st9' cx='512' cy='512' r='379.5'/>
|
|
75
|
+
<circle class='st9' cx='512' cy='512' r='339'/>
|
|
76
|
+
<circle class='st9' cx='512' cy='512' r='210'/>
|
|
77
|
+
<rect class='st11' x='690.2' y='193.1' width='15.8' height='427.6' transform='translate(701.4 -401.1) rotate(60)'/>
|
|
78
|
+
<circle class='st11' cx='512' cy='514.4' r='64'/>
|
|
79
|
+
<path class='st5' d='M517.2,513.4l365.8-213.9c-54.6-95.4-145.8-169.8-260.3-200.5-100.1-26.8-201.5-15.8-288.8,24.3l183.4,390Z'/>
|
|
80
|
+
<circle class='st4' cx='512' cy='256' r='96'/>
|
|
81
|
+
<circle class='st6' cx='512' cy='256' r='56'/>
|
|
82
|
+
<circle class='st3' cx='733.7' cy='640' r='96' transform='translate(-237.7 706.3) rotate(-45)'/>
|
|
83
|
+
<circle class='st2' cx='733.7' cy='640' r='56' transform='translate(-237.7 706.3) rotate(-45)'/>
|
|
84
|
+
<ellipse class='st1' cx='290.3' cy='640' rx='96' ry='96' transform='translate(-367.5 392.7) rotate(-45)'/>
|
|
85
|
+
<circle class='st0' cx='290.3' cy='640' r='56'/>
|
|
86
|
+
</svg>"
|
|
87
|
+
>
|
|
55
88
|
<style>
|
|
56
89
|
${report_assets_1.CSS_CONTENT}
|
|
57
90
|
</style>
|
|
@@ -61,79 +94,62 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
61
94
|
<header class="top-header">
|
|
62
95
|
<div class="header-row">
|
|
63
96
|
<div class="header-content">
|
|
64
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
|
|
65
|
-
viewBox="0 0 1024 1024" class="logo">
|
|
97
|
+
<svg class="logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 1024 1024">
|
|
66
98
|
<defs>
|
|
67
99
|
<style>
|
|
68
|
-
.st0, .st1 {
|
|
69
|
-
.st2 {
|
|
70
|
-
.
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
.st7
|
|
74
|
-
.
|
|
75
|
-
.st9 {
|
|
76
|
-
.
|
|
77
|
-
.st10
|
|
78
|
-
.
|
|
79
|
-
.
|
|
80
|
-
.st5 { fill: #141259; }
|
|
100
|
+
.st0, .st1 {fill: #ff8000;}
|
|
101
|
+
.st2, .st3 {fill: #40ff40;}
|
|
102
|
+
.st3, .st4, .st1 {opacity: .4;}
|
|
103
|
+
.st5 {fill: url(#linear-gradient);}
|
|
104
|
+
.st4, .st6 {fill: red;}
|
|
105
|
+
.st7 {stroke-width: 16px;}
|
|
106
|
+
.st7, .st8 {fill: #171772;}
|
|
107
|
+
.st7, .st8, .st9 {stroke: #55fffa; stroke-miterlimit: 10;}
|
|
108
|
+
.st8 {stroke-width: 10px;}
|
|
109
|
+
.st10 {fill: #14145e;}
|
|
110
|
+
.st9 {fill: none; opacity: .3; stroke-width: 6px;}
|
|
111
|
+
.st11 {fill: #55fffa;}
|
|
81
112
|
</style>
|
|
82
|
-
<linearGradient id="linear-gradient" x1="
|
|
83
|
-
<stop offset=".4" stop-color="#55fffa" stop-opacity="0"
|
|
84
|
-
<stop offset="1" stop-color="#55fffa" stop-opacity=".5"
|
|
113
|
+
<linearGradient id="linear-gradient" x1="239.3" y1="298.2" x2="815.3" y2="298.2" gradientTransform="translate(150 -115.1) rotate(15)" gradientUnits="userSpaceOnUse">
|
|
114
|
+
<stop offset=".4" stop-color="#55fffa" stop-opacity="0"/>
|
|
115
|
+
<stop offset="1" stop-color="#55fffa" stop-opacity=".5"/>
|
|
85
116
|
</linearGradient>
|
|
86
117
|
</defs>
|
|
87
|
-
<circle class="
|
|
88
|
-
<circle class="
|
|
89
|
-
<circle class="
|
|
90
|
-
<circle class="
|
|
91
|
-
<circle class="
|
|
92
|
-
<
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
<circle class="
|
|
98
|
-
<circle class="
|
|
99
|
-
<circle class="
|
|
100
|
-
<circle class="
|
|
101
|
-
<
|
|
102
|
-
<circle class="
|
|
103
|
-
<circle class="st0" cx="400.1" cy="673" r="25" />
|
|
104
|
-
<circle class="st1" cx="578.1" cy="135" r="50" />
|
|
105
|
-
<circle class="st0" cx="578.1" cy="135" r="25" />
|
|
106
|
-
<circle class="st11" cx="187" cy="569.5" r="50" />
|
|
107
|
-
<circle class="st13" cx="187" cy="569.5" r="25" />
|
|
108
|
-
<circle class="st11" cx="592" cy="894.1" r="50" />
|
|
109
|
-
<circle class="st13" cx="592" cy="894.1" r="25" />
|
|
110
|
-
<circle class="st11" cx="512" cy="314" r="50" />
|
|
111
|
-
<circle class="st13" cx="512" cy="314" r="25" />
|
|
112
|
-
<circle class="st10" cx="329" cy="854.6" r="50" />
|
|
113
|
-
<circle class="st12" cx="329" cy="854.6" r="25" />
|
|
118
|
+
<circle class="st10" cx="512" cy="512" r="512"/>
|
|
119
|
+
<circle class="st7" cx="512" cy="512" r="430"/>
|
|
120
|
+
<circle class="st8" cx="512" cy="512" r="256"/>
|
|
121
|
+
<circle class="st9" cx="512" cy="512" r="160"/>
|
|
122
|
+
<circle class="st9" cx="512" cy="512" r="379.5"/>
|
|
123
|
+
<circle class="st9" cx="512" cy="512" r="339"/>
|
|
124
|
+
<circle class="st9" cx="512" cy="512" r="210"/>
|
|
125
|
+
<rect class="st11" x="690.2" y="193.1" width="15.8" height="427.6" transform="translate(701.4 -401.1) rotate(60)"/>
|
|
126
|
+
<circle class="st11" cx="512" cy="514.4" r="64"/>
|
|
127
|
+
<path class="st5" d="M517.2,513.4l365.8-213.9c-54.6-95.4-145.8-169.8-260.3-200.5-100.1-26.8-201.5-15.8-288.8,24.3l183.4,390Z"/>
|
|
128
|
+
<circle class="st4" cx="512" cy="256" r="96"/>
|
|
129
|
+
<circle class="st6" cx="512" cy="256" r="56"/>
|
|
130
|
+
<circle class="st3" cx="733.7" cy="640" r="96" transform="translate(-237.7 706.3) rotate(-45)"/>
|
|
131
|
+
<circle class="st2" cx="733.7" cy="640" r="56" transform="translate(-237.7 706.3) rotate(-45)"/>
|
|
132
|
+
<ellipse class="st1" cx="290.3" cy="640" rx="96" ry="96" transform="translate(-367.5 392.7) rotate(-45)"/>
|
|
133
|
+
<circle class="st0" cx="290.3" cy="640" r="56"/>
|
|
114
134
|
</svg>
|
|
115
135
|
<div class="header-text">
|
|
116
136
|
<h1>Dependency Radar</h1>
|
|
117
137
|
<div class="header-meta">
|
|
118
138
|
<span class="meta-item"><span class="meta-label">Project</span> <strong id="project-path">${escapeHtml(data.project.projectDir)}</strong></span>
|
|
119
|
-
|
|
120
|
-
|
|
139
|
+
<span class="meta-item" id="git-branch-item" style="display: none;"><span class="meta-label">Branch</span> <strong id="git-branch"></strong></span>
|
|
140
|
+
<span class="meta-item" id="node-item" style="display: none;"><span class="meta-label">Node</span> <strong id="node-version"></strong></span>
|
|
121
141
|
<span class="meta-item"><span class="meta-label">Generated</span> <strong id="formatted-date">${escapeHtml(formattedDate)}</strong></span>
|
|
122
|
-
|
|
142
|
+
<span class="header-disclaimer" id="node-disclaimer" style="display: none;"></span>
|
|
123
143
|
</div>
|
|
124
144
|
</div>
|
|
125
145
|
</div>
|
|
126
146
|
<div class="cta-section">
|
|
127
|
-
<a href="
|
|
128
|
-
|
|
147
|
+
<a href="${escapeHtml(ctaUrl)}" class="cta-link" target="_blank" rel="noopener" id="cta-primary-link">
|
|
148
|
+
Enrich this scan
|
|
129
149
|
<span class="cta-arrow">→</span>
|
|
130
150
|
</a>
|
|
131
|
-
<
|
|
132
|
-
|
|
133
|
-
<span>Charts & assets for presentations</span>
|
|
134
|
-
<span>Actionable upgrade recommendations</span>
|
|
135
|
-
</div>
|
|
136
|
-
<div class="cta-text">dependency-radar.com</div>
|
|
151
|
+
<p class="cta-text">Beyond the standalone report</p>
|
|
152
|
+
<a href="${escapeHtml(ctaUrl)}" target="_blank" rel="noopener" class="cta-url" id="cta-secondary-link">dependency-radar.com</a>
|
|
137
153
|
</div>
|
|
138
154
|
</div>
|
|
139
155
|
</header>
|
|
@@ -141,90 +157,127 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
141
157
|
<!-- Sticky Filter Bar -->
|
|
142
158
|
<div class="filter-bar">
|
|
143
159
|
<div class="filter-bar-inner">
|
|
144
|
-
<div class="
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<div class="filter-group">
|
|
152
|
-
<span class="filter-label">Type</span>
|
|
153
|
-
<select id="direct-filter">
|
|
154
|
-
<option value="all">All</option>
|
|
155
|
-
<option value="direct">Dependency</option>
|
|
156
|
-
<option value="transitive">Sub-Dependency</option>
|
|
157
|
-
</select>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div class="filter-group">
|
|
161
|
-
<span class="filter-label">Scope</span>
|
|
162
|
-
<select id="runtime-filter">
|
|
163
|
-
<option value="all">All</option>
|
|
164
|
-
<option value="runtime">Runtime</option>
|
|
165
|
-
<option value="dev">Dev</option>
|
|
166
|
-
<option value="optional">Optional</option>
|
|
167
|
-
<option value="peer">Peer</option>
|
|
168
|
-
</select>
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
<div class="filter-group sort-wrapper">
|
|
172
|
-
<span class="filter-label">Sort</span>
|
|
173
|
-
<select id="sort-by">
|
|
174
|
-
<option value="name">Name</option>
|
|
175
|
-
<option value="severity">Severity</option>
|
|
176
|
-
<option value="depth">Depth</option>
|
|
177
|
-
</select>
|
|
178
|
-
<button type="button" class="sort-direction-btn" id="sort-direction" title="Toggle sort direction">↑</button>
|
|
179
|
-
</div>
|
|
180
|
-
|
|
181
|
-
<button type="button" class="license-filter-toggle" id="license-toggle">
|
|
182
|
-
License Categories
|
|
183
|
-
<span class="chevron">▼</span>
|
|
184
|
-
</button>
|
|
185
|
-
|
|
186
|
-
<label class="checkbox-filter">
|
|
187
|
-
<input type="checkbox" id="has-vulns" />
|
|
188
|
-
Has vulnerabilities
|
|
189
|
-
</label>
|
|
190
|
-
|
|
191
|
-
<div class="theme-toggle">
|
|
192
|
-
<span class="theme-toggle-label">Theme</span>
|
|
193
|
-
<div class="theme-switch" id="theme-switch" title="Toggle dark/light mode"></div>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<!-- Collapsible License Filter Panel (inside sticky bar) -->
|
|
198
|
-
<div class="license-filter-panel" id="license-panel">
|
|
199
|
-
<div class="license-filter-inner">
|
|
200
|
-
<div class="license-filter-header">
|
|
201
|
-
<span class="license-filter-title">Filter by License Type</span>
|
|
202
|
-
<div class="license-quick-actions">
|
|
203
|
-
<button type="button" class="quick-action-btn" id="license-all">Show All</button>
|
|
204
|
-
<button type="button" class="quick-action-btn" id="license-friendly">Business-Friendly Only</button>
|
|
160
|
+
<div class="filter-row">
|
|
161
|
+
<div class="filter-top-row">
|
|
162
|
+
<div class="search-wrapper">
|
|
163
|
+
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
164
|
+
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
|
|
165
|
+
</svg>
|
|
166
|
+
<input type="search" id="search" placeholder="Search packages..." />
|
|
205
167
|
</div>
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
class="filters-toggle"
|
|
171
|
+
id="filters-toggle"
|
|
172
|
+
aria-expanded="false"
|
|
173
|
+
>
|
|
174
|
+
Filters
|
|
175
|
+
<span class="chevron">▼</span>
|
|
176
|
+
</button>
|
|
206
177
|
</div>
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
178
|
+
|
|
179
|
+
<div class="filter-controls" id="filter-controls">
|
|
180
|
+
<div class="filter-controls-row">
|
|
181
|
+
<div class="filter-group">
|
|
182
|
+
<span class="filter-label">Type</span>
|
|
183
|
+
<select id="direct-filter">
|
|
184
|
+
<option value="all">All</option>
|
|
185
|
+
<option value="direct">Dependency</option>
|
|
186
|
+
<option value="transitive">Sub-Dependency</option>
|
|
187
|
+
</select>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<div class="filter-group">
|
|
191
|
+
<span class="filter-label">Scope</span>
|
|
192
|
+
<select id="runtime-filter">
|
|
193
|
+
<option value="all">All</option>
|
|
194
|
+
<option value="runtime">Runtime</option>
|
|
195
|
+
<option value="dev">Dev</option>
|
|
196
|
+
<option value="optional">Optional</option>
|
|
197
|
+
<option value="peer">Peer</option>
|
|
198
|
+
</select>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<button type="button" class="license-filter-toggle" id="license-toggle">
|
|
202
|
+
License Categories
|
|
203
|
+
<span class="chevron">▼</span>
|
|
204
|
+
</button>
|
|
205
|
+
|
|
206
|
+
<label class="checkbox-filter">
|
|
207
|
+
<input type="checkbox" id="has-vulns" />
|
|
208
|
+
Has vulnerabilities
|
|
209
|
+
</label>
|
|
210
|
+
|
|
211
|
+
<!-- Sort dropdown - visible on mobile, hidden on desktop (replaced by column headers) -->
|
|
212
|
+
<div class="filter-group sort-wrapper mobile-only" id="mobile-sort">
|
|
213
|
+
<span class="filter-label">SORT</span>
|
|
214
|
+
<select id="sort-by">
|
|
215
|
+
<option value="name">Name</option>
|
|
216
|
+
<option value="type">Type</option>
|
|
217
|
+
<option value="scope">Scope</option>
|
|
218
|
+
<option value="license">License</option>
|
|
219
|
+
<option value="severity">Severity</option>
|
|
220
|
+
<option value="install">Install</option>
|
|
221
|
+
<option value="depth">Depth</option>
|
|
222
|
+
</select>
|
|
223
|
+
<button type="button" class="sort-direction-btn" id="sort-direction" title="Toggle sort direction">↑</button>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div class="theme-toggle">
|
|
227
|
+
<span class="theme-toggle-label">Theme</span>
|
|
228
|
+
<div class="theme-switch" id="theme-switch" title="Toggle dark/light mode"></div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Collapsible License Filter Panel -->
|
|
234
|
+
<div class="license-filter-panel-row">
|
|
235
|
+
<div class="license-filter-panel" id="license-panel">
|
|
236
|
+
<div class="license-filter-inner">
|
|
237
|
+
<div class="license-filter-header">
|
|
238
|
+
<span class="license-filter-title">Filter by License Type</span>
|
|
239
|
+
<div class="license-quick-actions">
|
|
240
|
+
<button type="button" class="quick-action-btn" id="license-all">Show All</button>
|
|
241
|
+
<button type="button" class="quick-action-btn" id="license-friendly">Business-Friendly Only</button>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="license-groups">
|
|
245
|
+
<label class="license-group-checkbox">
|
|
246
|
+
<input type="checkbox" id="license-permissive" checked />
|
|
247
|
+
<span class="license-dot permissive"></span>
|
|
248
|
+
Permissive (MIT, BSD, Apache, ISC)
|
|
249
|
+
</label>
|
|
250
|
+
<label class="license-group-checkbox">
|
|
251
|
+
<input type="checkbox" id="license-weak-copyleft" checked />
|
|
252
|
+
<span class="license-dot weak-copyleft"></span>
|
|
253
|
+
Weak Copyleft (LGPL, MPL, EPL)
|
|
254
|
+
</label>
|
|
255
|
+
<label class="license-group-checkbox">
|
|
256
|
+
<input type="checkbox" id="license-strong-copyleft" checked />
|
|
257
|
+
<span class="license-dot strong-copyleft"></span>
|
|
258
|
+
Strong Copyleft (GPL, AGPL)
|
|
259
|
+
</label>
|
|
260
|
+
<label class="license-group-checkbox">
|
|
261
|
+
<input type="checkbox" id="license-unknown" checked />
|
|
262
|
+
<span class="license-dot unknown"></span>
|
|
263
|
+
Other / Unknown
|
|
264
|
+
</label>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<!-- Results summary and column headers row -->
|
|
271
|
+
<div class="column-headers-section" id="column-headers-section">
|
|
272
|
+
<div class="package-header-wrapper">
|
|
273
|
+
<div class="results-summary" id="results-summary"></div>
|
|
274
|
+
<button type="button" class="column-header package-header column-header-no-border" data-sort="name" id="package-header">
|
|
275
|
+
PACKAGE
|
|
276
|
+
<span class="sort-indicator"></span>
|
|
277
|
+
</button>
|
|
278
|
+
</div>
|
|
279
|
+
<!-- Column headers are dynamically generated by JavaScript from COLUMN_CONFIG -->
|
|
280
|
+
<div id="column-headers-container"></div>
|
|
228
281
|
</div>
|
|
229
282
|
</div>
|
|
230
283
|
</div>
|
|
@@ -232,7 +285,6 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
232
285
|
|
|
233
286
|
<!-- Main Content -->
|
|
234
287
|
<main class="main-content">
|
|
235
|
-
<div class="results-summary" id="results-summary"></div>
|
|
236
288
|
<div id="dependency-list" class="dependency-grid"></div>
|
|
237
289
|
</main>
|
|
238
290
|
|
package/dist/runners/npmLs.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runNpmLs = runNpmLs;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
9
|
const utils_1 = require("../utils");
|
|
9
10
|
const PNPM_DEPTH_ATTEMPTS = ['Infinity', '8', '4', '2', '1'];
|
|
10
11
|
const PNPM_MAX_OLD_SPACE_SIZE_MB = '8192';
|
|
@@ -45,7 +46,7 @@ function buildLsCommand(tool) {
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
async function runPnpmLsWithFallback(projectPath, targetFile, options) {
|
|
48
|
-
const
|
|
49
|
+
const installState = createPnpmInstallState(projectPath);
|
|
49
50
|
const attempts = [];
|
|
50
51
|
const env = {
|
|
51
52
|
NODE_OPTIONS: ensureNodeMaxOldSpaceSize(process.env.NODE_OPTIONS, PNPM_MAX_OLD_SPACE_SIZE_MB)
|
|
@@ -57,7 +58,7 @@ async function runPnpmLsWithFallback(projectPath, targetFile, options) {
|
|
|
57
58
|
env
|
|
58
59
|
});
|
|
59
60
|
const parsed = parseJsonOutput(result.stdout);
|
|
60
|
-
const normalized =
|
|
61
|
+
const normalized = normalizePnpmTree(parsed, installState);
|
|
61
62
|
const outOfMemory = isOutOfMemoryError(result.stderr);
|
|
62
63
|
attempts.push({
|
|
63
64
|
depth,
|
|
@@ -212,15 +213,23 @@ function normalizeNpmNode(name, node) {
|
|
|
212
213
|
}
|
|
213
214
|
return out;
|
|
214
215
|
}
|
|
215
|
-
function normalizePnpmTree(data) {
|
|
216
|
-
const roots = Array.isArray(data) ? data : [data];
|
|
217
|
-
const root = roots.find((entry) => entry
|
|
216
|
+
function normalizePnpmTree(data, installState) {
|
|
217
|
+
const roots = (Array.isArray(data) ? data : [data]).filter((entry) => entry && typeof entry === 'object');
|
|
218
|
+
const root = roots.find((entry) => !isPnpmErrorPayload(entry));
|
|
218
219
|
if (!root || typeof root !== 'object')
|
|
219
220
|
return undefined;
|
|
220
|
-
const dependencies = collectPnpmDependencyMap(root);
|
|
221
|
+
const dependencies = collectPnpmDependencyMap(root, installState);
|
|
221
222
|
return { dependencies };
|
|
222
223
|
}
|
|
223
|
-
function
|
|
224
|
+
function isPnpmErrorPayload(node) {
|
|
225
|
+
if (!node || typeof node !== 'object')
|
|
226
|
+
return false;
|
|
227
|
+
if (!('error' in node))
|
|
228
|
+
return false;
|
|
229
|
+
const error = node.error;
|
|
230
|
+
return typeof error === 'string' || (error !== null && typeof error === 'object');
|
|
231
|
+
}
|
|
232
|
+
function collectPnpmDependencyMap(node, installState) {
|
|
224
233
|
const out = {};
|
|
225
234
|
const groups = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
226
235
|
for (const group of groups) {
|
|
@@ -234,7 +243,7 @@ function collectPnpmDependencyMap(node) {
|
|
|
234
243
|
const name = typeof entry.name === 'string' ? entry.name : undefined;
|
|
235
244
|
if (!name)
|
|
236
245
|
continue;
|
|
237
|
-
const normalized = normalizePnpmNode(name, entry);
|
|
246
|
+
const normalized = normalizePnpmNode(name, entry, installState);
|
|
238
247
|
if (normalized)
|
|
239
248
|
out[name] = normalized;
|
|
240
249
|
}
|
|
@@ -244,7 +253,7 @@ function collectPnpmDependencyMap(node) {
|
|
|
244
253
|
for (const [name, entry] of Object.entries(value)) {
|
|
245
254
|
if (!entry || typeof entry !== 'object')
|
|
246
255
|
continue;
|
|
247
|
-
const normalized = normalizePnpmNode(name, entry);
|
|
256
|
+
const normalized = normalizePnpmNode(name, entry, installState);
|
|
248
257
|
if (normalized)
|
|
249
258
|
out[name] = normalized;
|
|
250
259
|
}
|
|
@@ -254,19 +263,21 @@ function collectPnpmDependencyMap(node) {
|
|
|
254
263
|
return out;
|
|
255
264
|
if ((node === null || node === void 0 ? void 0 : node.dependencies) && typeof node.dependencies === 'object') {
|
|
256
265
|
for (const [name, entry] of Object.entries(node.dependencies)) {
|
|
257
|
-
const normalized = normalizePnpmNode(name, entry);
|
|
266
|
+
const normalized = normalizePnpmNode(name, entry, installState);
|
|
258
267
|
if (normalized)
|
|
259
268
|
out[name] = normalized;
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
271
|
return out;
|
|
263
272
|
}
|
|
264
|
-
function normalizePnpmNode(name, node) {
|
|
273
|
+
function normalizePnpmNode(name, node, installState) {
|
|
265
274
|
const version = typeof (node === null || node === void 0 ? void 0 : node.version) === 'string' ? node.version.trim() : '';
|
|
266
275
|
if (!version || version === 'unknown' || version === 'missing' || version === 'invalid')
|
|
267
276
|
return undefined;
|
|
277
|
+
if (!isPnpmPackageInstalled(name, version, installState))
|
|
278
|
+
return undefined;
|
|
268
279
|
const out = { name, version, dependencies: {} };
|
|
269
|
-
const childMap = collectPnpmDependencyMap(node);
|
|
280
|
+
const childMap = collectPnpmDependencyMap(node, installState);
|
|
270
281
|
if (Object.keys(childMap).length > 0) {
|
|
271
282
|
out.dependencies = childMap;
|
|
272
283
|
}
|
|
@@ -277,6 +288,103 @@ function normalizePnpmNode(name, node) {
|
|
|
277
288
|
out.dev = Boolean(node.dev);
|
|
278
289
|
return out;
|
|
279
290
|
}
|
|
291
|
+
function createPnpmInstallState(projectPath) {
|
|
292
|
+
const nodeModulesRoots = findNodeModulesRoots(projectPath);
|
|
293
|
+
const virtualStoreEntries = new Set();
|
|
294
|
+
for (const root of nodeModulesRoots) {
|
|
295
|
+
const virtualStoreDir = path_1.default.join(root, '.pnpm');
|
|
296
|
+
if (!safePathExists(virtualStoreDir))
|
|
297
|
+
continue;
|
|
298
|
+
for (const entry of safeReadDirNames(virtualStoreDir)) {
|
|
299
|
+
virtualStoreEntries.add(entry);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
enabled: virtualStoreEntries.size > 0 || nodeModulesRoots.length > 0,
|
|
304
|
+
virtualStoreEntries,
|
|
305
|
+
nodeModulesRoots,
|
|
306
|
+
installedCache: new Map()
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function findNodeModulesRoots(startPath) {
|
|
310
|
+
const roots = [];
|
|
311
|
+
let current = path_1.default.resolve(startPath);
|
|
312
|
+
while (true) {
|
|
313
|
+
const candidate = path_1.default.join(current, 'node_modules');
|
|
314
|
+
if (safePathExists(candidate)) {
|
|
315
|
+
roots.push(candidate);
|
|
316
|
+
}
|
|
317
|
+
const parent = path_1.default.dirname(current);
|
|
318
|
+
if (parent === current)
|
|
319
|
+
break;
|
|
320
|
+
current = parent;
|
|
321
|
+
}
|
|
322
|
+
return roots;
|
|
323
|
+
}
|
|
324
|
+
function isPnpmPackageInstalled(name, version, installState) {
|
|
325
|
+
if (!installState.enabled)
|
|
326
|
+
return true;
|
|
327
|
+
const cacheKey = `${name}@${version}`;
|
|
328
|
+
const cached = installState.installedCache.get(cacheKey);
|
|
329
|
+
if (cached !== undefined)
|
|
330
|
+
return cached;
|
|
331
|
+
const normalizedName = normalizeScopedPackageNameForPnpmStore(name);
|
|
332
|
+
const storePrefix = `${normalizedName}@${version}`;
|
|
333
|
+
for (const entry of installState.virtualStoreEntries) {
|
|
334
|
+
if (entry === storePrefix || entry.startsWith(`${storePrefix}_`) || entry.startsWith(`${storePrefix}(`)) {
|
|
335
|
+
installState.installedCache.set(cacheKey, true);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Workspace links may resolve outside the virtual store, but still exist in node_modules.
|
|
340
|
+
for (const nodeModulesRoot of installState.nodeModulesRoots) {
|
|
341
|
+
const packageDir = path_1.default.join(nodeModulesRoot, ...name.split('/'));
|
|
342
|
+
if (safePathExists(packageDir) && packageDirectoryMatchesVersion(packageDir, version)) {
|
|
343
|
+
installState.installedCache.set(cacheKey, true);
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
installState.installedCache.set(cacheKey, false);
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
function normalizeScopedPackageNameForPnpmStore(name) {
|
|
351
|
+
if (!name.startsWith('@'))
|
|
352
|
+
return name;
|
|
353
|
+
const slashIndex = name.indexOf('/');
|
|
354
|
+
if (slashIndex <= 0)
|
|
355
|
+
return name;
|
|
356
|
+
return `${name.slice(0, slashIndex)}+${name.slice(slashIndex + 1)}`;
|
|
357
|
+
}
|
|
358
|
+
function safePathExists(targetPath) {
|
|
359
|
+
try {
|
|
360
|
+
return fs_1.default.existsSync(targetPath);
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function safeReadDirNames(dirPath) {
|
|
367
|
+
try {
|
|
368
|
+
return fs_1.default.readdirSync(dirPath);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function packageDirectoryMatchesVersion(packageDir, expectedVersion) {
|
|
375
|
+
const pkgJsonPath = path_1.default.join(packageDir, 'package.json');
|
|
376
|
+
if (!safePathExists(pkgJsonPath))
|
|
377
|
+
return true;
|
|
378
|
+
try {
|
|
379
|
+
const raw = fs_1.default.readFileSync(pkgJsonPath, 'utf8');
|
|
380
|
+
const parsed = JSON.parse(raw);
|
|
381
|
+
const version = typeof (parsed === null || parsed === void 0 ? void 0 : parsed.version) === 'string' ? parsed.version.trim() : '';
|
|
382
|
+
return !version || version === expectedVersion;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
280
388
|
function normalizeYarnTree(data) {
|
|
281
389
|
const treePayload = resolveYarnTreePayload(data);
|
|
282
390
|
if (!treePayload || !Array.isArray(treePayload.trees))
|