import { ComponentOptionsMixin , ComponentOptionsWithArrayProps , ComponentOptionsWithObjectProps , ComponentOptionsWithoutProps , ComponentPropsOptions , ComponentPublicInstance , ComputedOptions , EmitsOptions , MethodOptions , RenderFunction , SetupContext , ComponentInternalInstance , VNode , RootHydrateFunction , ExtractPropTypes , createVNode , defineComponent , nextTick , warn , ConcreteComponent , ComponentOptions } from '@vue/runtime-core' ; import { camelize , extend , hyphenate , isArray , toNumber } from '@vue/shared' ; import { hydrate , render } from '.' ; export type VueElementConstructor < P = { } > = { new ( initialProps ? ) : VueElement & P ; } ; export function defineCustomElement < Props , RawBindings = object > ( setup ) ; export function defineCustomElement < Props = { } , RawBindings = { } , D = { } , C extends ComputedOptions = { } , M extends MethodOptions = { } , Mixin extends ComponentOptionsMixin = ComponentOptionsMixin , Extends extends ComponentOptionsMixin = ComponentOptionsMixin , E extends EmitsOptions = EmitsOptions , EE extends string = string > ( options ) ; export function defineCustomElement < PropNames extends string , RawBindings , D , C extends ComputedOptions = { } , M extends MethodOptions = { } , Mixin extends ComponentOptionsMixin = ComponentOptionsMixin , Extends extends ComponentOptionsMixin = ComponentOptionsMixin , E extends EmitsOptions = Record < string , any > , EE extends string = string > ( options ) ; export function defineCustomElement < PropsOptions extends Readonly < ComponentPropsOptions > , RawBindings , D , C extends ComputedOptions = { } , M extends MethodOptions = { } , Mixin extends ComponentOptionsMixin = ComponentOptionsMixin , Extends extends ComponentOptionsMixin = ComponentOptionsMixin , E extends EmitsOptions = Record < string , any > , EE extends string = string > ( options ) ; export function defineCustomElement ( options ) ; export function defineCustomElement ( options , hydate ? ) { const Comp = defineComponent ( options as any ) ; class VueCustomElement extends VueElement { static def = Comp ; constructor ( initialProps ? ) { super ( Comp , initialProps , hydate ) ; } } return VueCustomElement ; } export const defineSSRCustomElement = ( ( options ) => { return defineCustomElement ( options , hydrate ) ; } ) as typeof defineCustomElement ; const BaseClass = ( typeof HTMLElement !== 'undefined' ? HTMLElement : class { } ) as typeof HTMLElement ; type InnerComponentDef = ConcreteComponent & { styles ? : string [ ] ; } ; export class VueElement extends BaseClass { internal _instance : ComponentInternalInstance | null = null ; private _connected = false ; private _resolved = false ; private _styles ? : HTMLStyleElement [ ] ; constructor ( private _def , private _props = { } , hydrate ? ) { super ( ) ; if ( this . shadowRoot && hydrate ) { hydrate ( this . _createVNode ( ) , this . shadowRoot ) ; } else { if ( __DEV__ && this . shadowRoot ) { warn ( `Custom element has pre-rendered declarative shadow root but is not ` + `defined as hydratable. Use \`defineSSRCustomElement\`.` ) ; } this . attachShadow ( { mode : 'open' } ) ; } for ( let i = 0 ; i < this . attributes . length ; i ++ ) { this . _setAttr ( this . attributes [ i ] . name ) ; } const observer = new MutationObserver ( mutations => { for ( const m of mutations ) { this . _setAttr ( m . attributeName ! ) ; } } ) ; observer . observe ( this , { attributes : true } ) ; } connectedCallback ( ) { this . _connected = true ; if ( ! this . _instance ) { this . _resolveDef ( ) ; render ( this . _createVNode ( ) , this . shadowRoot ! ) ; } } disconnectedCallback ( ) { this . _connected = false ; nextTick ( ( ) => { if ( ! this . _connected ) { render ( null , this . shadowRoot ! ) ; this . _instance = null ; } } ) ; } /**
     * resolve inner component definition (handle possible async component)
     */ private _resolveDef ( ) { if ( this . _resolved ) { return ; } const resolve = ( def ) => { this . _resolved = true ; for ( const key of Object . keys ( this ) ) { if ( key [ 0 ] !== '_' ) { this . _setProp ( key , this [ key as keyof this ] ) ; } } const { props , styles } = def ; const rawKeys = props ? ( isArray ( props ) ? props : Object . keys ( props ) ) : [ ] ; for ( const key of rawKeys . map ( camelize ) ) { Object . defineProperty ( this , key , { get ( ) { return this . _getProp ( key ) ; } , set ( val ) { this . _setProp ( key , val ) ; } } ) ; } this . _applyStyles ( styles ) ; } ; const asyncDef = ( this . _def as ComponentOptions ) . __asyncLoader ; if ( asyncDef ) { asyncDef ( ) . then ( resolve ) ; } else { resolve ( this . _def ) ; } } protected _setAttr ( key ) { this . _setProp ( camelize ( key ) , toNumber ( this . getAttribute ( key ) ) , false ) ; } internal protected _getProp ( key ) { return this . _props [ key ] ; } internal protected _setProp ( key , val , shouldReflect = true ) { if ( val !== this . _props [ key ] ) { this . _props [ key ] = val ; if ( this . _instance ) { render ( this . _createVNode ( ) , this . shadowRoot ! ) ; } if ( shouldReflect ) { if ( val === true ) { this . setAttribute ( hyphenate ( key ) , '' ) ; } else if ( typeof val === 'string' || typeof val === 'number' ) { this . setAttribute ( hyphenate ( key ) , val + '' ) ; } else if ( ! val ) { this . removeAttribute ( hyphenate ( key ) ) ; } } } } private _createVNode ( ) { const vnode = createVNode ( this . _def , extend ( { } , this . _props ) ) ; if ( ! this . _instance ) { vnode . ce = instance => { this . _instance = instance ; instance . isCE = true ; if ( __DEV__ ) { instance . ceReload = newStyles => { if ( this . _styles ) { this . _styles . forEach ( s => this . shadowRoot ! . removeChild ( s ) ) ; this . _styles . length = 0 ; } this . _applyStyles ( newStyles ) ; if ( ! ( this . _def as ComponentOptions ) . __asyncLoader ) { this . _instance = null ; render ( this . _createVNode ( ) , this . shadowRoot ! ) ; } } ; } instance . emit = ( event , ... args ) => { this . dispatchEvent ( new CustomEvent ( event , { detail : args } ) ) ; } ; let parent = this ; while ( ( parent = parent && ( parent . parentNode || ( parent as ShadowRoot ) . host ) ) ) { if ( parent instanceof VueElement ) { instance . parent = parent . _instance ; break ; } } } ; } return vnode ; } private _applyStyles ( styles ) { if ( styles ) { styles . forEach ( css => { const s = document . createElement ( 'style' ) ; s . textContent = css ; this . shadowRoot ! . appendChild ( s ) ; if ( __DEV__ ) { ; ( this . _styles || ( this . _styles = [ ] ) ) . push ( s ) ; } } ) ; } } }