Skip to content

Commit

Permalink
resolve comments, improved whitespace fixing
Browse files Browse the repository at this point in the history
  • Loading branch information
Nokel81 committed Dec 21, 2020
1 parent f2ead45 commit d18a7fe
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 81 deletions.
4 changes: 3 additions & 1 deletion docs/rules/jsx-no-invalid-rel.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Prevent usage of invalid `rel` (react/jsx-no-invalid-rel)

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.
The JSX elements: `a`, `area`, `link`, or `form` all have an 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.

## Rule Details

Expand Down
54 changes: 28 additions & 26 deletions lib/rules/jsx-no-invalid-rel.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ const standardValues = new Map(Object.entries({
}));
const components = new Set(['link', 'a', 'area', 'form']);

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

Expand All @@ -64,7 +63,7 @@ function checkLiteralValueNode(context, node, parentNodeName) {
if (typeof node.value !== 'string') {
return context.report({
node,
messageId: 'onlyStrings',
message: '"rel" attribute only supports strings.',
fix(fixer) {
return fixer.remove(node.parent.parent);
}
Expand All @@ -74,41 +73,47 @@ function checkLiteralValueNode(context, node, parentNodeName) {
if (!node.value.trim()) {
return context.report({
node,
messageId: 'emptyRel',
message: 'An empty "rel" attribute is meaningless.',
fix(fixer) {
return fixer.remove(node);
}
});
}

const parts = splitIntoRangedParts(node);
const parts = splitIntoRangedParts(node, /([^\s]+)/g);
for (const part of parts) {
const allowedTags = standardValues.get(part.value);
if (!allowedTags) {
context.report({
node,
messageId: 'realRelValues',
data: {
value: part.reportingValue
},
message: `${part.reportingValue} is never a valid "rel" attribute value.`,
fix(fixer) {
return fixer.removeRange(part.range);
}
});
} else if (!allowedTags.has(parentNodeName)) {
context.report({
node,
messageId: 'matchingRelValues',
data: {
value: part.reportingValue,
tag: parentNodeName
},
message: `${part.reportingValue} is not a valid "rel" attribute value for <${parentNodeName}>.`,
fix(fixer) {
return fixer.removeRange(part.range);
}
});
}
}

const whitespaceParts = splitIntoRangedParts(node, /(\s+)/g);
for (const whitespacePart of whitespaceParts) {
if (whitespacePart.value !== ' ' || whitespacePart.range[0] === (node.range[0] + 1) || whitespacePart.range[1] === (node.range[1] - 1)) {
context.report({
node,
message: '"rel" attribute values should be space delimited.',
fix(fixer) {
return fixer.removeRange(whitespacePart.range);
}
});
}
}
}

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

create(context) {
return {
JSXAttribute(node) {
// ignore attributes that aren't "rel"
if (node.type !== 'JSXIdentifier' && node.name.name !== 'rel') {
if (node.name.name !== 'rel') {
return;
}

const parentNodeName = node.parent.name.name;
if (!components.has(parentNodeName)) {
return context.report({
node,
messageId: 'relOnlyOnSpecific',
message: 'The "rel" attribute only has meaning on `<link>`, `<a>`, `<area>`, and `<form>` tags.',
fix(fixer) {
return fixer.remove(node);
}
Expand All @@ -150,7 +148,7 @@ module.exports = {
if (!node.value) {
return context.report({
node,
messageId: 'emptyRel',
message: 'An empty "rel" attribute is meaningless.',
fix(fixer) {
return fixer.remove(node);
}
Expand All @@ -165,10 +163,14 @@ module.exports = {
return checkLiteralValueNode(context, node.value.expression, parentNodeName);
}

if (node.value.type !== 'JSXExpressionContainer') {
return;
}

if (node.value.expression.type === 'ObjectExpression') {
return context.report({
node,
messageId: 'onlyStrings',
message: '"rel" attribute only supports strings.',
fix(fixer) {
return fixer.remove(node);
}
Expand All @@ -178,7 +180,7 @@ module.exports = {
if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') {
return context.report({
node,
messageId: 'onlyStrings',
message: '"rel" attribute only supports strings.',
fix(fixer) {
return fixer.remove(node);
}
Expand Down
Loading

0 comments on commit d18a7fe

Please sign in to comment.