@@ -39,18 +39,40 @@ const DIFF_HANDLERS: HandlerRegistry = {
39
39
* the template +newTemplate+.
40
40
*/
41
41
export function diffTemplate ( currentTemplate : { [ key : string ] : any } , newTemplate : { [ key : string ] : any } ) : types . TemplateDiff {
42
- const differences : types . ITemplateDiff = { } ;
43
- const unknown : { [ key : string ] : types . Difference < any > } = { } ;
44
- for ( const key of unionOf ( Object . keys ( currentTemplate ) , Object . keys ( newTemplate ) ) . sort ( ) ) {
45
- const oldValue = currentTemplate [ key ] ;
46
- const newValue = newTemplate [ key ] ;
47
- if ( deepEqual ( oldValue , newValue ) ) { continue ; }
48
- const handler : DiffHandler = DIFF_HANDLERS [ key ]
49
- || ( ( _diff , oldV , newV ) => unknown [ key ] = impl . diffUnknown ( oldV , newV ) ) ;
50
- handler ( differences , oldValue , newValue ) ;
42
+ // We're going to modify this in-place
43
+ newTemplate = deepCopy ( newTemplate ) ;
44
+
45
+ while ( true ) {
46
+ const differences : types . ITemplateDiff = { } ;
47
+ const unknown : { [ key : string ] : types . Difference < any > } = { } ;
48
+ for ( const key of unionOf ( Object . keys ( currentTemplate ) , Object . keys ( newTemplate ) ) . sort ( ) ) {
49
+ const oldValue = currentTemplate [ key ] ;
50
+ const newValue = newTemplate [ key ] ;
51
+ if ( deepEqual ( oldValue , newValue ) ) { continue ; }
52
+ const handler : DiffHandler = DIFF_HANDLERS [ key ]
53
+ || ( ( _diff , oldV , newV ) => unknown [ key ] = impl . diffUnknown ( oldV , newV ) ) ;
54
+ handler ( differences , oldValue , newValue ) ;
55
+
56
+ }
57
+ if ( Object . keys ( unknown ) . length > 0 ) { differences . unknown = new types . DifferenceCollection ( unknown ) ; }
58
+
59
+ // Propagate replacements for replaced resources
60
+ let didPropagateReferenceChanges = false ;
61
+ if ( differences . resources ) {
62
+ differences . resources . forEach ( ( logicalId , change ) => {
63
+ if ( change . changeImpact === types . ResourceImpact . WILL_REPLACE ) {
64
+ if ( propagateReplacedReferences ( newTemplate , logicalId ) ) {
65
+ didPropagateReferenceChanges = true ;
66
+ }
67
+ }
68
+ } ) ;
69
+ }
70
+
71
+ // We're done only if we didn't have to propagate any more replacements.
72
+ if ( ! didPropagateReferenceChanges ) {
73
+ return new types . TemplateDiff ( differences ) ;
74
+ }
51
75
}
52
- if ( Object . keys ( unknown ) . length > 0 ) { differences . unknown = new types . DifferenceCollection ( unknown ) ; }
53
- return new types . TemplateDiff ( differences ) ;
54
76
}
55
77
56
78
/**
@@ -59,3 +81,67 @@ export function diffTemplate(currentTemplate: { [key: string]: any }, newTemplat
59
81
export function diffResource ( oldValue : types . Resource , newValue : types . Resource ) : types . ResourceDifference {
60
82
return impl . diffResource ( oldValue , newValue ) ;
61
83
}
84
+
85
+ /**
86
+ * Replace all references to the given logicalID on the given template, in-place
87
+ *
88
+ * Returns true iff any references were replaced.
89
+ */
90
+ function propagateReplacedReferences ( template : object , logicalId : string ) : boolean {
91
+ let ret = false ;
92
+
93
+ function recurse ( obj : any ) {
94
+ if ( Array . isArray ( obj ) ) {
95
+ obj . forEach ( recurse ) ;
96
+ }
97
+
98
+ if ( typeof obj === 'object' && obj !== null ) {
99
+ if ( ! replaceReference ( obj ) ) {
100
+ Object . values ( obj ) . forEach ( recurse ) ;
101
+ }
102
+ }
103
+ }
104
+
105
+ function replaceReference ( obj : any ) {
106
+ const keys = Object . keys ( obj ) ;
107
+ if ( keys . length !== 1 ) { return false ; }
108
+ const key = keys [ 0 ] ;
109
+
110
+ if ( key === 'Ref' ) {
111
+ if ( obj . Ref === logicalId ) {
112
+ obj . Ref = logicalId + '(replaced)' ;
113
+ ret = true ;
114
+ }
115
+ return true ;
116
+ }
117
+
118
+ if ( key . startsWith ( 'Fn::' ) ) {
119
+ if ( Array . isArray ( obj [ key ] ) && obj [ key ] . length > 0 && obj [ key ] [ 0 ] === logicalId ) {
120
+ obj [ key ] [ 0 ] = logicalId + '(replaced)' ;
121
+ ret = true ;
122
+ }
123
+ return true ;
124
+ }
125
+
126
+ return false ;
127
+ }
128
+
129
+ recurse ( template ) ;
130
+ return ret ;
131
+ }
132
+
133
+ function deepCopy ( x : any ) : any {
134
+ if ( Array . isArray ( x ) ) {
135
+ return x . map ( deepCopy ) ;
136
+ }
137
+
138
+ if ( typeof x === 'object' && x !== null ) {
139
+ const ret : any = { } ;
140
+ for ( const key of Object . keys ( x ) ) {
141
+ ret [ key ] = deepCopy ( x [ key ] ) ;
142
+ }
143
+ return ret ;
144
+ }
145
+
146
+ return x ;
147
+ }
0 commit comments