Skip to content

Commit d18a7fe

Browse files
committed
resolve comments, improved whitespace fixing
1 parent f2ead45 commit d18a7fe

File tree

3 files changed

+161
-81
lines changed

3 files changed

+161
-81
lines changed

docs/rules/jsx-no-invalid-rel.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Prevent usage of invalid `rel` (react/jsx-no-invalid-rel)
22

3-
The JSX elements: `a`, `area`, `link`, or `form` all have a attribute called `rel`. There is is fixed list of values that have any meaning on these tags (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)). To help with minimizing confusion while reading code, only the appropriate values should be on each attribute.
3+
The JSX elements: `a`, `area`, `link`, or `form` all have an attribute called `rel`.
4+
There is is fixed list of values that have any meaning on these tags (see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel)).
5+
To help with minimizing confusion while reading code, only the appropriate values should be on each attribute.
46

57
## Rule Details
68

lib/rules/jsx-no-invalid-rel.js

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ const standardValues = new Map(Object.entries({
4040
}));
4141
const components = new Set(['link', 'a', 'area', 'form']);
4242

43-
function splitIntoRangedParts(node) {
43+
function splitIntoRangedParts(node, regex) {
4444
const res = [];
45-
const regex = /\s*([^\s]+)/g;
4645
const valueRangeStart = node.range[0] + 1; // the plus one is for the initial quote
4746
let match;
4847

@@ -64,7 +63,7 @@ function checkLiteralValueNode(context, node, parentNodeName) {
6463
if (typeof node.value !== 'string') {
6564
return context.report({
6665
node,
67-
messageId: 'onlyStrings',
66+
message: '"rel" attribute only supports strings.',
6867
fix(fixer) {
6968
return fixer.remove(node.parent.parent);
7069
}
@@ -74,41 +73,47 @@ function checkLiteralValueNode(context, node, parentNodeName) {
7473
if (!node.value.trim()) {
7574
return context.report({
7675
node,
77-
messageId: 'emptyRel',
76+
message: 'An empty "rel" attribute is meaningless.',
7877
fix(fixer) {
7978
return fixer.remove(node);
8079
}
8180
});
8281
}
8382

84-
const parts = splitIntoRangedParts(node);
83+
const parts = splitIntoRangedParts(node, /([^\s]+)/g);
8584
for (const part of parts) {
8685
const allowedTags = standardValues.get(part.value);
8786
if (!allowedTags) {
8887
context.report({
8988
node,
90-
messageId: 'realRelValues',
91-
data: {
92-
value: part.reportingValue
93-
},
89+
message: `${part.reportingValue} is never a valid "rel" attribute value.`,
9490
fix(fixer) {
9591
return fixer.removeRange(part.range);
9692
}
9793
});
9894
} else if (!allowedTags.has(parentNodeName)) {
9995
context.report({
10096
node,
101-
messageId: 'matchingRelValues',
102-
data: {
103-
value: part.reportingValue,
104-
tag: parentNodeName
105-
},
97+
message: `${part.reportingValue} is not a valid "rel" attribute value for <${parentNodeName}>.`,
10698
fix(fixer) {
10799
return fixer.removeRange(part.range);
108100
}
109101
});
110102
}
111103
}
104+
105+
const whitespaceParts = splitIntoRangedParts(node, /(\s+)/g);
106+
for (const whitespacePart of whitespaceParts) {
107+
if (whitespacePart.value !== ' ' || whitespacePart.range[0] === (node.range[0] + 1) || whitespacePart.range[1] === (node.range[1] - 1)) {
108+
context.report({
109+
node,
110+
message: '"rel" attribute values should be space delimited.',
111+
fix(fixer) {
112+
return fixer.removeRange(whitespacePart.range);
113+
}
114+
});
115+
}
116+
}
112117
}
113118

114119
module.exports = {
@@ -118,29 +123,22 @@ module.exports = {
118123
description: 'Forbid `rel` attribute with an invalid value`',
119124
category: 'Possible Errors',
120125
url: docsUrl('jsx-no-invalid-rel')
121-
},
122-
messages: {
123-
relOnlyOnSpecific: 'The "rel" attribute only has meaning on `<link>`, `<a>`, `<area>`, and `<form>` tags.',
124-
emptyRel: 'An empty "rel" attribute is meaningless.',
125-
onlyStrings: '"rel" attribute only supports strings',
126-
realRelValues: '{{ value }} is never a valid "rel" attribute value.',
127-
matchingRelValues: '"{{ value }}" is not a valid "rel" attribute value for <{{ tag }}>.'
128126
}
129127
},
130128

131129
create(context) {
132130
return {
133131
JSXAttribute(node) {
134132
// ignore attributes that aren't "rel"
135-
if (node.type !== 'JSXIdentifier' && node.name.name !== 'rel') {
133+
if (node.name.name !== 'rel') {
136134
return;
137135
}
138136

139137
const parentNodeName = node.parent.name.name;
140138
if (!components.has(parentNodeName)) {
141139
return context.report({
142140
node,
143-
messageId: 'relOnlyOnSpecific',
141+
message: 'The "rel" attribute only has meaning on `<link>`, `<a>`, `<area>`, and `<form>` tags.',
144142
fix(fixer) {
145143
return fixer.remove(node);
146144
}
@@ -150,7 +148,7 @@ module.exports = {
150148
if (!node.value) {
151149
return context.report({
152150
node,
153-
messageId: 'emptyRel',
151+
message: 'An empty "rel" attribute is meaningless.',
154152
fix(fixer) {
155153
return fixer.remove(node);
156154
}
@@ -165,10 +163,14 @@ module.exports = {
165163
return checkLiteralValueNode(context, node.value.expression, parentNodeName);
166164
}
167165

166+
if (node.value.type !== 'JSXExpressionContainer') {
167+
return;
168+
}
169+
168170
if (node.value.expression.type === 'ObjectExpression') {
169171
return context.report({
170172
node,
171-
messageId: 'onlyStrings',
173+
message: '"rel" attribute only supports strings.',
172174
fix(fixer) {
173175
return fixer.remove(node);
174176
}
@@ -178,7 +180,7 @@ module.exports = {
178180
if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
179181
return context.report({
180182
node,
181-
messageId: 'onlyStrings',
183+
message: '"rel" attribute only supports strings.',
182184
fix(fixer) {
183185
return fixer.remove(node);
184186
}

0 commit comments

Comments
 (0)