@@ -1668,7 +1668,19 @@ namespace ts {
16681668 const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*" ;
16691669 const singleAsteriskRegexFragmentOther = "[^/]*" ;
16701670
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 {
16721684 if ( specs === undefined || specs . length === 0 ) {
16731685 return undefined ;
16741686 }
@@ -1682,33 +1694,8 @@ namespace ts {
16821694 */
16831695 const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?" ;
16841696
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 ) ) ;
17121699 }
17131700
17141701 /**
@@ -1803,6 +1790,9 @@ namespace ts {
18031790 }
18041791
18051792 export interface FileMatcherPatterns {
1793+ /** One pattern for each "include" spec. */
1794+ includeFilePatterns : string [ ] ;
1795+ /** One pattern matching one of any of the "include" specs. */
18061796 includeFilePattern : string ;
18071797 includeDirectoryPattern : string ;
18081798 excludePattern : string ;
@@ -1815,6 +1805,7 @@ namespace ts {
18151805 const absolutePath = combinePaths ( currentDirectory , path ) ;
18161806
18171807 return {
1808+ includeFilePatterns : map ( getRegularExpressionsForWildcards ( includes , absolutePath , "files" ) , pattern => `^${ pattern } $` ) ,
18181809 includeFilePattern : getRegularExpressionForWildcard ( includes , absolutePath , "files" ) ,
18191810 includeDirectoryPattern : getRegularExpressionForWildcard ( includes , absolutePath , "directories" ) ,
18201811 excludePattern : getRegularExpressionForWildcard ( excludes , absolutePath , "exclude" ) ,
@@ -1829,38 +1820,76 @@ namespace ts {
18291820 const patterns = getFileMatcherPatterns ( path , excludes , includes , useCaseSensitiveFileNames , currentDirectory ) ;
18301821
18311822 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 ) ) ;
18331824 const includeDirectoryRegex = patterns . includeDirectoryPattern && new RegExp ( patterns . includeDirectoryPattern , regexFlag ) ;
18341825 const excludeRegex = patterns . excludePattern && new RegExp ( patterns . excludePattern , regexFlag ) ;
18351826
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+
18371831 for ( const basePath of patterns . basePaths ) {
1838- visitDirectory ( basePath , combinePaths ( currentDirectory , basePath ) ) ;
1832+ forEachFileInRecursiveDirectories ( basePath , combinePaths ( currentDirectory , basePath ) , { useCaseSensitiveFileNames , getFileSystemEntries , includeDirectory , visitFile } ) ;
18391833 }
1840- return result ;
18411834
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+ }
18521858 }
18531859 }
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 ) ;
18541872
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 ) ;
18611886 }
18621887 }
18631888 }
1889+
1890+ function sorted ( names : string [ ] ) : string [ ] {
1891+ return names . slice ( ) . sort ( visitor . useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive ) ;
1892+ }
18641893 }
18651894
18661895 /**
0 commit comments