import type { TSESLint } from '@typescript-eslint/experimental-utils' ; /**
 * When leveraging the convertAnnotatedSourceToFailureCase() utility, the
 * following characters are eligible to be used in the source code of expected
 * failure cases within ESLint unit tests in order to provide an easy way to
 * annotate where one or more ESLint errors are expected to occur within that
 * source.
 *
 * See the convertAnnotatedSourceToFailureCase() utility itself for more details.
 */ export const SPECIAL_UNDERLINE_CHARS = [ '~' , '^' , '#' , '%' , '¶' , '*' , '¨' , '@' , ] as const ; type MultipleErrorOptions < TMessageIds extends string > = BaseErrorOptions & { readonly messages : readonly ( Message < TMessageIds > & { readonly char : typeof SPECIAL_UNDERLINE_CHARS [ number ] ; } ) [ ] ; } ; type BaseErrorOptions = { readonly description : string ; readonly annotatedSource : string ; readonly options ? : readonly unknown [ ] ; readonly annotatedOutput ? : string ; readonly filename ? : string ; } ; type Message < TMessageIds extends string > = { readonly messageId : TMessageIds ; readonly data ? : Record < string , unknown > ; readonly suggestions ? : TSESLint . SuggestionOutput < TMessageIds > [ ] ; } ; type SingleErrorOptions < TMessageIds extends string > = BaseErrorOptions & Message < TMessageIds > ; Pipe export function convertAnnotatedSourceToFailureCase < TMessageIds extends string > ( errorOptions ) ; export function convertAnnotatedSourceToFailureCase < TMessageIds extends string > ( errorOptions ) ; export function convertAnnotatedSourceToFailureCase < TMessageIds extends string > ( errorOptions ) { const messages = 'messageId' in errorOptions ? [ { ... errorOptions , char : '~' } ] : errorOptions . messages ; let parsedSource = '' ; const errors = messages . map ( ( { char : currentValueChar , data , messageId , suggestions } ) => { const otherChars = messages . map ( ( { char } ) => char ) . filter ( ( char ) => char !== currentValueChar ) ; const parsedForChar = parseInvalidSource ( errorOptions . annotatedSource , currentValueChar , otherChars ) ; const { failure : { endPosition , startPosition } , } = parsedForChar ; parsedSource = parsedForChar . source ; if ( ! endPosition || ! startPosition ) { throw Error ( `Char '${ currentValueChar }' has been specified in \`messages\`, however it is not present in the source of the failure case` ) ; } return { data , messageId , line : startPosition . line + 1 , column : startPosition . character + 1 , endLine : endPosition . line + 1 , endColumn : endPosition . character + 1 , suggestions , } ; } ) ; return { code : parsedSource , filename : errorOptions . filename , options : errorOptions . options ?? [ ] , errors , output : errorOptions . annotatedOutput ? parseInvalidSource ( errorOptions . annotatedOutput ) . source : null , } ; } type SourcePosition = { readonly character : number ; readonly line : number ; } ; type ExpectedFailure = { readonly endPosition ? : SourcePosition ; readonly message : string ; readonly startPosition ? : SourcePosition ; } ; function escapeRegexp ( value ) { return value . replace ( /[-/\\^$*+?.()|[\]{}]/g , '\\$&' ) ; } param source param specialChar param otherChars returns { { source : string , failure : { message : string , startPosition : null , endPosition : any } } } function parseInvalidSource ( source , specialChar = '~' , otherChars = [ ] ) { let replacedSource ; if ( otherChars . length === 0 ) { replacedSource = source ; } else { const patternAsStr = `[${ otherChars . map ( escapeRegexp ) . join ( '' ) }]` ; const pattern = RegExp ( patternAsStr , 'g' ) ; replacedSource = source . replace ( pattern , ' ' ) ; } let col = 0 ; let line = 0 ; let lastCol = 0 ; let lastLine = 0 ; let startPosition ; for ( const currentChar of replacedSource ) { if ( currentChar === '\n' ) { col = 0 ; line ++ ; continue ; } col ++ ; if ( currentChar !== specialChar ) continue ; if ( ! startPosition ) { startPosition = { character : col - 1 , line : line - 1 , } ; } lastCol = col ; lastLine = line - 1 ; } const endPosition = { character : lastCol , line : lastLine , } ; const newSource = replacedSource . replace ( RegExp ( escapeRegexp ( specialChar ) , 'g' ) , '' ) ; return { failure : { endPosition , message : '' , startPosition : startPosition ! , } , source : newSource , } ; }