Repository: vuejs/eslint-plugin-vue
Pull request #795 (2019-02-01 12:38:24)

⭐️New: Add `vue/keyword-spacing` rule
This PR adds the wrapper rule of the [keyword-spacing](https://eslint.org/docs/rules/keyword-spacing) core rule to apply to the expressions in `<template>`.

mysticatea: Thank you for this PR.
Would you use `skipDynamicArguments` option because of a spacing rule?
ota-meshi: @mysticatea Thank you for your review.
I changed to use `skipDynamicArguments`, so please check again.

Patch:
diff --git a/docs/rules/README.md b/docs/rules/README.md
index 82baedd0..95fdb710 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -149,6 +149,7 @@ For example:
 | [vue/dot-location](./dot-location.md) | enforce consistent newlines before and after dots | :wrench: |
 | [vue/eqeqeq](./eqeqeq.md) | require the use of `===` and `!==` | :wrench: |
 | [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
+| [vue/keyword-spacing](./keyword-spacing.md) | enforce consistent spacing before and after keywords | :wrench: |
 | [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name |  |
 | [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
 | [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns |  |
diff --git a/docs/rules/keyword-spacing.md b/docs/rules/keyword-spacing.md
new file mode 100644
index 00000000..f053a90b
--- /dev/null
+++ b/docs/rules/keyword-spacing.md
@@ -0,0 +1,23 @@
+---
+pageClass: rule-details
+sidebarDepth: 0
+title: vue/keyword-spacing
+description: enforce consistent spacing before and after keywords
+---
+# vue/keyword-spacing
+> enforce consistent spacing before and after keywords
+
+- :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.
+
+This rule is the same rule as core [keyword-spacing] rule but it applies to the expressions in `<template>`.
+
+## :books: Further reading
+
+- [keyword-spacing]
+
+[keyword-spacing]: https://eslint.org/docs/rules/keyword-spacing
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/keyword-spacing.js)
+- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/keyword-spacing.js)
diff --git a/lib/configs/no-layout-rules.js b/lib/configs/no-layout-rules.js
index b50aaddb..1dcab231 100644
--- a/lib/configs/no-layout-rules.js
+++ b/lib/configs/no-layout-rules.js
@@ -17,6 +17,7 @@ module.exports = {
     'vue/html-quotes': 'off',
     'vue/html-self-closing': 'off',
     'vue/key-spacing': 'off',
+    'vue/keyword-spacing': 'off',
     'vue/max-attributes-per-line': 'off',
     'vue/multiline-html-element-content-newline': 'off',
     'vue/mustache-interpolation-spacing': 'off',
diff --git a/lib/index.js b/lib/index.js
index 8dbae682..51e821ae 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -27,6 +27,7 @@ module.exports = {
     'html-self-closing': require('./rules/html-self-closing'),
     'jsx-uses-vars': require('./rules/jsx-uses-vars'),
     'key-spacing': require('./rules/key-spacing'),
+    'keyword-spacing': require('./rules/keyword-spacing'),
     'match-component-file-name': require('./rules/match-component-file-name'),
     'max-attributes-per-line': require('./rules/max-attributes-per-line'),
     'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
diff --git a/lib/rules/keyword-spacing.js b/lib/rules/keyword-spacing.js
new file mode 100644
index 00000000..548c39ef
--- /dev/null
+++ b/lib/rules/keyword-spacing.js
@@ -0,0 +1,12 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const { wrapCoreRule } = require('../utils')
+
+// eslint-disable-next-line no-invalid-meta
+module.exports = wrapCoreRule(
+  require('eslint/lib/rules/keyword-spacing'),
+  { skipDynamicArguments: true }
+)


Test patch:
diff --git a/tests/lib/rules/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js
new file mode 100644
index 00000000..f20814ab
--- /dev/null
+++ b/tests/lib/rules/keyword-spacing.js
@@ -0,0 +1,160 @@
+/**
+ * @author Yosuke Ota
+ */
+'use strict'
+
+const RuleTester = require('eslint').RuleTester
+const rule = require('../../../lib/rules/keyword-spacing')
+
+const tester = new RuleTester({
+  parser: 'vue-eslint-parser',
+  parserOptions: { ecmaVersion: 2015 }
+})
+
+tester.run('keyword-spacing', rule, {
+  valid: [
+    `<template>
+      <div @event="
+        if (foo) {
+          //...
+        } else if (bar) {
+          //...
+        } else {
+          //...
+        }
+      " />
+    </template>`,
+    {
+      code:
+      `<template>
+        <div @event="
+          if(foo) {
+            //...
+          }else if(bar) {
+            //...
+          }else{
+            //...
+          }
+        " />
+      </template>`,
+      options: [{ before: false, after: false }]
+    },
+    `<template>
+      <div :[(function(){return(1)})()]="val" />
+    </template>`
+  ],
+  invalid: [
+    {
+      code:
+      `<template>
+        <div @event="
+          if(foo) {
+            //...
+          }else if(bar) {
+            //...
+          }else{
+            //...
+          }
+        " />
+      </template>`,
+      output:
+      `<template>
+        <div @event="
+          if (foo) {
+            //...
+          } else if (bar) {
+            //...
+          } else {
+            //...
+          }
+        " />
+      </template>`,
+      errors: [
+        {
+          message: 'Expected space(s) after "if".',
+          line: 3
+        },
+        {
+          message: 'Expected space(s) before "else".',
+          line: 5
+        },
+        {
+          message: 'Expected space(s) after "if".',
+          line: 5
+        },
+        {
+          message: 'Expected space(s) before "else".',
+          line: 7
+        },
+        {
+          message: 'Expected space(s) after "else".',
+          line: 7
+        }
+      ]
+    },
+    {
+      code:
+      `<template>
+        <div @event="
+          if (foo) {
+            //...
+          } else if (bar) {
+            //...
+          } else {
+            //...
+          }
+        " />
+      </template>`,
+      options: [{ before: false, after: false }],
+      output:
+      `<template>
+        <div @event="
+          if(foo) {
+            //...
+          }else if(bar) {
+            //...
+          }else{
+            //...
+          }
+        " />
+      </template>`,
+      errors: [
+        {
+          message: 'Unexpected space(s) after "if".',
+          line: 3
+        },
+        {
+          message: 'Unexpected space(s) before "else".',
+          line: 5
+        },
+        {
+          message: 'Unexpected space(s) after "if".',
+          line: 5
+        },
+        {
+          message: 'Unexpected space(s) before "else".',
+          line: 7
+        },
+        {
+          message: 'Unexpected space(s) after "else".',
+          line: 7
+        }
+      ]
+    },
+    {
+      code:
+      `<template>
+        <div :[(function(){return(1)})()]="(function(){return(1)})()" />
+      </template>`,
+      output:
+      `<template>
+        <div :[(function(){return(1)})()]="(function(){return (1)})()" />
+      </template>`,
+      errors: [
+        {
+          message: 'Expected space(s) after "return".',
+          line: 2
+        }]
+    }
+  ]
+})