@@ -204,6 +204,10 @@ namespace ts {
204204 GreaterThan = 1
205205 }
206206
207+ export function length ( array : any [ ] ) {
208+ return array ? array . length : 0 ;
209+ }
210+
207211 /**
208212 * Iterates through 'array' by index and performs the callback on each element of array until the callback
209213 * returns a truthy value, then returns that value.
@@ -256,6 +260,16 @@ namespace ts {
256260 return undefined ;
257261 }
258262
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+
259273 /**
260274 * Returns the first truthy result of `callback`, or else fails.
261275 * This is like `forEach`, but never returns undefined.
@@ -1720,7 +1734,19 @@ namespace ts {
17201734 const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*" ;
17211735 const singleAsteriskRegexFragmentOther = "[^/]*" ;
17221736
1723- 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 {
17241750 if ( specs === undefined || specs . length === 0 ) {
17251751 return undefined ;
17261752 }
@@ -1734,33 +1760,8 @@ namespace ts {
17341760 */
17351761 const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?" ;
17361762
1737- let pattern = "" ;
1738- let hasWrittenSubpattern = false ;
1739- for ( const spec of specs ) {
1740- if ( ! spec ) {
1741- continue ;
1742- }
1743-
1744- const subPattern = getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ;
1745- if ( subPattern === undefined ) {
1746- continue ;
1747- }
1748-
1749- if ( hasWrittenSubpattern ) {
1750- pattern += "|" ;
1751- }
1752-
1753- pattern += "(" + subPattern + ")" ;
1754- hasWrittenSubpattern = true ;
1755- }
1756-
1757- if ( ! pattern ) {
1758- return undefined ;
1759- }
1760-
1761- // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
1762- const terminator = usage === "exclude" ? "($|/)" : "$" ;
1763- return `^(${ pattern } )${ terminator } ` ;
1763+ return flatMap ( specs , spec =>
1764+ spec && getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ) ;
17641765 }
17651766
17661767 /**
@@ -1855,6 +1856,9 @@ namespace ts {
18551856 }
18561857
18571858 export interface FileMatcherPatterns {
1859+ /** One pattern for each "include" spec. */
1860+ includeFilePatterns : string [ ] ;
1861+ /** One pattern matching one of any of the "include" specs. */
18581862 includeFilePattern : string ;
18591863 includeDirectoryPattern : string ;
18601864 excludePattern : string ;
@@ -1867,6 +1871,7 @@ namespace ts {
18671871 const absolutePath = combinePaths ( currentDirectory , path ) ;
18681872
18691873 return {
1874+ includeFilePatterns : map ( getRegularExpressionsForWildcards ( includes , absolutePath , "files" ) , pattern => `^${ pattern } $` ) ,
18701875 includeFilePattern : getRegularExpressionForWildcard ( includes , absolutePath , "files" ) ,
18711876 includeDirectoryPattern : getRegularExpressionForWildcard ( includes , absolutePath , "directories" ) ,
18721877 excludePattern : getRegularExpressionForWildcard ( excludes , absolutePath , "exclude" ) ,
@@ -1881,26 +1886,39 @@ namespace ts {
18811886 const patterns = getFileMatcherPatterns ( path , excludes , includes , useCaseSensitiveFileNames , currentDirectory ) ;
18821887
18831888 const regexFlag = useCaseSensitiveFileNames ? "" : "i" ;
1884- const includeFileRegex = patterns . includeFilePattern && new RegExp ( patterns . includeFilePattern , regexFlag ) ;
1889+ const includeFileRegexes = patterns . includeFilePatterns && patterns . includeFilePatterns . map ( pattern => new RegExp ( pattern , regexFlag ) ) ;
18851890 const includeDirectoryRegex = patterns . includeDirectoryPattern && new RegExp ( patterns . includeDirectoryPattern , regexFlag ) ;
18861891 const excludeRegex = patterns . excludePattern && new RegExp ( patterns . excludePattern , regexFlag ) ;
18871892
1888- 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 ;
18891898 for ( const basePath of patterns . basePaths ) {
18901899 visitDirectory ( basePath , combinePaths ( currentDirectory , basePath ) ) ;
18911900 }
1892- return result ;
1901+
1902+ return flatten ( results ) ;
18931903
18941904 function visitDirectory ( path : string , absolutePath : string ) {
1895- const { files, directories } = getFileSystemEntries ( path ) ;
1905+ let { files, directories } = getFileSystemEntries ( path ) ;
1906+ files = files . slice ( ) . sort ( comparer ) ;
1907+ directories = directories . slice ( ) . sort ( comparer ) ;
18961908
18971909 for ( const current of files ) {
18981910 const name = combinePaths ( path , current ) ;
18991911 const absoluteName = combinePaths ( absolutePath , current ) ;
1900- if ( ( ! extensions || fileExtensionIsAny ( name , extensions ) ) &&
1901- ( ! includeFileRegex || includeFileRegex . test ( absoluteName ) ) &&
1902- ( ! excludeRegex || ! excludeRegex . test ( absoluteName ) ) ) {
1903- 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+ }
19041922 }
19051923 }
19061924
0 commit comments