diff --git a/lib/rules/no-unstable-nested-components.js b/lib/rules/no-unstable-nested-components.js index c7732c6f2c..a830d82760 100644 --- a/lib/rules/no-unstable-nested-components.js +++ b/lib/rules/no-unstable-nested-components.js @@ -7,6 +7,7 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const isCreateElement = require('../util/isCreateElement'); const report = require('../util/report'); // ------------------------------------------------------------------------------ @@ -42,33 +43,33 @@ function startsWithRender(text) { /** * Get closest parent matching given matcher * @param {ASTNode} node The AST node + * @param {Context} context eslint context * @param {Function} matcher Method used to match the parent * @returns {ASTNode} The matching parent node, if any */ -function getClosestMatchingParent(node, matcher) { +function getClosestMatchingParent(node, context, matcher) { if (!node || !node.parent || node.parent.type === 'Program') { return; } - if (matcher(node.parent)) { + if (matcher(node.parent, context)) { return node.parent; } - return getClosestMatchingParent(node.parent, matcher); + return getClosestMatchingParent(node.parent, context, matcher); } /** * Matcher used to check whether given node is a `createElement` call * @param {ASTNode} node The AST node + * @param {Context} context eslint context * @returns {Boolean} True if node is a `createElement` call, false if not */ -function isCreateElementMatcher(node) { +function isCreateElementMatcher(node, context) { return ( node && node.type === 'CallExpression' - && node.callee - && node.callee.property - && node.callee.property.name === 'createElement' + && isCreateElement(node, context) ); } @@ -133,9 +134,10 @@ function isMapCall(node) { /** * Check whether given node is `ReturnStatement` of a React hook * @param {ASTNode} node The AST node + * @param {Context} context eslint context * @returns {Boolean} True if node is a `ReturnStatement` of a React hook, false if not */ -function isReturnStatementOfHook(node) { +function isReturnStatementOfHook(node, context) { if ( !node || !node.parent @@ -144,7 +146,7 @@ function isReturnStatementOfHook(node) { return false; } - const callExpression = getClosestMatchingParent(node, isCallExpressionMatcher); + const callExpression = getClosestMatchingParent(node, context, isCallExpressionMatcher); return ( callExpression && callExpression.callee @@ -159,9 +161,10 @@ function isReturnStatementOfHook(node) { * {() =>
} * ``` * @param {ASTNode} node The AST node + * @param {Context} context eslint context * @returns {Boolean} True if component is declared inside a render prop, false if not */ -function isComponentInRenderProp(node) { +function isComponentInRenderProp(node, context) { if ( node && node.parent @@ -183,7 +186,7 @@ function isComponentInRenderProp(node) { return true; } - const jsxExpressionContainer = getClosestMatchingParent(node, isJSXExpressionContainerMatcher); + const jsxExpressionContainer = getClosestMatchingParent(node, context, isJSXExpressionContainerMatcher); // Check whether prop name indicates accepted patterns if ( @@ -345,12 +348,12 @@ module.exports = { return false; } - const createElementParent = getClosestMatchingParent(node, isCreateElementMatcher); + const createElementParent = getClosestMatchingParent(node, context, isCreateElementMatcher); return ( createElementParent && createElementParent.arguments - && createElementParent.arguments[1] === getClosestMatchingParent(node, isObjectExpressionMatcher) + && createElementParent.arguments[1] === getClosestMatchingParent(node, context, isObjectExpressionMatcher) ); } @@ -363,7 +366,7 @@ module.exports = { * @returns {Boolean} True if node is a component declared inside prop, false if not */ function isComponentInProp(node) { - const jsxAttribute = getClosestMatchingParent(node, isJSXAttributeOfExpressionContainerMatcher); + const jsxAttribute = getClosestMatchingParent(node, context, isJSXAttributeOfExpressionContainerMatcher); if (!jsxAttribute) { return isComponentInsideCreateElementsProp(node); @@ -406,14 +409,14 @@ module.exports = { if ( // Support allowAsProps option - (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node))) + (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node, context))) // Prevent reporting components created inside Array.map calls || isMapCall(node) || isMapCall(node.parent) // Do not mark components declared inside hooks (or falsly '() => null' clean-up methods) - || isReturnStatementOfHook(node) + || isReturnStatementOfHook(node, context) // Do not mark objects containing render methods || isDirectValueOfRenderProperty(node) @@ -430,6 +433,7 @@ module.exports = { // Get the closest parent component const parentComponent = getClosestMatchingParent( node, + context, (nodeToMatch) => components.get(nodeToMatch) );