@@ -1668,7 +1668,19 @@ namespace ts {
1668
1668
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*" ;
1669
1669
const singleAsteriskRegexFragmentOther = "[^/]*" ;
1670
1670
1671
- export function getRegularExpressionForWildcard ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) {
1671
+ export function getRegularExpressionForWildcard ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) : string | undefined {
1672
+ const patterns = getRegularExpressionsForWildcards ( specs , basePath , usage ) ;
1673
+ if ( ! patterns || ! patterns . length ) {
1674
+ return undefined ;
1675
+ }
1676
+
1677
+ const pattern = patterns . map ( pattern => `(${ pattern } )` ) . join ( "|" ) ;
1678
+ // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
1679
+ const terminator = usage === "exclude" ? "($|/)" : "$" ;
1680
+ return `^(${ pattern } )${ terminator } ` ;
1681
+ }
1682
+
1683
+ function getRegularExpressionsForWildcards ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) : string [ ] | undefined {
1672
1684
if ( specs === undefined || specs . length === 0 ) {
1673
1685
return undefined ;
1674
1686
}
@@ -1682,33 +1694,8 @@ namespace ts {
1682
1694
*/
1683
1695
const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?" ;
1684
1696
1685
- let pattern = "" ;
1686
- let hasWrittenSubpattern = false ;
1687
- for ( const spec of specs ) {
1688
- if ( ! spec ) {
1689
- continue ;
1690
- }
1691
-
1692
- const subPattern = getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ;
1693
- if ( subPattern === undefined ) {
1694
- continue ;
1695
- }
1696
-
1697
- if ( hasWrittenSubpattern ) {
1698
- pattern += "|" ;
1699
- }
1700
-
1701
- pattern += "(" + subPattern + ")" ;
1702
- hasWrittenSubpattern = true ;
1703
- }
1704
-
1705
- if ( ! pattern ) {
1706
- return undefined ;
1707
- }
1708
-
1709
- // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
1710
- const terminator = usage === "exclude" ? "($|/)" : "$" ;
1711
- return `^(${ pattern } )${ terminator } ` ;
1697
+ return flatMap ( specs , spec =>
1698
+ spec && getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ) ;
1712
1699
}
1713
1700
1714
1701
/**
@@ -1803,6 +1790,9 @@ namespace ts {
1803
1790
}
1804
1791
1805
1792
export interface FileMatcherPatterns {
1793
+ /** One pattern for each "include" spec. */
1794
+ includeFilePatterns : string [ ] ;
1795
+ /** One pattern matching one of any of the "include" specs. */
1806
1796
includeFilePattern : string ;
1807
1797
includeDirectoryPattern : string ;
1808
1798
excludePattern : string ;
@@ -1815,6 +1805,7 @@ namespace ts {
1815
1805
const absolutePath = combinePaths ( currentDirectory , path ) ;
1816
1806
1817
1807
return {
1808
+ includeFilePatterns : map ( getRegularExpressionsForWildcards ( includes , absolutePath , "files" ) , pattern => `^${ pattern } $` ) ,
1818
1809
includeFilePattern : getRegularExpressionForWildcard ( includes , absolutePath , "files" ) ,
1819
1810
includeDirectoryPattern : getRegularExpressionForWildcard ( includes , absolutePath , "directories" ) ,
1820
1811
excludePattern : getRegularExpressionForWildcard ( excludes , absolutePath , "exclude" ) ,
@@ -1829,38 +1820,76 @@ namespace ts {
1829
1820
const patterns = getFileMatcherPatterns ( path , excludes , includes , useCaseSensitiveFileNames , currentDirectory ) ;
1830
1821
1831
1822
const regexFlag = useCaseSensitiveFileNames ? "" : "i" ;
1832
- const includeFileRegex = patterns . includeFilePattern && new RegExp ( patterns . includeFilePattern , regexFlag ) ;
1823
+ const includeFileRegexes = patterns . includeFilePatterns && patterns . includeFilePatterns . map ( pattern => new RegExp ( pattern , regexFlag ) ) ;
1833
1824
const includeDirectoryRegex = patterns . includeDirectoryPattern && new RegExp ( patterns . includeDirectoryPattern , regexFlag ) ;
1834
1825
const excludeRegex = patterns . excludePattern && new RegExp ( patterns . excludePattern , regexFlag ) ;
1835
1826
1836
- const result : string [ ] = [ ] ;
1827
+ // Associate an array of results with each include regex. This keeps results in order of the "include" order.
1828
+ // If there are no "includes", then just put everything in results[0].
1829
+ const results : string [ ] [ ] = includeFileRegexes ? includeFileRegexes . map ( ( ) => [ ] ) : [ [ ] ] ;
1830
+
1837
1831
for ( const basePath of patterns . basePaths ) {
1838
- visitDirectory ( basePath , combinePaths ( currentDirectory , basePath ) ) ;
1832
+ forEachFileInRecursiveDirectories ( basePath , combinePaths ( currentDirectory , basePath ) , { useCaseSensitiveFileNames , getFileSystemEntries , includeDirectory , visitFile } ) ;
1839
1833
}
1840
- return result ;
1841
1834
1842
- function visitDirectory ( path : string , absolutePath : string ) {
1843
- const { files, directories } = getFileSystemEntries ( path ) ;
1844
-
1845
- for ( const current of files ) {
1846
- const name = combinePaths ( path , current ) ;
1847
- const absoluteName = combinePaths ( absolutePath , current ) ;
1848
- if ( ( ! extensions || fileExtensionIsAny ( name , extensions ) ) &&
1849
- ( ! includeFileRegex || includeFileRegex . test ( absoluteName ) ) &&
1850
- ( ! excludeRegex || ! excludeRegex . test ( absoluteName ) ) ) {
1851
- result . push ( name ) ;
1835
+ return flatten ( results ) ;
1836
+
1837
+ function includeDirectory ( absoluteDirectoryName : string ) : boolean {
1838
+ return ( ! includeDirectoryRegex || includeDirectoryRegex . test ( absoluteDirectoryName ) ) &&
1839
+ ( ! excludeRegex || ! excludeRegex . test ( absoluteDirectoryName ) ) ;
1840
+ }
1841
+
1842
+ function visitFile ( fileName : string , absoluteFileName : string ) : void {
1843
+ if ( extensions && ! fileExtensionIsAny ( fileName , extensions ) ||
1844
+ excludeRegex && excludeRegex . test ( absoluteFileName ) ) {
1845
+ return ;
1846
+ }
1847
+
1848
+ if ( ! includeFileRegexes ) {
1849
+ results [ 0 ] . push ( fileName ) ;
1850
+ }
1851
+ else {
1852
+ for ( let i = 0 ; i < includeFileRegexes . length ; i ++ ) {
1853
+ if ( includeFileRegexes [ i ] . test ( absoluteFileName ) ) {
1854
+ results [ i ] . push ( fileName ) ;
1855
+ // Only include a file once.
1856
+ break ;
1857
+ }
1852
1858
}
1853
1859
}
1860
+ }
1861
+ }
1862
+
1863
+ interface RecursiveDirectoryVisitor {
1864
+ useCaseSensitiveFileNames : boolean ;
1865
+ getFileSystemEntries : ( path : string ) => FileSystemEntries ;
1866
+ includeDirectory : ( absoluteDirectoryName : string ) => boolean ;
1867
+ visitFile : ( fileName : string , absoluteFileName : string ) => void ;
1868
+ }
1869
+
1870
+ function forEachFileInRecursiveDirectories ( start : string , absoluteStart : string , visitor : RecursiveDirectoryVisitor ) : void {
1871
+ visitDirectory ( start , absoluteStart ) ;
1854
1872
1855
- for ( const current of directories ) {
1856
- const name = combinePaths ( path , current ) ;
1857
- const absoluteName = combinePaths ( absolutePath , current ) ;
1858
- if ( ( ! includeDirectoryRegex || includeDirectoryRegex . test ( absoluteName ) ) &&
1859
- ( ! excludeRegex || ! excludeRegex . test ( absoluteName ) ) ) {
1860
- visitDirectory ( name , absoluteName ) ;
1873
+ function visitDirectory ( path : string , absolutePath : string ) {
1874
+ let { files, directories } = visitor . getFileSystemEntries ( path ) ;
1875
+ files = sorted ( files ) ;
1876
+ directories = sorted ( directories ) ;
1877
+
1878
+ for ( const file of files ) {
1879
+ visitor . visitFile ( combinePaths ( path , file ) , combinePaths ( absolutePath , file ) ) ;
1880
+ }
1881
+
1882
+ for ( const dir of directories ) {
1883
+ const absoluteName = combinePaths ( absolutePath , dir ) ;
1884
+ if ( visitor . includeDirectory ( absoluteName ) ) {
1885
+ visitDirectory ( combinePaths ( path , dir ) , absoluteName ) ;
1861
1886
}
1862
1887
}
1863
1888
}
1889
+
1890
+ function sorted ( names : string [ ] ) : string [ ] {
1891
+ return names . slice ( ) . sort ( visitor . useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive ) ;
1892
+ }
1864
1893
}
1865
1894
1866
1895
/**
0 commit comments