@@ -260,6 +260,16 @@ namespace ts {
260260 return undefined ;
261261 }
262262
263+ /** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */
264+ export function findIndex < T > ( array : T [ ] , predicate : ( element : T , index : number ) => boolean ) : number {
265+ for ( let i = 0 ; i < array . length ; i ++ ) {
266+ if ( predicate ( array [ i ] , i ) ) {
267+ return i ;
268+ }
269+ }
270+ return - 1 ;
271+ }
272+
263273 /**
264274 * Returns the first truthy result of `callback`, or else fails.
265275 * This is like `forEach`, but never returns undefined.
@@ -1724,7 +1734,19 @@ namespace ts {
17241734 const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*" ;
17251735 const singleAsteriskRegexFragmentOther = "[^/]*" ;
17261736
1727- export function getRegularExpressionForWildcard ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) {
1737+ export function getRegularExpressionForWildcard ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) : string | undefined {
1738+ const patterns = getRegularExpressionsForWildcards ( specs , basePath , usage ) ;
1739+ if ( ! patterns || ! patterns . length ) {
1740+ return undefined ;
1741+ }
1742+
1743+ const pattern = patterns . map ( pattern => `(${ pattern } )` ) . join ( "|" ) ;
1744+ // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
1745+ const terminator = usage === "exclude" ? "($|/)" : "$" ;
1746+ return `^(${ pattern } )${ terminator } ` ;
1747+ }
1748+
1749+ function getRegularExpressionsForWildcards ( specs : string [ ] , basePath : string , usage : "files" | "directories" | "exclude" ) : string [ ] | undefined {
17281750 if ( specs === undefined || specs . length === 0 ) {
17291751 return undefined ;
17301752 }
@@ -1738,33 +1760,8 @@ namespace ts {
17381760 */
17391761 const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?" ;
17401762
1741- let pattern = "" ;
1742- let hasWrittenSubpattern = false ;
1743- for ( const spec of specs ) {
1744- if ( ! spec ) {
1745- continue ;
1746- }
1747-
1748- const subPattern = getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ;
1749- if ( subPattern === undefined ) {
1750- continue ;
1751- }
1752-
1753- if ( hasWrittenSubpattern ) {
1754- pattern += "|" ;
1755- }
1756-
1757- pattern += "(" + subPattern + ")" ;
1758- hasWrittenSubpattern = true ;
1759- }
1760-
1761- if ( ! pattern ) {
1762- return undefined ;
1763- }
1764-
1765- // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
1766- const terminator = usage === "exclude" ? "($|/)" : "$" ;
1767- return `^(${ pattern } )${ terminator } ` ;
1763+ return flatMap ( specs , spec =>
1764+ spec && getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ) ;
17681765 }
17691766
17701767 /**
@@ -1859,6 +1856,9 @@ namespace ts {
18591856 }
18601857
18611858 export interface FileMatcherPatterns {
1859+ /** One pattern for each "include" spec. */
1860+ includeFilePatterns : string [ ] ;
1861+ /** One pattern matching one of any of the "include" specs. */
18621862 includeFilePattern : string ;
18631863 includeDirectoryPattern : string ;
18641864 excludePattern : string ;
@@ -1871,6 +1871,7 @@ namespace ts {
18711871 const absolutePath = combinePaths ( currentDirectory , path ) ;
18721872
18731873 return {
1874+ includeFilePatterns : map ( getRegularExpressionsForWildcards ( includes , absolutePath , "files" ) , pattern => `^${ pattern } $` ) ,
18741875 includeFilePattern : getRegularExpressionForWildcard ( includes , absolutePath , "files" ) ,
18751876 includeDirectoryPattern : getRegularExpressionForWildcard ( includes , absolutePath , "directories" ) ,
18761877 excludePattern : getRegularExpressionForWildcard ( excludes , absolutePath , "exclude" ) ,
@@ -1885,26 +1886,39 @@ namespace ts {
18851886 const patterns = getFileMatcherPatterns ( path , excludes , includes , useCaseSensitiveFileNames , currentDirectory ) ;
18861887
18871888 const regexFlag = useCaseSensitiveFileNames ? "" : "i" ;
1888- const includeFileRegex = patterns . includeFilePattern && new RegExp ( patterns . includeFilePattern , regexFlag ) ;
1889+ const includeFileRegexes = patterns . includeFilePatterns && patterns . includeFilePatterns . map ( pattern => new RegExp ( pattern , regexFlag ) ) ;
18891890 const includeDirectoryRegex = patterns . includeDirectoryPattern && new RegExp ( patterns . includeDirectoryPattern , regexFlag ) ;
18901891 const excludeRegex = patterns . excludePattern && new RegExp ( patterns . excludePattern , regexFlag ) ;
18911892
1892- const result : string [ ] = [ ] ;
1893+ // Associate an array of results with each include regex. This keeps results in order of the "include" order.
1894+ // If there are no "includes", then just put everything in results[0].
1895+ const results : string [ ] [ ] = includeFileRegexes ? includeFileRegexes . map ( ( ) => [ ] ) : [ [ ] ] ;
1896+
1897+ const comparer = useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive ;
18931898 for ( const basePath of patterns . basePaths ) {
18941899 visitDirectory ( basePath , combinePaths ( currentDirectory , basePath ) ) ;
18951900 }
1896- return result ;
1901+
1902+ return flatten ( results ) ;
18971903
18981904 function visitDirectory ( path : string , absolutePath : string ) {
1899- const { files, directories } = getFileSystemEntries ( path ) ;
1905+ let { files, directories } = getFileSystemEntries ( path ) ;
1906+ files = files . slice ( ) . sort ( comparer ) ;
1907+ directories = directories . slice ( ) . sort ( comparer ) ;
19001908
19011909 for ( const current of files ) {
19021910 const name = combinePaths ( path , current ) ;
19031911 const absoluteName = combinePaths ( absolutePath , current ) ;
1904- if ( ( ! extensions || fileExtensionIsAny ( name , extensions ) ) &&
1905- ( ! includeFileRegex || includeFileRegex . test ( absoluteName ) ) &&
1906- ( ! excludeRegex || ! excludeRegex . test ( absoluteName ) ) ) {
1907- result . push ( name ) ;
1912+ if ( extensions && ! fileExtensionIsAny ( name , extensions ) ) continue ;
1913+ if ( excludeRegex && excludeRegex . test ( absoluteName ) ) continue ;
1914+ if ( ! includeFileRegexes ) {
1915+ results [ 0 ] . push ( name ) ;
1916+ }
1917+ else {
1918+ const includeIndex = findIndex ( includeFileRegexes , re => re . test ( absoluteName ) ) ;
1919+ if ( includeIndex !== - 1 ) {
1920+ results [ includeIndex ] . push ( name ) ;
1921+ }
19081922 }
19091923 }
19101924
0 commit comments