Repository: vuejs/vue
Pull request: fix(v-pre): skip compiling custom component tags in v-pre blocks (fix…

… #8286)

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**

Turns out to be an easy fix…
Never thought the root cause could be in codegen and wasted a lot of time diving into vdom 😂.

A side effect of this fix is that every descendent component in a `v-pre` block would also carry a `{ pre: true }` property.

Changed files:
filename: src/compiler/codegen/index.js
patch: @@ -52,6 +52,10 @@ export function generate (
 }
 
 export function genElement (el: ASTElement, state: CodegenState): string {
+  if (el.parent) {
+    el.pre = el.pre || el.parent.pre
+  }
+
   if (el.staticRoot && !el.staticProcessed) {
     return genStatic(el, state)
   } else if (el.once && !el.onceProcessed) {
@@ -70,7 +74,10 @@ export function genElement (el: ASTElement, state: CodegenState): string {
     if (el.component) {
       code = genComponent(el.component, el, state)
     } else {
-      const data = el.plain ? undefined : genData(el, state)
+      let data
+      if (!el.plain || el.pre) {
+        data = genData(el, state)
+      }
 
       const children = el.inlineTemplate ? null : genChildren(el, state, true)
       code = `_c('${el.tag}'${

filename: src/core/vdom/create-element.js
patch: @@ -102,7 +102,7 @@ export function _createElement (
         config.parsePlatformTagName(tag), data, children,
         undefined, undefined, context
       )
-    } else if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
+    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
       // component
       vnode = createComponent(Ctor, data, context, children, tag)
     } else {

filename: test/unit/features/directives/pre.spec.js
patch: @@ -7,7 +7,7 @@ describe('Directive v-pre', function () {
         <div v-pre>{{ a }}</div>
         <div>{{ a }}</div>
         <div v-pre>
-          <component></component>
+          <component is="div"></component>
         </div>
       </div>`,
       data: {
@@ -17,7 +17,7 @@ describe('Directive v-pre', function () {
     vm.$mount()
     expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
     expect(vm.$el.children[1].textContent).toBe('123')
-    expect(vm.$el.lastChild.innerHTML).toBe('<component></component>')
+    expect(vm.$el.lastChild.innerHTML).toBe('<component is="div"></component>')
   })
 
   it('should not compile on root node', function () {
@@ -31,4 +31,15 @@ describe('Directive v-pre', function () {
     vm.$mount()
     expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
   })
+
+  // #8286
+  it('should not compile custom component tags', function () {
+    Vue.component('vtest', { template: ` <div>Hello World</div>` })
+    const vm = new Vue({
+      template: '<div v-pre><vtest></vtest></div>',
+      replace: true
+    })
+    vm.$mount()
+    expect(vm.$el.firstChild.tagName).toBe('VTEST')
+  })
 })