import { createVNode , defineComponent , render } from 'vue' ; export class VueComponentFactory { private static getComponentDefinition ( component , parent ) { let componentDefinition ; if ( typeof component === 'string' ) { componentDefinition = this . searchForComponentInstance ( parent , component ) ; } else { componentDefinition = { extends : defineComponent ( { ... component } ) } ; } if ( ! componentDefinition ) { console . error ( `Could not find component with name of ${ component }. Is it in Vue.components?` ) ; } if ( componentDefinition . extends ) { if ( componentDefinition . extends . setup ) { componentDefinition . setup = componentDefinition . extends . setup ; } componentDefinition . extends . props = this . addParamsToProps ( componentDefinition . extends . props ) ; } else { componentDefinition . props = this . addParamsToProps ( componentDefinition . props ) ; } return componentDefinition ; } private static addParamsToProps ( props ) { if ( ! props || ( Array . isArray ( props ) && props . indexOf ( 'params' ) === - 1 ) ) { props = [ 'params' , ... ( props ? props : [ ] ) ] ; } else if ( typeof props === 'object' && ! props . params ) { props [ 'params' ] = { type : Object } ; } return props ; } public static createAndMountComponent ( component , params , parent ) { const componentDefinition = VueComponentFactory . getComponentDefinition ( component , parent ) ; if ( ! componentDefinition ) { return ; } const { vNode , destroy , el } = this . mount ( componentDefinition , { params : Object . freeze ( params ) } , parent ) ; return { componentInstance : vNode . component . proxy , element : el , destroy , } ; } public static mount ( component , props , parent ) { let vNode = createVNode ( component , props ) ; vNode . appContext = parent . $ . appContext ; vNode . appContext . provides = { ... ( vNode . appContext . provides ? vNode . appContext . provides : { } ) , ... ( parent . $parent . $options . provide ? parent . $parent . $options . provide : { } ) } ; let el = document . createElement ( 'div' ) ; render ( vNode , el ) ; const destroy = ( ) => { if ( el ) { render ( null , el ) ; } el = null ; vNode = null ; } ; return { vNode , destroy , el } ; } public static searchForComponentInstance ( parent , component , maxDepth = 10 , suppressError = false ) { let componentInstance = null ; let currentParent = parent . $parent ; let depth = 0 ; while ( ! componentInstance && currentParent && currentParent . $options && ( ++ depth < maxDepth ) ) { const currentParentAsThis = currentParent as any ; componentInstance = currentParentAsThis . $options && currentParentAsThis . $options . components ? currentParentAsThis . $options . components ! [ component as any ] : null ; currentParent = currentParent . $parent ; } if ( ! componentInstance ) { const components = parent . $ . appContext . components ; if ( components && components [ component ] ) { componentInstance = components [ component ] ; } } if ( ! componentInstance && ! suppressError ) { console . error ( `Could not find component with name of ${ component }. Is it in Vue.components?` ) ; return null ; } return componentInstance ; } }