@@ -329,8 +329,8 @@ export interface CreateOptions {
329329 * best results.
330330 */
331331 require ?: Array < string > ;
332- readFile ?: ( path : string ) => string | undefined ;
333- fileExists ?: ( path : string ) => boolean ;
332+ readFile ?: ReadFileFunction ;
333+ fileExists ?: FileExistsFunction ;
334334 transformers ?:
335335 | _ts . CustomTransformers
336336 | ( ( p : _ts . Program ) => _ts . CustomTransformers ) ;
@@ -375,6 +375,9 @@ export interface TsConfigOptions
375375 | 'experimentalEsmLoader'
376376 > { }
377377
378+ export type ReadFileFunction = ( path : string ) => string | undefined ;
379+ export type FileExistsFunction = ( path : string ) => boolean ;
380+
378381/**
379382 * Like `Object.assign`, but ignores `undefined` properties.
380383 */
@@ -902,18 +905,104 @@ export function create(rawOptions: CreateOptions = {}): Service {
902905 } ;
903906 }
904907
908+ /**
909+ * Create filesystem access functions usable in `*Host` implementations which
910+ * implement appropriate caching
911+ */
912+ function createCachedFilesystemFunctions ( opts : {
913+ readFile : ReadFileFunction ;
914+ fileExists : FileExistsFunction ;
915+ } ) {
916+ const { readFile : _readFile , fileExists : _fileExists } = opts ;
917+ const readFile = cachedLookup ( debugFn ( 'readFile' , _readFile ) ) ;
918+ const readDirectory = ts . sys . readDirectory ;
919+ const getDirectories = cachedLookup (
920+ debugFn ( 'getDirectories' , ts . sys . getDirectories )
921+ ) ;
922+ const fileExists = cachedLookup ( debugFn ( 'fileExists' , _fileExists ) ) ;
923+ const directoryExists = cachedLookup (
924+ debugFn ( 'directoryExists' , ts . sys . directoryExists )
925+ ) ;
926+ const resolvePath = cachedLookup (
927+ debugFn ( 'resolvePath' , ts . sys . resolvePath )
928+ ) ;
929+ const realpath = ts . sys . realpath
930+ ? cachedLookup ( debugFn ( 'realpath' , ts . sys . realpath ) )
931+ : undefined ;
932+ return {
933+ readFile,
934+ readDirectory,
935+ getDirectories,
936+ fileExists,
937+ directoryExists,
938+ resolvePath,
939+ realpath,
940+ } ;
941+ }
942+
943+ function createUpdateMemoryCacheFunction ( opts : {
944+ onProjectMustUpdate : ( ) => void ;
945+ isFileKnownToBeInternal : ReturnType <
946+ typeof createResolverFunctions
947+ > [ 'isFileKnownToBeInternal' ] ;
948+ markBucketOfFilenameInternal : ReturnType <
949+ typeof createResolverFunctions
950+ > [ 'markBucketOfFilenameInternal' ] ;
951+ rootFileNames : Set < string > ;
952+ fileVersions : Map < string , number > ;
953+ fileContents : Map < string , string > ;
954+ } ) {
955+ const {
956+ onProjectMustUpdate,
957+ isFileKnownToBeInternal,
958+ fileContents,
959+ fileVersions,
960+ markBucketOfFilenameInternal,
961+ rootFileNames,
962+ } = opts ;
963+ const updateMemoryCache = ( contents : string , fileName : string ) => {
964+ let projectMustUpdate = false ;
965+ // Add to `rootFiles` as necessary, either to make TS include a file it has not seen,
966+ // or to trigger a re-classification of files from external to internal.
967+ if ( ! rootFileNames . has ( fileName ) && ! isFileKnownToBeInternal ( fileName ) ) {
968+ markBucketOfFilenameInternal ( fileName ) ;
969+ rootFileNames . add ( fileName ) ;
970+ projectMustUpdate = true ;
971+ }
972+
973+ const previousVersion = fileVersions . get ( fileName ) || 0 ;
974+ const previousContents = fileContents . get ( fileName ) ;
975+ // Avoid incrementing cache when nothing has changed.
976+ if ( contents !== previousContents ) {
977+ fileVersions . set ( fileName , previousVersion + 1 ) ;
978+ fileContents . set ( fileName , contents ) ;
979+ projectMustUpdate = true ;
980+ }
981+ if ( projectMustUpdate ) onProjectMustUpdate ( ) ;
982+ } ;
983+ return { updateMemoryCache } ;
984+ }
985+
905986 // Use full language services when the fast option is disabled.
906987 if ( ! transpileOnly ) {
988+ const {
989+ readFile : cachedReadFile ,
990+ fileExists : cachedFileExists ,
991+ directoryExists,
992+ getDirectories,
993+ readDirectory,
994+ realpath,
995+ resolvePath,
996+ } = createCachedFilesystemFunctions ( { readFile, fileExists } ) ;
907997 const fileContents = new Map < string , string > ( ) ;
908998 const rootFileNames = new Set ( config . fileNames ) ;
909- const cachedReadFile = cachedLookup ( debugFn ( 'readFile' , readFile ) ) ;
999+ const fileVersions = new Map (
1000+ Array . from ( rootFileNames ) . map ( ( fileName ) => [ fileName , 0 ] )
1001+ ) ;
9101002
9111003 // Use language services by default (TODO: invert next major version).
9121004 if ( ! options . compilerHost ) {
9131005 let projectVersion = 1 ;
914- const fileVersions = new Map (
915- Array . from ( rootFileNames ) . map ( ( fileName ) => [ fileName , 0 ] )
916- ) ;
9171006
9181007 const getCustomTransformers = ( ) => {
9191008 if ( typeof transformers === 'function' ) {
@@ -950,17 +1039,11 @@ export function create(rawOptions: CreateOptions = {}): Service {
9501039 return ts . ScriptSnapshot . fromString ( contents ) ;
9511040 } ,
9521041 readFile : cachedReadFile ,
953- readDirectory : ts . sys . readDirectory ,
954- getDirectories : cachedLookup (
955- debugFn ( 'getDirectories' , ts . sys . getDirectories )
956- ) ,
957- fileExists : cachedLookup ( debugFn ( 'fileExists' , fileExists ) ) ,
958- directoryExists : cachedLookup (
959- debugFn ( 'directoryExists' , ts . sys . directoryExists )
960- ) ,
961- realpath : ts . sys . realpath
962- ? cachedLookup ( debugFn ( 'realpath' , ts . sys . realpath ) )
963- : undefined ,
1042+ readDirectory,
1043+ getDirectories,
1044+ fileExists : cachedFileExists ,
1045+ directoryExists,
1046+ realpath,
9641047 getNewLine : ( ) => ts . sys . newLine ,
9651048 useCaseSensitiveFileNames : ( ) => ts . sys . useCaseSensitiveFileNames ,
9661049 getCurrentDirectory : ( ) => cwd ,
@@ -985,29 +1068,17 @@ export function create(rawOptions: CreateOptions = {}): Service {
9851068 ) ;
9861069 const service = ts . createLanguageService ( serviceHost , registry ) ;
9871070
988- const updateMemoryCache = ( contents : string , fileName : string ) => {
989- // Add to `rootFiles` as necessary, either to make TS include a file it has not seen,
990- // or to trigger a re-classification of files from external to internal.
991- if (
992- ! rootFileNames . has ( fileName ) &&
993- ! isFileKnownToBeInternal ( fileName )
994- ) {
995- markBucketOfFilenameInternal ( fileName ) ;
996- rootFileNames . add ( fileName ) ;
997- // Increment project version for every change to rootFileNames.
998- projectVersion ++ ;
999- }
1000-
1001- const previousVersion = fileVersions . get ( fileName ) || 0 ;
1002- const previousContents = fileContents . get ( fileName ) ;
1003- // Avoid incrementing cache when nothing has changed.
1004- if ( contents !== previousContents ) {
1005- fileVersions . set ( fileName , previousVersion + 1 ) ;
1006- fileContents . set ( fileName , contents ) ;
1007- // Increment project version for every file change.
1071+ const { updateMemoryCache } = createUpdateMemoryCacheFunction ( {
1072+ rootFileNames,
1073+ fileContents,
1074+ fileVersions,
1075+ isFileKnownToBeInternal,
1076+ markBucketOfFilenameInternal,
1077+ onProjectMustUpdate ( ) {
1078+ // Increment project version for every file change or addition to rootFileNames
10081079 projectVersion ++ ;
1009- }
1010- } ;
1080+ } ,
1081+ } ) ;
10111082
10121083 let previousProgram : _ts . Program | undefined = undefined ;
10131084
@@ -1070,6 +1141,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
10701141 return { name, comment } ;
10711142 } ;
10721143 } else {
1144+ // options.compilerHost === true
10731145 const sys : _ts . System & _ts . FormatDiagnosticsHost = {
10741146 ...ts . sys ,
10751147 ...diagnosticHost ,
@@ -1080,18 +1152,12 @@ export function create(rawOptions: CreateOptions = {}): Service {
10801152 if ( contents ) fileContents . set ( fileName , contents ) ;
10811153 return contents ;
10821154 } ,
1083- readDirectory : ts . sys . readDirectory ,
1084- getDirectories : cachedLookup (
1085- debugFn ( 'getDirectories' , ts . sys . getDirectories )
1086- ) ,
1087- fileExists : cachedLookup ( debugFn ( 'fileExists' , fileExists ) ) ,
1088- directoryExists : cachedLookup (
1089- debugFn ( 'directoryExists' , ts . sys . directoryExists )
1090- ) ,
1091- resolvePath : cachedLookup ( debugFn ( 'resolvePath' , ts . sys . resolvePath ) ) ,
1092- realpath : ts . sys . realpath
1093- ? cachedLookup ( debugFn ( 'realpath' , ts . sys . realpath ) )
1094- : undefined ,
1155+ readDirectory,
1156+ getDirectories,
1157+ fileExists : cachedFileExists ,
1158+ directoryExists,
1159+ resolvePath,
1160+ realpath,
10951161 } ;
10961162
10971163 const host : _ts . CompilerHost = ts . createIncrementalCompilerHost
@@ -1147,26 +1213,14 @@ export function create(rawOptions: CreateOptions = {}): Service {
11471213 : transformers ;
11481214
11491215 // Set the file contents into cache manually.
1150- const updateMemoryCache = ( contents : string , fileName : string ) => {
1151- const previousContents = fileContents . get ( fileName ) ;
1152- const contentsChanged = previousContents !== contents ;
1153- if ( contentsChanged ) {
1154- fileContents . set ( fileName , contents ) ;
1155- }
1156-
1157- // Add to `rootFiles` when discovered by compiler for the first time.
1158- let addedToRootFileNames = false ;
1159- if (
1160- ! rootFileNames . has ( fileName ) &&
1161- ! isFileKnownToBeInternal ( fileName )
1162- ) {
1163- markBucketOfFilenameInternal ( fileName ) ;
1164- rootFileNames . add ( fileName ) ;
1165- addedToRootFileNames = true ;
1166- }
1167-
1168- // Update program when file changes.
1169- if ( addedToRootFileNames || contentsChanged ) {
1216+ const { updateMemoryCache } = createUpdateMemoryCacheFunction ( {
1217+ rootFileNames,
1218+ fileVersions,
1219+ fileContents,
1220+ isFileKnownToBeInternal,
1221+ markBucketOfFilenameInternal,
1222+ onProjectMustUpdate ( ) {
1223+ // Update program when file changes.
11701224 builderProgram = ts . createEmitAndSemanticDiagnosticsBuilderProgram (
11711225 Array . from ( rootFileNames ) ,
11721226 config . options ,
@@ -1175,8 +1229,8 @@ export function create(rawOptions: CreateOptions = {}): Service {
11751229 config . errors ,
11761230 config . projectReferences
11771231 ) ;
1178- }
1179- } ;
1232+ } ,
1233+ } ) ;
11801234
11811235 getOutput = ( code : string , fileName : string ) => {
11821236 const output : [ string , string ] = [ '' , '' ] ;
0 commit comments