Repository: vuejs/eslint-plugin-vue
Pull request #1073 (2020-03-10 07:45:59)

Updated to detect Vue3 components.
This PR makes that the following Vue components are validated by each rule:

- `app.component('name', {/*component*/})`
- `app.mixin({/*component*/})`
- `createApp({/*component*/})`

---

ref #1035


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index ab1f5b22..86b51d48 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -39,8 +39,10 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties |  |
+| [vue/no-custom-modifiers-on-v-model](./no-custom-modifiers-on-v-model.md) | disallow custom modifiers on v-model used on the component |  |
 | [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-multiple-template-root](./no-multiple-template-root.md) | disallow adding multiple root nodes to the template |  |
 | [vue/no-parsing-error](./no-parsing-error.md) | disallow parsing errors in `<template>` |  |
 | [vue/no-reserved-keys](./no-reserved-keys.md) | disallow overwriting reserved keys |  |
 | [vue/no-shared-component-data](./no-shared-component-data.md) | enforce component's data property to be a function | :wrench: |
@@ -50,6 +52,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/no-unused-components](./no-unused-components.md) | disallow registering components that are not used inside templates |  |
 | [vue/no-unused-vars](./no-unused-vars.md) | disallow unused variable definitions of v-for directives or scope attributes |  |
 | [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) | disallow use v-if on the same element as v-for |  |
