import * as fs from "fs-extra" ; const path = require ( "path" ) ; const glob = require ( "glob" ) ; import ApexTypeListener from "./listeners/ApexTypeListener" ; import { ApexLexer , ApexParser , ApexParserListener , CaseInsensitiveInputStream , ThrowingErrorListener , CommonTokenStream , ParseTreeWalker } from "apex-parser" ; import SFPLogger , { LoggerLevel } from "../../logger/SFPLogger" ; import { ApexClasses } from "../../package/SFPPackage" ; /**
 * Get Apex type of cls files in a search directory.
 * Sorts files into classes, test classes and interfaces.
 */ export default class ApexTypeFetcher { private apexSortedByType : ApexSortedByType = { class : [ ] , testClass : [ ] , interface : [ ] , parseError : [ ] , } ; constructor ( private searchDir ) { } public getClassesClassifiedByType ( ) { let clsFiles ; if ( fs . existsSync ( this . searchDir ) ) { clsFiles = glob . sync ( `**/*.cls` , { cwd : this . searchDir , absolute : true , } ) ; } else { throw new Error ( `Search directory does not exist` ) ; } for ( let clsFile of clsFiles ) { let clsPayload = fs . readFileSync ( clsFile , "utf8" ) ; let fileDescriptor = { name : path . basename ( clsFile , ".cls" ) , filepath : clsFile , } ; let compilationUnitContext ; try { let lexer = new ApexLexer ( new CaseInsensitiveInputStream ( clsFile , clsPayload ) ) ; let tokens = new CommonTokenStream ( lexer ) ; let parser = new ApexParser ( tokens ) ; parser . removeErrorListeners ( ) ; parser . addErrorListener ( new ThrowingErrorListener ( ) ) ; compilationUnitContext = parser . compilationUnit ( ) ; } catch ( err ) { SFPLogger . log ( `Failed to parse ${ clsFile } in ${ this . searchDir }` , LoggerLevel . WARN ) ; SFPLogger . log ( err . message , LoggerLevel . WARN ) ; fileDescriptor [ "error" ] = err ; this . apexSortedByType [ "parseError" ] . push ( fileDescriptor ) ; continue ; } let apexTypeListener = new ApexTypeListener ( ) ; ParseTreeWalker . DEFAULT . walk ( apexTypeListener as ApexParserListener , compilationUnitContext ) ; let apexType = apexTypeListener . getApexType ( ) ; if ( apexType . class ) { this . apexSortedByType [ "class" ] . push ( fileDescriptor ) ; if ( apexType . testClass ) { this . apexSortedByType [ "testClass" ] . push ( fileDescriptor ) ; } } else if ( apexType . interface ) { this . apexSortedByType [ "interface" ] . push ( fileDescriptor ) ; } else { fileDescriptor [ "error" ] = { message : "Unknown Apex Type" } ; this . apexSortedByType [ "parseError" ] . push ( fileDescriptor ) ; } } return this . apexSortedByType ; } public getTestClasses ( ) { let testClassNames = this . apexSortedByType . testClass . map ( ( fileDescriptor ) => fileDescriptor . name ) ; return testClassNames ; } public getClassesOnlyExcludingTestsAndInterfaces ( ) { let packageClasses = this . apexSortedByType . class . map ( ( fileDescriptor ) => fileDescriptor . name ) ; if ( packageClasses != null ) { let testClassesInPackage = this . apexSortedByType . testClass . map ( ( fileDescriptor ) => fileDescriptor . name ) ; if ( testClassesInPackage != null && testClassesInPackage . length > 0 ) packageClasses = packageClasses . filter ( ( item ) => ! testClassesInPackage . includes ( item ) ) ; let interfacesInPackage = this . apexSortedByType . testClass . map ( ( fileDescriptor ) => fileDescriptor . name ) ; if ( interfacesInPackage != null && interfacesInPackage . length > 0 ) packageClasses = packageClasses . filter ( ( item ) => ! interfacesInPackage . includes ( item ) ) ; let parseError = this . apexSortedByType . parseError . map ( ( fileDescriptor ) => fileDescriptor . name ) ; if ( parseError != null && parseError . length > 0 ) packageClasses = packageClasses . filter ( ( item ) => ! parseError . includes ( item ) ) ; } return packageClasses ; } } export type ApexSortedByType = { class : FileDescriptor [ ] ; testClass : FileDescriptor [ ] ; interface : FileDescriptor [ ] ; parseError : FileDescriptor [ ] ; } ; export type FileDescriptor = { name : string ; filepath : string ; error ? : any ; } ;