import { Subscriber , Observable , Subject , AsyncSubject , BehaviorSubject , ReplaySubject , GroupedObservable } from 'rxjs' ; import { AnonymousSubject , SubjectSubscriber } from 'rxjs/internal/Subject' ; import { Node } from './node' ; import { componentInputs , componentOutputs , componentMetadata , componentQueryChildren , } from './decorators' ; import { isScalar , functionName , recurse , } from '../utils' ; import { isDebugElementComponent } from '../backend/utils/description' ; export enum ObjectType { Input = 0x1 , Output = 0x2 , Subject = 0x4 , Observable = 0x8 , EventEmitter = 0x10 , ViewChild = 0x20 , ViewChildren = 0x40 , ContentChild = 0x80 , ContentChildren = 0x100 } export type Metadata = Map < any , [ ObjectType , any ] > ; export type ComponentMetadata = Map < any , [ [ string , ObjectType , any ] ] > ; export interface InstanceWithMetadata { instance : any ; metadata : Metadata ; providers : { [ token ] : any ; } ; componentMetadata : ComponentMetadata ; } export const instanceWithMetadata = ( debugElement , node , instance ) => { if ( node == null ) { return null ; } const isComponent = isDebugElementComponent ( debugElement ) ; const objectMetadata = new Map < any , [ ObjectType , any ] > ( ) ; const components = new Map < any , [ [ string , ObjectType , any ] ] > ( ) ; const providers = debugElement . providerTokens . map ( t => [ tokenName ( t ) , debugElement . injector . get ( t ) ] ) . filter ( provider => provider [ 1 ] !== instance ) ; const result = { instance : isComponent ? instance : null , providers , metadata : objectMetadata , componentMetadata : components , } ; if ( ! isComponent ) { return result ; } recurse ( instance , obj => { const update = ( key , flag , additionalProps ) => { const existing = components . get ( obj ) ; if ( existing ) { existing . push ( [ key , flag , additionalProps ] ) ; } else { components . set ( obj , [ [ key , flag , additionalProps ] ] ) ; } } ; const component = obj ? componentMetadata ( obj . constructor ) : null ; if ( component ) { for ( const input of componentInputs ( component , obj ) ) { update ( input . propertyKey , ObjectType . Input , { alias : input . bindingPropertyName } ) ; } for ( const output of componentOutputs ( component , obj ) ) { update ( output . propertyKey , ObjectType . Output , { alias : output . bindingPropertyName } ) ; } const addQuery = ( decoratorType , objType ) => { for ( const vc of componentQueryChildren ( decoratorType , component , obj ) ) { update ( vc . propertyKey , objType , { selector : vc . selector } ) ; } } ; addQuery ( '@ViewChild' , ObjectType . ViewChild ) ; addQuery ( '@ViewChildren' , ObjectType . ViewChildren ) ; addQuery ( '@ContentChild' , ObjectType . ContentChild ) ; addQuery ( '@ContentChildren' , ObjectType . ContentChildren ) ; } const type = objectType ( obj ) ; if ( type !== 0 ) { const existing = objectMetadata . get ( obj ) ; if ( existing ) { objectMetadata . set ( obj , [ existing [ 0 ] | type , existing [ 1 ] ] ) ; } else { objectMetadata . set ( obj , [ type , null ] ) ; } } } ) ; result . metadata = Array . from ( < any > objectMetadata ) ; result . componentMetadata = Array . from ( < any > components ) ; return result ; } ; export const tokenName = ( token ) : string => functionName ( token ) || token . toString ( ) ; const objectType = ( object ) : ObjectType => { if ( object != null && ! isScalar ( object ) ) { const constructor = object && object . constructor ? object . constructor : ( { } ) . constructor ; switch ( functionName ( constructor ) ) { case 'EventEmitter' : return ObjectType . EventEmitter ; case functionName ( AsyncSubject ) : case functionName ( AnonymousSubject ) : case functionName ( BehaviorSubject ) : case functionName ( ReplaySubject ) : case functionName ( Subject ) : case functionName ( Subscriber ) : case functionName ( SubjectSubscriber ) : return ObjectType . Subject | ObjectType . Observable ; case functionName ( Observable ) : case functionName ( GroupedObservable ) : return ObjectType . Observable ; } } return 0 ; } ;