Repository: vuejs/eslint-plugin-vue
Pull request #650 (2018-11-12 14:29:54)

Update: use multiple-autofixes.
This PR changes to use multiple-autofixes.

https://eslint.org/blog/2017/06/eslint-v4.1.0-released#applying-multiple-autofixes-simultaneously


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index 4b8396be..4b2731d6 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -19,7 +19,7 @@ module.exports = {
     'eslint-plugin'
   ],
   rules: {
-    'eslint-plugin/report-message-format': ['error', '^[A-Z`\'].*\\.$'],
+    'eslint-plugin/report-message-format': ['error', '^[A-Z`\'{].*\\.$'],
     'eslint-plugin/prefer-placeholders': 'error',
     'eslint-plugin/consistent-output': 'error'
   },
diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js
index 37b8b39f..c1a4647c 100644
--- a/lib/rules/attributes-order.js
+++ b/lib/rules/attributes-order.js
@@ -77,25 +77,10 @@ function create (context) {
         const attributes = node.parent.attributes
         const shiftAttrs = attributes.slice(attributes.indexOf(previousNode), attributes.indexOf(node) + 1)
 
-        // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
-        // return shiftAttrs.map((attr, i) => {
-        //   const text = attr === previousNode ? sourceCode.getText(node) : sourceCode.getText(shiftAttrs[i - 1])
-        //   return fixer.replaceText(attr, text)
-        // })
-        const replaceDataList = shiftAttrs.map((attr, i) => {
+        return shiftAttrs.map((attr, i) => {
           const text = attr === previousNode ? sourceCode.getText(node) : sourceCode.getText(shiftAttrs[i - 1])
-          return {
-            range: attr.range,
-            text
-          }
+          return fixer.replaceText(attr, text)
         })
-        const replaceRange = [previousNode.range[0], node.range[1]]
-        let text = sourceCode.text.slice(replaceRange[0], replaceRange[1])
-        replaceDataList.reverse().forEach((data) => {
-          const textRange = data.range.map(r => r - replaceRange[0])
-          text = text.slice(0, textRange[0]) + data.text + text.slice(textRange[1], text.length)
-        })
-        return fixer.replaceTextRange(replaceRange, text)
       }
     })
   }
diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js
index 524017a6..a4b9832b 100644
--- a/lib/rules/component-name-in-template-casing.js
+++ b/lib/rules/component-name-in-template-casing.js
@@ -51,7 +51,6 @@ module.exports = {
     const caseType = allowedCaseOptions.indexOf(caseOption) !== -1 ? caseOption : defaultCase
     const ignores = options.ignores || []
     const tokens = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
-    const sourceCode = context.getSourceCode()
 
     let hasInvalidEOF = false
 
@@ -61,7 +60,11 @@ module.exports = {
           return
         }
 
-        if (!utils.isHtmlElementNode(node) || utils.isHtmlWellKnownElementName(node.rawName)) {
+        if (
+          (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
+          utils.isHtmlWellKnownElementName(node.rawName) ||
+          utils.isSvgWellKnownElementName(node.rawName)
+        ) {
           return
         }
 
@@ -88,13 +91,10 @@ module.exports = {
                 return fixer.replaceText(open, `<${casingName}`)
               }
               const endTagOpen = tokens.getFirstToken(endTag)
-              // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
-              // return [
-              //   fixer.replaceText(open, `<${casingName}`),
-              //   fixer.replaceText(endTagOpen, `</${casingName}`)
-              // ]
-              const code = `<${casingName}${sourceCode.text.slice(open.range[1], endTagOpen.range[0])}</${casingName}`
-              return fixer.replaceTextRange([open.range[0], endTagOpen.range[1]], code)
+              return [
+                fixer.replaceText(open, `<${casingName}`),
+                fixer.replaceText(endTagOpen, `</${casingName}`)
+              ]
             }
           })
         }
