import { NodeMap , NodeSet } from '#ast/components' ; import { NamedNode , Node } from '#ast/node-types' ; import { isNode , isReferenceNode } from '#ast/utilities/node-typeguards' ; import { nodeMetadata } from '#ast/node-metadata' ; import { Declaration } from '#ast/node-aliases' ; internal cloneNode . copyNode = function ( updatedNodes , node ) : Node { if ( updatedNodes . get ( node ) !== undefined ) return updatedNodes . get ( node ) ! ; const descriptors = Object . getOwnPropertyDescriptors ( node ) ; const newNode = Object . create ( Object . getPrototypeOf ( node ) ) ; const { childContainerProperties } = nodeMetadata [ node . kind ] ; for ( const propKey of Object . keys ( descriptors ) ) if ( descriptors [ propKey ] . value ) descriptors [ propKey ] . value = cloneNode . cloneItem ( updatedNodes , descriptors [ propKey ] . value , ! ! childContainerProperties ?. has ( propKey ) ) ; Object . defineProperties ( newNode , descriptors ) ; updatedNodes . set ( node , newNode ) ; return newNode ; } ; internal cloneNode . cloneItem = function ( updatedNodes , item , childContainer ? ) : any { if ( item instanceof NodeSet ) { const nodes = item . toArray ( ) ; return childContainer ? new NodeSet ( nodes . map ( node => cloneNode . copyNode ( updatedNodes , node ) ) ) : new NodeSet ( nodes ) ; } if ( item instanceof NodeMap ) { const nodes = item . toArray ( ) ; return childContainer ? new NodeMap ( nodes . map ( node => cloneNode . copyNode ( updatedNodes , node ) ) as NamedNode [ ] ) : new NodeMap ( nodes ) ; } if ( isNode ( item ) ) return childContainer ? cloneNode . copyNode ( updatedNodes , item ) : item ; return ( ( ( item === null ) || ( typeof item !== 'object' ) ) ? item : ( Array . isArray ( item ) ) ? item . map ( item => cloneNode . cloneItem ( updatedNodes , item ) ) : ( item instanceof Map ) ? new Map ( [ ... item . entries ( ) ] . map ( ( [ k , v ] ) => [ cloneNode . cloneItem ( updatedNodes , k ) , cloneNode . cloneItem ( updatedNodes , v ) ] ) ) : ( item instanceof Set ) ? new Set ( [ ... item ] . map ( setItem => cloneNode . cloneItem ( updatedNodes , setItem ) ) ) : Object . create ( Object . getPrototypeOf ( item ) , Object . getOwnPropertyDescriptors ( item ) ) ) ; } ; export function cloneNode < T extends Node > ( node ) { const updatedNodes = new Map < Node , Node > ( ) ; cloneNode . copyNode ( updatedNodes , node ) ; for ( const newNode of [ ... updatedNodes . values ( ) ] as Node [ ] ) { if ( newNode . parent ) newNode . updateProperties ( { parent : updatedNodes . get ( newNode . parent ) } ) ; if ( isReferenceNode ( newNode ) && updatedNodes . has ( newNode . target ) ) newNode . updateProperties ( { target : updatedNodes . get ( newNode . target ) as Declaration } ) ; } return updatedNodes . get ( node ) ! as T ; }