Repository: vuejs/eslint-plugin-vue (js)

Issue:
vue/attribute-hyphenation changes original code which would break app
<!--
  Before posting the issue, please confirm that the problem you're getting
  is not related with your code editor configuration.
  To make sure it's not, run: yarn eslint src/your-file.vue
-->

**Tell us about your environment**	
* **ESLint version:** 5.16.0
* **eslint-plugin-vue version:** 5.2.3
* **Node version:**	10.14.2

**Please show your full configuration:**
<!-- Paste content of your .eslintrc file -->
```js
// https://eslint.org/docs/user-guide/configuring

module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true
  },
  extends: ['plugin:vue/essential'],
  // required to lint *.vue files
  plugins: ['vue'],
  // add your custom rules here
  rules: {
    // 'vue/prop-name-casing': ['error', 'camelCase']
    'vue/attribute-hyphenation': 'error'
  }
}
```

**What did you do?**	
<!-- Please include the actual source code causing the issue. -->
```vue
<!-- HelloWorld/index.vue-->
<template>
  <div class="HelloWorld">
    <TestPara
      :AttrEe="4"
      :attr_ff="5"
      :attr_Gg="6"
      :Attr_Hh="7"
      :_attr_ii="8"
      :_attr_Jj="9"
      :_attrKk="10"
      :_AttrLl="11"
    />
  </div>
</template>

<script>
import TestPara from './TestPara'
export default {
  name: 'HelloWorld',
  components: {
    TestPara
  }
}
</script>
```
```vue
<template>
  <div class="TestPara"></div>
</template>

<script>
<!-- HelloWorld/TestPara.vue-->
export default {
  name: 'TestPara',
  props: [
    'AttrEe',
    'attr_ff',
    'attr_Gg',
    'Attr_Hh',
    '_attr_ii',
    '_attr_Jj',
    '_attrKk',
    '_AttrLl'
  ],
  created () {
    window.vm = this
    console.log(vm.$props)
  }
}
</script>
```
run 

```bash
cd .\vue\vue-prop-parse\
npx eslint .\src\components\HelloWorld\index.vue --fix
```
**What did you expect to happen?**

I can still access all the `$props` in the `TestPara` with the **valid value** without changing the code in the `TestPara`.

**What actually happened?**
<!-- Please include the actual, raw output from ESLint. -->
```vue
<!-- HelloWorld/index.vue-->
<template>
  <div class="HelloWorld">
    <TestPara
      :attr-ee="4"
      :attr_ff="5"
      :attr-gg="6"
      :attr-hh="7"
      :_attr_ii="8"
      :-attr-jj="9"
      :-attr-kk="10"
      :-attr-ll="11"
    />
  </div>
</template>

<script>
import TestPara from './TestPara'
export default {
  name: 'HelloWorld',
  components: {
    TestPara
  }
}
</script>

```
After lint, I got the error at the console with 

> '-attr-jj' is not a valid attribute name.

And `Attr_Hh`, `attr_Gg`, `_AttrLl`, `_attrKk`, `_attr_Jj` in $props are `undefined`. I check the [test cases](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/attribute-hyphenation.js) and find you didn't consider these situations like `_` in the attributes name.

In this case, I thought you shouldn't try to fix the original code? Because that would break the app down. 

What do you think?

Pull request:
Disable autofix in `vue/attribute-hyphenation` for attributes with underscore
fixes #924 

I have modified it in reference to https://github.com/vuejs/eslint-plugin-vue/issues/924#issuecomment-1263187134

Please let me know if you need anything else. Thanks!

Patch:
diff --git a/docs/.vitepress/build-system/build.ts b/docs/.vitepress/build-system/build.ts
index 698b0809..89578cab 100644
--- a/docs/.vitepress/build-system/build.ts
+++ b/docs/.vitepress/build-system/build.ts
@@ -16,7 +16,7 @@ const dirname = path.dirname(
 build(
   path.join(dirname, './src/eslint.mjs'),
   path.join(dirname, './shim/eslint.mjs'),
-  ['path', 'assert', 'util']
+  ['path', 'assert', 'util', 'esquery']
 )
 build(
   path.join(dirname, '../../../node_modules/assert'),
diff --git a/docs/.vitepress/build-system/shim/esquery.mjs b/docs/.vitepress/build-system/shim/esquery.mjs
new file mode 100644
index 00000000..5652b09f
--- /dev/null
+++ b/docs/.vitepress/build-system/shim/esquery.mjs
@@ -0,0 +1,5 @@
+import esquery from '../../../../node_modules/esquery/dist/esquery.esm.js'
+
+export const { parse, match, traverse, matches, query } = esquery
+
+export { default } from '../../../../node_modules/esquery/dist/esquery.esm.js'
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 9990ed7b..2945e424 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -154,6 +154,7 @@ export default defineConfig({
         path: path.join(dirname, './build-system/shim/path.mjs'),
 
         tslib: path.join(dirname, '../../node_modules/tslib/tslib.es6.js'),
+        esquery: path.join(dirname, './build-system/shim/esquery.mjs'),
         globby: path.join(dirname, './build-system/shim/globby.mjs')
       }
     },
diff --git a/docs/.vitepress/public/service-worker.js b/docs/.vitepress/public/service-worker.js
new file mode 100644
index 00000000..e80d3f2d
--- /dev/null
+++ b/docs/.vitepress/public/service-worker.js
@@ -0,0 +1,14 @@
+// https://github.com/NekR/self-destroying-sw
+/* globals self */
+self.addEventListener('install', (e) => {
+  self.skipWaiting()
+})
+
+self.addEventListener('activate', (e) => {
+  self.registration
+    .unregister()
+    .then(() => self.clients.matchAll())
+    .then((clients) => {
+      for (const client of clients) client.navigate(client.url)
+    })
+})
diff --git a/lib/rules/attribute-hyphenation.js b/lib/rules/attribute-hyphenation.js
index d2ae0cca..24bacffc 100644
--- a/lib/rules/attribute-hyphenation.js
+++ b/lib/rules/attribute-hyphenation.js
@@ -94,8 +94,16 @@ module.exports = {
         data: {
           text
         },
-        fix: (fixer) =>
-          fixer.replaceText(node.key, text.replace(name, caseConverter(name)))
+        fix: (fixer) => {
+          if (text.includes('_')) {
+            return null
+          }
+
+          return fixer.replaceText(
+            node.key,
+            text.replace(name, caseConverter(name))
+          )
+        }
       })
     }
 
diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js
index 65a6f312..df06a0b1 100644
--- a/lib/rules/component-name-in-template-casing.js
+++ b/lib/rules/component-name-in-template-casing.js
@@ -90,23 +90,21 @@ module.exports = {
         return false
       }
 
+      if (
+        (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
+        utils.isHtmlWellKnownElementName(node.rawName) ||
+        utils.isSvgWellKnownElementName(node.rawName)
+      ) {
+        return false
+      }
+
       if (!registeredComponentsOnly) {
         // If the user specifies registeredComponentsOnly as false, it checks all component tags.
-        if (
-          (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
-          utils.isHtmlWellKnownElementName(node.rawName) ||
-          utils.isSvgWellKnownElementName(node.rawName)
-        ) {
-          return false
-        }
-        return true
-      }
-      // We only verify the registered components.
-      if (registeredComponents.has(casing.pascalCase(node.rawName))) {
         return true
       }
 
-      return false
+      // We only verify the registered components.
+      return registeredComponents.has(casing.pascalCase(node.rawName))
     }
 
     let hasInvalidEOF = false


Test patch:
diff --git a/tests/lib/rules/attribute-hyphenation.js b/tests/lib/rules/attribute-hyphenation.js
index 002254dd..146c2565 100644
--- a/tests/lib/rules/attribute-hyphenation.js
+++ b/tests/lib/rules/attribute-hyphenation.js
@@ -58,6 +58,16 @@ ruleTester.run('attribute-hyphenation', rule, {
       filename: 'test.vue',
       code: '<template><div><slot myProp></slot></div></template>',
       options: ['never']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :attr_ff="prop"></custom></div></template>',
+      options: ['always']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :attr_ff="prop"></custom></div></template>',
+      options: ['never']
     }
   ],
 
@@ -280,6 +290,84 @@ ruleTester.run('attribute-hyphenation', rule, {
           line: 1
         }
       ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :attr_Gg="prop"></custom></div></template>',
+      output: null,
+      options: ['always'],
+      errors: [
+        {
+          message: "Attribute ':attr_Gg' must be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :Attr_Hh="prop"></custom></div></template>',
+      output: null,
+      options: ['always'],
+      errors: [
+        {
+          message: "Attribute ':Attr_Hh' must be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :_attr_Jj="prop"></custom></div></template>',
+      output: null,
+      options: ['always'],
+      errors: [
+        {
+          message: "Attribute ':_attr_Jj' must be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :_attrKk="prop"></custom></div></template>',
+      output: null,
+      options: ['always'],
+      errors: [
+        {
+          message: "Attribute ':_attrKk' must be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :_AttrLl="prop"></custom></div></template>',
+      output: null,
+      options: ['always'],
+      errors: [
+        {
+          message: "Attribute ':_AttrLl' must be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><custom :my-custom_prop="prop"></custom></div></template>',
+      output: null,
+      options: ['never'],
+      errors: [
+        {
+          message: "Attribute ':my-custom_prop' can't be hyphenated.",
+          type: 'VDirectiveKey',
+          line: 1
+        }
+      ]
     }
   ]
 })
diff --git a/tests/lib/rules/component-name-in-template-casing.js b/tests/lib/rules/component-name-in-template-casing.js
index 758254b9..d57a5ffb 100644
--- a/tests/lib/rules/component-name-in-template-casing.js
+++ b/tests/lib/rules/component-name-in-template-casing.js
@@ -162,6 +162,15 @@ tester.run('component-name-in-template-casing', rule, {
       `
     },
 
+    {
+      code: `
+        <template><div/></template>
+        <script setup>const Div = 0</script>
+      `,
+      options: ['PascalCase'],
+      filename: 'test.vue'
+    },
+
     // globals
     {
       code: `