Skip to content

Commit

Permalink
[Refactor] no-unstable-nested-components: use isCreateElement util (
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Sep 20, 2021
1 parent ddff237 commit f4d4314
Showing 1 changed file with 20 additions and 16 deletions.
36 changes: 20 additions & 16 deletions lib/rules/no-unstable-nested-components.js
Expand Up @@ -7,6 +7,7 @@

const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');

// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -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)
);
}

Expand Down Expand Up @@ -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
Expand All @@ -144,7 +146,7 @@ function isReturnStatementOfHook(node) {
return false;
}

const callExpression = getClosestMatchingParent(node, isCallExpressionMatcher);
const callExpression = getClosestMatchingParent(node, context, isCallExpressionMatcher);
return (
callExpression
&& callExpression.callee
Expand All @@ -159,9 +161,10 @@ function isReturnStatementOfHook(node) {
* <Component>{() => <div />}</Component>
* ```
* @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
Expand All @@ -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 (
Expand Down Expand Up @@ -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)
);
}

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -430,6 +433,7 @@ module.exports = {
// Get the closest parent component
const parentComponent = getClosestMatchingParent(
node,
context,
(nodeToMatch) => components.get(nodeToMatch)
);

Expand Down

0 comments on commit f4d4314

Please sign in to comment.