import browserslist from "browserslist" ; import { AstMetadataApiWithTargetsResolver , ESLintNode , BrowserListConfig , Target , HandleFailingRule , Context , } from "./types" ; import { TargetNameMappings } from "./constants" ; function isInsideIfStatement ( context ) { return context . getAncestors ( ) . some ( ( ancestor ) => { return ancestor . type === "IfStatement" ; } ) ; } function checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) { if ( ! isInsideIfStatement ( context ) ) { handleFailingRule ( failingRule , node ) ; } } export function lintCallExpression ( context , handleFailingRule , rules , node ) { if ( ! node . callee ) return ; const calleeName = node . callee . name ; const failingRule = rules . find ( ( rule ) => rule . object === calleeName ) ; if ( failingRule ) checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) ; } export function lintNewExpression ( context , handleFailingRule , rules , node ) { if ( ! node . callee ) return ; const calleeName = node . callee . name ; const failingRule = rules . find ( ( rule ) => rule . object === calleeName ) ; if ( failingRule ) checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) ; } export function lintExpressionStatement ( context , handleFailingRule , rules , node ) { if ( ! node ?. expression ?. name ) return ; const failingRule = rules . find ( ( rule ) => rule . object === node ?. expression ?. name ) ; if ( failingRule ) checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) ; } function protoChainFromMemberExpression ( node ) { if ( ! node . object ) return [ node . name ] ; const protoChain = ( ( ) => { switch ( node . object . type ) { case "NewExpression" : case "CallExpression" : return protoChainFromMemberExpression ( node . object . callee ) ; default : return protoChainFromMemberExpression ( node . object ) ; } } ) ( ) ; return [ ... protoChain , node . property . name ] ; } export function lintMemberExpression ( context , handleFailingRule , rules , node ) { if ( ! node . object || ! node . property ) return ; if ( ! node . object . name || node . object . name === "window" || node . object . name === "globalThis" ) { const rawProtoChain = protoChainFromMemberExpression ( node ) ; const [ firstObj ] = rawProtoChain ; const protoChain = firstObj === "window" || firstObj === "globalThis" ? rawProtoChain . slice ( 1 ) : rawProtoChain ; const protoChainId = protoChain . join ( "." ) ; const failingRule = rules . find ( ( rule ) => rule . protoChainId === protoChainId ) ; if ( failingRule ) { checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) ; } } else { const objectName = node . object . name ; const propertyName = node . property . name ; const failingRule = rules . find ( ( rule ) => rule . object === objectName && ( rule . property == null || rule . property === propertyName ) ) ; if ( failingRule ) checkNotInsideIfStatementAndReport ( context , handleFailingRule , failingRule , node ) ; } } export function reverseTargetMappings ( targetMappings ) { const reversedEntries = Object . entries ( targetMappings ) . map ( ( entry ) => entry . reverse ( ) ) ; return Object . fromEntries ( reversedEntries ) ; } param configPath export function determineTargetsFromConfig ( configPath , config ? ) { const browserslistOpts = { path : configPath } ; const eslintTargets = ( ( ) => { if ( Array . isArray ( config ) || typeof config === "string" ) { return browserslist ( config , browserslistOpts ) ; } if ( config && typeof config === "object" ) { return browserslist ( [ ... ( config . production || [ ] ) , ... ( config . development || [ ] ) ] , browserslistOpts ) ; } return [ ] ; } ) ( ) ; if ( browserslist . findConfig ( configPath ) ) { if ( eslintTargets . length ) { const browserslistTargets = browserslist ( undefined , browserslistOpts ) ; return Array . from ( new Set ( eslintTargets . concat ( browserslistTargets ) ) ) ; } } else if ( eslintTargets . length ) { return eslintTargets ; } return browserslist ( undefined , browserslistOpts ) ; } param targetslist returns export function parseBrowsersListVersion ( targetslist ) { return ( targetslist . map ( ( e ) : Target => { const [ target , version ] = e . split ( " " ) as [ keyof TargetNameMappings , number | string ] ; const parsedVersion = ( ( ) => { if ( typeof version === "number" ) return version ; if ( version === "all" ) return 0 ; return version . includes ( "-" ) ? parseFloat ( version . split ( "-" ) [ 0 ] ) : parseFloat ( version ) ; } ) ( ) ; return { target , version , parsedVersion , } ; } ) . sort ( ( a , b ) : number => { if ( b . target === a . target ) { return typeof b . parsedVersion === "string" || typeof a . parsedVersion === "string" ? 0 : b . parsedVersion - a . parsedVersion ; } return b . target > a . target ? 1 : - 1 ; } ) . filter ( ( e , i , items ) : boolean => i + 1 === items . length || e . target !== items [ i + 1 ] . target ) ) ; }