@@ -68,6 +68,9 @@ async function getMasterLockFile() {
6868
6969interface ResolutionSpec { pkgSpecifier : string , pkgName : string }
7070
71+ /**
72+ * Parsed output entry returned by `yarn why`
73+ */
7174interface YarnWhyOutput {
7275 value : string ;
7376 children : {
@@ -78,6 +81,28 @@ interface YarnWhyOutput {
7881 } ;
7982}
8083
84+ /**
85+ * Run `yarn why <package_name>` to see why a package is included
86+ * Don't use recursive (-R) since we want to build the graph ourselves
87+ * @function
88+ */
89+ const runYarnWhy = memoize ( async ( pkgName : string ) => {
90+ // Memoize the call so that we don't need to call yarn why multiple times for each package
91+ const { stdout : output , exitCode, stderr } = await getExecOutput ( 'yarn' , [ 'why' , pkgName , '--json' ] , { silent : true } ) ;
92+ if ( exitCode !== 0 ) {
93+ core . error ( stderr ) ;
94+ throw new Error ( `yarn why for ${ pkgName } failed!` ) ;
95+ }
96+
97+ return output . split ( '\n' ) . reduce < YarnWhyOutput [ ] > ( ( res , each ) => {
98+ each = each . trim ( ) ;
99+ if ( each === '' ) return res ;
100+
101+ const pkg = JSON . parse ( each ) as YarnWhyOutput ;
102+ return [ ...res , pkg ] ;
103+ } , [ ] ) ;
104+ } )
105+
81106/**
82107 * Determines the names of the packages that have changed versions
83108 */
@@ -92,45 +117,42 @@ export async function getPackagesWithResolutionChanges() {
92117 changes . delete ( edge ) ;
93118 }
94119
95- const changedDeps : ResolutionSpec [ ] = [ ] ;
120+ const frontier : ResolutionSpec [ ] = [ ] ;
121+ const changedDeps = new Set < string > ( ) ;
96122 for ( const edge of changes ) {
97123 const [ pkgSpecifier ] = edge . split ( ' -> ' ) ;
98- changedDeps . push ( { pkgSpecifier, pkgName : extractPackageName ( pkgSpecifier ) } ) ;
124+ changedDeps . add ( pkgSpecifier ) ;
125+ frontier . push ( {
126+ pkgSpecifier,
127+ pkgName : extractPackageName ( pkgSpecifier )
128+ } ) ;
99129 }
100130
101- const frontier = [ ...changedDeps ] ;
102131 while ( frontier . length > 0 ) {
103132 const { pkgName, pkgSpecifier } = frontier . shift ( ) ! ;
104133
105- // Run `yarn why <package_name>` to see why a package is included
106- // Don't use recursive (-R) since we want to build the graph ourselves
107- const { stdout : output , exitCode, stderr } = await getExecOutput ( 'yarn' , [ 'why' , pkgName , '--json' ] , { silent : true } ) ;
108- if ( exitCode !== 0 ) {
109- core . error ( stderr ) ;
110- throw new Error ( `yarn why for ${ pkgName } failed!` ) ;
111- }
112-
113- output . split ( '\n' ) . forEach ( each => {
114- each = each . trim ( ) ;
115- if ( each === '' ) return ;
134+ const reasons = await runYarnWhy ( pkgName ) ;
135+ reasons . forEach ( pkg => {
136+ if ( changedDeps . has ( pkg . value ) ) {
137+ // If we've already added this pkg specifier, don't need to explore it again
138+ return ;
139+ }
116140
117- const pkg = JSON . parse ( each ) as YarnWhyOutput ;
118141 const childrenSpecifiers = Object . values ( pkg . children ) . map ( ( { descriptor } ) => descriptor ) ;
119142 if ( ! childrenSpecifiers . includes ( pkgSpecifier ) ) return ;
120143
121- const toAdd : ResolutionSpec = { pkgSpecifier : pkg . value , pkgName : extractPackageName ( pkg . value ) } ;
122- frontier . push ( toAdd ) ;
123- changedDeps . push ( toAdd ) ;
144+ frontier . push ( { pkgSpecifier : pkg . value , pkgName : extractPackageName ( pkg . value ) } ) ;
145+ changedDeps . add ( pkg . value ) ;
124146 } ) ;
125147 }
126148
127149 core . info ( '=== Summary of dirty monorepo packages ===\n' ) ;
128- const pkgsToRebuild = changedDeps . filter ( ( { pkgSpecifier } ) => pkgSpecifier . includes ( '@workspace:' ) ) ;
129- for ( const { pkgName } of pkgsToRebuild ) {
150+ const pkgsToRebuild = [ ... changedDeps ] . filter ( pkgSpecifier => pkgSpecifier . includes ( '@workspace:' ) ) ;
151+ for ( const pkgName of pkgsToRebuild ) {
130152 core . info ( `- ${ pkgName } ` ) ;
131153 }
132154
133- return pkgsToRebuild . map ( ( { pkgName } ) => pkgName ) ;
155+ return pkgsToRebuild . map ( extractPackageName ) ;
134156}
135157
136158/**
0 commit comments