@@ -1057,7 +1057,7 @@ namespace ts {
10571057 errors . push ( createDiagnosticForNodeInSourceFile ( sourceFile , element . name , extraKeyDiagnosticMessage , keyText ) ) ;
10581058 }
10591059 const value = convertPropertyValueToJson ( element . initializer , option ) ;
1060- if ( typeof keyText !== "undefined" && typeof value !== "undefined" ) {
1060+ if ( typeof keyText !== "undefined" ) {
10611061 result [ keyText ] = value ;
10621062 // Notify key value set, if user asked for it
10631063 if ( jsonConversionNotifier &&
@@ -1104,7 +1104,7 @@ namespace ts {
11041104 return false ;
11051105
11061106 case SyntaxKind . NullKeyword :
1107- reportInvalidOptionValue ( ! ! option ) ;
1107+ reportInvalidOptionValue ( option && option . name === "extends" ) ; // "extends" is the only option we don't allow null/undefined for
11081108 return null ; // tslint:disable-line:no-null-keyword
11091109
11101110 case SyntaxKind . StringLiteral :
@@ -1189,6 +1189,7 @@ namespace ts {
11891189
11901190 function isCompilerOptionsValue ( option : CommandLineOption , value : any ) : value is CompilerOptionsValue {
11911191 if ( option ) {
1192+ if ( isNullOrUndefined ( value ) ) return true ; // All options are undefinable/nullable
11921193 if ( option . type === "list" ) {
11931194 return isArray ( value ) ;
11941195 }
@@ -1379,6 +1380,17 @@ namespace ts {
13791380 }
13801381 }
13811382
1383+ function isNullOrUndefined ( x : any ) : x is null | undefined {
1384+ // tslint:disable-next-line:no-null-keyword
1385+ return x === undefined || x === null ;
1386+ }
1387+
1388+ function directoryOfCombinedPath ( fileName : string , basePath : string ) {
1389+ // Use the `identity` function to avoid canonicalizing the path, as it must remain noncanonical
1390+ // until consistient casing errors are reported
1391+ return getDirectoryPath ( toPath ( fileName , basePath , identity ) ) ;
1392+ }
1393+
13821394 /**
13831395 * Parse the contents of a config file from json or json source file (tsconfig.json).
13841396 * @param json The contents of the config file to parse
@@ -1419,7 +1431,7 @@ namespace ts {
14191431
14201432 function getFileNames ( ) : ExpandResult {
14211433 let fileNames : ReadonlyArray < string > ;
1422- if ( hasProperty ( raw , "files" ) ) {
1434+ if ( hasProperty ( raw , "files" ) && ! isNullOrUndefined ( raw [ "files" ] ) ) {
14231435 if ( isArray ( raw [ "files" ] ) ) {
14241436 fileNames = < ReadonlyArray < string > > raw [ "files" ] ;
14251437 if ( fileNames . length === 0 ) {
@@ -1432,7 +1444,7 @@ namespace ts {
14321444 }
14331445
14341446 let includeSpecs : ReadonlyArray < string > ;
1435- if ( hasProperty ( raw , "include" ) ) {
1447+ if ( hasProperty ( raw , "include" ) && ! isNullOrUndefined ( raw [ "include" ] ) ) {
14361448 if ( isArray ( raw [ "include" ] ) ) {
14371449 includeSpecs = < ReadonlyArray < string > > raw [ "include" ] ;
14381450 }
@@ -1442,7 +1454,7 @@ namespace ts {
14421454 }
14431455
14441456 let excludeSpecs : ReadonlyArray < string > ;
1445- if ( hasProperty ( raw , "exclude" ) ) {
1457+ if ( hasProperty ( raw , "exclude" ) && ! isNullOrUndefined ( raw [ "exclude" ] ) ) {
14461458 if ( isArray ( raw [ "exclude" ] ) ) {
14471459 excludeSpecs = < ReadonlyArray < string > > raw [ "exclude" ] ;
14481460 }
@@ -1461,7 +1473,7 @@ namespace ts {
14611473 includeSpecs = [ "**/*" ] ;
14621474 }
14631475
1464- const result = matchFileNames ( fileNames , includeSpecs , excludeSpecs , basePath , options , host , errors , extraFileExtensions , sourceFile ) ;
1476+ const result = matchFileNames ( fileNames , includeSpecs , excludeSpecs , configFileName ? directoryOfCombinedPath ( configFileName , basePath ) : basePath , options , host , errors , extraFileExtensions , sourceFile ) ;
14651477
14661478 if ( result . fileNames . length === 0 && ! hasProperty ( raw , "files" ) && resolutionStack . length === 0 ) {
14671479 errors . push (
@@ -1552,7 +1564,7 @@ namespace ts {
15521564 host : ParseConfigHost ,
15531565 basePath : string ,
15541566 getCanonicalFileName : ( fileName : string ) => string ,
1555- configFileName : string ,
1567+ configFileName : string | undefined ,
15561568 errors : Push < Diagnostic >
15571569 ) : ParsedTsconfig {
15581570 if ( hasProperty ( json , "excludes" ) ) {
@@ -1571,7 +1583,8 @@ namespace ts {
15711583 errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "extends" , "string" ) ) ;
15721584 }
15731585 else {
1574- extendedConfigPath = getExtendsConfigPath ( json . extends , host , basePath , getCanonicalFileName , errors , createCompilerDiagnostic ) ;
1586+ const newBase = configFileName ? directoryOfCombinedPath ( configFileName , basePath ) : basePath ;
1587+ extendedConfigPath = getExtendsConfigPath ( json . extends , host , newBase , getCanonicalFileName , errors , createCompilerDiagnostic ) ;
15751588 }
15761589 }
15771590 return { raw : json , options, typeAcquisition, extendedConfigPath } ;
@@ -1582,7 +1595,7 @@ namespace ts {
15821595 host : ParseConfigHost ,
15831596 basePath : string ,
15841597 getCanonicalFileName : ( fileName : string ) => string ,
1585- configFileName : string ,
1598+ configFileName : string | undefined ,
15861599 errors : Push < Diagnostic >
15871600 ) : ParsedTsconfig {
15881601 const options = getDefaultCompilerOptions ( configFileName ) ;
@@ -1603,10 +1616,11 @@ namespace ts {
16031616 onSetValidOptionKeyValueInRoot ( key : string , _keyNode : PropertyName , value : CompilerOptionsValue , valueNode : Expression ) {
16041617 switch ( key ) {
16051618 case "extends" :
1619+ const newBase = configFileName ? directoryOfCombinedPath ( configFileName , basePath ) : basePath ;
16061620 extendedConfigPath = getExtendsConfigPath (
16071621 < string > value ,
16081622 host ,
1609- basePath ,
1623+ newBase ,
16101624 getCanonicalFileName ,
16111625 errors ,
16121626 ( message , arg0 ) =>
@@ -1803,6 +1817,7 @@ namespace ts {
18031817 }
18041818
18051819 function normalizeOptionValue ( option : CommandLineOption , basePath : string , value : any ) : CompilerOptionsValue {
1820+ if ( isNullOrUndefined ( value ) ) return undefined ;
18061821 if ( option . type === "list" ) {
18071822 const listOption = < CommandLineOptionOfListType > option ;
18081823 if ( listOption . element . isFilePath || typeof listOption . element . type !== "string" ) {
@@ -1827,6 +1842,7 @@ namespace ts {
18271842 }
18281843
18291844 function convertJsonOptionOfCustomType ( opt : CommandLineOptionOfCustomType , value : string , errors : Push < Diagnostic > ) {
1845+ if ( isNullOrUndefined ( value ) ) return undefined ;
18301846 const key = value . toLowerCase ( ) ;
18311847 const val = opt . type . get ( key ) ;
18321848 if ( val !== undefined ) {
@@ -1977,7 +1993,7 @@ namespace ts {
19771993 // remove a literal file.
19781994 if ( fileNames ) {
19791995 for ( const fileName of fileNames ) {
1980- const file = combinePaths ( basePath , fileName ) ;
1996+ const file = getNormalizedAbsolutePath ( fileName , basePath ) ;
19811997 literalFileMap . set ( keyMapper ( file ) , file ) ;
19821998 }
19831999 }
0 commit comments