diff --git a/src/helpers.ts b/src/helpers.ts index 11afaf24..f7d7843c 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,4 +1,3 @@ -/* eslint no-nested-ternary: off */ import browserslist from "browserslist"; import type { Rule } from "eslint"; import type * as ESTree from "estree"; @@ -15,21 +14,15 @@ import { TargetNameMappings } from "./constants"; - Each API is given to versioning.ts with compatibility info */ -enum GuardType { - // Guard is true if the browser supports the API - POSITIVE, - // Guard is false if the browser supports the API - NEGATIVE, -} - export interface GuardedScope { - scope: ESTree.Node & Rule.NodeParentExtension; + scope: Rule.Node; index: number; } /** - * Checks if the given node is used in a guard, and whether it's used to guard for or against the - * block statement. Should be called with either a bare Identifier, or a MemberExpression. + * Checks if the given node is used in an if statement, and if it is, returns the + * scope that the guard applies to and after which index it applies. + * Should be called with either a bare Identifier or a MemberExpression. */ export function determineGuardedScope( node: (ESTree.Identifier | ESTree.MemberExpression) & Rule.NodeParentExtension @@ -37,18 +30,18 @@ export function determineGuardedScope( const result = getIfStatementAndGuardType(node); if (!result) return null; - const [ifStatement, guardType] = result; + const [ifStatement, positiveGuard] = result; - if (guardType === GuardType.POSITIVE) { - return { scope: ifStatement.consequent as GuardedScope["scope"], index: 0 }; + if (positiveGuard) { + // It's okay to use the identifier inside of the if statement + return { scope: ifStatement.consequent as Rule.Node, index: 0 }; } - // guardType is NEGATIVE - if ( ifStatementHasEarlyReturn(ifStatement) && isBlockOrProgram(ifStatement.parent) ) { + // It's okay to use the identifier after the if statement const scope = ifStatement.parent; const index = scope.body.indexOf(ifStatement) + 1; return { scope, index }; @@ -65,9 +58,9 @@ export function isBlockOrProgram( function getIfStatementAndGuardType( node: (ESTree.Identifier | ESTree.MemberExpression) & Rule.NodeParentExtension -): [ESTree.IfStatement & Rule.NodeParentExtension, GuardType] | null { - let guardType = GuardType.POSITIVE; - let expression: ESTree.Node & Rule.NodeParentExtension = node; +): [ESTree.IfStatement & Rule.NodeParentExtension, boolean] | null { + let positiveGuard = true; + let expression: Rule.Node = node; if ( node.parent?.type === "UnaryExpression" && @@ -92,11 +85,11 @@ function getIfStatementAndGuardType( ) { // typeof foo === "function" // typeof foo !== "undefined" - guardType = GuardType.POSITIVE; + positiveGuard = true; } else { // typeof foo !== "function" // typepf foo === "undefined" - guardType = GuardType.NEGATIVE; + positiveGuard = false; } } @@ -109,20 +102,17 @@ function getIfStatementAndGuardType( expression.parent.operator === "!" ) { expression = expression.parent; - if (guardType === GuardType.POSITIVE) { - guardType = GuardType.NEGATIVE; - } else { - guardType = GuardType.POSITIVE; - } + positiveGuard = !positiveGuard; } + // TODO: allow && checks, but || checks aren't really safe // skip over && and || expressions while (expression.parent?.type === "LogicalExpression") { expression = expression.parent; } if (expression.parent?.type === "IfStatement") { - return [expression.parent, guardType]; + return [expression.parent, positiveGuard]; } return null; diff --git a/src/rules/compat.ts b/src/rules/compat.ts index 34832f6f..b59b2e70 100644 --- a/src/rules/compat.ts +++ b/src/rules/compat.ts @@ -246,7 +246,7 @@ const ruleModule: Rule.RuleModule = { let object = "window"; - let expression: ESTree.Node & Rule.NodeParentExtension = node; + let expression: Rule.Node = node; if (expression.parent.type === "MemberExpression") { expression = expression.parent; if (expression.object.type === "MemberExpression") {