@@ -20,10 +20,12 @@ export const info = {
20
20
const name = parentName + ' info'
21
21
22
22
const input = setupCommand ( name , info . description , argv , importMeta )
23
- const packageData = input && await fetchPackageData ( input . pkgName , input . pkgVersion , input )
24
-
25
- if ( packageData ) {
26
- formatPackageDataOutput ( packageData , { name, ...input } )
23
+ if ( input ) {
24
+ const spinner = ora ( `Looking up data for version ${ input . pkgVersion } of ${ input . pkgName } \n` ) . start ( )
25
+ const packageData = await fetchPackageData ( input . pkgName , input . pkgVersion , input , spinner )
26
+ if ( packageData ) {
27
+ formatPackageDataOutput ( packageData , { name, ...input } , spinner )
28
+ }
27
29
}
28
30
}
29
31
}
@@ -121,12 +123,12 @@ function setupCommand (name, description, argv, importMeta) {
121
123
/**
122
124
* @param {string } pkgName
123
125
* @param {string } pkgVersion
124
- * @param {Pick<CommandContext, 'includeAllIssues' | 'strict'> } context
126
+ * @param {Pick<CommandContext, 'includeAllIssues'> } context
127
+ * @param {import('ora').Ora } spinner
125
128
* @returns {Promise<void|PackageData> }
126
129
*/
127
- async function fetchPackageData ( pkgName , pkgVersion , { includeAllIssues, strict } ) {
130
+ async function fetchPackageData ( pkgName , pkgVersion , { includeAllIssues } , spinner ) {
128
131
const socketSdk = await setupSdk ( getDefaultKey ( ) || FREE_API_KEY )
129
- const spinner = ora ( `Looking up data for version ${ pkgVersion } of ${ pkgName } ` ) . start ( )
130
132
const result = await handleApiCall ( socketSdk . getIssuesByNPMPackage ( pkgName , pkgVersion ) , 'looking up package' )
131
133
const scoreResult = await handleApiCall ( socketSdk . getScoreByNPMPackage ( pkgName , pkgVersion ) , 'looking up package score' )
132
134
@@ -139,16 +141,8 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
139
141
}
140
142
141
143
// Conclude the status of the API call
142
-
143
144
const severityCount = getSeverityCount ( result . data , includeAllIssues ? undefined : 'high' )
144
145
145
- if ( objectSome ( severityCount ) ) {
146
- const issueSummary = formatSeverityCount ( severityCount )
147
- spinner [ strict ? 'fail' : 'succeed' ] ( `Package has these issues: ${ issueSummary } ` )
148
- } else {
149
- spinner . succeed ( 'Package has no issues' )
150
- }
151
-
152
146
return {
153
147
data : result . data ,
154
148
severityCount,
@@ -159,14 +153,14 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
159
153
/**
160
154
* @param {PackageData } packageData
161
155
* @param {{ name: string } & CommandContext } context
156
+ * @param {import('ora').Ora } spinner
162
157
* @returns {void }
163
158
*/
164
- function formatPackageDataOutput ( { data, severityCount, score } , { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict } ) {
159
+ function formatPackageDataOutput ( { data, severityCount, score } , { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict } , spinner ) {
165
160
if ( outputJson ) {
166
161
console . log ( JSON . stringify ( data , undefined , 2 ) )
167
162
} else {
168
- console . log ( '\nPackage report card:\n' )
169
-
163
+ console . log ( '\nPackage report card:' )
170
164
const scoreResult = {
171
165
'Supply Chain Risk' : Math . floor ( score . supplyChainRisk . score * 100 ) ,
172
166
'Maintenance' : Math . floor ( score . maintenance . score * 100 ) ,
@@ -176,9 +170,20 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
176
170
}
177
171
Object . entries ( scoreResult ) . map ( score => console . log ( `- ${ score [ 0 ] } : ${ formatScore ( score [ 1 ] ) } ` ) )
178
172
173
+ // Package issues list
174
+ if ( objectSome ( severityCount ) ) {
175
+ const issueSummary = formatSeverityCount ( severityCount )
176
+ console . log ( '\n' )
177
+ spinner [ strict ? 'fail' : 'succeed' ] ( `Package has these issues: ${ issueSummary } ` )
178
+ formatPackageIssuesDetails ( data )
179
+ } else {
180
+ console . log ( '\n' )
181
+ spinner . succeed ( 'Package has no issues' )
182
+ }
183
+
184
+ // Link to issues list
179
185
const format = new ChalkOrMarkdown ( ! ! outputMarkdown )
180
186
const url = `https://socket.dev/npm/package/${ pkgName } /overview/${ pkgVersion } `
181
-
182
187
console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ pkgName } v${ pkgVersion } ` , url , { fallbackToUrl : true } ) )
183
188
if ( ! outputMarkdown ) {
184
189
console . log ( chalk . dim ( '\nOr rerun' , chalk . italic ( name ) , 'using the' , chalk . italic ( '--json' ) , 'flag to get full JSON output' ) )
@@ -190,6 +195,31 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
190
195
}
191
196
}
192
197
198
+ /**
199
+ * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getIssuesByNPMPackage'>["data"] } packageData
200
+ * @returns {void[] }
201
+ */
202
+ function formatPackageIssuesDetails ( packageData ) {
203
+ const issueDetails = packageData . filter ( d => d . value ?. severity === 'high' || d . value ?. severity === 'critical' )
204
+ const uniqueIssues = issueDetails . reduce ( ( /** @type {{ [key: string]: number } } */ acc , issue ) => {
205
+ const { type } = issue
206
+ if ( type ) {
207
+ if ( ! acc [ type ] ) {
208
+ acc [ type ] = 1
209
+ } else {
210
+ acc [ type ] ++
211
+ }
212
+ }
213
+ return acc
214
+ } , { } )
215
+ return Object . keys ( uniqueIssues ) . map ( issue => {
216
+ if ( uniqueIssues [ issue ] === 1 ) {
217
+ return console . log ( `- ${ issue } ` )
218
+ }
219
+ return console . log ( `- ${ issue } : ${ uniqueIssues [ issue ] } ` )
220
+ } )
221
+ }
222
+
193
223
/**
194
224
* @param {number } score
195
225
* @returns {string }
0 commit comments