Repository: vuejs/vue
Pull request: allow 2 root nodes with v-if and v-else

Fixes #3878.


Changed files:
filename: src/compiler/parser/index.js
patch: @@ -153,8 +153,9 @@ export function parse (
         checkRootConstraints(root)
       } else if (process.env.NODE_ENV !== 'production' && !stack.length && !warned) {
         // allow 2 root elements with v-if and v-else
-        if ((root.attrsMap.hasOwnProperty('v-if') && element.attrsMap.hasOwnProperty('v-else'))) {
+        if (root.if && element.else) {
           checkRootConstraints(element)
+          root.elseBlock = element
         } else {
           warned = true
           warn(
@@ -201,10 +202,10 @@ export function parse (
 
     chars (text: string) {
       if (!currentParent) {
-        if (process.env.NODE_ENV !== 'production' && !warned) {
+        if (process.env.NODE_ENV !== 'production' && !warned && text === template) {
           warned = true
           warn(
-            'Component template should contain exactly one root element:\n\n' + template
+            'Component template requires a root element, rather than just text:\n\n' + template
           )
         }
         return

filename: test/unit/modules/compiler/compiler-options.spec.js
patch: @@ -118,7 +118,7 @@ describe('compile options', () => {
   it('should collect errors', () => {
     let compiled = compile('hello')
     expect(compiled.errors.length).toBe(1)
-    expect(compiled.errors[0]).toContain('should contain exactly one root element')
+    expect(compiled.errors[0]).toContain('root element')
 
     compiled = compile('<div v-if="a----">{{ b++++ }}</div>')
     expect(compiled.errors.length).toBe(2)

filename: test/unit/modules/compiler/parser.spec.js
patch: @@ -73,7 +73,7 @@ describe('parser', () => {
 
   it('not contain root element', () => {
     parse('hello world', baseOptions)
-    expect('Component template should contain exactly one root element').toHaveBeenWarned()
+    expect('Component template requires a root element, rather than just text').toHaveBeenWarned()
   })
 
   it('warn multiple root elements', () => {
@@ -83,10 +83,28 @@ describe('parser', () => {
 
   it('not warn 2 root elements with v-if and v-else', () => {
     parse('<div v-if="1"></div><div v-else></div>', baseOptions)
-    expect('Component template should contain exactly one root element:\n\n<div v-if="1"></div><div v-else></div>')
+    expect('Component template should contain exactly one root element')
       .not.toHaveBeenWarned()
   })
 
+  it('not warn 2 root elements with v-if and v-else on separate lines', () => {
+    parse(`
+      <div v-if="1"></div>
+      <div v-else></div>
+    `, baseOptions)
+    expect('Component template should contain exactly one root element')
+      .not.toHaveBeenWarned()
+  })
+
+  it('generate correct ast for 2 root elements with v-if and v-else on separate lines', () => {
+    const ast = parse(`
+      <div v-if="1"></div>
+      <p v-else></p>
+    `, baseOptions)
+    expect(ast.tag).toBe('div')
+    expect(ast.elseBlock.tag).toBe('p')
+  })
+
   it('warn 2 root elements with v-if', () => {
     parse('<div v-if="1"></div><div v-if="2"></div>', baseOptions)
     expect('Component template should contain exactly one root element:\n\n<div v-if="1"></div><div v-if="2"></div>')