@@ -21,9 +21,9 @@ export const info = {
21
21
22
22
const input = setupCommand ( name , info . description , argv , importMeta )
23
23
if ( input ) {
24
- const spinnerText = input . pkgVersion === 'latest' ? `Looking up data for the latest version of ${ input . pkgName } \n` : `Looking up data for version ${ input . pkgVersion } of ${ input . pkgName } \n`
24
+ const spinnerText = `Looking up data for packages: ${ input . packages . join ( ', ' ) } \n`
25
25
const spinner = ora ( spinnerText ) . start ( )
26
- const packageData = await fetchPackageData ( input . ecosystem , input . pkgName , input . pkgVersion , input , spinner )
26
+ const packageData = await fetchPackageData ( input . packages , input . includeAlerts , spinner )
27
27
if ( packageData ) {
28
28
formatPackageDataOutput ( packageData , { name, ...input } , spinner )
29
29
}
@@ -32,12 +32,14 @@ export const info = {
32
32
}
33
33
34
34
const infoFlags = prepareFlags ( {
35
- license : {
36
- type : 'boolean' ,
37
- shortFlag : 'l' ,
38
- default : false ,
39
- description : 'Include license - Default is false' ,
40
- } ,
35
+ // At the moment in API v0, alerts and license do the same thing.
36
+ // The license parameter will be implemented later.
37
+ // license: {
38
+ // type: 'boolean',
39
+ // shortFlag: 'l',
40
+ // default: false,
41
+ // description: 'Include license - Default is false',
42
+ // },
41
43
alerts : {
42
44
type : 'boolean' ,
43
45
shortFlag : 'a' ,
@@ -53,11 +55,8 @@ const infoFlags = prepareFlags({
53
55
* @property {boolean } includeAlerts
54
56
* @property {boolean } outputJson
55
57
* @property {boolean } outputMarkdown
56
- * @property {string } pkgName
57
- * @property {string } pkgVersion
58
+ * @property {string[] } packages
58
59
* @property {boolean } strict
59
- * @property {string } ecosystem
60
- * @property {boolean } includeLicense
61
60
*/
62
61
63
62
/**
@@ -76,14 +75,14 @@ function setupCommand (name, description, argv, importMeta) {
76
75
77
76
const cli = meow ( `
78
77
Usage
79
- $ ${ name } <ecosystem> <name>
78
+ $ ${ name } <ecosystem>: <name>@<version >
80
79
81
80
Options
82
81
${ printFlagList ( flags , 6 ) }
83
82
84
83
Examples
85
- $ ${ name } npm webtorrent
86
-
84
+ $ ${ name } npm: webtorrent
85
+
87
86
` , {
88
87
argv,
89
88
description,
@@ -93,75 +92,85 @@ function setupCommand (name, description, argv, importMeta) {
93
92
94
93
const {
95
94
alerts : includeAlerts ,
96
- license : includeLicense ,
97
95
json : outputJson ,
98
96
markdown : outputMarkdown ,
99
97
strict,
100
98
} = cli . flags
101
99
102
- const [ ecosystem = '' , rawPkgName = '' ] = cli . input
100
+ const [ rawPkgName = '' ] = cli . input
103
101
104
- if ( ! ecosystem && ! rawPkgName ) {
105
- console . error ( 'Please provide an ecosystem and a package name' )
102
+ if ( ! rawPkgName ) {
103
+ console . error ( 'Please provide an ecosystem and package name' )
106
104
cli . showHelp ( )
107
105
return
108
106
}
109
107
110
- const versionSeparator = rawPkgName . lastIndexOf ( '@' )
108
+ const /** @type { string[] } */ inputPkgs = [ ]
111
109
112
- const pkgName = versionSeparator < 1 ? rawPkgName : rawPkgName . slice ( 0 , versionSeparator )
113
- const pkgVersion = versionSeparator < 1 ? 'latest' : rawPkgName . slice ( versionSeparator + 1 )
110
+ cli . input . map ( pkg => {
111
+ const ecosystem = pkg . split ( ':' ) [ 0 ]
112
+ if ( ! ecosystem ) {
113
+ console . error ( `Package name ${ pkg } formatted incorrectly.` )
114
+ return cli . showHelp ( )
115
+ } else {
116
+ const versionSeparator = pkg . lastIndexOf ( '@' )
117
+ const ecosystemSeparator = pkg . lastIndexOf ( ecosystem )
118
+ const pkgName = versionSeparator < 1 ? pkg . slice ( ecosystemSeparator + ecosystem . length + 1 ) : pkg . slice ( ecosystemSeparator + ecosystem . length + 1 , versionSeparator )
119
+ const pkgVersion = versionSeparator < 1 ? 'latest' : pkg . slice ( versionSeparator + 1 )
120
+ inputPkgs . push ( `${ ecosystem } /${ pkgName } @${ pkgVersion } ` )
121
+ }
122
+ return inputPkgs
123
+ } )
114
124
115
125
return {
116
126
includeAlerts,
117
- includeLicense,
118
127
outputJson,
119
128
outputMarkdown,
120
- pkgName,
121
- pkgVersion,
129
+ packages : inputPkgs ,
122
130
strict,
123
- ecosystem
124
131
}
125
132
}
126
133
127
134
/**
128
135
* @typedef PackageData
129
136
* @property {import('@socketsecurity/sdk').SocketSdkReturnType<'batchPackageFetch'>["data"] } data
130
- * @property {Record<import('../../utils/format-issues.js').SocketIssue['severity'], number> | undefined } severityCount
131
137
*/
132
138
133
139
/**
134
- * @param {string } ecosystem
135
- * @param {string } pkgName
136
- * @param {string } pkgVersion
137
- * @param {Pick<CommandContext, 'includeAlerts' | 'includeLicense'> } context
140
+ * @param {string[] } packages
141
+ * @param {boolean } includeAlerts
138
142
* @param {import('ora').Ora } spinner
139
143
* @returns {Promise<void|PackageData> }
140
144
*/
141
- async function fetchPackageData ( ecosystem , pkgName , pkgVersion , { includeAlerts, includeLicense } , spinner ) {
145
+ async function fetchPackageData ( packages , includeAlerts , spinner ) {
142
146
const socketSdk = await setupSdk ( getDefaultKey ( ) || FREE_API_KEY )
147
+
148
+ const components = packages . map ( pkg => {
149
+ return { 'purl' : `pkg:${ pkg } ` }
150
+ } )
151
+
143
152
// @ts -ignore
144
153
const result = await handleApiCall ( socketSdk . batchPackageFetch (
145
- { license : includeLicense . toString ( ) , alerts : includeAlerts . toString ( ) } ,
154
+ { alerts : includeAlerts . toString ( ) } ,
146
155
{
147
- components :
148
- [ {
149
- 'purl' : `pkg:${ ecosystem } /${ pkgName } @${ pkgVersion } `
150
- } ]
156
+ components
151
157
} ) , 'looking up package' )
152
158
153
159
if ( ! result . success ) {
154
160
return handleUnsuccessfulApiResponse ( 'batchPackageFetch' , result , spinner )
155
161
}
156
162
157
163
// @ts -ignore
158
- const severityCount = result . data . alerts && getCountSeverity ( result . data . alerts , includeAlerts ? undefined : 'high' )
164
+ result . data . map ( pkg => {
165
+ const severityCount = pkg . alerts && getCountSeverity ( pkg . alerts , includeAlerts ? undefined : 'high' )
166
+ pkg . severityCount = severityCount
167
+ return pkg
168
+ } )
159
169
160
170
spinner . stop ( )
161
171
162
172
return {
163
- data : result . data ,
164
- severityCount
173
+ data : result . data
165
174
}
166
175
}
167
176
@@ -171,52 +180,57 @@ async function fetchPackageData (ecosystem, pkgName, pkgVersion, { includeAlerts
171
180
* @param {import('ora').Ora } spinner
172
181
* @returns {void }
173
182
*/
174
- function formatPackageDataOutput ( /** @type {{ [key: string]: any } } */ { data, severityCount } , { name , outputJson, outputMarkdown, pkgName , pkgVersion , strict } , spinner ) {
183
+ function formatPackageDataOutput ( /** @type {{ [key: string]: any } } */ { data } , { outputJson, outputMarkdown, strict } , spinner ) {
175
184
if ( outputJson ) {
176
185
console . log ( JSON . stringify ( data , undefined , 2 ) )
177
186
} else {
178
- console . log ( '\nPackage metrics:' )
179
-
180
- const scoreResult = {
181
- 'Supply Chain Risk' : Math . floor ( data . score . supplyChain * 100 ) ,
182
- 'Maintenance' : Math . floor ( data . score . maintenance * 100 ) ,
183
- 'Quality' : Math . floor ( data . score . quality * 100 ) ,
184
- 'Vulnerabilities' : Math . floor ( data . score . vulnerability * 100 ) ,
185
- 'License' : Math . floor ( data . score . license * 100 ) ,
186
- 'Overall' : Math . floor ( data . score . overall * 100 )
187
- }
188
- Object . entries ( scoreResult ) . map ( score => console . log ( `- ${ score [ 0 ] } : ${ formatScore ( score [ 1 ] ) } ` ) )
189
-
190
- // Package license
191
- console . log ( '\nPackage license:' )
192
- console . log ( `${ data . license } ` )
193
-
194
- // Package issues list
195
- if ( objectSome ( severityCount ) ) {
196
- const issueSummary = formatSeverityCount ( severityCount )
197
- console . log ( '\n' )
198
- spinner [ strict ? 'fail' : 'succeed' ] ( `Package has these issues: ${ issueSummary } ` )
199
- formatPackageIssuesDetails ( data . alerts , outputMarkdown )
200
- } else if ( severityCount && ! objectSome ( severityCount ) ) {
201
- console . log ( '\n' )
202
- spinner . succeed ( 'Package has no issues' )
203
- }
187
+ data . map ( ( /** @type {{[key:string]: any} } */ d ) => {
188
+ const { score, license, name, severityCount, version } = d
189
+ console . log ( `\nPackage metrics for ${ name } :` )
190
+
191
+ const scoreResult = {
192
+ 'Supply Chain Risk' : Math . floor ( score . supplyChain * 100 ) ,
193
+ 'Maintenance' : Math . floor ( score . maintenance * 100 ) ,
194
+ 'Quality' : Math . floor ( score . quality * 100 ) ,
195
+ 'Vulnerabilities' : Math . floor ( score . vulnerability * 100 ) ,
196
+ 'License' : Math . floor ( score . license * 100 ) ,
197
+ 'Overall' : Math . floor ( score . overall * 100 )
198
+ }
204
199
205
- // Link to issues list
206
- const format = new ChalkOrMarkdown ( ! ! outputMarkdown )
207
- const url = `https://socket.dev/npm/package/${ pkgName } /overview/${ pkgVersion } `
208
- if ( pkgVersion === 'latest' ) {
209
- console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ pkgName } ` , url , { fallbackToUrl : true } ) )
210
- } else {
211
- console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ pkgName } v${ pkgVersion } ` , url , { fallbackToUrl : true } ) )
212
- }
213
- if ( ! outputMarkdown ) {
214
- console . log ( chalk . dim ( '\nOr rerun' , chalk . italic ( name ) , 'using the' , chalk . italic ( '--json' ) , 'flag to get full JSON output' ) )
215
- }
216
- }
200
+ Object . entries ( scoreResult ) . map ( score => console . log ( `- ${ score [ 0 ] } : ${ formatScore ( score [ 1 ] ) } ` ) )
201
+
202
+ // Package license
203
+ console . log ( '\nPackage license:' )
204
+ console . log ( `${ license } ` )
205
+
206
+ // Package issues list
207
+ if ( objectSome ( severityCount ) ) {
208
+ const issueSummary = formatSeverityCount ( severityCount )
209
+ console . log ( '\n' )
210
+ spinner [ strict ? 'fail' : 'succeed' ] ( `Package has these issues: ${ issueSummary } ` )
211
+ formatPackageIssuesDetails ( data . alerts , outputMarkdown )
212
+ } else if ( severityCount && ! objectSome ( severityCount ) ) {
213
+ console . log ( '\n' )
214
+ spinner . succeed ( 'Package has no issues' )
215
+ }
217
216
218
- if ( strict && objectSome ( severityCount ) ) {
219
- process . exit ( 1 )
217
+ // Link to issues list
218
+ const format = new ChalkOrMarkdown ( ! ! outputMarkdown )
219
+ const url = `https://socket.dev/npm/package/${ name } /overview/${ version } `
220
+ if ( version === 'latest' ) {
221
+ console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ name } ` , url , { fallbackToUrl : true } ) )
222
+ } else {
223
+ console . log ( '\nDetailed info on socket.dev: ' + format . hyperlink ( `${ name } v${ version } ` , url , { fallbackToUrl : true } ) )
224
+ }
225
+ if ( ! outputMarkdown ) {
226
+ console . log ( chalk . dim ( '\nOr rerun' , chalk . italic ( name ) , 'using the' , chalk . italic ( '--json' ) , 'flag to get full JSON output' ) )
227
+ }
228
+
229
+ if ( strict && objectSome ( severityCount ) ) {
230
+ process . exit ( 1 )
231
+ }
232
+ return d
233
+ } )
220
234
}
221
235
}
222
236
0 commit comments