'use strict' ; import path from 'path' ; import ts from 'typescript' ; import { Extra } from './temp-types-based-on-js-source' ; type { ts . CompilerOptions } const defaultCompilerOptions = { allowNonTsExtensions : true , allowJs : true } ; type { Map < string , ts . WatchOfConfigFile < ts . SemanticDiagnosticsBuilderProgram > > } const knownWatchProgramMap = new Map < string , ts . WatchOfConfigFile < ts . SemanticDiagnosticsBuilderProgram > > ( ) ; type { Map < string , ts . FileWatcherCallback > } const watchCallbackTrackingMap = new Map < string , ts . FileWatcherCallback > ( ) ; type { { code : string , filePath : string } } const currentLintOperationState = { code : '' , filePath : '' } ; param { ts . Diagnostic } diagnostic returns { void } function diagnosticReporter ( diagnostic ) { throw new Error ( ts . flattenDiagnosticMessageText ( diagnostic . messageText , ts . sys . newLine ) ) ; } const noopFileWatcher = { close : ( ) => { } } ; param { string } code param { string } filePath param { string } extra . tsconfigRootDir param { string [ ] } extra . project returns { ts . Program [ ] } export function calculateProjectParserOptions ( code , filePath , extra ) { const results = [ ] ; const tsconfigRootDir = extra . tsconfigRootDir ; currentLintOperationState . code = code ; currentLintOperationState . filePath = filePath ; const watchCallback = watchCallbackTrackingMap . get ( filePath ) ; if ( typeof watchCallback !== 'undefined' ) { watchCallback ( filePath , ts . FileWatcherEventKind . Changed ) ; } for ( let tsconfigPath of extra . projects ) { if ( ! path . isAbsolute ( tsconfigPath ) ) { tsconfigPath = path . join ( tsconfigRootDir , tsconfigPath ) ; } const existingWatch = knownWatchProgramMap . get ( tsconfigPath ) ; if ( typeof existingWatch !== 'undefined' ) { results . push ( existingWatch . getProgram ( ) . getProgram ( ) ) ; continue ; } const watchCompilerHost = ts . createWatchCompilerHost ( tsconfigPath , { allowNonTsExtensions : true } as ts . CompilerOptions , ts . sys , ts . createSemanticDiagnosticsBuilderProgram , diagnosticReporter , ( ) => { } ) ; const oldReadFile = watchCompilerHost . readFile ; watchCompilerHost . readFile = ( filePath , encoding ) => path . normalize ( filePath ) === path . normalize ( currentLintOperationState . filePath ) ? currentLintOperationState . code : oldReadFile ( filePath , encoding ) ; watchCompilerHost . onUnRecoverableConfigFileDiagnostic = diagnosticReporter ; watchCompilerHost . afterProgramCreate = program => { const configFileDiagnostics = program . getConfigFileParsingDiagnostics ( ) . filter ( diag => diag . category === ts . DiagnosticCategory . Error && diag . code !== 18003 ) ; if ( configFileDiagnostics . length > 0 ) { diagnosticReporter ( configFileDiagnostics [ 0 ] ) ; } } ; watchCompilerHost . watchFile = ( fileName , callback ) => { const normalizedFileName = path . normalize ( fileName ) ; watchCallbackTrackingMap . set ( normalizedFileName , callback ) ; return { close : ( ) => { watchCallbackTrackingMap . delete ( normalizedFileName ) ; } } ; } ; watchCompilerHost . watchDirectory = ( ) => noopFileWatcher ; const oldOnDirectoryStructureHostCreate = ( watchCompilerHost as any ) . onCachedDirectoryStructureHostCreate ; ( watchCompilerHost as any ) . onCachedDirectoryStructureHostCreate = ( host ) => { const oldReadDirectory = host . readDirectory ; host . readDirectory = ( path , extensions ? , exclude ? , include ? , depth ? ) => oldReadDirectory ( path , ! extensions ? undefined : extensions . concat ( extra . extraFileExtensions ) , exclude , include , depth ) ; oldOnDirectoryStructureHostCreate ( host ) ; } ; const programWatch = ts . createWatchProgram ( watchCompilerHost ) ; const program = programWatch . getProgram ( ) . getProgram ( ) ; knownWatchProgramMap . set ( tsconfigPath , programWatch ) ; results . push ( program ) ; } return results ; } param code param filePath param { string } extra . tsconfigRootDir param { string [ ] } extra . project returns { ts . Program } export function createProgram ( code , filePath , extra ) { if ( ! extra . projects || extra . projects . length !== 1 ) { return undefined ; } let tsconfigPath = extra . projects [ 0 ] ; if ( ! path . isAbsolute ( tsconfigPath ) ) { tsconfigPath = path . join ( extra . tsconfigRootDir , tsconfigPath ) ; } const commandLine = ts . getParsedCommandLineOfConfigFile ( tsconfigPath , defaultCompilerOptions , { ... ts . sys , onUnRecoverableConfigFileDiagnostic : ( ) => { } } ) ; if ( ! commandLine ) { return undefined ; } const compilerHost = ts . createCompilerHost ( commandLine . options , true ) ; const oldReadFile = compilerHost . readFile ; compilerHost . readFile = ( fileName ) => path . normalize ( fileName ) === path . normalize ( filePath ) ? code : oldReadFile ( fileName ) ; return ts . createProgram ( [ filePath ] , commandLine . options , compilerHost ) ; }