Repository: vuejs/eslint-plugin-vue
Pull request #940 (2019-08-10 02:54:25)

Drop autofix of prop-name-casing.
This PR removes the `prop-name-casing` autofix.

The autofix of `prop-name-casing` breaks the program as pointed out in the following comment.

https://github.com/vuejs/eslint-plugin-vue/pull/402#issuecomment-445428304


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2da27550..c60601bc 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -58,17 +58,17 @@ jobs:
       - run:
           name: Install dependencies
           command: npm install
+      - save_cache:
+          key: v2-npm-lock-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package.json" }}
+          paths:
+            - node_modules
       - run:
           name: Install eslint 5
           command: |
             # We need to execute this command twice because of npm's bug.
             # See also: https://npm.community/t/error-node-modules-staging-eslint-e7cf6846-node-modules-eslint
-            npm install eslint@5.0.0 --no-save
-            npm install eslint@5.0.0 --no-save
+            npm install eslint@5.0.0 acorn@6.0.7 --no-save
+            npm install eslint@5.0.0 acorn@6.0.7 --no-save
       - run:
           name: Test
           command: npm test
-      - save_cache:
-          key: v2-npm-lock-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package.json" }}
-          paths:
-            - node_modules
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 97117319..a4760038 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -98,7 +98,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
 | [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 |  |
-| [vue/prop-name-casing](./prop-name-casing.md) | enforce specific casing for the Prop name in Vue components | :wrench: |
+| [vue/prop-name-casing](./prop-name-casing.md) | enforce specific casing for the Prop name in Vue components |  |
 | [vue/require-default-prop](./require-default-prop.md) | require default value for props |  |
 | [vue/require-prop-types](./require-prop-types.md) | require type definitions in props |  |
 | [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: |
diff --git a/docs/rules/attributes-order.md b/docs/rules/attributes-order.md
index 2f41dfa6..8c61d368 100644
--- a/docs/rules/attributes-order.md
+++ b/docs/rules/attributes-order.md
@@ -15,27 +15,27 @@ description: enforce order of attributes
 This rule aims to enforce ordering of component attributes. The default order is specified in the [Vue styleguide](https://vuejs.org/v2/style-guide/#Element-attribute-order-recommended) and is:
 
 - `DEFINITION`
-  ex: 'is'
+  e.g. 'is'
 - `LIST_RENDERING`
-  ex: 'v-for item in items'
+  e.g. 'v-for item in items'
 - `CONDITIONALS`
-  ex: 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
+  e.g. 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
 - `RENDER_MODIFIERS`
-  ex: 'v-once', 'v-pre'
+  e.g. 'v-once', 'v-pre'
 - `GLOBAL`
-  ex: 'id'
+  e.g. 'id'
 - `UNIQUE`
-  ex: 'ref', 'key', 'v-slot', 'slot'
+  e.g. 'ref', 'key', 'v-slot', 'slot'
 - `TWO_WAY_BINDING`
-  ex: 'v-model'
+  e.g. 'v-model'
 - `OTHER_DIRECTIVES`
-  ex: 'v-custom-directive'
+  e.g. 'v-custom-directive'
 - `OTHER_ATTR`
-  ex: 'custom-prop="foo"', 'v-bind:prop="foo"', ':prop="foo"'
+  e.g. 'custom-prop="foo"', 'v-bind:prop="foo"', ':prop="foo"'
 - `EVENTS`
-  ex: '@click="functionCall"', 'v-on="event"'
+  e.g. '@click="functionCall"', 'v-on="event"'
 - `CONTENT`
-  ex: 'v-text', 'v-html'
+  e.g. 'v-text', 'v-html'
 
 ### the default order
 
@@ -96,14 +96,14 @@ This rule aims to enforce ordering of component attributes. The default order is
     "order": [
       "DEFINITION",
       "LIST_RENDERING",
-      "CONDITIONALS", 
+      "CONDITIONALS",
       "RENDER_MODIFIERS",
-      "GLOBAL", 
-      "UNIQUE", 
-      "TWO_WAY_BINDING", 
-      "OTHER_DIRECTIVES", 
-      "OTHER_ATTR", 
-      "EVENTS", 
+      "GLOBAL",
+      "UNIQUE",
+      "TWO_WAY_BINDING",
+      "OTHER_DIRECTIVES",
+      "OTHER_ATTR",
+      "EVENTS",
       "CONTENT"
     ]
   }]
diff --git a/docs/rules/no-dupe-keys.md b/docs/rules/no-dupe-keys.md
index 2cbed8eb..f139792d 100644
--- a/docs/rules/no-dupe-keys.md
+++ b/docs/rules/no-dupe-keys.md
@@ -51,7 +51,7 @@ export default {
 }
 ```
 
-- `"groups"` (`string[]`) Array of additional groups to search for duplicates. Defailt is empty.
+- `"groups"` (`string[]`) Array of additional groups to search for duplicates. Default is empty.
 
 ### `"groups": ["firebase"]`
 
diff --git a/docs/rules/order-in-components.md b/docs/rules/order-in-components.md
index c28e283b..d215cf1e 100644
--- a/docs/rules/order-in-components.md
+++ b/docs/rules/order-in-components.md
@@ -74,11 +74,14 @@ export default {
       "inheritAttrs",
       "model",
       ["props", "propsData"],
+      "fetch",
+      "asyncData",
       "data",
       "computed",
       "watch",
       "LIFECYCLE_HOOKS",
       "methods",
+      "head",
       ["template", "render"],
       "renderError"
     ]
diff --git a/docs/rules/prop-name-casing.md b/docs/rules/prop-name-casing.md
index 846766c6..def33cd5 100644
--- a/docs/rules/prop-name-casing.md
+++ b/docs/rules/prop-name-casing.md
@@ -8,13 +8,12 @@ description: enforce specific casing for the Prop name in Vue components
 > enforce specific casing for the Prop name in Vue components
 
 - :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
 
 This rule enforce proper casing of props in vue components(camelCase).
 
-<eslint-code-block fix :rules="{'vue/prop-name-casing': ['error']}">
+<eslint-code-block :rules="{'vue/prop-name-casing': ['error']}">
 
 ```vue
 <script>
@@ -46,7 +45,7 @@ export default {
 
 ### `"snake_case"`
 
-<eslint-code-block fix :rules="{'vue/prop-name-casing': ['error', 'snake_case']}">
+<eslint-code-block :rules="{'vue/prop-name-casing': ['error', 'snake_case']}">
 
 ```vue
 <script>
diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md
index 2c7bcfdb..abf2390c 100644
--- a/docs/user-guide/README.md
+++ b/docs/user-guide/README.md
@@ -71,6 +71,7 @@ If you want to use custom parsers such as [babel-eslint](https://www.npmjs.com/p
 
 ```diff
 - "parser": "babel-eslint",
++ "parser": "vue-eslint-parser",
   "parserOptions": {
 +     "parser": "babel-eslint",
       "sourceType": "module"
diff --git a/lib/index.js b/lib/index.js
index 6c727f24..d2d3d0e1 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -75,6 +75,7 @@ module.exports = {
     'v-bind-style': require('./rules/v-bind-style'),
     'v-on-function-call': require('./rules/v-on-function-call'),
     'v-on-style': require('./rules/v-on-style'),
+    'v-slot-style': require('./rules/v-slot-style'),
     'valid-template-root': require('./rules/valid-template-root'),
     'valid-v-bind': require('./rules/valid-v-bind'),
     'valid-v-cloak': require('./rules/valid-v-cloak'),
@@ -88,6 +89,7 @@ module.exports = {
     'valid-v-once': require('./rules/valid-v-once'),
     'valid-v-pre': require('./rules/valid-v-pre'),
     'valid-v-show': require('./rules/valid-v-show'),
+    'valid-v-slot': require('./rules/valid-v-slot'),
     'valid-v-text': require('./rules/valid-v-text')
   },
   configs: {
diff --git a/lib/rules/order-in-components.js b/lib/rules/order-in-components.js
index 5702c22e..5f2c4ecd 100644
--- a/lib/rules/order-in-components.js
+++ b/lib/rules/order-in-components.js
@@ -19,13 +19,14 @@ const defaultOrder = [
   'inheritAttrs',
   'model',
   ['props', 'propsData'],
+  'fetch',
+  'asyncData',
   'data',
   'computed',
   'watch',
-  'asyncData',
-  'fetch',
   'LIFECYCLE_HOOKS',
   'methods',
+  'head',
   ['template', 'render'],
   'renderError'
 ]
@@ -97,11 +98,14 @@ function isNotSideEffectsNode (node, visitorKeys) {
   traverseNodes(node, {
     visitorKeys,
     enterNode (node, parent) {
-      if (!result || noSideEffectsNodes.has(node)) {
+      if (!result) {
         return
       }
 
       if (
+        // parent has no side effects
+        noSideEffectsNodes.has(parent) ||
+        // no side effects node
         node.type === 'FunctionExpression' ||
         node.type === 'Identifier' ||
         node.type === 'Literal' ||
@@ -109,15 +113,7 @@ function isNotSideEffectsNode (node, visitorKeys) {
         node.type === 'ArrowFunctionExpression' ||
         node.type === 'TemplateElement'
       ) {
-        // no side effects node
         noSideEffectsNodes.add(node)
-        traverseNodes(node, {
-          visitorKeys,
-          enterNode (node) {
-            noSideEffectsNodes.add(node)
-          },
-          leaveNode () {}
-        })
       } else if (
         node.type !== 'Property' &&
         node.type !== 'ObjectExpression' &&
@@ -137,6 +133,7 @@ function isNotSideEffectsNode (node, visitorKeys) {
     },
     leaveNode () {}
   })
+
   return result
 }
 
diff --git a/lib/rules/prop-name-casing.js b/lib/rules/prop-name-casing.js
index b38cae62..b09ed0b0 100644
--- a/lib/rules/prop-name-casing.js
+++ b/lib/rules/prop-name-casing.js
@@ -8,20 +8,6 @@ const utils = require('../utils')
 const casing = require('../utils/casing')
 const allowedCaseOptions = ['camelCase', 'snake_case']
 
-function canFixPropertyName (node, key, originalName) {
-  // Can not fix of computed property names & shorthand
-  if (node.computed || node.shorthand) {
-    return false
-  }
-
-  // Can not fix of unknown types
-  if (key.type !== 'Literal' && key.type !== 'Identifier') {
-    return false
-  }
-  // Can fix of ASCII printable characters
-  return originalName.match(/[ -~]+/)
-}
-
 // ------------------------------------------------------------------------------
 // Rule Definition
 // ------------------------------------------------------------------------------
@@ -53,12 +39,7 @@ function create (context) {
           data: {
             name: propName,
             caseType: caseType
-          },
-          fix: canFixPropertyName(item.node, item.key, propName) ? fixer => {
-            return item.key.type === 'Literal'
-              ? fixer.replaceText(item.key, item.key.raw.replace(item.key.value, convertedName))
-              : fixer.replaceText(item.key, convertedName)
-          } : undefined
+          }
         })
       }
     }
@@ -77,7 +58,7 @@ module.exports = {
       category: 'strongly-recommended',
       url: 'https://eslint.vuejs.org/rules/prop-name-casing.html'
     },
-    fixable: 'code',  // null or "code" or "whitespace"
+    fixable: null,  // null or "code" or "whitespace"
     schema: [
       {
         enum: allowedCaseOptions
diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js
index bf194fd8..29862b84 100644
--- a/lib/rules/use-v-on-exact.js
+++ b/lib/rules/use-v-on-exact.js
@@ -11,6 +11,7 @@
 const utils = require('../utils')
 
 const SYSTEM_MODIFIERS = new Set(['ctrl', 'shift', 'alt', 'meta'])
+const GLOBAL_MODIFIERS = new Set(['stop', 'prevent', 'capture', 'self', 'once', 'passive', 'native'])
 
 // ------------------------------------------------------------------------------
 // Helpers
@@ -36,6 +37,16 @@ function getEventDirectives (attributes, sourceCode) {
     }))
 }
 
+/**
+ * Checks whether given modifier is key modifier
+ *
+ * @param {string} modifier
+ * @returns {boolean}
+ */
+function isKeyModifier (modifier) {
+  return !GLOBAL_MODIFIERS.has(modifier) && !SYSTEM_MODIFIERS.has(modifier)
+}
+
 /**
  * Checks whether given modifier is system one
  *
@@ -86,6 +97,16 @@ function getSystemModifiersString (modifiers) {
   return modifiers.filter(isSystemModifier).sort().join(',')
 }
 
+/**
+ * Creates alphabetically sorted string with key modifiers
+ *
+ * @param {array[string]} modifiers
+ * @returns {string} e.g. "enter,tab"
+ */
+function getKeyModifiersString (modifiers) {
+  return modifiers.filter(isKeyModifier).sort().join(',')
+}
+
 /**
  * Compares two events based on their modifiers
  * to detect possible event leakeage
@@ -100,13 +121,21 @@ function hasConflictedModifiers (baseEvent, event) {
     event.modifiers.includes('exact')
   ) return false
 
-  const eventModifiers = getSystemModifiersString(event.modifiers)
-  const baseEventModifiers = getSystemModifiersString(baseEvent.modifiers)
+  const eventKeyModifiers = getKeyModifiersString(event.modifiers)
+  const baseEventKeyModifiers = getKeyModifiersString(baseEvent.modifiers)
+
+  if (
+    eventKeyModifiers && baseEventKeyModifiers &&
+    eventKeyModifiers !== baseEventKeyModifiers
+  ) return false
+
+  const eventSystemModifiers = getSystemModifiersString(event.modifiers)
+  const baseEventSystemModifiers = getSystemModifiersString(baseEvent.modifiers)
 
   return (
     baseEvent.modifiers.length >= 1 &&
-    baseEventModifiers !== eventModifiers &&
-    baseEventModifiers.indexOf(eventModifiers) > -1
+    baseEventSystemModifiers !== eventSystemModifiers &&
+    baseEventSystemModifiers.indexOf(eventSystemModifiers) > -1
   )
 }
 
diff --git a/lib/rules/valid-v-else.js b/lib/rules/valid-v-else.js
index e5412237..b0a09cb2 100644
--- a/lib/rules/valid-v-else.js
+++ b/lib/rules/valid-v-else.js
@@ -36,7 +36,7 @@ module.exports = {
           context.report({
             node,
             loc: node.loc,
-            message: "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."
+            message: "'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."
           })
         }
         if (utils.hasDirective(element, 'if')) {
diff --git a/package.json b/package.json
index e55bf568..8246a88b 100755
--- a/package.json
+++ b/package.json
@@ -46,11 +46,12 @@
     "eslint": "^5.0.0 || ^6.0.0"
   },
   "dependencies": {
-    "vue-eslint-parser": "^6.0.2"
+    "vue-eslint-parser": "^6.0.4"
   },
   "devDependencies": {
     "@types/node": "^4.2.16",
     "@typescript-eslint/parser": "^1.11.0",
+    "acorn": "^7.0.0",
     "babel-eslint": "^10.0.2",
     "chai": "^4.1.0",
     "eslint": "^6.0.0",


Test patch:
diff --git a/tests/lib/rules/prop-name-casing.js b/tests/lib/rules/prop-name-casing.js
index 842e07ab..d80f7ab0 100644
--- a/tests/lib/rules/prop-name-casing.js
+++ b/tests/lib/rules/prop-name-casing.js
@@ -300,13 +300,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: `
-        export default {
-          props: {
-            greetingText: String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting_text" is not in camelCase.',
@@ -324,13 +317,6 @@ ruleTester.run('prop-name-casing', rule, {
         }
       `,
       options: ['camelCase'],
-      output: `
-        export default {
-          props: {
-            greetingText: String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting_text" is not in camelCase.',
@@ -346,11 +332,6 @@ ruleTester.run('prop-name-casing', rule, {
         }
       `,
       options: ['camelCase'],
-      output: `
-        export default {
-          props: ['greetingText']
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting_text" is not in camelCase.',
@@ -368,13 +349,6 @@ ruleTester.run('prop-name-casing', rule, {
         }
       `,
       options: ['snake_case'],
-      output: `
-        export default {
-          props: {
-            greeting_text: String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greetingText" is not in snake_case.',
@@ -392,13 +366,6 @@ ruleTester.run('prop-name-casing', rule, {
         }
       `,
       options: ['camelCase'],
-      output: `
-        export default {
-          props: {
-            'greetingText': String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting-text" is not in camelCase.',
@@ -416,13 +383,6 @@ ruleTester.run('prop-name-casing', rule, {
         }
       `,
       options: ['snake_case'],
-      output: `
-        export default {
-          props: {
-            'greeting_text': String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting-text" is not in snake_case.',
@@ -439,13 +399,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: `
-        export default {
-          props: {
-            'greetingText': String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "greeting_text" is not in camelCase.',
@@ -463,7 +416,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: null,
       parserOptions,
       errors: [{
         message: 'Prop "greeting-text" is not in camelCase.',
@@ -481,7 +433,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: null,
       parserOptions,
       errors: [{
         message: 'Prop "greeting_text" is not in camelCase.',
@@ -499,7 +450,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: null,
       parserOptions,
       errors: [{
         message: 'Prop "\u{1F37B}" is not in camelCase.',
@@ -517,7 +467,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: null,
       parserOptions,
       errors: [{
         message: 'Prop "漢字" is not in camelCase.',
@@ -534,13 +483,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: `
-        export default {
-          props: {
-            'abc123Def': String
-          }
-        }
-      `,
       parserOptions,
       errors: [{
         message: 'Prop "abc-123-def" is not in camelCase.',
@@ -558,7 +500,6 @@ ruleTester.run('prop-name-casing', rule, {
           }
         }
       `,
-      output: null,
       parserOptions,
       errors: [{
         message: 'Prop "greeting-text" is not in camelCase.',
diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js
index 6d994826..726662ca 100644
--- a/tests/lib/rules/use-v-on-exact.js
+++ b/tests/lib/rules/use-v-on-exact.js
@@ -158,6 +158,27 @@ ruleTester.run('use-v-on-exact', rule, {
     },
     {
       code: `<template><button @[foo]="foo" @[bar].ctrl="bar"/></template>`
+    },
+    {
+      code: `<template>
+        <input
+          @keydown.enter="foo"
+          @keydown.shift.tab="bar"/>
+      </template>`
+    },
+    {
+      code: `<template>
+        <input
+          @keydown.enter="foo"
+          @keydown.shift.tab.prevent="bar"/>
+      </template>`
+    },
+    {
+      code: `<template>
+        <input-component
+          @keydown.enter.native="foo"
+          @keydown.shift.tab.native="bar"/>
+      </template>`
     }
   ],
 
diff --git a/tests/lib/rules/valid-v-else.js b/tests/lib/rules/valid-v-else.js
index 7d112a8c..7825e4df 100644
--- a/tests/lib/rules/valid-v-else.js
+++ b/tests/lib/rules/valid-v-else.js
@@ -44,32 +44,32 @@ tester.run('valid-v-else', rule, {
     {
       filename: 'test.vue',
       code: '<template><template v-else><div></div></template></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',
       code: '<template><div v-else></div></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',
       code: '<template><div><div v-else></div></div></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',
       code: '<template><div><div></div><div v-else></div></div></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',
       code: '<template><div><div if="foo"></div><div v-else></div></div></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',
       code: '<template><div><div v-if="foo"></div><div></div><div v-else></div></div></template>',
-      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else' directive."]
+      errors: ["'v-else' directives require being preceded by the element which has a 'v-if' or 'v-else-if' directive."]
     },
     {
       filename: 'test.vue',