Repository: vuejs/eslint-plugin-vue
Pull request #602 (2018-10-11 01:03:33)

Add rule `use-v-on-exact` #271 
Implementation of rule `use-v-on-exact` from #271



Patch:
diff --git a/docs/rules/use-v-on-extact.md b/docs/rules/use-v-on-extact.md
new file mode 100644
index 00000000..2753681a
--- /dev/null
+++ b/docs/rules/use-v-on-extact.md
@@ -0,0 +1,27 @@
+# Enforce usage of `exact` modifier on `v-on`
+
+- :gear: This rule is included in `"plugin:vue/essential"`.
+
+This rule enforce usage of `exact` modifier on `v-on` when there is another `v-on` with modifier.
+
+:+1: Examples of **correct** code for this rule:
+
+```html
+<template>
+  <button @click="foo" :click="foo"></button>
+  <button v-on:click.exact="foo" v-on:click.ctrl="foo"></button>
+</template>
+```
+
+:-1: Examples of **incorrect** code for this rule:
+
+```html
+<template>
+  <button v-on:click="foo" v-on:click.ctrl="foo"></button>
+</template>
+```
+
+## Related rules
+
+- [vue/v-on-style](./v-on-style.md)
+- [vue/valid-v-on](./valid-v-on.md)
diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js
new file mode 100644
index 00000000..b143d619
--- /dev/null
+++ b/lib/rules/use-v-on-exact.js
@@ -0,0 +1,65 @@
+/**
+ * @fileoverview enforce usage of `exact` modifier on `v-on`.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+  meta: {
+    docs: {
+      description: 'enforce usage of `exact` modifier on `v-on`',
+      category: undefined, // essential
+      url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/use-v-on-exact.md'
+    },
+    fixable: null,
+    schema: []
+  },
+
+  /**
+   * Creates AST event handlers for use-v-on-exact.
+   *
+   * @param {RuleContext} context - The rule context.
+   * @returns {Object} AST event handlers.
+   */
+  create (context) {
+    return utils.defineTemplateBodyVisitor(context, {
+      VStartTag (node) {
+        if (node.attributes.length > 1) {
+          const groups = node.attributes
+            .map(item => item.key)
+            .filter(item => item && item.type === 'VDirectiveKey' && item.name === 'on')
+            .reduce((rv, item) => {
+              (rv[item.argument] = rv[item.argument] || []).push(item)
+              return rv
+            }, {})
+
+          const directives = Object.keys(groups).map(key => groups[key])
+          // const directives = Object.values(groups) // Uncomment after node 6 is dropped
+            .filter(item => item.length > 1)
+          for (const group of directives) {
+            if (group.some(item => item.modifiers.length > 0)) { // check if there are directives with modifiers
+              const invalid = group.filter(item => item.modifiers.length === 0)
+              for (const node of invalid) {
+                context.report({
+                  node,
+                  loc: node.loc,
+                  message: "Consider to use '.exact' modifier."
+                })
+              }
+            }
+          }
+        }
+      }
+    })
+  }
+}


Test patch:
diff --git a/tests/lib/rules/use-v-on-exact.js b/tests/lib/rules/use-v-on-exact.js
new file mode 100644
index 00000000..e93a4660
--- /dev/null
+++ b/tests/lib/rules/use-v-on-exact.js
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview enforce usage of `exact` modifier on `v-on`.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/use-v-on-exact')
+
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parser: 'vue-eslint-parser',
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('use-v-on-exact', rule, {
+
+  valid: [
+    {
+      code: `<template><button @click="foo"/></template>`
+    },
+    {
+      code: `<template><button @click="foo" :click="foo"/></template>`
+    },
+    {
+      code: `<template><button @click.ctrl="foo"/></template>`
+    },
+    {
+      code: `<template><button @click.exact="foo"/></template>`
+    },
+    {
+      code: `<template><button @click.exact="foo" @click.ctrl="foo"/></template>`
+    },
+    {
+      code: `<template><button v-on:click="foo"/></template>`
+    },
+    {
+      code: `<template><button v-on:click.ctrl="foo"/></template>`
+    },
+    {
+      code: `<template><button v-on:click.exact="foo"/></template>`
+    },
+    {
+      code: `<template><button v-on:click.exact="foo" v-on:click.ctrl="foo"/></template>`
+    },
+    {
+      code: `<template><button @click="foo" @focus="foo"/></template>`
+    },
+    {
+      code: `<template><button @click="foo" @click="foo"/></template>`
+    },
+    {
+      code: `<template><button @click="foo"/><button @click.ctrl="foo"/></template>`
+    }
+  ],
+
+  invalid: [
+    {
+      code: `<template><button @click="foo" @click.ctrl="foo"/></template>`,
+      errors: ["Consider to use '.exact' modifier."]
+    },
+    {
+      code: `<template><button @click="foo" @focus="foo" @click.ctrl="foo"/></template>`,
+      errors: ["Consider to use '.exact' modifier."]
+    }
+  ]
+})