Skip to content

Commit

Permalink
Add vue/return-in-emits-validator rule (#1129)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi committed May 15, 2020
1 parent 7c53cd4 commit 8de47f1
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -73,6 +73,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/require-v-for-key](./require-v-for-key.md) | require `v-bind:key` with `v-for` directives | |
| [vue/require-valid-default-prop](./require-valid-default-prop.md) | enforce props default values to be valid | |
| [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property | |
| [vue/return-in-emits-validator](./return-in-emits-validator.md) | enforce that a return statement is present in emits validator | |
| [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` | |
| [vue/valid-template-root](./valid-template-root.md) | enforce valid template root | |
| [vue/valid-v-bind](./valid-v-bind.md) | enforce valid `v-bind` directives | |
Expand Down
63 changes: 63 additions & 0 deletions docs/rules/return-in-emits-validator.md
@@ -0,0 +1,63 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/return-in-emits-validator
description: enforce that a return statement is present in emits validator
---
# vue/return-in-emits-validator
> enforce that a return statement is present in emits validator
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.

## :book: Rule Details

This rule enforces that a `return` statement is present in `emits` validators.

<eslint-code-block :rules="{'vue/return-in-emits-validator': ['error']}">

```vue
<script>
export default {
emits: {
/* ✓ GOOD */
foo (evt) {
if (evt) {
return true
} else {
return false
}
},
bar: function () {
return true
},
baz (evt) {
if (evt) {
return true
}
},
/* ✗ BAD */
qux: function () {},
quux (evt) {
if (!evt) {
return false
}
}
}
}
</script>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :books: Further reading

- [Vue RFCs - 0030-emits-option](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/return-in-emits-validator.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/return-in-emits-validator.js)
1 change: 1 addition & 0 deletions lib/configs/vue3-essential.js
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'vue/require-v-for-key': 'error',
'vue/require-valid-default-prop': 'error',
'vue/return-in-computed-property': 'error',
'vue/return-in-emits-validator': 'error',
'vue/use-v-on-exact': 'error',
'vue/valid-template-root': 'error',
'vue/valid-v-bind': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -97,6 +97,7 @@ module.exports = {
'require-v-for-key': require('./rules/require-v-for-key'),
'require-valid-default-prop': require('./rules/require-valid-default-prop'),
'return-in-computed-property': require('./rules/return-in-computed-property'),
'return-in-emits-validator': require('./rules/return-in-emits-validator'),
'script-indent': require('./rules/script-indent'),
'singleline-html-element-content-newline': require('./rules/singleline-html-element-content-newline'),
'sort-keys': require('./rules/sort-keys'),
Expand Down
109 changes: 109 additions & 0 deletions lib/rules/return-in-emits-validator.js
@@ -0,0 +1,109 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')

/**
* @typedef {import('vue-eslint-parser').AST.ESLintExpression} Expression
*/

/**
* Checks if the given node value is falsy.
* @param {Expression} node The node to check
* @returns {boolean} If `true`, the given node value is falsy.
*/
function isFalsy (node) {
if (node.type === 'Literal') {
if (node.bigint) {
return node.bigint === '0'
} else if (!node.value) {
return true
}
} else if (node.type === 'Identifier') {
if (node.name === 'undefined' || node.name === 'NaN') {
return true
}
}
return false
}
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce that a return statement is present in emits validator',
categories: ['vue3-essential'],
url: 'https://eslint.vuejs.org/rules/return-in-emits-validator.html'
},
fixable: null, // or "code" or "whitespace"
schema: []
},

create (context) {
const emitsValidators = []

// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------

let scopeStack = null

return Object.assign({},
utils.defineVueVisitor(context,
{
ObjectExpression (obj, { node: vueNode }) {
if (obj !== vueNode) {
return
}
for (const emits of utils.getComponentEmits(obj)) {
const emitsValue = emits.value
if (!emitsValue) {
continue
}
if (emitsValue.type !== 'FunctionExpression' && emitsValue.type !== 'ArrowFunctionExpression') {
continue
}
emitsValidators.push(emits)
}
},
':function' (node) {
scopeStack = { upper: scopeStack, functionNode: node, hasReturnValue: false, possibleOfReturnTrue: false }
},
ReturnStatement (node) {
if (node.argument) {
scopeStack.hasReturnValue = true

if (!isFalsy(node.argument)) {
scopeStack.possibleOfReturnTrue = true
}
}
},
':function:exit' (node) {
if (!scopeStack.possibleOfReturnTrue) {
const emits = emitsValidators.find(e => e.value === node)
if (emits) {
context.report({
node,
message: scopeStack.hasReturnValue
? 'Expected to return a true value in "{{name}}" emits validator.'
: 'Expected to return a boolean value in "{{name}}" emits validator.',
data: {
name: emits.emitName
}
})
}
}

scopeStack = scopeStack.upper
}
}
),
)
}
}

0 comments on commit 8de47f1

Please sign in to comment.