@@ -3034,10 +3034,6 @@ namespace ts {
3034
3034
return filter ( map ( values , v => convertJsonOption ( option . element , v , basePath , errors ) ) , v => ! ! v ) ;
3035
3035
}
3036
3036
3037
- function trimString ( s : string ) {
3038
- return typeof s . trim === "function" ? s . trim ( ) : s . replace ( / ^ [ \s ] + | [ \s ] + $ / g, "" ) ;
3039
- }
3040
-
3041
3037
/**
3042
3038
* Tests for a path that ends in a recursive directory wildcard.
3043
3039
* Matches **, \**, **\, and \**\, but not a**b.
@@ -3051,36 +3047,6 @@ namespace ts {
3051
3047
*/
3052
3048
const invalidTrailingRecursionPattern = / ( ^ | \/ ) \* \* \/ ? $ / ;
3053
3049
3054
- /**
3055
- * Tests for a path where .. appears after a recursive directory wildcard.
3056
- * Matches **\..\*, **\a\..\*, and **\.., but not ..\**\*
3057
- *
3058
- * NOTE: used \ in place of / above to avoid issues with multiline comments.
3059
- *
3060
- * Breakdown:
3061
- * (^|\/) # matches either the beginning of the string or a directory separator.
3062
- * \*\*\/ # matches a recursive directory wildcard "**" followed by a directory separator.
3063
- * (.*\/)? # optionally matches any number of characters followed by a directory separator.
3064
- * \.\. # matches a parent directory path component ".."
3065
- * ($|\/) # matches either the end of the string or a directory separator.
3066
- */
3067
- const invalidDotDotAfterRecursiveWildcardPattern = / ( ^ | \/ ) \* \* \/ ( .* \/ ) ? \. \. ( $ | \/ ) / ;
3068
-
3069
- /**
3070
- * Tests for a path containing a wildcard character in a directory component of the path.
3071
- * Matches \*\, \?\, and \a*b\, but not \a\ or \a\*.
3072
- *
3073
- * NOTE: used \ in place of / above to avoid issues with multiline comments.
3074
- *
3075
- * Breakdown:
3076
- * \/ # matches a directory separator.
3077
- * [^/]*? # matches any number of characters excluding directory separators (non-greedy).
3078
- * [*?] # matches either a wildcard character (* or ?)
3079
- * [^/]* # matches any number of characters excluding directory separators (greedy).
3080
- * \/ # matches a directory separator.
3081
- */
3082
- const watchRecursivePattern = / \/ [ ^ / ] * ?[ * ? ] [ ^ / ] * \/ / ;
3083
-
3084
3050
/**
3085
3051
* Matches the portion of a wildcard path that does not contain wildcards.
3086
3052
* Matches \a of \a\*, or \a\b\c of \a\b\c\?\d.
@@ -3217,6 +3183,20 @@ namespace ts {
3217
3183
return matchesExcludeWorker ( pathToCheck , validatedExcludeSpecs , useCaseSensitiveFileNames , currentDirectory , basePath ) ;
3218
3184
}
3219
3185
3186
+ function invalidDotDotAfterRecursiveWildcard ( s : string ) {
3187
+ // We used to use the regex /(^|\/)\*\*\/(.*\/)?\.\.($|\/)/ to check for this case, but
3188
+ // in v8, that has polynomial performance because the recursive wildcard match - **/ -
3189
+ // can be matched in many arbitrary positions when multiple are present, resulting
3190
+ // in bad backtracking (and we don't care which is matched - just that some /.. segment
3191
+ // comes after some **/ segment).
3192
+ const wildcardIndex = startsWith ( s , "**/" ) ? 0 : s . indexOf ( "/**/" ) ;
3193
+ if ( wildcardIndex === - 1 ) {
3194
+ return false ;
3195
+ }
3196
+ const lastDotIndex = endsWith ( s , "/.." ) ? s . length : s . lastIndexOf ( "/../" ) ;
3197
+ return lastDotIndex > wildcardIndex ;
3198
+ }
3199
+
3220
3200
/* @internal */
3221
3201
export function matchesExclude (
3222
3202
pathToCheck : string ,
@@ -3226,7 +3206,7 @@ namespace ts {
3226
3206
) {
3227
3207
return matchesExcludeWorker (
3228
3208
pathToCheck ,
3229
- filter ( excludeSpecs , spec => ! invalidDotDotAfterRecursiveWildcardPattern . test ( spec ) ) ,
3209
+ filter ( excludeSpecs , spec => ! invalidDotDotAfterRecursiveWildcard ( spec ) ) ,
3230
3210
useCaseSensitiveFileNames ,
3231
3211
currentDirectory
3232
3212
) ;
@@ -3268,7 +3248,7 @@ namespace ts {
3268
3248
if ( disallowTrailingRecursion && invalidTrailingRecursionPattern . test ( spec ) ) {
3269
3249
return [ Diagnostics . File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 , spec ] ;
3270
3250
}
3271
- else if ( invalidDotDotAfterRecursiveWildcardPattern . test ( spec ) ) {
3251
+ else if ( invalidDotDotAfterRecursiveWildcard ( spec ) ) {
3272
3252
return [ Diagnostics . File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 , spec ] ;
3273
3253
}
3274
3254
}
@@ -3331,9 +3311,18 @@ namespace ts {
3331
3311
function getWildcardDirectoryFromSpec ( spec : string , useCaseSensitiveFileNames : boolean ) : { key : string , flags : WatchDirectoryFlags } | undefined {
3332
3312
const match = wildcardDirectoryPattern . exec ( spec ) ;
3333
3313
if ( match ) {
3314
+ // We check this with a few `indexOf` calls because 3 `indexOf`/`lastIndexOf` calls is
3315
+ // less algorithmically complex (roughly O(3n) worst-case) than the regex we used to use,
3316
+ // \/[^/]*?[*?][^/]*\/ which was polynominal in v8, since arbitrary sequences of wildcard
3317
+ // characters could match any of the central patterns, resulting in bad backtracking.
3318
+ const questionWildcardIndex = spec . indexOf ( "?" ) ;
3319
+ const starWildcardIndex = spec . indexOf ( "*" ) ;
3320
+ const lastDirectorySeperatorIndex = spec . lastIndexOf ( directorySeparator ) ;
3334
3321
return {
3335
3322
key : useCaseSensitiveFileNames ? match [ 0 ] : toFileNameLowerCase ( match [ 0 ] ) ,
3336
- flags : watchRecursivePattern . test ( spec ) ? WatchDirectoryFlags . Recursive : WatchDirectoryFlags . None
3323
+ flags : ( questionWildcardIndex !== - 1 && questionWildcardIndex < lastDirectorySeperatorIndex )
3324
+ || ( starWildcardIndex !== - 1 && starWildcardIndex < lastDirectorySeperatorIndex )
3325
+ ? WatchDirectoryFlags . Recursive : WatchDirectoryFlags . None
3337
3326
} ;
3338
3327
}
3339
3328
if ( isImplicitGlob ( spec ) ) {
0 commit comments