import { IComponent , ComponentList , TComAndDir , DirectiveList } from '../types' ; import { utils } from '../utils' ; import { Compile } from './compile' ; import { buildComponentScope } from './compiler-utils' ; import { Vnode , parseTemplateToVnode , isTagName } from '../vnode' ; import { mountDirective } from './directive-compiler' ; import { buildViewChildandChildren , buildContentChildandChildren , ChangeDetectionStrategy } from '../component' ; import { lifecycleCaller } from '../lifecycle' ; param { DirectiveList [ ] } directiveList function emitDirectiveDestory ( directiveList ) { directiveList . forEach ( directive => { lifecycleCaller ( directive . instanceScope , 'nvOnDestory' ) ; } ) ; } param { ComponentList [ ] } componentList function emitComponentDestory ( componentList ) { componentList . forEach ( component => { emitDirectiveDestory ( component . instanceScope . $directiveList ) ; emitComponentDestory ( component . instanceScope . $componentList ) ; lifecycleCaller ( component . instanceScope , 'nvOnDestory' ) ; } ) ; } export param { IComponent } componentInstance param { TComAndDir } componentAndDirectives export async function mountComponent ( componentInstance , componentAndDirectives ) { const cacheComponentList = [ ... componentInstance . $componentList ] ; const foundCacheComponentList = [ ] ; componentsConstructor ( componentInstance , componentAndDirectives ) ; const componentListLength = componentInstance . $componentList . length ; for ( let i = 0 ; i < componentListLength ; i ++ ) { const component = componentInstance . $componentList [ i ] ; const cacheComponentIndex = cacheComponentList . findIndex ( cache => cache . nativeElement === component . nativeElement ) ; const cacheComponent = cacheComponentList [ cacheComponentIndex ] ; if ( cacheComponentIndex !== - 1 ) { cacheComponentList . splice ( cacheComponentIndex , 1 ) ; foundCacheComponentList . push ( cacheComponent ) ; } if ( cacheComponent ) { component . instanceScope = cacheComponent . instanceScope ; if ( ! utils . isEqual ( component . instanceScope . $saveInputs , component . inputs ) ) { if ( component . instanceScope . nvReceiveInputs ) component . instanceScope . nvReceiveInputs ( { ... component . inputs } ) ; component . instanceScope . $saveInputs = component . inputs ; for ( const key in component . inputs ) { if ( component . instanceScope . $inputsList ) { component . instanceScope . $inputsList . forEach ( ( { propertyName , inputName } ) => { if ( inputName === key ) ( component . instanceScope as any ) [ propertyName ] = component . inputs [ key ] ; } ) ; } } component . isDirty = true ; } } else { component . instanceScope = buildComponentScope ( component . constructorFunction , component . inputs , component . nativeElement , componentInstance ) ; } component . instanceScope . $indivInstance = componentInstance . $indivInstance ; component . instanceScope . $nvContent = component . content ; if ( ! cacheComponent ) { lifecycleCaller ( component . instanceScope , 'nvOnInit' ) ; lifecycleCaller ( component . instanceScope , 'watchData' ) ; lifecycleCaller ( component . instanceScope , 'nvBeforeMount' ) ; } } const cacheComponentListLength = cacheComponentList . length ; for ( let i = 0 ; i < cacheComponentListLength ; i ++ ) { const cache = cacheComponentList [ i ] ; emitDirectiveDestory ( cache . instanceScope . $directiveList ) ; emitComponentDestory ( cache . instanceScope . $componentList ) ; lifecycleCaller ( cache . instanceScope , 'nvOnDestory' ) ; } for ( let i = 0 ; i < componentListLength ; i ++ ) { const component = componentInstance . $componentList [ i ] ; if ( ! foundCacheComponentList . find ( cache => cache . nativeElement === component . nativeElement ) ) { await component . instanceScope . render ( ) ; if ( ! component . instanceScope . $indivInstance . getIndivEnv . isServerRendering ) lifecycleCaller ( component . instanceScope , 'nvAfterMount' ) ; } else { if ( component . isDirty ) { if ( component . instanceScope . $nvChangeDetection === ChangeDetectionStrategy . OnPush ) { if ( component . instanceScope . nvDoCheck ) component . instanceScope . nvDoCheck ( ) ; await component . instanceScope . render ( ) ; } component . isDirty = false ; } } } buildViewChildandChildren ( componentInstance ) ; buildContentChildandChildren ( componentInstance ) ; } export param { IComponent } componentInstance param { TComAndDir } componentAndDirectives export function componentsConstructor ( componentInstance , componentAndDirectives ) { componentInstance . $componentList = [ ] ; componentAndDirectives . components . forEach ( component => { const declaration = componentInstance . $declarationMap . get ( component . name ) ; componentInstance . $componentList . push ( { nativeElement : component . nativeElement , inputs : component . inputs , instanceScope : null , constructorFunction : declaration , content : component . content , isFromContent : component . isFromContent , isDirty : false , } ) ; } ) ; } export param { Vnode } vnode param { TComAndDir } componentAndDirectives param { Vnode [ ] } contentComponentStack export function buildComponentsAndDirectives ( vnode , componentAndDirectives , contentComponentStack ) { if ( vnode . type === 'text' ) return ; const componentInputs = { } ; const fromContent = contentComponentStack . length ? true : false ; let hasPushStack = false ; if ( isTagName ( vnode , 'nv-content' ) && vnode . childNodes && vnode . childNodes . length > 0 ) { contentComponentStack . push ( vnode ) ; hasPushStack = true ; } if ( vnode . childNodes && vnode . childNodes . length > 0 ) vnode . childNodes . forEach ( child => { buildComponentsAndDirectives ( child , componentAndDirectives , contentComponentStack ) ; } ) ; if ( vnode . attributes && vnode . attributes . length > 0 ) { vnode . attributes . forEach ( attr => { if ( attr . type === 'directive' ) { componentAndDirectives . directives . push ( { nativeElement : vnode . nativeElement , inputs : attr . nvValue , name : attr . name , isFromContent : fromContent , } ) ; } if ( attr . type === 'prop' ) componentInputs [ attr . name ] = attr . nvValue ; } ) ; } if ( vnode . type === 'component' ) { componentAndDirectives . components . push ( { nativeElement : vnode . nativeElement , inputs : componentInputs , name : vnode . tagName , content : vnode . childNodes , isFromContent : fromContent , } ) ; } if ( hasPushStack ) contentComponentStack . pop ( ) ; } export param { * } nativeElement param { IComponent } componentInstance returns { Promise < IComponent > } export async function componentCompiler ( nativeElement , componentInstance ) { if ( ! componentInstance . $parseVnodeOptions ) { componentInstance . $parseVnodeOptions = { components : [ ] , directives : [ ] , } ; componentInstance . $declarationMap . forEach ( ( value , key ) => { if ( componentInstance . $parseVnodeOptions . components . indexOf ( key ) === - 1 && ( value as any ) . nvType === 'nvComponent' ) componentInstance . $parseVnodeOptions . components . push ( key ) ; if ( componentInstance . $parseVnodeOptions . directives . indexOf ( key ) === - 1 && ( value as any ) . nvType === 'nvDirective' ) componentInstance . $parseVnodeOptions . directives . push ( key ) ; } ) ; } if ( ! componentInstance . $templateVnode ) componentInstance . $templateVnode = parseTemplateToVnode ( componentInstance . $template , componentInstance . $parseVnodeOptions ) ; if ( ! componentInstance . $compileInstance ) componentInstance . $compileInstance = new Compile ( nativeElement , componentInstance ) ; let saveVnode = [ ] ; try { saveVnode = componentInstance . $compileInstance . startCompile ( ) ; } catch ( error ) { throw new Error ( `Error: ${ error }, compoent ${ ( componentInstance . constructor as any ) . selector } was compiled failed!` ) ; } componentInstance . $saveVnode = [ ... saveVnode ] ; let componentAndDirectives = { components : [ ] , directives : [ ] } ; let contentComponentStack = [ ] ; saveVnode . forEach ( vnode => buildComponentsAndDirectives ( vnode , componentAndDirectives , contentComponentStack ) ) ; try { mountDirective ( componentInstance , componentAndDirectives ) ; } catch ( error ) { throw new Error ( `Error: ${ error }, directives of compoent ${ ( componentInstance . constructor as any ) . selector } were compiled failed!` ) ; } try { await mountComponent ( componentInstance , componentAndDirectives ) ; lifecycleCaller ( componentInstance , 'nvHasRender' ) ; } catch ( error ) { throw new Error ( `Error: ${ error }, components of compoent ${ ( componentInstance . constructor as any ) . selector } were compiled failed!` ) ; } componentAndDirectives = null ; contentComponentStack = null ; return componentInstance ; }