Repository: vuejs/eslint-plugin-vue
Pull request #1083 (2020-03-23 13:12:19)

New: Add `vue/no-deprecated-data-object-declaration` rule
This PR adds the vue/no-deprecated-data-object-declaration rule.

This rule reports use of deprecated object declaration on `data` property.

[RFC0019](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0019-remove-data-object-declaration.md)
ref #1035

ota-meshi: Thank you for this PR.

However, I think it is the same as the [no-shared-component-data](https://eslint.vuejs.org/rules/no-shared-component-data.html) rule.

yoyo930021: The parent instance is slow disallow object declaration.
I think can change it and rename?
ota-meshi: Thank you for information. You're right.
yoyo930021: I finished.

Patch:
diff --git a/docs/.vuepress/components/eslint-code-block.vue b/docs/.vuepress/components/eslint-code-block.vue
index ab33abdf..fed685d9 100644
--- a/docs/.vuepress/components/eslint-code-block.vue
+++ b/docs/.vuepress/components/eslint-code-block.vue
@@ -18,8 +18,7 @@
 </template>
 
 <script>
-// https://github.com/vuejs/vuepress/issues/451
-import EslintEditor from '../../../node_modules/vue-eslint-editor'
+import EslintEditor from 'vue-eslint-editor'
 import { rules, processors } from '../../../'
 
 export default {
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 0b78e7eb..a6756baa 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -66,12 +66,18 @@ module.exports = {
   base: '/',
   title: 'eslint-plugin-vue',
   description: 'Official ESLint plugin for Vue.js',
-  serviceWorker: true,
   evergreen: true,
   head: [
     ['link', { rel: 'icon', href: '/favicon.png' }]
   ],
 
+  plugins: {
+    '@vuepress/pwa': {
+      serviceWorker: true,
+      updatePopup: true
+    }
+  },
+
   themeConfig: {
     repo: 'vuejs/eslint-plugin-vue',
     docsRepo: 'vuejs/eslint-plugin-vue',
@@ -79,9 +85,6 @@ module.exports = {
     docsBranch: 'master',
     editLinks: true,
     lastUpdated: true,
-    serviceWorker: {
-      updatePopup: true
-    },
 
     nav: [
       { text: 'User Guide', link: '/user-guide/' },
diff --git a/docs/.vuepress/style.styl b/docs/.vuepress/styles/index.styl
similarity index 81%
rename from docs/.vuepress/style.styl
rename to docs/.vuepress/styles/index.styl
index 564ebd26..48680bbf 100644
--- a/docs/.vuepress/style.styl
+++ b/docs/.vuepress/styles/index.styl
@@ -1,4 +1,4 @@
-.theme-container.rule-details .content > h1 {
+.theme-container.rule-details .theme-default-content > h1 {
   font-size: 1.8rem;
 
   + blockquote {
diff --git a/docs/.vuepress/theme/index.js b/docs/.vuepress/theme/index.js
new file mode 100644
index 00000000..b91b8a57
--- /dev/null
+++ b/docs/.vuepress/theme/index.js
@@ -0,0 +1,3 @@
+module.exports = {
+  extend: '@vuepress/theme-default'
+}
diff --git a/docs/.vuepress/theme/Layout.vue b/docs/.vuepress/theme/layouts/Layout.vue
similarity index 83%
rename from docs/.vuepress/theme/Layout.vue
rename to docs/.vuepress/theme/layouts/Layout.vue
index 78dab76f..b88c846c 100644
--- a/docs/.vuepress/theme/Layout.vue
+++ b/docs/.vuepress/theme/layouts/Layout.vue
@@ -12,7 +12,7 @@
     />
     <template
       slot="page-top">
-      <div class="content beta-doc-description">
+      <div class="theme-default-content beta-doc-description">
         <div class="warning custom-block">
           <p class="custom-block-title">Note</p>
           <p>This is a documentation for version <code>{{docVersion}}</code>.<template v-if="hasNotYetBeenReleased"> Also, this documentation may contain content that has not yet been released.</template><br>
@@ -30,14 +30,16 @@
     />
   </BaseLayout>
 </template>
+
 <script>
 /**
  * Layout definition to navigate to older versions of the document.
  */
-import BaseLayout from 'vuepress/lib/default-theme/Layout.vue'
+import BaseLayout from '@vuepress/theme-default/layouts/Layout.vue'
 import semver from 'semver'
-const version = semver.parse(require('../../../package.json').version)
+const version = semver.parse(require('../../../../package.json').version)
 export default {
+  name: 'MyLayout',
   components: {
     BaseLayout
   },
@@ -63,14 +65,15 @@ export default {
   }
 }
 </script>
+
 <style scoped>
 .beta-doc-description {
   padding-bottom: 0;
 }
-* ::v-deep .content ~ .content {
+* ::v-deep .theme-default-content ~ .theme-default-content {
   padding-top: 0;
 }
-* ::v-deep .content:not(.custom) h1{
+* ::v-deep .theme-default-content:not(.custom) h1{
   margin-top: -3.1rem;
 }
 </style>
diff --git a/docs/rules/README.md b/docs/rules/README.md
index e184873e..ebcc7b30 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -2,10 +2,10 @@
 sidebarDepth: 0
 ---
 
-<!-- This file is automatically generated in tools/update-docs-rules-index.js, do not change! -->
-
 # Available rules
 
+<!-- This file is automatically generated in tools/update-docs-rules-index.js, do not change! -->
+
 ::: tip Legend
   :wrench: Indicates that the rule is fixable, and using `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
 :::
@@ -39,11 +39,13 @@ 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-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data | :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-deprecated-v-on-number-modifiers](./no-deprecated-v-on-number-modifiers.md) | disallow using deprecated number (keycode) modifiers | :wrench: |
 | [vue/no-dupe-keys](./no-dupe-keys.md) | disallow duplication of field names |  |
 | [vue/no-duplicate-attributes](./no-duplicate-attributes.md) | disallow duplication of attributes |  |
 | [vue/no-lifecycle-after-await](./no-lifecycle-after-await.md) | disallow asynchronously registered lifecycle hooks |  |
diff --git a/docs/rules/no-deprecated-data-object-declaration.md b/docs/rules/no-deprecated-data-object-declaration.md
new file mode 100644
index 00000000..c095fadb
--- /dev/null
+++ b/docs/rules/no-deprecated-data-object-declaration.md
@@ -0,0 +1,65 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-data-object-declaration
+description: disallow using deprecated object declaration on data
+---
+# vue/no-deprecated-data-object-declaration
+> disallow using deprecated object declaration on data
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-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
+
+This rule reports use of deprecated object declaration on `data` property (in Vue.js 3.0.0+).
+The different from `vue/no-shared-component-data` is the root instance being also disallowed.
+
+<eslint-code-block fix :rules="{'vue/no-deprecated-data-object-declaration': ['error']}">
+
+```vue
+<script>
+/* ✗ BAD */
+createApp({
+  data: {
+    foo: null
+  }
+}).mount('#app')
+export default {
+  data: {
+    foo: null
+  }
+}
+
+/* ✓ GOOD */
+export default {
+  data () {
+    return {
+      foo: null
+    }
+  }
+}
+createApp({
+  data () {
+    return {
+      foo: null
+    }
+  }
+}).mount('#app')
+</script>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :books: Further reading
+
+- [RFC: remove data object declaration](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0019-remove-data-object-declaration.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-data-object-declaration.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-data-object-declaration.js)
diff --git a/docs/rules/no-deprecated-v-on-number-modifiers.md b/docs/rules/no-deprecated-v-on-number-modifiers.md
new file mode 100644
index 00000000..5ecba24a
--- /dev/null
+++ b/docs/rules/no-deprecated-v-on-number-modifiers.md
@@ -0,0 +1,52 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/no-deprecated-v-on-number-modifiers
+description: disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)
+---
+# vue/no-deprecated-v-on-number-modifiers
+> disallow using deprecated number (keycode) modifiers (in Vue.js 3.0.0+)
+
+- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
+- :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 `KeyboardEvent.keyCode` modifier on `v-on` directive (in Vue.js 3.0.0+)
+
+<eslint-code-block fix :rules="{'vue/no-deprecated-v-on-number-modifiers': ['error']}">
+
+```vue
+<template>
+  <!-- ✓ GOOD -->
+  <input v-on:keyup.page-down="onArrowUp">
+  <input @keyup.page-down="onArrowUp">
+  <input @keyup.9="onArrowUp"> <!-- 9 is KeyboardEvent.key -->
+
+
+  <!-- ✗ BAD -->
+  <input v-on:keyup.34="onArrowUp">
+  <input @keyup.34="onArrowUp">
+</template>
+```
+
+</eslint-code-block>
+
+## :wrench: Options
+
+Nothing.
+
+## :couple: Related rules
+
+- [valid-v-on]
+
+[valid-v-on]: valid-v-on.md
+
+## :books: Further reading
+
+- [RFC: drop keycode support](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0014-drop-keycode-support.md)
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-v-on-number-modifiers.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-v-on-number-modifiers.js)
diff --git a/docs/rules/no-unused-vars.md b/docs/rules/no-unused-vars.md
index 321aede1..4d5d9ee2 100644
--- a/docs/rules/no-unused-vars.md
+++ b/docs/rules/no-unused-vars.md
@@ -35,14 +35,18 @@ This rule report variable definitions of v-for directives or scope attributes if
 
 ```js
 {
-    "vue/no-unsed-vars": [{
-        "ignorePattern": '^_',
+    "vue/no-unused-vars": ["error", {
+        "ignorePattern": "^_"
     }]
 }
 ```
+
 - `ignorePattern` ... disables reporting when your definitions of v-for directives or scope attributes match your ignorePattern Regular expression. default `null`, will ignore nothing
+
 ## :rocket: Suggestion
+
 - When your ignorePattern set to `^_`, we could provide a suggestion which add a prefix`_` to your variable and no more eslint error
+
 ## :mag: Implementation
 
 - [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-unused-vars.js)
diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js
index 3e45ddd2..3f2865df 100644
--- a/lib/configs/vue3-essential.js
+++ b/lib/configs/vue3-essential.js
@@ -7,11 +7,13 @@ module.exports = {
   extends: require.resolve('./base'),
   rules: {
     'vue/no-async-in-computed-properties': 'error',
+    'vue/no-deprecated-data-object-declaration': 'error',
     'vue/no-deprecated-filter': 'error',
     'vue/no-deprecated-scope-attribute': 'error',
     'vue/no-deprecated-slot-attribute': 'error',
     'vue/no-deprecated-slot-scope-attribute': 'error',
     'vue/no-deprecated-v-bind-sync': 'error',
+    'vue/no-deprecated-v-on-number-modifiers': 'error',
     'vue/no-dupe-keys': 'error',
     'vue/no-duplicate-attributes': 'error',
     'vue/no-lifecycle-after-await': 'error',
diff --git a/lib/index.js b/lib/index.js
index 10e2e876..4c746fd8 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -40,11 +40,13 @@ module.exports = {
     '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-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'),
     'no-deprecated-filter': require('./rules/no-deprecated-filter'),
     'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
     'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
     'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
     'no-deprecated-v-bind-sync': require('./rules/no-deprecated-v-bind-sync'),
+    'no-deprecated-v-on-number-modifiers': require('./rules/no-deprecated-v-on-number-modifiers'),
     'no-dupe-keys': require('./rules/no-dupe-keys'),
     'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
     'no-empty-pattern': require('./rules/no-empty-pattern'),
diff --git a/lib/rules/no-deprecated-data-object-declaration.js b/lib/rules/no-deprecated-data-object-declaration.js
new file mode 100644
index 00000000..4c8399d7
--- /dev/null
+++ b/lib/rules/no-deprecated-data-object-declaration.js
@@ -0,0 +1,86 @@
+/**
+ * @fileoverview disallow using deprecated object declaration on data
+ * @author yoyo930021
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+function isOpenParen (token) {
+  return token.type === 'Punctuator' && token.value === '('
+}
+
+function isCloseParen (token) {
+  return token.type === 'Punctuator' && token.value === ')'
+}
+
+function getFirstAndLastTokens (node, sourceCode) {
+  let first = sourceCode.getFirstToken(node)
+  let last = sourceCode.getLastToken(node)
+
+  // If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses.
+  while (true) {
+    const prev = sourceCode.getTokenBefore(first)
+    const next = sourceCode.getTokenAfter(last)
+    if (isOpenParen(prev) && isCloseParen(next)) {
+      first = prev
+      last = next
+    } else {
+      return { first, last }
+    }
+  }
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated object declaration on data',
+      categories: ['vue3-essential'],
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-data-object-declaration.html'
+    },
+    fixable: 'code',
+    schema: [],
+    messages: {
+      objectDeclarationIsDeprecated: "Object declaration on \'data\' property is deprecated. Using function declaration instead."
+    }
+  },
+
+  create (context) {
+    const sourceCode = context.getSourceCode()
+
+    return utils.executeOnVue(context, (obj) => {
+      obj.properties
+        .filter(p =>
+          p.type === 'Property' &&
+          p.key.type === 'Identifier' &&
+          p.key.name === 'data' &&
+          p.value.type !== 'FunctionExpression' &&
+          p.value.type !== 'ArrowFunctionExpression' &&
+          p.value.type !== 'Identifier'
+        )
+        .forEach(p => {
+          context.report({
+            node: p,
+            messageId: 'objectDeclarationIsDeprecated',
+            fix (fixer) {
+              const tokens = getFirstAndLastTokens(p.value, sourceCode)
+
+              return [
+                fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
+                fixer.insertTextAfter(tokens.last, ';\n}')
+              ]
+            }
+          })
+        })
+    })
+  }
+}
diff --git a/lib/rules/no-deprecated-v-on-number-modifiers.js b/lib/rules/no-deprecated-v-on-number-modifiers.js
new file mode 100644
index 00000000..651991cd
--- /dev/null
+++ b/lib/rules/no-deprecated-v-on-number-modifiers.js
@@ -0,0 +1,58 @@
+/**
+ * @fileoverview disallow using deprecated number (keycode) modifiers
+ * @author yoyo930021
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+const keyCodeToKey = require('../utils/keycode-to-key.json')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    type: 'problem',
+    docs: {
+      description: 'disallow using deprecated number (keycode) modifiers',
+      categories: ['vue3-essential'],
+      url: 'https://eslint.vuejs.org/rules/no-deprecated-v-on-number-modifiers.html'
+    },
+    fixable: 'code',
+    schema: [],
+    messages: {
+      numberModifierIsDeprecated: "'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."
+    }
+  },
+
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      "VAttribute[directive=true][key.name.name='on']" (node) {
+        const modifier = node.key.modifiers.find(mod => Number.isInteger(parseInt(mod.name, 10)))
+        if (!modifier) return
+
+        const keyCodes = parseInt(modifier.name, 10)
+        if (
+          keyCodes > 9 || keyCodes < 0
+        ) {
+          context.report({
+            node,
+            loc: node.loc,
+            messageId: 'numberModifierIsDeprecated',
+            fix: (fixer) => {
+              const key = keyCodeToKey[keyCodes]
+              if (!key) return
+
+              return fixer.replaceTextRange(modifier.range, `${key}`)
+            }
+          })
+        }
+      }
+    })
+  }
+}
diff --git a/lib/rules/no-dupe-keys.js b/lib/rules/no-dupe-keys.js
index 1bee4d94..28018e44 100644
--- a/lib/rules/no-dupe-keys.js
+++ b/lib/rules/no-dupe-keys.js
@@ -10,7 +10,7 @@ const utils = require('../utils')
 // Rule Definition
 // ------------------------------------------------------------------------------
 
-const GROUP_NAMES = ['props', 'computed', 'data', 'methods']
+const GROUP_NAMES = ['props', 'computed', 'data', 'methods', 'setup']
 
 module.exports = {
   meta: {
diff --git a/lib/rules/no-reserved-keys.js b/lib/rules/no-reserved-keys.js
index 80ecada3..249f1054 100644
--- a/lib/rules/no-reserved-keys.js
+++ b/lib/rules/no-reserved-keys.js
@@ -11,7 +11,7 @@ const utils = require('../utils')
 // ------------------------------------------------------------------------------
 
 const RESERVED_KEYS = require('../utils/vue-reserved.json')
-const GROUP_NAMES = ['props', 'computed', 'data', 'methods']
+const GROUP_NAMES = ['props', 'computed', 'data', 'methods', 'setup']
 
 module.exports = {
   meta: {
diff --git a/lib/utils/keycode-to-key.json b/lib/utils/keycode-to-key.json
new file mode 100644
index 00000000..43f48030
--- /dev/null
+++ b/lib/utils/keycode-to-key.json
@@ -0,0 +1,100 @@
+{
+  "8": "backspace",
+  "9": "tab",
+  "13": "enter",
+  "16": "shift",
+  "17": "ctrl",
+  "18": "alt",
+  "19": "pause-break",
+  "20": "caps-lock",
+  "27": "escape",
+  "33": "page-up",
+  "34": "page-down",
+  "35": "end",
+  "36": "home",
+  "37": "left-arrow",
+  "38": "up-arrow",
+  "39": "right-arrow",
+  "40": "down-arrow",
+  "45": "insert",
+  "46": "delete",
+  "48": "0",
+  "49": "1",
+  "50": "2",
+  "51": "3",
+  "52": "4",
+  "53": "5",
+  "54": "6",
+  "55": "7",
+  "56": "8",
+  "57": "9",
+  "65": "a",
+  "66": "b",
+  "67": "c",
+  "68": "d",
+  "69": "e",
+  "70": "f",
+  "71": "g",
+  "72": "h",
+  "73": "i",
+  "74": "j",
+  "75": "k",
+  "76": "l",
+  "77": "m",
+  "78": "n",
+  "79": "o",
+  "80": "p",
+  "81": "q",
+  "82": "r",
+  "83": "s",
+  "84": "t",
+  "85": "u",
+  "86": "v",
+  "87": "w",
+  "88": "x",
+  "89": "y",
+  "90": "z",
+  "91": "left-window-key",
+  "92": "right-window-key",
+  "93": "select-key",
+  "96": "numpad-0",
+  "97": "numpad-1",
+  "98": "numpad-2",
+  "99": "numpad-3",
+  "100": "numpad-4",
+  "101": "numpad-5",
+  "102": "numpad-6",
+  "103": "numpad-7",
+  "104": "numpad-8",
+  "105": "numpad-9",
+  "106": "multiply",
+  "107": "add",
+  "109": "subtract",
+  "110": "decimal-point",
+  "111": "divide",
+  "112": "f1",
+  "113": "f2",
+  "114": "f3",
+  "115": "f4",
+  "116": "f5",
+  "117": "f6",
+  "118": "f7",
+  "119": "f8",
+  "120": "f9",
+  "121": "f10",
+  "122": "f11",
+  "123": "f12",
+  "144": "num-lock",
+  "145": "scroll-lock",
+  "186": "semi-colon",
+  "187": "equal-sign",
+  "188": "comma",
+  "189": "dash",
+  "190": "period",
+  "191": "forward-slash",
+  "192": "grave-accent",
+  "219": "open-bracket",
+  "220": "back-slash",
+  "221": "close-braket",
+  "222": "single-quote"
+}
diff --git a/package.json b/package.json
index c6bde732..2d568134 100644
--- a/package.json
+++ b/package.json
@@ -58,6 +58,7 @@
   "devDependencies": {
     "@types/node": "^4.2.16",
     "@typescript-eslint/parser": "^2.6.1",
+    "@vuepress/plugin-pwa": "^1.4.0",
     "acorn": "^7.1.0",
     "babel-eslint": "^10.0.2",
     "chai": "^4.1.0",
@@ -66,12 +67,12 @@
     "eslint-plugin-import": "^2.18.2",
     "eslint-plugin-vue": "file:.",
     "eslint-plugin-vue-libs": "^4.0.0",
-    "eslint4b": "^6.6.0",
+    "eslint4b": "^6.8.0",
     "lodash": "^4.17.4",
     "mocha": "^5.2.0",
     "nyc": "^12.0.2",
     "typescript": "^3.5.2",
-    "vue-eslint-editor": "^0.1.4",
-    "vuepress": "^0.14.5"
+    "vue-eslint-editor": "^1.1.0",
+    "vuepress": "^1.4.0"
   }
 }
diff --git a/tools/update-docs-rules-index.js b/tools/update-docs-rules-index.js
index e2283b3b..23b1172b 100644
--- a/tools/update-docs-rules-index.js
+++ b/tools/update-docs-rules-index.js
@@ -94,10 +94,10 @@ fs.writeFileSync(
 sidebarDepth: 0
 ---
 
-<!-- This file is automatically generated in tools/update-docs-rules-index.js, do not change! -->
-
 # Available rules
 
+<!-- This file is automatically generated in tools/update-docs-rules-index.js, do not change! -->
+
 ::: tip Legend
   :wrench: Indicates that the rule is fixable, and using \`--fix\` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the reported problems.
 :::


Test patch:
diff --git a/tests/lib/rules/no-deprecated-data-object-declaration.js b/tests/lib/rules/no-deprecated-data-object-declaration.js
new file mode 100644
index 00000000..1ced275c
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-data-object-declaration.js
@@ -0,0 +1,289 @@
+/**
+ * @fileoverview disallow using deprecated object declaration on data
+ * @author yoyo930021
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-data-object-declaration')
+
+const RuleTester = require('eslint').RuleTester
+
+const parserOptions = {
+  ecmaVersion: 2018,
+  sourceType: 'module'
+}
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester()
+ruleTester.run('no-deprecated-data-object-declaration', rule, {
+
+  valid: [
+    {
+      filename: 'test.js',
+      code: `
+        createApp({
+          data: function () {
+            return {
+              foo: 'bar'
+            }
+          }
+        }).mount('#app')
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.js',
+      code: `
+        createApp({
+          ...data,
+          data () {
+            return {
+              foo: 'bar'
+            }
+          }
+        }).mount('#app')
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.js',
+      code: `
+        const app = createApp(App)
+        app.component('some-comp', {
+          data: function () {
+            return {
+              foo: 'bar'
+            }
+          }
+        })
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data: function () {
+            return {
+              foo: 'bar'
+            }
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          ...foo
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data: () => {
+
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data () {
+
+          },
+          methods: {
+
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data () {
+
+          },
+          computed: {
+
+          }
+        }
+      `,
+      parserOptions
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.js',
+      code: `
+        const app = createApp(App)
+        app.component('some-comp', {
+          data: {
+            foo: 'bar'
+          }
+        })
+      `,
+      output: `
+        const app = createApp(App)
+        app.component('some-comp', {
+          data: function() {
+return {
+            foo: 'bar'
+          };
+}
+        })
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 4
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        app.component('some-comp', {
+          data: {
+            foo: 'bar'
+          }
+        })
+      `,
+      output: `
+        app.component('some-comp', {
+          data: function() {
+return {
+            foo: 'bar'
+          };
+}
+        })
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data: {
+            foo: 'bar'
+          }
+        }
+      `,
+      output: `
+        export default {
+          data: function() {
+return {
+            foo: 'bar'
+          };
+}
+        }
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data: /*a*/ (/*b*/{
+            foo: 'bar'
+          })
+        }
+      `,
+      output: `
+        export default {
+          data: /*a*/ function() {
+return (/*b*/{
+            foo: 'bar'
+          });
+}
+        }
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        createApp({
+          data: {
+            foo: 'bar'
+          }
+        })
+      `,
+      output: `
+        createApp({
+          data: function() {
+return {
+            foo: 'bar'
+          };
+}
+        })
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 3
+      }]
+    },
+    {
+      filename: 'test.js',
+      code: `
+        createApp({
+          data: {
+            foo: 'bar'
+          }
+        }).mount('#app')
+      `,
+      output: `
+        createApp({
+          data: function() {
+return {
+            foo: 'bar'
+          };
+}
+        }).mount('#app')
+      `,
+      parserOptions,
+      errors: [{
+        message: "Object declaration on \'data\' property is deprecated. Using function declaration instead.",
+        line: 3
+      }]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-deprecated-v-on-number-modifiers.js b/tests/lib/rules/no-deprecated-v-on-number-modifiers.js
new file mode 100644
index 00000000..8c6d7269
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-v-on-number-modifiers.js
@@ -0,0 +1,174 @@
+/**
+ * @fileoverview disallow using deprecated number (keyCodes) modifiers
+ * @author yoyo930021
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-deprecated-v-on-number-modifiers')
+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><input v-on:keyup.page-down='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.page-down='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.9='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.9='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.0='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.0='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.4='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.4='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.page-down.native='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.page-down.native='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.0.native='onArrowUp'></template>"
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.0.native='onArrowUp'></template>"
+    }
+  ],
+
+  invalid: [
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.34='onArrowUp'></template>",
+      output: "<template><input v-on:keyup.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.34.native='onArrowUp'></template>",
+      output: "<template><input v-on:keyup.page-down.native='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:keyup.unknown.34='onArrowUp'></template>",
+      output: "<template><input v-on:keyup.unknown.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:[dynamicArg].34='onArrowUp'></template>",
+      output: "<template><input v-on:[dynamicArg].page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:[dynamicArg].unknown.34='onArrowUp'></template>",
+      output: "<template><input v-on:[dynamicArg].unknown.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input v-on:[dynamicArg].34.unknown='onArrowUp'></template>",
+      output: "<template><input v-on:[dynamicArg].page-down.unknown='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.34='onArrowUp'></template>",
+      output: "<template><input @keyup.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.34.native='onArrowUp'></template>",
+      output: "<template><input @keyup.page-down.native='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.unknown.34='onArrowUp'></template>",
+      output: "<template><input @keyup.unknown.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @[dynamicArg].34='onArrowUp'></template>",
+      output: "<template><input @[dynamicArg].page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @[dynamicArg].unknown.34='onArrowUp'></template>",
+      output: "<template><input @[dynamicArg].unknown.page-down='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @[dynamicArg].34.unknown='onArrowUp'></template>",
+      output: "<template><input @[dynamicArg].page-down.unknown='onArrowUp'></template>",
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.10='onArrowUp'></template>",
+      output: null,
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.10.native='onArrowUp'></template>",
+      output: null,
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @keyup.unknown.10='onArrowUp'></template>",
+      output: null,
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    },
+    {
+      filename: 'test.vue',
+      code: "<template><input @[dynamicArg].unknown.10='onArrowUp'></template>",
+      output: null,
+      errors: ["'KeyboardEvent.keyCode' modifier on 'v-on' directive is deprecated. Using 'KeyboardEvent.key' instead."]
+    }
+  ]
+})
diff --git a/tests/lib/rules/no-dupe-keys.js b/tests/lib/rules/no-dupe-keys.js
index f1a35568..0844173f 100644
--- a/tests/lib/rules/no-dupe-keys.js
+++ b/tests/lib/rules/no-dupe-keys.js
@@ -44,6 +44,37 @@ ruleTester.run('no-dupe-keys', rule, {
       `,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
     },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          data () {
+            return {
+              dat: null
+            }
+          },
+          data () {
+            return
+          },
+          methods: {
+            _foo () {},
+            test () {
+            }
+          },
+          setup () {
+            const _foo = () => {}
+            const dat = ref(null)
+            const bar = computed(() => 'bar')
+
+            return {
+              bar
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' }
+    },
 
     {
       filename: 'test.vue',
@@ -134,6 +165,51 @@ ruleTester.run('no-dupe-keys', rule, {
       parserOptions: { ecmaVersion: 2018, sourceType: 'module' }
     },
 
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          ...foo(),
+          props: {
+            ...foo(),
+            foo: String
+          },
+          computed: {
+            ...mapGetters({
+              test: 'getTest'
+            }),
+            bar: {
+              get () {
+              }
+            }
+          },
+          data: {
+            ...foo(),
+            dat: null
+          },
+          methods: {
+            ...foo(),
+            test () {
+            }
+          },
+          data () {
+            return {
+              ...dat
+            }
+          },
+          setup () {
+            const com = computed(() => 1)
+
+            return {
+              ...foo(),
+              com
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 2018, sourceType: 'module' }
+    },
+
     {
       filename: 'test.vue',
       code: `
@@ -224,6 +300,39 @@ ruleTester.run('no-dupe-keys', rule, {
         }
       `,
       parserOptions: { ecmaVersion: 6, sourceType: 'module' }
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        // @vue/component
+        export const compA = {
+          props: {
+            propA: String
+          },
+          setup (props) {
+            const com = computed(() => props.propA)
+
+            return {
+              com
+            }
+          }
+        }
+
+        // @vue/component
+        export const compB = {
+          props: {
+            propA: String
+          },
+          setup (props) {
+            const com = computed(() => props.propA)
+
+            return {
+              com
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' }
     }
   ],
 
@@ -245,6 +354,13 @@ ruleTester.run('no-dupe-keys', rule, {
           methods: {
             foo () {
             }
+          },
+          setup () {
+            const foo = ref(1)
+
+            return {
+              foo
+            }
           }
         }
       `,
@@ -258,6 +374,9 @@ ruleTester.run('no-dupe-keys', rule, {
       }, {
         message: 'Duplicated key \'foo\'.',
         line: 14
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 21
       }]
     },
     {
@@ -277,6 +396,13 @@ ruleTester.run('no-dupe-keys', rule, {
           methods: {
             foo () {
             }
+          },
+          setup: () => {
+            const foo = computed(() => 0)
+
+            return {
+              foo
+            }
           }
         }
       `,
@@ -290,6 +416,9 @@ ruleTester.run('no-dupe-keys', rule, {
       }, {
         message: 'Duplicated key \'foo\'.',
         line: 14
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 21
       }]
     },
     {
@@ -341,6 +470,13 @@ ruleTester.run('no-dupe-keys', rule, {
           methods: {
             foo () {
             }
+          },
+          setup (props) {
+            const foo = computed(() => props.foo)
+
+            return {
+              foo
+            }
           }
         }
       `,
@@ -354,6 +490,9 @@ ruleTester.run('no-dupe-keys', rule, {
       }, {
         message: 'Duplicated key \'foo\'.',
         line: 16
+      }, {
+        message: 'Duplicated key \'foo\'.',
+        line: 23
       }]
     },
     {
@@ -374,6 +513,132 @@ ruleTester.run('no-dupe-keys', rule, {
         message: 'Duplicated key \'bar\'.',
         line: 7
       }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          methods: {
+            foo () {
+              return 0
+            }
+          },
+          setup () {
+            const foo = () => 0
+
+            return {
+              foo
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 12
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          methods: {
+            foo () {
+              return 0
+            }
+          },
+          setup () {
+            return {
+              foo: () => 0
+            }
+          }
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 10
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          methods: {
+            foo () {
+              return 0
+            }
+          },
+          setup: () => ({
+            foo: () => 0
+          })
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 9
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo () {
+              return 0
+            }
+          },
+          setup: () => ({
+            foo: computed(() => 0)
+          })
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 9
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data() {
+            return {
+              foo: 0
+            }
+          },
+          setup: () => ({
+            foo: ref(0)
+          })
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 9
+      }]
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          data() {
+            return {
+              foo: 0
+            }
+          },
+          setup: () => ({
+            foo: 0
+          })
+        }
+      `,
+      parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+      errors: [{
+        message: 'Duplicated key \'foo\'.',
+        line: 9
+      }]
     }
   ]
 })
diff --git a/tests/lib/rules/no-reserved-keys.js b/tests/lib/rules/no-reserved-keys.js
index 05836bc4..f3bc64ac 100644
--- a/tests/lib/rules/no-reserved-keys.js
+++ b/tests/lib/rules/no-reserved-keys.js
@@ -89,6 +89,32 @@ ruleTester.run('no-reserved-keys', rule, {
         }
       `,
       parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          props: ['foo'],
+          computed: {
+            bar () {
+            }
+          },
+          data: () => ({
+            dat: null
+          }),
+          methods: {
+            _foo () {},
+            test () {
+            }
+          },
+          setup () {
+            return {
+              _bar: () => {}
+            }
+          }
+        }
+      `,
+      parserOptions
     }
   ],
 
@@ -108,6 +134,23 @@ ruleTester.run('no-reserved-keys', rule, {
         line: 4
       }]
     },
+    {
+      filename: 'test.js',
+      code: `
+        new Vue({
+          setup () {
+            return {
+              $el: ''
+            }
+          }
+        })
+      `,
+      parserOptions: { ecmaVersion: 6 },
+      errors: [{
+        message: "Key '$el' is reserved.",
+        line: 5
+      }]
+    },
     {
       filename: 'test.js',
       code: `