/**
 * Fake implementations of various compiler dependencies.
 */ namespace fakes { const processExitSentinel = new Error ( "System exit" ) ; export interface SystemOptions { executingFilePath ? : string ; newLine ? : "\r\n" | "\n" ; env ? : Record < string , string > ; } /**
     * A fake `ts.System` that leverages a virtual file system.
     */ export class System implements ts . System { public readonly vfs : vfs . FileSystem ; public readonly args : string [ ] = [ ] ; public readonly output : string [ ] = [ ] ; public readonly newLine : string ; public readonly useCaseSensitiveFileNames : boolean ; public exitCode : number | undefined ; private readonly _executingFilePath : string | undefined ; private readonly _env : Record < string , string > | undefined ; constructor ( vfs , { executingFilePath , newLine = "\r\n" , env } = { } ) { this . vfs = vfs . isReadonly ? vfs . shadow ( ) : vfs ; this . useCaseSensitiveFileNames = ! this . vfs . ignoreCase ; this . newLine = newLine ; this . _executingFilePath = executingFilePath ; this . _env = env ; } private testTerminalWidth = Number . parseInt ( this . getEnvironmentVariable ( "TS_TEST_TERMINAL_WIDTH" ) ) ; getWidthOfTerminal = Number . isNaN ( this . testTerminalWidth ) ? undefined : ( ) => this . testTerminalWidth ; writeOutputIsTTY ( ) { return true ; } public write ( message ) { this . output . push ( message ) ; } public readFile ( path ) { try { const content = this . vfs . readFileSync ( path , "utf8" ) ; return content === undefined ? undefined : Utils . removeByteOrderMark ( content ) ; } catch { return undefined ; } } public writeFile ( path , data , writeByteOrderMark ? ) { this . vfs . mkdirpSync ( vpath . dirname ( path ) ) ; this . vfs . writeFileSync ( path , writeByteOrderMark ? Utils . addUTF8ByteOrderMark ( data ) : data ) ; } public deleteFile ( path ) { this . vfs . unlinkSync ( path ) ; } public fileExists ( path ) { const stats = this . _getStats ( path ) ; return stats ? stats . isFile ( ) : false ; } public directoryExists ( path ) { const stats = this . _getStats ( path ) ; return stats ? stats . isDirectory ( ) : false ; } public createDirectory ( path ) { this . vfs . mkdirpSync ( path ) ; } public getCurrentDirectory ( ) { return this . vfs . cwd ( ) ; } public getDirectories ( path ) { const result = [ ] ; try { for ( const file of this . vfs . readdirSync ( path ) ) { if ( this . vfs . statSync ( vpath . combine ( path , file ) ) . isDirectory ( ) ) { result . push ( file ) ; } } } catch { } return result ; } public readDirectory ( path , extensions ? , exclude ? , include ? , depth ? ) { return ts . matchFiles ( path , extensions , exclude , include , this . useCaseSensitiveFileNames , this . getCurrentDirectory ( ) , depth , path => this . getAccessibleFileSystemEntries ( path ) , path => this . realpath ( path ) ) ; } public getAccessibleFileSystemEntries ( path ) { const files = [ ] ; const directories = [ ] ; try { for ( const file of this . vfs . readdirSync ( path ) ) { try { const stats = this . vfs . statSync ( vpath . combine ( path , file ) ) ; if ( stats . isFile ( ) ) { files . push ( file ) ; } else if ( stats . isDirectory ( ) ) { directories . push ( file ) ; } } catch { } } } catch { } return { files , directories } ; } public exit ( exitCode ? ) { this . exitCode = exitCode ; throw processExitSentinel ; } public getFileSize ( path ) { const stats = this . _getStats ( path ) ; return stats && stats . isFile ( ) ? stats . size : 0 ; } public resolvePath ( path ) { return vpath . resolve ( this . vfs . cwd ( ) , path ) ; } public getExecutingFilePath ( ) { if ( this . _executingFilePath === undefined ) return ts . notImplemented ( ) ; return this . _executingFilePath ; } public getModifiedTime ( path ) { const stats = this . _getStats ( path ) ; return stats ? stats . mtime : undefined ! ; } public setModifiedTime ( path , time ) { this . vfs . utimesSync ( path , time , time ) ; } public createHash ( data ) { return `${ ts . generateDjb2Hash ( data ) }-${ data }` ; } public realpath ( path ) { try { return this . vfs . realpathSync ( path ) ; } catch { return path ; } } public getEnvironmentVariable ( name ) { return ( this . _env && this . _env [ name ] ) ! ; } private _getStats ( path ) { try { return this . vfs . existsSync ( path ) ? this . vfs . statSync ( path ) : undefined ; } catch { return undefined ; } } now ( ) { return new Date ( this . vfs . time ( ) ) ; } } /**
     * A fake `ts.ParseConfigHost` that leverages a virtual file system.
     */ export class ParseConfigHost implements ts . ParseConfigHost { public readonly sys : System ; constructor ( sys ) { if ( sys instanceof vfs . FileSystem ) sys = new System ( sys ) ; this . sys = sys ; } public get vfs ( ) { return this . sys . vfs ; } public get useCaseSensitiveFileNames ( ) { return this . sys . useCaseSensitiveFileNames ; } public fileExists ( fileName ) { return this . sys . fileExists ( fileName ) ; } public directoryExists ( directoryName ) { return this . sys . directoryExists ( directoryName ) ; } public readFile ( path ) { return this . sys . readFile ( path ) ; } public readDirectory ( path , extensions , excludes , includes , depth ) { return this . sys . readDirectory ( path , extensions , excludes , includes , depth ) ; } } /**
     * A fake `ts.CompilerHost` that leverages a virtual file system.
     */ export class CompilerHost implements ts . CompilerHost { public readonly sys : System ; public readonly defaultLibLocation : string ; public readonly outputs : documents . TextDocument [ ] = [ ] ; private readonly _outputsMap : collections . SortedMap < string , number > ; public readonly traces : string [ ] = [ ] ; public readonly shouldAssertInvariants = ! Harness . lightMode ; private _setParentNodes : boolean ; private _sourceFiles : collections . SortedMap < string , ts . SourceFile > ; private _parseConfigHost : ParseConfigHost | undefined ; private _newLine : string ; constructor ( sys , options = ts . getDefaultCompilerOptions ( ) , setParentNodes = false ) { if ( sys instanceof vfs . FileSystem ) sys = new System ( sys ) ; this . sys = sys ; this . defaultLibLocation = sys . vfs . meta . get ( "defaultLibLocation" ) || "" ; this . _newLine = ts . getNewLineCharacter ( options , ( ) => this . sys . newLine ) ; this . _sourceFiles = new collections . SortedMap < string , ts . SourceFile > ( { comparer : sys . vfs . stringComparer , sort : "insertion" } ) ; this . _setParentNodes = setParentNodes ; this . _outputsMap = new collections . SortedMap ( this . vfs . stringComparer ) ; } public get vfs ( ) { return this . sys . vfs ; } public get parseConfigHost ( ) { return this . _parseConfigHost || ( this . _parseConfigHost = new ParseConfigHost ( this . sys ) ) ; } public getCurrentDirectory ( ) { return this . sys . getCurrentDirectory ( ) ; } public useCaseSensitiveFileNames ( ) { return this . sys . useCaseSensitiveFileNames ; } public getNewLine ( ) { return this . _newLine ; } public getCanonicalFileName ( fileName ) { return this . sys . useCaseSensitiveFileNames ? fileName : fileName . toLowerCase ( ) ; } public deleteFile ( fileName ) { this . sys . deleteFile ( fileName ) ; } public fileExists ( fileName ) { return this . sys . fileExists ( fileName ) ; } public directoryExists ( directoryName ) { return this . sys . directoryExists ( directoryName ) ; } public getModifiedTime ( fileName ) { return this . sys . getModifiedTime ( fileName ) ; } public setModifiedTime ( fileName , time ) { return this . sys . setModifiedTime ( fileName , time ) ; } public getDirectories ( path ) { return this . sys . getDirectories ( path ) ; } public readDirectory ( path , extensions ? , exclude ? , include ? , depth ? ) { return this . sys . readDirectory ( path , extensions , exclude , include , depth ) ; } public readFile ( path ) { return this . sys . readFile ( path ) ; } public writeFile ( fileName , content , writeByteOrderMark ) { if ( writeByteOrderMark ) content = Utils . addUTF8ByteOrderMark ( content ) ; this . sys . writeFile ( fileName , content ) ; const document = new documents . TextDocument ( fileName , content ) ; document . meta . set ( "fileName" , fileName ) ; this . vfs . filemeta ( fileName ) . set ( "document" , document ) ; if ( ! this . _outputsMap . has ( document . file ) ) { this . _outputsMap . set ( document . file , this . outputs . length ) ; this . outputs . push ( document ) ; } this . outputs [ this . _outputsMap . get ( document . file ) ! ] = document ; } public trace ( s ) { this . traces . push ( s ) ; } public realpath ( path ) { return this . sys . realpath ( path ) ; } public getDefaultLibLocation ( ) { return vpath . resolve ( this . getCurrentDirectory ( ) , this . defaultLibLocation ) ; } public getDefaultLibFileName ( options ) { return vpath . resolve ( this . getDefaultLibLocation ( ) , ts . getDefaultLibFileName ( options ) ) ; } public getSourceFile ( fileName , languageVersion ) { const canonicalFileName = this . getCanonicalFileName ( vpath . resolve ( this . getCurrentDirectory ( ) , fileName ) ) ; const existing = this . _sourceFiles . get ( canonicalFileName ) ; if ( existing ) return existing ; const content = this . readFile ( canonicalFileName ) ; if ( content === undefined ) return undefined ; const cacheKey = this . vfs . shadowRoot && `SourceFile[languageVersion=${ languageVersion },setParentNodes=${ this . _setParentNodes }]` ; if ( cacheKey ) { const meta = this . vfs . filemeta ( canonicalFileName ) ; const sourceFileFromMetadata = meta . get ( cacheKey ) as ts . SourceFile | undefined ; if ( sourceFileFromMetadata && sourceFileFromMetadata . getFullText ( ) === content ) { this . _sourceFiles . set ( canonicalFileName , sourceFileFromMetadata ) ; return sourceFileFromMetadata ; } } const parsed = ts . createSourceFile ( fileName , content , languageVersion , this . _setParentNodes || this . shouldAssertInvariants ) ; if ( this . shouldAssertInvariants ) { Utils . assertInvariants ( parsed , undefined ) ; } this . _sourceFiles . set ( canonicalFileName , parsed ) ; if ( cacheKey ) { const stats = this . vfs . statSync ( canonicalFileName ) ; let fs = this . vfs ; while ( fs . shadowRoot ) { try { const shadowRootStats = fs . shadowRoot . existsSync ( canonicalFileName ) ? fs . shadowRoot . statSync ( canonicalFileName ) : undefined ! ; if ( shadowRootStats . dev !== stats . dev || shadowRootStats . ino !== stats . ino || shadowRootStats . mtimeMs !== stats . mtimeMs ) { break ; } fs = fs . shadowRoot ; } catch { break ; } } if ( fs !== this . vfs ) { fs . filemeta ( canonicalFileName ) . set ( cacheKey , parsed ) ; } } return parsed ; } } export type ExpectedDiagnosticMessage = [ ts . DiagnosticMessage , ... ( string | number ) [ ] ] ; export interface ExpectedDiagnosticMessageChain { message : ExpectedDiagnosticMessage ; next ? : ExpectedDiagnosticMessageChain [ ] ; } export interface ExpectedDiagnosticLocation { file : string ; start : number ; length : number ; } export interface ExpectedDiagnosticRelatedInformation extends ExpectedDiagnosticMessageChain { location ? : ExpectedDiagnosticLocation ; } export enum DiagnosticKind { Error = "Error" , Status = "Status" } export interface ExpectedErrorDiagnostic extends ExpectedDiagnosticRelatedInformation { relatedInformation ? : ExpectedDiagnosticRelatedInformation [ ] ; } export type ExpectedDiagnostic = ExpectedDiagnosticMessage | ExpectedErrorDiagnostic ; interface SolutionBuilderDiagnostic { kind : DiagnosticKind ; diagnostic : ts . Diagnostic ; } function indentedText ( indent , text ) { if ( ! indent ) return text ; let indentText = "" ; for ( let i = 0 ; i < indent ; i ++ ) { indentText += "  " ; } return `
${ indentText }${ text }` ; } function expectedDiagnosticMessageToText ( [ message , ... args ] ) { let text = ts . getLocaleSpecificMessage ( message ) ; if ( args . length ) { text = ts . formatStringFromArgs ( text , args ) ; } return text ; } function expectedDiagnosticMessageChainToText ( { message , next } , indent = 0 ) { let text = indentedText ( indent , expectedDiagnosticMessageToText ( message ) ) ; if ( next ) { indent ++ ; next . forEach ( kid => text += expectedDiagnosticMessageChainToText ( kid , indent ) ) ; } return text ; } function expectedDiagnosticRelatedInformationToText ( { location , ... diagnosticMessage } ) { const text = expectedDiagnosticMessageChainToText ( diagnosticMessage ) ; if ( location ) { const { file , start , length } = location ; return `${ file }(${ start }:${ length }):: ${ text }` ; } return text ; } function expectedErrorDiagnosticToText ( { relatedInformation , ... diagnosticRelatedInformation } ) { let text = `${ DiagnosticKind . Error }!: ${ expectedDiagnosticRelatedInformationToText ( diagnosticRelatedInformation ) }` ; if ( relatedInformation ) { for ( const kid of relatedInformation ) { text += `
  related:: ${ expectedDiagnosticRelatedInformationToText ( kid ) }` ; } } return text ; } function expectedDiagnosticToText ( errorOrStatus ) { return ts . isArray ( errorOrStatus ) ? `${ DiagnosticKind . Status }!: ${ expectedDiagnosticMessageToText ( errorOrStatus ) }` : expectedErrorDiagnosticToText ( errorOrStatus ) ; } function diagnosticMessageChainToText ( { messageText , next } , indent = 0 ) { let text = indentedText ( indent , messageText ) ; if ( next ) { indent ++ ; next . forEach ( kid => text += diagnosticMessageChainToText ( kid , indent ) ) ; } return text ; } function diagnosticRelatedInformationToText ( { file , start , length , messageText } ) { const text = typeof messageText === "string" ? messageText : diagnosticMessageChainToText ( messageText ) ; return file ? `${ file . fileName }(${ start }:${ length }):: ${ text }` : text ; } function diagnosticToText ( { kind , diagnostic : { relatedInformation , ... diagnosticRelatedInformation } } ) { let text = `${ kind }!: ${ diagnosticRelatedInformationToText ( diagnosticRelatedInformation ) }` ; if ( relatedInformation ) { for ( const kid of relatedInformation ) { text += `
  related:: ${ diagnosticRelatedInformationToText ( kid ) }` ; } } return text ; } export const version = "FakeTSVersion" ; export function patchHostForBuildInfoReadWrite < T extends ts . System > ( sys ) { const originalReadFile = sys . readFile ; sys . readFile = ( path , encoding ) => { const value = originalReadFile . call ( sys , path , encoding ) ; if ( ! value || ! ts . isBuildInfoFile ( path ) ) return value ; const buildInfo = ts . getBuildInfo ( value ) ; ts . Debug . assert ( buildInfo . version === version ) ; buildInfo . version = ts . version ; return ts . getBuildInfoText ( buildInfo ) ; } ; const originalWrite = sys . write ; sys . write = msg => originalWrite . call ( sys , msg . replace ( ts . version , version ) ) ; if ( sys . writeFile ) { const originalWriteFile = sys . writeFile ; sys . writeFile = ( fileName , content , writeByteOrderMark ) => { if ( ! ts . isBuildInfoFile ( fileName ) ) return originalWriteFile . call ( sys , fileName , content , writeByteOrderMark ) ; const buildInfo = ts . getBuildInfo ( content ) ; buildInfo . version = version ; originalWriteFile . call ( sys , fileName , ts . getBuildInfoText ( buildInfo ) , writeByteOrderMark ) ; } ; } return sys ; } export class SolutionBuilderHost extends CompilerHost implements ts . SolutionBuilderHost < ts . BuilderProgram > { createProgram : ts . CreateProgram < ts . BuilderProgram > ; private constructor ( sys , options ? , setParentNodes ? , createProgram ? ) { super ( sys , options , setParentNodes ) ; this . createProgram = createProgram || ts . createEmitAndSemanticDiagnosticsBuilderProgram ; } static create ( sys , options ? , setParentNodes ? , createProgram ? ) { const host = new SolutionBuilderHost ( sys , options , setParentNodes , createProgram ) ; patchHostForBuildInfoReadWrite ( host . sys ) ; return host ; } createHash ( data ) { return `${ ts . generateDjb2Hash ( data ) }-${ data }` ; } diagnostics : SolutionBuilderDiagnostic [ ] = [ ] ; reportDiagnostic ( diagnostic ) { this . diagnostics . push ( { kind : DiagnosticKind . Error , diagnostic } ) ; } reportSolutionBuilderStatus ( diagnostic ) { this . diagnostics . push ( { kind : DiagnosticKind . Status , diagnostic } ) ; } clearDiagnostics ( ) { this . diagnostics . length = 0 ; } assertDiagnosticMessages ( ... expectedDiagnostics ) { const actual = this . diagnostics . slice ( ) . map ( diagnosticToText ) ; const expected = expectedDiagnostics . map ( expectedDiagnosticToText ) ; assert . deepEqual ( actual , expected , `Diagnostic arrays did not match:
Actual: ${ JSON . stringify ( actual , undefined , " " ) }
Expected: ${ JSON . stringify ( expected , undefined , " " ) }` ) ; } assertErrors ( ... expectedDiagnostics ) { const actual = this . diagnostics . filter ( d => d . kind === DiagnosticKind . Error ) . map ( diagnosticToText ) ; const expected = expectedDiagnostics . map ( expectedDiagnosticToText ) ; assert . deepEqual ( actual , expected , `Diagnostics arrays did not match:
Actual: ${ JSON . stringify ( actual , undefined , " " ) }
Expected: ${ JSON . stringify ( expected , undefined , " " ) }
Actual All:: ${ JSON . stringify ( this . diagnostics . slice ( ) . map ( diagnosticToText ) , undefined , " " ) }` ) ; } printDiagnostics ( header = "== Diagnostics ==" ) { const out = ts . createDiagnosticReporter ( ts . sys ) ; ts . sys . write ( header + "\r\n" ) ; for ( const { diagnostic } of this . diagnostics ) { out ( diagnostic ) ; } } now ( ) { return this . sys . now ( ) ; } } }