Repository: vuejs/eslint-plugin-vue
Pull request #654 (2018-11-12 23:17:01)

chore: Report errors only for base and essential rules
Currently we report errors for all rules, but it's not a very semantic way of telling user something's not exactly ok. We should rather report errors only for things that are crucial - that is `base` and `essential` rules, and for recommendations throw warnings instead. It will also give users the ability to set treshold value. cc @chrisvfritz 


Patch:
diff --git a/README.md b/README.md
index 0ec45258..1c8473ce 100644
--- a/README.md
+++ b/README.md
@@ -318,6 +318,7 @@ In order to add a new rule, you should:
 - Write test scenarios & implement logic
 - Describe the rule in the generated `docs` file
 - Make sure all tests are passing
+- Run `npm run lint` and fix any errors
 - Run `npm run update` in order to update readme and recommended configuration
 - Create PR and link created issue in description
 
diff --git a/docs/rules/valid-v-bind.md b/docs/rules/valid-v-bind.md
index dfc9091d..bf3ecacf 100644
--- a/docs/rules/valid-v-bind.md
+++ b/docs/rules/valid-v-bind.md
@@ -15,6 +15,8 @@ This rule does not report `v-bind` directives which do not have their argument (
 
 This rule does not check syntax errors in directives because it's checked by [no-parsing-error] rule.
 
+:-1: Examples of **incorrect** code for this rule:
+
 ```html
 <div v-bind/>
 <div :aaa/>
diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js
index 6de8e668..eb85e8a9 100644
--- a/lib/rules/component-name-in-template-casing.js
+++ b/lib/rules/component-name-in-template-casing.js
@@ -22,7 +22,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'enforce specific casing for the component naming style in template',
-      category: undefined, // strongly-recommended
+      category: 'strongly-recommended',
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/component-name-in-template-casing.md'
     },
     fixable: 'code',
diff --git a/lib/rules/multiline-html-element-content-newline.js b/lib/rules/multiline-html-element-content-newline.js
index 415c8ddf..659b76d3 100644
--- a/lib/rules/multiline-html-element-content-newline.js
+++ b/lib/rules/multiline-html-element-content-newline.js
@@ -9,6 +9,7 @@
 // ------------------------------------------------------------------------------
 
 const utils = require('../utils')
+const casing = require('../utils/casing')
 
 // ------------------------------------------------------------------------------
 // Helpers
@@ -20,7 +21,7 @@ function isMultilineElement (element) {
 
 function parseOptions (options) {
   return Object.assign({
-    'ignores': ['pre', 'textarea']
+    ignores: ['pre', 'textarea']
   }, options)
 }
 
@@ -51,7 +52,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'require a line break before and after the contents of a multiline element',
-      category: undefined,
+      category: 'strongly-recommended',
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/multiline-html-element-content-newline.md'
     },
     fixable: 'whitespace',
@@ -80,12 +81,18 @@ module.exports = {
 
     let inIgnoreElement
 
+    function isIgnoredElement (node) {
+      return ignores.includes(node.name) ||
+        ignores.includes(casing.pascalCase(node.rawName)) ||
+        ignores.includes(casing.kebabCase(node.rawName))
+    }
+
     return utils.defineTemplateBodyVisitor(context, {
       'VElement' (node) {
         if (inIgnoreElement) {
           return
         }
-        if (ignores.indexOf(node.name) >= 0) {
+        if (isIgnoredElement(node)) {
           // ignore element name
           inIgnoreElement = node
           return
@@ -114,7 +121,7 @@ module.exports = {
             },
             messageId: 'unexpectedAfterClosingBracket',
             data: {
-              name: node.name,
+              name: node.rawName,
               actual: getPhrase(beforeLineBreaks)
             },
             fix (fixer) {
diff --git a/lib/rules/no-spaces-around-equal-signs-in-attribute.js b/lib/rules/no-spaces-around-equal-signs-in-attribute.js
index 3a2dbb89..eefe09e8 100644
--- a/lib/rules/no-spaces-around-equal-signs-in-attribute.js
+++ b/lib/rules/no-spaces-around-equal-signs-in-attribute.js
@@ -18,7 +18,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'disallow spaces around equal signs in attribute',
-      category: undefined,
+      category: 'strongly-recommended',
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/no-spaces-around-equal-signs-in-attribute.md'
     },
     fixable: 'whitespace',
diff --git a/lib/rules/singleline-html-element-content-newline.js b/lib/rules/singleline-html-element-content-newline.js
index 309f789d..61ed189f 100644
--- a/lib/rules/singleline-html-element-content-newline.js
+++ b/lib/rules/singleline-html-element-content-newline.js
@@ -9,6 +9,7 @@
 // ------------------------------------------------------------------------------
 
 const utils = require('../utils')
+const casing = require('../utils/casing')
 
 // ------------------------------------------------------------------------------
 // Helpers
@@ -20,8 +21,8 @@ function isSinglelineElement (element) {
 
 function parseOptions (options) {
   return Object.assign({
-    'ignores': ['pre', 'textarea'],
-    'ignoreWhenNoAttributes': true
+    ignores: ['pre', 'textarea'],
+    ignoreWhenNoAttributes: true
   }, options)
 }
 
@@ -46,17 +47,17 @@ module.exports = {
   meta: {
     docs: {
       description: 'require a line break before and after the contents of a singleline element',
-      category: undefined,
+      category: 'strongly-recommended',
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/singleline-html-element-content-newline.md'
     },
     fixable: 'whitespace',
     schema: [{
       type: 'object',
       properties: {
-        'ignoreWhenNoAttributes': {
+        ignoreWhenNoAttributes: {
           type: 'boolean'
         },
-        'ignores': {
+        ignores: {
           type: 'array',
           items: { type: 'string' },
           uniqueItems: true,
@@ -80,12 +81,18 @@ module.exports = {
 
     let inIgnoreElement
 
+    function isIgnoredElement (node) {
+      return ignores.includes(node.name) ||
+        ignores.includes(casing.pascalCase(node.rawName)) ||
+        ignores.includes(casing.kebabCase(node.rawName))
+    }
+
     return utils.defineTemplateBodyVisitor(context, {
       'VElement' (node) {
         if (inIgnoreElement) {
           return
         }
-        if (ignores.indexOf(node.name) >= 0) {
+        if (isIgnoredElement(node)) {
           // ignore element name
           inIgnoreElement = node
           return
@@ -114,7 +121,7 @@ module.exports = {
           },
           messageId: 'unexpectedAfterClosingBracket',
           data: {
-            name: node.name
+            name: node.rawName
           },
           fix (fixer) {
             const range = [node.startTag.range[1], contentFirst.range[0]]
@@ -134,7 +141,7 @@ module.exports = {
           },
           messageId: 'unexpectedBeforeOpeningBracket',
           data: {
-            name: node.name
+            name: node.rawName
           },
           fix (fixer) {
             const range = [contentLast.range[1], node.endTag.range[0]]
diff --git a/lib/rules/use-v-on-exact.js b/lib/rules/use-v-on-exact.js
index 959dec01..8291b9af 100644
--- a/lib/rules/use-v-on-exact.js
+++ b/lib/rules/use-v-on-exact.js
@@ -18,7 +18,7 @@ module.exports = {
   meta: {
     docs: {
       description: 'enforce usage of `exact` modifier on `v-on`',
-      category: undefined, // essential
+      category: 'essential',
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.4/docs/rules/use-v-on-exact.md'
     },
     fixable: null,
diff --git a/lib/utils/index.js b/lib/utils/index.js
index 9491117f..cf58a0fc 100644
--- a/lib/utils/index.js
+++ b/lib/utils/index.js
@@ -685,8 +685,12 @@ module.exports = {
       node: null
     }
 
+    function isReachable (segment) {
+      return segment.reachable
+    }
+
     function isValidReturn () {
-      if (!funcInfo.hasReturn) {
+      if (funcInfo.codePath.currentSegments.some(isReachable)) {
         return false
       }
       return !treatUndefinedAsUnspecified || funcInfo.hasReturnValue
diff --git a/tools/update-lib-configs.js b/tools/update-lib-configs.js
index 2d7e2a68..669b4cf8 100644
--- a/tools/update-lib-configs.js
+++ b/tools/update-lib-configs.js
@@ -14,9 +14,11 @@ const path = require('path')
 const eslint = require('eslint')
 const categories = require('./lib/categories')
 
+const errorCategories = ['base', 'essential']
+
 function formatRules (rules) {
   const obj = rules.reduce((setting, rule) => {
-    setting[rule.ruleId] = 'error'
+    setting[rule.ruleId] = errorCategories.includes(rule.meta.docs.category) ? 'error' : 'warn'
     return setting
   }, {})
   return JSON.stringify(obj, null, 2)


Test patch:
diff --git a/tests/lib/rules/multiline-html-element-content-newline.js b/tests/lib/rules/multiline-html-element-content-newline.js
index c60fa513..e653218e 100644
--- a/tests/lib/rules/multiline-html-element-content-newline.js
+++ b/tests/lib/rules/multiline-html-element-content-newline.js
@@ -91,13 +91,45 @@ tester.run('multiline-html-element-content-newline', rule, {
           >content</ignore-tag>
           <ignore-tag><div
             >content</div></ignore-tag>
-          <ignore-tag>>content
+          <ignore-tag>content
             content</ignore-tag>
         </template>`,
       options: [{
         ignores: ['ignore-tag']
       }]
     },
