|
|
@@ -5,7 +5,7 @@ import { parseHTML } from './html-parser'
|
|
|
import { parseText } from './text-parser'
|
|
|
import { parseFilters } from './filter-parser'
|
|
|
import { genAssignmentCode } from '../directives/model'
|
|
|
-import { extend, cached, no, camelize, hyphenate, hasOwn } from 'shared/util'
|
|
|
+import { extend, cached, no, camelize, hyphenate } from 'shared/util'
|
|
|
import { isIE, isEdge, isServerRendering } from 'core/util/env'
|
|
|
|
|
|
import {
|
|
|
@@ -45,7 +45,6 @@ let postTransforms
|
|
|
let platformIsPreTag
|
|
|
let platformMustUseProp
|
|
|
let platformGetTagNamespace
|
|
|
-let maybeComponent
|
|
|
|
|
|
export function createASTElement (
|
|
|
tag: string,
|
|
|
@@ -75,8 +74,6 @@ export function parse (
|
|
|
platformIsPreTag = options.isPreTag || no
|
|
|
platformMustUseProp = options.mustUseProp || no
|
|
|
platformGetTagNamespace = options.getTagNamespace || no
|
|
|
- const isReservedTag = options.isReservedTag || no
|
|
|
- maybeComponent = (el: ASTElement) => !!el.component || !isReservedTag(el.tag)
|
|
|
|
|
|
transforms = pluckModuleFunction(options.modules, 'transformNode')
|
|
|
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
|
|
|
@@ -102,7 +99,7 @@ export function parse (
|
|
|
|
|
|
function closeElement (element) {
|
|
|
if (!inVPre && !element.processed) {
|
|
|
- element = processElement(element, options)
|
|
|
+ element = processElement(element, options, currentParent)
|
|
|
}
|
|
|
// tree management
|
|
|
if (!stack.length && element !== root) {
|
|
|
@@ -156,7 +153,7 @@ export function parse (
|
|
|
{ start: el.start }
|
|
|
)
|
|
|
}
|
|
|
- if (hasOwn(el.attrsMap, 'v-for')) {
|
|
|
+ if (el.attrsMap.hasOwnProperty('v-for')) {
|
|
|
warnOnce(
|
|
|
'Cannot use v-for on stateful component root element because ' +
|
|
|
'it renders multiple elements.',
|
|
|
@@ -380,7 +377,8 @@ function processRawAttrs (el) {
|
|
|
|
|
|
export function processElement (
|
|
|
element: ASTElement,
|
|
|
- options: CompilerOptions
|
|
|
+ options: CompilerOptions,
|
|
|
+ parent: ASTElement | undefined
|
|
|
) {
|
|
|
processKey(element)
|
|
|
|
|
|
@@ -393,7 +391,7 @@ export function processElement (
|
|
|
)
|
|
|
|
|
|
processRef(element)
|
|
|
- processSlot(element)
|
|
|
+ processSlot(element, parent)
|
|
|
processComponent(element)
|
|
|
for (let i = 0; i < transforms.length; i++) {
|
|
|
element = transforms[i](element, options) || element
|
|
|
@@ -584,86 +582,19 @@ function processSlot (el) {
|
|
|
)
|
|
|
}
|
|
|
el.slotScope = slotScope
|
|
|
- if (process.env.NODE_ENV !== 'production' && nodeHas$Slot(el)) {
|
|
|
- warn('Unepxected mixed usage of `slot-scope` and `$slot`.', el)
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 2.6 $slot support
|
|
|
- // Context: https://github.com/vuejs/vue/issues/9180
|
|
|
- // Ideally, all slots should be compiled as functions (this is what we
|
|
|
- // are doing in 3.x), but for 2.x e want to preserve complete backwards
|
|
|
- // compatibility, and maintain the exact same compilation output for any
|
|
|
- // code that does not use the new syntax.
|
|
|
-
|
|
|
- // recursively check component children for presence of `$slot` in all
|
|
|
- // expressions until running into a nested child component.
|
|
|
- if (maybeComponent(el) && childrenHas$Slot(el)) {
|
|
|
- processScopedSlots(el)
|
|
|
- }
|
|
|
}
|
|
|
const slotTarget = getBindingAttr(el, 'slot')
|
|
|
if (slotTarget) {
|
|
|
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
|
|
|
// preserve slot as an attribute for native shadow DOM compat
|
|
|
// only for non-scoped slots.
|
|
|
- if (el.tag !== 'template' && !el.slotScope && !nodeHas$Slot(el)) {
|
|
|
+ if (el.tag !== 'template' && !el.slotScope) {
|
|
|
addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function childrenHas$Slot (el): boolean {
|
|
|
- return el.children ? el.children.some(nodeHas$Slot) : false
|
|
|
-}
|
|
|
-
|
|
|
-const $slotRE = /(^|[^\w_$])\$slot($|[^\w_$])/
|
|
|
-function nodeHas$Slot (node): boolean {
|
|
|
- // caching
|
|
|
- if (hasOwn(node, 'has$Slot')) {
|
|
|
- return (node.has$Slot: any)
|
|
|
- }
|
|
|
- if (node.type === 1) { // element
|
|
|
- for (const key in node.attrsMap) {
|
|
|
- if (dirRE.test(key) && $slotRE.test(node.attrsMap[key])) {
|
|
|
- return (node.has$Slot = true)
|
|
|
- }
|
|
|
- }
|
|
|
- return (node.has$Slot = childrenHas$Slot(node))
|
|
|
- } else if (node.type === 2) { // expression
|
|
|
- // TODO more robust logic for checking $slot usage
|
|
|
- return (node.has$Slot = $slotRE.test(node.expression))
|
|
|
- }
|
|
|
- return false
|
|
|
-}
|
|
|
-
|
|
|
-function processScopedSlots (el) {
|
|
|
- // 1. group children by slot target
|
|
|
- const groups: any = {}
|
|
|
- for (let i = 0; i < el.children.length; i++) {
|
|
|
- const child = el.children[i]
|
|
|
- const target = child.slotTarget || '"default"'
|
|
|
- if (!groups[target]) {
|
|
|
- groups[target] = []
|
|
|
- }
|
|
|
- groups[target].push(child)
|
|
|
- }
|
|
|
- // 2. for each slot group, check if the group contains $slot
|
|
|
- for (const name in groups) {
|
|
|
- const group = groups[name]
|
|
|
- if (group.some(nodeHas$Slot)) {
|
|
|
- // 3. if a group contains $slot, all nodes in that group gets assigned
|
|
|
- // as a scoped slot to el and removed from children
|
|
|
- el.plain = false
|
|
|
- const slots = el.scopedSlots || (el.scopedSlots = {})
|
|
|
- const slotContainer = slots[name] = createASTElement('template', [], el)
|
|
|
- slotContainer.children = group
|
|
|
- slotContainer.slotScope = '$slot'
|
|
|
- el.children = el.children.filter(c => group.indexOf(c) === -1)
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
function processComponent (el) {
|
|
|
let binding
|
|
|
if ((binding = getBindingAttr(el, 'is'))) {
|