import { INvModule , IComponent } from '../types' ; import { getService , NvInstanceFactory , rootInjector , Type } from '@indiv/di' ; import { NvModuleFactory } from '../nv-module' ; import { Renderer , Vnode } from '../vnode' ; import { ElementRef } from '../component' ; import { lifecycleCaller } from '../lifecycle' ; export interface IPlugin { bootstrap ( vm ) : void ; } class export class InDiv { public static globalApplication : InDiv ; private readonly pluginList : IPlugin [ ] = [ ] ; private rootElement : any ; private routeDOMKey : string = 'router-render' ; private rootModule : INvModule = null ; private declarations : Function [ ] ; private bootstrapComponent : IComponent ; private renderer : Renderer ; private isServerRendering : boolean = false ; private indivEnv : string = 'browser' ; private templateRootPath ? : string ; memberof constructor ( ) { rootInjector . setProviderAndInstance ( InDiv , InDiv , this ) ; } static param { Type < INvModule > } Nvmodule param { { plugins ? : Type < IPlugin > [ ] , ssrTemplateRootPath ? : string , * } } options returns { Promise < InDiv > } memberof public static async bootstrapFactory ( Nvmodule , options = { } ) { if ( ! InDiv . globalApplication ) InDiv . globalApplication = new InDiv ( ) ; else return InDiv . globalApplication ; InDiv . globalApplication . bootstrapModule ( Nvmodule ) ; if ( options . plugins ) options . plugins . forEach ( plugin => InDiv . globalApplication . use ( plugin ) ) ; if ( options . ssrTemplateRootPath ) InDiv . globalApplication . setTemplateRootPath ( options . ssrTemplateRootPath ) ; await InDiv . globalApplication . init ( ) ; return InDiv . globalApplication ; } param { Type < IPlugin > } Modal returns { number } memberof public use ( Plugin ) { const newPlugin = new Plugin ( ) ; newPlugin . bootstrap ( this ) ; return this . pluginList . push ( newPlugin ) - 1 ; } param { * } NewRenderer memberof public setRenderer ( NewRenderer ) { const _renderer = new NewRenderer ( ) ; if ( _renderer instanceof Renderer ) { this . renderer = _renderer ; rootInjector . setProvider ( Renderer , NewRenderer ) ; rootInjector . setInstance ( Renderer , this . renderer ) ; } else throw new Error ( 'Custom Renderer must extend class Renderer!' ) ; } readonly type { InDiv } memberof public get getGlobalApplication ( ) : InDiv { return InDiv . globalApplication ; } readonly type { Renderer } memberof public get getRenderer ( ) : Renderer { return this . renderer ; } readonly type { IComponent } memberof public get getBootstrapComponent ( ) : IComponent { return this . bootstrapComponent ; } param { string } routeDOMKey memberof public setRouteDOMKey ( routeDOMKey ) { this . routeDOMKey = routeDOMKey ; } readonly type { string } memberof public get getRouteDOMKey ( ) : string { return this . routeDOMKey ; } readonly type { INvModule } memberof public get getRootModule ( ) : INvModule { return this . rootModule ; } readonly type { Function [ ] } memberof public get getDeclarations ( ) : Function [ ] { return this . declarations ; } param { * } node memberof public setRootElement ( node ) { this . rootElement = node ; } readonly type { * } memberof public get getRootElement ( ) : any { return this . rootElement ; } param { string } env param { boolean } isServerRendering memberof public setIndivEnv ( env , isServerRendering ? ) { this . indivEnv = env ; if ( arguments . length === 2 ) this . isServerRendering = isServerRendering ; } readonly type { { isServerRendering : boolean ; indivEnv : string ; } } memberof public get getIndivEnv ( ) : { isServerRendering : boolean ; indivEnv : string ; } { return { isServerRendering : this . isServerRendering , indivEnv : this . indivEnv } ; } param { string } templateRootPath memberof public setTemplateRootPath ( templateRootPath ) { if ( ! this . isServerRendering ) return ; this . templateRootPath = templateRootPath ; } readonly type { string } memberof public get getTemplateRootPath ( ) : string { return this . templateRootPath ; } param { IComponent } component memberof public templateChecker ( component ) { } param { ( component : IComponent ) => void } checker memberof public setTemplateChecker ( checker ) { this . templateChecker = checker ; } param { Function } Nvmodule returns { INvModule } memberof public bootstrapModule ( Nvmodule ) { if ( ! Nvmodule ) throw new Error ( 'must send a root module' ) ; this . rootModule = NvModuleFactory ( Nvmodule , true ) ; this . declarations = [ ... this . rootModule . $declarations ] ; return this . rootModule ; } template R returns { Promise < void > } memberof public async init < R = Element > ( ) { if ( ! this . rootModule ) throw new Error ( 'must use bootstrapModule to declare a root NvModule before init' ) ; if ( ! this . renderer ) throw new Error ( 'must use plugin of platform to set a renderer in InDiv!' ) ; return await this . renderModuleBootstrap < R > ( ) ; } template R param { Function } BootstrapComponent param { R } nativeElement param { INvModule } otherModule returns { IComponent } memberof public initComponent < R = Element > ( BootstrapComponent , nativeElement , otherModule ? ) { const injector = otherModule ? otherModule . $privateInjector . fork ( ) : this . rootModule . $privateInjector . fork ( ) ; injector . setProviderAndInstance ( ElementRef , ElementRef , new ElementRef < R > ( nativeElement ) ) ; const component = NvInstanceFactory ( BootstrapComponent , null , injector ) ; component . $indivInstance = this ; if ( otherModule ) { otherModule . $declarations . forEach ( ( findDeclaration ) => { if ( ! component . $declarationMap . has ( ( findDeclaration as any ) . selector ) ) component . $declarationMap . set ( ( findDeclaration as any ) . selector , findDeclaration ) ; } ) ; } else { this . rootModule . $declarations . forEach ( ( findDeclaration ) => { if ( ! component . $declarationMap . has ( ( findDeclaration as any ) . selector ) ) component . $declarationMap . set ( ( findDeclaration as any ) . selector , findDeclaration ) ; } ) ; } lifecycleCaller ( component , 'nvOnInit' ) ; lifecycleCaller ( component , 'watchData' ) ; if ( ! component . $template && ! component . $templateUrl ) throw new Error ( 'must set template or templateUrl in bootstrap component' ) ; return component ; } template R param { IComponent } component param { R } nativeElement param { Vnode [ ] } initVnode returns { Promise < IComponent > } memberof public async runComponentRenderer < R = Element > ( component , nativeElement , initVnode ? ) { if ( ( component . $template || component . $templateUrl ) && nativeElement ) { lifecycleCaller ( component , 'nvBeforeMount' ) ; await this . render < R > ( component , nativeElement , initVnode ) ; if ( ! this . isServerRendering ) lifecycleCaller ( component , 'nvAfterMount' ) ; return component ; } else { throw new Error ( 'renderBootstrap failed: template or rootDom is not exit' ) ; } } template R param { Function } BootstrapComponent param { R } nativeElement param { INvModule } otherModule param { Vnode [ ] } initVnode returns { Promise < IComponent > } memberof public renderComponent < R = Element > ( BootstrapComponent , nativeElement , otherModule ? , initVnode ? ) { const component = this . initComponent ( BootstrapComponent , nativeElement , otherModule ) ; return this . runComponentRenderer ( component , nativeElement , initVnode ) ; } private template R returns { Promise < IComponent > } memberof private async renderModuleBootstrap < R = Element > ( ) { if ( ! this . rootModule . $bootstrap ) throw new Error ( 'need bootstrap for render Module Bootstrap' ) ; const BootstrapComponent = this . rootModule . $bootstrap ; this . bootstrapComponent = await this . renderComponent < R > ( BootstrapComponent , this . rootElement ) ; return this . bootstrapComponent ; } private template R param { IComponent } component param { R } nativeElement param { Vnode [ ] } initVnode returns { Promise < void > } memberof private async render < R = Element > ( component , nativeElement , initVnode ? ) { if ( initVnode ) component . $saveVnode = initVnode ; component . $nativeElement = nativeElement ; await component . render ( ) ; } }