Skip to content

Commit 917240b

Browse files
mdjermanovicljharb
authored andcommitted
[Fix] no-invalid-html-attribute: substitute placeholders in suggestion messages
1 parent 4e3f2ae commit 917240b

File tree

4 files changed

+83
-100
lines changed

4 files changed

+83
-100
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1414
[#3724]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3724
1515
[#3694]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3694
1616

17+
### Fixed
18+
* [`no-invalid-html-attribute`]: substitute placeholders in suggestion messages ([#3759][] @mdjermanovic)
19+
1720
## [7.34.4] - 2024.07.13
1821

1922
### Fixed

lib/rules/no-invalid-html-attribute.js

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
const matchAll = require('string.prototype.matchall');
99
const docsUrl = require('../util/docsUrl');
1010
const report = require('../util/report');
11-
const getMessageData = require('../util/message');
1211

1312
// ------------------------------------------------------------------------------
1413
// Rule Definition
@@ -224,6 +223,7 @@ const COMPONENT_ATTRIBUTE_MAP = new Map([
224223
['rel', new Set(['link', 'a', 'area', 'form'])],
225224
]);
226225

226+
/* eslint-disable eslint-plugin/no-unused-message-ids -- false positives, these messageIds are used */
227227
const messages = {
228228
emptyIsMeaningless: 'An empty “{{attributeName}}” attribute is meaningless.',
229229
neverValid: '“{{reportingValue}}” is never a valid “{{attributeName}}” attribute value.',
@@ -264,15 +264,11 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
264264
report(context, messages.onlyStrings, 'onlyStrings', {
265265
node,
266266
data,
267-
suggest: [
268-
Object.assign(
269-
getMessageData('suggestRemoveNonString', messages.suggestRemoveNonString),
270-
{
271-
data,
272-
fix(fixer) { return fixer.remove(parentNode); },
273-
}
274-
),
275-
],
267+
suggest: [{
268+
messageId: 'suggestRemoveNonString',
269+
data,
270+
fix(fixer) { return fixer.remove(parentNode); },
271+
}],
276272
});
277273
return;
278274
}
@@ -283,15 +279,11 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
283279
report(context, messages.noEmpty, 'noEmpty', {
284280
node,
285281
data,
286-
suggest: [
287-
Object.assign(
288-
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty),
289-
{
290-
data,
291-
fix(fixer) { return fixer.remove(node.parent); },
292-
}
293-
),
294-
],
282+
suggest: [{
283+
messageId: 'suggestRemoveEmpty',
284+
data,
285+
fix(fixer) { return fixer.remove(node.parent); },
286+
}],
295287
});
296288
return;
297289
}
@@ -307,18 +299,16 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
307299
reportingValue,
308300
};
309301

302+
const suggest = [{
303+
messageId: 'suggestRemoveInvalid',
304+
data,
305+
fix(fixer) { return fixer.removeRange(singlePart.range); },
306+
}];
307+
310308
report(context, messages.neverValid, 'neverValid', {
311309
node,
312310
data,
313-
suggest: [
314-
Object.assign(
315-
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid),
316-
{
317-
data,
318-
fix(fixer) { return fixer.removeRange(singlePart.range); },
319-
}
320-
),
321-
],
311+
suggest,
322312
});
323313
} else if (!allowedTags.has(parentNodeName)) {
324314
const data = {
@@ -327,18 +317,16 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
327317
elementName: parentNodeName,
328318
};
329319

320+
const suggest = [{
321+
messageId: 'suggestRemoveInvalid',
322+
data,
323+
fix(fixer) { return fixer.removeRange(singlePart.range); },
324+
}];
325+
330326
report(context, messages.notValidFor, 'notValidFor', {
331327
node,
332328
data,
333-
suggest: [
334-
Object.assign(
335-
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid),
336-
{
337-
data,
338-
fix(fixer) { return fixer.removeRange(singlePart.range); },
339-
}
340-
),
341-
],
329+
suggest,
342330
});
343331
}
344332
}
@@ -375,33 +363,27 @@ function checkLiteralValueNode(context, attributeName, node, parentNode, parentN
375363

376364
const whitespaceParts = splitIntoRangedParts(node, /(\s+)/g);
377365
for (const whitespacePart of whitespaceParts) {
366+
const data = { attributeName };
367+
378368
if (whitespacePart.range[0] === (node.range[0] + 1) || whitespacePart.range[1] === (node.range[1] - 1)) {
379369
report(context, messages.spaceDelimited, 'spaceDelimited', {
380370
node,
381-
data: { attributeName },
382-
suggest: [
383-
Object.assign(
384-
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces),
385-
{
386-
data: { attributeName },
387-
fix(fixer) { return fixer.removeRange(whitespacePart.range); },
388-
}
389-
),
390-
],
371+
data,
372+
suggest: [{
373+
messageId: 'suggestRemoveWhitespaces',
374+
data,
375+
fix(fixer) { return fixer.removeRange(whitespacePart.range); },
376+
}],
391377
});
392378
} else if (whitespacePart.value !== '\u0020') {
393379
report(context, messages.spaceDelimited, 'spaceDelimited', {
394380
node,
395-
data: { attributeName },
396-
suggest: [
397-
Object.assign(
398-
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces),
399-
{
400-
data: { attributeName },
401-
fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); },
402-
}
403-
),
404-
],
381+
data,
382+
suggest: [{
383+
messageId: 'suggestRemoveWhitespaces',
384+
data,
385+
fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); },
386+
}],
405387
});
406388
}
407389
}
@@ -426,15 +408,11 @@ function checkAttribute(context, node) {
426408
report(context, messages.onlyMeaningfulFor, 'onlyMeaningfulFor', {
427409
node: node.name,
428410
data,
429-
suggest: [
430-
Object.assign(
431-
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
432-
{
433-
data,
434-
fix(fixer) { return fixer.remove(node); },
435-
}
436-
),
437-
],
411+
suggest: [{
412+
messageId: 'suggestRemoveDefault',
413+
data,
414+
fix(fixer) { return fixer.remove(node); },
415+
}],
438416
});
439417
return;
440418
}
@@ -447,12 +425,11 @@ function checkAttribute(context, node) {
447425
report(context, messages.emptyIsMeaningless, 'emptyIsMeaningless', {
448426
node: node.name,
449427
data,
450-
suggest: [
451-
Object.assign(
452-
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty),
453-
{ data, fix }
454-
),
455-
],
428+
suggest: [{
429+
messageId: 'suggestRemoveEmpty',
430+
data,
431+
fix,
432+
}],
456433
});
457434
return;
458435
}
@@ -475,25 +452,23 @@ function checkAttribute(context, node) {
475452
report(context, messages.onlyStrings, 'onlyStrings', {
476453
node: node.value,
477454
data,
478-
suggest: [
479-
Object.assign(
480-
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
481-
{ data, fix }
482-
),
483-
],
455+
suggest: [{
456+
messageId: 'suggestRemoveDefault',
457+
data,
458+
fix,
459+
}],
484460
});
485461
} else if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
486462
const data = { attributeName: attribute };
487463