diff --git a/lib/rules/max-attributes-per-line.js b/lib/rules/max-attributes-per-line.js
index 517a4ef8..421e5539 100644
--- a/lib/rules/max-attributes-per-line.js
+++ b/lib/rules/max-attributes-per-line.js
@@ -70,6 +70,7 @@ module.exports = {
     const multilineMaximum = configuration.multiline
     const singlelinemMaximum = configuration.singleline
     const canHaveFirstLine = configuration.allowFirstLine
+    const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
 
     return utils.defineTemplateBodyVisitor(context, {
       'VStartTag' (node) {
@@ -78,17 +79,17 @@ module.exports = {
         if (!numberOfAttributes) return
 
         if (utils.isSingleLine(node) && numberOfAttributes > singlelinemMaximum) {
-          showErrors(node.attributes.slice(singlelinemMaximum), node)
+          showErrors(node.attributes.slice(singlelinemMaximum))
         }
 
         if (!utils.isSingleLine(node)) {
           if (!canHaveFirstLine && node.attributes[0].loc.start.line === node.loc.start.line) {
-            showErrors([node.attributes[0]], node)
+            showErrors([node.attributes[0]])
           }
 
           groupAttrsByLine(node.attributes)
             .filter(attrs => attrs.length > multilineMaximum)
-            .forEach(attrs => showErrors(attrs.splice(multilineMaximum), node))
+            .forEach(attrs => showErrors(attrs.splice(multilineMaximum)))
         }
       }
     })
@@ -128,16 +129,45 @@ module.exports = {
       return defaults
     }
 
-    function showErrors (attributes, node) {
+    function getPropData (prop) {
+      let propType = 'Attribute'
+      let propName = prop.key.name
+
+      if (utils.isBindingAttribute(prop)) {
+        propType = 'Binding'
+        propName = prop.key.raw.argument
+      } else if (utils.isEventAttribute(prop)) {
+        propType = 'Event'
+        propName = prop.key.raw.argument
+      } else if (prop.directive) {
+        propType = 'Directive'
+      }
+
+      return { propType, propName }
+    }
+
+    function showErrors (attributes) {
       attributes.forEach((prop, i) => {
+        const fix = (fixer) => {
+          if (i !== 0) return null
+
+          // Find the closest token before the current prop
+          // that is not a white space
+          const prevToken = template.getTokenBefore(prop, {
+            filter: (token) => token.type !== 'HTMLWhitespace'
+          })
+
+          const range = [prevToken.range[1], prop.range[0]]
+
+          return fixer.replaceTextRange(range, '\n')
+        }
+
         context.report({
           node: prop,
           loc: prop.loc,
-          message: 'Attribute "{{propName}}" should be on a new line.',
-          data: {
-            propName: prop.key.name
-          },
-          fix: i === 0 ? (fixer) => fixer.insertTextBefore(prop, '\n') : undefined
+          message: '{{propType}} "{{propName}}" should be on a new line.',
+          data: getPropData(prop),
+          fix
         })
       })
     }
diff --git a/lib/rules/no-shared-component-data.js b/lib/rules/no-shared-component-data.js
index bccd6878..9e5b1214 100644
--- a/lib/rules/no-shared-component-data.js
+++ b/lib/rules/no-shared-component-data.js
@@ -66,16 +66,10 @@ module.exports = {
             fix (fixer) {
               const tokens = getFirstAndLastTokens(p.value, sourceCode)
 
-              // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
-              //     return [
-              //       fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
-              //       fixer.insertTextAfter(tokens.last, ';\n}')
-              //     ]
-              // See: https://eslint.org/blog/2017/06/eslint-v4.1.0-released#applying-multiple-autofixes-simultaneously
-              const range = [tokens.first.range[0], tokens.last.range[1]]
-              const valueText = sourceCode.text.slice(range[0], range[1])
-              const replacement = `function() {\nreturn ${valueText};\n}`
-              return fixer.replaceTextRange(range, replacement)
+              return [
+                fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
+                fixer.insertTextAfter(tokens.last, ';\n}')
+              ]
             }
           })
         })
diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js
index bea2a2cd..167c89ab 100644
--- a/lib/rules/no-unused-components.js
+++ b/lib/rules/no-unused-components.js
@@ -37,28 +37,34 @@ module.exports = {
   create (context) {
     const options = context.options[0] || {}
     const ignoreWhenBindingPresent = options.ignoreWhenBindingPresent !== undefined ? options.ignoreWhenBindingPresent : true
-    const usedComponents = []
+    const usedComponents = new Set()
     let registeredComponents = []
     let ignoreReporting = false
     let templateLocation
 
     return utils.defineTemplateBodyVisitor(context, {
       VElement (node) {
-        if (utils.isHtmlElementNode(node) && !utils.isHtmlWellKnownElementName(node.rawName)) {
-          usedComponents.push(node.rawName)
+        if (
+          (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) ||
+          utils.isHtmlWellKnownElementName(node.rawName) ||
+          utils.isSvgWellKnownElementName(node.rawName)
+        ) {
+          return
         }
+
+        usedComponents.add(node.rawName)
       },
       "VAttribute[directive=true][key.name='bind'][key.argument='is']" (node) {
         if (node.value.type !== 'VExpressionContainer') return
 
         if (node.value.expression.type === 'Literal') {
-          usedComponents.push(node.value.expression.value)
+          usedComponents.add(node.value.expression.value)
         } else if (ignoreWhenBindingPresent) {
           ignoreReporting = true
         }
       },
       "VAttribute[directive=false][key.name='is']" (node) {
-        usedComponents.push(node.value.value)
+        usedComponents.add(node.value.value)
       },
       "VElement[name='template']" (rootNode) {
         templateLocation = templateLocation || rootNode.loc.start
@@ -77,13 +83,13 @@ module.exports = {
             // like "theComponent", "The-component" etc.
             // but except snake_case
             if (casing.pascalCase(name) === name || casing.camelCase(name) === name) {
-              return !usedComponents.some(n => {
+              return ![...usedComponents].some(n => {
                 return n.indexOf('_') === -1 && (name === casing.pascalCase(n) || casing.camelCase(n) === name)
               })
             } else {
               // In any other case the used component name must exactly match
               // the registered name
-              return usedComponents.indexOf(name) === -1
+              return !usedComponents.has(name)
             }
           })
           .forEach(({ node, name }) => context.report({
diff --git a/lib/rules/valid-v-model.js b/lib/rules/valid-v-model.js
index 757c169e..d18c6f91 100644
--- a/lib/rules/valid-v-model.js
+++ b/lib/rules/valid-v-model.js
@@ -156,7 +156,8 @@ module.exports = {
               context.report({
                 node,
                 loc: node.loc,
-                message: "'v-model' directives cannot update the iteration variable 'x' itself."
+                message: "'v-model' directives cannot update the iteration variable '{{varName}}' itself.",
+                data: { varName: id.name }
               })
             }
           }
diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js
index 13073d9a..fceec8c3 100644
--- a/lib/rules/valid-v-on.js
+++ b/lib/rules/valid-v-on.js
@@ -136,6 +136,7 @@ module.exports = {
   create (context) {
     const options = context.options[0] || {}
     const customModifiers = new Set(options.modifiers || [])
+    const sourceCode = context.getSourceCode()
 
     return utils.defineTemplateBodyVisitor(context, {
       "VAttribute[directive=true][key.name='on']" (node) {
@@ -149,12 +150,26 @@ module.exports = {
             })
           }
         }
-        if (!utils.hasAttributeValue(node) && !node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS)) {
-          context.report({
-            node,
-            loc: node.loc,
-            message: "'v-on' directives require that attribute value or verb modifiers."
-          })
+
+        if (
+          !utils.hasAttributeValue(node) &&
+          !node.key.modifiers.some(VERB_MODIFIERS.has, VERB_MODIFIERS)
+        ) {
+          if (node.value && sourceCode.getText(node.value.expression)) {
+            const value = sourceCode.getText(node.value)
+            context.report({
+              node,
+              loc: node.loc,
+              message: 'Avoid using JavaScript keyword as "v-on" value: {{value}}.',
+              data: { value }
+            })
+          } else {
+            context.report({
+              node,
+              loc: node.loc,
+              message: "'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."
+            })
+          }
         }
       }
     })
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 28caa444..9491117f 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -10,6 +10,7 @@
 // ------------------------------------------------------------------------------
 
 const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json'))
+const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json'))
 const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json'))
 const assert = require('assert')
 const vueEslintParser = require('vue-eslint-parser')
@@ -283,6 +284,16 @@ module.exports = {
     return HTML_ELEMENT_NAMES.has(name)
   },
 
+  /**
+   * Check whether the given name is an well-known SVG element or not.
+   * @param {string} name The name to check.
+   * @returns {boolean} `true` if the name is an well-known SVG element name.
+   */
+  isSvgWellKnownElementName (name) {
+    assert(typeof name === 'string')
+    return SVG_ELEMENT_NAMES.has(name)
+  },
+
   /**
    * Check whether the given name is a void element name or not.
    * @param {string} name The name to check.
@@ -294,6 +305,26 @@ module.exports = {
     return VOID_ELEMENT_NAMES.has(name)
   },
 
+  /**
+   * Check whether the given attribute node is a binding
+   * @param {ASTNode} name The attribute to check.
+   * @returns {boolean}
+   */
+  isBindingAttribute (attribute) {
+    return attribute.directive &&
+      attribute.key.name === 'bind' &&
+      attribute.key.argument
+  },
+
+  /**
+   * Check whether the given attribute node is an event
+   * @param {ASTNode} name The attribute to check.
+   * @returns {boolean}
+   */
+  isEventAttribute (attribute) {
+    return attribute.directive && attribute.key.name === 'on'
+  },
+
   /**
    * Parse member expression node to get array with all of its parts
    * @param {ASTNode} MemberExpression
diff --git a/lib/utils/svg-elements.json b/lib/utils/svg-elements.json
new file mode 100644
index 00000000..aaee2e6f
--- /dev/null
+++ b/lib/utils/svg-elements.json
@@ -0,0 +1,1 @@
+["a","animate","animateMotion","animateTransform","audio","canvas","circle","clipPath","defs","desc","discard","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","iframe","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","svg","switch","symbol","text","textPath","title","tspan","unknown","use","video","view"]


Test patch:
diff --git a/tests/lib/rules/component-name-in-template-casing.js b/tests/lib/rules/component-name-in-template-casing.js
index a432ac79..8dcd3937 100644
--- a/tests/lib/rules/component-name-in-template-casing.js
+++ b/tests/lib/rules/component-name-in-template-casing.js
@@ -29,6 +29,9 @@ tester.run('component-name-in-template-casing', rule, {
     '<template><div><slot></slot></div></template>',
     '<template><h1>Title</h1></template>',
     '<template><h1 :is="customTitle">Title</h1></template>',
+    '<template><svg><TheComponent /></svg></template>',
+    '<template><text /></template>',
+    '<template><circle cx="0" cy="0" :d="radius"></template>',
 
     // kebab-case
     {
@@ -65,6 +68,23 @@ tester.run('component-name-in-template-casing', rule, {
     '<template><the-component><!--test</the-component></template>'
   ],
   invalid: [
+    {
+      code: `
+      <template>
+        <svg>
+          <the-component />
+        </svg>
+      </template>
+      `,
+      output: `
+      <template>
+        <svg>
+          <TheComponent />
+        </svg>
+      </template>
+      `,
+      errors: ['Component name "the-component" is not PascalCase.']
+    },
     {
       code: `
       <template>
diff --git a/tests/lib/rules/max-attributes-per-line.js b/tests/lib/rules/max-attributes-per-line.js
index 0e8ab915..ce4d45ff 100644
--- a/tests/lib/rules/max-attributes-per-line.js
+++ b/tests/lib/rules/max-attributes-per-line.js
@@ -91,17 +91,53 @@ ruleTester.run('max-attributes-per-line', rule, {
   invalid: [
     {
       code: `<template><component name="John Doe" age="30"></component></template>`,
-      output: `<template><component name="John Doe" 
+      output: `<template><component name="John Doe"
 age="30"></component></template>`,
       errors: ['Attribute "age" should be on a new line.']
     },
+    {
+      code: `<template><component :name="user.name" :age="user.age"></component></template>`,
+      output: `<template><component :name="user.name"
+:age="user.age"></component></template>`,
+      errors: ['Binding "age" should be on a new line.']
+    },
+    {
+      code: `<template><component :is="test" v-bind="user"></component></template>`,
+      output: `<template><component :is="test"
+v-bind="user"></component></template>`,
+      errors: ['Directive "bind" should be on a new line.']
+    },
+    {
+      code: `<template><component :name="user.name" @buy="buyProduct"></component></template>`,
+      output: `<template><component :name="user.name"
+@buy="buyProduct"></component></template>`,
+      errors: ['Event "buy" should be on a new line.']
+    },
+    {
+      code: `<template><component :name="user.name" @click.stop></component></template>`,
+      output: `<template><component :name="user.name"
+@click.stop></component></template>`,
+      errors: ['Event "click" should be on a new line.']
+    },
+    {
+      code: `<template><component :name="user.name" v-if="something"></component></template>`,
+      output: `<template><component :name="user.name"
+v-if="something"></component></template>`,
+      errors: ['Directive "if" should be on a new line.']
+    },
+    {
+      code: `<template><component name="John Doe"    v-bind:age="user.age"></component></template>`,
+      output: `<template><component name="John Doe"
+v-bind:age="user.age"></component></template>`,
+      errors: ['Binding "age" should be on a new line.']
+    },
     {
       code: `<template><component job="Vet"
         name="John Doe"
         age="30">
         </component>
       </template>`,
-      output: `<template><component 
+      output: `<template><component
 job="Vet"
         name="John Doe"
         age="30">
@@ -116,7 +152,7 @@ job="Vet"
     {
       code: `<template><component name="John Doe" age="30" job="Vet"></component></template>`,
       options: [{ singleline: { max: 2 }}],
-      output: `<template><component name="John Doe" age="30" 
+      output: `<template><component name="John Doe" age="30"
 job="Vet"></component></template>`,
       errors: [{
         message: 'Attribute "job" should be on a new line.',
@@ -127,7 +163,7 @@ job="Vet"></component></template>`,
     {
       code: `<template><component name="John Doe" age="30" job="Vet"></component></template>`,
       options: [{ singleline: 1, multiline: { max: 1, allowFirstLine: false }}],
-      output: `<template><component name="John Doe" 
+      output: `<template><component name="John Doe"
 age="30" job="Vet"></component></template>`,
       errors: [{
         message: 'Attribute "age" should be on a new line.',
@@ -145,7 +181,7 @@ age="30" job="Vet"></component></template>`,
         </component>
       </template>`,
       options: [{ singleline: 3, multiline: { max: 1, allowFirstLine: false }}],
-      output: `<template><component 
+      output: `<template><component
 name="John Doe"
         age="30">
         </component>
@@ -164,7 +200,7 @@ name="John Doe"
       </template>`,
       options: [{ singleline: 3, multiline: { max: 1, allowFirstLine: false }}],
       output: `<template><component
-        name="John Doe" 
+        name="John Doe"
 age="30"
         job="Vet">
         </component>
@@ -183,7 +219,7 @@ age="30"
       </template>`,
       options: [{ singleline: 3, multiline: 1 }],
       output: `<template><component
-        name="John Doe" 
+        name="John Doe"
 age="30"
         job="Vet">
         </component>
@@ -203,7 +239,7 @@ age="30"
       options: [{ singleline: 3, multiline: { max: 2, allowFirstLine: false }}],
       output: `<template><component
         name="John Doe" age="30"
-        job="Vet" pet="dog" 
+        job="Vet" pet="dog"
 petname="Snoopy">
         </component>
       </template>`,
@@ -222,7 +258,7 @@ petname="Snoopy">
       options: [{ singleline: 3, multiline: { max: 2, allowFirstLine: false }}],
       output: `<template><component
         name="John Doe" age="30"
-        job="Vet" pet="dog" 
+        job="Vet" pet="dog"
 petname="Snoopy" extra="foo">
         </component>
       </template>`,
diff --git a/tests/lib/rules/no-unused-components.js b/tests/lib/rules/no-unused-components.js
index 402d43de..f62b83fb 100644
--- a/tests/lib/rules/no-unused-components.js
+++ b/tests/lib/rules/no-unused-components.js
@@ -52,6 +52,30 @@ tester.run('no-unused-components', rule, {
         }
       </script>`
     },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <svg>
+          <TheCircle />
+        </svg>
+      </template>
+      <script>
+        export default {
+          components: {
+            TheCircle
+          }
+        }
+      </script>`
+    },
+    {
+      filename: 'test.vue',
+      code: `<template>
+        <circle cx="0" cy="0" :d="radius" />
+      </template>
+      <script>
+        export default {}
+      </script>`
+    },
     {
       filename: 'test.vue',
       code: `<template>
diff --git a/tests/lib/rules/valid-v-model.js b/tests/lib/rules/valid-v-model.js
index 80cd4eb1..f5abd2dd 100644
--- a/tests/lib/rules/valid-v-model.js
+++ b/tests/lib/rules/valid-v-model.js
@@ -161,6 +161,11 @@ tester.run('valid-v-model', rule, {
       filename: 'test.vue',
       code: '<template><div><div v-for="x in list"><input v-model="(((x)))"></div></div></template>',
       errors: ["'v-model' directives cannot update the iteration variable 'x' itself."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div><div v-for="e in list"><input v-model="e"></div></div></template>',
+      errors: ["'v-model' directives cannot update the iteration variable 'e' itself."]
     }
   ]
 })
diff --git a/tests/lib/rules/valid-v-on.js b/tests/lib/rules/valid-v-on.js
index f24e10dc..87420ef3 100644
--- a/tests/lib/rules/valid-v-on.js
+++ b/tests/lib/rules/valid-v-on.js
@@ -107,12 +107,12 @@ tester.run('valid-v-on', rule, {
     {
       filename: 'test.vue',
       code: '<template><div v-on:click></div></template>',
-      errors: ["'v-on' directives require that attribute value or verb modifiers."]
+      errors: ["'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."]
     },
     {
       filename: 'test.vue',
       code: '<template><div @click></div></template>',
-      errors: ["'v-on' directives require that attribute value or verb modifiers."]
+      errors: ["'v-on' directives require a value or verb modifier (like 'stop' or 'prevent')."]
     },
     {
       filename: 'test.vue',
@@ -125,6 +125,16 @@ tester.run('valid-v-on', rule, {
       code: '<template><div @keydown.bar.aaa="foo"></div></template>',
       errors: ["'v-on' directives don't support the modifier 'bar'."],
       options: [{ modifiers: ['aaa'] }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div @click="const"></div></template>',
+      errors: ['Avoid using JavaScript keyword as "v-on" value: "const".']
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div @click="delete"></div></template>',
+      errors: ['Avoid using JavaScript keyword as "v-on" value: "delete".']
     }
   ]
 })