Skip to content

Commit 583dd01

Browse files
committed
refactor(parser): move element self processing to after children
This allows element processing logic to be based on its sub tree content, e.g. $slot usage detection
1 parent d278da2 commit 583dd01

File tree

1 file changed

+66
-53
lines changed

1 file changed

+66
-53
lines changed

Diff for: src/compiler/parser/index.js

+66-53
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,40 @@ export function parse (
9797
}
9898

9999
function closeElement (element) {
100+
if (!inVPre && !element.processed) {
101+
element = processElement(element, options, currentParent)
102+
}
103+
// tree management
104+
if (!stack.length && element !== root) {
105+
// allow root elements with v-if, v-else-if and v-else
106+
if (root.if && (element.elseif || element.else)) {
107+
if (process.env.NODE_ENV !== 'production') {
108+
checkRootConstraints(element)
109+
}
110+
addIfCondition(root, {
111+
exp: element.elseif,
112+
block: element
113+
})
114+
} else if (process.env.NODE_ENV !== 'production') {
115+
warnOnce(
116+
`Component template should contain exactly one root element. ` +
117+
`If you are using v-if on multiple elements, ` +
118+
`use v-else-if to chain them instead.`,
119+
{ start: element.start }
120+
)
121+
}
122+
}
123+
if (currentParent && !element.forbidden) {
124+
if (element.elseif || element.else) {
125+
processIfConditions(element, currentParent)
126+
} else if (element.slotScope) { // scoped slot
127+
const name = element.slotTarget || '"default"'
128+
;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
129+
} else {
130+
currentParent.children.push(element)
131+
element.parent = currentParent
132+
}
133+
}
100134
// check pre state
101135
if (element.pre) {
102136
inVPre = false
@@ -110,6 +144,23 @@ export function parse (
110144
}
111145
}
112146

147+
function checkRootConstraints (el) {
148+
if (el.tag === 'slot' || el.tag === 'template') {
149+
warnOnce(
150+
`Cannot use <${el.tag}> as component root element because it may ` +
151+
'contain multiple nodes.',
152+
{ start: el.start }
153+
)
154+
}
155+
if (el.attrsMap.hasOwnProperty('v-for')) {
156+
warnOnce(
157+
'Cannot use v-for on stateful component root element because ' +
158+
'it renders multiple elements.',
159+
el.rawAttrsMap['v-for']
160+
)
161+
}
162+
}
163+
113164
parseHTML(template, {
114165
warn,
115166
expectHTML: options.expectHTML,
@@ -174,62 +225,15 @@ export function parse (
174225
processFor(element)
175226
processIf(element)
176227
processOnce(element)
177-
// element-scope stuff
178-
processElement(element, options)
179228
}
180229

181-
function checkRootConstraints (el) {
182-
if (process.env.NODE_ENV !== 'production') {
183-
if (el.tag === 'slot' || el.tag === 'template') {
184-
warnOnce(
185-
`Cannot use <${el.tag}> as component root element because it may ` +
186-
'contain multiple nodes.',
187-
{ start: el.start }
188-
)
189-
}
190-
if (el.attrsMap.hasOwnProperty('v-for')) {
191-
warnOnce(
192-
'Cannot use v-for on stateful component root element because ' +
193-
'it renders multiple elements.',
194-
el.rawAttrsMap['v-for']
195-
)
196-
}
197-
}
198-
}
199-
200-
// tree management
201230
if (!root) {
202231
root = element
203-
checkRootConstraints(root)
204-
} else if (!stack.length) {
205-
// allow root elements with v-if, v-else-if and v-else
206-
if (root.if && (element.elseif || element.else)) {
207-
checkRootConstraints(element)
208-
addIfCondition(root, {
209-
exp: element.elseif,
210-
block: element
211-
})
212-
} else if (process.env.NODE_ENV !== 'production') {
213-
warnOnce(
214-
`Component template should contain exactly one root element. ` +
215-
`If you are using v-if on multiple elements, ` +
216-
`use v-else-if to chain them instead.`,
217-
{ start: element.start }
218-
)
219-
}
220-
}
221-
if (currentParent && !element.forbidden) {
222-
if (element.elseif || element.else) {
223-
processIfConditions(element, currentParent)
224-
} else if (element.slotScope) { // scoped slot
225-
currentParent.plain = false
226-
const name = element.slotTarget || '"default"'
227-
;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
228-
} else {
229-
currentParent.children.push(element)
230-
element.parent = currentParent
232+
if (process.env.NODE_ENV !== 'production') {
233+
checkRootConstraints(root)
231234
}
232235
}
236+
233237
if (!unary) {
234238
currentParent = element
235239
stack.push(element)
@@ -370,20 +374,29 @@ function processRawAttrs (el) {
370374
}
371375
}
372376

373-
export function processElement (element: ASTElement, options: CompilerOptions) {
377+
export function processElement (
378+
element: ASTElement,
379+
options: CompilerOptions,
380+
parent: ASTElement | undefined
381+
) {
374382
processKey(element)
375383

376384
// determine whether this is a plain element after
377385
// removing structural attributes
378-
element.plain = !element.key && !element.attrsList.length
386+
element.plain = (
387+
!element.key &&
388+
!element.scopedSlots &&
389+
!element.attrsList.length
390+
)
379391

380392
processRef(element)
381-
processSlot(element)
393+
processSlot(element, parent)
382394
processComponent(element)
383395
for (let i = 0; i < transforms.length; i++) {
384396
element = transforms[i](element, options) || element
385397
}
386398
processAttrs(element)
399+
return element
387400
}
388401

389402
function processKey (el) {

0 commit comments

Comments
 (0)