// All vue files combined. import { type VNode, type VNodeChild, isVNode } from './vnode' import { EffectScope, type ReactiveEffect, TrackOpTypes, isRef, markRaw, pauseTracking, proxyRefs, resetTracking, shallowReadonly, track, } from '@vue/reactivity' import { type ComponentPublicInstance, type ComponentPublicInstanceConstructor, PublicInstanceProxyHandlers, RuntimeCompiledPublicInstanceProxyHandlers, createDevRenderContext, exposePropsOnRenderContext, exposeSetupStateOnRenderContext, publicPropertiesMap, } from './componentPublicInstance' import { type ComponentPropsOptions, type NormalizedPropsOptions, initProps, normalizePropsOptions, } from './componentProps' import { type InternalSlots, type Slots, type SlotsType, type UnwrapSlotsType, initSlots, } from './componentSlots' import { warn } from './warning' import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling' import { type AppConfig, type AppContext, createAppContext, } from './apiCreateApp' import { type Directive, validateDirectiveName } from './directives' import { type ComponentOptions, type ComputedOptions, type MergedComponentOptions, type MethodOptions, applyOptions, resolveMergedOptions, } from './componentOptions' import { type EmitFn, type EmitsOptions, type EmitsToProps, type ObjectEmitsOptions, type ShortEmitsToObject, emit, normalizeEmitsOptions, } from './componentEmits' import { EMPTY_OBJ, type IfAny, NOOP, ShapeFlags, extend, getGlobalThis, isArray, isFunction, isObject, isPromise, makeMap, } from '@vue/shared' import type { SuspenseBoundary } from './components/Suspense' import type { CompilerOptions } from '@vue/compiler-core' import { markAttrsAccessed } from './componentRenderUtils' import { currentRenderingInstance } from './componentRenderContext' import { endMeasure, startMeasure } from './profiling' import { convertLegacyRenderFn } from './compat/renderFn' import { type CompatConfig, globalCompatConfig, validateCompatConfig, } from './compat/compatConfig' import type { SchedulerJob } from './scheduler' import type { LifecycleHooks } from './enums' export type Data = Record /** * Public utility type for extracting the instance type of a component. * Works with all valid component definition types. This is intended to replace * the usage of `InstanceType` which only works for * constructor-based component definition types. * * Exmaple: * ```ts * const MyComp = { ... } * declare const instance: ComponentInstance * ``` */ export type ComponentInstance = T extends { new (): ComponentPublicInstance } ? InstanceType : T extends FunctionalComponent ? ComponentPublicInstance> : T extends Component< infer Props, infer RawBindings, infer D, infer C, infer M > ? // NOTE we override Props/RawBindings/D to make sure is not `unknown` ComponentPublicInstance< unknown extends Props ? {} : Props, unknown extends RawBindings ? {} : RawBindings, unknown extends D ? {} : D, C, M > : never // not a vue Component /** * For extending allowed non-declared props on components in TSX */ export interface ComponentCustomProps {} /** * Default allowed non-declared props on component in TSX */ export interface AllowedComponentProps { class?: unknown style?: unknown } // Note: can't mark this whole interface internal because some public interfaces // extend it. export interface ComponentInternalOptions { /** * @internal */ __scopeId?: string /** * @internal */ __cssModules?: Data /** * @internal */ __hmrId?: string /** * Compat build only, for bailing out of certain compatibility behavior */ __isBuiltIn?: boolean /** * This one should be exposed so that devtools can make use of it */ __file?: string /** * name inferred from filename */ __name?: string } export interface FunctionalComponent< P = {}, E extends EmitsOptions | Record = {}, S extends Record = any, EE extends EmitsOptions = ShortEmitsToObject, > extends ComponentInternalOptions { // use of any here is intentional so it can be a valid JSX Element constructor ( props: P & EmitsToProps, ctx: Omit>>, 'expose'>, ): any props?: ComponentPropsOptions

