9
9
10
10
const utils = require ( '../utils' )
11
11
12
+ /**
13
+ * @typedef {import('vue-eslint-parser').AST.VOnExpression } VOnExpression
14
+ * @typedef {import('vue-eslint-parser').AST.Token } Token
15
+ * @typedef {import('vue-eslint-parser').AST.ESLintExpressionStatement } ExpressionStatement
16
+ * @typedef {import('vue-eslint-parser').AST.ESLintCallExpression } CallExpression
17
+ */
18
+
12
19
// ------------------------------------------------------------------------------
13
20
// Helpers
14
21
// ------------------------------------------------------------------------------
15
22
16
23
/**
17
- * Check whether the given token is a left parenthesis .
24
+ * Check whether the given token is a quote .
18
25
* @param {Token } token The token to check.
19
- * @returns {boolean } `true` if the token is a left parenthesis .
26
+ * @returns {boolean } `true` if the token is a quote .
20
27
*/
21
- function isLeftParen ( token ) {
22
- return token != null && token . type === 'Punctuator' && token . value === '('
28
+ function isQuote ( token ) {
29
+ return (
30
+ token != null &&
31
+ token . type === 'Punctuator' &&
32
+ ( token . value === '"' || token . value === "'" )
33
+ )
23
34
}
24
35
25
36
// ------------------------------------------------------------------------------
@@ -36,64 +47,125 @@ module.exports = {
36
47
url : 'https://eslint.vuejs.org/rules/v-on-function-call.html'
37
48
} ,
38
49
fixable : 'code' ,
39
- schema : [ { enum : [ 'always' , 'never' ] } ]
50
+ schema : [
51
+ { enum : [ 'always' , 'never' ] } ,
52
+ {
53
+ type : 'object' ,
54
+ properties : {
55
+ ignoreIncludesComment : {
56
+ type : 'boolean'
57
+ }
58
+ } ,
59
+ additionalProperties : false
60
+ }
61
+ ]
40
62
} ,
41
63
42
64
create ( context ) {
43
65
const always = context . options [ 0 ] === 'always'
44
66
67
+ /**
68
+ * @param {VOnExpression } node
69
+ * @returns {CallExpression | null }
70
+ */
71
+ function getInvalidNeverCallExpression ( node ) {
72
+ /** @type {ExpressionStatement } */
73
+ let exprStatement
74
+ let body = node . body
75
+ while ( true ) {
76
+ const statements = body . filter ( ( st ) => st . type !== 'EmptyStatement' )
77
+ if ( statements . length !== 1 ) {
78
+ return null
79
+ }
80
+ const statement = statements [ 0 ]
81
+ if ( statement . type === 'ExpressionStatement' ) {
82
+ exprStatement = statement
83
+ break
84
+ }
85
+ if ( statement . type === 'BlockStatement' ) {
86
+ body = statement . body
87
+ continue
88
+ }
89
+ return null
90
+ }
91
+ const expression = exprStatement . expression
92
+ if ( expression . type !== 'CallExpression' || expression . arguments . length ) {
93
+ return null
94
+ }
95
+ const callee = expression . callee
96
+ if ( callee . type !== 'Identifier' ) {
97
+ return null
98
+ }
99
+ return expression
100
+ }
101
+
45
102
return utils . defineTemplateBodyVisitor ( context , {
46
- "VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer > Identifier" (
47
- node
48
- ) {
49
- if ( ! always ) return
50
- context . report ( {
51
- node,
52
- loc : node . loc ,
53
- message :
54
- "Method calls inside of 'v-on' directives must have parentheses."
55
- } )
56
- } ,
103
+ ...( always
104
+ ? {
105
+ "VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer > Identifier" (
106
+ node
107
+ ) {
108
+ context . report ( {
109
+ node,
110
+ message :
111
+ "Method calls inside of 'v-on' directives must have parentheses."
112
+ } )
113
+ }
114
+ }
115
+ : {
116
+ /** @param {VOnExpression } node */
117
+ "VAttribute[directive=true][key.name.name='on'][key.argument!=null] VOnExpression" (
118
+ node
119
+ ) {
120
+ const expression = getInvalidNeverCallExpression ( node )
121
+ if ( ! expression ) {
122
+ return
123
+ }
124
+ const option = context . options [ 1 ] || { }
125
+ const ignoreIncludesComment = option . ignoreIncludesComment
57
126
58
- "VAttribute[directive=true][key.name.name='on'][key.argument!=null] VOnExpression > ExpressionStatement > CallExpression" (
59
- node
60
- ) {
61
- if (
62
- ! always &&
63
- node . arguments . length === 0 &&
64
- node . callee . type === 'Identifier'
65
- ) {
66
- context . report ( {
67
- node,
68
- loc : node . loc ,
69
- message :
70
- "Method calls without arguments inside of 'v-on' directives must not have parentheses." ,
71
- fix : ( fixer ) => {
72
127
const tokenStore = context . parserServices . getTemplateBodyTokenStore ( )
73
- const rightToken = tokenStore . getLastToken ( node )
74
- const leftToken = tokenStore . getTokenAfter (
75
- node . callee ,
76
- isLeftParen
77
- )
78
- const tokens = tokenStore . getTokensBetween (
79
- leftToken ,
80
- rightToken ,
81
- { includeComments : true }
128
+ /** @type {Token[] } */
129
+ const tokens = tokenStore . getTokens ( node . parent , {
130
+ includeComments : true
131
+ } )
132
+ let leftQuote
133
+ let rightQuote
134
+ if ( isQuote ( tokens [ 0 ] ) ) {
135
+ leftQuote = tokens . shift ( )
136
+ rightQuote = tokens . pop ( )
137
+ }
138
+
139
+ const hasComment = tokens . some (
140
+ ( token ) => token . type === 'Block' || token . type === 'Line'
82
141
)
83
142
84
- if ( tokens . length ) {
85
- // The comment is included and cannot be fixed.
86
- return null
143
+ if ( ignoreIncludesComment && hasComment ) {
144
+ return
87
145
}
88
146
89
- return fixer . removeRange ( [
90
- leftToken . range [ 0 ] ,
91
- rightToken . range [ 1 ]
92
- ] )
147
+ context . report ( {
148
+ node : expression ,
149
+ message :
150
+ "Method calls without arguments inside of 'v-on' directives must not have parentheses." ,
151
+ fix : hasComment
152
+ ? null /* The comment is included and cannot be fixed. */
153
+ : ( fixer ) => {
154
+ const range = leftQuote
155
+ ? [ leftQuote . range [ 1 ] , rightQuote . range [ 0 ] ]
156
+ : [
157
+ tokens [ 0 ] . range [ 0 ] ,
158
+ tokens [ tokens . length - 1 ] . range [ 1 ]
159
+ ]
160
+
161
+ return fixer . replaceTextRange (
162
+ range ,
163
+ context . getSourceCode ( ) . getText ( expression . callee )
164
+ )
165
+ }
166
+ } )
93
167
}
94
168
} )
95
- }
96
- }
97
169
} )
98
170
}
99
171
}
0 commit comments