Skip to content

Commit 5efd774

Browse files
akulsr0ljharb
authored andcommitted
[Fix] jsx-no-target-blank: allow ternaries with literals
1 parent 611b9ee commit 5efd774

File tree

3 files changed

+37
-11
lines changed

3 files changed

+37
-11
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1313

1414
### Fixed
1515
* configs: avoid legacy config system error ([#3461][] @ljharb)
16+
* [`jsx-no-target-blank`]: allow ternaries with literals ([#3464][] @akulsr0)
1617

1718
### Changed
1819
* [Perf] component detection: improve performance by avoiding traversing parents unnecessarily ([#3459][] @golopot)
1920

21+
[#3464]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3464
2022
[#3461]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3461
2123
[#3459]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3459
2224
[#3449]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3449

lib/rules/jsx-no-target-blank.js

+23-7
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ function hasDynamicLink(node, linkAttribute) {
6565
}
6666
}
6767

68-
function getStringFromValue(value) {
68+
/**
69+
* Get the string(s) from a value
70+
* @param {ASTNode} value The AST node being checked.
71+
* @param {ASTNode} targetValue The AST node being checked.
72+
* @returns {String | String[] | null} The string value, or null if not a string.
73+
*/
74+
function getStringFromValue(value, targetValue) {
6975
if (value) {
7076
if (value.type === 'Literal') {
7177
return value.value;
@@ -75,24 +81,34 @@ function getStringFromValue(value) {
7581
return value.expression.quasis[0].value.cooked;
7682
}
7783
const expr = value.expression;
78-
return expr && (
79-
expr.type === 'ConditionalExpression'
80-
? [expr.consequent.value, expr.alternate.value]
81-
: expr.value
82-
);
84+
if (expr && expr.type === 'ConditionalExpression') {
85+
const relValues = [expr.consequent.value, expr.alternate.value];
86+
if (targetValue.type === 'JSXExpressionContainer' && targetValue.expression && targetValue.expression.type === 'ConditionalExpression') {
87+
const targetTestCond = targetValue.expression.test.name;
88+
const relTestCond = value.expression.test.name;
89+
if (targetTestCond === relTestCond) {
90+
const targetBlankIndex = [targetValue.expression.consequent.value, targetValue.expression.alternate.value].indexOf('_blank');
91+
return relValues[targetBlankIndex];
92+
}
93+
}
94+
return relValues;
95+
}
96+
return expr.value;
8397
}
8498
}
8599
return null;
86100
}
87101

88102
function hasSecureRel(node, allowReferrer, warnOnSpreadAttributes, spreadAttributeIndex) {
89103
const relIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'rel'));
104+
const targetIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'target'));
90105
if (relIndex === -1 || (warnOnSpreadAttributes && relIndex < spreadAttributeIndex)) {
91106
return false;
92107
}
93108

94109
const relAttribute = node.attributes[relIndex];
95-
const value = getStringFromValue(relAttribute.value);
110+
const targetAttributeValue = node.attributes[targetIndex] && node.attributes[targetIndex].value;
111+
const value = getStringFromValue(relAttribute.value, targetAttributeValue);
96112
return [].concat(value).every((item) => {
97113
const tags = typeof item === 'string' ? item.toLowerCase().split(' ') : false;
98114
const noreferrer = tags && tags.indexOf('noreferrer') >= 0;

tests/lib/rules/jsx-no-target-blank.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ ruleTester.run('jsx-no-target-blank', rule, {
155155
code: '<a href={href} target="_blank" rel={isExternal ? "noreferrer" : "noopener"} />',
156156
options: [{ allowReferrer: true }],
157157
},
158+
{
159+
code: '<a href={href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noreferrer" : undefined} />',
160+
},
161+
{
162+
code: '<a href={href} target={isSelf ? "_self" : "_blank"} rel={isSelf ? undefined : "noreferrer"} />',
163+
},
164+
{
165+
code: '<a href={href} target={isSelf ? "_self" : ""} rel={isSelf ? undefined : ""} />',
166+
},
167+
{
168+
code: '<a href={href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} />',
169+
},
158170
]),
159171
invalid: parsers.all([
160172
{
@@ -378,10 +390,6 @@ ruleTester.run('jsx-no-target-blank', rule, {
378390
code: '<a href={href} target="_blank" rel={isExternal ? "undefined" : "noopener"} />',
379391
errors: defaultErrors,
380392
},
381-
{
382-
code: '<a href={href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} />',
383-
errors: defaultErrors,
384-
},
385393
{
386394
code: '<a href={href} target={isExternal ? "_blank" : undefined} rel={isExternal ? undefined : "noopener noreferrer"} />',
387395
errors: defaultErrors,

0 commit comments

Comments
 (0)