dependency-radar 0.2.0 → 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 +176 -19
- package/dist/aggregator.js +1021 -406
- package/dist/cli.js +857 -56
- package/dist/generated/spdx.js +855 -0
- package/dist/license.js +324 -0
- package/dist/report-assets.js +2 -2
- package/dist/report.js +88 -89
- package/dist/runners/importGraphRunner.js +11 -5
- 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 +159 -24
- package/package.json +23 -3
package/dist/report.js
CHANGED
|
@@ -13,17 +13,39 @@ async function renderReport(data, outputPath) {
|
|
|
13
13
|
await promises_1.default.writeFile(outputPath, html, 'utf8');
|
|
14
14
|
}
|
|
15
15
|
function buildHtml(data) {
|
|
16
|
-
var _a, _b, _c
|
|
16
|
+
var _a, _b, _c;
|
|
17
17
|
const json = JSON.stringify(data).replace(/</g, '\\u003c');
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
// Format the generated date
|
|
19
|
+
let formattedDate = data.generatedAt;
|
|
20
|
+
try {
|
|
21
|
+
const date = new Date(data.generatedAt);
|
|
22
|
+
if (Number.isNaN(date.getTime())) {
|
|
23
|
+
// Keep the original if parsing fails
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
formattedDate = new Intl.DateTimeFormat(undefined, {
|
|
27
|
+
day: 'numeric',
|
|
28
|
+
month: 'short',
|
|
29
|
+
year: 'numeric',
|
|
30
|
+
hour: '2-digit',
|
|
31
|
+
minute: '2-digit'
|
|
32
|
+
}).format(date);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Keep the original if parsing fails
|
|
37
|
+
}
|
|
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;
|
|
27
49
|
return `<!doctype html>
|
|
28
50
|
<html lang="en">
|
|
29
51
|
<head>
|
|
@@ -43,54 +65,21 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
43
65
|
viewBox="0 0 1024 1024" class="logo">
|
|
44
66
|
<defs>
|
|
45
67
|
<style>
|
|
46
|
-
.st0, .st1 {
|
|
47
|
-
fill: #
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.
|
|
51
|
-
fill: #
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.st6 {
|
|
61
|
-
fill: #0a0a33;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.st3 {
|
|
65
|
-
fill: #161466;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.st7 {
|
|
69
|
-
fill: url(#linear-gradient);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.st8, .st1 {
|
|
73
|
-
opacity: .4;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.st8, .st9 {
|
|
77
|
-
fill: red;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.st4 {
|
|
81
|
-
fill: #1c197f;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.st10 {
|
|
85
|
-
fill: #55fffa;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.st5 {
|
|
89
|
-
fill: #141259;
|
|
90
|
-
}
|
|
68
|
+
.st0, .st1 { fill: #ff8000; }
|
|
69
|
+
.st2 { fill: #191772; }
|
|
70
|
+
.st2, .st3, .st4, .st5 { stroke: #55fffa; stroke-miterlimit: 10; stroke-width: 6px; }
|
|
71
|
+
.st6 { fill: #0a0a33; }
|
|
72
|
+
.st7 { opacity: .3; }
|
|
73
|
+
.st7, .st8 { fill: #55fffa; }
|
|
74
|
+
.st3 { fill: #161466; }
|
|
75
|
+
.st9 { fill: url(#linear-gradient); }
|
|
76
|
+
.st10, .st1, .st11 { opacity: .4; }
|
|
77
|
+
.st10, .st12 { fill: red; }
|
|
78
|
+
.st4 { fill: #1c197f; }
|
|
79
|
+
.st13, .st11 { fill: #00be00; }
|
|
80
|
+
.st5 { fill: #141259; }
|
|
91
81
|
</style>
|
|
92
|
-
<linearGradient id="linear-gradient" x1="225" y1="287" x2="831.3" y2="287"
|
|
93
|
-
gradientUnits="userSpaceOnUse">
|
|
82
|
+
<linearGradient id="linear-gradient" x1="225" y1="287" x2="831.3" y2="287" gradientUnits="userSpaceOnUse">
|
|
94
83
|
<stop offset=".4" stop-color="#55fffa" stop-opacity="0" />
|
|
95
84
|
<stop offset="1" stop-color="#55fffa" stop-opacity=".5" />
|
|
96
85
|
</linearGradient>
|
|
@@ -100,31 +89,50 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
100
89
|
<circle class="st3" cx="512" cy="512" r="325" />
|
|
101
90
|
<circle class="st2" cx="512" cy="512" r="200" />
|
|
102
91
|
<circle class="st4" cx="512" cy="512" r="80" />
|
|
103
|
-
<path class="
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
<
|
|
108
|
-
<circle class="
|
|
109
|
-
<circle class="
|
|
110
|
-
<circle class="
|
|
92
|
+
<path class="st9" d="M517.7,512l313.6-317.1c-81.5-82.1-194.5-132.9-319.3-132.9s-209.1,38.8-287,103.4l292.7,346.6Z" />
|
|
93
|
+
<path class="st7" d="M891.9,618.4c-64.1,245.1-337.5,365.9-562.6,250.5,0,0-14.3-7.7-14.3-7.7-5.8-3.4-11.7-7.1-17.4-10.5-5.3-3.5-11.7-7.9-16.9-11.4-15-10.9-30.5-23.6-43.9-36.5-5.7-5.3-11.8-11.8-17.3-17.4-37-40.1-66.2-88-84.1-139.6-38.9-110.4-27.2-234.3,31.1-335.8,37.6-65.3,93.5-119.6,159.6-155.7,0,0,15-7.7,15-7.7,4.5-2.4,10.7-4.9,15.3-7.1,2.1-.9,5.6-2.6,7.7-3.4,3.9-1.5,11.8-4.7,15.7-6.2,4.7-1.8,11.1-3.8,15.9-5.4,0,0,4-1.3,4-1.3,6.7-1.8,13.5-4,20.3-5.7,116.4-29.7,241.1-7.4,339.7,61.6,18.6,13.1,36.2,27.6,52.6,43.4l-42.9,42.9c-17.1-17.4-35.9-33.2-55.9-47.1-4.7-2.9-13.9-9.3-18.6-11.7-3.1-1.8-8.1-4.7-11.1-6.4-4-2-10.7-5.5-14.7-7.6-5.1-2.4-11.6-5.3-16.7-7.6-5.7-2.3-11.4-4.5-17.1-6.8-60.2-21.9-126.3-27.5-189.3-16.1,0,0-14.6,2.9-14.6,2.9-6,1.4-12,3.1-18,4.5-5.5,1.7-12.3,3.7-17.8,5.5-4.4,1.5-11.4,4.1-15.8,5.7-3,1.2-9,3.7-12,5-43.3,18.6-83.4,46-116.3,79.8-105.7,106.7-134.5,269.3-74.7,406.7,49.8,114.5,155.4,198.1,278.4,219.7,94.7,17.4,195.6-3.1,276-56.1,76.9-50.4,135.7-128.5,160.8-217.2h0Z" />
|
|
94
|
+
<path class="st7" d="M770.9,586c-20.5,84.7-85.9,156.4-167.8,185.8-103.6,37.8-221.1,7.9-294.5-74.2-73.1-80.1-91.2-199-47.6-298,28-63.9,80.3-116.6,144.1-144.9,91-41.1,199.6-30.9,281.6,26,13.1,9.1,25.5,19.2,37,30.2,0,0-42.9,42.9-42.9,42.9-21.5-22.4-47.3-40.6-75.7-53.1-18.3-7.8-37.9-13.6-57.6-16.7-38.4-6-78.3-2.5-115,10.4-34,11.7-65.3,31.6-90.7,57.1-89.3,88.8-94.6,233.1-14.3,329.6,37,45.2,90.4,76.8,147.9,87.3,130.1,24.7,258-55.6,295.4-182.4h0Z" />
|
|
95
|
+
<path class="st7" d="M649.9,553.6c-13.4,61.2-70.2,107.5-133.1,109.4-48.9,2.2-96.8-21.6-125.4-61.3-75.1-106.5,4.4-248.5,133.3-247.2,40.8.5,80.9,16.7,110.7,44.7,0,0-42.9,42.9-42.9,42.9-11.9-13.1-27-23.5-43.8-29.5-28.9-10.5-62-8.3-89.4,5.9-61.5,31.6-81.6,109.8-45.9,168.4,17.8,29.7,48.4,51.3,82.3,58.2,66.3,14.4,134-26.8,154.1-91.6h0Z" />
|
|
96
|
+
<rect class="st8" x="664.2" y="129.5" width="16.7" height="450" transform="translate(447.6 -371.7) rotate(45)" />
|
|
97
|
+
<circle class="st8" cx="512" cy="512" r="32" />
|
|
98
|
+
<circle class="st10" cx="800" cy="662" r="50" />
|
|
99
|
+
<circle class="st12" cx="800" cy="662" r="25" />
|
|
100
|
+
<circle class="st10" cx="256.9" cy="315.2" r="50" />
|
|
101
|
+
<circle class="st12" cx="256.9" cy="315.2" r="25" />
|
|
111
102
|
<circle class="st1" cx="400.1" cy="673" r="50" />
|
|
112
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" />
|
|
113
114
|
</svg>
|
|
114
115
|
<div class="header-text">
|
|
115
116
|
<h1>Dependency Radar</h1>
|
|
116
|
-
<
|
|
117
|
-
Project
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
<div class="header-meta">
|
|
118
|
+
<span class="meta-item"><span class="meta-label">Project</span> <strong id="project-path">${escapeHtml(data.project.projectDir)}</strong></span>
|
|
119
|
+
${((_c = data.git) === null || _c === void 0 ? void 0 : _c.branch) ? `<span class="meta-item"><span class="meta-label">Branch</span> <strong>${escapeHtml(data.git.branch)}</strong></span>` : ''}
|
|
120
|
+
${nodeVersionText ? `<span class="meta-item"><span class="meta-label">Node</span> <strong>${escapeHtml(nodeVersionText)}</strong></span>` : ''}
|
|
121
|
+
<span class="meta-item"><span class="meta-label">Generated</span> <strong id="formatted-date">${escapeHtml(formattedDate)}</strong></span>
|
|
122
|
+
${nodeDisclaimer ? `<span class="header-disclaimer">${escapeHtml(nodeDisclaimer)}</span>` : ''}
|
|
123
|
+
</div>
|
|
121
124
|
</div>
|
|
122
125
|
</div>
|
|
123
126
|
<div class="cta-section">
|
|
124
127
|
<a href="https://dependency-radar.com" class="cta-link" target="_blank" rel="noopener">
|
|
125
|
-
Get
|
|
128
|
+
Get full analysis report
|
|
126
129
|
<span class="cta-arrow">→</span>
|
|
127
130
|
</a>
|
|
131
|
+
<div class="cta-benefits">
|
|
132
|
+
<span>AI-powered risk summary for stakeholders</span>
|
|
133
|
+
<span>Charts & assets for presentations</span>
|
|
134
|
+
<span>Actionable upgrade recommendations</span>
|
|
135
|
+
</div>
|
|
128
136
|
<div class="cta-text">dependency-radar.com</div>
|
|
129
137
|
</div>
|
|
130
138
|
</div>
|
|
@@ -150,12 +158,13 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
150
158
|
</div>
|
|
151
159
|
|
|
152
160
|
<div class="filter-group">
|
|
153
|
-
<span class="filter-label">
|
|
161
|
+
<span class="filter-label">Scope</span>
|
|
154
162
|
<select id="runtime-filter">
|
|
155
163
|
<option value="all">All</option>
|
|
156
164
|
<option value="runtime">Runtime</option>
|
|
157
|
-
<option value="
|
|
158
|
-
<option value="
|
|
165
|
+
<option value="dev">Dev</option>
|
|
166
|
+
<option value="optional">Optional</option>
|
|
167
|
+
<option value="peer">Peer</option>
|
|
159
168
|
</select>
|
|
160
169
|
</div>
|
|
161
170
|
|
|
@@ -165,7 +174,6 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
165
174
|
<option value="name">Name</option>
|
|
166
175
|
<option value="severity">Severity</option>
|
|
167
176
|
<option value="depth">Depth</option>
|
|
168
|
-
<option value="size">Size</option>
|
|
169
177
|
</select>
|
|
170
178
|
<button type="button" class="sort-direction-btn" id="sort-direction" title="Toggle sort direction">↑</button>
|
|
171
179
|
</div>
|
|
@@ -180,11 +188,6 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
180
188
|
Has vulnerabilities
|
|
181
189
|
</label>
|
|
182
190
|
|
|
183
|
-
<label class="checkbox-filter">
|
|
184
|
-
<input type="checkbox" id="unused-only" />
|
|
185
|
-
Not statically imported
|
|
186
|
-
</label>
|
|
187
|
-
|
|
188
191
|
<div class="theme-toggle">
|
|
189
192
|
<span class="theme-toggle-label">Theme</span>
|
|
190
193
|
<div class="theme-switch" id="theme-switch" title="Toggle dark/light mode"></div>
|
|
@@ -227,13 +230,16 @@ ${report_assets_1.CSS_CONTENT}
|
|
|
227
230
|
</div>
|
|
228
231
|
</div>
|
|
229
232
|
|
|
230
|
-
${renderToolErrors(data)}
|
|
231
|
-
|
|
232
233
|
<!-- Main Content -->
|
|
233
234
|
<main class="main-content">
|
|
234
235
|
<div class="results-summary" id="results-summary"></div>
|
|
235
236
|
<div id="dependency-list" class="dependency-grid"></div>
|
|
236
237
|
</main>
|
|
238
|
+
|
|
239
|
+
<footer class="report-footer">
|
|
240
|
+
<p><strong>About this report</strong></p>
|
|
241
|
+
<p>Dependency Radar does not perform malware scanning or security auditing. It surfaces factual signals from dependency metadata, known vulnerabilities (npm audit), dependency graphs, and install-time behaviour to support informed review.</p>
|
|
242
|
+
</footer>
|
|
237
243
|
|
|
238
244
|
<script type="application/json" id="radar-data">${json}</script>
|
|
239
245
|
<script>
|
|
@@ -245,10 +251,3 @@ ${report_assets_1.JS_CONTENT}
|
|
|
245
251
|
function escapeHtml(str) {
|
|
246
252
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
247
253
|
}
|
|
248
|
-
function renderToolErrors(data) {
|
|
249
|
-
const entries = Object.entries(data.toolErrors || {});
|
|
250
|
-
if (!entries.length)
|
|
251
|
-
return '';
|
|
252
|
-
const list = entries.map(([tool, err]) => `<div><strong>${escapeHtml(tool)}:</strong> ${escapeHtml(err)}</div>`).join('');
|
|
253
|
-
return `<div class="tool-errors"><strong>Some tools failed:</strong>${list}</div>`;
|
|
254
|
-
}
|
|
@@ -19,6 +19,7 @@ async function runImportGraph(projectPath, tempDir) {
|
|
|
19
19
|
const files = await collectSourceFiles(entry);
|
|
20
20
|
const fileGraph = {};
|
|
21
21
|
const packageGraph = {};
|
|
22
|
+
const packageCounts = {};
|
|
22
23
|
const unresolvedImports = [];
|
|
23
24
|
for (const file of files) {
|
|
24
25
|
const rel = normalizePath(projectPath, file);
|
|
@@ -27,9 +28,10 @@ async function runImportGraph(projectPath, tempDir) {
|
|
|
27
28
|
const resolved = await resolveImports(imports, path_1.default.dirname(file), projectPath);
|
|
28
29
|
fileGraph[rel] = resolved.files;
|
|
29
30
|
packageGraph[rel] = resolved.packages;
|
|
31
|
+
packageCounts[rel] = resolved.packageCounts;
|
|
30
32
|
unresolvedImports.push(...resolved.unresolved.map((spec) => ({ importer: rel, specifier: spec })));
|
|
31
33
|
}
|
|
32
|
-
const output = { files: fileGraph, packages: packageGraph, unresolvedImports };
|
|
34
|
+
const output = { files: fileGraph, packages: packageGraph, packageCounts, unresolvedImports };
|
|
33
35
|
await (0, utils_1.writeJsonFile)(targetFile, output);
|
|
34
36
|
return { ok: true, data: output, file: targetFile };
|
|
35
37
|
}
|
|
@@ -62,7 +64,7 @@ async function collectSourceFiles(rootDir) {
|
|
|
62
64
|
return files;
|
|
63
65
|
}
|
|
64
66
|
function extractImports(content) {
|
|
65
|
-
const matches =
|
|
67
|
+
const matches = [];
|
|
66
68
|
const patterns = [
|
|
67
69
|
/\bimport\s+(?:[^'"]+from\s+)?['"]([^'"]+)['"]/g,
|
|
68
70
|
/\bexport\s+(?:[^'"]+from\s+)?['"]([^'"]+)['"]/g,
|
|
@@ -73,14 +75,15 @@ function extractImports(content) {
|
|
|
73
75
|
let match;
|
|
74
76
|
while ((match = pattern.exec(content)) !== null) {
|
|
75
77
|
if (match[1])
|
|
76
|
-
matches.
|
|
78
|
+
matches.push(match[1]);
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
|
-
return
|
|
81
|
+
return matches;
|
|
80
82
|
}
|
|
81
83
|
async function resolveImports(specifiers, fileDir, projectPath) {
|
|
82
84
|
const resolvedFiles = [];
|
|
83
85
|
const resolvedPackages = [];
|
|
86
|
+
const packageCounts = {};
|
|
84
87
|
const unresolved = [];
|
|
85
88
|
for (const spec of specifiers) {
|
|
86
89
|
if (isBuiltinModule(spec))
|
|
@@ -95,12 +98,15 @@ async function resolveImports(specifiers, fileDir, projectPath) {
|
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
|
-
|
|
101
|
+
const pkgName = toPackageName(spec);
|
|
102
|
+
resolvedPackages.push(pkgName);
|
|
103
|
+
packageCounts[pkgName] = (packageCounts[pkgName] || 0) + 1;
|
|
99
104
|
}
|
|
100
105
|
}
|
|
101
106
|
return {
|
|
102
107
|
files: uniqSorted(resolvedFiles),
|
|
103
108
|
packages: uniqSorted(resolvedPackages),
|
|
109
|
+
packageCounts,
|
|
104
110
|
unresolved: uniqSorted(unresolved)
|
|
105
111
|
};
|
|
106
112
|
}
|
package/dist/runners/npmAudit.js
CHANGED
|
@@ -3,29 +3,94 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.runPackageAudit = runPackageAudit;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const utils_1 = require("../utils");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
function normalizeAuditOutput(tool, data) {
|
|
10
|
+
var _a;
|
|
11
|
+
if (!data)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (data.vulnerabilities || data.advisories)
|
|
14
|
+
return data;
|
|
15
|
+
// Yarn (classic/berry) often emits JSONL with auditAdvisory entries.
|
|
16
|
+
const entries = Array.isArray(data) ? data : [data];
|
|
17
|
+
const advisories = {};
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
if (!entry || typeof entry !== "object")
|
|
20
|
+
continue;
|
|
21
|
+
if (entry.type === "auditAdvisory" && ((_a = entry.data) === null || _a === void 0 ? void 0 : _a.advisory)) {
|
|
22
|
+
const adv = entry.data.advisory;
|
|
23
|
+
const key = String(adv.id ||
|
|
24
|
+
adv.github_advisory_id ||
|
|
25
|
+
`${adv.module_name || adv.module}:${adv.title || "advisory"}`);
|
|
26
|
+
advisories[key] = adv;
|
|
16
27
|
}
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
}
|
|
29
|
+
if (Object.keys(advisories).length > 0) {
|
|
30
|
+
return { advisories };
|
|
31
|
+
}
|
|
32
|
+
if (tool === "pnpm" && data.result && data.advisories) {
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
function buildAuditCommand(tool, yarnVersion) {
|
|
38
|
+
if (tool === "pnpm") {
|
|
39
|
+
return {
|
|
40
|
+
cmd: "pnpm",
|
|
41
|
+
args: ["audit", "--json"],
|
|
42
|
+
lockFiles: ["pnpm-lock.yaml"],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (tool === "yarn") {
|
|
46
|
+
const major = yarnVersion
|
|
47
|
+
? Number.parseInt(yarnVersion.split(".")[0], 10)
|
|
48
|
+
: NaN;
|
|
49
|
+
if (!Number.isNaN(major) && major >= 2) {
|
|
50
|
+
return {
|
|
51
|
+
cmd: "yarn",
|
|
52
|
+
args: ["npm", "audit", "--all", "--json"],
|
|
53
|
+
lockFiles: ["yarn.lock"],
|
|
54
|
+
};
|
|
19
55
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
56
|
+
return { cmd: "yarn", args: ["audit", "--json"], lockFiles: ["yarn.lock"] };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
cmd: "npm",
|
|
60
|
+
args: ["audit", "--json"],
|
|
61
|
+
lockFiles: ["package-lock.json", "npm-shrinkwrap.json"],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function runPackageAudit(projectPath, tempDir, tool, yarnVersion) {
|
|
65
|
+
const targetFile = path_1.default.join(tempDir, `${tool}-audit.json`);
|
|
66
|
+
try {
|
|
67
|
+
const { cmd, args, lockFiles } = buildAuditCommand(tool, yarnVersion);
|
|
68
|
+
const lockDir = await (0, utils_1.findLockDir)(projectPath, lockFiles);
|
|
69
|
+
const cwd = lockDir || projectPath;
|
|
70
|
+
const result = await (0, utils_1.runCommand)(cmd, args, { cwd });
|
|
71
|
+
const parsed = (0, utils_1.parseJsonOutput)(result.stdout);
|
|
72
|
+
const normalized = normalizeAuditOutput(tool, parsed);
|
|
73
|
+
if (normalized) {
|
|
74
|
+
await (0, utils_1.writeJsonFile)(targetFile, normalized);
|
|
75
|
+
return { ok: true, data: normalized, file: targetFile };
|
|
23
76
|
}
|
|
24
|
-
await (0, utils_1.writeJsonFile)(targetFile, {
|
|
25
|
-
|
|
77
|
+
await (0, utils_1.writeJsonFile)(targetFile, {
|
|
78
|
+
stdout: result.stdout,
|
|
79
|
+
stderr: result.stderr,
|
|
80
|
+
code: result.code,
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
error: `Failed to parse ${tool} audit output`,
|
|
85
|
+
file: targetFile,
|
|
86
|
+
};
|
|
26
87
|
}
|
|
27
88
|
catch (err) {
|
|
28
89
|
await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
|
|
29
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: `${tool} audit failed: ${String(err)}`,
|
|
93
|
+
file: targetFile,
|
|
94
|
+
};
|
|
30
95
|
}
|
|
31
96
|
}
|