Skip to content

Commit ae03c28

Browse files
niklashigimichalsnik
authored andcommitted
⭐️New: Add vue/v-on-parens rule (#481)
* Implement rule * Get rid of spread operator, update error message in test * Update meta * Add output in tests * Rename rule to `v-on-function-call` * Don't add parantheses on fix * Fix rule
1 parent 5d1f051 commit ae03c28

File tree

5 files changed

+211
-0
lines changed

5 files changed

+211
-0
lines changed

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ For example:
155155
| [vue/script-indent](./script-indent.md) | enforce consistent indentation in `<script>` | :wrench: |
156156
| [vue/space-infix-ops](./space-infix-ops.md) | require spacing around infix operators | :wrench: |
157157
| [vue/space-unary-ops](./space-unary-ops.md) | enforce consistent spacing before or after unary operators | :wrench: |
158+
| [vue/v-on-function-call](./v-on-function-call.md) | enforce or forbid parentheses after method calls without arguments in `v-on` directives | :wrench: |
158159

159160
## Deprecated
160161

docs/rules/v-on-function-call.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/v-on-function-call
5+
description: enforce or forbid parentheses after method calls without arguments in `v-on` directives
6+
---
7+
# vue/v-on-function-call
8+
> enforce or forbid parentheses after method calls without arguments in `v-on` directives
9+
10+
- :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.
11+
12+
## :book: Rule Details
13+
14+
:-1: Example of **incorrect** code for this rule:
15+
16+
```html
17+
<button v-on:click="closeModal()">
18+
Close
19+
</button>
20+
```
21+
22+
:+1: Example of **correct** code for this rule:
23+
24+
```html
25+
<button v-on:click="closeModal">
26+
Close
27+
</button>
28+
```
29+
30+
## :wrench: Options
31+
32+
Default is set to `never`.
33+
34+
```
35+
'vue/v-on-parens': [2, 'always'|'never']
36+
```
37+
38+
### `"always"` - Always use parentheses in `v-on` directives
39+
40+
:-1: Example of **incorrect** code:
41+
42+
```html
43+
<button v-on:click="closeModal">
44+
Close
45+
</button>
46+
```
47+
48+
:+1: Example of **correct** code:
49+
50+
```html
51+
<button v-on:click="closeModal()">
52+
Close
53+
</button>
54+
```
55+
56+
### `"never"` - Never use parentheses in `v-on` directives for method calls without arguments
57+
58+
:-1: Example of **incorrect** code:
59+
60+
```html
61+
<button v-on:click="closeModal()">
62+
Close
63+
</button>
64+
```
65+
66+
:+1: Example of **correct** code:
67+
68+
```html
69+
<button v-on:click="closeModal">
70+
Close
71+
</button>
72+
```
73+
74+
## :mag: Implementation
75+
76+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/v-on-function-call.js)
77+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/v-on-function-call.js)

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ module.exports = {
6868
'this-in-template': require('./rules/this-in-template'),
6969
'use-v-on-exact': require('./rules/use-v-on-exact'),
7070
'v-bind-style': require('./rules/v-bind-style'),
71+
'v-on-function-call': require('./rules/v-on-function-call'),
7172
'v-on-style': require('./rules/v-on-style'),
7273
'valid-template-root': require('./rules/valid-template-root'),
7374
'valid-v-bind': require('./rules/valid-v-bind'),

lib/rules/v-on-function-call.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @author Niklas Higi
3+
*/
4+
'use strict'
5+
6+
// ------------------------------------------------------------------------------
7+
// Requirements
8+
// ------------------------------------------------------------------------------
9+
10+
const utils = require('../utils')
11+
12+
// ------------------------------------------------------------------------------
13+
// Rule Definition
14+
// ------------------------------------------------------------------------------
15+
16+
module.exports = {
17+
meta: {
18+
type: 'suggestion',
19+
docs: {
20+
description: 'enforce or forbid parentheses after method calls without arguments in `v-on` directives',
21+
category: undefined,
22+
url: 'https://eslint.vuejs.org/rules/v-on-function-call.html'
23+
},
24+
fixable: 'code',
25+
schema: [
26+
{ enum: ['always', 'never'] }
27+
]
28+
},
29+
30+
create (context) {
31+
const always = context.options[0] === 'always'
32+
33+
return utils.defineTemplateBodyVisitor(context, {
34+
"VAttribute[directive=true][key.name='on'][key.argument!=null] > VExpressionContainer > Identifier" (node) {
35+
if (!always) return
36+
context.report({
37+
node,
38+
loc: node.loc,
39+
message: "Method calls inside of 'v-on' directives must have parentheses."
40+
})
41+
},
42+
43+
"VAttribute[directive=true][key.name='on'][key.argument!=null] VOnExpression > ExpressionStatement > *" (node) {
44+
if (!always && node.type === 'CallExpression' && node.arguments.length === 0) {
45+
context.report({
46+
node,
47+
loc: node.loc,
48+
message: "Method calls without arguments inside of 'v-on' directives must not have parentheses.",
49+
fix: fixer => {
50+
const nodeString = context.getSourceCode().getText().substring(node.range[0], node.range[1])
51+
// This ensures that parens are also removed if they contain whitespace
52+
const parensLength = nodeString.match(/\(\s*\)\s*$/)[0].length
53+
return fixer.removeRange([node.end - parensLength, node.end])
54+
}
55+
})
56+
}
57+
}
58+
})
59+
}
60+
}

tests/lib/rules/v-on-function-call.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @author Niklas Higi
3+
*/
4+
'use strict'
5+
6+
// ------------------------------------------------------------------------------
7+
// Requirements
8+
// ------------------------------------------------------------------------------
9+
10+
const RuleTester = require('eslint').RuleTester
11+
const rule = require('../../../lib/rules/v-on-function-call')
12+
13+
// ------------------------------------------------------------------------------
14+
// Tests
15+
// ------------------------------------------------------------------------------
16+
17+
const tester = new RuleTester({
18+
parser: 'vue-eslint-parser',
19+
parserOptions: { ecmaVersion: 2015 }
20+
})
21+
22+
tester.run('v-on-function-call', rule, {
23+
valid: [
24+
{
25+
filename: 'test.vue',
26+
code: ''
27+
},
28+
{
29+
filename: 'test.vue',
30+
code: '<template><div @click="foo(123)"></div></template>',
31+
options: ['always']
32+
},
33+
{
34+
filename: 'test.vue',
35+
code: '<template><div @click="foo(123)"></div></template>',
36+
options: ['never']
37+
},
38+
{
39+
filename: 'test.vue',
40+
code: '<template><div @click="foo()"></div></template>',
41+
options: ['always']
42+
},
43+
{
44+
filename: 'test.vue',
45+
code: '<template><div @click="foo"></div></template>',
46+
options: ['never']
47+
}
48+
],
49+
invalid: [
50+
{
51+
filename: 'test.vue',
52+
code: '<template><div @click="foo"></div></template>',
53+
output: `<template><div @click="foo"></div></template>`,
54+
errors: ["Method calls inside of 'v-on' directives must have parentheses."],
55+
options: ['always']
56+
},
57+
{
58+
filename: 'test.vue',
59+
code: '<template><div @click="foo()"></div></template>',
60+
output: `<template><div @click="foo"></div></template>`,
61+
errors: ["Method calls without arguments inside of 'v-on' directives must not have parentheses."],
62+
options: ['never']
63+
},
64+
{
65+
filename: 'test.vue',
66+
code: '<template><div @click="foo( )"></div></template>',
67+
output: `<template><div @click="foo"></div></template>`,
68+
errors: ["Method calls without arguments inside of 'v-on' directives must not have parentheses."],
69+
options: ['never']
70+
}
71+
]
72+
})

0 commit comments

Comments
 (0)