@@ -119,6 +119,30 @@ namespace ts {
119
119
category : Diagnostics . Advanced_Options ,
120
120
description : Diagnostics . Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively ,
121
121
} ,
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
+ } ,
122
146
] ;
123
147
124
148
/* @internal */
@@ -1165,9 +1189,9 @@ namespace ts {
1165
1189
const values = value . split ( "," ) ;
1166
1190
switch ( opt . element . type ) {
1167
1191
case "number" :
1168
- return map ( values , parseInt ) ;
1192
+ return mapDefined ( values , v => validateJsonOptionValue ( opt . element , parseInt ( v ) , errors ) ) ;
1169
1193
case "string" :
1170
- return map ( values , v => v || "" ) ;
1194
+ return mapDefined ( values , v => validateJsonOptionValue ( opt . element , v || "" , errors ) ) ;
1171
1195
default :
1172
1196
return mapDefined ( values , v => parseCustomTypeOption ( < CommandLineOptionOfCustomType > opt . element , v , errors ) ) ;
1173
1197
}
@@ -1297,7 +1321,7 @@ namespace ts {
1297
1321
}
1298
1322
else if ( opt . type === "boolean" ) {
1299
1323
if ( optValue === "false" ) {
1300
- options [ opt . name ] = false ;
1324
+ options [ opt . name ] = validateJsonOptionValue ( opt , /*value*/ false , errors ) ;
1301
1325
i ++ ;
1302
1326
}
1303
1327
else {
@@ -1319,20 +1343,20 @@ namespace ts {
1319
1343
if ( args [ i ] !== "null" ) {
1320
1344
switch ( opt . type ) {
1321
1345
case "number" :
1322
- options [ opt . name ] = parseInt ( args [ i ] ) ;
1346
+ options [ opt . name ] = validateJsonOptionValue ( opt , parseInt ( args [ i ] ) , errors ) ;
1323
1347
i ++ ;
1324
1348
break ;
1325
1349
case "boolean" :
1326
1350
// boolean flag has optional value true, false, others
1327
1351
const optValue = args [ i ] ;
1328
- options [ opt . name ] = optValue !== "false" ;
1352
+ options [ opt . name ] = validateJsonOptionValue ( opt , optValue !== "false" , errors ) ;
1329
1353
// consume next argument as boolean flag value
1330
1354
if ( optValue === "false" || optValue === "true" ) {
1331
1355
i ++ ;
1332
1356
}
1333
1357
break ;
1334
1358
case "string" :
1335
- options [ opt . name ] = args [ i ] || "" ;
1359
+ options [ opt . name ] = validateJsonOptionValue ( opt , args [ i ] || "" , errors ) ;
1336
1360
i ++ ;
1337
1361
break ;
1338
1362
case "list" :
@@ -1777,28 +1801,30 @@ namespace ts {
1777
1801
function convertArrayLiteralExpressionToJson (
1778
1802
elements : NodeArray < Expression > ,
1779
1803
elementOption : CommandLineOption | undefined
1780
- ) : any [ ] | void {
1804
+ ) {
1781
1805
if ( ! returnValue ) {
1782
- return elements . forEach ( element => convertPropertyValueToJson ( element , elementOption ) ) ;
1806
+ elements . forEach ( element => convertPropertyValueToJson ( element , elementOption ) ) ;
1807
+ return undefined ;
1783
1808
}
1784
1809
1785
1810
// Filter out invalid values
1786
1811
return filter ( elements . map ( element => convertPropertyValueToJson ( element , elementOption ) ) , v => v !== undefined ) ;
1787
1812
}
1788
1813
1789
1814
function convertPropertyValueToJson ( valueExpression : Expression , option : CommandLineOption | undefined ) : any {
1815
+ let invalidReported : boolean | undefined ;
1790
1816
switch ( valueExpression . kind ) {
1791
1817
case SyntaxKind . TrueKeyword :
1792
1818
reportInvalidOptionValue ( option && option . type !== "boolean" ) ;
1793
- return true ;
1819
+ return validateValueOk ( /*value*/ true ) ;
1794
1820
1795
1821
case SyntaxKind . FalseKeyword :
1796
1822
reportInvalidOptionValue ( option && option . type !== "boolean" ) ;
1797
- return false ;
1823
+ return validateValueOk ( /*value*/ false ) ;
1798
1824
1799
1825
case SyntaxKind . NullKeyword :
1800
1826
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
1802
1828
1803
1829
case SyntaxKind . StringLiteral :
1804
1830
if ( ! isDoubleQuotedString ( valueExpression ) ) {
@@ -1816,20 +1842,21 @@ namespace ts {
1816
1842
( message , arg0 , arg1 ) => createDiagnosticForNodeInSourceFile ( sourceFile , valueExpression , message , arg0 , arg1 )
1817
1843
)
1818
1844
) ;
1845
+ invalidReported = true ;
1819
1846
}
1820
1847
}
1821
- return text ;
1848
+ return validateValueOk ( text ) ;
1822
1849
1823
1850
case SyntaxKind . NumericLiteral :
1824
1851
reportInvalidOptionValue ( option && option . type !== "number" ) ;
1825
- return Number ( ( < NumericLiteral > valueExpression ) . text ) ;
1852
+ return validateValueOk ( Number ( ( < NumericLiteral > valueExpression ) . text ) ) ;
1826
1853
1827
1854
case SyntaxKind . PrefixUnaryExpression :
1828
1855
if ( ( < PrefixUnaryExpression > valueExpression ) . operator !== SyntaxKind . MinusToken || ( < PrefixUnaryExpression > valueExpression ) . operand . kind !== SyntaxKind . NumericLiteral ) {
1829
1856
break ; // not valid JSON syntax
1830
1857
}
1831
1858
reportInvalidOptionValue ( option && option . type !== "number" ) ;
1832
- return - Number ( ( < NumericLiteral > ( < PrefixUnaryExpression > valueExpression ) . operand ) . text ) ;
1859
+ return validateValueOk ( - Number ( ( < NumericLiteral > ( < PrefixUnaryExpression > valueExpression ) . operand ) . text ) ) ;
1833
1860
1834
1861
case SyntaxKind . ObjectLiteralExpression :
1835
1862
reportInvalidOptionValue ( option && option . type !== "object" ) ;
@@ -1843,20 +1870,20 @@ namespace ts {
1843
1870
// If need arises, we can modify this interface and callbacks as needed
1844
1871
if ( option ) {
1845
1872
const { elementOptions, extraKeyDiagnostics, name : optionName } = < TsConfigOnlyOption > option ;
1846
- return convertObjectLiteralExpressionToJson ( objectLiteralExpression ,
1847
- elementOptions , extraKeyDiagnostics , optionName ) ;
1873
+ return validateValueOk ( convertObjectLiteralExpressionToJson ( objectLiteralExpression ,
1874
+ elementOptions , extraKeyDiagnostics , optionName ) ) ;
1848
1875
}
1849
1876
else {
1850
- return convertObjectLiteralExpressionToJson (
1877
+ return validateValueOk ( convertObjectLiteralExpressionToJson (
1851
1878
objectLiteralExpression , /* knownOptions*/ undefined ,
1852
- /*extraKeyDiagnosticMessage */ undefined , /*parentOption*/ undefined ) ;
1879
+ /*extraKeyDiagnosticMessage */ undefined , /*parentOption*/ undefined ) ) ;
1853
1880
}
1854
1881
1855
1882
case SyntaxKind . ArrayLiteralExpression :
1856
1883
reportInvalidOptionValue ( option && option . type !== "list" ) ;
1857
- return convertArrayLiteralExpressionToJson (
1884
+ return validateValueOk ( convertArrayLiteralExpressionToJson (
1858
1885
( < ArrayLiteralExpression > valueExpression ) . elements ,
1859
- option && ( < CommandLineOptionOfListType > option ) . element ) ;
1886
+ option && ( < CommandLineOptionOfListType > option ) . element ) ) ;
1860
1887
}
1861
1888
1862
1889
// Not in expected format
@@ -1869,9 +1896,21 @@ namespace ts {
1869
1896
1870
1897
return undefined ;
1871
1898
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
+
1872
1910
function reportInvalidOptionValue ( isError : boolean | undefined ) {
1873
1911
if ( isError ) {
1874
1912
errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , valueExpression , Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , option ! . name , getCompilerOptionValueTypeString ( option ! ) ) ) ;
1913
+ invalidReported = true ;
1875
1914
}
1876
1915
}
1877
1916
}
@@ -2782,7 +2821,8 @@ namespace ts {
2782
2821
else if ( ! isString ( optType ) ) {
2783
2822
return convertJsonOptionOfCustomType ( < CommandLineOptionOfCustomType > opt , < string > value , errors ) ;
2784
2823
}
2785
- return normalizeNonListOptionValue ( opt , basePath , value ) ;
2824
+ const validatedValue = validateJsonOptionValue ( opt , value , errors ) ;
2825
+ return isNullOrUndefined ( validatedValue ) ? validatedValue : normalizeNonListOptionValue ( opt , basePath , validatedValue ) ;
2786
2826
}
2787
2827
else {
2788
2828
errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , opt . name , getCompilerOptionValueTypeString ( opt ) ) ) ;
@@ -2814,12 +2854,20 @@ namespace ts {
2814
2854
return value ;
2815
2855
}
2816
2856
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
+
2817
2865
function convertJsonOptionOfCustomType ( opt : CommandLineOptionOfCustomType , value : string , errors : Push < Diagnostic > ) {
2818
2866
if ( isNullOrUndefined ( value ) ) return undefined ;
2819
2867
const key = value . toLowerCase ( ) ;
2820
2868
const val = opt . type . get ( key ) ;
2821
2869
if ( val !== undefined ) {
2822
- return val ;
2870
+ return validateJsonOptionValue ( opt , val , errors ) ;
2823
2871
}
2824
2872
else {
2825
2873
errors . push ( createCompilerDiagnosticForInvalidCustomType ( opt ) ) ;
@@ -2921,11 +2969,11 @@ namespace ts {
2921
2969
// file system.
2922
2970
2923
2971
if ( includeSpecs ) {
2924
- validatedIncludeSpecs = validateSpecs ( includeSpecs , errors , /*allowTrailingRecursion */ false , jsonSourceFile , "include" ) ;
2972
+ validatedIncludeSpecs = validateSpecs ( includeSpecs , errors , /*disallowTrailingRecursion */ true , jsonSourceFile , "include" ) ;
2925
2973
}
2926
2974
2927
2975
if ( excludeSpecs ) {
2928
- validatedExcludeSpecs = validateSpecs ( excludeSpecs , errors , /*allowTrailingRecursion */ true , jsonSourceFile , "exclude" ) ;
2976
+ validatedExcludeSpecs = validateSpecs ( excludeSpecs , errors , /*disallowTrailingRecursion */ false , jsonSourceFile , "exclude" ) ;
2929
2977
}
2930
2978
2931
2979
// Wildcard directories (provided as part of a wildcard path) are stored in a
@@ -3068,11 +3116,11 @@ namespace ts {
3068
3116
return ! hasExtension ( pathToCheck ) && excludeRegex . test ( ensureTrailingDirectorySeparator ( pathToCheck ) ) ;
3069
3117
}
3070
3118
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 [ ] {
3072
3120
return specs . filter ( spec => {
3073
- const diag = specToDiagnostic ( spec , allowTrailingRecursion ) ;
3121
+ const diag = specToDiagnostic ( spec , disallowTrailingRecursion ) ;
3074
3122
if ( diag !== undefined ) {
3075
- errors . push ( createDiagnostic ( diag , spec ) ) ;
3123
+ errors . push ( createDiagnostic ( ... diag ) ) ;
3076
3124
}
3077
3125
return diag === undefined ;
3078
3126
} ) ;
@@ -3085,12 +3133,12 @@ namespace ts {
3085
3133
}
3086
3134
}
3087
3135
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 ] ;
3091
3139
}
3092
3140
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 ] ;
3094
3142
}
3095
3143
}
3096
3144
0 commit comments