import { ComponentInternalInstance, Data, getExposeProxy, isStatefulComponent } from './component' import { nextTick, queueJob } from './scheduler' import { instanceWatch, WatchOptions, WatchStopHandle } from './apiWatch' import { EMPTY_OBJ, hasOwn, isGloballyWhitelisted, NOOP, extend, isString, isFunction, UnionToIntersection } from '@vue/shared' import { toRaw, shallowReadonly, track, TrackOpTypes, ShallowUnwrapRef, UnwrapNestedRefs } from '@vue/reactivity' import { ExtractComputedReturns, ComponentOptionsBase, ComputedOptions, MethodOptions, ComponentOptionsMixin, OptionTypesType, OptionTypesKeys, resolveMergedOptions, shouldCacheAccess, MergedComponentOptionsOverride, InjectToObject, ComponentInjectOptions } from './componentOptions' import { EmitsOptions, EmitFn } from './componentEmits' import { Slots } from './componentSlots' import { markAttrsAccessed } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderContext' import { warn } from './warning' import { installCompatInstanceProperties } from './compat/instance' /** * Custom properties added to component instances in any way and can be accessed through `this` * * @example * Here is an example of adding a property `$router` to every component instance: * ```ts * import { createApp } from 'vue' * import { Router, createRouter } from 'vue-router' * * declare module '@vue/runtime-core' { * interface ComponentCustomProperties { * $router: Router * } * } * * // effectively adding the router to every component instance * const app = createApp({}) * const router = createRouter() * app.config.globalProperties.$router = router * * const vm = app.mount('#app') * // we can access the router from the instance * vm.$router.push('/') * ``` */ export interface ComponentCustomProperties {} type IsDefaultMixinComponent = T extends ComponentOptionsMixin ? ComponentOptionsMixin extends T ? true : false : false type MixinToOptionTypes = T extends ComponentOptionsBase< infer P, infer B, infer D, infer C, infer M, infer Mixin, infer Extends, any, any, infer Defaults > ? OptionTypesType

& IntersectionMixin & IntersectionMixin : never // ExtractMixin(map type) is used to resolve circularly references type ExtractMixin = { Mixin: MixinToOptionTypes }[T extends ComponentOptionsMixin ? 'Mixin' : never] type IntersectionMixin = IsDefaultMixinComponent extends true ? OptionTypesType<{}, {}, {}, {}, {}> : UnionToIntersection> type UnwrapMixinsType< T, Type extends OptionTypesKeys > = T extends OptionTypesType ? T[Type] : never type EnsureNonVoid = T extends void ? {} : T export type ComponentPublicInstanceConstructor< T extends ComponentPublicInstance< Props, RawBindings, D, C, M > = ComponentPublicInstance, Props = any, RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, M extends MethodOptions = MethodOptions > = { __isFragment?: never __isTeleport?: never __isSuspense?: never new (...args: any[]): T } export type CreateComponentPublicInstance< P = {}, B = {}, D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, I extends ComponentInjectOptions = {}, PublicMixin = IntersectionMixin & IntersectionMixin, PublicP = UnwrapMixinsType & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, PublicD = UnwrapMixinsType & EnsureNonVoid, PublicC extends ComputedOptions = UnwrapMixinsType & EnsureNonVoid, PublicM extends MethodOptions = UnwrapMixinsType & EnsureNonVoid, PublicDefaults = UnwrapMixinsType & EnsureNonVoid > = ComponentPublicInstance< PublicP, PublicB, PublicD, PublicC, PublicM, E, PublicProps, PublicDefaults, MakeDefaultsOptional, ComponentOptionsBase, I > // public properties exposed on the proxy, which is used as the render context // in templates (as `this` in the render option) export type ComponentPublicInstance< P = {}, // props type extracted from props option B = {}, // raw bindings returned from setup() D = {}, // return from data() C extends ComputedOptions = {}, M extends MethodOptions = {}, E extends EmitsOptions = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, Options = ComponentOptionsBase, I extends ComponentInjectOptions = {} > = { $: ComponentInternalInstance $data: D $props: MakeDefaultsOptional extends true ? Partial & Omit

