import { effect , pauseTracking , resetTracking , shallowReadonly , shallowReactive } from '@vue/reactivity' ; import { hyphenate , isFunction , isPromise } from '@vue/shared' ; import { exception , warn } from '../warn' ; import { HookTypes } from '../api/lifecycle' ; import * as Queue from './queue' ; import { adoptStyleSheets } from './css' ; import { render } from './compiler' ; import { propDefaults } from './props' ; import { applications } from '../api/app' ; import type { InjectionKey } from '../api/inject' ; import type { Directive } from './compiler' ; import type { IComponent , ComponentInstance , NormalizedComponentOptions , SetupContext , SetupResult , } from '../types' ; const instanceStack = [ ] ; const instances = new WeakMap < Element , ComponentInstance > ( ) ; returns export function getCurrentInstance ( ) { return instanceStack . length ? instanceStack [ instanceStack . length - 1 ] : null ; } param instance export function setCurrentInstance ( instance ) { if ( instance == null ) { instanceStack . pop ( ) ; } else { instanceStack . push ( instance ) ; } } param el returns export function getComponentInstance ( el ) { return instances . get ( el ) ; } param el param options export function createComponentInstance ( el , options ) { const { ShadyCSS , ShadowRoot } = window ; let shimAdoptedStyleSheets = false ; const parent = getCurrentInstance ( ) ; const instanceEffect = effect ( update , { lazy : true , scheduler : Queue . push } ) ; const props = shallowReactive ( options . props ? propDefaults ( options . props ) : { } ) ; let mounted = false ; let suspensible = false ; let setupResult = null ; let shadowRoot = null ; let provides = parent ?. provides ?? Object . create ( null ) ; let directives = null ; let emitted = null ; let events = null ; let hooks = null ; const instance = Object . create ( null ) ; instances . set ( el , Object . defineProperties ( instance , { el : { value : el } , effect : { value : instanceEffect } , root : { value : ( parent ?. root ?? instance ) } , parent : { value : parent } , props : { value : props } , setupResult : { get : ( ) => setupResult } , shadowRoot : { get : ( ) => shadowRoot } , provides : { get : ( ) => provides } , directives : { get : ( ) => directives } , emitted : { get : ( ) => emitted } , events : { get : ( ) => events } , mounted : { get : ( ) => mounted , } , suspensible : { get : ( ) => suspensible } , hooks : { get : ( ) => hooks } , options : { get : ( ) => options , set : ( value ) => ( options = value ) } , on : { value : on } , once : { value : once } , off : { value : off } , emit : { value : emit } , setup : { value : setup } , provide : { value : provide } , reflowStyles : { value : reflowStyles } , mount : { value : mount } , unmount : { value : unmount } , runHooks : { value : runHooks } , injectHook : { value : injectHook } , update : { value : update } , } ) ) ; function on ( event , listener ) { if ( ! events ) { events = { } ; } events [ event ] = listener ; } function once ( event , listener ) { if ( ! emitted ) { emitted = { } ; } if ( emitted [ event ] != null ) { emitted [ event ] = false ; } on ( event , listener ) ; } function off ( event , listener ? ) { const res = events ?. [ event ] ; if ( res && ( listener == null || res === listener ) ) { events [ event ] = null ; } } function emit ( event , ... args ) { const handler = events ?. [ event ] ; if ( ! handler ) { return ; } if ( __DEV__ && ! event . startsWith ( 'update:' ) ) { const name = hyphenate ( event ) ; const validator = options . emits [ name ] ; if ( isFunction ( validator ) ) { if ( ! validator ( ... args ) ) { warn ( `Component emitted event "${ name }" but it is not declared in the emits option.` ) ; } } else if ( validator !== null ) { warn ( `Invalid event arguments: event validation failed for event "${ name }".` ) ; } } if ( emitted ) { if ( emitted [ event ] === true ) { return ; } else if ( emitted [ event ] === false ) { emitted [ event ] = true ; } } handler . call ( null , ... args ) ; } function setup ( ) { directives = Object . create ( null ) ; hooks = Object . create ( null ) ; events = options . emits ? Object . create ( null ) : null ; provides = parent ? parent . provides : Object . create ( null ) ; const app = applications . get ( instance . root . el . id ) ; if ( app ) { if ( instance . root == instance ) { app . container = instance ; provides = Object . create ( app . context . provides ) ; } directives = Object . create ( app . context . directives ) ; } if ( options . hooks ) { Object . keys ( options . hooks ) . forEach ( key => { hooks [ key ] = new Set ( options . hooks [ key ] ) ; } ) ; } if ( options . directives ) { Object . keys ( options . directives ) . forEach ( key => { directives [ key ] = options . directives [ key ] ; } ) ; } try { const { setup } = options ; setCurrentInstance ( instance ) ; let setupProps ; let setupContext ; if ( setup . length >= 1 ) { if ( setup . length >= 2 ) { setupContext = { get attrs ( ) { return options . attrs ; } , get el ( ) { return el ; } , get emit ( ) { return emit ; } , } ; } setupProps = __DEV__ ? shallowReadonly ( props ) : shallowReactive ( props ) ; } pauseTracking ( ) ; const res = setup . call ( null , setupProps , setupContext ) ; if ( isFunction ( res ) ) { setupResult = res ; } else if ( res != null ) { throw new TypeError ( 'Setup function must return a TemplateFactory' ) ; } } catch ( ex ) { exception ( ex , 'setup()' ) ; } finally { resetTracking ( ) ; setCurrentInstance ( null ) ; } } function provide < T > ( key , value ) { if ( parent ?. provides === provides ) { provides = Object . create ( parent . provides ) ; } provides [ key as string ] = value ; } async function reflowStyles ( mount ? ) { const { tag , styles } = options ; if ( styles ?. length ) { if ( mount ) { ShadyCSS ?. styleElement ( el ) ; } else { ShadyCSS ?. styleSubtree ( el ) ; } if ( ShadowRoot && shadowRoot instanceof ShadowRoot ) { const res = await adoptStyleSheets ( shadowRoot , tag , styles ) ; shimAdoptedStyleSheets = ! res ; } } } function mount ( ) { setup ( ) ; if ( ! shadowRoot ) { shadowRoot = el . attachShadow ( { mode : 'open' } ) ; reflowStyles ( true ) ; } instanceEffect ( ) ; mounted = true ; } function unmount ( ) { runHooks ( HookTypes . BEFORE_UNMOUNT ) ; mounted = false ; Queue . push ( ( ) => runHooks ( HookTypes . UNMOUNTED ) ) ; } function runHooks ( hook , ... args ) { const _hooks = hooks [ hook ] ; if ( _hooks ?. size ) { pauseTracking ( ) ; _hooks . forEach ( hook => hook . apply ( instance , args ) ) ; resetTracking ( ) ; } } function injectHook ( name , hook ) { if ( ! hooks [ name ] ) { hooks [ name ] = new Set ( ) ; } hooks [ name ] . add ( hook ) ; } function update ( ) { if ( setupResult == null || isPromise ( setupResult ) ) { return ; } runHooks ( mounted ? HookTypes . BEFORE_UPDATE : HookTypes . BEFORE_MOUNT ) ; try { setCurrentInstance ( instance ) ; const template = setupResult ( ) ; if ( template != null ) { render ( template , shadowRoot , { scopeName : options . tag } ) ; } else if ( __DEV__ ) { warn ( 'Nullable returned from setup, this could be intential when using Portal or Suspense' , '#update()' ) ; } } catch ( ex ) { exception ( ex ) ; } finally { setCurrentInstance ( null ) ; } if ( shimAdoptedStyleSheets ) { const style = document . createElement ( 'style' ) ; style . textContent = options . styles . join ( '\n' ) ; shadowRoot . insertBefore ( style , shadowRoot . firstChild ) ; shimAdoptedStyleSheets = false ; } runHooks ( mounted ? HookTypes . UPDATED : HookTypes . MOUNTED ) ; } return instance ; }