44 */
55'use strict'
66
7+ const { toRegExp } = require ( '../utils/regexp' )
78const utils = require ( '../utils' )
89
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+
961module . exports = {
1062 meta : {
1163 type : 'suggestion' ,
@@ -17,7 +69,20 @@ module.exports = {
1769 } ,
1870 fixable : null ,
1971 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+ ] ,
2186 messages : {
2287 expectShort :
2388 "Boolean prop with 'true' value should be written in shorthand form." ,
@@ -34,68 +99,81 @@ module.exports = {
3499 create ( context ) {
35100 /** @type {'always' | 'never' } */
36101 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+ }
37150
38151 return utils . defineTemplateBodyVisitor ( context , {
39152 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
66154
67- if ( option !== 'always' ) {
68- return
69- }
155+ const name = getAttributeName ( node )
156+ if ( name === null ) return
70157
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 ) )
80159
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+ }
84176 }
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- } )
99177 }
100178 } )
101179 }
0 commit comments