8
8
const matchAll = require ( 'string.prototype.matchall' ) ;
9
9
const docsUrl = require ( '../util/docsUrl' ) ;
10
10
const report = require ( '../util/report' ) ;
11
+ const getMessageData = require ( '../util/message' ) ;
11
12
12
13
// ------------------------------------------------------------------------------
13
14
// Rule Definition
@@ -232,6 +233,11 @@ const messages = {
232
233
onlyMeaningfulFor : 'The ”{{attributeName}}“ attribute only has meaning on the tags: {{tagNames}}' ,
233
234
onlyStrings : '“{{attributeName}}” attribute only supports strings.' ,
234
235
spaceDelimited : '”{{attributeName}}“ attribute values should be space delimited.' ,
236
+ suggestRemoveDefault : '"remove {{attributeName}}"' ,
237
+ suggestRemoveEmpty : '"remove empty attribute {{attributeName}}"' ,
238
+ suggestRemoveInvalid : '“remove invalid attribute {{reportingValue}}”' ,
239
+ suggestRemoveWhitespaces : 'remove whitespaces in “{{reportingValue}}”' ,
240
+ suggestRemoveNonString : 'remove non-string value in “{{reportingValue}}”' ,
235
241
} ;
236
242
237
243
function splitIntoRangedParts ( node , regex ) {
@@ -254,9 +260,12 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
254
260
report ( context , messages . onlyStrings , 'onlyStrings' , {
255
261
node,
256
262
data : { attributeName } ,
257
- fix ( fixer ) {
258
- return fixer . remove ( parentNode ) ;
259
- } ,
263
+ suggest : [
264
+ Object . assign (
265
+ getMessageData ( 'suggestRemoveNonString' , messages . suggestRemoveNonString ) ,
266
+ { fix ( fixer ) { return fixer . remove ( parentNode ) ; } }
267
+ ) ,
268
+ ] ,
260
269
} ) ;
261
270
return ;
262
271
}
@@ -265,9 +274,12 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
265
274
report ( context , messages . noEmpty , 'noEmpty' , {
266
275
node,
267
276
data : { attributeName } ,
268
- fix ( fixer ) {
269
- return fixer . remove ( parentNode ) ;
270
- } ,
277
+ suggest : [
278
+ Object . assign (
279
+ getMessageData ( 'suggestRemoveEmpty' , messages . suggestRemoveEmpty ) ,
280
+ { fix ( fixer ) { return fixer . remove ( node . parent ) ; } }
281
+ ) ,
282
+ ] ,
271
283
} ) ;
272
284
return ;
273
285
}
@@ -276,16 +288,23 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
276
288
for ( const singlePart of singleAttributeParts ) {
277
289
const allowedTags = VALID_VALUES . get ( attributeName ) . get ( singlePart . value ) ;
278
290
const reportingValue = singlePart . reportingValue ;
291
+
292
+ const suggest = [
293
+ Object . assign (
294
+ getMessageData ( 'suggestRemoveInvalid' , messages . suggestRemoveInvalid ) ,
295
+ { fix ( fixer ) { return fixer . removeRange ( singlePart . range ) ; } }
296
+ ) ,
297
+ ] ;
298
+
279
299
if ( ! allowedTags ) {
300
+ const data = {
301
+ attributeName,
302
+ reportingValue,
303
+ } ;
280
304
report ( context , messages . neverValid , 'neverValid' , {
281
305
node,
282
- data : {
283
- attributeName,
284
- reportingValue,
285
- } ,
286
- fix ( fixer ) {
287
- return fixer . removeRange ( singlePart . range ) ;
288
- } ,
306
+ data,
307
+ suggest,
289
308
} ) ;
290
309
} else if ( ! allowedTags . has ( parentNodeName ) ) {
291
310
report ( context , messages . notValidFor , 'notValidFor' , {
@@ -295,9 +314,7 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
295
314
reportingValue,
296
315
elementName : parentNodeName ,
297
316
} ,
298
- fix ( fixer ) {
299
- return fixer . removeRange ( singlePart . range ) ;
300
- } ,
317
+ suggest,
301
318
} ) ;
302
319
}
303
320
}
@@ -324,6 +341,7 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
324
341
secondValue,
325
342
missingValue : Array . from ( siblings ) . join ( ', ' ) ,
326
343
} ,
344
+ suggest : false ,
327
345
} ) ;
328
346
}
329
347
}
@@ -337,17 +355,23 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
337
355
report ( context , messages . spaceDelimited , 'spaceDelimited' , {
338
356
node,
339
357
data : { attributeName } ,
340
- fix ( fixer ) {
341
- return fixer . removeRange ( whitespacePart . range ) ;
342
- } ,
358
+ suggest : [
359
+ Object . assign (
360
+ getMessageData ( 'suggestRemoveWhitespaces' , messages . suggestRemoveWhitespaces ) ,
361
+ { fix ( fixer ) { return fixer . removeRange ( whitespacePart . range ) ; } }
362
+ ) ,
363
+ ] ,
343
364
} ) ;
344
365
} else if ( whitespacePart . value !== '\u0020' ) {
345
366
report ( context , messages . spaceDelimited , 'spaceDelimited' , {
346
367
node,
347
368
data : { attributeName } ,
348
- fix ( fixer ) {
349
- return fixer . replaceTextRange ( whitespacePart . range , '\u0020' ) ;
350
- } ,
369
+ suggest : [
370
+ Object . assign (
371
+ getMessageData ( 'suggestRemoveWhitespaces' , messages . suggestRemoveWhitespaces ) ,
372
+ { fix ( fixer ) { return fixer . replaceTextRange ( whitespacePart . range , '\u0020' ) ; } }
373
+ ) ,
374
+ ] ,
351
375
} ) ;
352
376
}
353
377
}
@@ -358,10 +382,6 @@ const DEFAULT_ATTRIBUTES = ['rel'];
358
382
function checkAttribute ( context , node ) {
359
383
const attribute = node . name . name ;
360
384
361
- function fix ( fixer ) {
362
- return fixer . remove ( node ) ;
363
- }
364
-
365
385
const parentNodeName = node . parent . name . name ;
366
386
if ( ! COMPONENT_ATTRIBUTE_MAP . has ( attribute ) || ! COMPONENT_ATTRIBUTE_MAP . get ( attribute ) . has ( parentNodeName ) ) {
367
387
const tagNames = Array . from (
@@ -374,16 +394,28 @@ function checkAttribute(context, node) {
374
394
attributeName : attribute ,
375
395
tagNames,
376
396
} ,
377
- fix,
397
+ suggest : [
398
+ Object . assign (
399
+ getMessageData ( 'suggestRemoveDefault' , messages . suggestRemoveDefault ) ,
400
+ { fix ( fixer ) { return fixer . remove ( node ) ; } }
401
+ ) ,
402
+ ] ,
378
403
} ) ;
379
404
return ;
380
405
}
381
406
407
+ function fix ( fixer ) { return fixer . remove ( node ) ; }
408
+
382
409
if ( ! node . value ) {
383
410
report ( context , messages . emptyIsMeaningless , 'emptyIsMeaningless' , {
384
411
node,
385
412
data : { attributeName : attribute } ,
386
- fix,
413
+ suggest : [
414
+ Object . assign (
415
+ getMessageData ( 'suggestRemoveEmpty' , messages . suggestRemoveEmpty ) ,
416
+ { fix }
417
+ ) ,
418
+ ] ,
387
419
} ) ;
388
420
return ;
389
421
}
@@ -404,16 +436,23 @@ function checkAttribute(context, node) {
404
436
report ( context , messages . onlyStrings , 'onlyStrings' , {
405
437
node,
406
438
data : { attributeName : attribute } ,
407
- fix,
439
+ suggest : [
440
+ Object . assign (
441
+ getMessageData ( 'suggestRemoveDefault' , messages . suggestRemoveDefault ) ,
442
+ { fix }
443
+ ) ,
444
+ ] ,
408
445
} ) ;
409
- return ;
410
- }
411
-
412
- if ( node . value . expression . type === 'Identifier' && node . value . expression . name === 'undefined' ) {
446
+ } else if ( node . value . expression . type === 'Identifier' && node . value . expression . name === 'undefined' ) {
413
447
report ( context , messages . onlyStrings , 'onlyStrings' , {
414
448
node,
415
449
data : { attributeName : attribute } ,
416
- fix,
450
+ suggest : [
451
+ Object . assign (
452
+ getMessageData ( 'suggestRemoveDefault' , messages . suggestRemoveDefault ) ,
453
+ { fix }
454
+ ) ,
455
+ ] ,
417
456
} ) ;
418
457
}
419
458
}
@@ -441,18 +480,22 @@ function checkPropValidValue(context, node, value, attribute) {
441
480
attributeName : attribute ,
442
481
reportingValue : value . value ,
443
482
} ,
483
+ suggest : [
484
+ Object . assign (
485
+ getMessageData ( 'suggestRemoveInvalid' , messages . suggestRemoveInvalid ) ,
486
+ { fix ( fixer ) { return fixer . replaceText ( value , value . raw . replace ( value . value , '' ) ) ; } }
487
+ ) ,
488
+ ] ,
444
489
} ) ;
445
- return ;
446
- }
447
-
448
- if ( ! validTagSet . has ( node . arguments [ 0 ] . value ) ) {
490
+ } else if ( ! validTagSet . has ( node . arguments [ 0 ] . value ) ) {
449
491
report ( context , messages . notValidFor , 'notValidFor' , {
450
492
node : value ,
451
493
data : {
452
494
attributeName : attribute ,
453
495
reportingValue : value . raw ,
454
496
elementName : node . arguments [ 0 ] . value ,
455
497
} ,
498
+ suggest : false ,
456
499
} ) ;
457
500
}
458
501
}
@@ -493,6 +536,7 @@ function checkCreateProps(context, node, attribute) {
493
536
attributeName : attribute ,
494
537
tagNames,
495
538
} ,
539
+ suggest : false ,
496
540
} ) ;
497
541
498
542
// eslint-disable-next-line no-continue
@@ -505,6 +549,7 @@ function checkCreateProps(context, node, attribute) {
505
549
data : {
506
550
attributeName : attribute ,
507
551
} ,
552
+ suggest : false ,
508
553
} ) ;
509
554
510
555
// eslint-disable-next-line no-continue
@@ -531,7 +576,6 @@ function checkCreateProps(context, node, attribute) {
531
576
532
577
module . exports = {
533
578
meta : {
534
- fixable : 'code' ,
535
579
docs : {
536
580
description : 'Disallow usage of invalid attributes' ,
537
581
category : 'Possible Errors' ,
@@ -545,6 +589,8 @@ module.exports = {
545
589
enum : [ 'rel' ] ,
546
590
} ,
547
591
} ] ,
592
+ type : 'suggestion' ,
593
+ hasSuggestions : true , // eslint-disable-line eslint-plugin/require-meta-has-suggestions
548
594
} ,
549
595
550
596
create ( context ) {
0 commit comments