@@ -181,6 +181,30 @@ function parseJsonLines(result: string): Promise<{ dependencies: Index<ParsedDep
181
181
} )
182
182
}
183
183
184
+ /**
185
+ * Extract first json line from muli line yarn output
186
+ *
187
+ * @param result Output from yarn command to be parsed
188
+ */
189
+ function extractFirstJsonLine ( result : string ) : Promise < string > {
190
+ return new Promise ( ( resolve , reject ) => {
191
+ const parser = jsonlines . parse ( )
192
+ let firstFound = false
193
+
194
+ parser . on ( 'data' , value => {
195
+ if ( ! firstFound ) {
196
+ firstFound = true
197
+ resolve ( JSON . stringify ( value ) )
198
+ }
199
+ } )
200
+ parser . on ( 'error' , reject )
201
+
202
+ parser . write ( result )
203
+
204
+ parser . end ( )
205
+ } )
206
+ }
207
+
184
208
const cmd = process . platform === 'win32' ? 'yarn.cmd' : 'yarn'
185
209
186
210
/**
@@ -297,20 +321,49 @@ export const semver = withNpmConfigFromYarn(npm.semver)
297
321
*
298
322
* @param packageName
299
323
* @param version
324
+ * @param spawnOptions
300
325
* @returns Promised {packageName: version} collection
301
326
*/
302
- export const getPeerDependencies = async ( packageName : string , version : Version ) : Promise < Index < Version > > => {
303
- const { stdout : yarnVersion } = await spawn ( cmd , [ '--version' ] , { rejectOnError : false } , { } )
327
+ export const getPeerDependencies = async (
328
+ packageName : string ,
329
+ version : Version ,
330
+ spawnOptions : SpawnOptions ,
331
+ ) : Promise < Index < Version > > => {
332
+ const { stdout : yarnVersion } = await spawn ( cmd , [ '--version' ] , { rejectOnError : false } , spawnOptions )
304
333
if ( yarnVersion . startsWith ( '1' ) ) {
305
334
const args = [ '--json' , 'info' , `${ packageName } @${ version } ` , 'peerDependencies' ]
306
- const { stdout } = await spawn ( cmd , args , { rejectOnError : false } , { } )
335
+ const { stdout } = await spawn ( cmd , args , { rejectOnError : false } , spawnOptions )
307
336
return stdout ? npm . parseJson < { data ?: Index < Version > } > ( stdout , { command : args . join ( ' ' ) } ) . data || { } : { }
308
337
} else {
309
338
const args = [ '--json' , 'npm' , 'info' , `${ packageName } @${ version } ` , '--fields' , 'peerDependencies' ]
310
- const { stdout } = await spawn ( cmd , args , { rejectOnError : false } , { } )
311
- return stdout
312
- ? npm . parseJson < { peerDependencies ?: Index < Version > } > ( stdout , { command : args . join ( ' ' ) } ) . peerDependencies || { }
313
- : { }
339
+ const { stdout } = await spawn ( cmd , args , { rejectOnError : false } , spawnOptions )
340
+ if ( ! stdout ) {
341
+ return { }
342
+ }
343
+ try {
344
+ return (
345
+ npm . parseJson < { peerDependencies ?: Index < Version > } > ( stdout , { command : args . join ( ' ' ) } ) . peerDependencies || { }
346
+ )
347
+ } catch ( parseError ) {
348
+ /*
349
+ If package does not exist, yarn returns multiple json errors. As such, we want to extract just the first one, instead of crashing.
350
+ Example response:
351
+ {"type":"error","name":35,"displayName":"YN0035","indent":"","data":"Package not found"}
352
+ {"type":"error","name":35,"displayName":"YN0035","indent":"","data":" \u001b[96mResponse Code\u001b[39m: \u001b]8;;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404\u0007\u001b[93m404\u001b[39m (Not Found)\u001b]8;;\u0007"}
353
+ {"type":"error","name":35,"displayName":"YN0035","indent":"","data":" \u001b[96mRequest Method\u001b[39m: GET"}
354
+ {"type":"error","name":35,"displayName":"YN0035","indent":"","data":" \u001b[96mRequest URL\u001b[39m: \u001b[95mhttps://registry.yarnpkg.com/fffffffffffff\u001b[39m"}
355
+ */
356
+ try {
357
+ const firstObj = await extractFirstJsonLine ( stdout )
358
+ if ( firstObj ) {
359
+ return (
360
+ npm . parseJson < { peerDependencies ?: Index < Version > } > ( firstObj , { command : args . join ( ' ' ) } )
361
+ . peerDependencies || { }
362
+ )
363
+ }
364
+ } catch { }
365
+ throw parseError
366
+ }
314
367
}
315
368
}
316
369
0 commit comments