@@ -20,10 +20,12 @@ export const info = {
2020 const name = parentName + ' info'
2121
2222 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+ }
2729 }
2830 }
2931}
@@ -121,12 +123,12 @@ function setupCommand (name, description, argv, importMeta) {
121123/**
122124 * @param {string } pkgName
123125 * @param {string } pkgVersion
124- * @param {Pick<CommandContext, 'includeAllIssues' | 'strict'> } context
126+ * @param {Pick<CommandContext, 'includeAllIssues'> } context
127+ * @param {import('ora').Ora } spinner
125128 * @returns {Promise<void|PackageData> }
126129 */
127- async function fetchPackageData ( pkgName , pkgVersion , { includeAllIssues, strict } ) {
130+ async function fetchPackageData ( pkgName , pkgVersion , { includeAllIssues } , spinner ) {
128131 const socketSdk = await setupSdk ( getDefaultKey ( ) || FREE_API_KEY )
129- const spinner = ora ( `Looking up data for version ${ pkgVersion } of ${ pkgName } ` ) . start ( )
130132 const result = await handleApiCall ( socketSdk . getIssuesByNPMPackage ( pkgName , pkgVersion ) , 'looking up package' )
131133 const scoreResult = await handleApiCall ( socketSdk . getScoreByNPMPackage ( pkgName , pkgVersion ) , 'looking up package score' )
132134
@@ -139,16 +141,8 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
139141 }
140142
141143 // Conclude the status of the API call
142-
143144 const severityCount = getSeverityCount ( result . data , includeAllIssues ? undefined : 'high' )
144145
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-
152146 return {
153147 data : result . data ,
154148 severityCount,
@@ -159,14 +153,14 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
159153/**
160154 * @param {PackageData } packageData
161155 * @param {{ name: string } & CommandContext } context
156+ * @param {import('ora').Ora } spinner
162157 * @returns {void }
163158 */
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 ) {
165160 if ( outputJson ) {
166161 console . log ( JSON . stringify ( data , undefined , 2 ) )
167162 } else {
168- console . log ( '\nPackage report card:\n' )
169-
163+ console . log ( '\nPackage report card:' )
170164 const scoreResult = {
171165 'Supply Chain Risk' : Math . floor ( score . supplyChainRisk . score * 100 ) ,
172166 'Maintenance' : Math . floor ( score . maintenance . score * 100 ) ,
@@ -176,9 +170,20 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict
176170 }
177171 Object . entries ( scoreResult ) . map ( score => console . log ( `- ${ score [ 0 ] } : ${ formatScore ( score [ 1 ] ) } ` ) )
178172
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
179185 const format = new ChalkOrMarkdown ( ! ! outputMarkdown )
180186 const url = `https://socket.dev/npm/package/${ pkgName } /overview/${ pkgVersion } `
181-
182187 console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ pkgName } v${ pkgVersion } ` , url , { fallbackToUrl : true } ) )
183188 if ( ! outputMarkdown ) {
184189 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
190195 }
191196}
192197
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+
193223/**
194224 * @param {number } score
195225 * @returns {string }
0 commit comments