: P & PublicProps $attrs: Data $refs: Data $slots: Slots $root: ComponentPublicInstance | null $parent: ComponentPublicInstance | null $emit: EmitFn $el: any $options: Options & MergedComponentOptionsOverride $forceUpdate: () => void $nextTick: typeof nextTick $watch any)>( source: T, cb: T extends (...args: any) => infer R ? (...args: [R, R]) => any : (...args: any) => any, options?: WatchOptions ): WatchStopHandle } & P & ShallowUnwrapRef & UnwrapNestedRefs & ExtractComputedReturns & M & ComponentCustomProperties & InjectToObject export type PublicPropertiesMap = Record< string, (i: ComponentInternalInstance) => any > /** * #2437 In Vue 3, functional components do not have a public instance proxy but * they exist in the internal parent chain. For code that relies on traversing * public $parent chains, skip functional ones and go to the parent instead. */ const getPublicInstance = ( i: ComponentInternalInstance | null ): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null => { if (!i) return null if (isStatefulComponent(i)) return getExposeProxy(i) || i.proxy return getPublicInstance(i.parent) } export const publicPropertiesMap: PublicPropertiesMap = // Move PURE marker to new line to workaround compiler discarding it // due to type annotation /*#__PURE__*/ extend(Object.create(null), { $: i => i, $el: i => i.vnode.el, $data: i => i.data, $props: i => (__DEV__ ? shallowReadonly(i.props) : i.props), $attrs: i => (__DEV__ ? shallowReadonly(i.attrs) : i.attrs), $slots: i => (__DEV__ ? shallowReadonly(i.slots) : i.slots), $refs: i => (__DEV__ ? shallowReadonly(i.refs) : i.refs), $parent: i => getPublicInstance(i.parent), $root: i => getPublicInstance(i.root), $emit: i => i.emit, $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type), $forceUpdate: i => i.f || (i.f = () => queueJob(i.update)), $nextTick: i => i.n || (i.n = nextTick.bind(i.proxy!)), $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP) } as PublicPropertiesMap) if (__COMPAT__) { installCompatInstanceProperties(publicPropertiesMap) } const enum AccessTypes { OTHER, SETUP, DATA, PROPS, CONTEXT } export interface ComponentRenderContext { [key: string]: any _: ComponentInternalInstance } export const isReservedPrefix = (key: string) => key === '_' || key === '$' const hasSetupBinding = (state: Data, key: string) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key) export const PublicInstanceProxyHandlers: ProxyHandler = { get({ _: instance }: ComponentRenderContext, key: string) { const { ctx, setupState, data, props, accessCache, type, appContext } = instance // for internal formatters to know that this is a Vue instance if (__DEV__ && key === '__isVue') { return true } // data / props / ctx // This getter gets called for every property access on the render context // during render and is a major hotspot. The most expensive part of this // is the multiple hasOwn() calls. It's much faster to do a simple property // access on a plain object, so we use an accessCache object (with null // prototype) to memoize what access type a key corresponds to. let normalizedProps if (key[0] !== '$') { const n = accessCache![key] if (n !== undefined) { switch (n) { case AccessTypes.SETUP: return setupState[key] case AccessTypes.DATA: return data[key] case AccessTypes.CONTEXT: return ctx[key] case AccessTypes.PROPS: return props![key] // default: just fallthrough } } else if (hasSetupBinding(setupState, key)) { accessCache![key] = AccessTypes.SETUP return setupState[key] } else if (data !== EMPTY_OBJ && hasOwn(data, key)) { accessCache![key] = AccessTypes.DATA return data[key] } else if ( // only cache other properties when instance has declared (thus stable) // props (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key) ) { accessCache![key] = AccessTypes.PROPS return props![key] } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { accessCache![key] = AccessTypes.CONTEXT return ctx[key] } else if (!__FEATURE_OPTIONS_API__ || shouldCacheAccess) { accessCache![key] = AccessTypes.OTHER } } const publicGetter = publicPropertiesMap[key] let cssModule, globalProperties // public $xxx properties if (publicGetter) { if (key === '$attrs') { track(instance, TrackOpTypes.GET, key) __DEV__ && markAttrsAccessed() } return publicGetter(instance) } else if ( // css module (injected by vue-loader) (cssModule = type.__cssModules) && (cssModule = cssModule[key]) ) { return cssModule } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { // user may set custom properties to `this` that start with `$` accessCache![key] = AccessTypes.CONTEXT return ctx[key] } else if ( // global properties ((globalProperties = appContext.config.globalProperties), hasOwn(globalProperties, key)) ) { if (__COMPAT__) { const desc = Object.getOwnPropertyDescriptor(globalProperties, key)! if (desc.get) { return desc.get.call(instance.proxy) } else { const val = globalProperties[key] return isFunction(val) ? Object.assign(val.bind(instance.proxy), val) : val } } else { return globalProperties[key] } } else if ( __DEV__ && currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading // to infinite warning loop key.indexOf('__v') !== 0) ) { if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) { warn( `Property ${JSON.stringify( key )} must be accessed via $data because it starts with a reserved ` + `character ("$" or "_") and is not proxied on the render context.` ) } else if (instance === currentRenderingInstance) { warn( `Property ${JSON.stringify(key)} was accessed during render ` + `but is not defined on instance.` ) } } }, set( { _: instance }: ComponentRenderContext, key: string, value: any ): boolean { const { data, setupState, ctx } = instance if (hasSetupBinding(setupState, key)) { setupState[key] = value return true } else if ( __DEV__ && setupState.__isScriptSetup && hasOwn(setupState, key) ) { warn(`Cannot mutate