import { Node , NumericLiteral , Project , SourceFile , StringLiteral , SyntaxKind , TemplateLiteral , } from "ts-morph" ; import { Handler } from "../common" ; const shortCodes = new Map < number , Node > ( ) ; const newAssetFiles = new Set < SourceFile > ( ) ; let maxShortCode = - 1 ; function getCallsiteString ( msg ) { return `${ msg . getSourceFile ( ) . getFilePath ( ) }@${ msg . getStartLineNumber ( ) }` ; } param sourceFile returns function getAssertMessageParams ( sourceFile ) { const calls = sourceFile . getDescendantsOfKind ( SyntaxKind . CallExpression ) ; const messageArgs = [ ] ; for ( const call of calls ) { if ( call . getExpression ( ) . getText ( ) === "assert" ) { const args = call . getArguments ( ) ; if ( args . length >= 1 && args [ 1 ] !== undefined ) { const kind = args [ 1 ] . getKind ( ) ; switch ( kind ) { case SyntaxKind . StringLiteral : case SyntaxKind . NumericLiteral : case SyntaxKind . TemplateExpression : case SyntaxKind . NoSubstitutionTemplateLiteral : messageArgs . push ( args [ 1 ] as any ) ; break ; case SyntaxKind . BinaryExpression : case SyntaxKind . CallExpression : break ; default : throw new Error ( `Unknown argument kind: ${ kind }\n${ getCallsiteString ( args [ 1 ] ) }` ) ; } } } } return messageArgs ; } export const handler = { name : "assert-short-codes" , match : /^(packages|(common\/lib\/common-utils)|(server\/routerlicious\/packages\/protocol-base)).*\/tsconfig\.json/i , handler : ( tsconfigPath ) => { if ( tsconfigPath . includes ( "test" ) ) { return ; } const project = new Project ( { skipFileDependencyResolution : true , tsConfigFilePath : tsconfigPath , } ) ; for ( const sourceFile of project . getSourceFiles ( ) ) { for ( const msg of getAssertMessageParams ( sourceFile ) ) { if ( msg . getKind ( ) === SyntaxKind . NumericLiteral ) { const numLit = msg as NumericLiteral ; if ( ! numLit . getText ( ) . startsWith ( "0x" ) ) { return `Shortcodes must be provided by automation and be in hex format: ${ numLit . getText ( ) }\n\t${ getCallsiteString ( numLit ) }` ; } const numLitValue = numLit . getLiteralValue ( ) ; if ( shortCodes . has ( numLitValue ) ) { return `Duplicate shortcode 0x${ numLitValue . toString ( 16 ) } detected\n\t${ getCallsiteString ( shortCodes . get ( numLitValue ) ! ) }\n\t${ getCallsiteString ( numLit ) }` ; } shortCodes . set ( numLitValue , numLit ) ; maxShortCode = Math . max ( numLitValue , maxShortCode ) ; } else { newAssetFiles . add ( sourceFile ) ; } } } ; } , final : ( root , resolve ) => { const errors = [ ] ; for ( const s of newAssetFiles ) { s . refreshFromFileSystemSync ( ) ; for ( const msg of getAssertMessageParams ( s ) ) { if ( msg . getKind ( ) !== SyntaxKind . NumericLiteral ) { if ( resolve ) { const shortCode = ++ maxShortCode ; shortCodes . set ( shortCode , msg ) ; const text = msg . getText ( ) ; const shortCodeStr = `0x${ shortCode . toString ( 16 ) . padStart ( 3 , "0" ) }` ; msg . replaceWithText ( `${ shortCodeStr } /* ${ text } */` ) ; } else { break ; } } } if ( resolve ) { s . saveSync ( ) ; } } const result = errors . length > 0 ? { error : errors . join ( "\n" ) } : undefined ; return result ; } } ;