import type * as d from '../declarations' ; import { attachStyles } from './styles' ; import { BUILD , NAMESPACE } from '@app-data' ; import { CMP_FLAGS , HOST_FLAGS } from '@utils' ; import { consoleError , doc , getHostRef , nextTick , plt , writeTask , win } from '@platform' ; import { PLATFORM_FLAGS } from './runtime-constants' ; import { renderVdom } from './vdom/vdom-render' ; import { createTime } from './profile' ; import { emitEvent } from './event-emitter' ; export const attachToAncestor = ( hostRef , ancestorComponent ) => { if ( BUILD . asyncLoading && ancestorComponent && ! hostRef . $onRenderResolve$ && ancestorComponent [ 's-p' ] ) { ancestorComponent [ 's-p' ] . push ( new Promise ( ( r ) => ( hostRef . $onRenderResolve$ = r ) ) ) ; } } ; export const scheduleUpdate = ( hostRef , isInitialLoad ) => { if ( BUILD . taskQueue && BUILD . updatable ) { hostRef . $flags$ |= HOST_FLAGS . isQueuedForUpdate ; } if ( BUILD . asyncLoading && hostRef . $flags$ & HOST_FLAGS . isWaitingForChildren ) { hostRef . $flags$ |= HOST_FLAGS . needsRerender ; return ; } attachToAncestor ( hostRef , hostRef . $ancestorComponent$ ) ; const dispatch = ( ) => dispatchHooks ( hostRef , isInitialLoad ) ; return BUILD . taskQueue ? writeTask ( dispatch ) : dispatch ( ) ; } ; const dispatchHooks = ( hostRef , isInitialLoad ) => { const elm = hostRef . $hostElement$ ; const endSchedule = createTime ( 'scheduleUpdate' , hostRef . $cmpMeta$ . $tagName$ ) ; const instance = BUILD . lazyLoad ? hostRef . $lazyInstance$ : ( elm as any ) ; let promise ; if ( isInitialLoad ) { if ( BUILD . lazyLoad && BUILD . hostListener ) { hostRef . $flags$ |= HOST_FLAGS . isListenReady ; if ( hostRef . $queuedListeners$ ) { hostRef . $queuedListeners$ . map ( ( [ methodName , event ] ) => safeCall ( instance , methodName , event ) ) ; hostRef . $queuedListeners$ = null ; } } emitLifecycleEvent ( elm , 'componentWillLoad' ) ; if ( BUILD . cmpWillLoad ) { promise = safeCall ( instance , 'componentWillLoad' ) ; } } else { emitLifecycleEvent ( elm , 'componentWillUpdate' ) ; if ( BUILD . cmpWillUpdate ) { promise = safeCall ( instance , 'componentWillUpdate' ) ; } } emitLifecycleEvent ( elm , 'componentWillRender' ) ; if ( BUILD . cmpWillRender ) { promise = then ( promise , ( ) => safeCall ( instance , 'componentWillRender' ) ) ; } endSchedule ( ) ; return then ( promise , ( ) => updateComponent ( hostRef , instance , isInitialLoad ) ) ; } ; const updateComponent = async ( hostRef , instance , isInitialLoad ) => { const elm = hostRef . $hostElement$ as d . RenderNode ; const endUpdate = createTime ( 'update' , hostRef . $cmpMeta$ . $tagName$ ) ; const rc = elm [ 's-rc' ] ; if ( BUILD . style && isInitialLoad ) { attachStyles ( hostRef ) ; } const endRender = createTime ( 'render' , hostRef . $cmpMeta$ . $tagName$ ) ; if ( BUILD . isDev ) { hostRef . $flags$ |= HOST_FLAGS . devOnRender ; } if ( BUILD . hydrateServerSide ) { await callRender ( hostRef , instance , elm ) ; } else { callRender ( hostRef , instance , elm ) ; } if ( BUILD . cssVarShim && plt . $cssShim$ ) { plt . $cssShim$ . updateHost ( elm ) ; } if ( BUILD . isDev ) { hostRef . $renderCount$ ++ ; hostRef . $flags$ &= ~ HOST_FLAGS . devOnRender ; } if ( BUILD . hydrateServerSide ) { try { serverSideConnected ( elm ) ; if ( isInitialLoad ) { if ( hostRef . $cmpMeta$ . $flags$ & CMP_FLAGS . shadowDomEncapsulation ) { elm [ 's-en' ] = '' ; } else if ( hostRef . $cmpMeta$ . $flags$ & CMP_FLAGS . scopedCssEncapsulation ) { elm [ 's-en' ] = 'c' ; } } } catch ( e ) { consoleError ( e , elm ) ; } } if ( BUILD . asyncLoading && rc ) { rc . map ( ( cb ) => cb ( ) ) ; elm [ 's-rc' ] = undefined ; } endRender ( ) ; endUpdate ( ) ; if ( BUILD . asyncLoading ) { const childrenPromises = elm [ 's-p' ] ; const postUpdate = ( ) => postUpdateComponent ( hostRef ) ; if ( childrenPromises . length === 0 ) { postUpdate ( ) ; } else { Promise . all ( childrenPromises ) . then ( postUpdate ) ; hostRef . $flags$ |= HOST_FLAGS . isWaitingForChildren ; childrenPromises . length = 0 ; } } else { postUpdateComponent ( hostRef ) ; } } ; let renderingRef = null ; const callRender = ( hostRef , instance , elm ) => { const allRenderFn = BUILD . allRenderFn ? true : false ; const lazyLoad = BUILD . lazyLoad ? true : false ; const taskQueue = BUILD . taskQueue ? true : false ; const updatable = BUILD . updatable ? true : false ; try { renderingRef = instance ; instance = allRenderFn ? instance . render ( ) : instance . render && instance . render ( ) ; if ( updatable && taskQueue ) { hostRef . $flags$ &= ~ HOST_FLAGS . isQueuedForUpdate ; } if ( updatable || lazyLoad ) { hostRef . $flags$ |= HOST_FLAGS . hasRendered ; } if ( BUILD . hasRenderFn || BUILD . reflect ) { if ( BUILD . vdomRender || BUILD . reflect ) { if ( BUILD . hydrateServerSide ) { return Promise . resolve ( instance ) . then ( ( value ) => renderVdom ( hostRef , value ) ) ; } else { renderVdom ( hostRef , instance ) ; } } else { elm . textContent = instance ; } } } catch ( e ) { consoleError ( e , hostRef . $hostElement$ ) ; } renderingRef = null ; return null ; } ; export const getRenderingRef = ( ) => renderingRef ; export const postUpdateComponent = ( hostRef ) => { const tagName = hostRef . $cmpMeta$ . $tagName$ ; const elm = hostRef . $hostElement$ ; const endPostUpdate = createTime ( 'postUpdate' , tagName ) ; const instance = BUILD . lazyLoad ? hostRef . $lazyInstance$ : ( elm as any ) ; const ancestorComponent = hostRef . $ancestorComponent$ ; if ( BUILD . cmpDidRender ) { if ( BUILD . isDev ) { hostRef . $flags$ |= HOST_FLAGS . devOnRender ; } safeCall ( instance , 'componentDidRender' ) ; if ( BUILD . isDev ) { hostRef . $flags$ &= ~ HOST_FLAGS . devOnRender ; } } emitLifecycleEvent ( elm , 'componentDidRender' ) ; if ( ! ( hostRef . $flags$ & HOST_FLAGS . hasLoadedComponent ) ) { hostRef . $flags$ |= HOST_FLAGS . hasLoadedComponent ; if ( BUILD . asyncLoading && BUILD . cssAnnotations ) { addHydratedFlag ( elm ) ; } if ( BUILD . cmpDidLoad ) { if ( BUILD . isDev ) { hostRef . $flags$ |= HOST_FLAGS . devOnDidLoad ; } safeCall ( instance , 'componentDidLoad' ) ; if ( BUILD . isDev ) { hostRef . $flags$ &= ~ HOST_FLAGS . devOnDidLoad ; } } emitLifecycleEvent ( elm , 'componentDidLoad' ) ; endPostUpdate ( ) ; if ( BUILD . asyncLoading ) { hostRef . $onReadyResolve$ ( elm ) ; if ( ! ancestorComponent ) { appDidLoad ( tagName ) ; } } } else { if ( BUILD . cmpDidUpdate ) { if ( BUILD . isDev ) { hostRef . $flags$ |= HOST_FLAGS . devOnRender ; } safeCall ( instance , 'componentDidUpdate' ) ; if ( BUILD . isDev ) { hostRef . $flags$ &= ~ HOST_FLAGS . devOnRender ; } } emitLifecycleEvent ( elm , 'componentDidUpdate' ) ; endPostUpdate ( ) ; } if ( BUILD . hotModuleReplacement ) { elm [ 's-hmr-load' ] && elm [ 's-hmr-load' ] ( ) ; } if ( BUILD . method && BUILD . lazyLoad ) { hostRef . $onInstanceResolve$ ( elm ) ; } if ( BUILD . asyncLoading ) { if ( hostRef . $onRenderResolve$ ) { hostRef . $onRenderResolve$ ( ) ; hostRef . $onRenderResolve$ = undefined ; } if ( hostRef . $flags$ & HOST_FLAGS . needsRerender ) { nextTick ( ( ) => scheduleUpdate ( hostRef , false ) ) ; } hostRef . $flags$ &= ~ ( HOST_FLAGS . isWaitingForChildren | HOST_FLAGS . needsRerender ) ; } } ; export const forceUpdate = ( ref ) => { if ( BUILD . updatable ) { const hostRef = getHostRef ( ref ) ; const isConnected = hostRef . $hostElement$ . isConnected ; if ( isConnected && ( hostRef . $flags$ & ( HOST_FLAGS . hasRendered | HOST_FLAGS . isQueuedForUpdate ) ) === HOST_FLAGS . hasRendered ) { scheduleUpdate ( hostRef , false ) ; } return isConnected ; } return false ; } ; export const appDidLoad = ( who ) => { if ( BUILD . cssAnnotations ) { addHydratedFlag ( doc . documentElement ) ; } if ( BUILD . asyncQueue ) { plt . $flags$ |= PLATFORM_FLAGS . appLoaded ; } nextTick ( ( ) => emitEvent ( win , 'appload' , { detail : { namespace : NAMESPACE } } ) ) ; if ( BUILD . profile && performance . measure ) { performance . measure ( `[Stencil] ${ NAMESPACE } initial load (by ${ who })` , 'st:app:start' ) ; } } ; export const safeCall = ( instance , method , arg ? ) => { if ( instance && instance [ method ] ) { try { return instance [ method ] ( arg ) ; } catch ( e ) { consoleError ( e ) ; } } return undefined ; } ; const then = ( promise , thenFn ) => { return promise && promise . then ? promise . then ( thenFn ) : thenFn ( ) ; } ; const emitLifecycleEvent = ( elm , lifecycleName ) => { if ( BUILD . lifecycleDOMEvents ) { emitEvent ( elm , 'stencil_' + lifecycleName , { bubbles : true , composed : true , detail : { namespace : NAMESPACE , } , } ) ; } } ; const addHydratedFlag = ( elm ) => BUILD . hydratedClass ? elm . classList . add ( 'hydrated' ) : BUILD . hydratedAttribute ? elm . setAttribute ( 'hydrated' , '' ) : undefined ; const serverSideConnected = ( elm ) => { const children = elm . children ; if ( children != null ) { for ( let i = 0 , ii = children . length ; i < ii ; i ++ ) { const childElm = children [ i ] as any ; if ( typeof childElm . connectedCallback === 'function' ) { childElm . connectedCallback ( ) ; } serverSideConnected ( childElm ) ; } } } ;