@@ -1875,14 +1875,54 @@ namespace ts {
18751875 const reservedCharacterPattern = / [ ^ \w \s \/ ] / g;
18761876 const wildcardCharCodes = [ CharacterCodes . asterisk , CharacterCodes . question ] ;
18771877
1878- /**
1879- * Matches any single directory segment unless it is the last segment and a .min.js file
1880- * Breakdown:
1881- * [^./] # matches everything up to the first . character (excluding directory seperators)
1882- * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
1883- */
1884- const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*" ;
1885- const singleAsteriskRegexFragmentOther = "[^/]*" ;
1878+ /* @internal */
1879+ export const commonPackageFolders : ReadonlyArray < string > = [ "node_modules" , "bower_components" , "jspm_packages" ] ;
1880+
1881+ const implicitExcludePathRegexPattern = `(?!(${ commonPackageFolders . join ( "|" ) } )(/|$))` ;
1882+
1883+ interface WildcardMatcher {
1884+ singleAsteriskRegexFragment : string ;
1885+ doubleAsteriskRegexFragment : string ;
1886+ replaceWildcardCharacter : ( match : string ) => string ;
1887+ }
1888+
1889+ const filesMatcher : WildcardMatcher = {
1890+ /**
1891+ * Matches any single directory segment unless it is the last segment and a .min.js file
1892+ * Breakdown:
1893+ * [^./] # matches everything up to the first . character (excluding directory seperators)
1894+ * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
1895+ */
1896+ singleAsteriskRegexFragment : "([^./]|(\\.(?!min\\.js$))?)*" ,
1897+ /**
1898+ * Regex for the ** wildcard. Matches any number of subdirectories. When used for including
1899+ * files or directories, does not match subdirectories that start with a . character
1900+ */
1901+ doubleAsteriskRegexFragment : `(/${ implicitExcludePathRegexPattern } [^/.][^/]*)*?` ,
1902+ replaceWildcardCharacter : match => replaceWildcardCharacter ( match , filesMatcher . singleAsteriskRegexFragment )
1903+ } ;
1904+
1905+ const directoriesMatcher : WildcardMatcher = {
1906+ singleAsteriskRegexFragment : "[^/]*" ,
1907+ /**
1908+ * Regex for the ** wildcard. Matches any number of subdirectories. When used for including
1909+ * files or directories, does not match subdirectories that start with a . character
1910+ */
1911+ doubleAsteriskRegexFragment : `(/${ implicitExcludePathRegexPattern } [^/.][^/]*)*?` ,
1912+ replaceWildcardCharacter : match => replaceWildcardCharacter ( match , directoriesMatcher . singleAsteriskRegexFragment )
1913+ } ;
1914+
1915+ const excludeMatcher : WildcardMatcher = {
1916+ singleAsteriskRegexFragment : "[^/]*" ,
1917+ doubleAsteriskRegexFragment : "(/.+?)?" ,
1918+ replaceWildcardCharacter : match => replaceWildcardCharacter ( match , excludeMatcher . singleAsteriskRegexFragment )
1919+ } ;
1920+
1921+ const wildcardMatchers = {
1922+ files : filesMatcher ,
1923+ directories : directoriesMatcher ,
1924+ exclude : excludeMatcher
1925+ } ;
18861926
18871927 export function getRegularExpressionForWildcard ( specs : ReadonlyArray < string > , basePath : string , usage : "files" | "directories" | "exclude" ) : string | undefined {
18881928 const patterns = getRegularExpressionsForWildcards ( specs , basePath , usage ) ;
@@ -1901,17 +1941,8 @@ namespace ts {
19011941 return undefined ;
19021942 }
19031943
1904- const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther ;
1905- const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther ;
1906-
1907- /**
1908- * Regex for the ** wildcard. Matches any number of subdirectories. When used for including
1909- * files or directories, does not match subdirectories that start with a . character
1910- */
1911- const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?" ;
1912-
19131944 return flatMap ( specs , spec =>
1914- spec && getSubPatternFromSpec ( spec , basePath , usage , singleAsteriskRegexFragment , doubleAsteriskRegexFragment , replaceWildcardCharacter ) ) ;
1945+ spec && getSubPatternFromSpec ( spec , basePath , usage , wildcardMatchers [ usage ] ) ) ;
19151946 }
19161947
19171948 /**
@@ -1922,7 +1953,7 @@ namespace ts {
19221953 return ! / [ . * ? ] / . test ( lastPathComponent ) ;
19231954 }
19241955
1925- function getSubPatternFromSpec ( spec : string , basePath : string , usage : "files" | "directories" | "exclude" , singleAsteriskRegexFragment : string , doubleAsteriskRegexFragment : string , replaceWildcardCharacter : ( match : string ) => string ) : string | undefined {
1956+ function getSubPatternFromSpec ( spec : string , basePath : string , usage : "files" | "directories" | "exclude" , { singleAsteriskRegexFragment , doubleAsteriskRegexFragment, replaceWildcardCharacter } : WildcardMatcher ) : string | undefined {
19261957 let subpattern = "" ;
19271958 let hasRecursiveDirectoryWildcard = false ;
19281959 let hasWrittenComponent = false ;
@@ -1961,20 +1992,36 @@ namespace ts {
19611992 }
19621993
19631994 if ( usage !== "exclude" ) {
1995+ let componentPattern = "" ;
19641996 // The * and ? wildcards should not match directories or files that start with . if they
19651997 // appear first in a component. Dotted directories and files can be included explicitly
19661998 // like so: **/.*/.*
19671999 if ( component . charCodeAt ( 0 ) === CharacterCodes . asterisk ) {
1968- subpattern += "([^./]" + singleAsteriskRegexFragment + ")?" ;
2000+ componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?" ;
19692001 component = component . substr ( 1 ) ;
19702002 }
19712003 else if ( component . charCodeAt ( 0 ) === CharacterCodes . question ) {
1972- subpattern += "[^./]" ;
2004+ componentPattern += "[^./]" ;
19732005 component = component . substr ( 1 ) ;
19742006 }
1975- }
19762007
1977- subpattern += component . replace ( reservedCharacterPattern , replaceWildcardCharacter ) ;
2008+ componentPattern += component . replace ( reservedCharacterPattern , replaceWildcardCharacter ) ;
2009+
2010+ // Patterns should not include subfolders like node_modules unless they are
2011+ // explicitly included as part of the path.
2012+ //
2013+ // As an optimization, if the component pattern is the same as the component,
2014+ // then there definitely were no wildcard characters and we do not need to
2015+ // add the exclusion pattern.
2016+ if ( componentPattern !== component ) {
2017+ subpattern += implicitExcludePathRegexPattern ;
2018+ }
2019+
2020+ subpattern += componentPattern ;
2021+ }
2022+ else {
2023+ subpattern += component . replace ( reservedCharacterPattern , replaceWildcardCharacter ) ;
2024+ }
19782025 }
19792026
19802027 hasWrittenComponent = true ;
@@ -1988,14 +2035,6 @@ namespace ts {
19882035 return subpattern ;
19892036 }
19902037
1991- function replaceWildCardCharacterFiles ( match : string ) {
1992- return replaceWildcardCharacter ( match , singleAsteriskRegexFragmentFiles ) ;
1993- }
1994-
1995- function replaceWildCardCharacterOther ( match : string ) {
1996- return replaceWildcardCharacter ( match , singleAsteriskRegexFragmentOther ) ;
1997- }
1998-
19992038 function replaceWildcardCharacter ( match : string , singleAsteriskRegexFragment : string ) {
20002039 return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match ;
20012040 }
0 commit comments