@@ -126,10 +126,17 @@ export class BomBuilder {
126126 rootPackage . dependencies . delete ( dep )
127127 }
128128 }
129+
130+ // Build map of workspace packages' devDependencies for filtering
131+ const workspaceDevDeps = this . omitDevDependencies
132+ ? this . gatherWorkspaceDevDependencies ( workspace . project )
133+ : new Map < LocatorHash , Set < string > > ( )
134+
129135 for await ( const component of this . gatherDependencies (
130136 rootComponent , rootPackage ,
131137 workspace . project ,
132- fetchManifest , fetchLicenseEvidences
138+ fetchManifest , fetchLicenseEvidences ,
139+ workspaceDevDeps
133140 ) ) {
134141 component . licenses . forEach ( setLicensesDeclared )
135142
@@ -337,8 +344,51 @@ export class BomBuilder {
337344 }
338345 }
339346
340- private * getDeps ( pkg : Package , project : Project ) : Generator < Package > {
347+ /**
348+ * Gathers devDependencies from all workspace packages in the project.
349+ * Returns a map of package locatorHash to Set of devDependency names.
350+ */
351+ private gatherWorkspaceDevDependencies ( project : Project ) : Map < LocatorHash , Set < string > > {
352+ const workspaceDevDeps = new Map < LocatorHash , Set < string > > ( )
353+
354+ for ( const workspace of project . workspaces ) {
355+ const pkg = workspace . anchoredPackage
356+ // Only process workspace packages (not external dependencies)
357+ if ( pkg . reference . startsWith ( 'workspace:' ) ) {
358+ const devDeps = new Set < string > ( )
359+ for ( const depIdent of workspace . manifest . devDependencies . keys ( ) ) {
360+ devDeps . add ( depIdent )
361+ }
362+ if ( devDeps . size > 0 ) {
363+ workspaceDevDeps . set ( pkg . locatorHash , devDeps )
364+ this . console . debug ( 'DEBUG | workspace %s has %d devDependencies' ,
365+ structUtils . prettyLocatorNoColors ( pkg ) ,
366+ devDeps . size
367+ )
368+ }
369+ }
370+ }
371+
372+ return workspaceDevDeps
373+ }
374+
375+ private * getDeps (
376+ pkg : Package ,
377+ project : Project ,
378+ workspaceDevDeps : Map < LocatorHash , Set < string > >
379+ ) : Generator < Package > {
380+ const parentDevDeps = workspaceDevDeps . get ( pkg . locatorHash )
381+
341382 for ( const depDesc of pkg . dependencies . values ( ) ) {
383+ // Skip if this is a devDependency of the parent workspace package
384+ if ( parentDevDeps ?. has ( depDesc . identHash ) === true ) {
385+ this . console . debug ( 'DEBUG | skipping devDependency %s of workspace package %s' ,
386+ depDesc . identHash ,
387+ structUtils . prettyLocatorNoColors ( pkg )
388+ )
389+ continue
390+ }
391+
342392 const depRes = project . storedResolutions . get ( depDesc . descriptorHash )
343393 if ( typeof depRes === 'undefined' ) {
344394 throw new Error ( `missing depRes for : ${ depDesc . descriptorHash } ` )
@@ -355,7 +405,8 @@ export class BomBuilder {
355405 component : Component , pkg : Package ,
356406 project : Project ,
357407 fetchManifest : ManifestFetcher ,
358- fetchLicenseEvidences : LicenseEvidenceFetcher
408+ fetchLicenseEvidences : LicenseEvidenceFetcher ,
409+ workspaceDevDeps : Map < LocatorHash , Set < string > >
359410 ) : AsyncGenerator < Component > {
360411 // ATTENTION: multiple packages may have the same `identHash`, but the `locatorHash` is unique.
361412 const knownComponents = new Map < LocatorHash , Component > ( [ [ pkg . locatorHash , component ] ] )
@@ -364,7 +415,7 @@ export class BomBuilder {
364415 let pendingEntry // eslint-disable-line @typescript-eslint/init-declarations -- ack
365416 while ( ( pendingEntry = pending . pop ( ) ) !== undefined ) {
366417 const [ pendingPkg , pendingComponent ] = pendingEntry
367- for ( const depPkg of this . getDeps ( pendingPkg , project ) ) {
418+ for ( const depPkg of this . getDeps ( pendingPkg , project , workspaceDevDeps ) ) {
368419 let depComponent = knownComponents . get ( depPkg . locatorHash )
369420 if ( depComponent === undefined ) {
370421 const _depIDN = structUtils . prettyLocatorNoColors ( depPkg )
0 commit comments