diff --git a/CHANGELOG.md b/CHANGELOG.md index e70c0cc101..b57162789e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,12 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed * configs: avoid legacy config system error ([#3461][] @ljharb) +* [`jsx-no-target-blank`]: allow ternaries with literals ([#3464][] @akulsr0) ### Changed * [Perf] component detection: improve performance by avoiding traversing parents unnecessarily ([#3459][] @golopot) +[#3464]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3464 [#3461]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3461 [#3459]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3459 [#3449]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3449 diff --git a/lib/rules/jsx-no-target-blank.js b/lib/rules/jsx-no-target-blank.js index ca1eb7c7cb..9b77e37f4c 100644 --- a/lib/rules/jsx-no-target-blank.js +++ b/lib/rules/jsx-no-target-blank.js @@ -65,7 +65,13 @@ function hasDynamicLink(node, linkAttribute) { } } -function getStringFromValue(value) { +/** + * Get the string(s) from a value + * @param {ASTNode} value The AST node being checked. + * @param {ASTNode} targetValue The AST node being checked. + * @returns {String | String[] | null} The string value, or null if not a string. + */ +function getStringFromValue(value, targetValue) { if (value) { if (value.type === 'Literal') { return value.value; @@ -75,11 +81,19 @@ function getStringFromValue(value) { return value.expression.quasis[0].value.cooked; } const expr = value.expression; - return expr && ( - expr.type === 'ConditionalExpression' - ? [expr.consequent.value, expr.alternate.value] - : expr.value - ); + if (expr && expr.type === 'ConditionalExpression') { + const relValues = [expr.consequent.value, expr.alternate.value]; + if (targetValue.type === 'JSXExpressionContainer' && targetValue.expression && targetValue.expression.type === 'ConditionalExpression') { + const targetTestCond = targetValue.expression.test.name; + const relTestCond = value.expression.test.name; + if (targetTestCond === relTestCond) { + const targetBlankIndex = [targetValue.expression.consequent.value, targetValue.expression.alternate.value].indexOf('_blank'); + return relValues[targetBlankIndex]; + } + } + return relValues; + } + return expr.value; } } return null; @@ -87,12 +101,14 @@ function getStringFromValue(value) { function hasSecureRel(node, allowReferrer, warnOnSpreadAttributes, spreadAttributeIndex) { const relIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'rel')); + const targetIndex = findLastIndex(node.attributes, (attr) => (attr.type === 'JSXAttribute' && attr.name.name === 'target')); if (relIndex === -1 || (warnOnSpreadAttributes && relIndex < spreadAttributeIndex)) { return false; } const relAttribute = node.attributes[relIndex]; - const value = getStringFromValue(relAttribute.value); + const targetAttributeValue = node.attributes[targetIndex] && node.attributes[targetIndex].value; + const value = getStringFromValue(relAttribute.value, targetAttributeValue); return [].concat(value).every((item) => { const tags = typeof item === 'string' ? item.toLowerCase().split(' ') : false; const noreferrer = tags && tags.indexOf('noreferrer') >= 0; diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js index 2208e371dc..f5ab6b8793 100644 --- a/tests/lib/rules/jsx-no-target-blank.js +++ b/tests/lib/rules/jsx-no-target-blank.js @@ -155,6 +155,18 @@ ruleTester.run('jsx-no-target-blank', rule, { code: '', options: [{ allowReferrer: true }], }, + { + code: '', + }, + { + code: '', + }, + { + code: '', + }, + { + code: '', + }, ]), invalid: parsers.all([ { @@ -378,10 +390,6 @@ ruleTester.run('jsx-no-target-blank', rule, { code: '', errors: defaultErrors, }, - { - code: '', - errors: defaultErrors, - }, { code: '', errors: defaultErrors,