@@ -82,26 +82,31 @@ const ERROR_KEY = 'error'
82
82
// is a json error that should be merged into the finished output
83
83
const JSON_ERROR_KEY = 'jsonError'
84
84
85
+ const isPlainObject = ( v ) => v && typeof v === 'object' && ! Array . isArray ( v )
86
+
85
87
const getArrayOrObject = ( items ) => {
86
- // Arrays cant be merged, if the first item is an array return that
87
- if ( Array . isArray ( items [ 0 ] ) ) {
88
- return items [ 0 ]
89
- }
90
- // We use objects with 0,1,2,etc keys to merge array
91
- if ( items . length && items . every ( ( o , i ) => Object . hasOwn ( o , i ) ) ) {
92
- return Object . assign ( [ ] , ...items )
88
+ if ( items . length ) {
89
+ const foundNonObject = items . find ( o => ! isPlainObject ( o ) )
90
+ // Non-objects and arrays cant be merged, so just return the first item
91
+ if ( foundNonObject ) {
92
+ return foundNonObject
93
+ }
94
+ // We use objects with 0,1,2,etc keys to merge array
95
+ if ( items . every ( ( o , i ) => Object . hasOwn ( o , i ) ) ) {
96
+ return Object . assign ( [ ] , ...items )
97
+ }
93
98
}
94
- // Otherwise its an object with all items merged together
95
- return Object . assign ( { } , ...items )
99
+ // Otherwise its an object with all object items merged together
100
+ return Object . assign ( { } , ...items . filter ( o => isPlainObject ( o ) ) )
96
101
}
97
102
98
- const mergeJson = ( { [ JSON_ERROR_KEY ] : metaError } , buffer ) => {
103
+ const getJsonBuffer = ( { [ JSON_ERROR_KEY ] : metaError } , buffer ) => {
99
104
const items = [ ]
100
105
// meta also contains the meta object passed to flush
101
106
const errors = metaError ? [ metaError ] : [ ]
102
107
// index 1 is the meta, 2 is the logged argument
103
108
for ( const [ , { [ JSON_ERROR_KEY ] : error } , obj ] of buffer ) {
104
- if ( obj && typeof obj === 'object' ) {
109
+ if ( obj ) {
105
110
items . push ( obj )
106
111
}
107
112
if ( error ) {
@@ -113,13 +118,12 @@ const mergeJson = ({ [JSON_ERROR_KEY]: metaError }, buffer) => {
113
118
return null
114
119
}
115
120
116
- // If all items are keyed with array indexes, then we return the
117
- // array. This skips any error checking since we cant really set
118
- // an error property on an array in a way that can be stringified
119
- // XXX(BREAKING_CHANGE): remove this in favor of always returning an object
120
121
const res = getArrayOrObject ( items )
121
122
122
- if ( ! Array . isArray ( res ) && errors . length ) {
123
+ // This skips any error checking since we can only set an error property
124
+ // on an object that can be stringified
125
+ // XXX(BREAKING_CHANGE): remove this in favor of always returning an object with result and error keys
126
+ if ( isPlainObject ( res ) && errors . length ) {
123
127
// This is not ideal. JSON output has always been keyed at the root with an `error`
124
128
// key, so we cant change that without it being a breaking change. At the same time
125
129
// some commands output arbitrary keys at the top level of the output, such as package
@@ -292,9 +296,11 @@ class Display {
292
296
switch ( level ) {
293
297
case output . KEYS . flush : {
294
298
this . #outputState. buffering = false
295
- const json = this . #json ? mergeJson ( meta , this . #outputState. buffer ) : null
296
- if ( json ) {
297
- this . #writeOutput( output . KEYS . standard , meta , JSON . stringify ( json , null , 2 ) )
299
+ if ( this . #json) {
300
+ const json = getJsonBuffer ( meta , this . #outputState. buffer )
301
+ if ( json ) {
302
+ this . #writeOutput( output . KEYS . standard , meta , JSON . stringify ( json , null , 2 ) )
303
+ }
298
304
} else {
299
305
this . #outputState. buffer . forEach ( ( item ) => this . #writeOutput( ...item ) )
300
306
}
0 commit comments