@@ -113,7 +113,8 @@ export function scanApiVersionSetReference(
113113export function resolveTransitiveDependencies (
114114 extractedPolicies : Map < string , string > ,
115115 extractedApis : Map < string , Record < string , unknown > > ,
116- currentFilter : FilterConfig
116+ currentFilter : FilterConfig ,
117+ extractedNamedValues : Map < string , Record < string , unknown > > = new Map ( )
117118) : FilterConfig {
118119 const expanded = { ...currentFilter } ;
119120 let changed = true ;
@@ -142,6 +143,23 @@ export function resolveTransitiveDependencies(
142143 changed = true ;
143144 }
144145 }
146+
147+ // Scan extracted named values for references to other named values.
148+ for ( const [ name , namedValueJson ] of extractedNamedValues ) {
149+ const currentNamedValues = expanded . namedValueNames ;
150+ if ( currentNamedValues !== undefined ) {
151+ const isIncluded = currentNamedValues . some ( ( n ) => n . toLowerCase ( ) === name . toLowerCase ( ) ) ;
152+ if ( ! isIncluded ) {
153+ continue ;
154+ }
155+ }
156+
157+ for ( const ref of scanNamedValueReferences ( namedValueJson ) ) {
158+ if ( addToFilter ( expanded , ref ) ) {
159+ changed = true ;
160+ }
161+ }
162+ }
145163 }
146164
147165 if ( iterations > 1 ) {
@@ -195,33 +213,107 @@ function addToFilter(
195213 */
196214export function findTransitiveDependencies (
197215 policies : Map < string , string > ,
198- apis : Map < string , Record < string , unknown > >
216+ apis : Map < string , Record < string , unknown > > ,
217+ namedValues : Map < string , Record < string , unknown > > = new Map ( )
199218) : ResourceDescriptor [ ] {
200219 const dependencies : ResourceDescriptor [ ] = [ ] ;
201220 const seen = new Set < string > ( ) ;
221+ const namedValueKeyToName = new Map < string , string > ( ) ;
222+
223+ const addDependency = ( dep : TransitiveDependency ) : boolean => {
224+ const key = `${ dep . type } :${ dep . name . toLowerCase ( ) } ` ;
225+ if ( seen . has ( key ) ) {
226+ return false ;
227+ }
228+ seen . add ( key ) ;
229+ dependencies . push ( { type : dep . type , nameParts : [ dep . name ] } ) ;
230+ return true ;
231+ } ;
232+
233+ for ( const [ name ] of namedValues ) {
234+ namedValueKeyToName . set ( name . toLowerCase ( ) , name ) ;
235+ }
236+
237+ const namedValueQueue : string [ ] = [ ] ;
202238
203239 // Scan all policies
204240 for ( const [ , policyXml ] of policies ) {
205241 for ( const dep of scanPolicyReferences ( policyXml ) ) {
206- const key = `${ dep . type } :${ dep . name . toLowerCase ( ) } ` ;
207- if ( ! seen . has ( key ) ) {
208- seen . add ( key ) ;
209- dependencies . push ( { type : dep . type , nameParts : [ dep . name ] } ) ;
242+ if ( addDependency ( dep ) && dep . type === ResourceType . NamedValue ) {
243+ namedValueQueue . push ( dep . name ) ;
210244 }
211245 }
212246 }
213247
214248 // Scan API version set references
215249 for ( const [ , apiJson ] of apis ) {
216250 const dep = scanApiVersionSetReference ( apiJson ) ;
217- if ( dep ) {
218- const key = `${ dep . type } :${ dep . name . toLowerCase ( ) } ` ;
219- if ( ! seen . has ( key ) ) {
220- seen . add ( key ) ;
221- dependencies . push ( { type : dep . type , nameParts : [ dep . name ] } ) ;
251+ if ( dep && addDependency ( dep ) && dep . type === ResourceType . NamedValue ) {
252+ namedValueQueue . push ( dep . name ) ;
253+ }
254+ }
255+
256+ // Expand named value chains (e.g. A references B, B references C).
257+ while ( namedValueQueue . length > 0 ) {
258+ const currentName = namedValueQueue . shift ( ) ;
259+ if ( ! currentName ) {
260+ continue ;
261+ }
262+
263+ const canonicalName = namedValueKeyToName . get ( currentName . toLowerCase ( ) ) ;
264+ if ( ! canonicalName ) {
265+ continue ;
266+ }
267+
268+ const namedValueJson = namedValues . get ( canonicalName ) ;
269+ if ( ! namedValueJson ) {
270+ continue ;
271+ }
272+
273+ for ( const dep of scanNamedValueReferences ( namedValueJson ) ) {
274+ if ( addDependency ( dep ) && dep . type === ResourceType . NamedValue ) {
275+ namedValueQueue . push ( dep . name ) ;
222276 }
223277 }
224278 }
225279
226280 return dependencies ;
227281}
282+
283+ /**
284+ * Scan named value JSON payload for references to other named values.
285+ */
286+ function scanNamedValueReferences ( namedValueJson : Record < string , unknown > ) : TransitiveDependency [ ] {
287+ const refs : TransitiveDependency [ ] = [ ] ;
288+ const stack : unknown [ ] = [ namedValueJson ] ;
289+
290+ while ( stack . length > 0 ) {
291+ const value = stack . pop ( ) ;
292+ if ( typeof value === 'string' ) {
293+ for ( const match of value . matchAll ( NAMED_VALUE_PATTERN ) ) {
294+ if ( match [ 1 ] ) {
295+ refs . push ( {
296+ type : ResourceType . NamedValue ,
297+ name : match [ 1 ] . trim ( ) ,
298+ } ) ;
299+ }
300+ }
301+ continue ;
302+ }
303+
304+ if ( Array . isArray ( value ) ) {
305+ for ( const item of value ) {
306+ stack . push ( item ) ;
307+ }
308+ continue ;
309+ }
310+
311+ if ( value && typeof value === 'object' ) {
312+ for ( const child of Object . values ( value as Record < string , unknown > ) ) {
313+ stack . push ( child ) ;
314+ }
315+ }
316+ }
317+
318+ return refs ;
319+ }
0 commit comments