diff --git a/.github/workflows/node-18+.yml b/.github/workflows/node-18+.yml index e27cb0e7bf..986af8fd23 100644 --- a/.github/workflows/node-18+.yml +++ b/.github/workflows/node-18+.yml @@ -26,6 +26,7 @@ jobs: matrix: node-version: ${{ fromJson(needs.matrix.outputs.latest) }} eslint: + - 9 - 8 - 7 - 6 @@ -48,6 +49,7 @@ jobs: npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@5" "babel-eslint@${{ matrix.babel-eslint }}" env: NPM_CONFIG_LEGACY_PEER_DEPS: true + ESLINT_USE_FLAT_CONFIG: false - run: npx ls-engines - run: npm run unit-test - uses: codecov/codecov-action@v3.1.5 diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index e4abdaede9..cf9fb42385 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -4,6 +4,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const isAssignmentLHS = require('../util/ast').isAssignmentLHS; @@ -101,7 +102,7 @@ module.exports = { function handleStatelessComponent(node) { const params = evalParams(node.params); - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(astUtil.getScope(context, node).block); if (!SFCComponent) { return; } @@ -119,7 +120,7 @@ module.exports = { } function handleStatelessComponentExit(node) { - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(astUtil.getScope(context, node).block); if (SFCComponent) { sfcParams.pop(); } @@ -191,7 +192,7 @@ module.exports = { 'FunctionExpression:exit': handleStatelessComponentExit, MemberExpression(node) { - let scope = context.getScope(node); + let scope = astUtil.getScope(context, node); let SFCComponent = components.get(scope.block); while (!SFCComponent && scope.upper && scope.upper !== scope) { SFCComponent = components.get(scope.upper.block); @@ -209,7 +210,7 @@ module.exports = { VariableDeclarator(node) { const classComponent = utils.getParentComponent(node); - const SFCComponent = components.get(context.getScope(node).block); + const SFCComponent = components.get(astUtil.getScope(context, node).block); const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern'); // let {foo} = props; @@ -247,7 +248,8 @@ module.exports = { && destructureInSignature === 'always' && node.init.name === 'props' ) { - const scopeSetProps = context.getScope().set.get('props'); + const scope = astUtil.getScope(context, node); + const scopeSetProps = scope.set.get('props'); const propsRefs = scopeSetProps && scopeSetProps.references; if (!propsRefs) { return; diff --git a/lib/rules/forbid-prop-types.js b/lib/rules/forbid-prop-types.js index 92f0f0f51f..fa5a01d072 100644 --- a/lib/rules/forbid-prop-types.js +++ b/lib/rules/forbid-prop-types.js @@ -162,7 +162,7 @@ module.exports = { checkProperties(node.properties); break; case 'Identifier': { - const propTypesObject = variableUtil.findVariableByName(context, node.name); + const propTypesObject = variableUtil.findVariableByName(context, node); if (propTypesObject && propTypesObject.properties) { checkProperties(propTypesObject.properties); } diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js index 38b4dd8b4b..33ea21cfe9 100644 --- a/lib/rules/jsx-fragments.js +++ b/lib/rules/jsx-fragments.js @@ -103,8 +103,8 @@ module.exports = { }; } - function refersToReactFragment(name) { - const variableInit = variableUtil.findVariableByName(context, name); + function refersToReactFragment(node, name) { + const variableInit = variableUtil.findVariableByName(context, node, name); if (!variableInit) { return false; } @@ -185,7 +185,7 @@ module.exports = { const openingEl = node.openingElement; const elName = elementType(openingEl); - if (fragmentNames.has(elName) || refersToReactFragment(elName)) { + if (fragmentNames.has(elName) || refersToReactFragment(node, elName)) { if (reportOnReactVersion(node)) { return; } diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 2ea6653351..d5d52fa3d0 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -149,7 +149,7 @@ module.exports = { return; } - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); const element = findJSXElementOrFragment(variables, node.expression.name, []); if (element) { diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js index 17e56e2e04..1ee6d82255 100644 --- a/lib/rules/jsx-no-bind.js +++ b/lib/rules/jsx-no-bind.js @@ -8,6 +8,7 @@ 'use strict'; const propName = require('jsx-ast-utils/propName'); +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); @@ -123,7 +124,7 @@ module.exports = { } function getBlockStatementAncestors(node) { - return context.getAncestors(node).filter( + return astUtil.getAncestors(context, node).filter( (ancestor) => ancestor.type === 'BlockStatement' ).reverse(); } diff --git a/lib/rules/jsx-no-constructed-context-values.js b/lib/rules/jsx-no-constructed-context-values.js index 8bcf045dad..1e784c1501 100644 --- a/lib/rules/jsx-no-constructed-context-values.js +++ b/lib/rules/jsx-no-constructed-context-values.js @@ -6,6 +6,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -179,7 +180,7 @@ module.exports = { } const valueExpression = valueNode.expression; - const invocationScope = context.getScope(); + const invocationScope = astUtil.getScope(context, node); // Check if the value prop is a construction const constructInfo = isConstruction(valueExpression, invocationScope); diff --git a/lib/rules/jsx-no-undef.js b/lib/rules/jsx-no-undef.js index d7a42ccff2..eb6c34e717 100644 --- a/lib/rules/jsx-no-undef.js +++ b/lib/rules/jsx-no-undef.js @@ -5,6 +5,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); @@ -49,7 +50,7 @@ module.exports = { * @returns {void} */ function checkIdentifierInJSX(node) { - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); const sourceCode = context.getSourceCode(); const sourceType = sourceCode.ast.sourceType; const scopeUpperBound = !allowGlobals && sourceType === 'module' ? 'module' : 'global'; diff --git a/lib/rules/jsx-sort-default-props.js b/lib/rules/jsx-sort-default-props.js index f35e40dc5f..d7522eb6b5 100644 --- a/lib/rules/jsx-sort-default-props.js +++ b/lib/rules/jsx-sort-default-props.js @@ -87,11 +87,12 @@ module.exports = { /** * Find a variable by name in the current scope. - * @param {string} name Name of the variable to look for. + * @param {ASTNode} node The node to check. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ - function findVariableByName(name) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === name); + function findVariableByName(node) { + const name = node.name; + const variable = variableUtil.variablesInScope(context, node).find((item) => item.name === name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; @@ -147,7 +148,7 @@ module.exports = { if (node.type === 'ObjectExpression') { checkSorted(node.properties); } else if (node.type === 'Identifier') { - const propTypesObject = findVariableByName(node.name); + const propTypesObject = findVariableByName(node); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } diff --git a/lib/rules/no-access-state-in-setstate.js b/lib/rules/no-access-state-in-setstate.js index 89d4976077..9e7deaba0c 100644 --- a/lib/rules/no-access-state-in-setstate.js +++ b/lib/rules/no-access-state-in-setstate.js @@ -5,6 +5,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); @@ -47,8 +48,9 @@ module.exports = { return current.arguments[0] === node; } - function isClassComponent() { - return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)); + function isClassComponent(node) { + return !!(componentUtil.getParentES6Component(context, node) + || componentUtil.getParentES5Component(context, node)); } // The methods array contains all methods or functions that are using this.state @@ -58,7 +60,7 @@ module.exports = { const vars = []; return { CallExpression(node) { - if (!isClassComponent()) { + if (!isClassComponent(node)) { return; } // Appends all the methods that are calling another @@ -103,7 +105,7 @@ module.exports = { if ( node.property.name === 'state' && node.object.type === 'ThisExpression' - && isClassComponent() + && isClassComponent(node) ) { let current = node; while (current.type !== 'Program') { @@ -134,7 +136,7 @@ module.exports = { if (current.type === 'VariableDeclarator') { vars.push({ node, - scope: context.getScope(), + scope: astUtil.getScope(context, node), variableName: current.id.name, }); break; @@ -155,10 +157,11 @@ module.exports = { current.parent.value === current || current.parent.object === current ) { + const scope = astUtil.getScope(context, node); while (current.type !== 'Program') { if (isFirstArgumentInSetStateCall(current, node)) { vars - .filter((v) => v.scope === context.getScope() && v.variableName === node.name) + .filter((v) => v.scope === scope && v.variableName === node.name) .forEach((v) => { report(context, messages.useCallback, 'useCallback', { node: v.node, @@ -176,7 +179,7 @@ module.exports = { if (property && property.key && property.key.name === 'state' && isDerivedFromThis) { vars.push({ node: property.key, - scope: context.getScope(), + scope: astUtil.getScope(context, node), variableName: property.key.name, }); } diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index 08ecd7a6a9..1867e2e8ea 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -28,7 +28,7 @@ function isCreateCloneElement(node, context) { } if (node.type === 'Identifier') { - const variable = variableUtil.findVariableByName(context, node.name); + const variable = variableUtil.findVariableByName(context, node); if (variable && variable.type === 'ImportSpecifier') { return variable.parent.source.value === 'react'; } diff --git a/lib/rules/no-danger-with-children.js b/lib/rules/no-danger-with-children.js index 17d55930ff..d864aa6828 100644 --- a/lib/rules/no-danger-with-children.js +++ b/lib/rules/no-danger-with-children.js @@ -31,8 +31,8 @@ module.exports = { schema: [], // no options }, create(context) { - function findSpreadVariable(name) { - return variableUtil.variablesInScope(context).find((item) => item.name === name); + function findSpreadVariable(name, node) { + return variableUtil.variablesInScope(context, node).find((item) => item.name === name); } /** * Takes a ObjectExpression and returns the value of the prop if it has it @@ -50,7 +50,7 @@ module.exports = { return prop.key.name === propName; } if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') { - const variable = findSpreadVariable(prop.argument.name); + const variable = findSpreadVariable(prop.argument.name, node); if (variable && variable.defs.length && variable.defs[0].node.init) { if (seenProps.indexOf(prop.argument.name) > -1) { return false; @@ -73,7 +73,7 @@ module.exports = { const attributes = node.openingElement.attributes; return attributes.find((attribute) => { if (attribute.type === 'JSXSpreadAttribute') { - const variable = findSpreadVariable(attribute.argument.name); + const variable = findSpreadVariable(attribute.argument.name, node); if (variable && variable.defs.length && variable.defs[0].node.init) { return findObjectProp(variable.defs[0].node.init, propName, []); } @@ -127,7 +127,7 @@ module.exports = { let props = node.arguments[1]; if (props.type === 'Identifier') { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === props.name); + const variable = variableUtil.variablesInScope(context, node).find((item) => item.name === props.name); if (variable && variable.defs.length && variable.defs[0].node.init) { props = variable.defs[0].node.init; } diff --git a/lib/rules/no-direct-mutation-state.js b/lib/rules/no-direct-mutation-state.js index 08d77e9bca..4fbc2c6b06 100644 --- a/lib/rules/no-direct-mutation-state.js +++ b/lib/rules/no-direct-mutation-state.js @@ -97,7 +97,7 @@ module.exports = { }, AssignmentExpression(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); if (shouldIgnoreComponent(component) || !node.left || !node.left.object) { return; } @@ -113,7 +113,7 @@ module.exports = { }, UpdateExpression(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); if (shouldIgnoreComponent(component) || node.argument.type !== 'MemberExpression') { return; } diff --git a/lib/rules/no-is-mounted.js b/lib/rules/no-is-mounted.js index c15b6e81a4..3348bef1d4 100644 --- a/lib/rules/no-is-mounted.js +++ b/lib/rules/no-is-mounted.js @@ -5,6 +5,7 @@ 'use strict'; +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -40,7 +41,7 @@ module.exports = { if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'isMounted') { return; } - const ancestors = context.getAncestors(callee); + const ancestors = astUtil.getAncestors(context, callee); for (let i = 0, j = ancestors.length; i < j; i++) { if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') { report(context, messages.noIsMounted, 'noIsMounted', { diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index 486eb211ff..33fa945b8a 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -67,7 +67,7 @@ module.exports = { ) { return; } - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); const setStateUsages = (component && component.setStateUsages) || []; setStateUsages.push(callee); components.set(node, { diff --git a/lib/rules/no-string-refs.js b/lib/rules/no-string-refs.js index 2decbbe6bb..48fed36bac 100644 --- a/lib/rules/no-string-refs.js +++ b/lib/rules/no-string-refs.js @@ -49,7 +49,7 @@ module.exports = { */ function isRefsUsage(node) { return !!( - (componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)) + (componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node)) && node.object.type === 'ThisExpression' && node.property.name === 'refs' ); diff --git a/lib/rules/no-this-in-sfc.js b/lib/rules/no-this-in-sfc.js index 9c1535ef0b..29d7743302 100644 --- a/lib/rules/no-this-in-sfc.js +++ b/lib/rules/no-this-in-sfc.js @@ -33,7 +33,7 @@ module.exports = { create: Components.detect((context, components, utils) => ({ MemberExpression(node) { if (node.object.type === 'ThisExpression') { - const component = components.get(utils.getParentStatelessComponent()); + const component = components.get(utils.getParentStatelessComponent(node)); if (!component || (component.node && component.node.parent && component.node.parent.type === 'Property')) { return; } diff --git a/lib/rules/no-unstable-nested-components.js b/lib/rules/no-unstable-nested-components.js index 9b60e496bf..6adaf9638d 100644 --- a/lib/rules/no-unstable-nested-components.js +++ b/lib/rules/no-unstable-nested-components.js @@ -305,7 +305,7 @@ module.exports = { * @returns {Boolean} True if node is inside class component's render block, false if not */ function isInsideRenderMethod(node) { - const parentComponent = utils.getParentComponent(); + const parentComponent = utils.getParentComponent(node); if (!parentComponent || parentComponent.type !== 'ClassDeclaration') { return false; @@ -333,8 +333,8 @@ module.exports = { * @returns {Boolean} True if given node a function component declared inside class component, false if not */ function isFunctionComponentInsideClassComponent(node) { - const parentComponent = utils.getParentComponent(); - const parentStatelessComponent = utils.getParentStatelessComponent(); + const parentComponent = utils.getParentComponent(node); + const parentStatelessComponent = utils.getParentStatelessComponent(node); return ( parentComponent diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index 73a0e52163..fede8ee833 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -9,8 +9,8 @@ 'use strict'; +const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); -const ast = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const report = require('../util/report'); @@ -43,7 +43,7 @@ function getName(node) { } function isThisExpression(node) { - return ast.unwrapTSAsExpression(uncast(node)).type === 'ThisExpression'; + return astUtil.unwrapTSAsExpression(uncast(node)).type === 'ThisExpression'; } function getInitialClassInfo() { @@ -64,7 +64,7 @@ function getInitialClassInfo() { } function isSetStateCall(node) { - const unwrappedCalleeNode = ast.unwrapTSAsExpression(node.callee); + const unwrappedCalleeNode = astUtil.unwrapTSAsExpression(node.callee); return ( unwrappedCalleeNode.type === 'MemberExpression' @@ -107,7 +107,7 @@ module.exports = { 'componentDidUpdate', ]; - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); while (scope) { const parent = scope.block && scope.block.parent; if ( @@ -188,7 +188,7 @@ module.exports = { // Used to record used state fields and new aliases for both // AssignmentExpressions and VariableDeclarators. function handleAssignment(left, right) { - const unwrappedRight = ast.unwrapTSAsExpression(right); + const unwrappedRight = astUtil.unwrapTSAsExpression(right); switch (left.type) { case 'Identifier': @@ -291,8 +291,8 @@ module.exports = { return; } - const unwrappedNode = ast.unwrapTSAsExpression(node); - const unwrappedArgumentNode = ast.unwrapTSAsExpression(unwrappedNode.arguments[0]); + const unwrappedNode = astUtil.unwrapTSAsExpression(node); + const unwrappedArgumentNode = astUtil.unwrapTSAsExpression(unwrappedNode.arguments[0]); // If we're looking at a `this.setState({})` invocation, record all the // properties as state fields. @@ -307,7 +307,7 @@ module.exports = { && unwrappedNode.arguments.length > 0 && unwrappedArgumentNode.type === 'ArrowFunctionExpression' ) { - const unwrappedBodyNode = ast.unwrapTSAsExpression(unwrappedArgumentNode.body); + const unwrappedBodyNode = astUtil.unwrapTSAsExpression(unwrappedArgumentNode.body); if (unwrappedBodyNode.type === 'ObjectExpression') { addStateFields(unwrappedBodyNode); @@ -329,7 +329,7 @@ module.exports = { } // If we see state being assigned as a class property using an object // expression, record all the fields of that object as state fields. - const unwrappedValueNode = ast.unwrapTSAsExpression(node.value); + const unwrappedValueNode = astUtil.unwrapTSAsExpression(node.value); const name = getName(node.key); if ( @@ -368,7 +368,8 @@ module.exports = { return; } - const childScope = context.getScope().childScopes.find((x) => x.block === node.value); + const nodeScope = astUtil.getScope(context, node); + const childScope = nodeScope.childScopes.find((x) => x.block === node.value); if (!childScope) { return; } @@ -451,8 +452,8 @@ module.exports = { return; } - const unwrappedLeft = ast.unwrapTSAsExpression(node.left); - const unwrappedRight = ast.unwrapTSAsExpression(node.right); + const unwrappedLeft = astUtil.unwrapTSAsExpression(node.left); + const unwrappedRight = astUtil.unwrapTSAsExpression(node.right); // Check for assignments like `this.state = {}` if ( @@ -492,7 +493,7 @@ module.exports = { if (!classInfo) { return; } - if (isStateReference(ast.unwrapTSAsExpression(node.object))) { + if (isStateReference(astUtil.unwrapTSAsExpression(node.object))) { // If we see this.state[foo] access, give up. if (node.computed && node.property.type !== 'Literal') { classInfo = null; diff --git a/lib/rules/prefer-exact-props.js b/lib/rules/prefer-exact-props.js index 037fa29914..9a3cccf2e6 100644 --- a/lib/rules/prefer-exact-props.js +++ b/lib/rules/prefer-exact-props.js @@ -147,8 +147,7 @@ module.exports = { } else if (isNonExactPropWrapperFunction(right)) { reportPropTypesError(node); } else if (right.type === 'Identifier') { - const identifier = right.name; - const propsDefinition = variableUtil.findVariableByName(context, identifier); + const propsDefinition = variableUtil.findVariableByName(context, right); if (isNonEmptyObjectExpression(propsDefinition)) { reportPropTypesError(node); } else if (isNonExactPropWrapperFunction(propsDefinition)) { diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 2743ea38aa..48f8098b62 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -341,7 +341,7 @@ module.exports = { // Mark `render` that do not return some JSX ReturnStatement(node) { let blockNode; - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); while (scope) { blockNode = scope.block && scope.block.parent; if (blockNode && (blockNode.type === 'MethodDefinition' || blockNode.type === 'Property')) { diff --git a/lib/rules/react-in-jsx-scope.js b/lib/rules/react-in-jsx-scope.js index e1ee1b6d8c..c6eb199878 100644 --- a/lib/rules/react-in-jsx-scope.js +++ b/lib/rules/react-in-jsx-scope.js @@ -36,7 +36,7 @@ module.exports = { const pragma = pragmaUtil.getFromContext(context); function checkIfReactIsInScope(node) { - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); if (variableUtil.findVariable(variables, pragma)) { return; } diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index 05bd071fc6..dab0fcc813 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -7,6 +7,7 @@ const values = require('object.values'); +const astUtil = require('../util/ast'); const Components = require('../util/Components'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); @@ -153,11 +154,12 @@ module.exports = { /** * Checks if we are declaring function in class + * @param {ASTNode} node The AST node being checked. * @returns {Boolean} True if we are declaring function in class, false if not. */ - function isFunctionInClass() { + function isFunctionInClass(node) { let blockNode; - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); while (scope) { blockNode = scope.block; if (blockNode && blockNode.type === 'ClassDeclaration') { @@ -172,7 +174,7 @@ module.exports = { return { ArrowFunctionExpression(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) @@ -192,7 +194,7 @@ module.exports = { FunctionDeclaration(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) @@ -201,7 +203,7 @@ module.exports = { FunctionExpression(node) { // Skip if the function is declared in the class - if (isFunctionInClass()) { + if (isFunctionInClass(node)) { return; } // Stateless Functional Components cannot be optimized (yet) diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index cb06206952..dfbcdc84d0 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -60,7 +60,7 @@ module.exports = { return { ReturnStatement(node) { - const ancestors = context.getAncestors(node).reverse(); + const ancestors = astUtil.getAncestors(context, node).reverse(); let depth = 0; ancestors.forEach((ancestor) => { if (/Function(Expression|Declaration)$/.test(ancestor.type)) { diff --git a/lib/rules/sort-default-props.js b/lib/rules/sort-default-props.js index aae419ba18..21afe0002f 100644 --- a/lib/rules/sort-default-props.js +++ b/lib/rules/sort-default-props.js @@ -82,11 +82,12 @@ module.exports = { /** * Find a variable by name in the current scope. - * @param {string} name Name of the variable to look for. + * @param {ASTNode} node Node to look for. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ - function findVariableByName(name) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === name); + function findVariableByName(node) { + const name = node.name; + const variable = variableUtil.variablesInScope(context, node).find((item) => item.name === name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; @@ -142,7 +143,7 @@ module.exports = { if (node.type === 'ObjectExpression') { checkSorted(node.properties); } else if (node.type === 'Identifier') { - const propTypesObject = findVariableByName(node.name); + const propTypesObject = findVariableByName(node); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index 4a79aa5407..fc0eca052d 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -206,7 +206,7 @@ module.exports = { checkSorted(node.properties); break; case 'Identifier': { - const propTypesObject = variableUtil.findVariableByName(context, node.name); + const propTypesObject = variableUtil.findVariableByName(context, node); if (propTypesObject && propTypesObject.properties) { checkSorted(propTypesObject.properties); } @@ -259,7 +259,7 @@ module.exports = { if (firstArg.properties) { checkSorted(firstArg.properties); } else if (firstArg.type === 'Identifier') { - const variable = variableUtil.findVariableByName(context, firstArg.name); + const variable = variableUtil.findVariableByName(context, firstArg); if (variable && variable.properties) { checkSorted(variable.properties); } diff --git a/lib/rules/state-in-constructor.js b/lib/rules/state-in-constructor.js index e6cc87b283..b0d083601a 100644 --- a/lib/rules/state-in-constructor.js +++ b/lib/rules/state-in-constructor.js @@ -43,7 +43,7 @@ module.exports = { option === 'always' && !node.static && node.key.name === 'state' - && componentUtil.getParentES6Component(context) + && componentUtil.getParentES6Component(context, node) ) { report(context, messages.stateInitConstructor, 'stateInitConstructor', { node, @@ -54,8 +54,8 @@ module.exports = { if ( option === 'never' && componentUtil.isStateMemberExpression(node.left) - && astUtil.inConstructor(context) - && componentUtil.getParentES6Component(context) + && astUtil.inConstructor(context, node) + && componentUtil.getParentES6Component(context, node) ) { report(context, messages.stateInitClassProp, 'stateInitClassProp', { node, diff --git a/lib/rules/static-property-placement.js b/lib/rules/static-property-placement.js index 59f559121b..e6c7cbc22c 100644 --- a/lib/rules/static-property-placement.js +++ b/lib/rules/static-property-placement.js @@ -6,9 +6,9 @@ 'use strict'; const fromEntries = require('object.fromentries'); +const astUtil = require('../util/ast'); const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); -const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); const propsUtil = require('../util/props'); const report = require('../util/report'); @@ -95,12 +95,14 @@ module.exports = { // ---------------------------------------------------------------------- /** - * Checks if we are declaring context in class - * @returns {Boolean} True if we are declaring context in class, false if not. + * Checks if we are declaring context in class + * + * @param {ASTNode} node The node to check + * @returns {Boolean} True if we are declaring context in class, false if not. */ - function isContextInClass() { + function isContextInClass(node) { let blockNode; - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); while (scope) { blockNode = scope.block; if (blockNode && blockNode.type === 'ClassDeclaration') { @@ -148,7 +150,7 @@ module.exports = { // ---------------------------------------------------------------------- return { 'ClassProperty, PropertyDefinition'(node) { - if (!componentUtil.getParentES6Component(context)) { + if (!componentUtil.getParentES6Component(context, node)) { return; } @@ -159,7 +161,7 @@ module.exports = { // If definition type is undefined then it must not be a defining expression or if the definition is inside a // class body then skip this node. const right = node.parent.right; - if (!right || right.type === 'undefined' || isContextInClass()) { + if (!right || right.type === 'undefined' || isContextInClass(node)) { return; } @@ -177,7 +179,7 @@ module.exports = { MethodDefinition(node) { // If the function is inside a class and is static getter then check if correctly positioned - if (componentUtil.getParentES6Component(context) && node.static && node.kind === 'get') { + if (componentUtil.getParentES6Component(context, node) && node.static && node.kind === 'get') { // Report error if needed reportNodeIncorrectlyPositioned(node, STATIC_GETTER); } diff --git a/lib/rules/style-prop-object.js b/lib/rules/style-prop-object.js index 4d6684a6f1..038d53b094 100644 --- a/lib/rules/style-prop-object.js +++ b/lib/rules/style-prop-object.js @@ -61,7 +61,7 @@ module.exports = { * @param {object} node A Identifier node */ function checkIdentifiers(node) { - const variable = variableUtil.variablesInScope(context).find((item) => item.name === node.name); + const variable = variableUtil.variablesInScope(context, node).find((item) => item.name === node.name); if (!variable || !variable.defs[0] || !variable.defs[0].node.init) { return; diff --git a/lib/types.d.ts b/lib/types.d.ts index e13e204524..c55be767f0 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -5,7 +5,19 @@ declare global { interface ASTNode extends estree.BaseNode { [_: string]: any; // TODO: fixme } - type Scope = eslint.Scope.Scope; + interface Reference extends eslint.Scope.Reference { + identifier: eslint.Scope.Reference['identifier'] & { parent?: ASTNode }; + } + interface Variable extends eslint.Scope.Variable { + references: Reference[] + } + interface Scope extends eslint.Scope.Scope { + block: estree.Node & ASTNode; + variableScope: Scope + childScopes: Scope[] + variables: Variable[] + upper: Scope | null + } type Token = eslint.AST.Token; type Fixer = eslint.Rule.RuleFixer; type JSXAttribute = ASTNode; @@ -13,7 +25,11 @@ declare global { type JSXFragment = ASTNode; type JSXSpreadAttribute = ASTNode; - type Context = eslint.Rule.RuleContext + type SourceCode = eslint.SourceCode & { + getScope: (node: ASTNode) => Scope | null; + getAncestors: (node: ASTNode) => ASTNode[]; + }; + type Context = eslint.Rule.RuleContext & { sourceCode?: SourceCode }; type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set) => object; diff --git a/lib/util/Components.js b/lib/util/Components.js index a31989702f..582dc263f6 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -292,10 +292,11 @@ function componentRule(rule, context) { * Check if variable is destructured from pragma import * * @param {string} variable The variable name to check + * @param {ASTNode} node The node to check its name * @returns {Boolean} True if createElement is destructured from the pragma */ - isDestructuredFromPragmaImport(variable) { - return isDestructuredFromPragmaImport(variable, context); + isDestructuredFromPragmaImport(variable, node) { + return isDestructuredFromPragmaImport(variable, context, node); }, isReturningJSX(ASTNode, strict) { @@ -412,7 +413,7 @@ function componentRule(rule, context) { return wrapperFunction.property === node.callee.name && (!wrapperFunction.object // Functions coming from the current pragma need special handling - || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node.callee.name)) + || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node.callee.name, node)) ); }); }, @@ -427,13 +428,14 @@ function componentRule(rule, context) { /** * Get the parent component node from the current scope * + * @param {ASTNode} node * @returns {ASTNode} component node, null if we are not in a component */ - getParentComponent() { + getParentComponent(node) { return ( - componentUtil.getParentES6Component(context) - || componentUtil.getParentES5Component(context) - || utils.getParentStatelessComponent() + componentUtil.getParentES6Component(context, node) + || componentUtil.getParentES5Component(context, node) + || utils.getParentStatelessComponent(node) ); }, @@ -612,10 +614,11 @@ function componentRule(rule, context) { /** * Get the parent stateless component node from the current scope * + * @param {ASTNode} _node * @returns {ASTNode} component node, null if we are not in a component */ - getParentStatelessComponent() { - let scope = context.getScope(); + getParentStatelessComponent(_node) { + let scope = (context.sourceCode || context).getScope(_node); while (scope) { const node = scope.block; const statelessComponent = utils.getStatelessComponent(node); @@ -641,6 +644,7 @@ function componentRule(rule, context) { let componentNode; // Get the component path const componentPath = []; + const initalNode = node; while (node) { if (node.property && node.property.type === 'Identifier') { componentPath.push(node.property.name); @@ -659,7 +663,7 @@ function componentRule(rule, context) { return null; } let variableInScope; - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, initalNode); for (i = 0, j = variables.length; i < j; i++) { if (variables[i].name === variableName) { variableInScope = variables[i]; @@ -776,7 +780,7 @@ function componentRule(rule, context) { && node.callee.type === 'Identifier' && node.callee.name.match(USE_HOOK_PREFIX_REGEX); - const scope = (isPotentialReactHookCall || isPotentialHookCall) && context.getScope(); + const scope = (isPotentialReactHookCall || isPotentialHookCall) && astUtil.getScope(context, node); const reactResolvedDefs = isPotentialReactHookCall && scope.references @@ -888,7 +892,7 @@ function componentRule(rule, context) { }, ThisExpression(node) { - const component = utils.getParentStatelessComponent(); + const component = utils.getParentStatelessComponent(node); if (!component || !/Function/.test(component.type) || !node.parent.property) { return; } diff --git a/lib/util/annotations.js b/lib/util/annotations.js index 60aaef8cd0..6044ac34f8 100644 --- a/lib/util/annotations.js +++ b/lib/util/annotations.js @@ -19,7 +19,7 @@ function isAnnotatedFunctionPropsDeclaration(node, context) { const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0]; - const tokens = context.getFirstTokens(typeNode, 2); + const tokens = (context.sourceCode || context).getFirstTokens(typeNode, 2); const isAnnotated = typeNode.typeAnnotation; const isDestructuredProps = typeNode.type === 'ObjectPattern'; const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); diff --git a/lib/util/ast.js b/lib/util/ast.js index fd6019a3f2..9e846c7698 100644 --- a/lib/util/ast.js +++ b/lib/util/ast.js @@ -201,6 +201,29 @@ function getFirstNodeInLine(context, node) { return token; } +/** + * Get the scope of a context bound node + * @param {Context} context + * @param {ASTNode} node + * @return {Scope} + */ +function getScope(context, node) { + if (context.sourceCode && context.sourceCode.getScope) { + return context.sourceCode.getScope(node); + } + return context.getScope(); +} + +/** + * Get the ancestors of a node + * @param {Context} context + * @param {ASTNode} node + * @return {ASTNode[]} + */ +function getAncestors(context, node) { + return (context.sourceCode || context).getAncestors(node); +} + /** * Checks if the node is the first in its line, excluding whitespace. * @param {Object} context The node to check @@ -253,10 +276,11 @@ function isClass(node) { /** * Check if we are in a class constructor * @param {Context} context + * @param {ASTNode} node * @return {boolean} */ -function inConstructor(context) { - let scope = context.getScope(); +function inConstructor(context, node) { + let scope = getScope(context, node); while (scope) { // @ts-ignore if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') { @@ -431,11 +455,13 @@ function isTSTypeParameterInstantiation(node) { module.exports = { traverse, findReturnStatement, + getAncestors, getFirstNodeInLine, getPropertyName, getPropertyNameNode, getComponentProperties, getKeyValue, + getScope, isParenthesized, isAssignmentLHS, isClass, diff --git a/lib/util/componentUtil.js b/lib/util/componentUtil.js index 35d54edcc5..588a0e52d8 100644 --- a/lib/util/componentUtil.js +++ b/lib/util/componentUtil.js @@ -116,10 +116,11 @@ function isES6Component(node, context) { /** * Get the parent ES5 component node from the current scope * @param {Context} context + * @param {ASTNode} _node * @returns {ASTNode|null} */ -function getParentES5Component(context) { - let scope = context.getScope(); +function getParentES5Component(context, _node) { + let scope = (context.sourceCode || context).getScope(_node); while (scope) { // @ts-ignore const node = scope.block && scope.block.parent && scope.block.parent.parent; @@ -134,10 +135,11 @@ function getParentES5Component(context) { /** * Get the parent ES6 component node from the current scope * @param {Context} context + * @param {ASTNode} _node * @returns {ASTNode | null} */ -function getParentES6Component(context) { - let scope = context.getScope(); +function getParentES6Component(context, _node) { + let scope = (context.sourceCode || context).getScope(_node); while (scope && scope.type !== 'class') { scope = scope.upper; } @@ -156,7 +158,7 @@ function getParentES6Component(context) { */ function isPureComponent(node, context) { const pragma = getPragma(context); - const sourceCode = context.getSourceCode(); + const sourceCode = context.sourceCode || context.getSourceCode(); if (node.superClass) { return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(sourceCode.getText(node.superClass)); } diff --git a/lib/util/defaultProps.js b/lib/util/defaultProps.js index 44b42454ce..57e1ce96b8 100644 --- a/lib/util/defaultProps.js +++ b/lib/util/defaultProps.js @@ -24,7 +24,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { */ function resolveNodeValue(node) { if (node.type === 'Identifier') { - return variableUtil.findVariableByName(context, node.name); + return variableUtil.findVariableByName(context, node); } if ( node.type === 'CallExpression' @@ -172,7 +172,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } // find component this propTypes/defaultProps belongs to - const component = components.get(componentUtil.getParentES6Component(context)); + const component = components.get(componentUtil.getParentES6Component(context, node)); if (!component) { return; } @@ -215,7 +215,7 @@ module.exports = function defaultPropsInstructions(context, components, utils) { } // find component this propTypes/defaultProps belongs to - const component = components.get(componentUtil.getParentES6Component(context)); + const component = components.get(componentUtil.getParentES6Component(context, node)); if (!component) { return; } diff --git a/lib/util/isCreateElement.js b/lib/util/isCreateElement.js index c28dc563fb..53d2e4514f 100644 --- a/lib/util/isCreateElement.js +++ b/lib/util/isCreateElement.js @@ -24,7 +24,7 @@ module.exports = function isCreateElement(node, context) { node && node.callee && node.callee.name === 'createElement' - && isDestructuredFromPragmaImport('createElement', context) + && isDestructuredFromPragmaImport('createElement', context, node) ) { return true; } diff --git a/lib/util/isDestructuredFromPragmaImport.js b/lib/util/isDestructuredFromPragmaImport.js index 6f8deb08bc..15c330e1d5 100644 --- a/lib/util/isDestructuredFromPragmaImport.js +++ b/lib/util/isDestructuredFromPragmaImport.js @@ -8,11 +8,12 @@ const variableUtil = require('./variable'); * * @param {string} variable The variable name to check * @param {Context} context eslint context + * @param {ASTNode} node The node to check its name * @returns {Boolean} True if createElement is destructured from the pragma */ -module.exports = function isDestructuredFromPragmaImport(variable, context) { +module.exports = function isDestructuredFromPragmaImport(variable, context, node) { const pragma = pragmaUtil.getFromContext(context); - const variables = variableUtil.variablesInScope(context); + const variables = variableUtil.variablesInScope(context, node); const variableInScope = variableUtil.getVariable(variables, variable); if (variableInScope) { const latestDef = variableUtil.getLatestVariableDefinition(variableInScope); diff --git a/lib/util/jsx.js b/lib/util/jsx.js index 55073bfe1e..87317b6605 100644 --- a/lib/util/jsx.js +++ b/lib/util/jsx.js @@ -121,7 +121,7 @@ function isReturningJSX(ASTnode, context, strict, ignoreNull) { } return false; case 'Identifier': { - const variable = variableUtil.findVariableByName(context, node.name); + const variable = variableUtil.findVariableByName(context, node); return isJSX(variable); } default: diff --git a/lib/util/makeNoMethodSetStateRule.js b/lib/util/makeNoMethodSetStateRule.js index 1225092802..2bf2b6a261 100644 --- a/lib/util/makeNoMethodSetStateRule.js +++ b/lib/util/makeNoMethodSetStateRule.js @@ -7,6 +7,7 @@ const findLast = require('array.prototype.findlast'); +const astUtil = require('./ast'); const docsUrl = require('./docsUrl'); const report = require('./report'); const testReactVersion = require('./version').testReactVersion; @@ -93,7 +94,7 @@ module.exports = function makeNoMethodSetStateRule(methodName, shouldCheckUnsafe ) { return; } - const ancestors = context.getAncestors(callee); + const ancestors = astUtil.getAncestors(context, callee); let depth = 0; findLast(ancestors, (ancestor) => { // ancestors.some((ancestor) => { diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index a189f871ca..1b11bd2a5f 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -368,10 +368,15 @@ module.exports = function propTypesInstructions(context, components, utils) { node && node.type === 'Identifier' ) { - const scope = context.getScope(); - const identVariable = scope.variableScope.variables.find( - (variable) => variable.name === node.name - ); + const scope = astUtil.getScope(context, node); + const allVars = []; + let vars = (scope.childScopes[0] || scope).variableScope; + do { + vars.variables.forEach((v) => allVars.push(v)); + vars = vars.upper; + } while (vars); + const identVariable = allVars.find((v) => v.defs.length && v.name === node.name); + if (identVariable) { const definition = identVariable.defs[identVariable.defs.length - 1]; callback(definition.node.init); @@ -934,7 +939,7 @@ module.exports = function propTypesInstructions(context, components, utils) { break; } case 'Identifier': { - const variablesInScope = variableUtil.variablesInScope(context); + const variablesInScope = variableUtil.variablesInScope(context, node); const firstMatchingVariable = variablesInScope .find((variableInScope) => variableInScope.name === propTypes.name); if (firstMatchingVariable) { diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index 6a0a650333..d11cc8c99f 100644 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -81,10 +81,11 @@ function mustBeValidated(component) { * Check if we are in a lifecycle method * @param {object} context * @param {boolean} checkAsyncSafeLifeCycles + * @param {ASTNode} node * @return {boolean} true if we are in a class constructor, false if not */ -function inLifeCycleMethod(context, checkAsyncSafeLifeCycles) { - let scope = context.getScope(); +function inLifeCycleMethod(context, checkAsyncSafeLifeCycles, node) { + let scope = astUtil.getScope(context, node); while (scope) { if (scope.block && scope.block.parent && scope.block.parent.key) { const name = scope.block.parent.key.name; @@ -158,11 +159,12 @@ function isSetStateUpdater(node) { && node.parent.arguments[0] === node; } -function isPropArgumentInSetStateUpdater(context, name) { +function isPropArgumentInSetStateUpdater(context, node) { + const name = node.name; if (typeof name !== 'string') { return; } - let scope = context.getScope(); + let scope = astUtil.getScope(context, node); while (scope) { const unwrappedParentCalleeNode = scope.block && scope.block.parent @@ -186,10 +188,11 @@ function isPropArgumentInSetStateUpdater(context, name) { /** * @param {Context} context + * @param {ASTNode} node * @returns {boolean} */ -function isInClassComponent(context) { - return !!(componentUtil.getParentES6Component(context) || componentUtil.getParentES5Component(context)); +function isInClassComponent(context, node) { + return !!(componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node)); } /** @@ -226,7 +229,7 @@ function hasSpreadOperator(context, node) { function isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafeLifeCycles) { const unwrappedObjectNode = ast.unwrapTSAsExpression(node.object); - if (isInClassComponent(context)) { + if (isInClassComponent(context, node)) { // this.props.* if (isThisDotProps(unwrappedObjectNode)) { return true; @@ -234,12 +237,12 @@ function isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafe // props.* or prevProps.* or nextProps.* if ( isCommonVariableNameForProps(unwrappedObjectNode.name) - && (inLifeCycleMethod(context, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context)) + && (inLifeCycleMethod(context, checkAsyncSafeLifeCycles, node) || astUtil.inConstructor(context, node)) ) { return true; } // this.setState((_, props) => props.*)) - if (isPropArgumentInSetStateUpdater(context, unwrappedObjectNode.name)) { + if (isPropArgumentInSetStateUpdater(context, unwrappedObjectNode)) { return true; } return false; @@ -362,7 +365,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) throw new Error(`${node.type} ASTNodes are not handled by markPropTypesAsUsed`); } - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); const usedPropTypes = (component && component.usedPropTypes) || []; let ignoreUnusedPropTypesValidation = (component && component.ignoreUnusedPropTypesValidation) || false; @@ -472,7 +475,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) const unwrappedInitNode = ast.unwrapTSAsExpression(node.init); // let props = this.props - if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context) && node.id.type === 'Identifier') { + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node) && node.id.type === 'Identifier') { propVariables.set(node.id.name, []); } @@ -501,14 +504,14 @@ module.exports = function usedPropTypesInstructions(context, components, utils) // let {firstname} = props if ( isCommonVariableNameForProps(unwrappedInitNode.name) - && (utils.getParentStatelessComponent() || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles)) + && (utils.getParentStatelessComponent(node) || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles)) ) { markPropTypesAsUsed(node.id); return; } // let {firstname} = this.props - if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context)) { + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node)) { markPropTypesAsUsed(node.id); return; } @@ -532,7 +535,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils) 'FunctionExpression:exit': popScope, JSXSpreadAttribute(node) { - const component = components.get(utils.getParentComponent()); + const component = components.get(utils.getParentComponent(node)); components.set(component ? component.node : node, { ignoreUnusedPropTypesValidation: node.argument.type !== 'ObjectExpression', }); diff --git a/lib/util/variable.js b/lib/util/variable.js index a93cf18b5e..1293ec6d75 100644 --- a/lib/util/variable.js +++ b/lib/util/variable.js @@ -6,6 +6,7 @@ 'use strict'; const toReversed = require('array.prototype.toreversed'); +const astUtil = require('./ast'); /** * Search a particular variable in a list @@ -27,16 +28,17 @@ function getVariable(variables, name) { return variables.find((variable) => variable.name === name); } -/** - * List all variable in a given scope - * - * Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21 - * - * @param {Object} context The current rule context. - * @returns {Array} The variables list - */ -function variablesInScope(context) { - let scope = context.getScope(); +function variablesInScope(context, node) { + /** + * List all variable in a given scope + * + * Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21 + * + * @param {Object} context The current rule context. + * @param {ASTNode} node The node to start looking for variables from. + * @returns {Array} The variables list + */ + let scope = astUtil.getScope(context, node); let variables = scope.variables; while (scope.type !== 'global') { @@ -56,11 +58,12 @@ function variablesInScope(context) { /** * Find a variable by name in the current scope. * @param {Object} context The current rule context. - * @param {string} name Name of the variable to look for. + * @param {ASTNode} node The node to check its name. + * @param {string} [name] The name of the variable to search. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. */ -function findVariableByName(context, name) { - const variable = getVariable(variablesInScope(context), name); +function findVariableByName(context, node, name) { + const variable = getVariable(variablesInScope(context, node), name || node.name); if (!variable || !variable.defs[0] || !variable.defs[0].node) { return null; diff --git a/package.json b/package.json index 66d7078685..0eae7f391e 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "postlint:docs": "npm run update:eslint-docs -- --check", "lint": "eslint .", "postlint": "npm run type-check", - "pretest": "npm run lint", + "pretest": "ESLINT_USE_FLAT_CONFIG=false npm run lint", "test": "npm run unit-test", "posttest": "aud --production", "type-check": "tsc", - "unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js", + "unit-test": "ESLINT_USE_FLAT_CONFIG=false istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js", "update:eslint-docs": "eslint-doc-generator" }, "repository": { @@ -57,7 +57,7 @@ "@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4.0.0 || ^5.0.0", "aud": "^2.0.4", "babel-eslint": "^8 || ^9 || ^10.1.0", - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint-config-airbnb-base": "^15.0.0", "eslint-doc-generator": "^1.7.0", "eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5", @@ -78,7 +78,7 @@ "typescript-eslint-parser": "^20.1.1" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" }, "engines": { "node": ">=4"