Repository: vuejs/eslint-plugin-vue
Pull request #1039 (2020-01-25 21:24:43)

Vue3 v-model API changes
### Add suppor for `v-model` API changes
 * support for `v-model:argument` syntax (replacement for `v-bind.sync`)
 *  support for `v-model.customModifier` syntax

**Relates to:**
 #1035 
[RFC0005](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md)
[RFC0011](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md)

**Scope:**
* `vue/valid-v-model` - modify rule implementation to do not report an error when:
  * `v-model` is used with an argument on Vue Component. (`v-model:argument` syntax)
  * `v-model` is used with custom modifiers on Vue Component. (`v-model. customModifier` syntax)

* `vue/no-v-model-argument`- new rule which reports an error when `v-model` is used with an argument on Vue Component (Vue 2 backward compatibility rule, previously handled by `vue/valid-v-model`)

* `vue/no-deprecated-v-bind-sync` - new rule which reports an error when deprecated `.sync` modifier is used on `v-bind` directive. 

* `vue/no-custom-modifiers-on-v-model` - new rule which reports an error when v-model is used with custom modifiers on Vue Component (Vue 2 backward compatibility rule, previously handled by vue/valid-v-model)

**Clarification needed:**
- [ ] [Object spread behaviour](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md#duplicating-the-object-spread-behavior-of-v-bindsyncxxx) (previously handled by `v-bind.sync="xxx"`). 
According to RFC it's not clear which option was selected for the final implementation. Testing that with `Vue 3.0.0-alpha.3` it seems that spread on `v-model` is currently not supported, nevertheless, it requires additional confirmation before merge.


Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index ab1f5b22..d0aed44c 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: |
@@ -158,6 +163,7 @@ For example:
 | [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-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions |  |
@@ -174,9 +180,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 +188,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-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-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/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..c13af25c 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -39,14 +39,17 @@ 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-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-reserved-component-names': require('./rules/no-reserved-component-names'),
     'no-reserved-keys': require('./rules/no-reserved-keys'),
@@ -63,6 +66,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-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-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,


Test patch:
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-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-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/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: [