import type { TSESTree , TSESLint } from '@typescript-eslint/experimental-utils' ; type Writeable < T > = { - readonly [ P in keyof T ] : T [ P ] ; } ; export type MutableReportDescriptor = Writeable < TSESLint . ReportDescriptor < string > > ; export interface IssueLocation { column : number ; line : number ; endColumn : number ; endLine : number ; message ? : string ; data ? : Record < string , unknown > ; } export interface EncodedMessage { message : string ; cost ? : number ; secondaryLocations : IssueLocation [ ] ; } /**
 * Returns a location of the "main" function token:
 * - function name for a function declaration, method or accessor
 * - "function" keyword for a function expression
 * - "=>" for an arrow function
 */ export function getMainFunctionTokenLocation < T = string > ( fn , parent , context ) { let location ; if ( fn . type === 'FunctionDeclaration' ) { if ( fn . id ) { location = fn . id . loc ; } else { const token = getTokenByValue ( fn , 'function' , context ) ; location = token && token . loc ; } } else if ( fn . type === 'FunctionExpression' ) { if ( parent && ( parent . type === 'MethodDefinition' || parent . type === 'Property' ) ) { location = parent . key . loc ; } else { const token = getTokenByValue ( fn , 'function' , context ) ; location = token && token . loc ; } } else if ( fn . type === 'ArrowFunctionExpression' ) { const token = context . getSourceCode ( ) . getTokensBefore ( fn . body ) . reverse ( ) . find ( token => token . value === '=>' ) ; location = token && token . loc ; } return location ! ; } /**
 * Wrapper for `context.report`, supporting secondary locations and cost.
 * Encode those extra information in the issue message when rule is executed
 * in Sonar* environment.
 */ export function report < T = string > ( context , reportDescriptor , secondaryLocations , message , cost ? ) { if ( ( context . options [ context . options . length - 1 ] as unknown ) !== 'sonar-runtime' ) { context . report ( reportDescriptor ) ; return ; } const encodedMessage = { secondaryLocations , message : expandMessage ( message , reportDescriptor . data ) , cost , } ; reportDescriptor . messageId = 'sonarRuntime' ; if ( reportDescriptor . data === undefined ) { reportDescriptor . data = { } ; } ( reportDescriptor . data as Record < string , unknown > ) . sonarRuntimeData = JSON . stringify ( encodedMessage ) ; context . report ( reportDescriptor ) ; } function expandMessage ( message , reportDescriptorData ) { let expandedMessage = message ; if ( reportDescriptorData !== undefined ) { for ( const [ key , value ] of Object . entries ( reportDescriptorData ) ) { expandedMessage = replaceAll ( expandedMessage , `{{${ key }}}` , ( value as object ) . toString ( ) ) ; } } return expandedMessage ; } function replaceAll ( target , search , replacement ) { return target . split ( search ) . join ( replacement ) ; } /**
 * Converts `SourceLocation` range into `IssueLocation`
 */ export function issueLocation ( startLoc , endLoc = startLoc , message = '' , data = { } ) { const issueLocation = { line : startLoc . start . line , column : startLoc . start . column , endLine : endLoc . end . line , endColumn : endLoc . end . column , message , } ; if ( data !== undefined && Object . keys ( data ) . length > 0 ) { issueLocation . data = data ; } return issueLocation ; } export function toSecondaryLocation ( locationHolder , message ? ) { const { loc } = locationHolder ; return { message , column : loc . start . column , line : loc . start . line , endColumn : loc . end . column , endLine : loc . end . line , } ; } function getTokenByValue < T = string > ( node , value , context ) { return context . getSourceCode ( ) . getTokens ( node ) . find ( token => token . value === value ) ; } export function getFirstTokenAfter < T = string > ( node , context ) { return context . getSourceCode ( ) . getTokenAfter ( node ) ; } export function getFirstToken < T = string > ( node , context ) { return context . getSourceCode ( ) . getTokens ( node ) [ 0 ] ; }