import config from '../config' import KNode, { createEmptyKNode } from './knode' 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 { KNodeData } from 'types/knode' 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 ): KNode | 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?: KNodeData, children?: any, normalizationType?: number ): KNode | Array { if (isDef(data) && isDef((data as any).__ob__)) { __DEV__ && warn( `Avoid using observed data object as knode data: ${JSON.stringify( data )}\n` + 'Always create fresh knode data objects in each render!', context ) return createEmptyKNode() } // object syntax in k-bind if (isDef(data) && isDef(data.is)) { tag = data.is } if (!tag) { // in case of component :is set to falsy value return createEmptyKNode() } // 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 knode, ns if (typeof tag === 'string') { let Ctor ns = (context.$knode && context.$knode.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 k-on is only valid on components but it was used on <${tag}>.`, context ) } knode = new KNode( config.parsePlatformTagName(tag), data, children, undefined, undefined, context ) } else if ( (!data || !data.pre) && isDef((Ctor = resolveAsset(context.$options, 'components', tag))) ) { // component knode = 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 knode = new KNode(tag, data, children, undefined, undefined, context) } } else { // direct component options / constructor knode = createComponent(tag as any, data, context, children) } if (isArray(knode)) { return knode } else if (isDef(knode)) { if (isDef(ns)) applyNS(knode, ns) if (isDef(data)) registerDeepBindings(data) return knode } else { return createEmptyKNode() } } function applyNS(knode, ns, force?: boolean) { knode.ns = ns if (knode.tag === 'foreignObject') { // use default namespace inside foreignObject ns = undefined force = true } if (isDef(knode.children)) { for (let i = 0, l = knode.children.length; i < l; i++) { const child = knode.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) } }