@@ -6,26 +6,78 @@ import ShallowCloneError from './errors/ShallowCloneError';
6
6
7
7
type FinalCloneTypes = KeyValuePair | ArraySlice | ObjectSlice ;
8
8
9
- const invokeClone = < T extends Element | FinalCloneTypes > ( value : T ) : T => {
10
- if ( typeof value ?. clone === 'function' ) {
11
- return value . clone ( ) as T ;
12
- }
13
- return value ;
9
+ type DeepCloneOptions < T extends Element | FinalCloneTypes > = {
10
+ visited ?: WeakMap < T , T > ;
14
11
} ;
15
12
16
- export const cloneDeep = < T extends Element | FinalCloneTypes > ( value : T ) : T => {
13
+ export const cloneDeep = < T extends Element | FinalCloneTypes > (
14
+ value : T ,
15
+ options : DeepCloneOptions < T > = { } ,
16
+ ) : T => {
17
+ const { visited = new WeakMap < T , T > ( ) } = options ;
18
+ const passThroughOptions = { ...options , visited } ;
19
+
20
+ // detect cycle and return memoized value
21
+ if ( visited . has ( value ) ) {
22
+ return visited . get ( value ) as T ;
23
+ }
24
+
25
+ if ( value instanceof KeyValuePair ) {
26
+ const { key, value : val } = value ;
27
+ const keyCopy = isElement ( key )
28
+ ? cloneDeep ( key , passThroughOptions as DeepCloneOptions < Element > )
29
+ : key ;
30
+ const valueCopy = isElement ( val )
31
+ ? cloneDeep ( val , passThroughOptions as DeepCloneOptions < Element > )
32
+ : val ;
33
+ const copy = new KeyValuePair ( keyCopy , valueCopy ) as T ;
34
+ visited . set ( value , copy ) ;
35
+ return copy ;
36
+ }
37
+
17
38
if ( value instanceof ObjectSlice ) {
18
- const items = [ ...( value as ObjectSlice ) ] . map ( invokeClone ) as T [ ] ;
19
- return new ObjectSlice ( items ) as T ;
39
+ const mapper = ( element : T ) => cloneDeep ( element , passThroughOptions ) ;
40
+ const items = [ ...( value as ObjectSlice ) ] . map ( mapper ) as T [ ] ;
41
+ const copy = new ObjectSlice ( items ) as T ;
42
+ visited . set ( value , copy ) ;
43
+ return copy ;
20
44
}
21
45
22
46
if ( value instanceof ArraySlice ) {
23
- const items = [ ...( value as ArraySlice ) ] . map ( invokeClone ) as T [ ] ;
24
- return new ArraySlice ( items ) as T ;
47
+ const mapper = ( element : T ) => cloneDeep ( element , passThroughOptions ) ;
48
+ const items = [ ...( value as ArraySlice ) ] . map ( mapper ) as T [ ] ;
49
+ const copy = new ArraySlice ( items ) as T ;
50
+ visited . set ( value , copy ) ;
51
+ return copy ;
25
52
}
26
53
27
- if ( typeof value ?. clone === 'function' ) {
28
- return value . clone ( ) as T ;
54
+ if ( isElement ( value ) ) {
55
+ const copy = cloneShallow ( value ) ; // eslint-disable-line @typescript-eslint/no-use-before-define
56
+
57
+ visited . set ( value , copy ) ;
58
+
59
+ if ( value . content ) {
60
+ if ( isElement ( value . content ) ) {
61
+ copy . content = cloneDeep (
62
+ value . content ,
63
+ passThroughOptions as DeepCloneOptions < Element > ,
64
+ ) as any ;
65
+ } else if ( ( value . content as unknown ) instanceof KeyValuePair ) {
66
+ copy . content = cloneDeep (
67
+ value . content as unknown as KeyValuePair ,
68
+ passThroughOptions as DeepCloneOptions < KeyValuePair > ,
69
+ ) as any ;
70
+ } else if ( Array . isArray ( value . content ) ) {
71
+ const mapper = ( element : unknown ) => cloneDeep ( element as T , passThroughOptions ) ;
72
+ copy . content = value . content . map ( mapper ) ;
73
+ } else {
74
+ copy . content = value . content ;
75
+ }
76
+ } else {
77
+ copy . content = value . content ;
78
+ }
79
+
80
+ return copy ;
29
81
}
30
82
31
83
throw new DeepCloneError ( "Value provided to cloneDeep function couldn't be cloned" , {
0 commit comments