@@ -119,6 +119,30 @@ namespace ts {
119119 category : Diagnostics . Advanced_Options ,
120120 description : Diagnostics . Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively ,
121121 } ,
122+ {
123+ name : "excludeDirectories" ,
124+ type : "list" ,
125+ element : {
126+ name : "excludeDirectories" ,
127+ type : "string" ,
128+ isFilePath : true ,
129+ extraValidation : specToDiagnostic
130+ } ,
131+ category : Diagnostics . Advanced_Options ,
132+ description : Diagnostics . Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively ,
133+ } ,
134+ {
135+ name : "excludeFiles" ,
136+ type : "list" ,
137+ element : {
138+ name : "excludeFiles" ,
139+ type : "string" ,
140+ isFilePath : true ,
141+ extraValidation : specToDiagnostic
142+ } ,
143+ category : Diagnostics . Advanced_Options ,
144+ description : Diagnostics . Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively ,
145+ } ,
122146 ] ;
123147
124148 /* @internal */
@@ -1165,9 +1189,9 @@ namespace ts {
11651189 const values = value . split ( "," ) ;
11661190 switch ( opt . element . type ) {
11671191 case "number" :
1168- return map ( values , parseInt ) ;
1192+ return mapDefined ( values , v => validateJsonOptionValue ( opt . element , parseInt ( v ) , errors ) ) ;
11691193 case "string" :
1170- return map ( values , v => v || "" ) ;
1194+ return mapDefined ( values , v => validateJsonOptionValue ( opt . element , v || "" , errors ) ) ;
11711195 default :
11721196 return mapDefined ( values , v => parseCustomTypeOption ( < CommandLineOptionOfCustomType > opt . element , v , errors ) ) ;
11731197 }
@@ -1297,7 +1321,7 @@ namespace ts {
12971321 }
12981322 else if ( opt . type === "boolean" ) {
12991323 if ( optValue === "false" ) {
1300- options [ opt . name ] = false ;
1324+ options [ opt . name ] = validateJsonOptionValue ( opt , /*value*/ false , errors ) ;
13011325 i ++ ;
13021326 }
13031327 else {
@@ -1319,20 +1343,20 @@ namespace ts {
13191343 if ( args [ i ] !== "null" ) {
13201344 switch ( opt . type ) {
13211345 case "number" :
1322- options [ opt . name ] = parseInt ( args [ i ] ) ;
1346+ options [ opt . name ] = validateJsonOptionValue ( opt , parseInt ( args [ i ] ) , errors ) ;
13231347 i ++ ;
13241348 break ;
13251349 case "boolean" :
13261350 // boolean flag has optional value true, false, others
13271351 const optValue = args [ i ] ;
1328- options [ opt . name ] = optValue !== "false" ;
1352+ options [ opt . name ] = validateJsonOptionValue ( opt , optValue !== "false" , errors ) ;
13291353 // consume next argument as boolean flag value
13301354 if ( optValue === "false" || optValue === "true" ) {
13311355 i ++ ;
13321356 }
13331357 break ;
13341358 case "string" :
1335- options [ opt . name ] = args [ i ] || "" ;
1359+ options [ opt . name ] = validateJsonOptionValue ( opt , args [ i ] || "" , errors ) ;
13361360 i ++ ;
13371361 break ;
13381362 case "list" :
@@ -1777,28 +1801,30 @@ namespace ts {
17771801 function convertArrayLiteralExpressionToJson (
17781802 elements : NodeArray < Expression > ,
17791803 elementOption : CommandLineOption | undefined
1780- ) : any [ ] | void {
1804+ ) {
17811805 if ( ! returnValue ) {
1782- return elements . forEach ( element => convertPropertyValueToJson ( element , elementOption ) ) ;
1806+ elements . forEach ( element => convertPropertyValueToJson ( element , elementOption ) ) ;
1807+ return undefined ;
17831808 }
17841809
17851810 // Filter out invalid values
17861811 return filter ( elements . map ( element => convertPropertyValueToJson ( element , elementOption ) ) , v => v !== undefined ) ;
17871812 }
17881813
17891814 function convertPropertyValueToJson ( valueExpression : Expression , option : CommandLineOption | undefined ) : any {
1815+ let invalidReported : boolean | undefined ;
17901816 switch ( valueExpression . kind ) {
17911817 case SyntaxKind . TrueKeyword :
17921818 reportInvalidOptionValue ( option && option . type !== "boolean" ) ;
1793- return true ;
1819+ return validateValueOk ( /*value*/ true ) ;
17941820
17951821 case SyntaxKind . FalseKeyword :
17961822 reportInvalidOptionValue ( option && option . type !== "boolean" ) ;
1797- return false ;
1823+ return validateValueOk ( /*value*/ false ) ;
17981824
17991825 case SyntaxKind . NullKeyword :
18001826 reportInvalidOptionValue ( option && option . name === "extends" ) ; // "extends" is the only option we don't allow null/undefined for
1801- return null ; // eslint-disable-line no-null/no-null
1827+ return validateValueOk ( /*value*/ null ) ; // eslint-disable-line no-null/no-null
18021828
18031829 case SyntaxKind . StringLiteral :
18041830 if ( ! isDoubleQuotedString ( valueExpression ) ) {
@@ -1816,20 +1842,21 @@ namespace ts {
18161842 ( message , arg0 , arg1 ) => createDiagnosticForNodeInSourceFile ( sourceFile , valueExpression , message , arg0 , arg1 )
18171843 )
18181844 ) ;
1845+ invalidReported = true ;
18191846 }
18201847 }
1821- return text ;
1848+ return validateValueOk ( text ) ;
18221849
18231850 case SyntaxKind . NumericLiteral :
18241851 reportInvalidOptionValue ( option && option . type !== "number" ) ;
1825- return Number ( ( < NumericLiteral > valueExpression ) . text ) ;
1852+ return validateValueOk ( Number ( ( < NumericLiteral > valueExpression ) . text ) ) ;
18261853
18271854 case SyntaxKind . PrefixUnaryExpression :
18281855 if ( ( < PrefixUnaryExpression > valueExpression ) . operator !== SyntaxKind . MinusToken || ( < PrefixUnaryExpression > valueExpression ) . operand . kind !== SyntaxKind . NumericLiteral ) {
18291856 break ; // not valid JSON syntax
18301857 }
18311858 reportInvalidOptionValue ( option && option . type !== "number" ) ;
1832- return - Number ( ( < NumericLiteral > ( < PrefixUnaryExpression > valueExpression ) . operand ) . text ) ;
1859+ return validateValueOk ( - Number ( ( < NumericLiteral > ( < PrefixUnaryExpression > valueExpression ) . operand ) . text ) ) ;
18331860
18341861 case SyntaxKind . ObjectLiteralExpression :
18351862 reportInvalidOptionValue ( option && option . type !== "object" ) ;
@@ -1843,20 +1870,20 @@ namespace ts {
18431870 // If need arises, we can modify this interface and callbacks as needed
18441871 if ( option ) {
18451872 const { elementOptions, extraKeyDiagnostics, name : optionName } = < TsConfigOnlyOption > option ;
1846- return convertObjectLiteralExpressionToJson ( objectLiteralExpression ,
1847- elementOptions , extraKeyDiagnostics , optionName ) ;
1873+ return validateValueOk ( convertObjectLiteralExpressionToJson ( objectLiteralExpression ,
1874+ elementOptions , extraKeyDiagnostics , optionName ) ) ;
18481875 }
18491876 else {
1850- return convertObjectLiteralExpressionToJson (
1877+ return validateValueOk ( convertObjectLiteralExpressionToJson (
18511878 objectLiteralExpression , /* knownOptions*/ undefined ,
1852- /*extraKeyDiagnosticMessage */ undefined , /*parentOption*/ undefined ) ;
1879+ /*extraKeyDiagnosticMessage */ undefined , /*parentOption*/ undefined ) ) ;
18531880 }
18541881
18551882 case SyntaxKind . ArrayLiteralExpression :
18561883 reportInvalidOptionValue ( option && option . type !== "list" ) ;
1857- return convertArrayLiteralExpressionToJson (
1884+ return validateValueOk ( convertArrayLiteralExpressionToJson (
18581885 ( < ArrayLiteralExpression > valueExpression ) . elements ,
1859- option && ( < CommandLineOptionOfListType > option ) . element ) ;
1886+ option && ( < CommandLineOptionOfListType > option ) . element ) ) ;
18601887 }
18611888
18621889 // Not in expected format
@@ -1869,9 +1896,21 @@ namespace ts {
18691896
18701897 return undefined ;
18711898
1899+ function validateValueOk ( value : CompilerOptionsValue ) {
1900+ if ( ! invalidReported ) {
1901+ const diagnostic = option ?. extraValidation ?.( value ) ;
1902+ if ( diagnostic ) {
1903+ errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , valueExpression , ...diagnostic ) ) ;
1904+ return undefined ;
1905+ }
1906+ }
1907+ return value ;
1908+ }
1909+
18721910 function reportInvalidOptionValue ( isError : boolean | undefined ) {
18731911 if ( isError ) {
18741912 errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , valueExpression , Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , option ! . name , getCompilerOptionValueTypeString ( option ! ) ) ) ;
1913+ invalidReported = true ;
18751914 }
18761915 }
18771916 }
@@ -2782,7 +2821,8 @@ namespace ts {
27822821 else if ( ! isString ( optType ) ) {
27832822 return convertJsonOptionOfCustomType ( < CommandLineOptionOfCustomType > opt , < string > value , errors ) ;
27842823 }
2785- return normalizeNonListOptionValue ( opt , basePath , value ) ;
2824+ const validatedValue = validateJsonOptionValue ( opt , value , errors ) ;
2825+ return isNullOrUndefined ( validatedValue ) ? validatedValue : normalizeNonListOptionValue ( opt , basePath , validatedValue ) ;
27862826 }
27872827 else {
27882828 errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , opt . name , getCompilerOptionValueTypeString ( opt ) ) ) ;
@@ -2814,12 +2854,20 @@ namespace ts {
28142854 return value ;
28152855 }
28162856
2857+ function validateJsonOptionValue < T extends CompilerOptionsValue > ( opt : CommandLineOption , value : T , errors : Push < Diagnostic > ) : T | undefined {
2858+ if ( isNullOrUndefined ( value ) ) return undefined ;
2859+ const d = opt . extraValidation ?.( value ) ;
2860+ if ( ! d ) return value ;
2861+ errors . push ( createCompilerDiagnostic ( ...d ) ) ;
2862+ return undefined ;
2863+ }
2864+
28172865 function convertJsonOptionOfCustomType ( opt : CommandLineOptionOfCustomType , value : string , errors : Push < Diagnostic > ) {
28182866 if ( isNullOrUndefined ( value ) ) return undefined ;
28192867 const key = value . toLowerCase ( ) ;
28202868 const val = opt . type . get ( key ) ;
28212869 if ( val !== undefined ) {
2822- return val ;
2870+ return validateJsonOptionValue ( opt , val , errors ) ;
28232871 }
28242872 else {
28252873 errors . push ( createCompilerDiagnosticForInvalidCustomType ( opt ) ) ;
@@ -2921,11 +2969,11 @@ namespace ts {
29212969 // file system.
29222970
29232971 if ( includeSpecs ) {
2924- validatedIncludeSpecs = validateSpecs ( includeSpecs , errors , /*allowTrailingRecursion */ false , jsonSourceFile , "include" ) ;
2972+ validatedIncludeSpecs = validateSpecs ( includeSpecs , errors , /*disallowTrailingRecursion */ true , jsonSourceFile , "include" ) ;
29252973 }
29262974
29272975 if ( excludeSpecs ) {
2928- validatedExcludeSpecs = validateSpecs ( excludeSpecs , errors , /*allowTrailingRecursion */ true , jsonSourceFile , "exclude" ) ;
2976+ validatedExcludeSpecs = validateSpecs ( excludeSpecs , errors , /*disallowTrailingRecursion */ false , jsonSourceFile , "exclude" ) ;
29292977 }
29302978
29312979 // Wildcard directories (provided as part of a wildcard path) are stored in a
@@ -3068,11 +3116,11 @@ namespace ts {
30683116 return ! hasExtension ( pathToCheck ) && excludeRegex . test ( ensureTrailingDirectorySeparator ( pathToCheck ) ) ;
30693117 }
30703118
3071- function validateSpecs ( specs : readonly string [ ] , errors : Push < Diagnostic > , allowTrailingRecursion : boolean , jsonSourceFile : TsConfigSourceFile | undefined , specKey : string ) : readonly string [ ] {
3119+ function validateSpecs ( specs : readonly string [ ] , errors : Push < Diagnostic > , disallowTrailingRecursion : boolean , jsonSourceFile : TsConfigSourceFile | undefined , specKey : string ) : readonly string [ ] {
30723120 return specs . filter ( spec => {
3073- const diag = specToDiagnostic ( spec , allowTrailingRecursion ) ;
3121+ const diag = specToDiagnostic ( spec , disallowTrailingRecursion ) ;
30743122 if ( diag !== undefined ) {
3075- errors . push ( createDiagnostic ( diag , spec ) ) ;
3123+ errors . push ( createDiagnostic ( ... diag ) ) ;
30763124 }
30773125 return diag === undefined ;
30783126 } ) ;
@@ -3085,12 +3133,12 @@ namespace ts {
30853133 }
30863134 }
30873135
3088- function specToDiagnostic ( spec : string , allowTrailingRecursion : boolean ) : DiagnosticMessage | undefined {
3089- if ( ! allowTrailingRecursion && invalidTrailingRecursionPattern . test ( spec ) ) {
3090- return Diagnostics . File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 ;
3136+ function specToDiagnostic ( spec : string , disallowTrailingRecursion ? : boolean ) : [ DiagnosticMessage , string ] | undefined {
3137+ if ( disallowTrailingRecursion && invalidTrailingRecursionPattern . test ( spec ) ) {
3138+ return [ Diagnostics . File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 , spec ] ;
30913139 }
30923140 else if ( invalidDotDotAfterRecursiveWildcardPattern . test ( spec ) ) {
3093- return Diagnostics . File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 ;
3141+ return [ Diagnostics . File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0 , spec ] ;
30943142 }
30953143 }
30963144
0 commit comments