emits?: EE | (keyof EE)[] slots?: IfAny> inheritAttrs?: boolean displayName?: string compatConfig?: CompatConfig } export interface ClassComponent { new (...args: any[]): ComponentPublicInstance __vccOpts: ComponentOptions } /** * Concrete component type matches its actual value: it's either an options * object, or a function. Use this where the code expects to work with actual * values, e.g. checking if its a function or not. This is mostly for internal * implementation code. */ export type ConcreteComponent< Props = {}, RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, M extends MethodOptions = MethodOptions, E extends EmitsOptions | Record = {}, S extends Record = any, > = | ComponentOptions | FunctionalComponent /** * A type used in public APIs where a component type is expected. * The constructor type is an artificial type returned by defineComponent(). */ export type Component< Props = any, RawBindings = any, D = any, C extends ComputedOptions = ComputedOptions, M extends MethodOptions = MethodOptions, E extends EmitsOptions | Record = {}, S extends Record = any, > = | ConcreteComponent | ComponentPublicInstanceConstructor export type { ComponentOptions } export type LifecycleHook = (TFn & SchedulerJob)[] | null // use `E extends any` to force evaluating type to fix #2362 export type SetupContext< E = EmitsOptions, S extends SlotsType = {}, > = E extends any ? { attrs: Data slots: UnwrapSlotsType emit: EmitFn expose: = Record>( exposed?: Exposed, ) => void } : never /** * @internal */ export type InternalRenderFunction = { ( ctx: ComponentPublicInstance, cache: ComponentInternalInstance['renderCache'], // for compiler-optimized bindings $props: ComponentInternalInstance['props'], $setup: ComponentInternalInstance['setupState'], $data: ComponentInternalInstance['data'], $options: ComponentInternalInstance['ctx'], ): VNodeChild _rc?: boolean // isRuntimeCompiled // __COMPAT__ only _compatChecked?: boolean // v3 and already checked for v2 compat _compatWrapped?: boolean // is wrapped for v2 compat } /** * We expose a subset of properties on the internal instance as they are * useful for advanced external libraries and tools. */ export interface ComponentInternalInstance { uid: number type: ConcreteComponent parent: ComponentInternalInstance | null root: ComponentInternalInstance appContext: AppContext /** * Vnode representing this component in its parent's vdom tree */ vnode: VNode /** * The pending new vnode from parent updates * @internal */ next: VNode | null /** * Root vnode of this component's own vdom tree */ subTree: VNode /** * Render effect instance */ effect: ReactiveEffect /** * Bound effect runner to be passed to schedulers */ update: SchedulerJob /** * The render function that returns vdom tree. * @internal */ render: InternalRenderFunction | null /** * SSR render function * @internal */ ssrRender?: Function | null /** * Object containing values this component provides for its descendants * @internal */ provides: Data /** * Tracking reactive effects (e.g. watchers) associated with this component * so that they can be automatically stopped on component unmount * @internal */ scope: EffectScope /** * cache for proxy access type to avoid hasOwnProperty calls * @internal */ accessCache: Data | null /** * cache for render function values that rely on _ctx but won't need updates * after initialized (e.g. inline handlers) * @internal */ renderCache: (Function | VNode | undefined)[] /** * Resolved component registry, only for components with mixins or extends * @internal */ components: Record | null /** * Resolved directive registry, only for components with mixins or extends * @internal */ directives: Record | null /** * Resolved filters registry, v2 compat only * @internal */ filters?: Record /** * resolved props options * @internal */ propsOptions: NormalizedPropsOptions /** * resolved emits options * @internal */ emitsOptions: ObjectEmitsOptions | null /** * resolved inheritAttrs options * @internal */ inheritAttrs?: boolean /** * is custom element? * @internal */ isCE?: boolean /** * custom element specific HMR method * @internal */ ceReload?: (newStyles?: string[]) => void // the rest are only for stateful components --------------------------------- // main proxy that serves as the public instance (`this`) proxy: ComponentPublicInstance | null // exposed properties via expose() exposed: Record | null exposeProxy: Record | null /** * alternative proxy used only for runtime-compiled render functions using * `with` block * @internal */ withProxy: ComponentPublicInstance | null /** * This is the target for the public instance proxy. It also holds properties * injected by user options (computed, methods etc.) and user-attached * custom properties (via `this.x = ...`) * @internal */ ctx: Data // state data: Data props: Data attrs: Data slots: InternalSlots refs: Data emit: EmitFn attrsProxy: Data | null slotsProxy: Slots | null /** * used for keeping track of .once event handlers on components * @internal */ emitted: Record | null /** * used for caching the value returned from props default factory functions to * avoid unnecessary watcher trigger * @internal */ propsDefaults: Data /** * setup related * @internal */ setupState: Data /** * devtools access to additional info * @internal */ devtoolsRawSetupState?: any /** * @internal */ setupContext: SetupContext | null /** * suspense related * @internal */ suspense: SuspenseBoundary | null /** * suspense pending batch id * @internal */ suspenseId: number /** * @internal */ asyncDep: Promise | null /** * @internal */ asyncResolved: boolean // lifecycle isMounted: boolean isUnmounted: boolean isDeactivated: boolean /** * @internal */ [LifecycleHooks.BEFORE_CREATE]: LifecycleHook /** * @internal */ [LifecycleHooks.CREATED]: LifecycleHook /** * @internal */ [LifecycleHooks.BEFORE_MOUNT]: LifecycleHook /** * @internal */ [LifecycleHooks.MOUNTED]: LifecycleHook /** * @internal */ [LifecycleHooks.BEFORE_UPDATE]: LifecycleHook /** * @internal */ [LifecycleHooks.UPDATED]: LifecycleHook /** * @internal */ [LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook /** * @internal */ [LifecycleHooks.UNMOUNTED]: LifecycleHook /** * @internal */ [LifecycleHooks.RENDER_TRACKED]: LifecycleHook /** * @internal */ [LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook /** * @internal */ [LifecycleHooks.ACTIVATED]: LifecycleHook /** * @internal */ [LifecycleHooks.DEACTIVATED]: LifecycleHook /** * @internal */ [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook /** * @internal */ [LifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise> /** * For caching bound $forceUpdate on public proxy access * @internal */ f?: () => void /** * For caching bound $nextTick on public proxy access * @internal */ n?: () => Promise /** * `updateTeleportCssVars` * For updating css vars on contained teleports * @internal */ ut?: (vars?: Record) => void /** * dev only. For style v-bind hydration mismatch checks * @internal */ getCssVars?: () => Record /** * v2 compat only, for caching mutated $options * @internal */ resolvedOptions?: MergedComponentOptions } const emptyAppContext = createAppContext() let uid = 0 export function createComponentInstance( vnode: VNode, parent: ComponentInternalInstance | null, suspense: SuspenseBoundary | null, ): ComponentInternalInstance { const type = vnode.type as ConcreteComponent // inherit parent app context - or - if root, adopt from root vnode const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext const instance: ComponentInternalInstance = { uid: uid++, vnode, type, parent, appContext, root: null!, // to be immediately set next: null, subTree: null!, // will be set synchronously right after creation effect: null!, update: null!, // will be set synchronously right after creation scope: new EffectScope(true /* detached */), render: null, proxy: null, exposed: null, exposeProxy: null, withProxy: null, provides: parent ? parent.provides : Object.create(appContext.provides), accessCache: null!, renderCache: [], // local resolved assets components: null, directives: null, // resolved props and emits options propsOptions: normalizePropsOptions(type, appContext), emitsOptions: normalizeEmitsOptions(type, appContext), // emit emit: null!, // to be set immediately emitted: null, // props default value propsDefaults: EMPTY_OBJ, // inheritAttrs inheritAttrs: type.inheritAttrs, // state ctx: EMPTY_OBJ, data: EMPTY_OBJ, props: EMPTY_OBJ, attrs: EMPTY_OBJ, slots: EMPTY_OBJ, refs: EMPTY_OBJ, setupState: EMPTY_OBJ, setupContext: null, attrsProxy: null, slotsProxy: null, // suspense related suspense, suspenseId: suspense ? suspense.pendingId : 0, asyncDep: null, asyncResolved: false, // lifecycle hooks // not using enums here because it results in computed properties isMounted: false, isUnmounted: false, isDeactivated: false, bc: null, c: null, bm: null, m: null, bu: null, u: null, um: null, bum: null, da: null, a: null, rtg: null, rtc: null, ec: null, sp: null, } if (__DEV__) { instance.ctx = createDevRenderContext(instance) } else { instance.ctx = { _: instance } } instance.root = parent ? parent.root : instance instance.emit = emit.bind(null, instance) // apply custom element special handling if (vnode.ce) { vnode.ce(instance) } return instance } export let currentInstance: ComponentInternalInstance | null = null export const getCurrentInstance: () => ComponentInternalInstance | null = () => currentInstance || currentRenderingInstance let internalSetCurrentInstance: ( instance: ComponentInternalInstance | null, ) => void let setInSSRSetupState: (state: boolean) => void /** * The following makes getCurrentInstance() usage across multiple copies of Vue * work. Some cases of how this can happen are summarized in #7590. In principle * the duplication should be avoided, but in practice there are often cases * where the user is unable to resolve on their own, especially in complicated * SSR setups. * * Note this fix is technically incomplete, as we still rely on other singletons * for effectScope and global reactive dependency maps. However, it does make * some of the most common cases work. It also warns if the duplication is * found during browser execution. */ if (__SSR__) { type Setter = (v: any) => void const g = getGlobalThis() const registerGlobalSetter = (key: string, setter: Setter) => { let setters: Setter[] if (!(setters = g[key])) setters = g[key] = [] setters.push(setter) return (v: any) => { if (setters.length > 1) setters.forEach(set => set(v)) else setters[0](v) } } internalSetCurrentInstance = registerGlobalSetter( `__VUE_INSTANCE_SETTERS__`, v => (currentInstance = v), ) // also make `isInSSRComponentSetup` sharable across copies of Vue. // this is needed in the SFC playground when SSRing async components, since // we have to load both the runtime and the server-renderer from CDNs, they // contain duplicated copies of Vue runtime code. setInSSRSetupState = registerGlobalSetter( `__VUE_SSR_SETTERS__`, v => (isInSSRComponentSetup = v), ) } else { internalSetCurrentInstance = i => { currentInstance = i } setInSSRSetupState = v => { isInSSRComponentSetup = v } } export const setCurrentInstance = (instance: ComponentInternalInstance) => { const prev = currentInstance internalSetCurrentInstance(instance) instance.scope.on() return (): void => { instance.scope.off() internalSetCurrentInstance(prev) } } export const unsetCurrentInstance = (): void => { currentInstance && currentInstance.scope.off() internalSetCurrentInstance(null) } const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component') export function validateComponentName( name: string, { isNativeTag }: AppConfig, ): void { if (isBuiltInTag(name) || isNativeTag(name)) { warn( 'Do not use built-in or reserved HTML elements as component id: ' + name, ) } } export function isStatefulComponent(instance: ComponentInternalInstance): number { return instance.vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT } export let isInSSRComponentSetup = false export function setupComponent( instance: ComponentInternalInstance, isSSR = false, ): Promise | undefined { isSSR && setInSSRSetupState(isSSR) const { props, children } = instance.vnode const isStateful = isStatefulComponent(instance) initProps(instance, props, isStateful, isSSR) initSlots(instance, children) const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : undefined isSSR && setInSSRSetupState(false) return setupResult } function setupStatefulComponent( instance: ComponentInternalInstance, isSSR: boolean, ) { const Component = instance.type as ComponentOptions if (__DEV__) { if (Component.name) { validateComponentName(Component.name, instance.appContext.config) } if (Component.components) { const names = Object.keys(Component.components) for (let i = 0; i < names.length; i++) { validateComponentName(names[i], instance.appContext.config) } } if (Component.directives) { const names = Object.keys(Component.directives) for (let i = 0; i < names.length; i++) { validateDirectiveName(names[i]) } } if (Component.compilerOptions && isRuntimeOnly()) { warn( `"compilerOptions" is only supported when using a build of Vue that ` + `includes the runtime compiler. Since you are using a runtime-only ` + `build, the options should be passed via your build tool config instead.`, ) } } // 0. create render proxy property access cache instance.accessCache = Object.create(null) // 1. create public instance / render proxy instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers) if (__DEV__) { exposePropsOnRenderContext(instance) } // 2. call setup() const { setup } = Component if (setup) { const setupContext = (instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null) const reset = setCurrentInstance(instance) pauseTracking() const setupResult = callWithErrorHandling( setup, instance, ErrorCodes.SETUP_FUNCTION, [ __DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext, ], ) resetTracking() reset() if (isPromise(setupResult)) { setupResult.then(unsetCurrentInstance, unsetCurrentInstance) if (isSSR) { // return the promise so server-renderer can wait on it return setupResult .then((resolvedResult: unknown) => { handleSetupResult(instance, resolvedResult, isSSR) }) .catch(e => { handleError(e, instance, ErrorCodes.SETUP_FUNCTION) }) } else if (__FEATURE_SUSPENSE__) { // async setup returned Promise. // bail here and wait for re-entry. instance.asyncDep = setupResult if (__DEV__ && !instance.suspense) { const name = Component.name ?? 'Anonymous' warn( `Component <${name}>: setup function returned a promise, but no ` + ` boundary was found in the parent component tree. ` + `A component with async setup() must be nested in a ` + `in order to be rendered.`, ) } } else if (__DEV__) { warn( `setup() returned a Promise, but the version of Vue you are using ` + `does not support it yet.`, ) } } else { handleSetupResult(instance, setupResult, isSSR) } } else { finishComponentSetup(instance, isSSR) } } export function handleSetupResult( instance: ComponentInternalInstance, setupResult: unknown, isSSR: boolean, ): void { if (isFunction(setupResult)) { // setup returned an inline render function if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) { // when the function's name is `ssrRender` (compiled by SFC inline mode), // set it as ssrRender instead. instance.ssrRender = setupResult } else { instance.render = setupResult as InternalRenderFunction } } else if (isObject(setupResult)) { if (__DEV__ && isVNode(setupResult)) { warn( `setup() should not return VNodes directly - ` + `return a render function instead.`, ) } // setup returned bindings. // assuming a render function compiled from template is present. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { instance.devtoolsRawSetupState = setupResult } instance.setupState = proxyRefs(setupResult) if (__DEV__) { exposeSetupStateOnRenderContext(instance) } } else if (__DEV__ && setupResult !== undefined) { warn( `setup() should return an object. Received: ${ setupResult === null ? 'null' : typeof setupResult }`, ) } finishComponentSetup(instance, isSSR) } type CompileFunction = ( template: string | object, options?: CompilerOptions, ) => InternalRenderFunction let compile: CompileFunction | undefined let installWithProxy: (i: ComponentInternalInstance) => void /** * For runtime-dom to register the compiler. * Note the exported method uses any to avoid d.ts relying on the compiler types. */ export function registerRuntimeCompiler(_compile: any): void { compile = _compile installWithProxy = i => { if (i.render!._rc) { i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers) } } } // dev only export const isRuntimeOnly = (): boolean => !compile export function finishComponentSetup( instance: ComponentInternalInstance, isSSR: boolean, skipOptions?: boolean, ): void { const Component = instance.type as ComponentOptions if (__COMPAT__) { convertLegacyRenderFn(instance) if (__DEV__ && Component.compatConfig) { validateCompatConfig(Component.compatConfig) } } // template / render function normalization // could be already set when returned from setup() if (!instance.render) { // only do on-the-fly compile if not in SSR - SSR on-the-fly compilation // is done by server-renderer if (!isSSR && compile && !Component.render) { const template = (__COMPAT__ && instance.vnode.props && instance.vnode.props['inline-template']) || Component.template || resolveMergedOptions(instance).template if (template) { if (__DEV__) { startMeasure(instance, `compile`) } const { isCustomElement, compilerOptions } = instance.appContext.config const { delimiters, compilerOptions: componentCompilerOptions } = Component const finalCompilerOptions: CompilerOptions = extend( extend( { isCustomElement, delimiters, }, compilerOptions, ), componentCompilerOptions, ) if (__COMPAT__) { // pass runtime compat config into the compiler finalCompilerOptions.compatConfig = Object.create(globalCompatConfig) if (Component.compatConfig) { // @ts-expect-error types are not compatible extend(finalCompilerOptions.compatConfig, Component.compatConfig) } } Component.render = compile(template, finalCompilerOptions) if (__DEV__) { endMeasure(instance, `compile`) } } } instance.render = (Component.render || NOOP) as InternalRenderFunction // for runtime-compiled render functions using `with` blocks, the render // proxy used needs a different `has` handler which is more performant and // also only allows a whitelist of globals to fallthrough. if (installWithProxy) { installWithProxy(instance) } } // support for 2.x options if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) { const reset = setCurrentInstance(instance) pauseTracking() try { applyOptions(instance) } finally { resetTracking() reset() } } // warn missing template/render // the runtime compilation of template in SSR is done by server-render if (__DEV__ && !Component.render && instance.render === NOOP && !isSSR) { /* istanbul ignore if */ if (!compile && Component.template) { warn( `Component provided template option but ` + `runtime compilation is not supported in this build of Vue.` + (__ESM_BUNDLER__ ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".` : __ESM_BROWSER__ ? ` Use "vue.esm-browser.js" instead.` : __GLOBAL__ ? ` Use "vue.global.js" instead.` : ``) /* should not happen */, ) } else { warn(`Component is missing template or render function: `, Component) } } } const attrsProxyHandlers = __DEV__ ? { get(target: Data, key: string) { markAttrsAccessed() track(target, TrackOpTypes.GET, '') return target[key] }, set() { warn(`setupContext.attrs is readonly.`) return false }, deleteProperty() { warn(`setupContext.attrs is readonly.`) return false }, } : { get(target: Data, key: string) { track(target, TrackOpTypes.GET, '') return target[key] }, } /** * Dev-only */ function getSlotsProxy(instance: ComponentInternalInstance): Slots { return ( instance.slotsProxy || (instance.slotsProxy = new Proxy(instance.slots, { get(target, key: string) { track(instance, TrackOpTypes.GET, '$slots') return target[key] }, })) ) } export function createSetupContext( instance: ComponentInternalInstance, ): SetupContext { const expose: SetupContext['expose'] = exposed => { if (__DEV__) { if (instance.exposed) { warn(`expose() should be called only once per setup().`) } if (exposed != null) { let exposedType: string = typeof exposed if (exposedType === 'object') { if (isArray(exposed)) { exposedType = 'array' } else if (isRef(exposed)) { exposedType = 'ref' } } if (exposedType !== 'object') { warn( `expose() should be passed a plain object, received ${exposedType}.`, ) } } } instance.exposed = exposed || {} } if (__DEV__) { // We use getters in dev in case libs like test-utils overwrite instance // properties (overwrites should not be done in prod) let attrsProxy: Data return Object.freeze({ get attrs() { return ( attrsProxy || (attrsProxy = new Proxy(instance.attrs, attrsProxyHandlers)) ) }, get slots() { return getSlotsProxy(instance) }, get emit() { return (event: string, ...args: any[]) => instance.emit(event, ...args) }, expose, }) } else { return { attrs: new Proxy(instance.attrs, attrsProxyHandlers), slots: instance.slots, emit: instance.emit, expose, } } } export function getComponentPublicInstance( instance: ComponentInternalInstance, ): Record | ComponentPublicInstance | null { if (instance.exposed) { return ( instance.exposeProxy || (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), { get(target, key: string) { if (key in target) { return target[key] } else if (key in publicPropertiesMap) { return publicPropertiesMap[key](instance) } }, has(target, key: string) { return key in target || key in publicPropertiesMap }, })) ) } else { return instance.proxy } } const classifyRE = /(?:^|[-_])(\w)/g const classify = (str: string): string => str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '') export function getComponentName( Component: ConcreteComponent, includeInferred = true, ): string | false | undefined { return isFunction(Component) ? Component.displayName || Component.name : Component.name || (includeInferred && Component.__name) } /* istanbul ignore next */ export function formatComponentName( instance: ComponentInternalInstance | null, Component: ConcreteComponent, isRoot = false, ): string { let name = getComponentName(Component) if (!name && Component.__file) { const match = Component.__file.match(/([^/\\]+)\.\w+$/) if (match) { name = match[1] } } if (!name && instance && instance.parent) { // try to infer the name based on reverse resolution const inferFromRegistry = (registry: Record | undefined) => { for (const key in registry) { if (registry[key] === Component) { return key } } } name = inferFromRegistry( instance.components || (instance.parent.type as ComponentOptions).components, ) || inferFromRegistry(instance.appContext.components) } return name ? classify(name) : isRoot ? `App` : `Anonymous` } export function isClassComponent(value: unknown): value is ClassComponent { return isFunction(value) && '__vccOpts' in value } import { Comment, Fragment, Static, Text, type VNode, type VNodeArrayChildren, type VNodeHook, type VNodeProps, cloneIfMounted, createVNode, invokeVNodeHook, isSameVNodeType, normalizeVNode, } from './vnode' import { type ComponentInternalInstance, type ComponentOptions, type Data, type LifecycleHook, createComponentInstance, setupComponent, } from './component' import { filterSingleRoot, renderComponentRoot, shouldUpdateComponent, updateHOCHostEl, } from './componentRenderUtils' import { EMPTY_ARR, EMPTY_OBJ, NOOP, PatchFlags, ShapeFlags, getGlobalThis, invokeArrayFns, isArray, isReservedProp, } from '@vue/shared' import { type SchedulerJob, flushPostFlushCbs, flushPreFlushCbs, invalidateJob, queueJob, queuePostFlushCb, } from './scheduler' import { ReactiveEffect, pauseTracking, resetTracking } from '@vue/reactivity' import { updateProps } from './componentProps' import { updateSlots } from './componentSlots' import { popWarningContext, pushWarningContext, warn } from './warning' import { type CreateAppFunction, createAppAPI } from './apiCreateApp' import { setRef } from './rendererTemplateRef' import { type SuspenseBoundary, type SuspenseImpl, queueEffectWithSuspense, } from './components/Suspense' import type { TeleportImpl, TeleportVNode } from './components/Teleport' import { type KeepAliveContext, isKeepAlive } from './components/KeepAlive' import { isHmrUpdating, registerHMR, unregisterHMR } from './hmr' import { type RootHydrateFunction, createHydrationFunctions } from './hydration' import { invokeDirectiveHook } from './directives' import { endMeasure, startMeasure } from './profiling' import { devtoolsComponentAdded, devtoolsComponentRemoved, devtoolsComponentUpdated, setDevtoolsHook, } from './devtools' import { initFeatureFlags } from './featureFlags' import { isAsyncWrapper } from './apiAsyncComponent' import { isCompatEnabled } from './compat/compatConfig' import { DeprecationTypes } from './compat/compatConfig' import type { TransitionHooks } from './components/BaseTransition' export interface Renderer { render: RootRenderFunction createApp: CreateAppFunction } export interface HydrationRenderer extends Renderer { hydrate: RootHydrateFunction } export type ElementNamespace = 'svg' | 'mathml' | undefined export type RootRenderFunction = ( vnode: VNode | null, container: HostElement, namespace?: ElementNamespace, ) => void export interface RendererOptions< HostNode = RendererNode, HostElement = RendererElement, > { patchProp( el: HostElement, key: string, prevValue: any, nextValue: any, namespace?: ElementNamespace, prevChildren?: VNode[], parentComponent?: ComponentInternalInstance | null, parentSuspense?: SuspenseBoundary | null, unmountChildren?: UnmountChildrenFn, ): void insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void remove(el: HostNode): void createElement( type: string, namespace?: ElementNamespace, isCustomizedBuiltIn?: string, vnodeProps?: (VNodeProps & { [key: string]: any }) | null, ): HostElement createText(text: string): HostNode createComment(text: string): HostNode setText(node: HostNode, text: string): void setElementText(node: HostElement, text: string): void parentNode(node: HostNode): HostElement | null nextSibling(node: HostNode): HostNode | null querySelector?(selector: string): HostElement | null setScopeId?(el: HostElement, id: string): void cloneNode?(node: HostNode): HostNode insertStaticContent?( content: string, parent: HostElement, anchor: HostNode | null, namespace: ElementNamespace, start?: HostNode | null, end?: HostNode | null, ): [HostNode, HostNode] } // Renderer Node can technically be any object in the context of core renderer // logic - they are never directly operated on and always passed to the node op // functions provided via options, so the internal constraint is really just // a generic object. export interface RendererNode { [key: string]: any } export interface RendererElement extends RendererNode {} // An object exposing the internals of a renderer, passed to tree-shakeable // features so that they can be decoupled from this file. Keys are shortened // to optimize bundle size. export interface RendererInternals< HostNode = RendererNode, HostElement = RendererElement, > { p: PatchFn um: UnmountFn r: RemoveFn m: MoveFn mt: MountComponentFn mc: MountChildrenFn pc: PatchChildrenFn pbc: PatchBlockChildrenFn n: NextFn o: RendererOptions } // These functions are created inside a closure and therefore their types cannot // be directly exported. In order to avoid maintaining function signatures in // two places, we declare them once here and use them inside the closure. type PatchFn = ( n1: VNode | null, // null means this is a mount n2: VNode, container: RendererElement, anchor?: RendererNode | null, parentComponent?: ComponentInternalInstance | null, parentSuspense?: SuspenseBoundary | null, namespace?: ElementNamespace, slotScopeIds?: string[] | null, optimized?: boolean, ) => void type MountChildrenFn = ( children: VNodeArrayChildren, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, slotScopeIds: string[] | null, optimized: boolean, start?: number, ) => void type PatchChildrenFn = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, slotScopeIds: string[] | null, optimized: boolean, ) => void type PatchBlockChildrenFn = ( oldChildren: VNode[], newChildren: VNode[], fallbackContainer: RendererElement, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, slotScopeIds: string[] | null, ) => void type MoveFn = ( vnode: VNode, container: RendererElement, anchor: RendererNode | null, type: MoveType, parentSuspense?: SuspenseBoundary | null, ) => void type NextFn = (vnode: VNode) => RendererNode | null type UnmountFn = ( vnode: VNode, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, doRemove?: boolean, optimized?: boolean, ) => void type RemoveFn = (vnode: VNode) => void type UnmountChildrenFn = ( children: VNode[], parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, doRemove?: boolean, optimized?: boolean, start?: number, ) => void export type MountComponentFn = ( initialVNode: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, optimized: boolean, ) => void type ProcessTextOrCommentFn = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, ) => void export type SetupRenderEffectFn = ( instance: ComponentInternalInstance, initialVNode: VNode, container: RendererElement, anchor: RendererNode | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, optimized: boolean, ) => void export enum MoveType { ENTER, LEAVE, REORDER, } export const queuePostRenderEffect: typeof queuePostFlushCb | ((fn: Function | Function[], suspense: SuspenseBoundary | null) => void) = __FEATURE_SUSPENSE__ ? __TEST__ ? // vitest can't seem to handle eager circular dependency (fn: Function | Function[], suspense: SuspenseBoundary | null) => queueEffectWithSuspense(fn, suspense) : queueEffectWithSuspense : queuePostFlushCb /** * The createRenderer function accepts two generic arguments: * HostNode and HostElement, corresponding to Node and Element types in the * host environment. For example, for runtime-dom, HostNode would be the DOM * `Node` interface and HostElement would be the DOM `Element` interface. * * Custom renderers can pass in the platform specific types like this: * * ``` js * const { render, createApp } = createRenderer({ * patchProp, * ...nodeOps * }) * ``` */ export function createRenderer< HostNode = RendererNode, HostElement = RendererElement, >(options: RendererOptions): Renderer { return baseCreateRenderer(options) } // Separate API for creating hydration-enabled renderer. // Hydration logic is only used when calling this function, making it // tree-shakable. export function createHydrationRenderer( options: RendererOptions, ): HydrationRenderer { return baseCreateRenderer(options, createHydrationFunctions) } // overload 1: no hydration function baseCreateRenderer< HostNode = RendererNode, HostElement = RendererElement, >(options: RendererOptions): Renderer // overload 2: with hydration function baseCreateRenderer( options: RendererOptions, createHydrationFns: typeof createHydrationFunctions, ): HydrationRenderer // implementation function baseCreateRenderer( options: RendererOptions, createHydrationFns?: typeof createHydrationFunctions, ): any { // compile-time feature flags check if (__ESM_BUNDLER__ && !__TEST__) { initFeatureFlags() } const target = getGlobalThis() target.__VUE__ = true if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target) } const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, setScopeId: hostSetScopeId = NOOP, insertStaticContent: hostInsertStaticContent, } = options // Note: functions inside this closure should use `const xxx = () => {}` // style in order to prevent being inlined by minifiers. const patch: PatchFn = ( n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, namespace = undefined, slotScopeIds = null, optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren, ) => { if (n1 === n2) { return } // patching & not same type, unmount old tree if (n1 && !isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense, true) n1 = null } if (n2.patchFlag === PatchFlags.BAIL) { optimized = false n2.dynamicChildren = null } const { type, ref, shapeFlag } = n2 switch (type) { case Text: processText(n1, n2, container, anchor) break case Comment: processCommentNode(n1, n2, container, anchor) break case Static: if (n1 == null) { mountStaticNode(n2, container, anchor, namespace) } else if (__DEV__) { patchStaticNode(n1, n2, container, namespace) } break case Fragment: processFragment( n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, ) break default: if (shapeFlag & ShapeFlags.ELEMENT) { processElement( n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, ) } else if (shapeFlag & ShapeFlags.COMPONENT) { processComponent( n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, ) } else if (shapeFlag & ShapeFlags.TELEPORT) { ;(type as typeof TeleportImpl).process( n1 as TeleportVNode, n2 as TeleportVNode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, internals, ) } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { ;(type as typeof SuspenseImpl).process( n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, internals, ) } else if (__DEV__) { warn('Invalid VNode type:', type, `(${typeof type})`) } } // set ref if (ref != null && parentComponent) { setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2) } } const processText: ProcessTextOrCommentFn = (n1, n2, container, anchor) => { if (n1 == null) { hostInsert( (n2.el = hostCreateText(n2.children as string)), container, anchor, ) } else { const el = (n2.el = n1.el!) if (n2.children !== n1.children) { hostSetText(el, n2.children as string) } } } const processCommentNode: ProcessTextOrCommentFn = ( n1, n2, container, anchor, ) => { if (n1 == null) { hostInsert( (n2.el = hostCreateComment((n2.children as string) || '')), container, anchor, ) } else { // there's no support for dynamic comments n2.el = n1.el } } const mountStaticNode = ( n2: VNode, container: RendererElement, anchor: RendererNode | null, namespace: ElementNamespace, ) => { // static nodes are only present when used with compiler-dom/runtime-dom // which guarantees presence of hostInsertStaticContent. ;[n2.el, n2.anchor] = hostInsertStaticContent!( n2.children as string, container, anchor, namespace, n2.el, n2.anchor, ) } /** * Dev / HMR only */ const patchStaticNode = ( n1: VNode, n2: VNode, container: RendererElement, namespace: ElementNamespace, ) => { // static nodes are only patched during dev for HMR if (n2.children !== n1.children) { const anchor = hostNextSibling(n1.anchor!) // remove existing removeStaticNode(n1) // insert new ;[n2.el, n2.anchor] = hostInsertStaticContent!( n2.children as string, container, anchor, namespace, ) } else { n2.el = n1.el n2.anchor = n1.anchor } } const moveStaticNode = ( { el, anchor }: VNode, container: RendererElement, nextSibling: RendererNode | null, ) => { let next while (el && el !== anchor) { next = hostNextSibling(el) hostInsert(el, container, nextSibling) el = next } hostInsert(anchor!, container, nextSibling) } const removeStaticNode = ({ el, anchor }: VNode) => { let next while (el && el !== anchor) { next = hostNextSibling(el) hostRemove(el) el = next } hostRemove(anchor!) } const processElement = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, slotScopeIds: string[] | null, optimized: boolean, ) => { if (n2.type === 'svg') { namespace = 'svg' } else if (n2.type === 'math') { namespace = 'mathml' } if (n1 == null) { mountElement( n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, ) } else { patchElement( n1, n2, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, ) } } const mountElement = ( vnode: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, namespace: ElementNamespace, slotScopeIds: string[] | null, optimized: boolean, ) => { let el: RendererElement let vnodeHook: VNodeHook | undefined | null const { props, shapeFlag, transition, dirs } = vnode el = vnode.el = hostCreateElement( vnode.type as string, namespace, props && props.is, props, ) // mount children first, since some props may rely on child content // being already rendered, e.g. `