import config from '../config' import VNode, { createEmptyVNode } from './vnode' import { createComponent } from './create-component' import { traverse } from '../observer/traverse' import { warn, isDef, isUndef, isArray, isTrue, isObject, isPrimitive, resolveAsset, isFunction } from '../util/index' import { normalizeChildren, simpleNormalizeChildren } from './helpers/index' import type { Component } from 'types/component' import type { VNodeData } from 'types/vnode' const SIMPLE_NORMALIZE = 1 const ALWAYS_NORMALIZE = 2 // wrapper function for providing a more flexible interface // without getting yelled at by flow export function createElement( context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean ): VNode | Array { if (isArray(data) || isPrimitive(data)) { normalizationType = children children = data data = undefined } if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE } return _createElement(context, tag, data, children, normalizationType) } export function _createElement( context: Component, tag?: string | Component | Function | Object, data?: VNodeData, children?: any, normalizationType?: number ): VNode | Array { if (isDef(data) && isDef((data as any).__ob__)) { __DEV__ && warn( `Avoid using observed data object as vnode data: ${JSON.stringify( data )}\n` + 'Always create fresh vnode data objects in each render!', context ) return createEmptyVNode() } // object syntax in v-bind if (isDef(data) && isDef(data.is)) { tag = data.is } if (!tag) { // in case of component :is set to falsy value return createEmptyVNode() } // warn against non-primitive key if (__DEV__ && isDef(data) && isDef(data.key) && !isPrimitive(data.key)) { warn( 'Avoid using non-primitive value as key, ' + 'use string/number value instead.', context ) } // support single function children as default scoped slot if (isArray(children) && isFunction(children[0])) { data = data || {} data.scopedSlots = { default: children[0] } children.length = 0 } if (normalizationType === ALWAYS_NORMALIZE) { children = normalizeChildren(children) } else if (normalizationType === SIMPLE_NORMALIZE) { children = simpleNormalizeChildren(children) } let vnode, ns if (typeof tag === 'string') { let Ctor ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag) if (config.isReservedTag(tag)) { // platform built-in elements if ( __DEV__ && isDef(data) && isDef(data.nativeOn) && data.tag !== 'component' ) { warn( `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`, context ) } vnode = new VNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if ( (!data || !data.pre) && isDef((Ctor = resolveAsset(context.$options, 'components', tag))) ) { // component vnode = createComponent(Ctor, data, context, children, tag) } else { // unknown or unlisted namespaced elements // check at runtime because it may get assigned a namespace when its // parent normalizes children vnode = new VNode(tag, data, children, undefined, undefined, context) } } else { // direct component options / constructor vnode = createComponent(tag as any, data, context, children) } if (isArray(vnode)) { return vnode } else if (isDef(vnode)) { if (isDef(ns)) applyNS(vnode, ns) if (isDef(data)) registerDeepBindings(data) return vnode } else { return createEmptyVNode() } } function applyNS(vnode, ns, force?: boolean) { vnode.ns = ns if (vnode.tag === 'foreignObject') { // use default namespace inside foreignObject ns = undefined force = true } if (isDef(vnode.children)) { for (let i = 0, l = vnode.children.length; i < l; i++) { const child = vnode.children[i] if ( isDef(child.tag) && (isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg')) ) { applyNS(child, ns, force) } } } } // ref #5318 // necessary to ensure parent re-render when deep bindings like :style and // :class are used on slot nodes function registerDeepBindings(data) { if (isObject(data.style)) { traverse(data.style) } if (isObject(data.class)) { traverse(data.class) } }