1
- import { relative , basename , extname , resolve , dirname , join } from 'path' ;
2
1
import { Module } from 'module' ;
2
+ import { relative , basename , extname , resolve , dirname , join , delimiter as pathDelimiter } from 'path' ;
3
3
import * as util from 'util' ;
4
4
import { fileURLToPath } from 'url' ;
5
5
6
+ import * as ynModule from 'yn' ;
6
7
import sourceMapSupport = require( 'source-map-support' ) ;
7
8
import { BaseError } from 'make-error' ;
8
9
import type * as _ts from 'typescript' ;
@@ -374,21 +375,6 @@ export interface Service {
374
375
*/
375
376
export type Register = Service ;
376
377
377
- /**
378
- * Cached fs operation wrapper.
379
- */
380
- function cachedLookup < T > ( fn : ( arg : string ) => T ) : ( arg : string ) => T {
381
- const cache = new Map < string , T > ( ) ;
382
-
383
- return ( arg : string ) : T => {
384
- if ( ! cache . has ( arg ) ) {
385
- cache . set ( arg , fn ( arg ) ) ;
386
- }
387
-
388
- return cache . get ( arg ) ! ;
389
- } ;
390
- }
391
-
392
378
/** @internal */
393
379
export function getExtensions ( config : _ts . ParsedCommandLine ) {
394
380
const tsExtensions = [ '.ts' ] ;
@@ -779,29 +765,42 @@ export function create(rawOptions: CreateOptions = {}): Service {
779
765
}
780
766
781
767
/**
782
- * Create filesystem access functions usable in `*Host` implementations which
783
- * implement appropriate caching
768
+ * Create filesystem access functions which implement appropriate caching and
769
+ * are usable in `*Host` implementations.
784
770
*/
785
- function createCachedFilesystemFunctions ( opts : {
771
+ function createCachedFilesystem ( opts : {
786
772
readFile : ReadFileFunction ;
787
773
fileExists : FileExistsFunction ;
788
774
} ) {
789
775
const { readFile : _readFile , fileExists : _fileExists } = opts ;
790
- const readFile = cachedLookup ( debugFn ( 'readFile' , _readFile ) ) ;
791
- const readDirectory = ts . sys . readDirectory ;
776
+
777
+ const fileExistsCache = new Map < string , boolean > ( ) ;
778
+ const fileExists = cachedLookup ( debugFn ( 'fileExists' , _fileExists ) , fileExistsCache ) ;
779
+ const readFileCache = new Map < string , string | undefined > ( ) ;
780
+ const readFile = cachedLookup ( debugFn ( 'readFile' , _readFile ) , readFileCache ) ;
781
+ function setFileContents ( path : string , content : string | undefined ) {
782
+ readFileCache . set ( path , content ) ;
783
+ fileExistsCache . set ( path , true ) ;
784
+ }
785
+ // Not cached until TS exposes a proper way to inject fs dependencies so that
786
+ // this function obeys our readFile, etc caches.
787
+ const readDirectory = ( path : string , extensions ?: readonly string [ ] , exclude ?: readonly string [ ] , include ?: readonly string [ ] , depth ?: number ) : string [ ] => {
788
+ debug ( 'readDirectory' , path , extensions , exclude , include , depth ) ;
789
+ return ts . sys . readDirectory ( path , extensions , exclude , include , depth ) ;
790
+ }
792
791
const getDirectories = cachedLookup (
793
792
debugFn ( 'getDirectories' , ts . sys . getDirectories )
794
793
) ;
795
- const fileExists = cachedLookup ( debugFn ( 'fileExists' , _fileExists ) ) ;
796
794
const directoryExists = cachedLookup (
797
795
debugFn ( 'directoryExists' , ts . sys . directoryExists )
798
796
) ;
799
- const resolvePath = cachedLookup (
800
- debugFn ( 'resolvePath' , ts . sys . resolvePath )
801
- ) ;
797
+ // Cache probably has little effect; this is an in-memory transformation based on CWD
798
+ const resolvePath = debugFn ( 'resolvePath' , ts . sys . resolvePath ) ;
799
+ // Note: cache does not understand when intermediate symlinks are changed; cannot be invalidated correctly.
802
800
const realpath = ts . sys . realpath
803
801
? cachedLookup ( debugFn ( 'realpath' , ts . sys . realpath ) )
804
802
: undefined ;
803
+
805
804
return {
806
805
readFile,
807
806
readDirectory,
@@ -810,7 +809,19 @@ export function create(rawOptions: CreateOptions = {}): Service {
810
809
directoryExists,
811
810
resolvePath,
812
811
realpath,
812
+ setFileContents,
813
+ fileContents : readFileCache ,
813
814
} ;
815
+
816
+ function cachedLookup < T > ( fn : ( arg : string ) => T , cache = new Map < string , T > ( ) ) : ( arg : string ) => T {
817
+ return ( arg : string ) : T => {
818
+ if ( ! cache . has ( arg ) ) {
819
+ cache . set ( arg , fn ( arg ) ) ;
820
+ }
821
+
822
+ return cache . get ( arg ) ! ;
823
+ } ;
824
+ }
814
825
}
815
826
816
827
function createUpdateMemoryCacheFunction ( opts : {
@@ -823,12 +834,14 @@ export function create(rawOptions: CreateOptions = {}): Service {
823
834
> [ 'markBucketOfFilenameInternal' ] ;
824
835
rootFileNames : Set < string > ;
825
836
fileVersions : Map < string , number > ;
826
- fileContents : Map < string , string > ;
837
+ fileContents : Map < string , string | undefined > ;
838
+ setFileContents ( path : string , contents : string | undefined ) : void ;
827
839
} ) {
828
840
const {
829
841
onProjectMustUpdate,
830
842
isFileKnownToBeInternal,
831
843
fileContents,
844
+ setFileContents,
832
845
fileVersions,
833
846
markBucketOfFilenameInternal,
834
847
rootFileNames,
@@ -848,7 +861,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
848
861
// Avoid incrementing cache when nothing has changed.
849
862
if ( contents !== previousContents ) {
850
863
fileVersions . set ( fileName , previousVersion + 1 ) ;
851
- fileContents . set ( fileName , contents ) ;
864
+ setFileContents ( fileName , contents ) ;
852
865
projectMustUpdate = true ;
853
866
}
854
867
if ( projectMustUpdate ) onProjectMustUpdate ( ) ;
@@ -866,8 +879,9 @@ export function create(rawOptions: CreateOptions = {}): Service {
866
879
readDirectory,
867
880
realpath,
868
881
resolvePath,
869
- } = createCachedFilesystemFunctions ( { readFile, fileExists } ) ;
870
- const fileContents = new Map < string , string > ( ) ;
882
+ setFileContents,
883
+ fileContents,
884
+ } = createCachedFilesystem ( { readFile, fileExists } ) ;
871
885
const rootFileNames = new Set ( config . fileNames ) ;
872
886
const fileVersions = new Map (
873
887
Array . from ( rootFileNames ) . map ( ( fileName ) => [ fileName , 0 ] )
@@ -891,26 +905,22 @@ export function create(rawOptions: CreateOptions = {}): Service {
891
905
Required < Pick < _ts . LanguageServiceHost , 'fileExists' | 'readFile' > > = {
892
906
getProjectVersion : ( ) => String ( projectVersion ) ,
893
907
getScriptFileNames : ( ) => Array . from ( rootFileNames ) ,
894
- getScriptVersion : ( fileName : string ) => {
895
- const version = fileVersions . get ( fileName ) ;
896
- return version ? version . toString ( ) : '' ;
897
- } ,
908
+ // Language service calls getScriptSnapshot, then getScriptVersion, in that order
898
909
getScriptSnapshot ( fileName : string ) {
899
- // TODO ordering of this with getScriptVersion? Should they sync up?
900
- let contents = fileContents . get ( fileName ) ;
901
-
902
- // Read contents into TypeScript memory cache.
903
- if ( contents === undefined ) {
904
- contents = cachedReadFile ( fileName ) ;
905
- if ( contents === undefined ) return ;
906
-
907
- fileVersions . set ( fileName , 1 ) ;
908
- fileContents . set ( fileName , contents ) ;
909
- projectVersion ++ ;
910
- }
911
-
910
+ debug ( 'getScriptSnapshot' , fileName ) ;
911
+ const contents = cachedReadFile ( fileName ) ;
912
+ if ( contents === undefined ) return ;
912
913
return ts . ScriptSnapshot . fromString ( contents ) ;
913
914
} ,
915
+ getScriptVersion ( fileName : string ) {
916
+ debug ( 'getScriptVersion' , fileName ) ;
917
+ let version = fileVersions . get ( fileName ) ;
918
+ if ( version === undefined ) {
919
+ version = 1 ;
920
+ fileVersions . set ( fileName , version ) ;
921
+ }
922
+ return version . toString ( ) ;
923
+ } ,
914
924
readFile : cachedReadFile ,
915
925
readDirectory,
916
926
getDirectories,
@@ -944,6 +954,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
944
954
const { updateMemoryCache } = createUpdateMemoryCacheFunction ( {
945
955
rootFileNames,
946
956
fileContents,
957
+ setFileContents,
947
958
fileVersions,
948
959
isFileKnownToBeInternal,
949
960
markBucketOfFilenameInternal,
@@ -1090,6 +1101,7 @@ export function create(rawOptions: CreateOptions = {}): Service {
1090
1101
rootFileNames,
1091
1102
fileVersions,
1092
1103
fileContents,
1104
+ setFileContents,
1093
1105
isFileKnownToBeInternal,
1094
1106
markBucketOfFilenameInternal,
1095
1107
onProjectMustUpdate ( ) {
0 commit comments