/* @flow */
|
|
import VNode, { createTextVNode } from 'core/vdom/vnode'
|
import { isFalse, isTrue, isDef, isUndef, isPrimitive } from 'shared/util'
|
|
// The template compiler attempts to minimize the need for normalization by
|
// statically analyzing the template at compile time.
|
//
|
// For plain HTML markup, normalization can be completely skipped because the
|
// generated render function is guaranteed to return Array<VNode>. There are
|
// two cases where extra normalization is needed:
|
|
// 1. When the children contains components - because a functional component
|
// may return an Array instead of a single root. In this case, just a simple
|
// normalization is needed - if any child is an Array, we flatten the whole
|
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
|
// because functional components already normalize their own children.
|
export function simpleNormalizeChildren (children: any) {
|
for (let i = 0; i < children.length; i++) {
|
if (Array.isArray(children[i])) {
|
return Array.prototype.concat.apply([], children)
|
}
|
}
|
return children
|
}
|
|
// 2. When the children contains constructs that always generated nested Arrays,
|
// e.g. <template>, <slot>, v-for, or when the children is provided by user
|
// with hand-written render functions / JSX. In such cases a full normalization
|
// is needed to cater to all possible types of children values.
|
export function normalizeChildren (children: any): ?Array<VNode> {
|
return isPrimitive(children)
|
? [createTextVNode(children)]
|
: Array.isArray(children)
|
? normalizeArrayChildren(children)
|
: undefined
|
}
|
|
function isTextNode (node): boolean {
|
return isDef(node) && isDef(node.text) && isFalse(node.isComment)
|
}
|
|
function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
|
const res = []
|
let i, c, lastIndex, last
|
for (i = 0; i < children.length; i++) {
|
c = children[i]
|
if (isUndef(c) || typeof c === 'boolean') continue
|
lastIndex = res.length - 1
|
last = res[lastIndex]
|
// nested
|
if (Array.isArray(c)) {
|
if (c.length > 0) {
|
c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
|
// merge adjacent text nodes
|
if (isTextNode(c[0]) && isTextNode(last)) {
|
res[lastIndex] = createTextVNode(last.text + (c[0]: any).text)
|
c.shift()
|
}
|
res.push.apply(res, c)
|
}
|
} else if (isPrimitive(c)) {
|
if (isTextNode(last)) {
|
// merge adjacent text nodes
|
// this is necessary for SSR hydration because text nodes are
|
// essentially merged when rendered to HTML strings
|
res[lastIndex] = createTextVNode(last.text + c)
|
} else if (c !== '') {
|
// convert primitive to vnode
|
res.push(createTextVNode(c))
|
}
|
} else {
|
if (isTextNode(c) && isTextNode(last)) {
|
// merge adjacent text nodes
|
res[lastIndex] = createTextVNode(last.text + c.text)
|
} else {
|
// default key for nested array children (likely generated by v-for)
|
if (isTrue(children._isVList) &&
|
isDef(c.tag) &&
|
isUndef(c.key) &&
|
isDef(nestedIndex)) {
|
c.key = `__vlist${nestedIndex}_${i}__`
|
}
|
res.push(c)
|
}
|
}
|
}
|
return res
|
}
|