@@ -1202,6 +1202,13 @@ namespace ts {
1202
1202
return Array . isArray ? Array . isArray ( value ) : value instanceof Array ;
1203
1203
}
1204
1204
1205
+ /**
1206
+ * Tests whether a value is string
1207
+ */
1208
+ export function isString ( text : any ) : text is string {
1209
+ return typeof text === "string" ;
1210
+ }
1211
+
1205
1212
export function tryCast < TOut extends TIn , TIn = any > ( value : TIn | undefined , test : ( value : TIn ) => value is TOut ) : TOut | undefined {
1206
1213
return value !== undefined && test ( value ) ? value : undefined ;
1207
1214
}
@@ -1212,7 +1219,10 @@ namespace ts {
1212
1219
}
1213
1220
1214
1221
/** Does nothing. */
1215
- export function noop ( ) : void { }
1222
+ export function noop ( ) : void { }
1223
+
1224
+ /** Do nothing and return false */
1225
+ export function returnFalse ( ) : false { return false ; }
1216
1226
1217
1227
/** Throws an error because a function is not implemented. */
1218
1228
export function notImplemented ( ) : never {
@@ -1455,16 +1465,16 @@ namespace ts {
1455
1465
function compareMessageText ( text1 : string | DiagnosticMessageChain , text2 : string | DiagnosticMessageChain ) : Comparison {
1456
1466
while ( text1 && text2 ) {
1457
1467
// We still have both chains.
1458
- const string1 = typeof text1 === "string" ? text1 : text1 . messageText ;
1459
- const string2 = typeof text2 === "string" ? text2 : text2 . messageText ;
1468
+ const string1 = isString ( text1 ) ? text1 : text1 . messageText ;
1469
+ const string2 = isString ( text2 ) ? text2 : text2 . messageText ;
1460
1470
1461
1471
const res = compareValues ( string1 , string2 ) ;
1462
1472
if ( res ) {
1463
1473
return res ;
1464
1474
}
1465
1475
1466
- text1 = typeof text1 === "string" ? undefined : text1 . next ;
1467
- text2 = typeof text2 === "string" ? undefined : text2 . next ;
1476
+ text1 = isString ( text1 ) ? undefined : text1 . next ;
1477
+ text2 = isString ( text2 ) ? undefined : text2 . next ;
1468
1478
}
1469
1479
1470
1480
if ( ! text1 && ! text2 ) {
@@ -2066,8 +2076,8 @@ namespace ts {
2066
2076
}
2067
2077
2068
2078
export interface FileSystemEntries {
2069
- files : ReadonlyArray < string > ;
2070
- directories : ReadonlyArray < string > ;
2079
+ readonly files : ReadonlyArray < string > ;
2080
+ readonly directories : ReadonlyArray < string > ;
2071
2081
}
2072
2082
2073
2083
export interface FileMatcherPatterns {
@@ -2620,4 +2630,204 @@ namespace ts {
2620
2630
export function isCheckJsEnabledForFile ( sourceFile : SourceFile , compilerOptions : CompilerOptions ) {
2621
2631
return sourceFile . checkJsDirective ? sourceFile . checkJsDirective . enabled : compilerOptions . checkJs ;
2622
2632
}
2633
+
2634
+ export interface HostForCaching extends PartialSystem {
2635
+ useCaseSensitiveFileNames : boolean ;
2636
+ }
2637
+
2638
+ export interface CachedHost {
2639
+ addOrDeleteFileOrFolder ( fileOrFolder : string , fileOrFolderPath : Path ) : void ;
2640
+ addOrDeleteFile ( fileName : string , filePath : Path , eventKind : FileWatcherEventKind ) : void ;
2641
+ clearCache ( ) : void ;
2642
+ }
2643
+
2644
+ export interface CachedPartialSystem extends PartialSystem , CachedHost {
2645
+ }
2646
+
2647
+ interface MutableFileSystemEntries {
2648
+ readonly files : string [ ] ;
2649
+ readonly directories : string [ ] ;
2650
+ }
2651
+
2652
+ export function createCachedPartialSystem ( host : HostForCaching ) : CachedPartialSystem {
2653
+ const cachedReadDirectoryResult = createMap < MutableFileSystemEntries > ( ) ;
2654
+ const getCurrentDirectory = memoize ( ( ) => host . getCurrentDirectory ( ) ) ;
2655
+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
2656
+ return {
2657
+ writeFile,
2658
+ fileExists,
2659
+ directoryExists,
2660
+ createDirectory,
2661
+ getCurrentDirectory,
2662
+ getDirectories,
2663
+ readDirectory,
2664
+ addOrDeleteFileOrFolder,
2665
+ addOrDeleteFile,
2666
+ clearCache
2667
+ } ;
2668
+
2669
+ function toPath ( fileName : string ) {
2670
+ return ts . toPath ( fileName , getCurrentDirectory ( ) , getCanonicalFileName ) ;
2671
+ }
2672
+
2673
+ function getCachedFileSystemEntries ( rootDirPath : Path ) : MutableFileSystemEntries | undefined {
2674
+ return cachedReadDirectoryResult . get ( rootDirPath ) ;
2675
+ }
2676
+
2677
+ function getCachedFileSystemEntriesForBaseDir ( path : Path ) : MutableFileSystemEntries | undefined {
2678
+ return getCachedFileSystemEntries ( getDirectoryPath ( path ) ) ;
2679
+ }
2680
+
2681
+ function getBaseNameOfFileName ( fileName : string ) {
2682
+ return getBaseFileName ( normalizePath ( fileName ) ) ;
2683
+ }
2684
+
2685
+ function createCachedFileSystemEntries ( rootDir : string , rootDirPath : Path ) {
2686
+ const resultFromHost : MutableFileSystemEntries = {
2687
+ files : map ( host . readDirectory ( rootDir , /*extensions*/ undefined , /*exclude*/ undefined , /*include*/ [ "*.*" ] ) , getBaseNameOfFileName ) || [ ] ,
2688
+ directories : host . getDirectories ( rootDir ) || [ ]
2689
+ } ;
2690
+
2691
+ cachedReadDirectoryResult . set ( rootDirPath , resultFromHost ) ;
2692
+ return resultFromHost ;
2693
+ }
2694
+
2695
+ /**
2696
+ * If the readDirectory result was already cached, it returns that
2697
+ * Otherwise gets result from host and caches it.
2698
+ * The host request is done under try catch block to avoid caching incorrect result
2699
+ */
2700
+ function tryReadDirectory ( rootDir : string , rootDirPath : Path ) : MutableFileSystemEntries | undefined {
2701
+ const cachedResult = getCachedFileSystemEntries ( rootDirPath ) ;
2702
+ if ( cachedResult ) {
2703
+ return cachedResult ;
2704
+ }
2705
+
2706
+ try {
2707
+ return createCachedFileSystemEntries ( rootDir , rootDirPath ) ;
2708
+ }
2709
+ catch ( _e ) {
2710
+ // If there is exception to read directories, dont cache the result and direct the calls to host
2711
+ Debug . assert ( ! cachedReadDirectoryResult . has ( rootDirPath ) ) ;
2712
+ return undefined ;
2713
+ }
2714
+ }
2715
+
2716
+ function fileNameEqual ( name1 : string , name2 : string ) {
2717
+ return getCanonicalFileName ( name1 ) === getCanonicalFileName ( name2 ) ;
2718
+ }
2719
+
2720
+
10000
function hasEntry ( entries : ReadonlyArray < string > , name : string ) {
2721
+ return some ( entries , file => fileNameEqual ( file , name ) ) ;
2722
+ }
2723
+
2724
+ function updateFileSystemEntry ( entries : string [ ] , baseName : string , isValid : boolean ) {
2725
+ if ( hasEntry ( entries , baseName ) ) {
2726
+ if ( ! isValid ) {
2727
+ return filterMutate ( entries , entry => ! fileNameEqual ( entry , baseName ) ) ;
2728
+ }
2729
+ }
2730
+ else if ( isValid ) {
2731
+ return entries . push ( baseName ) ;
2732
+ }
2733
+ }
2734
+
2735
+ function writeFile ( fileName : string , data : string , writeByteOrderMark ?: boolean ) : void {
2736
+ const path = toPath ( fileName ) ;
2737
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2738
+ if ( result ) {
2739
+ updateFilesOfFileSystemEntry ( result , getBaseNameOfFileName ( fileName ) , /*fileExists*/ true ) ;
2740
+ }
2741
+ return host . writeFile ( fileName , data , writeByteOrderMark ) ;
2742
+ }
2743
+
2744
+ function fileExists ( fileName : string ) : boolean {
2745
+ const path = toPath ( fileName ) ;
2746
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2747
+ return result && hasEntry ( result . files , getBaseNameOfFileName ( fileName ) ) ||
2748
+ host . fileExists ( fileName ) ;
2749
+ }
2750
+
2751
+ function directoryExists ( dirPath : string ) : boolean {
2752
+ const path = toPath ( dirPath ) ;
2753
+ return cachedReadDirectoryResult . has ( path ) || host . directoryExists ( dirPath ) ;
2754
+ }
2755
+
2756
+ function createDirectory ( dirPath : string ) {
2757
+ const path = toPath ( dirPath ) ;
2758
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2759
+ const baseFileName = getBaseNameOfFileName ( dirPath ) ;
2760
+ if ( result ) {
2761
+ updateFileSystemEntry ( result . directories , baseFileName , /*isValid*/ true ) ;
2762
+ }
2763
+ host . createDirectory ( dirPath ) ;
2764
+ }
2765
+
2766
+ function getDirectories ( rootDir : string ) : string [ ] {
2767
+ const rootDirPath = toPath ( rootDir ) ;
2768
+ const result = tryReadDirectory ( rootDir , rootDirPath ) ;
2769
+ if ( result ) {
2770
+ return result . directories . slice ( ) ;
2771
+ }
2772
+ return host . getDirectories ( rootDir ) ;
2773
+ }
2774
+
2775
+ function readDirectory ( rootDir : string , extensions ?: ReadonlyArray < string > , excludes ?: ReadonlyArray < string > , includes ?: ReadonlyArray < string > , depth ?: number ) : string [ ] {
2776
+ const rootDirPath = toPath ( rootDir ) ;
2777
+ const result = tryReadDirectory ( rootDir , rootDirPath ) ;
2778
+ if ( result ) {
2779
+ return matchFiles ( rootDir , extensions , excludes , includes , host . useCaseSensitiveFileNames , getCurrentDirectory ( ) , depth , getFileSystemEntries ) ;
2780
+ }
2781
+ return host . readDirectory ( rootDir , extensions , excludes , includes , depth ) ;
2782
+
2783
+ function getFileSystemEntries ( dir : string ) {
2784
+ const path = toPath ( dir ) ;
2785
+ if ( path === rootDirPath ) {
2786
+ return result ;
2787
+ }
2788
+ return getCachedFileSystemEntries ( path ) || createCachedFileSystemEntries ( dir , path ) ;
2789
+ }
2790
+ }
2791
+
2792
+ function addOrDeleteFileOrFolder ( fileOrFolder : string , fileOrFolderPath : Path ) {
2793
+ const existingResult = getCachedFileSystemEntries ( fileOrFolderPath ) ;
2794
+ if ( existingResult ) {
2795
+ // This was a folder already present, remove it if this doesnt exist any more
2796
+ if ( ! host . directoryExists ( fileOrFolder ) ) {
2797
+ cachedReadDirectoryResult . delete ( fileOrFolderPath ) ;
2798
+ }
2799
+ }
2800
+ else {
2801
+ // This was earlier a file (hence not in cached directory contents)
2802
+ // or we never cached the directory containing it
2803
+ const parentResult = getCachedFileSystemEntriesForBaseDir ( fileOrFolderPath ) ;
2804
+ if ( parentResult ) {
2805
+ const baseName = getBaseNameOfFileName ( fileOrFolder ) ;
2806
+ if ( parentResult ) {
2807
+ updateFilesOfFileSystemEntry ( parentResult , baseName , host . fileExists ( fileOrFolderPath ) ) ;
2808
+ updateFileSystemEntry ( parentResult . directories , baseName , host . directoryExists ( fileOrFolderPath ) ) ;
2809
+ }
2810
+ }
2811
+ }
2812
+ }
2813
+
2814
+ function addOrDeleteFile ( fileName : string , filePath : Path , eventKind : FileWatcherEventKind ) {
2815
+ if ( eventKind === FileWatcherEventKind . Changed ) {
2816
+ return ;
2817
+ }
2818
+
2819
+ const parentResult = getCachedFileSystemEntriesForBaseDir ( filePath ) ;
2820
+ if ( parentResult ) {
2821
+ updateFilesOfFileSystemEntry ( parentResult , getBaseNameOfFileName ( fileName ) , eventKind === FileWatcherEventKind . Created ) ;
2822
+ }
2823
+ }
2824
+
2825
+ function updateFilesOfFileSystemEntry ( parentResult : MutableFileSystemEntries , baseName : string , fileExists : boolean ) {
2826
+ updateFileSystemEntry ( parentResult . files , baseName , fileExists ) ;
2827
+ }
2828
+
2829
+ function clearCache ( ) {
2830
+ cachedReadDirectoryResult . clear ( ) ;
2831
+ }
2832
+ }
2623
2833
}
0 commit comments