+| [vue/no-v-model-argument](./no-v-model-argument.md) | disallow adding an argument to `v-model` used in custom component |  |
 | [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 |  |
@@ -58,6 +61,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [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` |  |
 | [vue/valid-template-root](./valid-template-root.md) | enforce valid template root |  |
+| [vue/valid-v-bind-sync](./valid-v-bind-sync.md) | enforce valid `.sync` modifier on `v-bind` directives |  |
 | [vue/valid-v-bind](./valid-v-bind.md) | enforce valid `v-bind` directives |  |
 | [vue/valid-v-cloak](./valid-v-cloak.md) | enforce valid `v-cloak` directives |  |
 | [vue/valid-v-else-if](./valid-v-else-if.md) | enforce valid `v-else-if` directives |  |
@@ -70,6 +74,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/valid-v-once](./valid-v-once.md) | enforce valid `v-once` directives |  |
 | [vue/valid-v-pre](./valid-v-pre.md) | enforce valid `v-pre` directives |  |
 | [vue/valid-v-show](./valid-v-show.md) | enforce valid `v-show` directives |  |
+| [vue/valid-v-slot](./valid-v-slot.md) | enforce valid `v-slot` directives |  |
 | [vue/valid-v-text](./valid-v-text.md) | enforce valid `v-text` directives |  |
 
 ## Priority B: Strongly Recommended (Improving Readability)
@@ -85,6 +90,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/attribute-hyphenation](./attribute-hyphenation.md) | enforce attribute naming style on custom components in template | :wrench: |
+| [vue/component-definition-name-casing](./component-definition-name-casing.md) | enforce specific casing for component definition name | :wrench: |
 | [vue/html-closing-bracket-newline](./html-closing-bracket-newline.md) | require or disallow a line break before tag's closing brackets | :wrench: |
 | [vue/html-closing-bracket-spacing](./html-closing-bracket-spacing.md) | require or disallow a space before tag's closing brackets | :wrench: |
 | [vue/html-end-tags](./html-end-tags.md) | enforce end tag style | :wrench: |
@@ -94,7 +100,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: |
 | [vue/multiline-html-element-content-newline](./multiline-html-element-content-newline.md) | require a line break before and after the contents of a multiline element | :wrench: |
 | [vue/mustache-interpolation-spacing](./mustache-interpolation-spacing.md) | enforce unified spacing in mustache interpolations | :wrench: |
-| [vue/name-property-casing](./name-property-casing.md) | enforce specific casing for the name property in Vue components | :wrench: |
 | [vue/no-multi-spaces](./no-multi-spaces.md) | disallow multiple spaces | :wrench: |
 | [vue/no-spaces-around-equal-signs-in-attribute](./no-spaces-around-equal-signs-in-attribute.md) | disallow spaces around equal signs in attribute | :wrench: |
 | [vue/no-template-shadow](./no-template-shadow.md) | disallow variable declarations from shadowing variables declared in the outer scope |  |
@@ -104,6 +109,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [vue/singleline-html-element-content-newline](./singleline-html-element-content-newline.md) | require a line break before and after the contents of a singleline element | :wrench: |
 | [vue/v-bind-style](./v-bind-style.md) | enforce `v-bind` directive style | :wrench: |
 | [vue/v-on-style](./v-on-style.md) | enforce `v-on` directive style | :wrench: |
+| [vue/v-slot-style](./v-slot-style.md) | enforce `v-slot` directive style | :wrench: |
 
 ## Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
 
@@ -118,6 +124,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
+| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements |  |
 | [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack |  |
 | [vue/order-in-components](./order-in-components.md) | enforce order of properties in components | :wrench: |
 | [vue/this-in-template](./this-in-template.md) | disallow usage of `this` in template |  |
@@ -145,9 +152,7 @@ For example:
 | [vue/brace-style](./brace-style.md) | enforce consistent brace style for blocks | :wrench: |
 | [vue/camelcase](./camelcase.md) | enforce camelcase naming convention |  |
 | [vue/comma-dangle](./comma-dangle.md) | require or disallow trailing commas | :wrench: |
-| [vue/component-definition-name-casing](./component-definition-name-casing.md) | enforce specific casing for component definition name | :wrench: |
 | [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: |
-| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements |  |
 | [vue/dot-location](./dot-location.md) | enforce consistent newlines before and after dots | :wrench: |
 | [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: |
 | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
@@ -155,13 +160,17 @@ For example:
 | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name |  |
 | [vue/max-len](./max-len.md) | enforce a maximum line length |  |
 | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
+| [vue/no-deprecated-filter](./no-deprecated-filter.md) | disallow using deprecated filters syntax |  |
 | [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-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns |  |
 | [vue/no-irregular-whitespace](./no-irregular-whitespace.md) | disallow irregular whitespace |  |
+| [vue/no-ref-as-operand](./no-ref-as-operand.md) | disallow use of value wrapped by `ref()` (Composition API) as an operand |  |
 | [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions |  |
 | [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax |  |
+| [vue/no-setup-props-destructure](./no-setup-props-destructure.md) | disallow destructuring of `props` passed to `setup` |  |
 | [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes |  |
 | [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: |
 | [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
@@ -174,9 +183,6 @@ For example:
 | [vue/space-unary-ops](./space-unary-ops.md) | enforce consistent spacing before or after unary operators | :wrench: |
 | [vue/static-class-names-order](./static-class-names-order.md) | enforce static class names order | :wrench: |
 | [vue/v-on-function-call](./v-on-function-call.md) | enforce or forbid parentheses after method calls without arguments in `v-on` directives | :wrench: |
-| [vue/v-slot-style](./v-slot-style.md) | enforce `v-slot` directive style | :wrench: |
-| [vue/valid-v-bind-sync](./valid-v-bind-sync.md) | enforce valid `.sync` modifier on `v-bind` directives |  |
-| [vue/valid-v-slot](./valid-v-slot.md) | enforce valid `v-slot` directives |  |
 
 ## Deprecated
 
@@ -185,4 +191,5 @@ For example:
 
 | Rule ID | Replaced by |
 |:--------|:------------|
+| [vue/name-property-casing](./name-property-casing.md) | [vue/component-definition-name-casing](./component-definition-name-casing.md) |
 | [vue/no-confusing-v-for-v-if](./no-confusing-v-for-v-if.md) | [vue/no-use-v-if-with-v-for](./no-use-v-if-with-v-for.md) |
diff --git a/docs/rules/component-definition-name-casing.md b/docs/rules/component-definition-name-casing.md
index d43d0ded..d5566ca9 100644
--- a/docs/rules/component-definition-name-casing.md
+++ b/docs/rules/component-definition-name-casing.md
@@ -7,6 +7,7 @@ description: enforce specific casing for component definition name
 # vue/component-definition-name-casing
 > enforce specific casing for component definition name
 
+- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
 - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
 
 Define a style for component definition name casing for consistency purposes.
diff --git a/docs/rules/component-tags-order.md b/docs/rules/component-tags-order.md
index d8c45447..e66cb665 100644
--- a/docs/rules/component-tags-order.md
+++ b/docs/rules/component-tags-order.md
@@ -7,6 +7,8 @@ description: enforce order of component top-level elements
 # vue/component-tags-order
 > enforce order of component top-level elements
 
+- :gear: This rule is included in `"plugin:vue/recommended"`.
+
 ## :book: Rule Details
 
 This rule warns about the order of the `<script>`, `<template>` & `<style>` tags.
diff --git a/docs/rules/name-property-casing.md b/docs/rules/name-property-casing.md
index 98aa4a26..852d4e71 100644
--- a/docs/rules/name-property-casing.md
+++ b/docs/rules/name-property-casing.md
@@ -7,7 +7,7 @@ description: enforce specific casing for the name property in Vue components
 # vue/name-property-casing
 > enforce specific casing for the name property in Vue components
 
-- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+- :warning: This rule was **deprecated** and replaced by [vue/component-definition-name-casing](component-definition-name-casing.md) rule.
 - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
 
 ## :book: Rule Details
diff --git a/docs/rules/no-custom-modifiers-on-v-model.md b/docs/rules/no-custom-modifiers-on-v-model.md
new file mode 100644
index 00000000..f204294b
--- /dev/null
+++ b/docs/rules/no-custom-modifiers-on-v-model.md
@@ -0,0 +1,53 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-custom-modifiers-on-v-model
+description: disallow custom modifiers on v-model used on the component
+---
+# vue/no-custom-modifiers-on-v-model
+> disallow custom modifiers on v-model used on the component
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether `v-model `used on the component do not have custom modifiers.
+
+## Rule Details
+
+This rule reports `v-model` directives in the following cases:
+
+- The directive used on the component has custom modifiers. E.g. `<MyComponent v-model.aaa="foo" />`
+
+<eslint-code-block :rules="{'vue/no-custom-modifiers-on-v-model': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-model="foo" />
+  <MyComponent v-model.trim="foo" />
+  <MyComponent v-model.lazy="foo" />
+  <MyComponent v-model.number="foo" />
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-model.aaa="foo" />
+  <MyComponent v-model.aaa.bbb="foo" />
+
+</template>
+```
+
+</eslint-code-block>
+
+### Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-model]
+
+[valid-v-model]: valid-v-model.md
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-custom-modifiers-on-v-model.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-custom-modifiers-on-v-model.js)
diff --git a/docs/rules/no-deprecated-filter.md b/docs/rules/no-deprecated-filter.md
new file mode 100644
index 00000000..f72a8ee8
--- /dev/null
+++ b/docs/rules/no-deprecated-filter.md
@@ -0,0 +1,50 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-filter
+description: disallow using deprecated filters syntax
+---
+# vue/no-deprecated-filter
+> disallow using deprecated filters syntax
+
+
+## :book: Rule Details
+
+This rule reports deprecated `filters` syntax (removed in Vue.js v3.0.0+)
+
+<eslint-code-block :rules="{'vue/no-deprecated-filter': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  {{ filter(msg) }}
+  {{ filter(msg, '€') }}
+  {{ filterB(filterA(msg)) }}
+  <div v-bind:id="filter(msg)"></div>
+  <div v-bind:id="filter(msg, '€')"></div>
+  <div v-bind:id="filterB(filterA(msg))"></div>
+
+  <!-- ✗ BAD -->
+  {{ msg | filter }}
+  {{ msg | filter('€') }}
+  {{ msg | filterA | filterB }}
+  <div v-bind:id="msg | filter"></div>
+  <div v-bind:id="msg | filter('€')"></div>
+  <div v-bind:id="msg | filterA | filterB"></div>
+</template>
+```
+
+</eslint-code-block>
+
+### :wrench: Options
+
+Nothing.
+
+## :books: Further Reading
+
+- [Vue RFCs - Remove support for filters.](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0015-remove-filters.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-filter.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-filter.js)
diff --git a/docs/rules/no-deprecated-v-bind-sync.md b/docs/rules/no-deprecated-v-bind-sync.md
new file mode 100644
index 00000000..056b10a9
--- /dev/null
+++ b/docs/rules/no-deprecated-v-bind-sync.md
@@ -0,0 +1,52 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-v-bind-sync
+description: disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-v-bind-sync
+> disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+
+- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
+
+## :book: Rule Details
+
+This rule reports use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+
+<eslint-code-block fix :rules="{'vue/no-deprecated-v-bind-sync': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-bind:propName="foo"/>
+  <MyComponent :propName="foo"/>
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-bind:propName.sync="foo"/>
+  <MyComponent v-bind:[dynamiArg].sync="foo"/>
+  <MyComponent v-bind.sync="foo"/>
+  <MyComponent :propName.sync="foo"/>
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-bind]
+
+[valid-v-bind]: valid-v-bind.md
+
+## :books: Further reading
+
+- [RFC: Replace v-bind.sync with v-model argument](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-v-bind-sync.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-v-bind-sync.js)
diff --git a/docs/rules/no-multiple-template-root.md b/docs/rules/no-multiple-template-root.md
new file mode 100644
index 00000000..e22cb0f4
--- /dev/null
+++ b/docs/rules/no-multiple-template-root.md
@@ -0,0 +1,65 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-multiple-template-root
+description: disallow adding multiple root nodes to the template
+---
+# vue/no-multiple-template-root
+> disallow adding multiple root nodes to the template
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether template contains single root element valid for Vue 2.
+
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root is text -->
+<template>Lorem ipsum</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- There are multiple root elements -->
+<template>
+  <div>hello</div>
+  <div>hello</div>
+</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root element has `v-for` directives -->
+<template>
+  <div v-for="item in items"/>
+</template>
+```
+
+</eslint-code-block>
+
+<eslint-code-block :rules="{'vue/no-multiple-template-root': ['error']}">
+
+```vue
+<!-- The root element is `<template>` or `<slot>` -->
+<template>
+  <slot />
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-multiple-template-root.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-multiple-template-root.js)
diff --git a/docs/rules/no-ref-as-operand.md b/docs/rules/no-ref-as-operand.md
new file mode 100644
index 00000000..e6d0b94b
--- /dev/null
+++ b/docs/rules/no-ref-as-operand.md
@@ -0,0 +1,58 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-ref-as-operand
+description: disallow use of value wrapped by `ref()` (Composition API) as an operand
+---
+# vue/no-ref-as-operand
+> disallow use of value wrapped by `ref()` (Composition API) as an operand
+
+## :book: Rule Details
+
+This rule reports cases where a ref is used incorrectly as an operand.
+
+<eslint-code-block :rules="{'vue/no-ref-as-operand': ['error']}">
+
+```vue
+<script>
+import { ref } from 'vue'
+
+export default {
+  setup () {
+    const count = ref(0)
+    const ok = ref(true)
+
+    /* ✓ GOOD */
+    count.value++
+    count.value + 1
+    1 + count.value
+    var msg = ok.value ? 'yes' : 'no'
+
+    /* ✗ BAD */
+    count++
+    count + 1
+    1 + count
+    var msg = ok ? 'yes' : 'no'
+
+    return {
+      count
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further reading
+
+- [Vue RFCs - 0013-composition-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-ref-as-operand.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-ref-as-operand.js)
diff --git a/docs/rules/no-setup-props-destructure.md b/docs/rules/no-setup-props-destructure.md
new file mode 100644
index 00000000..2c56d9aa
--- /dev/null
+++ b/docs/rules/no-setup-props-destructure.md
@@ -0,0 +1,98 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-setup-props-destructure
+description: disallow destructuring of `props` passed to `setup`
+---
+# vue/no-setup-props-destructure
+> disallow destructuring of `props` passed to `setup`
+
+## :book: Rule Details
+
+This rule reports the destructuring of `props` passed to `setup` causing the value to lose reactivity.
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  /* ✓ GOOD */
+  setup(props) {
+    watch(() => {
+      console.log(props.count)
+    })
+
+    return () => {
+      return h('div', props.count)
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+Destructuring the `props` passed to `setup` will cause the value to lose reactivity.
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  /* ✗ BAD */
+  setup({ count }) {
+    watch(() => {
+      console.log(count) // not going to detect changes
+    })
+
+    return () => {
+      return h('div', count) // not going to update
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+Also, destructuring in root scope of `setup()` should error, but ok inside nested callbacks or returned render functions:
+
+<eslint-code-block :rules="{'vue/no-setup-props-destructure': ['error']}">
+
+```vue
+<script>
+export default {
+  setup(props) {
+    /* ✗ BAD */
+    const { count } = props
+
+    watch(() => {
+      /* ✓ GOOD */
+      const { count } = props
+      console.log(count)
+    })
+
+    return () => {
+      /* ✓ GOOD */
+      const { count } = props
+      return h('div', count)
+    }
+  }
+}
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further reading
+
+- [Vue RFCs - 0013-composition-api](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0013-composition-api.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-setup-props-destructure.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-setup-props-destructure.js)
diff --git a/docs/rules/no-v-model-argument.md b/docs/rules/no-v-model-argument.md
new file mode 100644
index 00000000..f67b3a3b
--- /dev/null
+++ b/docs/rules/no-v-model-argument.md
@@ -0,0 +1,49 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-v-model-argument
+description: disallow adding an argument to `v-model` used in custom component
+---
+# vue/no-v-model-argument
+> disallow adding an argument to `v-model` used in custom component
+
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
+This rule checks whether `v-model` used on custom component do not have an argument.
+
+## :book: Rule Details
+
+This rule reports `v-model` directives in the following cases:
+
+- The directive used on component has an argument. E.g. `<MyComponent v-model:aaa="foo" />`
+
+<eslint-code-block :rules="{'vue/no-v-model-argument': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <MyComponent v-model="foo" />
+
+
+  <!-- ✗ BAD -->
+  <MyComponent v-model:aaa="foo" />
+</template>
+```
+
+</eslint-code-block>
+
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-model]
+
+[valid-v-model]: valid-v-model.md
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-v-model-argument.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-v-model-argument.js)
diff --git a/docs/rules/v-slot-style.md b/docs/rules/v-slot-style.md
index 41b5ea6b..9fdddaa7 100644
--- a/docs/rules/v-slot-style.md
+++ b/docs/rules/v-slot-style.md
@@ -7,6 +7,7 @@ description: enforce `v-slot` directive style
 # vue/v-slot-style
 > enforce `v-slot` directive style
 
+- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
 - :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
 
 ## :book: Rule Details
diff --git a/docs/rules/valid-template-root.md b/docs/rules/valid-template-root.md
index 777a674d..7d6e0778 100644
--- a/docs/rules/valid-template-root.md
+++ b/docs/rules/valid-template-root.md
@@ -27,42 +27,8 @@ This rule reports the template root in the following cases:
 <eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
 
 ```vue
-<!-- The root is text -->
-<template>Lorem ipsum</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- There are multiple root elements -->
-<template>
-  <div>hello</div>
-  <div>hello</div>
-</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- The root element has `v-for` directives -->
-<template>
-  <div v-for="item in items"/>
-</template>
-```
-
-</eslint-code-block>
-
-<eslint-code-block :rules="{'vue/valid-template-root': ['error']}">
-
-```vue
-<!-- The root element is `<template>` or `<slot>` -->
-<template>
-  <slot />
-</template>
+<!-- The root with src attribute is not empty -->
+<template src="foo.html"><div></div></template>
 ```
 
 </eslint-code-block>
diff --git a/docs/rules/valid-v-bind-sync.md b/docs/rules/valid-v-bind-sync.md
index 6e8d9504..9ebd51fd 100644
--- a/docs/rules/valid-v-bind-sync.md
+++ b/docs/rules/valid-v-bind-sync.md
@@ -7,6 +7,8 @@ description: enforce valid `.sync` modifier on `v-bind` directives
 # vue/valid-v-bind-sync
 > enforce valid `.sync` modifier on `v-bind` directives
 
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
 This rule checks whether every `.sync` modifier on `v-bind` directives is valid.
 
 ## :book: Rule Details
@@ -62,7 +64,7 @@ Nothing.
 
 ## :books: Further reading
 
-- [Guide - `.sync` Modifier]([https://vuejs.org/v2/guide/list.html#v-for-with-a-Component](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier))
+- [Guide - `.sync` Modifier](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)
 
 ## :mag: Implementation
 
diff --git a/docs/rules/valid-v-bind.md b/docs/rules/valid-v-bind.md
index 30d096e4..03c550f4 100644
--- a/docs/rules/valid-v-bind.md
+++ b/docs/rules/valid-v-bind.md
@@ -54,6 +54,14 @@ Nothing.
 
 [no-parsing-error]: no-parsing-error.md
 
+- [no-deprecated-v-bind-sync]
+
+[no-deprecated-v-bind-sync]: no-deprecated-v-bind-sync.md
+
+- [valid-v-bind-sync]
+
+[valid-v-bind-sync]: valid-v-bind-sync.md
+
 ## :mag: Implementation
 
 - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-v-bind.js)
diff --git a/docs/rules/valid-v-model.md b/docs/rules/valid-v-model.md
index 0aa8a753..934b5edf 100644
--- a/docs/rules/valid-v-model.md
+++ b/docs/rules/valid-v-model.md
@@ -15,8 +15,8 @@ This rule checks whether every `v-model` directive is valid.
 
 This rule reports `v-model` directives in the following cases:
 
-- The directive has that argument. E.g. `<input v-model:aaa="foo">`
-- The directive has the modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
+- The directive used on HTMLElement has an argument. E.g. `<input v-model:aaa="foo">`
+- The directive used on HTMLElement has modifiers which are not supported. E.g. `<input v-model.bbb="foo">`
 - The directive does not have that attribute value. E.g. `<input v-model>`
 - The directive does not have the attribute value which is valid as LHS. E.g. `<input v-model="foo() + bar()">`
 - The directive is on unsupported elements. E.g. `<div v-model="foo"></div>`
@@ -32,6 +32,9 @@ This rule reports `v-model` directives in the following cases:
   <input v-model.lazy="foo">
   <textarea v-model="foo"/>
   <MyComponent v-model="foo"/>
+  <MyComponent v-model:propName="foo"/>
+  <MyComponent v-model.modifier="foo"/>
+  <MyComponent v-model:propName.modifier="foo"/>
   <div v-for="todo in todos">
     <input v-model="todo.name">
   </div>
diff --git a/docs/rules/valid-v-slot.md b/docs/rules/valid-v-slot.md
index edb17c8d..df3fce52 100644
--- a/docs/rules/valid-v-slot.md
+++ b/docs/rules/valid-v-slot.md
@@ -7,6 +7,8 @@ description: enforce valid `v-slot` directives
 # vue/valid-v-slot
 > enforce valid `v-slot` directives
 
+- :gear: This rule is included in all of `"plugin:vue/essential"`, `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.
+
 This rule checks whether every `v-slot` directive is valid.
 
 ## :book: Rule Details
diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md
index 01710c07..1c4d0e38 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/README.md
@@ -85,6 +85,9 @@ All component-related rules are applied to code that passes any of the following
 * `Vue.component()` expression
 * `Vue.extend()` expression
 * `Vue.mixin()` expression
+* `app.component()` expression
+* `app.mixin()` expression
+* `createApp()` expression
 * `export default {}` in `.vue` or `.jsx` file
 
 However, if you want to take advantage of the rules in any of your custom objects that are Vue components, you might need to use the special comment `// @vue/component` that marks an object in the next line as a Vue component in any file, e.g.:
diff --git a/lib/configs/essential.js b/lib/configs/essential.js
index a335310f..21335f19 100644
--- a/lib/configs/essential.js
+++ b/lib/configs/essential.js
@@ -7,8 +7,10 @@ module.exports = {
   extends: require.resolve('./base'),
   rules: {
     'vue/no-async-in-computed-properties': 'error',
+    'vue/no-custom-modifiers-on-v-model': 'error',
     'vue/no-dupe-keys': 'error',
     'vue/no-duplicate-attributes': 'error',
+    'vue/no-multiple-template-root': 'error',
     'vue/no-parsing-error': 'error',
     'vue/no-reserved-keys': 'error',
     'vue/no-shared-component-data': 'error',
@@ -18,6 +20,7 @@ module.exports = {
     'vue/no-unused-components': 'error',
     'vue/no-unused-vars': 'error',
     'vue/no-use-v-if-with-v-for': 'error',
+    'vue/no-v-model-argument': 'error',
     'vue/require-component-is': 'error',
     'vue/require-prop-type-constructor': 'error',
     'vue/require-render-return': 'error',
@@ -26,6 +29,7 @@ module.exports = {
     'vue/return-in-computed-property': 'error',
     'vue/use-v-on-exact': 'error',
     'vue/valid-template-root': 'error',
+    'vue/valid-v-bind-sync': 'error',
     'vue/valid-v-bind': 'error',
     'vue/valid-v-cloak': 'error',
     'vue/valid-v-else-if': 'error',
@@ -38,6 +42,7 @@ module.exports = {
     'vue/valid-v-once': 'error',
     'vue/valid-v-pre': 'error',
     'vue/valid-v-show': 'error',
+    'vue/valid-v-slot': 'error',
     'vue/valid-v-text': 'error'
   }
 }
diff --git a/lib/configs/recommended.js b/lib/configs/recommended.js
index 51bcc2e8..e3ca8eba 100644
--- a/lib/configs/recommended.js
+++ b/lib/configs/recommended.js
@@ -7,6 +7,7 @@ module.exports = {
   extends: require.resolve('./strongly-recommended'),
   rules: {
     'vue/attributes-order': 'warn',
+    'vue/component-tags-order': 'warn',
     'vue/no-v-html': 'warn',
     'vue/order-in-components': 'warn',
     'vue/this-in-template': 'warn'
diff --git a/lib/configs/strongly-recommended.js b/lib/configs/strongly-recommended.js
index 7c32816c..22018866 100644
--- a/lib/configs/strongly-recommended.js
+++ b/lib/configs/strongly-recommended.js
@@ -7,6 +7,7 @@ module.exports = {
   extends: require.resolve('./essential'),
   rules: {
     'vue/attribute-hyphenation': 'warn',
+    'vue/component-definition-name-casing': 'warn',
     'vue/html-closing-bracket-newline': 'warn',
     'vue/html-closing-bracket-spacing': 'warn',
     'vue/html-end-tags': 'warn',
@@ -16,7 +17,6 @@ module.exports = {
     'vue/max-attributes-per-line': 'warn',
     'vue/multiline-html-element-content-newline': 'warn',
     'vue/mustache-interpolation-spacing': 'warn',
-    'vue/name-property-casing': 'warn',
     'vue/no-multi-spaces': 'warn',
     'vue/no-spaces-around-equal-signs-in-attribute': 'warn',
     'vue/no-template-shadow': 'warn',
@@ -25,6 +25,7 @@ module.exports = {
     'vue/require-prop-types': 'warn',
     'vue/singleline-html-element-content-newline': 'warn',
     'vue/v-bind-style': 'warn',
-    'vue/v-on-style': 'warn'
+    'vue/v-on-style': 'warn',
+    'vue/v-slot-style': 'warn'
   }
 }
diff --git a/lib/index.js b/lib/index.js
index 02e65ba9..e772dc59 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -39,18 +39,24 @@ module.exports = {
     'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
     'no-boolean-default': require('./rules/no-boolean-default'),
     'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
+    'no-deprecated-filter': require('./rules/no-deprecated-filter'),
+    'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
     '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-dupe-keys': require('./rules/no-dupe-keys'),
     'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
     'no-empty-pattern': require('./rules/no-empty-pattern'),
     'no-irregular-whitespace': require('./rules/no-irregular-whitespace'),
     'no-multi-spaces': require('./rules/no-multi-spaces'),
+    'no-multiple-template-root': require('./rules/no-multiple-template-root'),
     'no-parsing-error': require('./rules/no-parsing-error'),
+    'no-ref-as-operand': require('./rules/no-ref-as-operand'),
     'no-reserved-component-names': require('./rules/no-reserved-component-names'),
     'no-reserved-keys': require('./rules/no-reserved-keys'),
     'no-restricted-syntax': require('./rules/no-restricted-syntax'),
+    'no-setup-props-destructure': require('./rules/no-setup-props-destructure'),
     'no-shared-component-data': require('./rules/no-shared-component-data'),
     'no-side-effects-in-computed-properties': require('./rules/no-side-effects-in-computed-properties'),
     'no-spaces-around-equal-signs-in-attribute': require('./rules/no-spaces-around-equal-signs-in-attribute'),
@@ -63,6 +69,7 @@ module.exports = {
     'no-unused-vars': require('./rules/no-unused-vars'),
     'no-use-v-if-with-v-for': require('./rules/no-use-v-if-with-v-for'),
     'no-v-html': require('./rules/no-v-html'),
+    'no-v-model-argument': require('./rules/no-v-model-argument'),
     'object-curly-spacing': require('./rules/object-curly-spacing'),
     'order-in-components': require('./rules/order-in-components'),
     'padding-line-between-blocks': require('./rules/padding-line-between-blocks'),
diff --git a/lib/rules/component-definition-name-casing.js b/lib/rules/component-definition-name-casing.js
index 9713cca2..efb3c359 100644
--- a/lib/rules/component-definition-name-casing.js
+++ b/lib/rules/component-definition-name-casing.js
@@ -17,9 +17,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce specific casing for component definition name',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'strongly-recommended',
+      category: 'strongly-recommended',
       url: 'https://eslint.vuejs.org/rules/component-definition-name-casing.html'
     },
     fixable: 'code', // or "code" or "whitespace"
diff --git a/lib/rules/component-tags-order.js b/lib/rules/component-tags-order.js
index 7129aa36..3cfb7484 100644
--- a/lib/rules/component-tags-order.js
+++ b/lib/rules/component-tags-order.js
@@ -21,9 +21,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce order of component top-level elements',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'recommended',
+      category: 'recommended',
       url: 'https://eslint.vuejs.org/rules/component-tags-order.html'
     },
     fixable: null,
diff --git a/lib/rules/name-property-casing.js b/lib/rules/name-property-casing.js
index 98403b31..97987233 100644
--- a/lib/rules/name-property-casing.js
+++ b/lib/rules/name-property-casing.js
@@ -18,10 +18,10 @@ module.exports = {
     docs: {
       description: 'enforce specific casing for the name property in Vue components',
       category: 'strongly-recommended',
-      url: 'https://eslint.vuejs.org/rules/name-property-casing.html'
+      url: 'https://eslint.vuejs.org/rules/name-property-casing.html',
+      replacedBy: ['component-definition-name-casing']
     },
-    // deprecated: true, // TODO Change with major version.
-    // replacedBy: ['component-definition-name-casing'], // TODO Change with major version.
+    deprecated: true,
     fixable: 'code', // or "code" or "whitespace"
     schema: [
       {
diff --git a/lib/rules/no-custom-modifiers-on-v-model.js b/lib/rules/no-custom-modifiers-on-v-model.js
new file mode 100644
index 00000000..0d4305af
--- /dev/null
+++ b/lib/rules/no-custom-modifiers-on-v-model.js
@@ -0,0 +1,57 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+const VALID_MODIFIERS = new Set(['lazy', 'number', 'trim'])
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow custom modifiers on v-model used on the component',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-custom-modifiers-on-v-model.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      notSupportedModifier: "'v-model' directives don't support the modifier '{{name}}'."
+    }
+  },
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='model']" (node) {
+        const element = node.parent.parent
+
+        if (utils.isCustomComponent(element)) {
+          for (const modifier of node.key.modifiers) {
+            if (!VALID_MODIFIERS.has(modifier.name)) {
+              context.report({
+                node,
+                loc: node.loc,
+                messageId: 'notSupportedModifier',
+                data: { name: modifier.name }
+              })
+            }
+          }
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-filter.js b/lib/rules/no-deprecated-filter.js
new file mode 100644
index 00000000..2ec4256e
--- /dev/null
+++ b/lib/rules/no-deprecated-filter.js
@@ -0,0 +1,43 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview disallow using deprecated filters syntax
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated filters syntax',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-filter.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      noDeprecatedFilter: 'Filters are deprecated.'
+    }
+  },
+
+  create: function (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      'VFilterSequenceExpression' (node) {
+        context.report({
+          node,
+          loc: node.loc,
+          messageId: 'noDeprecatedFilter'
+        })
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-v-bind-sync.js b/lib/rules/no-deprecated-v-bind-sync.js
new file mode 100644
index 00000000..240e3b0b
--- /dev/null
+++ b/lib/rules/no-deprecated-v-bind-sync.js
@@ -0,0 +1,54 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-bind-sync.html'
+    },
+    fixable: 'code',
+    schema: [],
+    messages: {
+      syncModifierIsDeprecated: "'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."
+    }
+  },
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='bind']" (node) {
+        if (node.key.modifiers.map(mod => mod.name).includes('sync')) {
+          context.report({
+            node,
+            loc: node.loc,
+            messageId: 'syncModifierIsDeprecated',
+            fix: (fixer) => {
+              const isUsingSpreadSyntax = node.key.argument == null
+              const hasMultipleModifiers = node.key.modifiers.length > 1
+              if (isUsingSpreadSyntax || hasMultipleModifiers) {
+                return
+              }
+
+              const bindArgument = context.getSourceCode().getText(node.key.argument)
+              return fixer.replaceText(node.key, `v-model:${bindArgument}`)
+            }
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-multiple-template-root.js b/lib/rules/no-multiple-template-root.js
new file mode 100644
index 00000000..d4de9461
--- /dev/null
+++ b/lib/rules/no-multiple-template-root.js
@@ -0,0 +1,98 @@
+/**
+ * @fileoverview disallow adding multiple root nodes to the template
+ * @author Przemyslaw Falowski (@przemkow)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow adding multiple root nodes to the template',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-multiple-template-root.html'
+    },
+    fixable: null,
+    schema: []
+  },
+
+  create: function (context) {
+    const sourceCode = context.getSourceCode()
+
+    return {
+      Program (program) {
+        const element = program.templateBody
+        if (element == null) {
+          return
+        }
+
+        const rootElements = []
+        let extraText = null
+        let extraElement = null
+        let vIf = false
+        for (const child of element.children) {
+          if (child.type === 'VElement') {
+            if (rootElements.length === 0) {
+              rootElements.push(child)
+              vIf = utils.hasDirective(child, 'if')
+            } else if (vIf && utils.hasDirective(child, 'else-if')) {
+              rootElements.push(child)
+            } else if (vIf && utils.hasDirective(child, 'else')) {
+              rootElements.push(child)
+              vIf = false
+            } else {
+              extraElement = child
+            }
+          } else if (sourceCode.getText(child).trim() !== '') {
+            extraText = child
+          }
+        }
+
+        if (extraText != null) {
+          context.report({
+            node: extraText,
+            loc: extraText.loc,
+            message: 'The template root requires an element rather than texts.'
+          })
+        } else if (extraElement != null) {
+          context.report({
+            node: extraElement,
+            loc: extraElement.loc,
+            message: 'The template root requires exactly one element.'
+          })
+        } else {
+          for (const element of rootElements) {
+            const tag = element.startTag
+            const name = element.name
+
+            if (name === 'template' || name === 'slot') {
+              context.report({
+                node: tag,
+                loc: tag.loc,
+                message: "The template root disallows '<{{name}}>' elements.",
+                data: { name }
+              })
+            }
+            if (utils.hasDirective(element, 'for')) {
+              context.report({
+                node: tag,
+                loc: tag.loc,
+                message: "The template root disallows 'v-for' directives."
+              })
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-ref-as-operand.js b/lib/rules/no-ref-as-operand.js
new file mode 100644
index 00000000..ea57d9da
--- /dev/null
+++ b/lib/rules/no-ref-as-operand.js
@@ -0,0 +1,124 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+const { ReferenceTracker, findVariable } = require('eslint-utils')
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow use of value wrapped by `ref()` (Composition API) as an operand',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      requireDotValue: 'Must use `.value` to read or write the value wrapped by `ref()`.'
+    }
+  },
+  create (context) {
+    const refReferenceIds = new Map()
+
+    function reportIfRefWrapped (node) {
+      if (!refReferenceIds.has(node)) {
+        return
+      }
+      context.report({
+        node,
+        messageId: 'requireDotValue'
+      })
+    }
+    return {
+      'Program' () {
+        const tracker = new ReferenceTracker(context.getScope())
+        const traceMap = {
+          vue: {
+            [ReferenceTracker.ESM]: true,
+            ref: {
+              [ReferenceTracker.CALL]: true
+            }
+          }
+        }
+
+        for (const { node } of tracker.iterateEsmReferences(traceMap)) {
+          const variableDeclarator = node.parent
+          if (
+            !variableDeclarator ||
+            variableDeclarator.type !== 'VariableDeclarator' ||
+            variableDeclarator.id.type !== 'Identifier'
+          ) {
+            continue
+          }
+          const variable = findVariable(context.getScope(), variableDeclarator.id)
+          if (!variable) {
+            continue
+          }
+          const variableDeclaration = (
+            variableDeclarator.parent &&
+            variableDeclarator.parent.type === 'VariableDeclaration' &&
+            variableDeclarator.parent
+          ) || null
+          for (const reference of variable.references) {
+            if (!reference.isRead()) {
+              continue
+            }
+
+            refReferenceIds.set(reference.identifier, {
+              variableDeclarator,
+              variableDeclaration
+            })
+          }
+        }
+      },
+      // if (refValue)
+      'IfStatement>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // switch (refValue)
+      'SwitchStatement>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // -refValue, +refValue, !refValue, ~refValue, typeof refValue
+      'UnaryExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue++, refValue--
+      'UpdateExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue+1, refValue-1
+      'BinaryExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
+      'AssignmentExpression>Identifier' (node) {
+        reportIfRefWrapped(node)
+      },
+      // refValue || other, refValue && other. ignore: other || refValue
+      'LogicalExpression>Identifier' (node) {
+        if (node.parent.left !== node) {
+          return
+        }
+        // Report only constants.
+        const info = refReferenceIds.get(node)
+        if (!info) {
+          return
+        }
+        if (!info.variableDeclaration || info.variableDeclaration.kind !== 'const') {
+          return
+        }
+        reportIfRefWrapped(node)
+      },
+      // refValue ? x : y
+      'ConditionalExpression>Identifier' (node) {
+        if (node.parent.test !== node) {
+          return
+        }
+        reportIfRefWrapped(node)
+      }
+    }
+  }
+}
diff --git a/lib/rules/no-setup-props-destructure.js b/lib/rules/no-setup-props-destructure.js
new file mode 100644
index 00000000..af7e3754
--- /dev/null
+++ b/lib/rules/no-setup-props-destructure.js
@@ -0,0 +1,136 @@
+/**
+ * @author Yosuke Ota
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+const { findVariable } = require('eslint-utils')
+const utils = require('../utils')
+
+module.exports = {
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description: 'disallow destructuring of `props` passed to `setup`',
+      category: undefined,
+      url: 'https://eslint.vuejs.org/rules/no-setup-props-destructure.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      destructuring: 'Destructuring the `props` will cause the value to lose reactivity.',
+      getProperty: 'Getting a value from the `props` in root scope of `setup()` will cause the value to lose reactivity.'
+    }
+  },
+  create (context) {
+    const setupFunctions = new Map()
+    const forbiddenNodes = new Map()
+
+    function addForbiddenNode (property, node, messageId) {
+      let list = forbiddenNodes.get(property)
+      if (!list) {
+        list = []
+        forbiddenNodes.set(property, list)
+      }
+      list.push({
+        node,
+        messageId
+      })
+    }
+
+    function verify (left, right, { propsReferenceIds, setupProperty }) {
+      if (!right) {
+        return
+      }
+
+      if (left.type === 'ArrayPattern' || left.type === 'ObjectPattern') {
+        if (propsReferenceIds.has(right)) {
+          addForbiddenNode(setupProperty, left, 'getProperty')
+        }
+      } else if (left.type === 'Identifier' && right.type === 'MemberExpression') {
+        if (propsReferenceIds.has(right.object)) {
+          addForbiddenNode(setupProperty, right, 'getProperty')
+        }
+      }
+    }
+
+    let scopeStack = { upper: null, functionNode: null }
+
+    return Object.assign(
+      {
+        'Property[value.type=/^(Arrow)?FunctionExpression$/]' (node) {
+          if (utils.getStaticPropertyName(node) !== 'setup') {
+            return
+          }
+          const param = node.value.params[0]
+          if (!param) {
+            // no arguments
+            return
+          }
+          if (param.type === 'RestElement') {
+            // cannot check
+            return
+          }
+          if (param.type === 'ArrayPattern' || param.type === 'ObjectPattern') {
+            addForbiddenNode(node, param, 'destructuring')
+            return
+          }
+          setupFunctions.set(node.value, {
+            setupProperty: node,
+            propsParam: param,
+            propsReferenceIds: new Set()
+          })
+        },
+        ':function' (node) {
+          scopeStack = { upper: scopeStack, functionNode: node }
+        },
+        ':function>*' (node) {
+          const setupFunctionData = setupFunctions.get(node.parent)
+          if (!setupFunctionData || setupFunctionData.propsParam !== node) {
+            return
+          }
+          const variable = findVariable(context.getScope(), node)
+          if (!variable) {
+            return
+          }
+          const { propsReferenceIds } = setupFunctionData
+          for (const reference of variable.references) {
+            if (!reference.isRead()) {
+              continue
+            }
+
+            propsReferenceIds.add(reference.identifier)
+          }
+        },
+        'VariableDeclarator' (node) {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData) {
+            return
+          }
+          verify(node.id, node.init, setupFunctionData)
+        },
+        'AssignmentExpression' (node) {
+          const setupFunctionData = setupFunctions.get(scopeStack.functionNode)
+          if (!setupFunctionData) {
+            return
+          }
+          verify(node.left, node.right, setupFunctionData)
+        },
+        ':function:exit' (node) {
+          scopeStack = scopeStack.upper
+
+          setupFunctions.delete(node)
+        }
+      },
+      utils.executeOnVue(context, obj => {
+        const reportsList = obj.properties
+          .map(item => forbiddenNodes.get(item))
+          .filter(reports => !!reports)
+        for (const reports of reportsList) {
+          for (const report of reports) {
+            context.report(report)
+          }
+        }
+      })
+    )
+  }
+}
diff --git a/lib/rules/no-v-model-argument.js b/lib/rules/no-v-model-argument.js
new file mode 100644
index 00000000..85b149ea
--- /dev/null
+++ b/lib/rules/no-v-model-argument.js
@@ -0,0 +1,47 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on custom component do not have an argument
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow adding an argument to `v-model` used in custom component',
+      category: 'essential',
+      url: 'https://eslint.vuejs.org/rules/no-v-model-argument.html'
+    },
+    fixable: null,
+    schema: [],
+    messages: {
+      vModelRequireNoArgument: "'v-model' directives require no argument."
+    }
+  },
+
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='model']" (node) {
+        const element = node.parent.parent
+
+        if (node.key.argument && utils.isCustomComponent(element)) {
+          context.report({
+            node,
+            loc: node.loc,
+            messageId: 'vModelRequireNoArgument'
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/v-slot-style.js b/lib/rules/v-slot-style.js
index 782dc435..1fc51ec0 100644
--- a/lib/rules/v-slot-style.js
+++ b/lib/rules/v-slot-style.js
@@ -77,9 +77,7 @@ module.exports = {
     type: 'suggestion',
     docs: {
       description: 'enforce `v-slot` directive style',
-      category: undefined, // strongly-recommended
-      // TODO Change with major version.
-      // category: 'strongly-recommended',
+      category: 'strongly-recommended',
       url: 'https://eslint.vuejs.org/rules/v-slot-style.html'
     },
     fixable: 'code',
diff --git a/lib/rules/valid-template-root.js b/lib/rules/valid-template-root.js
index 2932435d..860b550d 100644
--- a/lib/rules/valid-template-root.js
+++ b/lib/rules/valid-template-root.js
@@ -39,72 +39,27 @@ module.exports = {
 
         const hasSrc = utils.hasAttribute(element, 'src')
         const rootElements = []
-        let extraText = null
-        let extraElement = null
-        let vIf = false
+
         for (const child of element.children) {
-          if (child.type === 'VElement') {
-            if (rootElements.length === 0 && !hasSrc) {
-              rootElements.push(child)
-              vIf = utils.hasDirective(child, 'if')
-            } else if (vIf && utils.hasDirective(child, 'else-if')) {
-              rootElements.push(child)
-            } else if (vIf && utils.hasDirective(child, 'else')) {
-              rootElements.push(child)
-              vIf = false
-            } else {
-              extraElement = child
-            }
-          } else if (sourceCode.getText(child).trim() !== '') {
-            extraText = child
+          if (sourceCode.getText(child).trim() !== '') {
+            rootElements.push(child)
           }
         }
 
-        if (hasSrc && (extraText != null || extraElement != null)) {
-          context.report({
-            node: extraText || extraElement,
-            loc: (extraText || extraElement).loc,
-            message: "The template root with 'src' attribute is required to be empty."
-          })
-        } else if (extraText != null) {
-          context.report({
-            node: extraText,
-            loc: extraText.loc,
-            message: 'The template root requires an element rather than texts.'
-          })
-        } else if (extraElement != null) {
-          context.report({
-            node: extraElement,
-            loc: extraElement.loc,
-            message: 'The template root requires exactly one element.'
-          })
+        if (hasSrc && rootElements.length) {
+          for (const element of rootElements) {
+            context.report({
+              node: element,
+              loc: element.loc,
+              message: "The template root with 'src' attribute is required to be empty."
+            })
+          }
         } else if (rootElements.length === 0 && !hasSrc) {
           context.report({
             node: element,
             loc: element.loc,
-            message: 'The template root requires exactly one element.'
+            message: 'The template requires child element.'
           })
-        } else {
-          for (const element of rootElements) {
-            const tag = element.startTag
-            const name = element.name
-
-            if (name === 'template' || name === 'slot') {
-              context.report({
-                node: tag,
-                loc: tag.loc,
-                message: "The template root disallows '<{{name}}>' elements.",
-                data: { name }
-              })
-            }
-            if (utils.hasDirective(element, 'for')) {
-              context.report({
-                node: tag,
-                loc: tag.loc,
-                message: "The template root disallows 'v-for' directives."
-              })
-            }
-          }
         }
       }
     }
diff --git a/lib/rules/valid-v-bind-sync.js b/lib/rules/valid-v-bind-sync.js
index e8a03fbf..a0029ea6 100644
--- a/lib/rules/valid-v-bind-sync.js
+++ b/lib/rules/valid-v-bind-sync.js
@@ -52,9 +52,7 @@ module.exports = {
     type: 'problem',
     docs: {
       description: 'enforce valid `.sync` modifier on `v-bind` directives',
-      category: undefined,
-      // TODO Change with major version.
-      // category: 'essential',
+      category: 'essential',
       url: 'https://eslint.vuejs.org/rules/valid-v-bind-sync.html'
     },
     fixable: null,
diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js
index d969f2c0..69b35890 100644
--- a/lib/rules/valid-v-model.js
+++ b/lib/rules/valid-v-model.js
@@ -112,24 +112,27 @@ module.exports = {
           })
         }
 
-        if (node.key.argument) {
-          context.report({
-            node,
-            loc: node.loc,
-            message: "'v-model' directives require no argument."
-          })
-        }
-
-        for (const modifier of node.key.modifiers) {
-          if (!VALID_MODIFIERS.has(modifier.name)) {
+        if (!utils.isCustomComponent(element)) {
+          if (node.key.argument) {
             context.report({
               node,
               loc: node.loc,
-              message: "'v-model' directives don't support the modifier '{{name}}'.",
-              data: { name: modifier.name }
+              message: "'v-model' directives require no argument."
             })
           }
+
+          for (const modifier of node.key.modifiers) {
+            if (!VALID_MODIFIERS.has(modifier.name)) {
+              context.report({
+                node,
+                loc: node.loc,
+                message: "'v-model' directives don't support the modifier '{{name}}'.",
+                data: { name: modifier.name }
+              })
+            }
+          }
         }
+
         if (!utils.hasAttributeValue(node)) {
           context.report({
             node,
diff --git a/lib/rules/valid-v-slot.js b/lib/rules/valid-v-slot.js
index 3b2b57b7..2704ce81 100644
--- a/lib/rules/valid-v-slot.js
+++ b/lib/rules/valid-v-slot.js
@@ -142,9 +142,7 @@ module.exports = {
     type: 'problem',
     docs: {
       description: 'enforce valid `v-slot` directives',
-      category: undefined, // essential
-      // TODO Change with major version.
-      // category: 'essential',
+      category: 'essential',
       url: 'https://eslint.vuejs.org/rules/valid-v-slot.html'
     },
     fixable: null,
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 011ec657..d0c91829 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -264,7 +264,7 @@ module.exports = {
     return componentsNode.value.properties
       .filter(p => p.type === 'Property')
       .map(node => {
-        const name = this.getStaticPropertyName(node)
+        const name = getStaticPropertyName(node)
         return name ? { node, name } : null
       })
       .filter(comp => comp != null)
@@ -402,42 +402,7 @@ module.exports = {
    * @param {Property|MethodDefinition|MemberExpression|Literal|TemplateLiteral|Identifier} node - The node to get.
    * @return {string|null} The property name if static. Otherwise, null.
    */
-  getStaticPropertyName (node) {
-    let prop
-    switch (node && node.type) {
-      case 'Property':
-      case 'MethodDefinition':
-        prop = node.key
-        break
-      case 'MemberExpression':
-        prop = node.property
-        break
-      case 'Literal':
-      case 'TemplateLiteral':
-      case 'Identifier':
-        prop = node
-        break
-      // no default
-    }
-
-    switch (prop && prop.type) {
-      case 'Literal':
-        return String(prop.value)
-      case 'TemplateLiteral':
-        if (prop.expressions.length === 0 && prop.quasis.length === 1) {
-          return prop.quasis[0].value.cooked
-        }
-        break
-      case 'Identifier':
-        if (!node.computed) {
-          return prop.name
-        }
-        break
-      // no default
-    }
-
-    return null
-  },
+  getStaticPropertyName,
 
   /**
    * Get all props by looking at all component's properties
@@ -464,8 +429,8 @@ module.exports = {
         .filter(prop => prop.type === 'Property')
         .map(prop => {
           return {
-            key: prop.key, value: this.unwrapTypes(prop.value), node: prop,
-            propName: this.getStaticPropertyName(prop)
+            key: prop.key, value: unwrapTypes(prop.value), node: prop,
+            propName: getStaticPropertyName(prop)
           }
         })
     } else {
@@ -548,28 +513,52 @@ module.exports = {
       const callee = node.callee
 
       if (callee.type === 'MemberExpression') {
-        const calleeObject = this.unwrapTypes(callee.object)
+        const calleeObject = unwrapTypes(callee.object)
+
+        if (calleeObject.type === 'Identifier') {
+          const propName = getStaticPropertyName(callee.property)
+          if (calleeObject.name === 'Vue') {
+            // for Vue.js 2.x
+            // Vue.component('xxx', {}) || Vue.mixin({}) || Vue.extend('xxx', {})
+            const isFullVueComponentForVue2 =
+              ['component', 'mixin', 'extend'].includes(propName) &&
+              isObjectArgument(node)
+
+            return isFullVueComponentForVue2
+          }
 
-        const isFullVueComponent = calleeObject.type === 'Identifier' &&
-          calleeObject.name === 'Vue' &&
-          callee.property.type === 'Identifier' &&
-          ['component', 'mixin', 'extend'].indexOf(callee.property.name) > -1 &&
-          node.arguments.length >= 1 &&
-          node.arguments.slice(-1)[0].type === 'ObjectExpression'
+          // for Vue.js 3.x
+          // app.component('xxx', {}) || app.mixin({})
+          const isFullVueComponent =
+            ['component', 'mixin'].includes(propName) &&
+            isObjectArgument(node)
 
-        return isFullVueComponent
+          return isFullVueComponent
+        }
       }
 
       if (callee.type === 'Identifier') {
-        const isDestructedVueComponent = callee.name === 'component' &&
-          node.arguments.length >= 1 &&
-          node.arguments.slice(-1)[0].type === 'ObjectExpression'
-
-        return isDestructedVueComponent
+        if (callee.name === 'component') {
+          // for Vue.js 2.x
+          // component('xxx', {})
+          const isDestructedVueComponent = isObjectArgument(node)
+          return isDestructedVueComponent
+        }
+        if (callee.name === 'createApp') {
+          // for Vue.js 3.x
+          // createApp({})
+          const isAppVueComponent = isObjectArgument(node)
+          return isAppVueComponent
+        }
       }
     }
 
     return false
+
+    function isObjectArgument (node) {
+      return node.arguments.length > 0 &&
+        unwrapTypes(node.arguments.slice(-1)[0]).type === 'ObjectExpression'
+    }
   },
 
   /**
@@ -584,7 +573,7 @@ module.exports = {
       callee.type === 'Identifier' &&
       callee.name === 'Vue' &&
       node.arguments.length &&
-      node.arguments[0].type === 'ObjectExpression'
+      unwrapTypes(node.arguments[0]).type === 'ObjectExpression'
   },
 
   /**
@@ -647,7 +636,7 @@ module.exports = {
       'CallExpression:exit' (node) {
         // Vue.component('xxx', {}) || component('xxx', {})
         if (!_this.isVueComponent(node) || isDuplicateNode(node.arguments.slice(-1)[0])) return
-        cb(node.arguments.slice(-1)[0])
+        cb(unwrapTypes(node.arguments.slice(-1)[0]))
       }
     }
   },
@@ -664,10 +653,10 @@ module.exports = {
         const callee = callExpr.callee
 
         if (callee.type === 'MemberExpression') {
-          const calleeObject = this.unwrapTypes(callee.object)
+          const calleeObject = unwrapTypes(callee.object)
 
           if (calleeObject.type === 'Identifier' &&
-            calleeObject.name === 'Vue' &&
+            // calleeObject.name === 'Vue' && // Any names can be used in Vue.js 3.x. e.g. app.component()
             callee.property === node &&
             callExpr.arguments.length >= 1) {
             cb(callExpr)
@@ -682,9 +671,9 @@ module.exports = {
    * @param {Set} groups Name of parent group
    */
   * iterateProperties (node, groups) {
-    const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(this.getStaticPropertyName(p.key)))
+    const nodes = node.properties.filter(p => p.type === 'Property' && groups.has(getStaticPropertyName(p.key)))
     for (const item of nodes) {
-      const name = this.getStaticPropertyName(item.key)
+      const name = getStaticPropertyName(item.key)
       if (!name) continue
 
       if (item.value.type === 'ArrayExpression') {
@@ -705,7 +694,7 @@ module.exports = {
   * iterateArrayExpression (node, groupName) {
     assert(node.type === 'ArrayExpression')
     for (const item of node.elements) {
-      const name = this.getStaticPropertyName(item)
+      const name = getStaticPropertyName(item)
       if (name) {
         const obj = { name, groupName, node: item }
         yield obj
@@ -721,7 +710,7 @@ module.exports = {
   * iterateObjectExpression (node, groupName) {
     assert(node.type === 'ObjectExpression')
     for (const item of node.properties) {
-      const name = this.getStaticPropertyName(item)
+      const name = getStaticPropertyName(item)
       if (name) {
         const obj = { name, groupName, node: item.key }
         yield obj
@@ -865,7 +854,56 @@ module.exports = {
    * @param {T} node
    * @return {T}
    */
-  unwrapTypes (node) {
-    return node.type === 'TSAsExpression' ? node.expression : node
+  unwrapTypes
+}
+/**
+* Unwrap typescript types like "X as F"
+* @template T
+* @param {T} node
+* @return {T}
+*/
+function unwrapTypes (node) {
+  return node.type === 'TSAsExpression' ? node.expression : node
+}
+
+/**
+ * Gets the property name of a given node.
+ * @param {Property|MethodDefinition|MemberExpression|Literal|TemplateLiteral|Identifier} node - The node to get.
+ * @return {string|null} The property name if static. Otherwise, null.
+ */
+function getStaticPropertyName (node) {
+  let prop
+  switch (node && node.type) {
+    case 'Property':
+    case 'MethodDefinition':
+      prop = node.key
+      break
+    case 'MemberExpression':
+      prop = node.property
+      break
+    case 'Literal':
+    case 'TemplateLiteral':
+    case 'Identifier':
+      prop = node
+      break
+      // no default
   }
+
+  switch (prop && prop.type) {
+    case 'Literal':
+      return String(prop.value)
+    case 'TemplateLiteral':
+      if (prop.expressions.length === 0 && prop.quasis.length === 1) {
+        return prop.quasis[0].value.cooked
+      }
+      break
+    case 'Identifier':
+      if (!node.computed) {
+        return prop.name
+      }
+      break
+      // no default
+  }
+
+  return null
 }
diff --git a/package.json b/package.json
index e6d02ba4..57a44ce2 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
     "test:base": "mocha \"tests/lib/**/*.js\" --reporter dot",
     "test": "nyc npm run test:base -- \"tests/integrations/*.js\" --timeout 60000",
     "debug": "mocha --inspect-brk \"tests/lib/**/*.js\" --reporter dot --timeout 60000",
+    "cover:report": "nyc report --reporter=html",
     "lint": "eslint . --rulesdir eslint-internal-rules",
     "pretest": "npm run lint",
     "preversion": "npm test && npm run update && git add .",
@@ -47,9 +48,10 @@
     "eslint": "^5.0.0 || ^6.0.0"
   },
   "dependencies": {
+    "eslint-utils": "^2.0.0",
     "natural-compare": "^1.4.0",
-    "vue-eslint-parser": "^7.0.0",
-    "semver": "^5.6.0"
+    "semver": "^5.6.0",
+    "vue-eslint-parser": "^7.0.0"
   },
   "devDependencies": {
     "@types/node": "^4.2.16",


Test patch:
diff --git a/tests/lib/rules/component-definition-name-casing.js b/tests/lib/rules/component-definition-name-casing.js
index 98cb4367..d14eedb7 100644
--- a/tests/lib/rules/component-definition-name-casing.js
+++ b/tests/lib/rules/component-definition-name-casing.js
@@ -116,6 +116,12 @@ ruleTester.run('component-definition-name-casing', rule, {
       options: ['kebab-case'],
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('FooBar', component)`,
+      options: ['PascalCase'],
+      parserOptions
+    },
     {
       filename: 'test.vue',
       code: `Vue.mixin({})`,
@@ -137,6 +143,12 @@ ruleTester.run('component-definition-name-casing', rule, {
       options: ['kebab-case'],
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component(\`fooBar\${foo}\`, component)`,
+      options: ['kebab-case'],
+      parserOptions
+    },
     // https://github.com/vuejs/eslint-plugin-vue/issues/1018
     {
       filename: 'test.js',
@@ -292,6 +304,17 @@ ruleTester.run('component-definition-name-casing', rule, {
         line: 1
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('foo-bar', component)`,
+      output: `app.component('FooBar', component)`,
+      parserOptions,
+      errors: [{
+        message: 'Property name "foo-bar" is not PascalCase.',
+        type: 'Literal',
+        line: 1
+      }]
+    },
     {
       filename: 'test.vue',
       code: `(Vue as VueConstructor<Vue>).component('foo-bar', component)`,
@@ -315,6 +338,17 @@ ruleTester.run('component-definition-name-casing', rule, {
         line: 1
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('foo-bar', {})`,
+      output: `app.component('FooBar', {})`,
+      parserOptions,
+      errors: [{
+        message: 'Property name "foo-bar" is not PascalCase.',
+        type: 'Literal',
+        line: 1
+      }]
+    },
     {
       filename: 'test.js',
       code: `Vue.component('foo_bar', {})`,
diff --git a/tests/lib/rules/match-component-file-name.js b/tests/lib/rules/match-component-file-name.js
index d8a1b141..a97c5941 100644
--- a/tests/lib/rules/match-component-file-name.js
+++ b/tests/lib/rules/match-component-file-name.js
@@ -429,6 +429,16 @@ ruleTester.run('match-component-file-name', rule, {
       options: [{ extensions: ['js'] }],
       parserOptions
     },
+    {
+      filename: 'MyComponent.js',
+      code: `
+        app.component('MyComponent', {
+          template: '<div />'
+        })
+      `,
+      options: [{ extensions: ['js'] }],
+      parserOptions
+    },
     {
       filename: 'MyComponent.js',
       code: `
@@ -701,6 +711,19 @@ ruleTester.run('match-component-file-name', rule, {
         message: 'Component name `MComponent` should match file name `MyComponent`.'
       }]
     },
+    {
+      filename: 'MyComponent.js',
+      code: `
+        app.component(\`MComponent\`, {
+          template: '<div />'
+        })
+      `,
+      options: [{ extensions: ['js'] }],
+      parserOptions,
+      errors: [{
+        message: 'Component name `MComponent` should match file name `MyComponent`.'
+      }]
+    },
 
     // casing
     {
diff --git a/tests/lib/rules/no-custom-modifiers-on-v-model.js b/tests/lib/rules/no-custom-modifiers-on-v-model.js
new file mode 100644
index 00000000..ad80cdb7
--- /dev/null
+++ b/tests/lib/rules/no-custom-modifiers-on-v-model.js
@@ -0,0 +1,72 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on the component do not have custom modifiers
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-custom-modifiers-on-v-model')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-custom-modifiers-on-v-model', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.trim="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.trim="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.lazy="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.lazy="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.number="foo"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.number="foo"></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:propName.aaa="foo"></template>',
+      errors: ["'v-model' directives don't support the modifier 'aaa'."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.aaa="foo"></template>',
+      errors: ["'v-model' directives don't support the modifier 'aaa'."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-filter.js b/tests/lib/rules/no-deprecated-filter.js
new file mode 100644
index 00000000..20957e46
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-filter.js
@@ -0,0 +1,72 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview disallow using deprecated filters syntax
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-filter')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-deprecated-filter', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg }}</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ method(msg) }}</template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filter }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filter(x) }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{ msg | filterA | filterB }}</template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-for="msg in messages">{{ msg | filter }}</div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filter"></div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filter(aaa)"></div></template>',
+      errors: ['Filters are deprecated.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-bind:id="msg | filterA | filterB"></div></template>',
+      errors: ['Filters are deprecated.']
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-v-bind-sync.js b/tests/lib/rules/no-deprecated-v-bind-sync.js
new file mode 100644
index 00000000..6cf90026
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-v-bind-sync.js
@@ -0,0 +1,154 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview Disallow use of deprecated `.sync` modifier on `v-bind` directive (in Vue.js 3.0.0+)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-v-bind-sync')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-deprecated-v-bind-sync', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:foo='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :foo='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:[dynamicArg]='bar'/></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :[dynamicArg]='bar'/></template>"
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:foo.sync='bar'/></template>",
+      output: "<template><MyComponent v-model:foo='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :foo.sync='bar'/></template>",
+      output: "<template><MyComponent v-model:foo='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind:[dynamicArg].sync='bar'/></template>",
+      output: "<template><MyComponent v-model:[dynamicArg]='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent :[dynamicArg].sync='bar'/></template>",
+      output: "<template><MyComponent v-model:[dynamicArg]='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><MyComponent v-bind.sync='bar'/></template>",
+      output: "<template><MyComponent v-bind.sync='bar'/></template>",
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent :foo.sync.unknown="foo" /></template>',
+      output: '<template><MyComponent :foo.sync.unknown="foo" /></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent :[dynamicArg].sync.unknown="foo" /></template>',
+      output: '<template><MyComponent :[dynamicArg].sync.unknown="foo" /></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="x.foo" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="x.foo" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x - 1]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x - 1]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[`${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[`${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[`prefix_${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[`prefix_${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x ? x : \'_\']" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x ? x : \'_\']" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x || \'_\']" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x || \'_\']" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[x()]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[x()]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[/r/.match(x) ? 0 : 1]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[/r/.match(x) ? 0 : 1]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[typeof x]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[typeof x]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="x in list"><MyComponent :foo.sync="foo[tag`${x}`]" /></div></div></template>',
+      output: '<template><div><div v-for="x in list"><MyComponent v-model:foo="foo[tag`${x}`]" /></div></div></template>',
+      errors: ["'.sync' modifier on 'v-bind' directive is deprecated. Use 'v-model:propName' instead."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-multiple-template-root.js b/tests/lib/rules/no-multiple-template-root.js
new file mode 100644
index 00000000..3c8a99bc
--- /dev/null
+++ b/tests/lib/rules/no-multiple-template-root.js
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview disallow adding multiple root nodes to the template
+ * @author Przemyslaw Falowski (@przemkow)
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/no-multiple-template-root')
+
+var RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+ruleTester.run('no-multiple-template-root', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><div>abc</div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <div>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div v-if="foo">abc</div>\n    <div v-else>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <!-- comment -->\n    <div v-if="foo">abc</div>\n    <div v-else-if="bar">abc</div>\n    <div v-else>abc</div>\n</template>'
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>\n    <c1 v-if="1" />\n    <c2 v-else-if="1" />\n    <c3 v-else />\n</template>`
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-if="foo"></div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-if="foo"></div><div v-else-if="bar"></div></template>'
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><div></div><div></div></template>',
+      errors: ['The template root requires exactly one element.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>\n    <div></div>\n    <div></div>\n</template>',
+      errors: ['The template root requires exactly one element.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>{{a b c}}</template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div></div>aaaaaa</template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template>aaaaaa<div></div></template>',
+      errors: ['The template root requires an element rather than texts.']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-for="x in list"></div></template>',
+      errors: ["The template root disallows 'v-for' directives."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><slot></slot></template>',
+      errors: ["The template root disallows '<slot>' elements."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><template></template></template>',
+      errors: ["The template root disallows '<template>' elements."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js
new file mode 100644
index 00000000..b8632c88
--- /dev/null
+++ b/tests/lib/rules/no-ref-as-operand.js
@@ -0,0 +1,334 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-ref-as-operand')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019, sourceType: 'module' }
+})
+
+tester.run('no-ref-as-operand', rule, {
+  valid: [
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    console.log(count.value) // 0
+
+    count.value++
+    console.log(count.value) // 1
+    `,
+    `
+    <script>
+      import { ref } from 'vue'
+      export default {
+        setup() {
+          const count = ref(0)
+          console.log(count.value) // 0
+
+          count.value++
+          console.log(count.value) // 1
+          return {
+            count
+          }
+        }
+      }
+    </script>
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    if (count.value) {}
+    switch (count.value) {}
+    var foo = -count.value
+    var foo = +count.value
+    count.value++
+    count.value--
+    count.value + 1
+    1 - count.value
+    count.value || other
+    count.value && other
+    var foo = count.value ? x : y
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(true)
+    if (bar) foo
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(true)
+    var a = other || foo // ignore
+    var b = other && foo // ignore
+
+    let bar = ref(true)
+    var a = bar || other
+    var b = bar || other
+    `,
+    `
+    import { ref } from 'vue'
+    let count = not_ref(0)
+
+    count++
+    `,
+    `
+    import { ref } from 'vue'
+    const foo = ref(0)
+    const bar = ref(0)
+    var baz = x ? foo : bar
+    `,
+    `
+    import { ref } from 'vue'
+    // Probably wrong, but not checked by this rule.
+    const {value} = ref(0)
+    value++
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref(0)
+    function foo() {
+      let count = 0
+      count++
+    }
+    `,
+    `
+    import { ref } from 'unknown'
+    const count = ref(0)
+    count++
+    `,
+    `
+    import { ref } from 'vue'
+    const count = ref
+    count++
+    `
+  ],
+  invalid: [
+    {
+      code: `
+      import { ref } from 'vue'
+      let count = ref(0)
+
+      count++ // error
+      console.log(count + 1) // error
+      console.log(1 + count) // error
+      `,
+      errors: [
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 5,
+          column: 7,
+          endLine: 5,
+          endColumn: 12
+        },
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 6,
+          column: 19,
+          endLine: 6,
+          endColumn: 24
+        },
+        {
+          message: 'Must use `.value` to read or write the value wrapped by `ref()`.',
+          line: 7,
+          column: 23,
+          endLine: 7,
+          endColumn: 28
+        }
+      ]
+    },
+    {
+      code: `
+      <script>
+        import { ref } from 'vue'
+        export default {
+          setup() {
+            let count = ref(0)
+
+            count++ // error
+            console.log(count + 1) // error
+            console.log(1 + count) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 8,
+          column: 13,
+          endLine: 8,
+          endColumn: 18
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 9,
+          column: 25,
+          endLine: 9,
+          endColumn: 30
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 10,
+          column: 29,
+          endLine: 10,
+          endColumn: 34
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      if (foo) {
+        //
+      }
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      switch (foo) {
+        //
+      }
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(0)
+      var a = -foo
+      var b = +foo
+      var c = !foo
+      var d = ~foo
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 6
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      let foo = ref(0)
+      foo += 1
+      foo -= 1
+      baz += foo
+      baz -= foo
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 6
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      const foo = ref(true)
+      var a = foo || other
+      var b = foo && other
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 5
+        }
+      ]
+    },
+    {
+      code: `
+      import { ref } from 'vue'
+      let foo = ref(true)
+      var a = foo ? x : y
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 4
+        }
+      ]
+    },
+    {
+      code: `
+      <script>
+        import { ref } from 'vue'
+        let count = ref(0)
+        export default {
+          setup() {
+            count++ // error
+            console.log(count + 1) // error
+            console.log(1 + count) // error
+            return {
+              count
+            }
+          }
+        }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'requireDotValue',
+          line: 7
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 8
+        },
+        {
+          messageId: 'requireDotValue',
+          line: 9
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-reserved-component-names.js b/tests/lib/rules/no-reserved-component-names.js
index 43a34f15..0585b5c9 100644
--- a/tests/lib/rules/no-reserved-component-names.js
+++ b/tests/lib/rules/no-reserved-component-names.js
@@ -274,6 +274,11 @@ ruleTester.run('no-reserved-component-names', rule, {
       code: `Vue.component('FooBar', {})`,
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `app.component('FooBar', {})`,
+      parserOptions
+    },
     {
       filename: 'test.js',
       code: `
@@ -349,6 +354,18 @@ ruleTester.run('no-reserved-component-names', rule, {
         }]
       }
     }),
+    ...invalidElements.map(name => {
+      return {
+        filename: 'test.vue',
+        code: `app.component('${name}', component)`,
+        parserOptions,
+        errors: [{
+          message: `Name "${name}" is reserved.`,
+          type: 'Literal',
+          line: 1
+        }]
+      }
+    }),
     ...invalidElements.map(name => {
       return {
         filename: 'test.vue',
@@ -361,6 +378,18 @@ ruleTester.run('no-reserved-component-names', rule, {
         }]
       }
     }),
+    ...invalidElements.map(name => {
+      return {
+        filename: 'test.vue',
+        code: `app.component(\`${name}\`, {})`,
+        parserOptions,
+        errors: [{
+          message: `Name "${name}" is reserved.`,
+          type: 'TemplateLiteral',
+          line: 1
+        }]
+      }
+    }),
     ...invalidElements.map(name => {
       return {
         filename: 'test.vue',
diff --git a/tests/lib/rules/no-setup-props-destructure.js b/tests/lib/rules/no-setup-props-destructure.js
new file mode 100644
index 00000000..2f3bccc5
--- /dev/null
+++ b/tests/lib/rules/no-setup-props-destructure.js
@@ -0,0 +1,335 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/no-setup-props-destructure')
+
+const tester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2019, sourceType: 'module' }
+})
+
+tester.run('no-setup-props-destructure', rule, {
+  valid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            console.log(props.count) // ok
+          })
+
+          return () => {
+            return h('div', props.count) // ok
+          }
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            const { count } = props // ok
+            console.log(count)
+          })
+
+          return () => {
+            const { count } = props // ok
+            return h('div', count)
+          }
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        _setup({count}) {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup() {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(...args) {
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          watch(() => {
+            ({ count } = props)
+          })
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      var noVue = {
+        setup(props) {
+          const { count } = props
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          const {x} = noProps
+          ({y} = noProps)
+          const z = noProps.z
+        }
+      }
+      </script>
+      `
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          ({props} = x)
+        }
+      }
+      </script>
+      `
+    }
+  ],
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup({ count }) { // error
+          watch(() => {
+            console.log(count) // not going to detect changes
+          })
+
+          return () => {
+            return h('div', count) // not going to update
+          }
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'destructuring',
+          line: 4,
+          column: 15,
+          endLine: 4,
+          endColumn: 24
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          const { count } = props // error
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5,
+          column: 17,
+          endLine: 5,
+          endColumn: 26
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup: (props) => {
+          const { count } = props
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(props) {
+          ({ count } = props)
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { count } = p
+        }
+      }
+
+      Vue.component('component', {
+        setup(p) {
+          const { count } = p
+        }
+      })
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        },
+        {
+          messageId: 'getProperty',
+          line: 11
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { count } = p
+        },
+        _setup(p) {
+          const { count } = p
+        },
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const { x } = p
+          const { y } = p
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        },
+        {
+          messageId: 'getProperty',
+          line: 6
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          const foo = p.bar
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 5
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+      <script>
+      export default {
+        setup(p) {
+          let foo
+          foo = p.bar
+        }
+      }
+      </script>
+      `,
+      errors: [
+        {
+          messageId: 'getProperty',
+          line: 6
+        }
+      ]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-shared-component-data.js b/tests/lib/rules/no-shared-component-data.js
index ce0e0062..ade9e9ef 100644
--- a/tests/lib/rules/no-shared-component-data.js
+++ b/tests/lib/rules/no-shared-component-data.js
@@ -135,6 +135,30 @@ ruleTester.run('no-shared-component-data', rule, {
 return {
             foo: 'bar'
           };
+}
+        })
+      `,
+      parserOptions,
+      errors: [{
+        message: '`data` property in component must be a function.',
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        app.component('some-comp', {
+          data: {
+            foo: 'bar'
+          }
+        })
+      `,
+      output: `
+        app.component('some-comp', {
+          data: function() {
+return {
+            foo: 'bar'
+          };
 }
         })
       `,
diff --git a/tests/lib/rules/no-side-effects-in-computed-properties.js b/tests/lib/rules/no-side-effects-in-computed-properties.js
index df186b3a..fa14ef24 100644
--- a/tests/lib/rules/no-side-effects-in-computed-properties.js
+++ b/tests/lib/rules/no-side-effects-in-computed-properties.js
@@ -296,6 +296,25 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
         message: 'Unexpected side effect in "test1" computed property.'
       }],
       parser: require.resolve('@typescript-eslint/parser')
+    },
+
+    {
+      code: `app.component('test', {
+        computed: {
+          test1() {
+            this.firstName = 'lorem'
+            asd.qwe.zxc = 'lorem'
+            return this.firstName + ' ' + this.lastName
+          },
+        }
+      })`,
+      parserOptions,
+      errors: [
+        {
+          line: 4,
+          message: 'Unexpected side effect in "test1" computed property.'
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/no-v-model-argument.js b/tests/lib/rules/no-v-model-argument.js
new file mode 100644
index 00000000..5cf5c595
--- /dev/null
+++ b/tests/lib/rules/no-v-model-argument.js
@@ -0,0 +1,44 @@
+/**
+ * @author Przemyslaw Falowski (@przemkow)
+ * @fileoverview This rule checks whether v-model used on custom component do not have an argument
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-v-model-argument')
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: require.resolve('vue-eslint-parser'),
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('no-v-model-argument', rule, {
+
+  valid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model="bar"></MyComponent></template>'
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:foo="bar"></MyComponent></template>',
+      errors: ["'v-model' directives require no argument."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:foo.trim="bar"></MyComponent></template>',
+      errors: ["'v-model' directives require no argument."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/order-in-components.js b/tests/lib/rules/order-in-components.js
index b022508f..f94b4ec6 100644
--- a/tests/lib/rules/order-in-components.js
+++ b/tests/lib/rules/order-in-components.js
@@ -243,6 +243,38 @@ ruleTester.run('order-in-components', rule, {
         line: 9
       }]
     },
+    {
+      filename: 'test.js',
+      code: `
+        app.component('smart-list', {
+          name: 'app',
+          data () {
+            return {
+              msg: 'Welcome to Your Vue.js App'
+            }
+          },
+          components: {},
+          template: '<div></div>'
+        })
+      `,
+      parserOptions: { ecmaVersion: 6 },
+      output: `
+        app.component('smart-list', {
+          name: 'app',
+          components: {},
+          data () {
+            return {
+              msg: 'Welcome to Your Vue.js App'
+            }
+          },
+          template: '<div></div>'
+        })
+      `,
+      errors: [{
+        message: 'The "components" property should be above the "data" property on line 4.',
+        line: 9
+      }]
+    },
     {
       filename: 'test.js',
       code: `
diff --git a/tests/lib/rules/require-render-return.js b/tests/lib/rules/require-render-return.js
index e7fde4e6..580a6b89 100644
--- a/tests/lib/rules/require-render-return.js
+++ b/tests/lib/rules/require-render-return.js
@@ -86,7 +86,7 @@ ruleTester.run('require-render-return', rule, {
         render() {
           if (a) {
             if (b) {
-              
+
             }
             if (c) {
               return true
@@ -180,6 +180,21 @@ ruleTester.run('require-render-return', rule, {
         line: 2
       }]
     },
+    {
+      code: `app.component('test', {
+        render: function () {
+          if (a) {
+            return
+          }
+        }
+      })`,
+      parserOptions,
+      errors: [{
+        message: 'Expected to return a value in render function.',
+        type: 'Identifier',
+        line: 2
+      }]
+    },
     {
       code: `Vue.component('test2', {
         render: function () {
@@ -199,7 +214,7 @@ ruleTester.run('require-render-return', rule, {
       code: `Vue.component('test2', {
         render: function () {
           if (a) {
-            
+
           } else {
             return h('div', 'hello')
           }
diff --git a/tests/lib/rules/valid-template-root.js b/tests/lib/rules/valid-template-root.js
index 2fd9dbc0..033188cc 100644
--- a/tests/lib/rules/valid-template-root.js
+++ b/tests/lib/rules/valid-template-root.js
@@ -74,53 +74,45 @@ tester.run('valid-template-root', rule, {
     {
       filename: 'test.vue',
       code: '<template lang="pug">test</template>'
-    }
-  ],
-  invalid: [
+    },
     {
       filename: 'test.vue',
-      code: '<template>\n</template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template><div></div><div></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div></div><div></div></template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template>\n    <div></div>\n    <div></div>\n</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>\n    <div></div>\n    <div></div>\n</template>',
-      errors: ['The template root requires exactly one element.']
+      code: '<template>{{a b c}}</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>{{a b c}}</template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template><div></div>aaaaaa</template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div></div>aaaaaa</template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template>aaaaaa<div></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template>aaaaaa<div></div></template>',
-      errors: ['The template root requires an element rather than texts.']
+      code: '<template><div v-for="x in list"></div></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><div v-for="x in list"></div></template>',
-      errors: ["The template root disallows 'v-for' directives."]
+      code: '<template><slot></slot></template>'
     },
     {
       filename: 'test.vue',
-      code: '<template><slot></slot></template>',
-      errors: ["The template root disallows '<slot>' elements."]
-    },
+      code: '<template><template></template></template>'
+    }
+  ],
+  invalid: [
     {
       filename: 'test.vue',
-      code: '<template><template></template></template>',
-      errors: ["The template root disallows '<template>' elements."]
+      code: '<template>\n</template>',
+      errors: ['The template requires child element.']
     },
     {
       filename: 'test.vue',
diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js
index f267ad45..9b2d19cc 100644
--- a/tests/lib/rules/valid-v-model.js
+++ b/tests/lib/rules/valid-v-model.js
@@ -114,6 +114,26 @@ tester.run('valid-v-model', rule, {
     {
       filename: 'test.vue',
       code: '<template><input v-bind:type="a" v-model="b"></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa.modifier="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.modifier="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model:aaa.modifier.modifierTwo="a"></MyComponent></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><MyComponent v-model.modifier.modifierTwo="a"></MyComponent></template>'
     }
   ],
   invalid: [
diff --git a/tests/lib/utils/vue-component.js b/tests/lib/utils/vue-component.js
index db5d9733..69d663e9 100644
--- a/tests/lib/utils/vue-component.js
+++ b/tests/lib/utils/vue-component.js
@@ -108,6 +108,12 @@ function validTests (ext) {
       code: `export default Foo.extend({})`,
       parser: require.resolve('@typescript-eslint/parser'),
       parserOptions
+    },
+    {
+      filename: `test.${ext}`,
+      code: `export default Foo.extend({} as ComponentOptions)`,
+      parser: require.resolve('@typescript-eslint/parser'),
+      parserOptions
     }
   ]
 }
@@ -144,6 +150,18 @@ function invalidTests (ext) {
       parserOptions,
       errors: [makeError(1)]
     },
+    {
+      filename: `test.${ext}`,
+      code: `app.component('name', {})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
+    {
+      filename: `test.${ext}`,
+      code: `app.mixin({})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
     {
       filename: `test.${ext}`,
       code: `export default (Vue as VueConstructor<Vue>).extend({})`,
@@ -158,6 +176,19 @@ function invalidTests (ext) {
       parserOptions,
       errors: [makeError(1)]
     },
+    {
+      filename: `test.${ext}`,
+      code: `export default Vue.extend({} as ComponentOptions)`,
+      parser: require.resolve('@typescript-eslint/parser'),
+      parserOptions,
+      errors: [makeError(1)]
+    },
+    {
+      filename: `test.${ext}`,
+      code: `createApp({})`,
+      parserOptions,
+      errors: [makeError(1)]
+    },
     {
       filename: `test.${ext}`,
       code: `