@@ -111,3 +111,81 @@ export function isEvent(evt: unknown): evt is Event {
111111export function isPropPresent ( obj : Record < string , unknown > , prop : string ) {
112112 return prop in obj && obj [ prop ] !== IS_ABSENT ;
113113}
114+
115+ /**
116+ * Compares if two values are the same borrowed from:
117+ * https://github.com/epoberezkin/fast-deep-equal
118+ * We added a case for file matching since `Object.keys` doesn't work with Files.
119+ * */
120+ export function isEqual ( a : any , b : any ) {
121+ if ( a === b ) return true ;
122+
123+ if ( a && b && typeof a === 'object' && typeof b === 'object' ) {
124+ if ( a . constructor !== b . constructor ) return false ;
125+
126+ // eslint-disable-next-line no-var
127+ var length , i , keys ;
128+ if ( Array . isArray ( a ) ) {
129+ length = a . length ;
130+ // eslint-disable-next-line eqeqeq
131+ if ( length != b . length ) return false ;
132+ for ( i = length ; i -- !== 0 ; ) if ( ! isEqual ( a [ i ] , b [ i ] ) ) return false ;
133+ return true ;
134+ }
135+
136+ if ( a instanceof Map && b instanceof Map ) {
137+ if ( a . size !== b . size ) return false ;
138+ for ( i of a . entries ( ) ) if ( ! b . has ( i [ 0 ] ) ) return false ;
139+ for ( i of a . entries ( ) ) if ( ! isEqual ( i [ 1 ] , b . get ( i [ 0 ] ) ) ) return false ;
140+ return true ;
141+ }
142+
143+ // We added this part for file comparison, arguably a little naive but should work for most cases.
144+ // #3911
145+ if ( a instanceof File && b instanceof File ) {
146+ if ( a . size !== b . size ) return false ;
147+ if ( a . name !== b . name ) return false ;
148+ if ( a . lastModified !== b . lastModified ) return false ;
149+ if ( a . type !== b . type ) return false ;
150+
151+ return true ;
152+ }
153+
154+ if ( a instanceof Set && b instanceof Set ) {
155+ if ( a . size !== b . size ) return false ;
156+ for ( i of a . entries ( ) ) if ( ! b . has ( i [ 0 ] ) ) return false ;
157+ return true ;
158+ }
159+
160+ if ( ArrayBuffer . isView ( a ) && ArrayBuffer . isView ( b ) ) {
161+ length = ( a as any ) . length ;
162+ // eslint-disable-next-line eqeqeq
163+ if ( length != ( b as any ) . length ) return false ;
164+ for ( i = length ; i -- !== 0 ; ) if ( ( a as any ) [ i ] !== ( b as any ) [ i ] ) return false ;
165+ return true ;
166+ }
167+
168+ if ( a . constructor === RegExp ) return a . source === b . source && a . flags === b . flags ;
169+ if ( a . valueOf !== Object . prototype . valueOf ) return a . valueOf ( ) === b . valueOf ( ) ;
170+ if ( a . toString !== Object . prototype . toString ) return a . toString ( ) === b . toString ( ) ;
171+
172+ keys = Object . keys ( a ) ;
173+ length = keys . length ;
174+ if ( length !== Object . keys ( b ) . length ) return false ;
175+
176+ for ( i = length ; i -- !== 0 ; ) if ( ! Object . prototype . hasOwnProperty . call ( b , keys [ i ] ) ) return false ;
177+
178+ for ( i = length ; i -- !== 0 ; ) {
179+ // eslint-disable-next-line no-var
180+ var key = keys [ i ] ;
181+
182+ if ( ! isEqual ( a [ key ] , b [ key ] ) ) return false ;
183+ }
184+
185+ return true ;
186+ }
187+
188+ // true if both NaN, false otherwise
189+ // eslint-disable-next-line no-self-compare
190+ return a !== a && b !== b ;
191+ }
0 commit comments