@@ -61,7 +61,9 @@ module.exports = {
61
61
maxItems : 2
62
62
}
63
63
]
64
- }
64
+ } ,
65
+
66
+ fixable : "code"
65
67
} ,
66
68
67
69
create ( context ) {
@@ -74,7 +76,8 @@ module.exports = {
74
76
MESSAGE_UNNECESSARY = "Unnecessarily quoted property '{{property}}' found." ,
75
77
MESSAGE_UNQUOTED = "Unquoted property '{{property}}' found." ,
76
78
MESSAGE_NUMERIC = "Unquoted number literal '{{property}}' used as key." ,
77
- MESSAGE_RESERVED = "Unquoted reserved word '{{property}}' used as key." ;
79
+ MESSAGE_RESERVED = "Unquoted reserved word '{{property}}' used as key." ,
80
+ sourceCode = context . getSourceCode ( ) ;
78
81
79
82
80
83
/**
@@ -100,6 +103,31 @@ module.exports = {
100
103
( tokens [ 0 ] . type === "Numeric" && ! skipNumberLiterals && String ( + tokens [ 0 ] . value ) === tokens [ 0 ] . value ) ) ;
101
104
}
102
105
106
+ /**
107
+ * Returns a string representation of a property node with quotes removed
108
+ * @param {ASTNode } key Key AST Node, which may or may not be quoted
109
+ * @returns {string } A replacement string for this property
110
+ */
111
+ function getUnquotedKey ( key ) {
112
+ return key . type === "Identifier" ? key . name : key . value ;
113
+ }
114
+
115
+ /**
116
+ * Returns a string representation of a property node with quotes added
117
+ * @param {ASTNode } key Key AST Node, which may or may not be quoted
118
+ * @returns {string } A replacement string for this property
119
+ */
120
+ function getQuotedKey ( key ) {
121
+ if ( key . type === "Literal" && typeof key . value === "string" ) {
122
+
123
+ // If the key is already a string literal, don't replace the quotes with double quotes.
124
+ return sourceCode . getText ( key ) ;
125
+ }
126
+
127
+ // Otherwise, the key is either an identifier or a number literal.
128
+ return `"${ key . type === "Identifier" ? key . name : key . value } "` ;
129
+ }
130
+
103
131
/**
104
132
* Ensures that a property's key is quoted only when necessary
105
133
* @param {ASTNode } node Property AST node
@@ -131,12 +159,27 @@ module.exports = {
131
159
}
132
160
133
161
if ( CHECK_UNNECESSARY && areQuotesRedundant ( key . value , tokens , NUMBERS ) ) {
134
- context . report ( node , MESSAGE_UNNECESSARY , { property : key . value } ) ;
162
+ context . report ( {
163
+ node,
164
+ message : MESSAGE_UNNECESSARY ,
165
+ data : { property : key . value } ,
166
+ fix : fixer => fixer . replaceText ( key , getUnquotedKey ( key ) )
167
+ } ) ;
135
168
}
136
169
} else if ( KEYWORDS && key . type === "Identifier" && isKeyword ( key . name ) ) {
137
- context . report ( node , MESSAGE_RESERVED , { property : key . name } ) ;
170
+ context . report ( {
171
+ node,
172
+ message : MESSAGE_RESERVED ,
173
+ data : { property : key . name } ,
174
+ fix : fixer => fixer . replaceText ( key , getQuotedKey ( key ) )
175
+ } ) ;
138
176
} else if ( NUMBERS && key . type === "Literal" && typeof key . value === "number" ) {
139
- context . report ( node , MESSAGE_NUMERIC , { property : key . value } ) ;
177
+ context . report ( {
178
+ node,
179
+ message : MESSAGE_NUMERIC ,
180
+ data : { property : key . value } ,
181
+ fix : fixer => fixer . replaceText ( key , getQuotedKey ( key ) )
182
+ } ) ;
140
183
}
141
184
}
142
185
@@ -149,8 +192,11 @@ module.exports = {
149
192
const key = node . key ;
150
193
151
194
if ( ! node . method && ! node . computed && ! node . shorthand && ! ( key . type === "Literal" && typeof key . value === "string" ) ) {
152
- context . report ( node , MESSAGE_UNQUOTED , {
153
- property : key . name || key . value
195
+ context . report ( {
196
+ node,
197
+ message : MESSAGE_UNQUOTED ,
198
+ data : { property : key . name || key . value } ,
199
+ fix : fixer => fixer . replaceText ( key , getQuotedKey ( key ) )
154
200
} ) ;
155
201
}
156
202
}
@@ -162,8 +208,9 @@ module.exports = {
162
208
* @returns {void }
163
209
*/
164
210
function checkConsistency ( node , checkQuotesRedundancy ) {
165
- let quotes = false ,
166
- lackOfQuotes = false ,
211
+ const quotedProps = [ ] ,
212
+ unquotedProps = [ ] ;
213
+ let keywordKeyName = null ,
167
214
necessaryQuotes = false ;
168
215
169
216
node . properties . forEach ( function ( property ) {
@@ -176,7 +223,7 @@ module.exports = {
176
223
177
224
if ( key . type === "Literal" && typeof key . value === "string" ) {
178
225
179
- quotes = true ;
226
+ quotedProps . push ( property ) ;
180
227
181
228
if ( checkQuotesRedundancy ) {
182
229
try {
@@ -189,21 +236,40 @@ module.exports = {
189
236
necessaryQuotes = necessaryQuotes || ! areQuotesRedundant ( key . value , tokens ) || KEYWORDS && isKeyword ( tokens [ 0 ] . value ) ;
190
237
}
191
238
} else if ( KEYWORDS && checkQuotesRedundancy && key . type === "Identifier" && isKeyword ( key . name ) ) {
239
+ unquotedProps . push ( property ) ;
192
240
necessaryQuotes = true ;
193
- context . report ( node , "Properties should be quoted as '{{property}}' is a reserved word." , { property : key . name } ) ;
241
+ keywordKeyName = key . name ;
194
242
} else {
195
- lackOfQuotes = true ;
196
- }
197
-
198
- if ( quotes && lackOfQuotes ) {
199
- context . report ( node , "Inconsistently quoted property '{{key}}' found." , {
200
- key : key . name || key . value
201
- } ) ;
243
+ unquotedProps . push ( property ) ;
202
244
}
203
245
} ) ;
204
246
205
- if ( checkQuotesRedundancy && quotes && ! necessaryQuotes ) {
206
- context . report ( node , "Properties shouldn't be quoted as all quotes are redundant." ) ;
247
+ if ( checkQuotesRedundancy && quotedProps . length && ! necessaryQuotes ) {
248
+ quotedProps . forEach ( property => {
249
+ context . report ( {
250
+ node : property ,
251
+ message : "Properties shouldn't be quoted as all quotes are redundant." ,
252
+ fix : fixer => fixer . replaceText ( property . key , getUnquotedKey ( property . key ) )
253
+ } ) ;
254
+ } ) ;
255
+ } else if ( unquotedProps . length && keywordKeyName ) {
256
+ unquotedProps . forEach ( property => {
257
+ context . report ( {
258
+ node : property ,
259
+ message : "Properties should be quoted as '{{property}}' is a reserved word." ,
260
+ data : { property : keywordKeyName } ,
261
+ fix : fixer => fixer . replaceText ( property . key , getQuotedKey ( property . key ) )
262
+ } ) ;
263
+ } ) ;
264
+ } else if ( quotedProps . length && unquotedProps . length ) {
265
+ unquotedProps . forEach ( property => {
266
+ context . report ( {
267
+ node : property ,
268
+ message : "Inconsistently quoted property '{{key}}' found." ,
269
+ data : { key : property . key . name || property . key . value } ,
270
+ fix : fixer => fixer . replaceText ( property . key , getQuotedKey ( property . key ) )
271
+ } ) ;
272
+ } ) ;
207
273
}
208
274
}
209
275
0 commit comments