@@ -467,13 +467,20 @@ namespace ts {
467
467
return result ;
468
468
}
469
469
470
- export function flatMapIter < T , U > ( iter : Iterator < T > , mapfn : ( x : T ) => U [ ] | undefined ) : U [ ] {
470
+ export function flatMapIter < T , U > ( iter : Iterator < T > , mapfn : ( x : T ) => U | U [ ] | undefined ) : U [ ] {
471
471
const result : U [ ] = [ ] ;
472
472
while ( true ) {
473
473
const { value, done } = iter . next ( ) ;
474
474
if ( done ) break ;
475
475
const res = mapfn ( value ) ;
476
- if ( res ) result . push ( ...res ) ;
476
+ if ( res ) {
477
+ if ( isArray ( res ) ) {
478
+ result . push ( ...res ) ;
479
+ }
480
+ else {
481
+ result . push ( res ) ;
482
+ }
483
+ }
477
484
}
478
485
return result ;
479
486
}
@@ -523,6 +530,19 @@ namespace ts {
523
530
return result ;
524
531
}
525
532
533
+ export function mapDefinedIter < T , U > ( iter : Iterator < T > , mapFn : ( x : T ) => U | undefined ) : U [ ] {
534
+ const result : U [ ] = [ ] ;
535
+ while ( true ) {
536
+ const { value, done } = iter . next ( ) ;
537
+ if ( done ) break ;
538
+ const res = mapFn ( value ) ;
539
+ if ( res !== undefined ) {
540
+ result . push ( res ) ;
541
+ }
542
+ }
543
+ return result ;
544
+ }
545
+
526
546
/**
527
547
* Computes the first matching span of elements and returns a tuple of the first span
528
548
* and the remaining elements.
@@ -766,7 +786,7 @@ namespace ts {
766
786
* @param end The offset in `from` at which to stop copying values (non-inclusive).
767
787
*/
768
788
export function addRange < T > ( to : T [ ] | undefined , from : ReadonlyArray < T > | undefined , start ?: number , end ?: number ) : T [ ] | undefined {
769
- if ( from === undefined ) return to ;
789
+ if ( from === undefined || from . length === 0 ) return to ;
770
790
if ( to === undefined ) return from . slice ( start , end ) ;
771
791
start = start === undefined ? 0 : toOffset ( from , start ) ;
772
792
end = end === undefined ? from . length : toOffset ( from , end ) ;
@@ -1222,6 +1242,13 @@ namespace ts {
1222
1242
return Array . isArray ? Array . isArray ( value ) : value instanceof Array ;
1223
1243
}
1224
1244
1245
+ /**
1246
+ * Tests whether a value is string
1247
+ */
1248
+ export function isString ( text : any ) : text is string {
1249
+ return typeof text === "string" ;
1250
+ }
1251
+
1225
1252
export function tryCast < TOut extends TIn , TIn = any > ( value : TIn | undefined , test : ( value : TIn ) => value is TOut ) : TOut | undefined {
1226
1253
return value !== undefined && test ( value ) ? value : undefined ;
1227
1254
}
@@ -1232,7 +1259,13 @@ namespace ts {
1232
1259
}
1233
1260
1234
1261
/** Does nothing. */
1235
- export function noop ( ) : void { }
1262
+ export function noop ( ) : void { }
1263
+
1264
+ /** Do nothing and return false */
1265
+ export function returnFalse ( ) : false { return false ; }
1266
+
1267
+ /** Do nothing and return true */
1268
+ export function returnTrue ( ) : true { return true ; }
1236
1269
1237
1270
/** Returns its argument. */
1238
1271
export function identity < T > ( x : T ) { return x ; }
@@ -1476,16 +1509,16 @@ namespace ts {
1476
1509
function compareMessageText ( text1 : string | DiagnosticMessageChain , text2 : string | DiagnosticMessageChain ) : Comparison {
1477
1510
while ( text1 && text2 ) {
1478
1511
// We still have both chains.
1479
- const string1 = typeof text1 === "string" ? text1 : text1 . messageText ;
1480
- const string2 = typeof text2 === "string" ? text2 : text2 . messageText ;
1512
+ const string1 = isString ( text1 ) ? text1 : text1 . messageText ;
1513
+ const string2 = isString ( text2 ) ? text2 : text2 . messageText ;
1481
1514
1482
1515
const res = compareValues ( string1 , string2 ) ;
1483
1516
if ( res ) {
1484
1517
return res ;
1485
1518
}
1486
1519
1487
- text1 = typeof text1 === "string" ? undefined : text1 . next ;
1488
- text2 = typeof text2 === "string" ? undefined : text2 . next ;
1520
+ text1 = isString ( text1 ) ? undefined : text1 . next ;
1521
+ text2 = isString ( text2 ) ? undefined : text2 . next ;
1489
1522
}
1490
1523
1491
1524
if ( ! text1 && ! text2 ) {
@@ -1811,6 +1844,8 @@ namespace ts {
1811
1844
* Removes a trailing directory separator from a path.
1812
1845
* @param path The path.
1813
1846
*/
1847
+ export function removeTrailingDirectorySeparator ( path : Path ) : Path ;
1848
+ export function removeTrailingDirectorySeparator ( path : string ) : string ;
1814
1849
export function removeTrailingDirectorySeparator ( path : string ) {
1815
1850
if ( path . charAt ( path . length - 1 ) === directorySeparator ) {
1816
1851
return path . substr ( 0 , path . length - 1 ) ;
@@ -2073,8 +2108,8 @@ namespace ts {
2073
2108
}
2074
2109
2075
2110
export interface FileSystemEntries {
2076
- files : ReadonlyArray < string > ;
2077
- directories : ReadonlyArray < string > ;
2111
+ readonly files : ReadonlyArray < string > ;
2112
+ readonly directories : ReadonlyArray < string > ;
2078
2113
}
2079
2114
2080
2115
export interface FileMatcherPatterns {
@@ -2227,7 +2262,7 @@ namespace ts {
2227
2262
return ScriptKind . TS ;
2228
2263
case Extension . Tsx :
2229
2264
return ScriptKind . TSX ;
2230
- case ".json" :
2265
+ case Extension . Json :
2231
2266
return ScriptKind . JSON ;
2232
2267
default :
2233
2268
return ScriptKind . Unknown ;
@@ -2631,5 +2666,203 @@ namespace ts {
2631
2666
return ( arg : T ) => f ( arg ) && g ( arg ) ;
2632
2667
}
2633
2668
2634
- export function assertTypeIsNever ( _ : never ) : void { }
2669
+ export function assertTypeIsNever ( _ : never ) : void { }
2670
+
2671
+ export interface CachedDirectoryStructureHost extends DirectoryStructureHost {
2672
+ addOrDeleteFileOrDirectory ( fileOrDirectory : string , fileOrDirectoryPath : Path ) : void ;
2673
+ addOrDeleteFile ( fileName : string , filePath : Path , eventKind : FileWatcherEventKind ) : void ;
2674
+ clearCache ( ) : void ;
2675
+ }
2676
+
2677
+ interface MutableFileSystemEntries {
2678
+ readonly files : string [ ] ;
2679
+ readonly directories : string [ ] ;
2680
+ }
2681
+
2682
+ export function createCachedDirectoryStructureHost ( host : DirectoryStructureHost ) : CachedDirectoryStructureHost {
2683
+ const cachedReadDirectoryResult = createMap < MutableFileSystemEntries > ( ) ;
2684
+ const getCurrentDirectory = memoize ( ( ) => host . getCurrentDirectory ( ) ) ;
2685
+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
2686
+ return {
2687
+ useCaseSensitiveFileNames : host . useCaseSensitiveFileNames ,
2688
+ newLine : host . newLine ,
2689
+ readFile : ( path , encoding ) => host . readFile ( path , encoding ) ,
2690
+ write : s => host . write ( s ) ,
2691
+ writeFile,
2692
+ fileExists,
2693
+ directoryExists,
2694
+ createDirectory,
2695
+ getCurrentDirectory,
2696
+ getDirectories,
2697
+ readDirectory,
2698
+ addOrDeleteFileOrDirectory,
2699
+ addOrDeleteFile,
2700
+ clearCache,
2701
+ exit : code => host . exit ( code )
2702
+ } ;
2703
+
2704
+ function toPath ( fileName : string ) {
2705
+ return ts . toPath ( fileName , getCurrentDirectory ( ) , getCanonicalFileName ) ;
2706
+ }
2707
+
2708
+ function getCachedFileSystemEntries ( rootDirPath : Path ) : MutableFileSystemEntries | undefined {
2709
+ return cachedReadDirectoryResult . get ( rootDirPath ) ;
2710
+ }
2711
+
2712
+ function getCachedFileSystemEntriesForBaseDir ( path : Path ) : MutableFileSystemEntries | undefined {
2713
+ return getCachedFileSystemEntries ( getDirectoryPath ( path ) ) ;
2714
+ }
2715
+
2716
+ function getBaseNameOfFileName ( fileName : string ) {
2717
+ return getBaseFileName ( normalizePath ( fileName ) ) ;
2718
+ }
2719
+
2720
+ function createCachedFileSystemEntries ( rootDir : string , rootDirPath : Path ) {
2721
+ const resultFromHost : MutableFileSystemEntries = {
2722
+ files : map ( host . readDirectory ( rootDir , /*extensions*/ undefined , /*exclude*/ undefined , /*include*/ [ "*.*" ] ) , getBaseNameOfFileName ) || [ ] ,
2723
+ directories : host . getDirectories ( rootDir ) || [ ]
2724
+ } ;
2725
+
2726
+ cachedReadDirectoryResult . set ( rootDirPath , resultFromHost ) ;
2727
+ return resultFromHost ;
2728
+ }
2729
+
2730
+ /**
2731
+ * If the readDirectory result was already cached, it returns that
2732
+ * Otherwise gets result from host and caches it.
2733
+ * The host request is done under try catch block to avoid caching incorrect result
2734
+ */
2735
+ function tryReadDirectory ( rootDir : string , rootDirPath : Path ) : MutableFileSystemEntries | undefined {
2736
+ const cachedResult = getCachedFileSystemEntries ( rootDirPath ) ;
2737
+ if ( cachedResult ) {
2738
+ return cachedResult ;
2739
+ }
2740
+
2741
+ try {
2742
+ return createCachedFileSystemEntries ( rootDir , rootDirPath ) ;
2743
+ }
2744
+ catch ( _e ) {
2745
+ // If there is exception to read directories, dont cache the result and direct the calls to host
2746
+ Debug . assert ( ! cachedReadDirectoryResult . has ( rootDirPath ) ) ;
2747
+ return undefined ;
2748
+ }
2749
+ }
2750
+
2751
+ function fileNameEqual ( name1 : string , name2 : string ) {
2752
+ return getCanonicalFileName ( name1 ) === getCanonicalFileName ( name2 ) ;
2753
+ }
2754
+
2755
+ function hasEntry ( entries : ReadonlyArray < string > , name : string ) {
2756
+ return some ( entries , file => fileNameEqual ( file , name ) ) ;
2757
+ }
2758
+
2759
+ function updateFileSystemEntry ( entries : string [ ] , baseName : string , isValid : boolean ) {
2760
+ if ( hasEntry ( entries , baseName ) ) {
2761
+ if ( ! isValid ) {
2762
+ return filterMutate ( entries , entry => ! fileNameEqual ( entry , baseName ) ) ;
2763
+ }
2764
+ }
2765
+ else if ( isValid ) {
2766
+ return entries . push ( baseName ) ;
2767
+ }
2768
+ }
2769
+
2770
+ function writeFile ( fileName : string , data : string , writeByteOrderMark ?: boolean ) : void {
2771
+ const path = toPath ( fileName ) ;
2772
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2773
+ if ( result ) {
2774
+ updateFilesOfFileSystemEntry ( result , getBaseNameOfFileName ( fileName ) , /*fileExists*/ true ) ;
2775
+ }
2776
+ return host . writeFile ( fileName , data , writeByteOrderMark ) ;
2777
+ }
2778
+
2779
+ function fileExists ( fileName : string ) : boolean {
2780
+ const path = toPath ( fileName ) ;
2781
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2782
+ return result && hasEntry ( result . files , getBaseNameOfFileName ( fileName ) ) ||
2783
+ host . fileExists ( fileName ) ;
2784
+ }
2785
+
2786
+ function directoryExists ( dirPath : string ) : boolean {
2787
+ const path = toPath ( dirPath ) ;
2788
+ return cachedReadDirectoryResult . has ( path ) || host . directoryExists ( dirPath ) ;
2789
+ }
2790
+
2791
+ function createDirectory ( dirPath : string ) {
2792
+ const path = toPath ( dirPath ) ;
2793
+ const result = getCachedFileSystemEntriesForBaseDir ( path ) ;
2794
+ const baseFileName = getBaseNameOfFileName ( dirPath ) ;
2795
+ if ( result ) {
2796
+ updateFileSystemEntry ( result . directories , baseFileName , /*isValid*/ true ) ;
2797
+ }
2798
+ host . createDirectory ( dirPath ) ;
2799
+ }
2800
+
2801
+ function getDirectories ( rootDir : string ) : string [ ] {
2802
+ const rootDirPath = toPath ( rootDir ) ;
2803
+ const result = tryReadDirectory ( rootDir , rootDirPath ) ;
2804
+ if ( result ) {
2805
+ return result . directories . slice ( ) ;
2806
+ }
2807
+ return host . getDirectories ( rootDir ) ;
2808
+ }
2809
+
2810
+ function readDirectory ( rootDir : string , extensions ?: ReadonlyArray < string > , excludes ?: ReadonlyArray < string > , includes ?: ReadonlyArray < string > , depth ?: number ) : string [ ] {
2811
+ const rootDirPath = toPath ( rootDir ) ;
2812
+ const result = tryReadDirectory ( rootDir , rootDirPath ) ;
2813
+ if ( result ) {
2814
+ return matchFiles ( rootDir , extensions , excludes , includes , host . useCaseSensitiveFileNames , getCurrentDirectory ( ) , depth , getFileSystemEntries ) ;
2815
+ }
2816
+ return host . readDirectory ( rootDir , extensions , excludes , includes , depth ) ;
2817
+
2818
+ function getFileSystemEntries ( dir : string ) {
2819
+ const path = toPath ( dir ) ;
2820
+ if ( path === rootDirPath ) {
2821
+ return result ;
2822
+ }
2823
+ return getCachedFileSystemEntries ( path ) || createCachedFileSystemEntries ( dir , path ) ;
2824
+ }
2825
+ }
2826
+
2827
+ function addOrDeleteFileOrDirectory ( fileOrDirectory : string , fileOrDirectoryPath : Path ) {
2828
+ const existingResult = getCachedFileSystemEntries ( fileOrDirectoryPath ) ;
2829
+ if ( existingResult ) {
2830
+ // This was a folder already present, remove it if this doesnt exist any more
2831
+ if ( ! host . directoryExists ( fileOrDirectory ) ) {
2832
+ cachedReadDirectoryResult . delete ( fileOrDirectoryPath ) ;
2833
+ }
2834
+ }
2835
+ else {
2836
+ // This was earlier a file (hence not in cached directory contents)
2837
+ // or we never cached the directory containing it
2838
+ const parentResult = getCachedFileSystemEntriesForBaseDir ( fileOrDirectoryPath ) ;
2839
+ if ( parentResult ) {
2840
+ const baseName = getBaseNameOfFileName ( fileOrDirectory ) ;
2841
+ if ( parentResult ) {
2842
+ updateFilesOfFileSystemEntry ( parentResult , baseName , host . fileExists ( fileOrDirectoryPath ) ) ;
2843
+ updateFileSystemEntry ( parentResult . directories , baseName , host . directoryExists ( fileOrDirectoryPath ) ) ;
2844
+ }
2845
+ }
2846
+ }
2847
+ }
2848
+
2849
+ function addOrDeleteFile ( fileName : string , filePath : Path , eventKind : FileWatcherEventKind ) {
2850
+ if ( eventKind === FileWatcherEventKind . Changed ) {
2851
+ return ;
2852
+ }
2853
+
2854
+ const parentResult = getCachedFileSystemEntriesForBaseDir ( filePath ) ;
2855
+ if ( parentResult ) {
2856
+ updateFilesOfFileSystemEntry ( parentResult , getBaseNameOfFileName ( fileName ) , eventKind === FileWatcherEventKind . Created ) ;
2857
+ }
2858
+ }
2859
+
2860
+ function updateFilesOfFileSystemEntry ( parentResult : MutableFileSystemEntries , baseName : string , fileExists : boolean ) {
2861
+ updateFileSystemEntry ( parentResult . files , baseName , fileExists ) ;
2862
+ }
2863
+
2864
+ function clearCache ( ) {
2865
+ cachedReadDirectoryResult . clear ( ) ;
2866
+ }
2867
+ }
2635
2868
}
0 commit comments