From 0170dbed4e48e17594546767fa686a430b5675fd Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 29 Jul 2024 22:57:20 -0700 Subject: [PATCH] [Refactor] add `astUtil.isCallExpression` predicate --- lib/rules/boolean-prop-naming.js | 7 ++++--- lib/rules/display-name.js | 5 ++--- lib/rules/forbid-prop-types.js | 4 ++-- lib/rules/jsx-no-bind.js | 4 ++-- lib/rules/jsx-no-useless-fragment.js | 4 ++-- lib/rules/no-access-state-in-setstate.js | 3 ++- lib/rules/no-adjacent-inline-elements.js | 3 ++- lib/rules/no-array-index-key.js | 4 ++-- lib/rules/no-object-type-as-default-prop.js | 3 ++- lib/rules/no-typos.js | 5 +++-- lib/rules/no-unstable-nested-components.js | 15 +++------------ lib/rules/no-unused-state.js | 2 +- lib/rules/prefer-exact-props.js | 4 ++-- lib/rules/prefer-stateless-function.js | 2 +- lib/rules/sort-prop-types.js | 3 ++- lib/util/Components.js | 4 ++-- lib/util/ast.js | 12 +++++++++++- lib/util/defaultProps.js | 2 +- lib/util/isCreateContext.js | 6 ++++-- lib/util/isDestructuredFromPragmaImport.js | 5 +++-- lib/util/propTypes.js | 5 ++--- lib/util/usedPropTypes.js | 5 ++--- 22 files changed, 57 insertions(+), 50 deletions(-) diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 2c48b5a7bd..27becc4525 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -10,6 +10,7 @@ const values = require('object.values'); const Components = require('../util/Components'); const propsUtil = require('../util/props'); +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const propWrapperUtil = require('../util/propWrapper'); const report = require('../util/report'); @@ -26,7 +27,7 @@ const getText = eslintUtil.getText; function nestedPropTypes(prop) { return ( prop.type === 'Property' - && prop.value.type === 'CallExpression' + && astUtil.isCallExpression(prop.value) ); } @@ -311,7 +312,7 @@ module.exports = { } if ( node.value - && node.value.type === 'CallExpression' + && astUtil.isCallExpression(node.value) && propWrapperUtil.isPropWrapperFunction( context, getText(context, node.value.callee) @@ -337,7 +338,7 @@ module.exports = { } const right = node.parent.right; if ( - right.type === 'CallExpression' + astUtil.isCallExpression(right) && propWrapperUtil.isPropWrapperFunction( context, getText(context, right.callee) diff --git a/lib/rules/display-name.js b/lib/rules/display-name.js index 027882a461..b85ec34f1c 100644 --- a/lib/rules/display-name.js +++ b/lib/rules/display-name.js @@ -76,10 +76,9 @@ module.exports = { * @returns {boolean} True if React.forwardRef is nested inside React.memo, false if not. */ function isNestedMemo(node) { - return node.type === 'CallExpression' + return astUtil.isCallExpression(node) && node.arguments - && node.arguments[0] - && node.arguments[0].type === 'CallExpression' + && astUtil.isCallExpression(node.arguments[0]) && utils.isPragmaComponentWrapper(node); } diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js index 581c8969b9..0ef23c4715 100644 --- a/lib/rules/forbid-prop-types.js +++ b/lib/rules/forbid-prop-types.js @@ -134,7 +134,7 @@ module.exports = { ) { value = value.object; } - if (value.type === 'CallExpression') { + if (astUtil.isCallExpression(value)) { if (!isPropTypesPackage(value.callee)) { return; } @@ -169,7 +169,7 @@ module.exports = { if (propTypesObject && propTypesObject.properties) { checkProperties(propTypesObject.properties); } - } else if (node.type === 'CallExpression') { + } else if (astUtil.isCallExpression(node)) { const innerNode = node.arguments && node.arguments[0]; if ( propWrapperUtil.isPropWrapperFunction(context, getText(context, node.callee)) diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index 588ff0b03f..93b53414ac 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -9,6 +9,7 @@ const propName = require('jsx-ast-utils/propName'); const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); const getAncestors = require('../util/eslint').getAncestors; @@ -83,10 +84,9 @@ module.exports = { } function getNodeViolationType(node) { - const nodeType = node.type; if ( !configuration.allowBind - && nodeType === 'CallExpression' + && astUtil.isCallExpression(node) && node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'bind' diff --git a/lib/rules/jsx-no-useless-fragment.js b/lib/rules/jsx-no-useless-fragment.js index 4c2886469f..9827560f0e 100644 --- a/lib/rules/jsx-no-useless-fragment.js +++ b/lib/rules/jsx-no-useless-fragment.js @@ -7,6 +7,7 @@ const arrayIncludes = require('array-includes'); const pragmaUtil = require('../util/pragma'); +const astUtil = require('../util/ast'); const jsxUtil = require('../util/jsx'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -75,8 +76,7 @@ function isKeyedElement(node) { function containsCallExpression(node) { return node && node.type === 'JSXExpressionContainer' - && node.expression - && node.expression.type === 'CallExpression'; + && astUtil.isCallExpression(node.expression); } const messages = { diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js index be72ea42e4..d1db7ba422 100644 --- a/lib/rules/no-access-state-in-setstate.js +++ b/lib/rules/no-access-state-in-setstate.js @@ -6,6 +6,7 @@ 'use strict'; const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); const getScope = require('../util/eslint').getScope; @@ -32,7 +33,7 @@ module.exports = { create(context) { function isSetStateCall(node) { - return node.type === 'CallExpression' + return astUtil.isCallExpression(node) && node.callee.property && node.callee.property.name === 'setState' && node.callee.object.type === 'ThisExpression'; diff --git a/lib/rules/no-adjacent-inline-elements.js b/lib/rules/no-adjacent-inline-elements.js index 5d7fdb8d6e..9d096d97b2 100644 --- a/lib/rules/no-adjacent-inline-elements.js +++ b/lib/rules/no-adjacent-inline-elements.js @@ -8,6 +8,7 @@ const docsUrl = require('../util/docsUrl'); const isCreateElement = require('../util/isCreateElement'); const report = require('../util/report'); +const astUtil = require('../util/ast'); // ------------------------------------------------------------------------------ // Helpers @@ -62,7 +63,7 @@ function isInline(node) { if (node.type === 'JSXElement' && inlineNames.indexOf(node.openingElement.name.name) > -1) { return true; } - if (node.type === 'CallExpression' && inlineNames.indexOf(node.arguments[0].value) > -1) { + if (astUtil.isCallExpression(node) && inlineNames.indexOf(node.arguments[0].value) > -1) { return true; } return false; diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index ffd5c927b4..90381b3a08 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -190,7 +190,7 @@ module.exports = { } if ( - node.type === 'CallExpression' + astUtil.isCallExpression(node) && node.callee && node.callee.type === 'MemberExpression' && node.callee.object @@ -207,7 +207,7 @@ module.exports = { } if ( - node.type === 'CallExpression' + astUtil.isCallExpression(node) && node.callee && node.callee.type === 'Identifier' && node.callee.name === 'String' diff --git a/lib/rules/no-object-type-as-default-prop.js b/lib/rules/no-object-type-as-default-prop.js index 42012413e1..2683bfea4c 100644 --- a/lib/rules/no-object-type-as-default-prop.js +++ b/lib/rules/no-object-type-as-default-prop.js @@ -9,6 +9,7 @@ const values = require('object.values'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); const report = require('../util/report'); const FORBIDDEN_TYPES_MAP = { @@ -55,7 +56,7 @@ function verifyDefaultPropsDestructuring(context, properties) { }, }); } else if ( - propDefaultValueType === 'CallExpression' + astUtil.isCallExpression(propDefaultValue.right) && propDefaultValue.right.callee.type === 'Identifier' && propDefaultValue.right.callee.name === 'Symbol' ) { diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index d60d2e7cf2..a598a8e114 100644 --- a/lib/rules/no-typos.js +++ b/lib/rules/no-typos.js @@ -7,6 +7,7 @@ const PROP_TYPES = Object.keys(require('prop-types')); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); const lifecycleMethods = require('../util/lifecycleMethods'); @@ -109,11 +110,11 @@ module.exports = { && node.property.name !== 'isRequired' ) { // PropTypes.myProp checkValidPropType(node.property); - } else if (node.object.type === 'CallExpression') { + } else if (astUtil.isCallExpression(node.object)) { checkValidPropTypeQualifier(node.property); checkValidCallExpression(node.object); } - } else if (node.type === 'CallExpression') { + } else if (astUtil.isCallExpression(node)) { checkValidCallExpression(node); } } diff --git a/lib/rules/no-unstable-nested-components.js b/lib/rules/no-unstable-nested-components.js index 1f84975634..e17f65e487 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 astUtil = require('../util/ast'); const isCreateElement = require('../util/isCreateElement'); const report = require('../util/report'); @@ -66,8 +67,7 @@ function getClosestMatchingParent(node, context, matcher) { */ function isCreateElementMatcher(node, context) { return ( - node - && node.type === 'CallExpression' + astUtil.isCallExpression(node) && isCreateElement(context, node) ); } @@ -117,15 +117,6 @@ function isPropertyOfObjectExpressionMatcher(node) { ); } -/** - * Matcher used to check whether given node is a `CallExpression` - * @param {ASTNode} node The AST node - * @returns {boolean} True if node is a `CallExpression`, false if not - */ -function isCallExpressionMatcher(node) { - return node && node.type === 'CallExpression'; -} - /** * Check whether given node or its parent is directly inside `map` call * ```jsx @@ -158,7 +149,7 @@ function isReturnStatementOfHook(node, context) { return false; } - const callExpression = getClosestMatchingParent(node, context, isCallExpressionMatcher); + const callExpression = getClosestMatchingParent(node, context, astUtil.isCallExpression); return ( callExpression && callExpression.callee diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index df3fc35975..21d77748ec 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -502,7 +502,7 @@ module.exports = { // Otherwise, record that we saw this property being accessed. addUsedStateField(node.property); // If we see a `this.state` access in a CallExpression, give up. - } else if (isStateReference(node) && node.parent.type === 'CallExpression') { + } else if (isStateReference(node) && astUtil.isCallExpression(node.parent)) { classInfo = null; } }, diff --git a/lib/rules/prefer-exact-props.js b/lib/rules/prefer-exact-props.js index 6d7db27d0f..5227d9448b 100644 --- a/lib/rules/prefer-exact-props.js +++ b/lib/rules/prefer-exact-props.js @@ -6,6 +6,7 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); const propsUtil = require('../util/props'); const propWrapperUtil = require('../util/propWrapper'); const variableUtil = require('../util/variable'); @@ -81,8 +82,7 @@ module.exports = { function isNonExactPropWrapperFunction(node) { return ( - node - && node.type === 'CallExpression' + astUtil.isCallExpression(node) && !propWrapperUtil.isExactPropWrapperFunction(context, getText(context, node.callee)) ); } diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 0234aacf71..46847cb195 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -70,7 +70,7 @@ module.exports = { return ( body.length === 1 && body[0].type === 'ExpressionStatement' - && body[0].expression.type === 'CallExpression' + && astUtil.isCallExpression(body[0].expression) && body[0].expression.callee.type === 'Super' ); } diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index 3dcb7f4690..6aaebea184 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -4,6 +4,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const variableUtil = require('../util/variable'); const propsUtil = require('../util/props'); const docsUrl = require('../util/docsUrl'); @@ -196,7 +197,7 @@ module.exports = { if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } - } else if (node.type === 'CallExpression') { + } else if (astUtil.isCallExpression(node)) { const innerNode = node.arguments && node.arguments[0]; if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) { checkNode(innerNode); diff --git a/lib/util/Components.js b/lib/util/Components.js index 9796783bcd..446fd9022a 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -407,7 +407,7 @@ function componentRule(rule, context) { }, isPragmaComponentWrapper(node) { - if (!node || node.type !== 'CallExpression') { + if (!astUtil.isCallExpression(node)) { return false; } @@ -763,7 +763,7 @@ function componentRule(rule, context) { * @returns {boolean} True if the node is a call to a React hook */ isReactHookCall(node, expectedHookNames) { - if (node.type !== 'CallExpression') { + if (!astUtil.isCallExpression(node)) { return false; } diff --git a/lib/util/ast.js b/lib/util/ast.js index cde155a6fb..452b1a1ef2 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -92,7 +92,7 @@ function traverseReturns(ASTNode, context, onReturn) { } /* TODO: properly warn on React.forwardRefs having typo properties - if (nodeType === 'CallExpression') { + if (astUtil.isCallExpression(ASTNode)) { const callee = ASTNode.callee; const pragma = pragmaUtil.getFromContext(context); if ( @@ -347,6 +347,15 @@ function isTSAsExpression(node) { return node && node.type === 'TSAsExpression'; } +/** + * Matcher used to check whether given node is a `CallExpression` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `CallExpression`, false if not + */ +function isCallExpression(node) { + return node && node.type === 'CallExpression'; +} + /** * Extracts the expression node that is wrapped inside a TS type assertion * @@ -448,6 +457,7 @@ module.exports = { getPropertyNameNode, inConstructor, isAssignmentLHS, + isCallExpression, isClass, isFunction, isFunctionLike, diff --git a/lib/util/defaultProps.js b/lib/util/defaultProps.js index cd14c7fa41..b0e88844d5 100644 --- a/lib/util/defaultProps.js +++ b/lib/util/defaultProps.js @@ -26,7 +26,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { return variableUtil.findVariableByName(context, node, node.name); } if ( - node.type === 'CallExpression' + astUtil.isCallExpression(node) && propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && node.arguments && node.arguments[0] ) { diff --git a/lib/util/isCreateContext.js b/lib/util/isCreateContext.js index fde00a9dc1..fd73ecc31d 100644 --- a/lib/util/isCreateContext.js +++ b/lib/util/isCreateContext.js @@ -1,5 +1,7 @@ 'use strict'; +const astUtil = require('./ast'); + /** * Checks if the node is a React.createContext call * @param {ASTNode} node - The AST node being checked. @@ -11,7 +13,7 @@ module.exports = function isCreateContext(node) { && node.init.callee ) { if ( - node.init.type === 'CallExpression' + astUtil.isCallExpression(node.init) && node.init.callee.name === 'createContext' ) { return true; @@ -30,7 +32,7 @@ module.exports = function isCreateContext(node) { node.expression && node.expression.type === 'AssignmentExpression' && node.expression.operator === '=' - && node.expression.right.type === 'CallExpression' + && astUtil.isCallExpression(node.expression.right) && node.expression.right.callee ) { const right = node.expression.right; diff --git a/lib/util/isDestructuredFromPragmaImport.js b/lib/util/isDestructuredFromPragmaImport.js index dc927d0cff..122fb545af 100644 --- a/lib/util/isDestructuredFromPragmaImport.js +++ b/lib/util/isDestructuredFromPragmaImport.js @@ -1,5 +1,6 @@ 'use strict'; +const astUtil = require('./ast'); const pragmaUtil = require('./pragma'); const variableUtil = require('./variable'); @@ -39,14 +40,14 @@ module.exports = function isDestructuredFromPragmaImport(context, node, variable let requireExpression = null; // get "require('react')" from: "{variable} = require('react')" - if (latestDef.node.init.type === 'CallExpression') { + if (astUtil.isCallExpression(latestDef.node.init)) { requireExpression = latestDef.node.init; } // get "require('react')" from: "variable = require('react').variable" if ( !requireExpression && latestDef.node.init.type === 'MemberExpression' - && latestDef.node.init.object.type === 'CallExpression' + && astUtil.isCallExpression(latestDef.node.init.object) ) { requireExpression = latestDef.node.init.object; } diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index 28a460d6b6..a1dce37d8b 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -446,8 +446,7 @@ module.exports = function propTypesInstructions(context, components, utils) { // Verify PropTypes that are functions if ( - value - && value.type === 'CallExpression' + astUtil.isCallExpression(value) && value.callee && value.callee.property && value.callee.property.name @@ -794,7 +793,7 @@ module.exports = function propTypesInstructions(context, components, utils) { switch (res.type) { case 'ObjectExpression': iterateProperties(context, res.properties, (key, value, propNode) => { - if (propNode && propNode.argument && propNode.argument.type === 'CallExpression') { + if (propNode && astUtil.isCallExpression(propNode.argument)) { const propNodeTypeParams = propNode.argument.typeParameters; if (propNodeTypeParams) { this.visitTSNode(propNodeTypeParams); diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index c5dfc733e9..41eb307d6b 100644 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -163,7 +163,7 @@ function isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) { * @return {boolean} */ function isSetStateUpdater(node) { - const unwrappedParentCalleeNode = node.parent && node.parent.type === 'CallExpression' + const unwrappedParentCalleeNode = astUtil.isCallExpression(node.parent) && ast.unwrapTSAsExpression(node.parent.callee); return unwrappedParentCalleeNode @@ -180,8 +180,7 @@ function isPropArgumentInSetStateUpdater(context, node, name) { let scope = getScope(context, node); while (scope) { const unwrappedParentCalleeNode = scope.block - && scope.block.parent - && scope.block.parent.type === 'CallExpression' + && astUtil.isCallExpression(scope.block.parent) && ast.unwrapTSAsExpression(scope.block.parent.callee); if ( unwrappedParentCalleeNode