@@ -22,10 +22,6 @@ class SBOM extends BaseCommand {
2222 'workspaces' ,
2323 ]
2424
25- get #parsedResponse ( ) {
26- return JSON . stringify ( this . #response, null , 2 )
27- }
28-
2925 async exec ( ) {
3026 const sbomFormat = this . npm . config . get ( 'sbom-format' )
3127 const packageLockOnly = this . npm . config . get ( 'package-lock-only' )
@@ -35,56 +31,43 @@ class SBOM extends BaseCommand {
3531 throw this . usageError ( `Must specify --sbom-format flag with one of: ${ SBOM_FORMATS . join ( ', ' ) } .` )
3632 }
3733
38- const Arborist = require ( '@npmcli/arborist' )
39-
4034 const opts = {
4135 ...this . npm . flatOptions ,
4236 path : this . npm . prefix ,
4337 forceActual : true ,
4438 }
39+ const Arborist = require ( '@npmcli/arborist' )
4540 const arb = new Arborist ( opts )
4641
47- let tree
48- if ( packageLockOnly ) {
49- try {
50- tree = await arb . loadVirtual ( opts )
51- } catch ( err ) {
52- /* eslint-disable-next-line max-len */
53- throw this . usageError ( 'A package lock or shrinkwrap file is required in package-lock-only mode' )
54- }
55- } else {
56- tree = await arb . loadActual ( opts )
57- }
42+ const tree = packageLockOnly ? await arb . loadVirtual ( opts ) . catch ( ( ) => {
43+ /* eslint-disable-next-line max-len */
44+ throw this . usageError ( 'A package lock or shrinkwrap file is required in package-lock-only mode' )
45+ } ) : await arb . loadActual ( opts )
5846
5947 // Collect the list of selected workspaces in the project
60- let wsNodes
61- if ( this . workspaceNames && this . workspaceNames . length ) {
62- wsNodes = arb . workspaceNodes ( tree , this . workspaceNames )
63- }
48+ const wsNodes = this . workspaceNames ?. length
49+ ? arb . workspaceNodes ( tree , this . workspaceNames )
50+ : null
6451
6552 // Build the selector and query the tree for the list of nodes
6653 const selector = this . #buildSelector( { wsNodes } )
6754 log . info ( 'sbom' , `Using dependency selector: ${ selector } ` )
6855 const items = await tree . querySelectorAll ( selector )
6956
70- const errors = new Set ( )
71- for ( const node of items ) {
72- detectErrors ( node ) . forEach ( error => errors . add ( error ) )
73- }
74-
75- if ( errors . size > 0 ) {
76- throw Object . assign (
77- new Error ( [ ...errors ] . join ( '\n' ) ) ,
78- { code : 'ESBOMPROBLEMS' }
79- )
57+ const errors = items . flatMap ( node => detectErrors ( node ) )
58+ if ( errors . length ) {
59+ throw Object . assign ( new Error ( [ ...new Set ( errors ) ] . join ( '\n' ) ) , {
60+ code : 'ESBOMPROBLEMS' ,
61+ } )
8062 }
8163
8264 // Populate the response with the list of unique nodes (sorted by location)
83- this . #buildResponse(
84- items
85- . sort ( ( a , b ) => localeCompare ( a . location , b . location ) )
86- )
87- output . standard ( this . #parsedResponse)
65+ this . #buildResponse( items . sort ( ( a , b ) => localeCompare ( a . location , b . location ) ) )
66+
67+ // TODO(BREAKING_CHANGE): all sbom output is in json mode but setting it before
68+ // any of the errors will cause those to be thrown in json mode.
69+ this . npm . config . set ( 'json' , true )
70+ output . buffer ( this . #response)
8871 }
8972
9073 async execWorkspaces ( args ) {
@@ -122,10 +105,9 @@ class SBOM extends BaseCommand {
122105 const packageType = this . npm . config . get ( 'sbom-type' )
123106 const packageLockOnly = this . npm . config . get ( 'package-lock-only' )
124107
125- this . #response =
126- sbomFormat === 'cyclonedx'
127- ? cyclonedxOutput ( { npm : this . npm , nodes : items , packageType, packageLockOnly } )
128- : spdxOutput ( { npm : this . npm , nodes : items , packageType } )
108+ this . #response = sbomFormat === 'cyclonedx'
109+ ? cyclonedxOutput ( { npm : this . npm , nodes : items , packageType, packageLockOnly } )
110+ : spdxOutput ( { npm : this . npm , nodes : items , packageType } )
129111 }
130112}
131113
0 commit comments