4
4
*/
5
5
'use strict'
6
6
7
+ const { toRegExp } = require ( '../utils/regexp' )
7
8
const utils = require ( '../utils' )
8
9
10
+ /**
11
+ * @typedef { 'always' | 'never' } PreferOption
12
+ */
13
+
14
+ /**
15
+ * @param {VDirective | VAttribute } node
16
+ * @returns {string | null }
17
+ */
18
+ function getAttributeName ( node ) {
19
+ if ( ! node . directive ) {
20
+ return node . key . rawName
21
+ }
22
+
23
+ if (
24
+ ( node . key . name . name === 'bind' || node . key . name . name === 'model' ) &&
25
+ node . key . argument &&
26
+ node . key . argument . type === 'VIdentifier'
27
+ ) {
28
+ return node . key . argument . rawName
29
+ }
30
+
31
+ return null
32
+ }
33
+ /**
34
+ * @param {VAttribute | VDirective } node
35
+ * @param {boolean } isExcepted
36
+ * @param {PreferOption } option
37
+ */
38
+ function shouldConvertToLongForm ( node , isExcepted , option ) {
39
+ return (
40
+ ! node . directive &&
41
+ ! node . value &&
42
+ ( option === 'always' ? isExcepted : ! isExcepted )
43
+ )
44
+ }
45
+
46
+ /**
47
+ * @param {VAttribute | VDirective } node
48
+ * @param {boolean } isExcepted
49
+ * @param {PreferOption } option
50
+ */
51
+ function shouldConvertToShortForm ( node , isExcepted , option ) {
52
+ const isLiteralTrue =
53
+ node . directive &&
54
+ node . value ?. expression ?. type === 'Literal' &&
55
+ node . value . expression . value === true &&
56
+ Boolean ( node . key . argument )
57
+
58
+ return isLiteralTrue && ( option === 'always' ? ! isExcepted : isExcepted )
59
+ }
60
+
9
61
module . exports = {
10
62
meta : {
11
63
type : 'suggestion' ,
@@ -17,7 +69,20 @@ module.exports = {
17
69
} ,
18
70
fixable : null ,
19
71
hasSuggestions : true ,
20
- schema : [ { enum : [ 'always' , 'never' ] } ] ,
72
+ schema : [
73
+ { enum : [ 'always' , 'never' ] } ,
74
+ {
75
+ type : 'object' ,
76
+ properties : {
77
+ except : {
78
+ type : 'array' ,
79
+ items : { type : 'string' } ,
80
+ uniqueItems : true
81
+ }
82
+ } ,
83
+ additionalProperties : false
84
+ }
85
+ ] ,
21
86
messages : {
22
87
expectShort :
23
88
"Boolean prop with 'true' value should be written in shorthand form." ,
@@ -34,68 +99,81 @@ module.exports = {
34
99
create ( context ) {
35
100
/** @type {'always' | 'never' } */
36
101
const option = context . options [ 0 ] || 'always'
102
+ /** @type {RegExp[] } */
103
+ const exceptReg = ( context . options [ 1 ] ?. except || [ ] ) . map ( toRegExp )
104
+
105
+ /**
106
+ * @param {VAttribute | VDirective } node
107
+ * @param {string } messageId
108
+ * @param {string } longVuePropText
109
+ * @param {string } longHtmlAttrText
110
+ */
111
+ function reportLongForm (
112
+ node ,
113
+ messageId ,
114
+ longVuePropText ,
115
+ longHtmlAttrText
116
+ ) {
117
+ context . report ( {
118
+ node,
119
+ messageId,
120
+ suggest : [
121
+ {
122
+ messageId : 'rewriteIntoLongVueProp' ,
123
+ fix : ( fixer ) => fixer . replaceText ( node , longVuePropText )
124
+ } ,
125
+ {
126
+ messageId : 'rewriteIntoLongHtmlAttr' ,
127
+ fix : ( fixer ) => fixer . replaceText ( node , longHtmlAttrText )
128
+ }
129
+ ]
130
+ } )
131
+ }
132
+
133
+ /**
134
+ * @param {VAttribute | VDirective } node
135
+ * @param {string } messageId
136
+ * @param {string } shortFormText
137
+ */
138
+ function reportShortForm ( node , messageId , shortFormText ) {
139
+ context . report ( {
140
+ node,
141
+ messageId,
142
+ suggest : [
143
+ {
144
+ messageId : 'rewriteIntoShort' ,
145
+ fix : ( fixer ) => fixer . replaceText ( node , shortFormText )
146
+ }
147
+ ]
148
+ } )
149
+ }
37
150
38
151
return utils . defineTemplateBodyVisitor ( context , {
39
152
VAttribute ( node ) {
40
- if ( ! utils . isCustomComponent ( node . parent . parent ) ) {
41
- return
42
- }
43
-
44
- if ( option === 'never' && ! node . directive && ! node . value ) {
45
- context . report ( {
46
- node,
47
- messageId : 'expectLong' ,
48
- suggest : [
49
- {
50
- messageId : 'rewriteIntoLongVueProp' ,
51
- fix : ( fixer ) =>
52
- fixer . replaceText ( node , `:${ node . key . rawName } ="true"` )
53
- } ,
54
- {
55
- messageId : 'rewriteIntoLongHtmlAttr' ,
56
- fix : ( fixer ) =>
57
- fixer . replaceText (
58
- node ,
59
- `${ node . key . rawName } ="${ node . key . rawName } "`
60
- )
61
- }
62
- ]
63
- } )
64
- return
65
- }
153
+ if ( ! utils . isCustomComponent ( node . parent . parent ) ) return
66
154
67
- if ( option !== 'always' ) {
68
- return
69
- }
155
+ const name = getAttributeName ( node )
156
+ if ( name === null ) return
70
157
71
- if (
72
- ! node . directive ||
73
- ! node . value ||
74
- ! node . value . expression ||
75
- node . value . expression . type !== 'Literal' ||
76
- node . value . expression . value !== true
77
- ) {
78
- return
79
- }
158
+ const isExcepted = exceptReg . some ( ( re ) => re . test ( name ) )
80
159
81
- const { argument } = node . key
82
- if ( ! argument ) {
83
- return
160
+ if ( shouldConvertToLongForm ( node , isExcepted , option ) ) {
161
+ const key = /** @type {VIdentifier } */ ( node . key )
162
+ reportLongForm (
163
+ node ,
164
+ 'expectLong' ,
165
+ `:${ key . rawName } ="true"` ,
166
+ `${ key . rawName } ="${ key . rawName } "`
167
+ )
168
+ } else if ( shouldConvertToShortForm ( node , isExcepted , option ) ) {
169
+ const directiveKey = /** @type {VDirectiveKey } */ ( node . key )
170
+ if (
171
+ directiveKey . argument &&
172
+ directiveKey . argument . type === 'VIdentifier'
173
+ ) {
174
+ reportShortForm ( node , 'expectShort' , directiveKey . argument . rawName )
175
+ }
84
176
}
85
-
86
- context . report ( {
87
- node,
88
- messageId : 'expectShort' ,
89
- suggest : [
90
- {
91
- messageId : 'rewriteIntoShort' ,
92
- fix : ( fixer ) => {
93
- const sourceCode = context . getSourceCode ( )
94
- return fixer . replaceText ( node , sourceCode . getText ( argument ) )
95
- }
96
- }
97
- ]
98
- } )
99
177
}
100
178
} )
101
179
}
0 commit comments