namespace Harness { export interface IO { newLine ( ) : string ; getCurrentDirectory ( ) : string ; useCaseSensitiveFileNames ( ) : boolean ; resolvePath ( path ) : string | undefined ; getFileSize ( path ) : number ; readFile ( path ) : string | undefined ; writeFile ( path , contents ) : void ; directoryName ( path ) : string | undefined ; getDirectories ( path ) : string [ ] ; createDirectory ( path ) : void ; fileExists ( fileName ) : boolean ; directoryExists ( path ) : boolean ; deleteFile ( fileName ) : void ; enumerateTestFiles ( runner ) : ( string | FileBasedTest ) [ ] ; listFiles ( path , filter ? , options ? ) : string [ ] ; log ( text ) : void ; args ( ) : string [ ] ; getExecutingFilePath ( ) : string ; getWorkspaceRoot ( ) : string ; exit ( exitCode ? ) : void ; readDirectory ( path , extension ? , exclude ? , include ? , depth ? ) : readonly string [ ] ; getAccessibleFileSystemEntries ( dirname ) : ts . FileSystemEntries ; tryEnableSourceMapsForHost ? ( ) : void ; getEnvironmentVariable ? ( name ) : string ; getMemoryUsage ? ( ) : number | undefined ; joinPath ( ... components ) : string ; } export let IO ; export function setHarnessIO ( io ) { IO = io ; } export const harnessNewLine = "\r\n" ; export const virtualFileSystemRoot = "/" ; function createNodeIO ( ) { const workspaceRoot = Utils . findUpRoot ( ) ; let fs , pathModule ; if ( require ) { fs = require ( "fs" ) ; pathModule = require ( "path" ) ; } else { fs = pathModule = { } ; } function deleteFile ( path ) { try { fs . unlinkSync ( path ) ; } catch { } } function directoryName ( path ) { const dirPath = pathModule . dirname ( path ) ; return dirPath === path ? undefined : dirPath ; } function joinPath ( ... components ) { return pathModule . join ( ... components ) ; } function enumerateTestFiles ( runner ) { return runner . getTestFiles ( ) ; } function listFiles ( path , spec , options = { } ) { function filesInFolder ( folder ) { let paths = [ ] ; for ( const file of fs . readdirSync ( folder ) ) { const pathToFile = pathModule . join ( folder , file ) ; if ( ! fs . existsSync ( pathToFile ) ) continue ; const stat = fs . statSync ( pathToFile ) ; if ( options . recursive && stat . isDirectory ( ) ) { paths = paths . concat ( filesInFolder ( pathToFile ) ) ; } else if ( stat . isFile ( ) && ( ! spec || file . match ( spec ) ) ) { paths . push ( pathToFile ) ; } } return paths ; } return filesInFolder ( path ) ; } function getAccessibleFileSystemEntries ( dirname ) { try { const entries = fs . readdirSync ( dirname || "." ) . sort ( ts . sys . useCaseSensitiveFileNames ? ts . compareStringsCaseSensitive : ts . compareStringsCaseInsensitive ) ; const files = [ ] ; const directories = [ ] ; for ( const entry of entries ) { if ( entry === "." || entry === ".." ) continue ; const name = vpath . combine ( dirname , entry ) ; try { const stat = fs . statSync ( name ) ; if ( ! stat ) continue ; if ( stat . isFile ( ) ) { files . push ( entry ) ; } else if ( stat . isDirectory ( ) ) { directories . push ( entry ) ; } } catch { } } return { files , directories } ; } catch ( e ) { return { files : [ ] , directories : [ ] } ; } } function createDirectory ( path ) { try { fs . mkdirSync ( path ) ; } catch ( e ) { if ( e . code === "ENOENT" ) { createDirectory ( vpath . dirname ( path ) ) ; createDirectory ( path ) ; } else if ( ! ts . sys . directoryExists ( path ) ) { throw e ; } } } return { newLine : ( ) => harnessNewLine , getCurrentDirectory : ( ) => ts . sys . getCurrentDirectory ( ) , useCaseSensitiveFileNames : ( ) => ts . sys . useCaseSensitiveFileNames , resolvePath : ( path ) => ts . sys . resolvePath ( path ) , getFileSize : ( path ) => ts . sys . getFileSize ! ( path ) , readFile : path => ts . sys . readFile ( path ) , writeFile : ( path , content ) => ts . sys . writeFile ( path , content ) , directoryName , getDirectories : path => ts . sys . getDirectories ( path ) , createDirectory , fileExists : path => ts . sys . fileExists ( path ) , directoryExists : path => ts . sys . directoryExists ( path ) , deleteFile , listFiles , enumerateTestFiles , log : s => console . log ( s ) , args : ( ) => ts . sys . args , getExecutingFilePath : ( ) => ts . sys . getExecutingFilePath ( ) , getWorkspaceRoot : ( ) => workspaceRoot , exit : exitCode => ts . sys . exit ( exitCode ) , readDirectory : ( path , extension , exclude , include , depth ) => ts . sys . readDirectory ( path , extension , exclude , include , depth ) , getAccessibleFileSystemEntries , tryEnableSourceMapsForHost : ( ) => ts . sys . tryEnableSourceMapsForHost && ts . sys . tryEnableSourceMapsForHost ( ) , getMemoryUsage : ( ) => ts . sys . getMemoryUsage && ts . sys . getMemoryUsage ( ) , getEnvironmentVariable : name => ts . sys . getEnvironmentVariable ( name ) , joinPath } ; } export function mockHash ( s ) { return `hash-${ s }` ; } IO = createNodeIO ( ) ; if ( IO . tryEnableSourceMapsForHost && /^development$/i . test ( IO . getEnvironmentVariable ! ( "NODE_ENV" ) ) ) { IO . tryEnableSourceMapsForHost ( ) ; } export const libFolder = "built/local/" ; const tcServicesFileName = ts . combinePaths ( libFolder , "typescriptServices.js" ) ; export const tcServicesFile = IO . readFile ( tcServicesFileName ) + IO . newLine ( ) + `//# sourceURL=${ IO . resolvePath ( tcServicesFileName ) }` ; export type SourceMapEmitterCallback = ( emittedFile : string , emittedLine : number , emittedColumn : number , sourceFile : string , sourceLine : number , sourceColumn : number , sourceName : string ) => void ; export let userSpecifiedRoot = "" ; export let lightMode = false ; export function setLightMode ( flag ) { lightMode = flag ; } /** Functionality for compiling TypeScript code */ export namespace Compiler { /** Aggregate various writes into a single array of lines. Useful for passing to the
         *  TypeScript compiler to fill with source code or errors.
         */ export class WriterAggregator { public lines : string [ ] = [ ] ; public currentLine : string = undefined ! ; public Write ( str ) { this . currentLine = [ ( this . currentLine || "" ) , str ] . join ( "" ) ; } public WriteLine ( str ) { this . lines . push ( [ ( this . currentLine || "" ) , str ] . join ( "" ) ) ; this . currentLine = undefined ! ; } public Close ( ) { if ( this . currentLine !== undefined ) this . lines . push ( this . currentLine ) ; this . currentLine = undefined ! ; } public reset ( ) { this . lines = [ ] ; this . currentLine = undefined ! ; } } export function createSourceFileAndAssertInvariants ( fileName , sourceText , languageVersion ) { const shouldAssertInvariants = ! lightMode ; const result = ts . createSourceFile ( fileName , sourceText , languageVersion , shouldAssertInvariants ) ; if ( shouldAssertInvariants ) { Utils . assertInvariants ( result , undefined ) ; } return result ; } export const defaultLibFileName = "lib.d.ts" ; export const es2015DefaultLibFileName = "lib.es2015.d.ts" ; let libFileNameSourceFileMap ; export function getDefaultLibrarySourceFile ( fileName = defaultLibFileName ) { if ( ! isDefaultLibraryFile ( fileName ) ) { return undefined ; } if ( ! libFileNameSourceFileMap ) { libFileNameSourceFileMap = new ts . Map ( ts . getEntries ( { [ defaultLibFileName ] : createSourceFileAndAssertInvariants ( defaultLibFileName , IO . readFile ( libFolder + "lib.es5.d.ts" ) ! , ts . ScriptTarget . Latest ) } ) ) ; } let sourceFile = libFileNameSourceFileMap . get ( fileName ) ; if ( ! sourceFile ) { libFileNameSourceFileMap . set ( fileName , sourceFile = createSourceFileAndAssertInvariants ( fileName , IO . readFile ( libFolder + fileName ) ! , ts . ScriptTarget . Latest ) ) ; } return sourceFile ; } export function getDefaultLibFileName ( options ) { switch ( ts . getEmitScriptTarget ( options ) ) { case ts . ScriptTarget . ESNext : case ts . ScriptTarget . ES2017 : return "lib.es2017.d.ts" ; case ts . ScriptTarget . ES2016 : return "lib.es2016.d.ts" ; case ts . ScriptTarget . ES2015 : return es2015DefaultLibFileName ; default : return defaultLibFileName ; } } export const fourslashFileName = "fourslash.ts" ; export let fourslashSourceFile ; export function getCanonicalFileName ( fileName ) { return fileName ; } interface HarnessOptions { useCaseSensitiveFileNames ? : boolean ; includeBuiltFile ? : string ; baselineFile ? : string ; libFiles ? : string ; noTypesAndSymbols ? : boolean ; } const harnessOptionDeclarations = [ { name : "allowNonTsExtensions" , type : "boolean" , defaultValueDescription : false } , { name : "useCaseSensitiveFileNames" , type : "boolean" , defaultValueDescription : false } , { name : "baselineFile" , type : "string" } , { name : "includeBuiltFile" , type : "string" } , { name : "fileName" , type : "string" } , { name : "libFiles" , type : "string" } , { name : "noErrorTruncation" , type : "boolean" , defaultValueDescription : false } , { name : "suppressOutputPathCheck" , type : "boolean" , defaultValueDescription : false } , { name : "noImplicitReferences" , type : "boolean" , defaultValueDescription : false } , { name : "currentDirectory" , type : "string" } , { name : "symlink" , type : "string" } , { name : "link" , type : "string" } , { name : "noTypesAndSymbols" , type : "boolean" , defaultValueDescription : false } , { name : "fullEmitPaths" , type : "boolean" , defaultValueDescription : false } , ] ; let optionsIndex ; function getCommandLineOption ( name ) { if ( ! optionsIndex ) { optionsIndex = new ts . Map < string , ts . CommandLineOption > ( ) ; const optionDeclarations = harnessOptionDeclarations . concat ( ts . optionDeclarations ) ; for ( const option of optionDeclarations ) { optionsIndex . set ( option . name . toLowerCase ( ) , option ) ; } } return optionsIndex . get ( name . toLowerCase ( ) ) ; } export function setCompilerOptionsFromHarnessSetting ( settings , options ) { for ( const name in settings ) { if ( settings . hasOwnProperty ( name ) ) { const value = settings [ name ] ; if ( value === undefined ) { throw new Error ( `Cannot have undefined value for compiler option '${ name }'.` ) ; } const option = getCommandLineOption ( name ) ; if ( option ) { const errors = [ ] ; options [ option . name ] = optionValue ( option , value , errors ) ; if ( errors . length > 0 ) { throw new Error ( `Unknown value '${ value }' for compiler option '${ name }'.` ) ; } } else { throw new Error ( `Unknown compiler option '${ name }'.` ) ; } } } } function optionValue ( option , value , errors ) { switch ( option . type ) { case "boolean" : return value . toLowerCase ( ) === "true" ; case "string" : return value ; case "number" : { const numverValue = parseInt ( value , 10 ) ; if ( isNaN ( numverValue ) ) { throw new Error ( `Value must be a number, got: ${ JSON . stringify ( value ) }` ) ; } return numverValue ; } case "list" : return ts . parseListTypeOption ( option , value , errors ) ; default : return ts . parseCustomTypeOption ( option as ts . CommandLineOptionOfCustomType , value , errors ) ; } } export interface TestFile { unitName : string ; content : string ; fileOptions ? : any ; } export function compileFiles ( inputFiles , otherFiles , harnessSettings , compilerOptions , currentDirectory , symlinks ? ) { const options = compilerOptions ? ts . cloneCompilerOptions ( compilerOptions ) : { noResolve : false } ; options . target = ts . getEmitScriptTarget ( options ) ; options . newLine = options . newLine || ts . NewLineKind . CarriageReturnLineFeed ; options . noErrorTruncation = true ; options . skipDefaultLibCheck = typeof options . skipDefaultLibCheck === "undefined" ? true : options . skipDefaultLibCheck ; if ( typeof currentDirectory === "undefined" ) { currentDirectory = vfs . srcFolder ; } if ( harnessSettings ) { setCompilerOptionsFromHarnessSetting ( harnessSettings , options ) ; } if ( options . rootDirs ) { options . rootDirs = ts . map ( options . rootDirs , d => ts . getNormalizedAbsolutePath ( d , currentDirectory ) ) ; } const useCaseSensitiveFileNames = options . useCaseSensitiveFileNames !== undefined ? options . useCaseSensitiveFileNames : true ; const programFileNames = inputFiles . map ( file => file . unitName ) . filter ( fileName => ! ts . fileExtensionIs ( fileName , ts . Extension . Json ) ) ; if ( options . includeBuiltFile ) { programFileNames . push ( vpath . combine ( vfs . builtFolder , options . includeBuiltFile ) ) ; } if ( options . libFiles ) { for ( const fileName of options . libFiles . split ( "," ) ) { programFileNames . push ( vpath . combine ( vfs . testLibFolder , fileName ) ) ; } } const docs = inputFiles . concat ( otherFiles ) . map ( documents . TextDocument . fromTestFile ) ; const fs = vfs . createFromFileSystem ( IO , ! useCaseSensitiveFileNames , { documents : docs , cwd : currentDirectory } ) ; if ( symlinks ) { fs . apply ( symlinks ) ; } const host = new fakes . CompilerHost ( fs , options ) ; const result = compiler . compileFiles ( host , programFileNames , options ) ; result . symlinks = symlinks ; return result ; } export interface DeclarationCompilationContext { declInputFiles : TestFile [ ] ; declOtherFiles : TestFile [ ] ; harnessSettings : TestCaseParser . CompilerSettings & HarnessOptions | undefined ; options : ts . CompilerOptions ; currentDirectory : string ; } export function prepareDeclarationCompilationContext ( inputFiles , otherFiles , result , harnessSettings , options , currentDirectory ) { if ( options . declaration && result . diagnostics . length === 0 ) { if ( options . emitDeclarationOnly ) { if ( result . js . size > 0 || result . dts . size === 0 ) { throw new Error ( "Only declaration files should be generated when emitDeclarationOnly:true" ) ; } } else if ( result . dts . size !== result . getNumberOfJsFiles ( false ) ) { throw new Error ( "There were no errors and declFiles generated did not match number of js files generated" ) ; } } const declInputFiles = [ ] ; const declOtherFiles = [ ] ; if ( options . declaration && result . diagnostics . length === 0 && result . dts . size > 0 ) { ts . forEach ( inputFiles , file => addDtsFile ( file , declInputFiles ) ) ; ts . forEach ( otherFiles , file => addDtsFile ( file , declOtherFiles ) ) ; return { declInputFiles , declOtherFiles , harnessSettings , options , currentDirectory : currentDirectory || harnessSettings . currentDirectory } ; } function addDtsFile ( file , dtsFiles ) { if ( vpath . isDeclaration ( file . unitName ) || vpath . isJson ( file . unitName ) ) { dtsFiles . push ( file ) ; } else if ( vpath . isTypeScript ( file . unitName ) || ( vpath . isJavaScript ( file . unitName ) && ts . getAllowJSCompilerOption ( options ) ) ) { const declFile = findResultCodeFile ( file . unitName ) ; if ( declFile && ! findUnit ( declFile . file , declInputFiles ) && ! findUnit ( declFile . file , declOtherFiles ) ) { dtsFiles . push ( { unitName : declFile . file , content : Utils . removeByteOrderMark ( declFile . text ) } ) ; } } } function findResultCodeFile ( fileName ) { const sourceFile = result . program ! . getSourceFile ( fileName ) ! ; assert ( sourceFile , "Program has no source file with name '" + fileName + "'" ) ; let sourceFileName ; const outFile = options . outFile || options . out ; if ( ! outFile ) { if ( options . outDir ) { let sourceFilePath = ts . getNormalizedAbsolutePath ( sourceFile . fileName , result . vfs . cwd ( ) ) ; sourceFilePath = sourceFilePath . replace ( result . program ! . getCommonSourceDirectory ( ) , "" ) ; sourceFileName = ts . combinePaths ( options . outDir , sourceFilePath ) ; } else { sourceFileName = sourceFile . fileName ; } } else { sourceFileName = outFile ; } const dTsFileName = ts . removeFileExtension ( sourceFileName ) + ts . getDeclarationEmitExtensionForPath ( sourceFileName ) ; return result . dts . get ( dTsFileName ) ; } function findUnit ( fileName , units ) { return ts . forEach ( units , unit => unit . unitName === fileName ? unit : undefined ) ; } } export function compileDeclarationFiles ( context , symlinks ) { if ( ! context ) { return ; } const { declInputFiles , declOtherFiles , harnessSettings , options , currentDirectory } = context ; const output = compileFiles ( declInputFiles , declOtherFiles , harnessSettings , options , currentDirectory , symlinks ) ; return { declInputFiles , declOtherFiles , declResult : output } ; } export function minimalDiagnosticsToString ( diagnostics , pretty ? ) { const host = { getCanonicalFileName , getCurrentDirectory : ( ) => "" , getNewLine : ( ) => IO . newLine ( ) } ; return ( pretty ? ts . formatDiagnosticsWithColorAndContext : ts . formatDiagnostics ) ( diagnostics , host ) ; } export function getErrorBaseline ( inputFiles , diagnostics , pretty ? ) { let outputLines = "" ; const gen = iterateErrorBaseline ( inputFiles , diagnostics , { pretty } ) ; for ( let { done , value } = gen . next ( ) ; ! done ; { done , value } = gen . next ( ) ) { const [ , content ] = value ; outputLines += content ; } if ( pretty ) { outputLines += ts . getErrorSummaryText ( ts . getErrorCountForSummary ( diagnostics ) , ts . getFilesInErrorForSummary ( diagnostics ) , IO . newLine ( ) , { getCurrentDirectory : ( ) => "" } ) ; } return outputLines ; } export const diagnosticSummaryMarker = "__diagnosticSummary" ; export const globalErrorsMarker = "__globalErrors" ; export function * iterateErrorBaseline ( inputFiles , diagnostics , options ? ) { diagnostics = ts . sort ( diagnostics , ts . compareDiagnostics ) ; let outputLines = "" ; let totalErrorsReportedInNonLibraryFiles = 0 ; let errorsReported = 0 ; let firstLine = true ; function newLine ( ) { if ( firstLine ) { firstLine = false ; return "" ; } return "\r\n" ; } const formatDiagnsoticHost = { getCurrentDirectory : ( ) => options && options . currentDirectory ? options . currentDirectory : "" , getNewLine : ( ) => IO . newLine ( ) , getCanonicalFileName : ts . createGetCanonicalFileName ( options && options . caseSensitive !== undefined ? options . caseSensitive : true ) , } ; function outputErrorText ( error ) { const message = ts . flattenDiagnosticMessageText ( error . messageText , IO . newLine ( ) ) ; const errLines = Utils . removeTestPathPrefixes ( message ) . split ( "\n" ) . map ( s => s . length > 0 && s . charAt ( s . length - 1 ) === "\r" ? s . substr ( 0 , s . length - 1 ) : s ) . filter ( s => s . length > 0 ) . map ( s => "!!! " + ts . diagnosticCategoryName ( error ) + " TS" + error . code + ": " + s ) ; if ( error . relatedInformation ) { for ( const info of error . relatedInformation ) { errLines . push ( `!!! related TS${ info . code }${ info . file ? " " + ts . formatLocation ( info . file , info . start ! , formatDiagnsoticHost , ts . identity ) : "" }: ${ ts . flattenDiagnosticMessageText ( info . messageText , IO . newLine ( ) ) }` ) ; } } errLines . forEach ( e => outputLines += ( newLine ( ) + e ) ) ; errorsReported ++ ; if ( ! error . file || ! isDefaultLibraryFile ( error . file . fileName ) ) { totalErrorsReportedInNonLibraryFiles ++ ; } } yield [ diagnosticSummaryMarker , Utils . removeTestPathPrefixes ( minimalDiagnosticsToString ( diagnostics , options && options . pretty ) ) + IO . newLine ( ) + IO . newLine ( ) , diagnostics . length ] ; const globalErrors = diagnostics . filter ( err => ! err . file ) ; globalErrors . forEach ( outputErrorText ) ; yield [ globalErrorsMarker , outputLines , errorsReported ] ; outputLines = "" ; errorsReported = 0 ; const dupeCase = new ts . Map < string , number > ( ) ; for ( const inputFile of inputFiles . filter ( f => f . content !== undefined ) ) { const fileErrors = diagnostics . filter ( ( e ) : e is ts . DiagnosticWithLocation => { const errFn = e . file ; return ! ! errFn && ts . comparePaths ( Utils . removeTestPathPrefixes ( errFn . fileName ) , Utils . removeTestPathPrefixes ( inputFile . unitName ) , options && options . currentDirectory || "" , ! ( options && options . caseSensitive ) ) === ts . Comparison . EqualTo ; } ) ; outputLines += ( newLine ( ) + "==== " + inputFile . unitName + " (" + fileErrors . length + " errors) ====" ) ; let markedErrorCount = 0 ; const lineStarts = ts . computeLineStarts ( inputFile . content ) ; let lines = inputFile . content . split ( "\n" ) ; if ( lines . length === 1 ) { lines = lines [ 0 ] . split ( "\r" ) ; } lines . forEach ( ( line , lineIndex ) => { if ( line . length > 0 && line . charAt ( line . length - 1 ) === "\r" ) { line = line . substr ( 0 , line . length - 1 ) ; } const thisLineStart = lineStarts [ lineIndex ] ; let nextLineStart ; if ( lineIndex === lines . length - 1 ) { nextLineStart = inputFile . content . length ; } else { nextLineStart = lineStarts [ lineIndex + 1 ] ; } outputLines += ( newLine ( ) + "    " + line ) ; fileErrors . forEach ( errDiagnostic => { const err = errDiagnostic as ts . TextSpan ; const end = ts . textSpanEnd ( err ) ; if ( ( end >= thisLineStart ) && ( ( err . start < nextLineStart ) || ( lineIndex === lines . length - 1 ) ) ) { const relativeOffset = err . start - thisLineStart ; const length = ( end - err . start ) - Math . max ( 0 , thisLineStart - err . start ) ; const squiggleStart = Math . max ( 0 , relativeOffset ) ; outputLines += ( newLine ( ) + "    " + line . substr ( 0 , squiggleStart ) . replace ( /[^\s]/g , " " ) + new Array ( Math . min ( length , line . length - squiggleStart ) + 1 ) . join ( "~" ) ) ; if ( ( lineIndex === lines . length - 1 ) || nextLineStart > end ) { outputErrorText ( errDiagnostic ) ; markedErrorCount ++ ; } } } ) ; } ) ; assert . equal ( markedErrorCount , fileErrors . length , "count of errors in " + inputFile . unitName ) ; const isDupe = dupeCase . has ( sanitizeTestFilePath ( inputFile . unitName ) ) ; yield [ checkDuplicatedFileName ( inputFile . unitName , dupeCase ) , outputLines , errorsReported ] ; if ( isDupe && ! ( options && options . caseSensitive ) ) { totalErrorsReportedInNonLibraryFiles -= errorsReported ; } outputLines = "" ; errorsReported = 0 ; } const numLibraryDiagnostics = ts . countWhere ( diagnostics , diagnostic => { return ! ! diagnostic . file && ( isDefaultLibraryFile ( diagnostic . file . fileName ) || isBuiltFile ( diagnostic . file . fileName ) ) ; } ) ; const numTest262HarnessDiagnostics = ts . countWhere ( diagnostics , diagnostic => { return ! ! diagnostic . file && diagnostic . file . fileName . indexOf ( "test262-harness" ) >= 0 ; } ) ; assert . equal ( totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics , diagnostics . length , "total number of errors" ) ; } export function doErrorBaseline ( baselinePath , inputFiles , errors , pretty ? ) { Baseline . runBaseline ( baselinePath . replace ( /\.tsx?$/ , ".errors.txt" ) , ! errors || ( errors . length === 0 ) ? null : getErrorBaseline ( inputFiles , errors , pretty ) ) ; } export function doTypeAndSymbolBaseline ( baselinePath , program , allFiles , opts ? , multifile ? , skipTypeBaselines ? , skipSymbolBaselines ? , hasErrorBaseline ? ) { const fullWalker = new TypeWriterWalker ( program , true , ! ! hasErrorBaseline ) ; let typesError , symbolsError ; try { checkBaseLines ( false ) ; } catch ( e ) { typesError = e ; } try { checkBaseLines ( true ) ; } catch ( e ) { symbolsError = e ; } if ( typesError && symbolsError ) { throw new Error ( typesError . stack + IO . newLine ( ) + symbolsError . stack ) ; } if ( typesError ) { throw typesError ; } if ( symbolsError ) { throw symbolsError ; } return ; function checkBaseLines ( isSymbolBaseLine ) { const fullExtension = isSymbolBaseLine ? ".symbols" : ".types" ; const outputFileName = ts . endsWith ( baselinePath , ts . Extension . Ts ) || ts . endsWith ( baselinePath , ts . Extension . Tsx ) ? baselinePath . replace ( /\.tsx?/ , "" ) : baselinePath ; if ( ! multifile ) { const fullBaseLine = generateBaseLine ( isSymbolBaseLine , isSymbolBaseLine ? skipSymbolBaselines : skipTypeBaselines ) ; Baseline . runBaseline ( outputFileName + fullExtension , fullBaseLine , opts ) ; } else { Baseline . runMultifileBaseline ( outputFileName , fullExtension , ( ) => { return iterateBaseLine ( isSymbolBaseLine , isSymbolBaseLine ? skipSymbolBaselines : skipTypeBaselines ) ; } , opts ) ; } } function generateBaseLine ( isSymbolBaseline , skipBaseline ? ) { let result = "" ; const gen = iterateBaseLine ( isSymbolBaseline , skipBaseline ) ; for ( let { done , value } = gen . next ( ) ; ! done ; { done , value } = gen . next ( ) ) { const [ , content ] = value ; result += content ; } return result || null ; } function * iterateBaseLine ( isSymbolBaseline , skipBaseline ? ) { if ( skipBaseline ) { return ; } const dupeCase = new ts . Map < string , number > ( ) ; for ( const file of allFiles ) { const { unitName } = file ; let typeLines = "=== " + unitName + " ===\r\n" ; const codeLines = ts . flatMap ( file . content . split ( /\r?\n/g ) , e => e . split ( /[\r\u2028\u2029]/g ) ) ; const gen = isSymbolBaseline ? fullWalker . getSymbols ( unitName ) : fullWalker . getTypes ( unitName ) ; let lastIndexWritten ; for ( let { done , value : result } = gen . next ( ) ; ! done ; { done , value : result } = gen . next ( ) ) { if ( isSymbolBaseline && ! result . symbol ) { return ; } if ( lastIndexWritten === undefined ) { typeLines += codeLines . slice ( 0 , result . line + 1 ) . join ( "\r\n" ) + "\r\n" ; } else if ( result . line !== lastIndexWritten ) { if ( ! ( ( lastIndexWritten + 1 < codeLines . length ) && ( codeLines [ lastIndexWritten + 1 ] . match ( /^\s*[{|}]\s*$/ ) || codeLines [ lastIndexWritten + 1 ] . trim ( ) === "" ) ) ) { typeLines += "\r\n" ; } typeLines += codeLines . slice ( lastIndexWritten + 1 , result . line + 1 ) . join ( "\r\n" ) + "\r\n" ; } lastIndexWritten = result . line ; const typeOrSymbolString = isSymbolBaseline ? result . symbol : result . type ; const formattedLine = result . sourceText . replace ( /\r?\n/g , "" ) + " : " + typeOrSymbolString ; typeLines += ">" + formattedLine + "\r\n" ; } if ( lastIndexWritten === undefined ) { for ( const codeLine of codeLines ) { typeLines += codeLine + "\r\nNo type information for this code." ; } } else { if ( lastIndexWritten + 1 < codeLines . length ) { if ( ! ( ( lastIndexWritten + 1 < codeLines . length ) && ( codeLines [ lastIndexWritten + 1 ] . match ( /^\s*[{|}]\s*$/ ) || codeLines [ lastIndexWritten + 1 ] . trim ( ) === "" ) ) ) { typeLines += "\r\n" ; } typeLines += codeLines . slice ( lastIndexWritten + 1 ) . join ( "\r\n" ) ; } typeLines += "\r\n" ; } yield [ checkDuplicatedFileName ( unitName , dupeCase ) , Utils . removeTestPathPrefixes ( typeLines ) ] ; } } } export function doSourcemapBaseline ( baselinePath , options , result , harnessSettings ) { const declMaps = ts . getAreDeclarationMapsEnabled ( options ) ; if ( options . inlineSourceMap ) { if ( result . maps . size > 0 && ! declMaps ) { throw new Error ( "No sourcemap files should be generated if inlineSourceMaps was set." ) ; } return ; } else if ( options . sourceMap || declMaps ) { if ( result . maps . size !== ( ( options . sourceMap ? result . getNumberOfJsFiles ( false ) : 0 ) + ( declMaps ? result . getNumberOfJsFiles ( true ) : 0 ) ) ) { throw new Error ( "Number of sourcemap files should be same as js files." ) ; } let sourceMapCode ; if ( ( options . noEmitOnError && result . diagnostics . length !== 0 ) || result . maps . size === 0 ) { sourceMapCode = null ; } else { sourceMapCode = "" ; result . maps . forEach ( sourceMap => { if ( sourceMapCode ) sourceMapCode += "\r\n" ; sourceMapCode += fileOutput ( sourceMap , harnessSettings ) ; if ( ! options . inlineSourceMap ) { sourceMapCode += createSourceMapPreviewLink ( sourceMap . text , result ) ; } } ) ; } Baseline . runBaseline ( baselinePath . replace ( /\.tsx?/ , ".js.map" ) , sourceMapCode ) ; } } function createSourceMapPreviewLink ( sourcemap , result ) { const sourcemapJSON = JSON . parse ( sourcemap ) ; const outputJSFile = result . outputs . find ( td => td . file . endsWith ( sourcemapJSON . file ) ) ; if ( ! outputJSFile ) return "" ; const sourceTDs = ts . map ( sourcemapJSON . sources , ( s ) => result . inputs . find ( td => td . file . endsWith ( s ) ) ) ; const anyUnfoundSources = ts . contains ( sourceTDs , undefined ) ; if ( anyUnfoundSources ) return "" ; const hash = "#base64," + ts . map ( [ outputJSFile . text , sourcemap ] . concat ( sourceTDs . map ( td => td ! . text ) ) , ( s ) => ts . convertToBase64 ( decodeURIComponent ( encodeURIComponent ( s ) ) ) ) . join ( "," ) ; return "\n//// https://sokra.github.io/source-map-visualization" + hash + "\n" ; } export function doJsEmitBaseline ( baselinePath , header , options , result , tsConfigFiles , toBeCompiled , otherFiles , harnessSettings ) { if ( ! options . noEmit && ! options . emitDeclarationOnly && result . js . size === 0 && result . diagnostics . length === 0 ) { throw new Error ( "Expected at least one js file to be emitted or at least one error to be created." ) ; } let tsCode = "" ; const tsSources = otherFiles . concat ( toBeCompiled ) ; if ( tsSources . length > 1 ) { tsCode += "//// [" + header + "] ////\r\n\r\n" ; } for ( let i = 0 ; i < tsSources . length ; i ++ ) { tsCode += "//// [" + ts . getBaseFileName ( tsSources [ i ] . unitName ) + "]\r\n" ; tsCode += tsSources [ i ] . content + ( i < ( tsSources . length - 1 ) ? "\r\n" : "" ) ; } let jsCode = "" ; result . js . forEach ( file => { if ( jsCode . length && jsCode . charCodeAt ( jsCode . length - 1 ) !== ts . CharacterCodes . lineFeed ) { jsCode += "\r\n" ; } if ( ! result . diagnostics . length && ! ts . endsWith ( file . file , ts . Extension . Json ) ) { const fileParseResult = ts . createSourceFile ( file . file , file . text , ts . getEmitScriptTarget ( options ) , false , ts . endsWith ( file . file , "x" ) ? ts . ScriptKind . JSX : ts . ScriptKind . JS ) ; if ( ts . length ( fileParseResult . parseDiagnostics ) ) { jsCode += getErrorBaseline ( [ file . asTestFile ( ) ] , fileParseResult . parseDiagnostics ) ; return ; } } jsCode += fileOutput ( file , harnessSettings ) ; } ) ; if ( result . dts . size > 0 ) { jsCode += "\r\n\r\n" ; result . dts . forEach ( declFile => { jsCode += fileOutput ( declFile , harnessSettings ) ; } ) ; } const declFileContext = prepareDeclarationCompilationContext ( toBeCompiled , otherFiles , result , harnessSettings , options , undefined ) ; const declFileCompilationResult = compileDeclarationFiles ( declFileContext , result . symlinks ) ; if ( declFileCompilationResult && declFileCompilationResult . declResult . diagnostics . length ) { jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n" ; jsCode += "\r\n\r\n" ; jsCode += getErrorBaseline ( tsConfigFiles . concat ( declFileCompilationResult . declInputFiles , declFileCompilationResult . declOtherFiles ) , declFileCompilationResult . declResult . diagnostics ) ; } Baseline . runBaseline ( baselinePath . replace ( /\.tsx?/ , ts . Extension . Js ) , jsCode . length > 0 ? tsCode + "\r\n\r\n" + jsCode : null ) ; } function fileOutput ( file , harnessSettings ) { const fileName = harnessSettings . fullEmitPaths ? Utils . removeTestPathPrefixes ( file . file ) : ts . getBaseFileName ( file . file ) ; return "//// [" + fileName + "]\r\n" + Utils . removeTestPathPrefixes ( file . text ) ; } export function collateOutputs ( outputFiles ) { const gen = iterateOutputs ( outputFiles ) ; let result = "" ; for ( let { done , value } = gen . next ( ) ; ! done ; { done , value } = gen . next ( ) ) { if ( result . length ) { result += "\r\n\r\n" ; } const [ , content ] = value ; result += content ; } return result ; } export function * iterateOutputs ( outputFiles ) { const files = Array . from ( outputFiles ) ; files . slice ( ) . sort ( ( a , b ) => ts . compareStringsCaseSensitive ( cleanName ( a . file ) , cleanName ( b . file ) ) ) ; const dupeCase = new ts . Map < string , number > ( ) ; for ( const outputFile of files ) { yield [ checkDuplicatedFileName ( outputFile . file , dupeCase ) , "/*====== " + outputFile . file + " ======*/\r\n" + Utils . removeByteOrderMark ( outputFile . text ) ] ; } function cleanName ( fn ) { const lastSlash = ts . normalizeSlashes ( fn ) . lastIndexOf ( "/" ) ; return fn . substr ( lastSlash + 1 ) . toLowerCase ( ) ; } } function checkDuplicatedFileName ( resultName , dupeCase ) { resultName = sanitizeTestFilePath ( resultName ) ; if ( dupeCase . has ( resultName ) ) { const count = 1 + dupeCase . get ( resultName ) ! ; dupeCase . set ( resultName , count ) ; resultName = `${ resultName }.dupe${ count }` ; } else { dupeCase . set ( resultName , 0 ) ; } return resultName ; } export function sanitizeTestFilePath ( name ) { const path = ts . toPath ( ts . normalizeSlashes ( name . replace ( /[\^<>:"|?*%]/g , "_" ) ) . replace ( /\.\.\//g , "__dotdot/" ) , "" , Utils . canonicalizeForHarness ) ; if ( ts . startsWith ( path , "/" ) ) { return path . substring ( 1 ) ; } return path ; } } export interface FileBasedTest { file : string ; configurations ? : FileBasedTestConfiguration [ ] ; } export interface FileBasedTestConfiguration { [ key ] : string ; } function splitVaryBySettingValue ( text , varyBy ) { if ( ! text ) return undefined ; let star = false ; const includes = [ ] ; const excludes = [ ] ; for ( let s of text . split ( /,/g ) ) { s = s . trim ( ) . toLowerCase ( ) ; if ( s . length === 0 ) continue ; if ( s === "*" ) { star = true ; } else if ( ts . startsWith ( s , "-" ) || ts . startsWith ( s , "!" ) ) { excludes . push ( s . slice ( 1 ) ) ; } else { includes . push ( s ) ; } } if ( includes . length <= 1 && ! star && excludes . length === 0 ) { return undefined ; } const variations = [ ] ; const values = getVaryByStarSettingValues ( varyBy ) ; for ( const include of includes ) { const value = values ?. get ( include ) ; if ( ts . findIndex ( variations , v => v . key === include || value !== undefined && v . value === value ) === - 1 ) { variations . push ( { key : include , value } ) ; } } if ( star && values ) { for ( const [ key , value ] of ts . arrayFrom ( values . entries ( ) ) ) { if ( ts . findIndex ( variations , v => v . key === key || v . value === value ) === - 1 ) { variations . push ( { key , value } ) ; } } } for ( const exclude of excludes ) { const value = values ?. get ( exclude ) ; let index ; while ( ( index = ts . findIndex ( variations , v => v . key === exclude || value !== undefined && v . value === value ) ) >= 0 ) { ts . orderedRemoveItemAt ( variations , index ) ; } } if ( variations . length === 0 ) { throw new Error ( `Variations in test option '@${ varyBy }' resulted in an empty set.` ) ; } return ts . map ( variations , v => v . key ) ; } function computeFileBasedTestConfigurationVariations ( configurations , variationState , varyByEntries , offset ) { if ( offset >= varyByEntries . length ) { configurations . push ( { ... variationState } ) ; return ; } const [ varyBy , entries ] = varyByEntries [ offset ] ; for ( const entry of entries ) { variationState [ varyBy ] = entry ; computeFileBasedTestConfigurationVariations ( configurations , variationState , varyByEntries , offset + 1 ) ; } } let booleanVaryByStarSettingValues ; function getVaryByStarSettingValues ( varyBy ) { const option = ts . forEach ( ts . optionDeclarations , decl => ts . equateStringsCaseInsensitive ( decl . name , varyBy ) ? decl : undefined ) ; if ( option ) { if ( typeof option . type === "object" ) { return option . type ; } if ( option . type === "boolean" ) { return booleanVaryByStarSettingValues || ( booleanVaryByStarSettingValues = new ts . Map ( ts . getEntries ( { true : 1 , false : 0 } ) ) ) ; } } } /**
     * Compute FileBasedTestConfiguration variations based on a supplied list of variable settings.
     */ export function getFileBasedTestConfigurations ( settings , varyBy ) { let varyByEntries ; let variationCount = 1 ; for ( const varyByKey of varyBy ) { if ( ts . hasProperty ( settings , varyByKey ) ) { const entries = splitVaryBySettingValue ( settings [ varyByKey ] , varyByKey ) ; if ( entries ) { if ( ! varyByEntries ) varyByEntries = [ ] ; variationCount *= entries . length ; if ( variationCount > 25 ) throw new Error ( `Provided test options exceeded the maximum number of variations: ${ varyBy . map ( v => `'@${ v }'` ) . join ( ", " ) }` ) ; varyByEntries . push ( [ varyByKey , entries ] ) ; } } } if ( ! varyByEntries ) return undefined ; const configurations = [ ] ; computeFileBasedTestConfigurationVariations ( configurations , { } , varyByEntries , 0 ) ; return configurations ; } /**
     * Compute a description for this configuration based on its entries
     */ export function getFileBasedTestConfigurationDescription ( configuration ) { let name = "" ; if ( configuration ) { const keys = Object . keys ( configuration ) . sort ( ) ; for ( const key of keys ) { if ( name ) name += ", " ; name += `@${ key }: ${ configuration [ key ] }` ; } } return name ; } export namespace TestCaseParser { /** all the necessary information to set the right compiler settings */ export interface CompilerSettings { [ name ] : string ; } /** All the necessary information to turn a multi file test into useful units for later compilation */ export interface TestUnitData { content : string ; name : string ; fileOptions : any ; originalFilePath : string ; references : string [ ] ; } const optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm ; const linkRegex = /^[\/]{2}\s*@link\s*:\s*([^\r\n]*)\s*->\s*([^\r\n]*)/gm ; export function parseSymlinkFromTest ( line , symlinks ) { const linkMetaData = linkRegex . exec ( line ) ; linkRegex . lastIndex = 0 ; if ( ! linkMetaData ) return undefined ; if ( ! symlinks ) symlinks = { } ; symlinks [ linkMetaData [ 2 ] . trim ( ) ] = new vfs . Symlink ( linkMetaData [ 1 ] . trim ( ) ) ; return symlinks ; } export function extractCompilerSettings ( content ) { const opts = { } ; let match ; while ( ( match = optionRegex . exec ( content ) ) !== null ) { opts [ match [ 1 ] ] = match [ 2 ] . trim ( ) ; } return opts ; } export interface TestCaseContent { settings : CompilerSettings ; testUnitData : TestUnitData [ ] ; tsConfig : ts . ParsedCommandLine | undefined ; tsConfigFileUnitData : TestUnitData | undefined ; symlinks ? : vfs . FileSet ; } /** Given a test file containing // @FileName directives, return an array of named units of code to be added to an existing compiler instance */ export function makeUnitsFromTest ( code , fileName , rootDir ? , settings = extractCompilerSettings ( code ) ) { const testUnitData = [ ] ; const lines = Utils . splitContentByNewlines ( code ) ; let currentFileContent ; let currentFileOptions = { } ; let currentFileName ; let refs = [ ] ; let symlinks ; for ( const line of lines ) { let testMetaData ; const possiblySymlinks = parseSymlinkFromTest ( line , symlinks ) ; if ( possiblySymlinks ) { symlinks = possiblySymlinks ; } else if ( testMetaData = optionRegex . exec ( line ) ) { optionRegex . lastIndex = 0 ; const metaDataName = testMetaData [ 1 ] . toLowerCase ( ) ; currentFileOptions [ testMetaData [ 1 ] ] = testMetaData [ 2 ] . trim ( ) ; if ( metaDataName !== "filename" ) { continue ; } if ( currentFileName ) { const newTestFile = { content : currentFileContent ! , name : currentFileName , fileOptions : currentFileOptions , originalFilePath : fileName , references : refs } ; testUnitData . push ( newTestFile ) ; currentFileContent = undefined ; currentFileOptions = { } ; currentFileName = testMetaData [ 2 ] . trim ( ) ; refs = [ ] ; } else { currentFileName = testMetaData [ 2 ] . trim ( ) ; } } else { if ( currentFileContent === undefined ) { currentFileContent = "" ; } else if ( currentFileContent !== "" ) { currentFileContent = currentFileContent + "\n" ; } currentFileContent = currentFileContent + line ; } } currentFileName = testUnitData . length > 0 || currentFileName ? currentFileName : ts . getBaseFileName ( fileName ) ; const newTestFile2 = { content : currentFileContent || "" , name : currentFileName , fileOptions : currentFileOptions , originalFilePath : fileName , references : refs } ; testUnitData . push ( newTestFile2 ) ; const parseConfigHost = { useCaseSensitiveFileNames : false , readDirectory : ( ) => [ ] , fileExists : ( ) => true , readFile : ( name ) => ts . forEach ( testUnitData , data => data . name . toLowerCase ( ) === name . toLowerCase ( ) ? data . content : undefined ) } ; let tsConfig ; let tsConfigFileUnitData ; for ( let i = 0 ; i < testUnitData . length ; i ++ ) { const data = testUnitData [ i ] ; if ( getConfigNameFromFileName ( data . name ) ) { const configJson = ts . parseJsonText ( data . name , data . content ) ; assert . isTrue ( configJson . endOfFileToken !== undefined ) ; let baseDir = ts . normalizePath ( ts . getDirectoryPath ( data . name ) ) ; if ( rootDir ) { baseDir = ts . getNormalizedAbsolutePath ( baseDir , rootDir ) ; } tsConfig = ts . parseJsonSourceFileConfigFileContent ( configJson , parseConfigHost , baseDir ) ; tsConfig . options . configFilePath = data . name ; tsConfigFileUnitData = data ; ts . orderedRemoveItemAt ( testUnitData , i ) ; break ; } } return { settings , testUnitData , tsConfig , tsConfigFileUnitData , symlinks } ; } } /** Support class for baseline files */ export namespace Baseline { const noContent = "<no content>" ; export interface BaselineOptions { Subfolder ? : string ; Baselinefolder ? : string ; PrintDiff ? : true ; } export function localPath ( fileName , baselineFolder ? , subfolder ? ) { if ( baselineFolder === undefined ) { return baselinePath ( fileName , "local" , "tests/baselines" , subfolder ) ; } else { return baselinePath ( fileName , "local" , baselineFolder , subfolder ) ; } } function referencePath ( fileName , baselineFolder ? , subfolder ? ) { if ( baselineFolder === undefined ) { return baselinePath ( fileName , "reference" , "tests/baselines" , subfolder ) ; } else { return baselinePath ( fileName , "reference" , baselineFolder , subfolder ) ; } } function baselinePath ( fileName , type , baselineFolder , subfolder ? ) { if ( subfolder !== undefined ) { return userSpecifiedRoot + baselineFolder + "/" + subfolder + "/" + type + "/" + fileName ; } else { return userSpecifiedRoot + baselineFolder + "/" + type + "/" + fileName ; } } const fileCache = { } ; function compareToBaseline ( actual , relativeFileName , opts ) { if ( actual === undefined ) { return undefined ! ; } const refFileName = referencePath ( relativeFileName , opts && opts . Baselinefolder , opts && opts . Subfolder ) ; if ( actual === null ) { actual = noContent ; } let expected = "<no content>" ; if ( IO . fileExists ( refFileName ) ) { expected = IO . readFile ( refFileName ) ! ; } return { expected , actual } ; } function writeComparison ( expected , actual , relativeFileName , actualFileName , opts ? ) { function createDirectoryStructure ( dirName ) { if ( fileCache [ dirName ] || IO . directoryExists ( dirName ) ) { fileCache [ dirName ] = true ; return ; } const parentDirectory = IO . directoryName ( dirName ) ! ; if ( parentDirectory !== "" && parentDirectory !== dirName ) { createDirectoryStructure ( parentDirectory ) ; } IO . createDirectory ( dirName ) ; fileCache [ dirName ] = true ; } createDirectoryStructure ( IO . directoryName ( actualFileName ) ! ) ; if ( IO . fileExists ( actualFileName ) ) { IO . deleteFile ( actualFileName ) ; } const encodedActual = Utils . encodeString ( actual ) ; if ( expected !== encodedActual ) { if ( actual === noContent ) { IO . writeFile ( actualFileName + ".delete" , "" ) ; } else { IO . writeFile ( actualFileName , encodedActual ) ; } if ( ! ! require && opts && opts . PrintDiff ) { const Diff = require ( "diff" ) ; const patch = Diff . createTwoFilesPatch ( "Expected" , "Actual" , expected , actual , "The current baseline" , "The new version" ) ; throw new Error ( `The baseline file ${ relativeFileName } has changed.${ ts . ForegroundColorEscapeSequences . Grey }\n\n${ patch }` ) ; } else { if ( ! IO . fileExists ( expected ) ) { throw new Error ( `New baseline created at ${ IO . joinPath ( "tests" , "baselines" , "local" , relativeFileName ) }` ) ; } else { throw new Error ( `The baseline file ${ relativeFileName } has changed.` ) ; } } } } export function runBaseline ( relativeFileName , actual , opts ? ) { const actualFileName = localPath ( relativeFileName , opts && opts . Baselinefolder , opts && opts . Subfolder ) ; if ( actual === undefined ) { throw new Error ( "The generated content was \"undefined\". Return \"null\" if no baselining is required.\"" ) ; } const comparison = compareToBaseline ( actual , relativeFileName , opts ) ; writeComparison ( comparison . expected , comparison . actual , relativeFileName , actualFileName , opts ) ; } export function runMultifileBaseline ( relativeFileBase , extension , generateContent , opts ? , referencedExtensions ? ) { const gen = generateContent ( ) ; const writtenFiles = new ts . Map < string , true > ( ) ; const errors = [ ] ; if ( gen !== null ) { for ( let { done , value } = gen . next ( ) ; ! done ; { done , value } = gen . next ( ) ) { const [ name , content , count ] = value as [ string , string , number | undefined ] ; if ( count === 0 ) continue ; const relativeFileName = relativeFileBase + "/" + name + extension ; const actualFileName = localPath ( relativeFileName , opts && opts . Baselinefolder , opts && opts . Subfolder ) ; const comparison = compareToBaseline ( content , relativeFileName , opts ) ; try { writeComparison ( comparison . expected , comparison . actual , relativeFileName , actualFileName ) ; } catch ( e ) { errors . push ( e ) ; } writtenFiles . set ( relativeFileName , true ) ; } } const referenceDir = referencePath ( relativeFileBase , opts && opts . Baselinefolder , opts && opts . Subfolder ) ; let existing = IO . readDirectory ( referenceDir , referencedExtensions || [ extension ] ) ; if ( extension === ".ts" || referencedExtensions && referencedExtensions . indexOf ( ".ts" ) > - 1 && referencedExtensions . indexOf ( ".d.ts" ) === - 1 ) { existing = existing . filter ( f => ! ts . endsWith ( f , ".d.ts" ) ) ; } const missing = [ ] ; for ( const name of existing ) { const localCopy = name . substring ( referenceDir . length - relativeFileBase . length ) ; if ( ! writtenFiles . has ( localCopy ) ) { missing . push ( localCopy ) ; } } if ( missing . length ) { for ( const file of missing ) { IO . writeFile ( localPath ( file + ".delete" , opts && opts . Baselinefolder , opts && opts . Subfolder ) , "" ) ; } } if ( errors . length || missing . length ) { let errorMsg = "" ; if ( errors . length ) { errorMsg += `The baseline for ${ relativeFileBase } in ${ errors . length } files has changed:${ "\n    " + errors . slice ( 0 , 5 ) . map ( e => e . message ) . join ( "\n    " ) + ( errors . length > 5 ? "\n" + `    and ${ errors . length - 5 } more` : "" ) }` ; } if ( errors . length && missing . length ) { errorMsg += "\n" ; } if ( missing . length ) { const writtenFilesArray = ts . arrayFrom ( writtenFiles . keys ( ) ) ; errorMsg += `Baseline missing ${ missing . length } files:${ "\n    " + missing . slice ( 0 , 5 ) . join ( "\n    " ) + ( missing . length > 5 ? "\n" + `    and ${ missing . length - 5 } more` : "" ) + "\n" }Written ${ writtenFiles . size } files:${ "\n    " + writtenFilesArray . slice ( 0 , 5 ) . join ( "\n    " ) + ( writtenFilesArray . length > 5 ? "\n" + `    and ${ writtenFilesArray . length - 5 } more` : "" ) }` ; } throw new Error ( errorMsg ) ; } } } export function isDefaultLibraryFile ( filePath ) { const fileName = ts . getBaseFileName ( ts . normalizeSlashes ( filePath ) ) ; return ts . startsWith ( fileName , "lib." ) && ts . endsWith ( fileName , ts . Extension . Dts ) ; } export function isBuiltFile ( filePath ) { return filePath . indexOf ( libFolder ) === 0 || filePath . indexOf ( vpath . addTrailingSeparator ( vfs . builtFolder ) ) === 0 ; } export function getDefaultLibraryFile ( filePath , io ) { const libFile = userSpecifiedRoot + libFolder + ts . getBaseFileName ( ts . normalizeSlashes ( filePath ) ) ; return { unitName : libFile , content : io . readFile ( libFile ) ! } ; } export function getConfigNameFromFileName ( filename ) { const flc = ts . getBaseFileName ( filename ) . toLowerCase ( ) ; return ts . find ( [ "tsconfig.json" as "tsconfig.json" , "jsconfig.json" as "jsconfig.json" ] , x => x === flc ) ; } if ( Error ) ( Error as any ) . stackTraceLimit = 100 ; }