@@ -261,6 +261,7 @@ class PkgDepResolver {
261261 private _depthResolving : DepthResolving | undefined ;
262262 private _localsByDepth ?: DepItem [ ] [ ] ;
263263 private _buildLocal ?: LocalPkgBuilder ;
264+ private _peerDepWarnings : Map < string , Set < string > > ;
264265
265266 constructor ( pkg : PackageJson , options : PkgDepResolverOptions ) {
266267 this . _options = Object . assign ( { } , options ) ;
@@ -289,6 +290,7 @@ class PkgDepResolver {
289290 this . _optResolver . _depResolver = this ;
290291 this . _promiseQ . on ( "empty" , ( ) => this . checkOptResolver ( ) ) ;
291292 this . _lockOnly = this . _fyn . lockOnly ;
293+ this . _peerDepWarnings = new Map ( ) ;
292294 //
293295 // We have to resolve each package in the order they were seen
294296 // through the dep tree because in case an earlier resolved version
@@ -408,7 +410,7 @@ class PkgDepResolver {
408410 ) : void {
409411 const peerDepMeta = json . peerDependenciesMeta || { } ;
410412 _ . each ( json . peerDependencies || ( json as Record < string , unknown > ) . peerDepenencies as Record < string , string > , ( semver : string , name : string ) => {
411- const peerId = chalk . cyan ( `${ name } @${ semver } ` ) ;
413+ const peerId = `${ name } @${ semver } ` ;
412414 const resolved = this . resolvePackage ( {
413415 item : { name, semver } as DepItem ,
414416 meta : { versions : { } } as PackageMeta
@@ -417,14 +419,15 @@ class PkgDepResolver {
417419 // Skip warning if peer dependency is marked as optional in peerDependenciesMeta
418420 const isOptional = peerDepMeta [ name ] && peerDepMeta [ name ] . optional ;
419421 if ( ! isOptional ) {
420- logger . warn (
421- chalk . yellow ( "Warning:" ) ,
422- `peer dependencies ${ peerId } of ${ pkgId } ${ chalk . red ( "is missing" ) } `
423- ) ;
422+ // Collect warning instead of logging immediately
423+ if ( ! this . _peerDepWarnings . has ( peerId ) ) {
424+ this . _peerDepWarnings . set ( peerId , new Set ( ) ) ;
425+ }
426+ this . _peerDepWarnings . get ( peerId ) ! . add ( pkgId ) ;
424427 }
425428 } else {
426429 logger . debug (
427- `peer dependencies ${ peerId } of ${ pkgId } ` ,
430+ `peer dependencies ${ chalk . cyan ( peerId ) } of ${ pkgId } ` ,
428431 `${ chalk . green ( "resolved to" ) } ${ resolved } `
429432 ) ;
430433 _ . set ( depInfo , [ "res" , "per" , name ] , { resolved } ) ;
@@ -439,6 +442,40 @@ class PkgDepResolver {
439442 return this . resolvePkgPeerDep ( json , pkgId , depInfo ) ;
440443 }
441444
445+ _logConsolidatedPeerDepWarnings ( ) : void {
446+ if ( this . _peerDepWarnings . size === 0 ) {
447+ return ;
448+ }
449+
450+ // Sort peer dependencies alphabetically
451+ const sortedPeerDeps = Array . from ( this . _peerDepWarnings . entries ( ) ) . sort ( ( a , b ) => {
452+ return a [ 0 ] . localeCompare ( b [ 0 ] ) ;
453+ } ) ;
454+
455+ for ( const [ peerId , pkgIds ] of sortedPeerDeps ) {
456+ // Sort requiring packages alphabetically
457+ const packages = Array . from ( pkgIds ) . sort ( ) ;
458+ const peerIdDisplay = chalk . cyan ( peerId ) ;
459+
460+ // Use same format for all cases
461+ const sampleSize = Math . min ( 3 , packages . length ) ;
462+ const samplePackages = packages . slice ( 0 , sampleSize ) ;
463+ const remainingCount = packages . length - sampleSize ;
464+
465+ let pkgList : string ;
466+ if ( remainingCount > 0 ) {
467+ pkgList = `${ samplePackages . join ( ", " ) } , ... and ${ remainingCount } more` ;
468+ } else {
469+ pkgList = packages . join ( ", " ) ;
470+ }
471+
472+ logger . warn (
473+ chalk . yellow ( "Warning:" ) ,
474+ `peer dependencies ${ peerIdDisplay } ${ chalk . red ( "is missing" ) } (by: ${ pkgList } )`
475+ ) ;
476+ }
477+ }
478+
442479 queueDepth ( depth : number ) : void {
443480 if ( depth > 1 ) {
444481 const parentDepth = this . _depthResolving ! [ depth - 1 ] ;
0 commit comments