488464
report(context, messages.onlyStrings, 'onlyStrings', {
489465
node: node.value,
490466
data,
491-
suggest: [
492-
Object.assign(
493-
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault),
494-
{ data, fix }
495-
),
496-
],
467+
suggest: [{
468+
messageId: 'suggestRemoveDefault',
469+
data,
470+
fix,
471+
}],
497472
});
498473
}
499474
}
@@ -523,15 +498,11 @@ function checkPropValidValue(context, node, value, attribute) {
523498
report(context, messages.neverValid, 'neverValid', {
524499
node: value,
525500
data,
526-
suggest: [
527-
Object.assign(
528-
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid),
529-
{
530-
data,
531-
fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); },
532-
}
533-
),
534-
],
501+
suggest: [{
502+
messageId: 'suggestRemoveInvalid',
503+
data,
504+
fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); },
505+
}],
535506
});
536507
} else if (!validTagSet.has(node.arguments[0].value)) {
537508
report(context, messages.notValidFor, 'notValidFor', {

tests/helpers/parsers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ const parsers = {
105105
&& {
106106
errors: testObject.errors.map(
107107
(errorObject) => {
108-
const nextSuggestions = errorObject.suggestions && {
108+
const nextSuggestions = errorObject.suggestions && typeof errorObject.suggestions !== 'number' && {
109109
suggestions: errorObject.suggestions.map((suggestion) => Object.assign({}, suggestion, {
110110
output: suggestion.output + extraComment,
111111
})),

tests/lib/rules/no-invalid-html-attribute.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// Requirements
1010
// ------------------------------------------------------------------------------
1111

12+
const semver = require('semver');
13+
const eslintPkg = require('eslint/package.json');
1214
const RuleTester = require('../../helpers/ruleTester');
1315
const rule = require('../../../lib/rules/no-invalid-html-attribute');
1416
const parsers = require('../../helpers/parsers');
@@ -488,12 +490,19 @@ ruleTester.run('no-invalid-html-attribute', rule, {
488490
attributeName: 'rel',
489491
reportingValue: 1,
490492
},
491-
// suggestions: [
492-
// {
493-
// messageId: 'suggestRemoveDefault',
494-
// output: 'React.createElement("a", { })',
495-
// },
496-
// ],
493+
494+
// FIXME: this suggestion produces invalid code
495+
// In ESLint > 9, RuleTester doesn't allow suggestions with parsing errors.
496+
suggestions: semver.major(eslintPkg.version) < 9
497+
? [
498+
{
499+
messageId: 'suggestRemoveInvalid',
500+
data: { reportingValue: '1' },
501+
output: 'React.createElement("a", { rel: })',
502+
},
503+
]
504+
: 1,
505+
497506
type: 'Literal',
498507
},
499508
],

0 commit comments

Comments
 (0)