+    {
+      code: `
+        <template>
+          <IgnoreTag>content</IgnoreTag>
+          <IgnoreTag
+            id="test-pre"
+          >content</IgnoreTag>
+          <IgnoreTag><div
+            >content</div></IgnoreTag>
+          <IgnoreTag>content
+            content</IgnoreTag>
+        </template>`,
+      options: [{
+        ignores: ['IgnoreTag']
+      }]
+    },
+    {
+      code: `
+        <template>
+          <ignore-tag>content</ignore-tag>
+          <ignore-tag
+            id="test-pre"
+          >content</ignore-tag>
+          <ignore-tag><div
+            >content</div></ignore-tag>
+          <ignore-tag>content
+            content</ignore-tag>
+        </template>`,
+      options: [{
+        ignores: ['IgnoreTag']
+      }]
+    },
     // Ignore if no closing brackets
     `
       <template>
diff --git a/tests/lib/rules/require-render-return.js b/tests/lib/rules/require-render-return.js
index 38132b28..e7fde4e6 100644
--- a/tests/lib/rules/require-render-return.js
+++ b/tests/lib/rules/require-render-return.js
@@ -70,6 +70,36 @@ ruleTester.run('require-render-return', rule, {
       }`,
       parserOptions
     },
+    {
+      filename: 'test.vue',
+      code: `export default {
+        render() {
+          const foo = function () {}
+          return foo
+        }
+      }`,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `export default {
+        render() {
+          if (a) {
+            if (b) {
+              
+            }
+            if (c) {
+              return true
+            } else {
+              return foo
+            }
+          } else {
+            return foo
+          }
+        }
+      }`,
+      parserOptions
+    },
     {
       filename: 'test.vue',
       code: `export default {
@@ -119,6 +149,22 @@ ruleTester.run('require-render-return', rule, {
         line: 2
       }]
     },
+    {
+      filename: 'test.vue',
+      code: `export default {
+        render: function () {
+          if (foo) {
+            return h('div', 'hello')
+          }
+        }
+      }`,
+      parserOptions,
+      errors: [{
+        message: 'Expected to return a value in render function.',
+        type: 'Identifier',
+        line: 2
+      }]
+    },
     {
       code: `Vue.component('test', {
         render: function () {
@@ -133,6 +179,38 @@ ruleTester.run('require-render-return', rule, {
         type: 'Identifier',
         line: 2
       }]
+    },
+    {
+      code: `Vue.component('test2', {
+        render: function () {
+          if (a) {
+            return h('div', 'hello')
+          }
+        }
+      })`,
+      parserOptions,
+      errors: [{
+        message: 'Expected to return a value in render function.',
+        type: 'Identifier',
+        line: 2
+      }]
+    },
+    {
+      code: `Vue.component('test2', {
+        render: function () {
+          if (a) {
+            
+          } else {
+            return h('div', 'hello')
+          }
+        }
+      })`,
+      parserOptions,
+      errors: [{
+        message: 'Expected to return a value in render function.',
+        type: 'Identifier',
+        line: 2
+      }]
     }
   ]
 })
