Repository: vuejs/eslint-plugin-vue
Pull request #1105 (2020-04-22 06:20:48)

Rename `require-v-if-inside-transition` rule to `require-toggle-inside-transition`

ref https://github.com/vuejs/eslint-plugin-vue/pull/1099#issuecomment-617516595


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 60bfc833..4dc127cb 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,6 +5,7 @@ workflows:
       - node-v8
       - node-v10
       - node-v12
+      - node-v14
 
 version: 2
 jobs:
@@ -31,9 +32,23 @@ jobs:
           command: npm test
 
   node-v8:
-    <<: *node-base
     docker:
       - image: node:8
+    steps:
+      - run:
+          name: Versions
+          command: npm version
+      - checkout
+      - run:
+          name: Install eslint@6
+          command: |
+            npm install -D eslint@6.0.0
+      - run:
+          name: Install dependencies
+          command: npm install
+      - run:
+          name: Test
+          command: npm test
   node-v10:
     <<: *node-base
     docker:
@@ -42,3 +57,7 @@ jobs:
     <<: *node-base
     docker:
       - image: node:12
+  node-v14:
+    <<: *node-base
+    docker:
+      - image: node:14
diff --git a/.eslintrc.js b/.eslintrc.js
index 2693499d..e89fc7ff 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -27,12 +27,12 @@ module.exports = {
   overrides: [{
     files: ['lib/rules/*.js'],
     rules: {
-      "consistent-docs-description": "error",
-      "no-invalid-meta": "error",
-      "no-invalid-meta-docs-categories": "error",
+      'consistent-docs-description': 'error',
+      'no-invalid-meta': 'error',
+      'no-invalid-meta-docs-categories': 'error',
       'eslint-plugin/require-meta-type': 'error',
-      "require-meta-docs-url": ["error", {
-        "pattern": `https://eslint.vuejs.org/rules/{{name}}.html`
+      'require-meta-docs-url': ['error', {
+        'pattern': `https://eslint.vuejs.org/rules/{{name}}.html`
       }]
     }
   }]
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 89fa12b5..ad262e40 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -42,12 +42,15 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/no-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data (in Vue.js 3.0.0+) | :wrench: |
 | [vue/no-deprecated-events-api](./no-deprecated-events-api.md) | disallow using deprecated events api (in Vue.js 3.0.0+) |  |
 | [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax (in Vue.js 3.0.0+) |  |
+| [vue/no-deprecated-functional-template](./no-deprecated-functional-template.md) | disallow using deprecated the `functional` template (in Vue.js 3.0.0+) |  |
+| [vue/no-deprecated-html-element-is](./no-deprecated-html-element-is.md) | disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+) |  |
 | [vue/no-deprecated-inline-template](./no-deprecated-inline-template.md) | disallow using deprecated `inline-template` attribute (in Vue.js 3.0.0+) |  |
 | [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
 | [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
 | [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |
 | [vue/no-deprecated-v-bind-sync](./no-deprecated-v-bind-sync.md) | disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+) | :wrench: |
 | [vue/no-deprecated-v-on-number-modifiers](./no-deprecated-v-on-number-modifiers.md) | disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+) | :wrench: |
+| [vue/no-deprecated-vue-config-keycodes](./no-deprecated-vue-config-keycodes.md) | disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+) |  |
 | [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names |  |
 | [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes |  |
 | [vue/no-lifecycle-after-await](./no-lifecycle-after-await.md) | disallow asynchronously registered lifecycle hooks |  |
@@ -66,8 +69,8 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/require-component-is](./require-component-is.md) | require `v-bind:is` of `<component>` elements |  |
 | [vue/require-prop-type-constructor](./require-prop-type-constructor.md) | require prop type to be a constructor | :wrench: |
 | [vue/require-render-return](./require-render-return.md) | enforce render function to always return value |  |
+| [vue/require-toggle-inside-transition](./require-toggle-inside-transition.md) | require control the display of the content inside `<transition>` |  |
 | [vue/require-v-for-key](./require-v-for-key.md) | require `v-bind:key` with `v-for` directives |  |
-| [vue/require-v-if-inside-transition](./require-v-if-inside-transition.md) | require control the display of the content inside `<transition>` |  |
 | [vue/require-valid-default-prop](./require-valid-default-prop.md) | enforce props default values to be valid |  |
 | [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property |  |
 | [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` |  |
diff --git a/docs/rules/no-deprecated-functional-template.md b/docs/rules/no-deprecated-functional-template.md
new file mode 100644
index 00000000..a7e288f7
--- /dev/null
+++ b/docs/rules/no-deprecated-functional-template.md
@@ -0,0 +1,39 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-functional-template
+description: disallow using deprecated the `functional` template (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-functional-template
+> disallow using deprecated the `functional` template (in Vue.js 3.0.0+)
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+
+## :book: Rule Details
+
+This rule reports deprecated the `functional` template (in Vue.js 3.0.0+)
+
+<eslint-code-block :rules="{'vue/no-deprecated-functional-template': ['error']}">
+
+```vue
+<!-- ✗ BAD -->
+<template functional>
+  <!-- ... -->
+</template>
+```
+
+</eslint-code-block>
+
+### :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Vue RFCs - 0007-functional-async-api-change](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0007-functional-async-api-change.md)
+- [Guide - Functional Components](https://vuejs.org/v2/guide/render-function.html#Functional-Components)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-functional-template.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-functional-template.js)
diff --git a/docs/rules/no-deprecated-html-element-is.md b/docs/rules/no-deprecated-html-element-is.md
new file mode 100644
index 00000000..800072b9
--- /dev/null
+++ b/docs/rules/no-deprecated-html-element-is.md
@@ -0,0 +1,43 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-html-element-is
+description: disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-html-element-is
+> disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+)
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+
+## :book: Rule Details
+
+This rule reports deprecated the `is` attribute on HTML elements (removed in Vue.js v3.0.0+)
+
+<eslint-code-block :rules="{'vue/no-deprecated-html-element-is': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <div />
+  <component is="foo">
+
+  <!-- ✗ BAD -->
+  <div is="foo" />
+  <div :is="foo" />
+</template>
+```
+
+</eslint-code-block>
+
+### :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Vue RFCs - 0027-custom-elements-interop](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0027-custom-elements-interop.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-html-element-is.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-html-element-is.js)
diff --git a/docs/rules/no-deprecated-vue-config-keycodes.md b/docs/rules/no-deprecated-vue-config-keycodes.md
new file mode 100644
index 00000000..16caa0ef
--- /dev/null
+++ b/docs/rules/no-deprecated-vue-config-keycodes.md
@@ -0,0 +1,46 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-vue-config-keycodes
+description: disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-vue-config-keycodes
+> disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+
+## :book: Rule Details
+
+This rule reports use of deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)
+
+<eslint-code-block filename="a.js" language="javascript ":rules="{'vue/no-deprecated-vue-config-keycodes': ['error']}">
+
+```js
+/* ✗ BAD */
+Vue.config.keyCodes = {
+  // ...
+}
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [vue/no-deprecated-v-on-number-modifiers]
+- [API - Global Config - keyCodes]
+
+[vue/no-deprecated-v-on-number-modifiers]: ./no-deprecated-v-on-number-modifiers.md
+[API - Global Config - keyCodes]: https://vuejs.org/v2/api/#keyCodes
+
+## :books: Further reading
+
+- [Vue RFCs - 0014-drop-keycode-support](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-vue-config-keycodes.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-vue-config-keycodes.js)
diff --git a/docs/rules/no-reserved-component-names.md b/docs/rules/no-reserved-component-names.md
index 3aa5f5db..2d11b233 100644
--- a/docs/rules/no-reserved-component-names.md
+++ b/docs/rules/no-reserved-component-names.md
@@ -9,7 +9,7 @@ description: disallow the use of reserved names in component definitions
 
 ## :book: Rule Details
 
-This rule prevents name collisions between vue components and standard html elements. 
+This rule prevents name collisions between Vue components and standard HTML elements and built-in components.
 
 <eslint-code-block :rules="{'vue/no-reserved-component-names': ['error']}">
 
@@ -26,7 +26,47 @@ export default {
 
 ## :wrench: Options
 
-Nothing.
+```json
+{
+  "vue/no-reserved-component-names": ["error", {
+    "disallowVueBuiltInComponents": false,
+    "disallowVue3BuiltInComponents": false
+  }]
+}
+```
+
+- `disallowVueBuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 2.x built-in component names. Default is `false`.
+- `disallowVue3BuiltInComponents` (`boolean`) ... If `true`, disallow Vue.js 3.x built-in component names. Default is `false`.
+
+### `"disallowVueBuiltInComponents": true`
+
+<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {disallowVueBuiltInComponents: true}]}">
+
+```vue
+<script>
+/* ✗ BAD */
+export default {
+  name: 'transition-group'
+}
+</script>
+```
+
+</eslint-code-block>
+
+### `"disallowVue3BuiltInComponents": true`
+
+<eslint-code-block :rules="{'vue/no-reserved-component-names': ['error', {disallowVue3BuiltInComponents: true}]}">
+
+```vue
+<script>
+/* ✗ BAD */
+export default {
+  name: 'teleport'
+}
+</script>
+```
+
+</eslint-code-block>
 
 ## :books: Further reading
 
@@ -34,6 +74,7 @@ Nothing.
 - [List of SVG elements](https://developer.mozilla.org/en-US/docs/Web/SVG/Element)
 - [Kebab case elements](https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name/22545622#22545622)
 - [Valid custom element name](https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name)
+- [API - Built-In Components](https://vuejs.org/v2/api/index.html#Built-In-Components)
 
 ## :mag: Implementation
 
diff --git a/docs/rules/require-v-if-inside-transition.md b/docs/rules/require-toggle-inside-transition.md
similarity index 78%
rename from docs/rules/require-v-if-inside-transition.md
rename to docs/rules/require-toggle-inside-transition.md
index 5b541d22..c5f4b869 100644
--- a/docs/rules/require-v-if-inside-transition.md
+++ b/docs/rules/require-toggle-inside-transition.md
@@ -1,10 +1,10 @@
 ---
 pageClass: rule-details
 sidebarDepth: 0
-title: vue/require-v-if-inside-transition
+title: vue/require-toggle-inside-transition
 description: require control the display of the content inside `<transition>`
 ---
-# vue/require-v-if-inside-transition
+# vue/require-toggle-inside-transition
 > require control the display of the content inside `<transition>`
 
 - :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
@@ -13,7 +13,7 @@ description: require control the display of the content inside `<transition>`
 
 This rule reports elements inside `<transition>` that do not control the display.
 
-<eslint-code-block :rules="{'vue/require-v-if-inside-transition': ['error']}">
+<eslint-code-block :rules="{'vue/require-toggle-inside-transition': ['error']}">
 
 ```vue
 <template>
@@ -38,5 +38,5 @@ Nothing.
 
 ## :mag: Implementation
 
-- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-v-if-inside-transition.js)
-- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-v-if-inside-transition.js)
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/require-toggle-inside-transition.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/require-toggle-inside-transition.js)
diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js
index eb137448..c76edfdc 100644
--- a/lib/configs/vue3-essential.js
+++ b/lib/configs/vue3-essential.js
@@ -10,12 +10,15 @@ module.exports = {
     'vue/no-deprecated-data-object-declaration': 'error',
     'vue/no-deprecated-events-api': 'error',
     'vue/no-deprecated-filter': 'error',
+    'vue/no-deprecated-functional-template': 'error',
+    'vue/no-deprecated-html-element-is': 'error',
     'vue/no-deprecated-inline-template': 'error',
     'vue/no-deprecated-scope-attribute': 'error',
     'vue/no-deprecated-slot-attribute': 'error',
     'vue/no-deprecated-slot-scope-attribute': 'error',
     'vue/no-deprecated-v-bind-sync': 'error',
     'vue/no-deprecated-v-on-number-modifiers': 'error',
+    'vue/no-deprecated-vue-config-keycodes': 'error',
     'vue/no-dupe-keys': 'error',
     'vue/no-duplicate-attributes': 'error',
     'vue/no-lifecycle-after-await': 'error',
@@ -34,8 +37,8 @@ module.exports = {
     'vue/require-component-is': 'error',
     'vue/require-prop-type-constructor': 'error',
     'vue/require-render-return': 'error',
+    'vue/require-toggle-inside-transition': 'error',
     'vue/require-v-for-key': 'error',
-    'vue/require-v-if-inside-transition': 'error',
     'vue/require-valid-default-prop': 'error',
     'vue/return-in-computed-property': 'error',
     'vue/use-v-on-exact': 'error',
diff --git a/lib/index.js b/lib/index.js
index 740b49e3..cd0691de 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -43,12 +43,15 @@ module.exports = {
     'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
     'no-deprecated-events-api': require('./rules/no-deprecated-events-api'),
     'no-deprecated-filter': require('./rules/no-deprecated-filter'),
+    'no-deprecated-functional-template': require('./rules/no-deprecated-functional-template'),
+    'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
     'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
     'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
     'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
     'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
     'no-deprecated-v-bind-sync': require('./rules/no-deprecated-v-bind-sync'),
     'no-deprecated-v-on-number-modifiers': require('./rules/no-deprecated-v-on-number-modifiers'),
+    'no-deprecated-vue-config-keycodes': require('./rules/no-deprecated-vue-config-keycodes'),
     'no-dupe-keys': require('./rules/no-dupe-keys'),
     'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
     'no-empty-pattern': require('./rules/no-empty-pattern'),
@@ -88,8 +91,8 @@ module.exports = {
     'require-prop-type-constructor': require('./rules/require-prop-type-constructor'),
     'require-prop-types': require('./rules/require-prop-types'),
     'require-render-return': require('./rules/require-render-return'),
+    'require-toggle-inside-transition': require('./rules/require-toggle-inside-transition'),
     'require-v-for-key': require('./rules/require-v-for-key'),
-    'require-v-if-inside-transition': require('./rules/require-v-if-inside-transition'),
     'require-valid-default-prop': require('./rules/require-valid-default-prop'),
     'return-in-computed-property': require('./rules/return-in-computed-property'),
     'script-indent': require('./rules/script-indent'),
diff --git a/lib/rules/no-deprecated-functional-template.js b/lib/rules/no-deprecated-functional-template.js
new file mode 100644
index 00000000..7ab88623
--- /dev/null
+++ b/lib/rules/no-deprecated-functional-template.js
@@ -0,0 +1,51 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated the `functional` template (in Vue.js 3.0.0+)',
+      categories: ['vue3-essential'],
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-functional-template.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      unexpected: 'The `functional` template are deprecated.'
+    }
+  },
+
+  create (context) {
+    return {
+      Program (program) {
+        const element = program.templateBody
+        if (element == null) {
+          return
+        }
+
+        const functional = utils.getAttribute(element, 'functional')
+
+        if (functional) {
+          context.report({
+            node: functional,
+            messageId: 'unexpected'
+          })
+        }
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-deprecated-html-element-is.js b/lib/rules/no-deprecated-html-element-is.js
new file mode 100644
index 00000000..b45a700f
--- /dev/null
+++ b/lib/rules/no-deprecated-html-element-is.js
@@ -0,0 +1,50 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+)',
+      categories: ['vue3-essential'],
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-html-element-is.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      unexpected: 'The `is` attribute on HTML element are deprecated.'
+    }
+  },
+
+  create: function (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='bind'][key.argument.name='is'], VAttribute[directive=false][key.name='is']" (node) {
+        const element = node.parent.parent
+        if (
+          !utils.isHtmlWellKnownElementName(element.rawName) &&
+          !utils.isSvgWellKnownElementName(element.rawName)
+        ) {
+          return
+        }
+        context.report({
+          node,
+          loc: node.loc,
+          messageId: 'unexpected'
+        })
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-vue-config-keycodes.js b/lib/rules/no-deprecated-vue-config-keycodes.js
new file mode 100644
index 00000000..9bae48ab
--- /dev/null
+++ b/lib/rules/no-deprecated-vue-config-keycodes.js
@@ -0,0 +1,44 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated `Vue.config.keyCodes` (in Vue.js 3.0.0+)',
+      categories: ['vue3-essential'],
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-vue-config-keycodes.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      unexpected: '`Vue.config.keyCodes` are deprecated.'
+    }
+  },
+
+  create: function (context) {
+    return {
+      "MemberExpression[property.type='Identifier'][property.name='keyCodes']" (node) {
+        const config = node.object
+        if (config.type !== 'MemberExpression' ||
+          config.property.type !== 'Identifier' ||
+          config.property.name !== 'config' ||
+          config.object.type !== 'Identifier' ||
+          config.object.name !== 'Vue') {
+          return
+        }
+        context.report({
+          node,
+          messageId: 'unexpected'
+        })
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-reserved-component-names.js b/lib/rules/no-reserved-component-names.js
index 75e53556..c566684e 100644
--- a/lib/rules/no-reserved-component-names.js
+++ b/lib/rules/no-reserved-component-names.js
@@ -22,20 +22,44 @@ const kebabCaseElements = [
   'missing-glyph'
 ]
 
+// https://vuejs.org/v2/api/index.html#Built-In-Components
+const vueBuiltInComponents = [
+  'component',
+  'transition',
+  'transition-group',
+  'keep-alive',
+  'slot'
+]
+
+const vue3BuiltInComponents = [
+  'teleport',
+  'suspense'
+]
+
 const isLowercase = (word) => /^[a-z]*$/.test(word)
 const capitalizeFirstLetter = (word) => word[0].toUpperCase() + word.substring(1, word.length)
 
-const RESERVED_NAMES = new Set(
-  [
-    ...kebabCaseElements,
-    ...kebabCaseElements.map(casing.pascalCase),
-    ...htmlElements,
-    ...htmlElements.map(capitalizeFirstLetter),
-    ...deprecatedHtmlElements,
-    ...deprecatedHtmlElements.map(capitalizeFirstLetter),
-    ...svgElements,
-    ...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
-  ])
+const RESERVED_NAMES_IN_HTML = new Set([
+  ...htmlElements,
+  ...htmlElements.map(capitalizeFirstLetter)
+])
+const RESERVED_NAMES_IN_VUE = new Set([
+  ...vueBuiltInComponents,
+  ...vueBuiltInComponents.map(casing.pascalCase)
+])
+const RESERVED_NAMES_IN_VUE3 = new Set([
+  ...RESERVED_NAMES_IN_VUE,
+  ...vue3BuiltInComponents,
+  ...vue3BuiltInComponents.map(casing.pascalCase)
+])
+const RESERVED_NAMES_IN_OTHERS = new Set([
+  ...deprecatedHtmlElements,
+  ...deprecatedHtmlElements.map(capitalizeFirstLetter),
+  ...kebabCaseElements,
+  ...kebabCaseElements.map(casing.pascalCase),
+  ...svgElements,
+  ...svgElements.filter(isLowercase).map(capitalizeFirstLetter)
+])
 
 // ------------------------------------------------------------------------------
 // Rule Definition
@@ -46,14 +70,41 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'disallow the use of reserved names in component definitions',
-      categories: undefined, // 'essential'
+      categories: undefined,
       url: 'https://eslint.vuejs.org/rules/no-reserved-component-names.html'
     },
     fixable: null,
-    schema: []
+    schema: [{
+      type: 'object',
+      properties: {
+        disallowVueBuiltInComponents: {
+          type: 'boolean'
+        },
+        disallowVue3BuiltInComponents: {
+          type: 'boolean'
+        }
+      }
+    }],
+    messages: {
+      reserved: 'Name "{{name}}" is reserved.',
+      reservedInHtml: 'Name "{{name}}" is reserved in HTML.',
+      reservedInVue: 'Name "{{name}}" is reserved in Vue.js.',
+      reservedInVue3: 'Name "{{name}}" is reserved in Vue.js 3.x.'
+    }
   },
 
   create (context) {
+    const options = context.options[0] || {}
+    const disallowVueBuiltInComponents = options.disallowVueBuiltInComponents === true
+    const disallowVue3BuiltInComponents = options.disallowVue3BuiltInComponents === true
+
+    const reservedNames = new Set([
+      ...RESERVED_NAMES_IN_HTML,
+      ...(disallowVueBuiltInComponents ? RESERVED_NAMES_IN_VUE : []),
+      ...(disallowVue3BuiltInComponents ? RESERVED_NAMES_IN_VUE3 : []),
+      ...RESERVED_NAMES_IN_OTHERS
+    ])
+
     function canVerify (node) {
       return node.type === 'Literal' || (
         node.type === 'TemplateLiteral' &&
@@ -70,7 +121,7 @@ module.exports = {
       } else {
         name = node.value
       }
-      if (RESERVED_NAMES.has(name)) {
+      if (reservedNames.has(name)) {
         report(node, name)
       }
     }
@@ -78,7 +129,9 @@ module.exports = {
     function report (node, name) {
       context.report({
         node: node,
-        message: 'Name "{{name}}" is reserved.',
+        messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml'
+          : RESERVED_NAMES_IN_VUE.has(name) ? 'reservedInVue'
+            : RESERVED_NAMES_IN_VUE3.has(name) ? 'reservedInVue3' : 'reserved',
         data: {
           name: name
         }
@@ -98,7 +151,7 @@ module.exports = {
       utils.executeOnVue(context, (obj) => {
         // Report if a component has been registered locally with a reserved name.
         utils.getRegisteredComponents(obj)
-          .filter(({ name }) => RESERVED_NAMES.has(name))
+          .filter(({ name }) => reservedNames.has(name))
           .forEach(({ node, name }) => report(node, name))
 
         const node = obj.properties
diff --git a/lib/rules/require-v-if-inside-transition.js b/lib/rules/require-toggle-inside-transition.js
similarity index 96%
rename from lib/rules/require-v-if-inside-transition.js
rename to lib/rules/require-toggle-inside-transition.js
index 5288e2e3..a4fb3e48 100644
--- a/lib/rules/require-v-if-inside-transition.js
+++ b/lib/rules/require-toggle-inside-transition.js
@@ -40,7 +40,7 @@ module.exports = {
     docs: {
       description: 'require control the display of the content inside `<transition>`',
       categories: ['vue3-essential'],
-      url: 'https://eslint.vuejs.org/rules/require-v-if-inside-transition.html'
+      url: 'https://eslint.vuejs.org/rules/require-toggle-inside-transition.html'
     },
     fixable: null,
     schema: [],
diff --git a/package.json b/package.json
index 1e7cb40b..4be6eed1 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
     "node": ">=8.10"
   },
   "peerDependencies": {
-    "eslint": "^5.0.0 || ^6.0.0"
+    "eslint": "^6.0.0 || ^7.0.0"
   },
   "dependencies": {
     "eslint-utils": "^2.0.0",
@@ -62,7 +62,7 @@
     "acorn": "^7.1.0",
     "babel-eslint": "^10.0.2",
     "chai": "^4.1.0",
-    "eslint": "^6.0.0",
+    "eslint": "^7.0.0",
     "eslint-plugin-eslint-plugin": "^2.0.1",
     "eslint-plugin-import": "^2.18.2",
     "eslint-plugin-vue": "file:.",


Test patch:
diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js
index 5583c9a1..74a38a87 100644
--- a/tests/lib/rules/attributes-order.js
+++ b/tests/lib/rules/attributes-order.js
@@ -625,23 +625,23 @@ tester.run('attributes-order', rule, {
       errors: [
         {
           message: 'Attribute "is" should go before "v-once".',
-          nodeType: 'VIdentifier'
+          type: 'VIdentifier'
         },
         {
           message: 'Attribute "v-on:click" should go before "v-once".',
-          nodeType: 'VDirectiveKey'
+          type: 'VDirectiveKey'
         },
         {
           message: 'Attribute "ref" should go before "v-once".',
-          nodeType: 'VIdentifier'
+          type: 'VIdentifier'
         },
         {
           message: 'Attribute "id" should go before "v-text".',
-          nodeType: 'VIdentifier'
+          type: 'VIdentifier'
         },
         {
           message: 'Attribute "myProp" should go before "v-text".',
-          nodeType: 'VIdentifier'
+          type: 'VIdentifier'
         }
       ]
     },
@@ -683,7 +683,7 @@ tester.run('attributes-order', rule, {
       errors: [
         {
           message: 'Attribute "v-if" should go before "class".',
-          nodeType: 'VIdentifier'
+          type: 'VDirectiveKey'
         }
       ]
     },
@@ -709,7 +709,7 @@ tester.run('attributes-order', rule, {
       errors: [
         {
           message: 'Attribute "v-slot" should go before "v-model".',
-          nodeType: 'VIdentifier'
+          type: 'VDirectiveKey'
         }
       ]
     },
diff --git a/tests/lib/rules/multiline-html-element-content-newline.js b/tests/lib/rules/multiline-html-element-content-newline.js
index 81786ecb..63cf626a 100644
--- a/tests/lib/rules/multiline-html-element-content-newline.js
+++ b/tests/lib/rules/multiline-html-element-content-newline.js
@@ -286,7 +286,7 @@ content
           message: 'Expected 1 line break after opening tag (`<div>`), but no line breaks found.',
           line: 5,
           column: 12,
-          nodeType: 'HTMLTagClose',
+          type: 'HTMLTagClose',
           endLine: 5,
           endColumn: 12
         },
@@ -294,7 +294,7 @@ content
           message: 'Expected 1 line break before closing tag (`</div>`), but no line breaks found.',
           line: 5,
           column: 19,
-          nodeType: 'HTMLEndTagOpen',
+          type: 'HTMLEndTagOpen',
           endLine: 5,
           endColumn: 19
         }
diff --git a/tests/lib/rules/no-deprecated-functional-template.js b/tests/lib/rules/no-deprecated-functional-template.js
new file mode 100644
index 00000000..9b031cf2
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-functional-template.js
@@ -0,0 +1,65 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-functional-template')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019 }
+})
+
+ruleTester.run('no-deprecated-functional-template', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div /></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template f><div /></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template functional></template>',
+      errors: [
+        {
+          line: 1,
+          column: 11,
+          messageId: 'unexpected',
+          endLine: 1,
+          endColumn: 21
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template functional><div /></template>',
+      errors: [
+        {
+          line: 1,
+          column: 11,
+          messageId: 'unexpected'
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-html-element-is.js b/tests/lib/rules/no-deprecated-html-element-is.js
new file mode 100644
index 00000000..96164494
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-html-element-is.js
@@ -0,0 +1,69 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-html-element-is')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019 }
+})
+
+ruleTester.run('no-deprecated-html-element-is', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><component is="foo" /></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div /></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><Foo is="foo" /></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><component :is="\'foo\'" /></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><div is="foo" /></template>',
+      errors: [
+        {
+          line: 1,
+          column: 16,
+          messageId: 'unexpected',
+          endLine: 1,
+          endColumn: 24
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div :is="foo" /></template>',
+      errors: [
+        {
+          line: 1,
+          column: 16,
+          messageId: 'unexpected'
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-vue-config-keycodes.js b/tests/lib/rules/no-deprecated-vue-config-keycodes.js
new file mode 100644
index 00000000..02fc42c4
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-vue-config-keycodes.js
@@ -0,0 +1,55 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-vue-config-keycodes')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-deprecated-vue-config-keycodes', rule, {
+
+  valid: [
+    {
+      filename: 'test.js',
+      code: `Vue.config.silent = true`
+    },
+    {
+      filename: 'test.js',
+      code: 'config.keyCodes = {}'
+    },
+    {
+      filename: 'test.js',
+      code: 'V.config.keyCodes = {}'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.js',
+      code: 'Vue.config.keyCodes = {}',
+      errors: [{
+        message: '`Vue.config.keyCodes` are deprecated.',
+        line: 1,
+        column: 1,
+        type: 'MemberExpression',
+        // messageId: 'unexpected',
+        endLine: 1,
+        endColumn: 20
+      }]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-reserved-component-names.js b/tests/lib/rules/no-reserved-component-names.js
index 0585b5c9..a3bc3bb0 100644
--- a/tests/lib/rules/no-reserved-component-names.js
+++ b/tests/lib/rules/no-reserved-component-names.js
@@ -11,6 +11,12 @@
 const rule = require('../../../lib/rules/no-reserved-component-names')
 const RuleTester = require('eslint').RuleTester
 
+const htmlElements = require('../../../lib/utils/html-elements.json')
+const RESERVED_NAMES_IN_HTML = new Set([
+  ...htmlElements,
+  ...htmlElements.map((word) => word[0].toUpperCase() + word.substring(1, word.length))
+])
+
 // ------------------------------------------------------------------------------
 // Tests
 // ------------------------------------------------------------------------------
@@ -234,6 +240,18 @@ const invalidElements = [
   'xmp', 'Xmp'
 ]
 
+const vue2BuiltInComponents = [
+  'component', 'Component',
+  'transition', 'Transition',
+  'transition-group', 'TransitionGroup',
+  'keep-alive', 'KeepAlive'
+]
+
+const vue3BuiltInComponents = [
+  'teleport', 'Teleport',
+  'suspense', 'Suspense'
+]
+
 const parserOptions = {
   ecmaVersion: 2018,
   sourceType: 'module'
@@ -269,6 +287,16 @@ ruleTester.run('no-reserved-component-names', rule, {
       `,
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          name: 'FooBar'
+        }
+      `,
+      options: [{ disallowVueBuiltInComponents: true, disallowVue3BuiltInComponents: true }],
+      parserOptions
+    },
     {
       filename: 'test.vue',
       code: `Vue.component('FooBar', {})`,
@@ -322,7 +350,41 @@ ruleTester.run('no-reserved-component-names', rule, {
       filename: 'test.js',
       code: `fn1(component.data)`,
       parserOptions
-    }
+    },
+    ...vue2BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions
+      }
+    }),
+    ...vue3BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions
+      }
+    }),
+    ...vue3BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions,
+        options: [{ disallowVueBuiltInComponents: true }]
+      }
+    })
   ],
 
   invalid: [
@@ -336,7 +398,8 @@ ruleTester.run('no-reserved-component-names', rule, {
         `,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'Literal',
           line: 3
         }]
@@ -348,7 +411,8 @@ ruleTester.run('no-reserved-component-names', rule, {
         code: `Vue.component('${name}', component)`,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'Literal',
           line: 1
         }]
@@ -360,7 +424,8 @@ ruleTester.run('no-reserved-component-names', rule, {
         code: `app.component('${name}', component)`,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'Literal',
           line: 1
         }]
@@ -372,7 +437,8 @@ ruleTester.run('no-reserved-component-names', rule, {
         code: `Vue.component(\`${name}\`, {})`,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'TemplateLiteral',
           line: 1
         }]
@@ -384,7 +450,8 @@ ruleTester.run('no-reserved-component-names', rule, {
         code: `app.component(\`${name}\`, {})`,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'TemplateLiteral',
           line: 1
         }]
@@ -400,11 +467,66 @@ ruleTester.run('no-reserved-component-names', rule, {
         }`,
         parserOptions,
         errors: [{
-          message: `Name "${name}" is reserved.`,
+          messageId: RESERVED_NAMES_IN_HTML.has(name) ? 'reservedInHtml' : 'reserved',
+          data: { name },
           type: 'Property',
           line: 3
         }]
       }
+    }),
+    ...vue2BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions,
+        options: [{ disallowVueBuiltInComponents: true }],
+        errors: [{
+          messageId: 'reservedInVue',
+          data: { name },
+          type: 'Literal',
+          line: 3
+        }]
+      }
+    }),
+    ...vue2BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions,
+        options: [{ disallowVue3BuiltInComponents: true }],
+        errors: [{
+          messageId: 'reservedInVue',
+          data: { name },
+          type: 'Literal',
+          line: 3
+        }]
+      }
+    }),
+    ...vue3BuiltInComponents.map(name => {
+      return {
+        filename: `${name}.vue`,
+        code: `
+          export default {
+            name: '${name}'
+          }
+        `,
+        parserOptions,
+        options: [{ disallowVue3BuiltInComponents: true }],
+        errors: [{
+          messageId: 'reservedInVue3',
+          data: { name },
+          type: 'Literal',
+          line: 3
+        }]
+      }
     })
   ]
 })
diff --git a/tests/lib/rules/require-v-if-inside-transition.js b/tests/lib/rules/require-toggle-inside-transition.js
similarity index 96%
rename from tests/lib/rules/require-v-if-inside-transition.js
rename to tests/lib/rules/require-toggle-inside-transition.js
index 08e3edda..76c2b7c5 100644
--- a/tests/lib/rules/require-v-if-inside-transition.js
+++ b/tests/lib/rules/require-toggle-inside-transition.js
@@ -9,7 +9,7 @@
 // ------------------------------------------------------------------------------
 
 const RuleTester = require('eslint').RuleTester
-const rule = require('../../../lib/rules/require-v-if-inside-transition')
+const rule = require('../../../lib/rules/require-toggle-inside-transition')
 
 // ------------------------------------------------------------------------------
 // Tests
@@ -20,7 +20,7 @@ const tester = new RuleTester({
   parserOptions: { ecmaVersion: 2015 }
 })
 
-tester.run('require-v-if-inside-transition', rule, {
+tester.run('require-toggle-inside-transition', rule, {
   valid: [
     {
       filename: 'test.vue',
diff --git a/tests/lib/rules/singleline-html-element-content-newline.js b/tests/lib/rules/singleline-html-element-content-newline.js
index f8fb538c..c47e9c57 100644
--- a/tests/lib/rules/singleline-html-element-content-newline.js
+++ b/tests/lib/rules/singleline-html-element-content-newline.js
@@ -227,7 +227,7 @@ content
           message: 'Expected 1 line break after opening tag (`<div>`), but no line breaks found.',
           line: 3,
           column: 30,
-          nodeType: 'HTMLTagClose',
+          type: 'HTMLTagClose',
           endLine: 3,
           endColumn: 30
         },
@@ -235,7 +235,7 @@ content
           message: 'Expected 1 line break before closing tag (`</div>`), but no line breaks found.',
           line: 3,
           column: 37,
-          nodeType: 'HTMLEndTagOpen',
+          type: 'HTMLEndTagOpen',
           endLine: 3,
           endColumn: 37
         }