@@ -4,6 +4,7 @@ import { WrappedFunction } from '@sentry/types';
4
4
5
5
import { htmlTreeAsString } from './browser' ;
6
6
import { isElement , isError , isEvent , isInstanceOf , isPlainObject , isPrimitive } from './is' ;
7
+ import { memoBuilder , MemoFunc } from './memo' ;
7
8
import { truncate } from './string' ;
8
9
9
10
/**
@@ -203,61 +204,42 @@ export function extractExceptionKeysForMessage(exception: Record<string, unknown
203
204
}
204
205
205
206
/**
206
- * Given any object, return a new object having removed all fields whose value was `undefined`.
207
+ * Given any object, return the new object with removed keys that value was `undefined`.
207
208
* Works recursively on objects and arrays.
208
209
*
209
210
* Attention: This function keeps circular references in the returned object.
210
211
*/
211
- export function dropUndefinedKeys < T > ( inputValue : T ) : T {
212
- // This map keeps track of what already visited nodes map to.
213
- // Our Set - based memoBuilder doesn't work here because we want to the output object to have the same circular
214
- // references as the input object.
215
- const memoizationMap = new Map < unknown , unknown > ( ) ;
216
-
212
+ export function dropUndefinedKeys < T > ( val : T ) : T {
217
213
// This function just proxies `_dropUndefinedKeys` to keep the `memoBuilder` out of this function's API
218
- return _dropUndefinedKeys ( inputValue , memoizationMap ) ;
214
+ return _dropUndefinedKeys ( val , memoBuilder ( ) ) ;
219
215
}
220
216
221
- function _dropUndefinedKeys < T > ( inputValue : T , memoizationMap : Map < unknown , unknown > ) : T {
222
- if ( isPlainObject ( inputValue ) ) {
223
- // If this node has already been visited due to a circular reference, return the object it was mapped to in the new object
224
- const memoVal = memoizationMap . get ( inputValue ) ;
225
- if ( memoVal !== undefined ) {
226
- return memoVal as T ;
227
- }
228
-
229
- const returnValue : { [ key : string ] : any } = { } ;
230
- // Store the mapping of this value in case we visit it again, in case of circular data
231
- memoizationMap . set ( inputValue , returnValue ) ;
217
+ function _dropUndefinedKeys < T > ( val : T , memo : MemoFunc ) : T {
218
+ const [ memoize ] = memo ; // we don't need unmemoize because we don't need to visit nodes twice
232
219
233
- for ( const key of Object . keys ( inputValue ) ) {
234
- if ( typeof inputValue [ key ] !== 'undefined' ) {
235
- returnValue [ key ] = _dropUndefinedKeys ( inputValue [ key ] , memoizationMap ) ;
220
+ if ( isPlainObject ( val ) ) {
221
+ if ( memoize ( val ) ) {
222
+ return val ;
223
+ }
224
+ const rv : { [ key : string ] : any } = { } ;
225
+ for ( const key of Object . keys ( val ) ) {
226
+ if ( typeof val [ key ] !== 'undefined' ) {
227
+ rv [ key ] = _dropUndefinedKeys ( val [ key ] , memo ) ;
236
228
}
237
229
}
238
-
239
- return returnValue as T ;
230
+ return rv as T ;
240
231
}
241
232
242
- if ( Array . isArray ( inputValue ) ) {
243
- // If this node has already been visited due to a circular reference, return the array it was mapped to in the new object
244
- const memoVal = memoizationMap . get ( inputValue ) ;
245
- if ( memoVal !== undefined ) {
246
- return memoVal as T ;
233
+ if ( Array . isArray ( val ) ) {
234
+ if ( memoize ( val ) ) {
235
+ return val ;
247
236
}
248
-
249
- const returnValue : unknown [ ] = [ ] ;
250
- // Store the mapping of this value in case we visit it again, in case of circular data
251
- memoizationMap . set ( inputValue , returnValue ) ;
252
-
253
- inputValue . forEach ( ( item : unknown ) => {
254
- returnValue . push ( _dropUndefinedKeys ( item , memoizationMap ) ) ;
255
- } ) ;
256
-
257
- return returnValue as unknown as T ;
237
+ return ( val as any [ ] ) . map ( item => {
238
+ return _dropUndefinedKeys ( item , memo ) ;
239
+ } ) as any ;
258
240
}
259
241
260
- return inputValue ;
242
+ return val ;
261
243
}
262
244
263
245
/**
0 commit comments