diff --git a/tests/lib/rules/singleline-html-element-content-newline.js b/tests/lib/rules/singleline-html-element-content-newline.js
index dcb65fd7..a5c9bce0 100644
--- a/tests/lib/rules/singleline-html-element-content-newline.js
+++ b/tests/lib/rules/singleline-html-element-content-newline.js
@@ -141,6 +141,28 @@ tester.run('singleline-html-element-content-newline', rule, {
         ignores: ['ignore-tag']
       }]
     },
+    {
+      code: `
+        <template>
+          <IgnoreTag>content</IgnoreTag>
+          <IgnoreTag attr>content</IgnoreTag>
+          <IgnoreTag><span attr>content</span></IgnoreTag>
+        </template>`,
+      options: [{
+        ignores: ['IgnoreTag']
+      }]
+    },
+    {
+      code: `
+        <template>
+          <ignore-tag>content</ignore-tag>
+          <ignore-tag attr>content</ignore-tag>
+          <ignore-tag><span attr>content</span></ignore-tag>
+        </template>`,
+      options: [{
+        ignores: ['IgnoreTag']
+      }]
+    },
     // not target
     `
       <template>
@@ -434,6 +456,24 @@ singleline element
       errors: [
         'Expected 1 line break after opening tag (`<div>`), but no line breaks found.'
       ]
+    },
+    {
+      code: `
+        <template>
+          <Div class="panel">content</Div>
+        </template>
+      `,
+      output: `
+        <template>
+          <Div class="panel">
+content
+</Div>
+        </template>
+      `,
+      errors: [
+        'Expected 1 line break after opening tag (`<Div>`), but no line breaks found.',
+        'Expected 1 line break before closing tag (`</Div>`), but no line breaks found.'
+      ]
     }
   ]
 })