@@ -1088,58 +1088,38 @@ namespace ts {
10881088 * @param host Instance of ParseConfigHost used to enumerate files in folder.
10891089 * @param basePath A root directory to resolve relative path entries in the config
10901090 * file to. e.g. outDir
1091+ * @param resolutionStack Only present for backwards-compatibility. Should be empty.
10911092 */
1092- export function parseJsonConfigFileContent ( json : any , host : ParseConfigHost , basePath : string , existingOptions : CompilerOptions = { } , configFileName ?: string , resolutionStack : Path [ ] = [ ] , extraFileExtensions : JsFileExtensionInfo [ ] = [ ] ) : ParsedCommandLine {
1093+ export function parseJsonConfigFileContent (
1094+ json : any ,
1095+ host : ParseConfigHost ,
1096+ basePath : string ,
1097+ existingOptions : CompilerOptions = { } ,
1098+ configFileName ?: string ,
1099+ resolutionStack : Path [ ] = [ ] ,
1100+ extraFileExtensions : JsFileExtensionInfo [ ] = [ ] ,
1101+ ) : ParsedCommandLine {
10931102 const errors : Diagnostic [ ] = [ ] ;
1094- basePath = normalizeSlashes ( basePath ) ;
1095- const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
1096- const resolvedPath = toPath ( configFileName || "" , basePath , getCanonicalFileName ) ;
1097- if ( resolutionStack . indexOf ( resolvedPath ) >= 0 ) {
1098- return {
1099- options : { } ,
1100- fileNames : [ ] ,
1101- typeAcquisition : { } ,
1102- raw : json ,
1103- errors : [ createCompilerDiagnostic ( Diagnostics . Circularity_detected_while_resolving_configuration_Colon_0 , [ ...resolutionStack , resolvedPath ] . join ( " -> " ) ) ] ,
1104- wildcardDirectories : { }
1105- } ;
1106- }
11071103
1108- let options : CompilerOptions = convertCompilerOptionsFromJsonWorker ( json [ "compilerOptions" ] , basePath , errors , configFileName ) ;
1104+ let options = ( ( ) => {
1105+ const { include, exclude, files, options, compileOnSave } = parseConfig ( json , host , basePath , configFileName , resolutionStack , errors ) ;
1106+ if ( include ) { json . include = include ; }
1107+ if ( exclude ) { json . exclude = exclude ; }
1108+ if ( files ) { json . files = files ; }
1109+ if ( compileOnSave !== undefined ) { json . compileOnSave = compileOnSave ; }
1110+ return options ;
1111+ } ) ( ) ;
1112+
1113+ options = extend ( existingOptions , options ) ;
1114+ options . configFilePath = configFileName ;
1115+
11091116 // typingOptions has been deprecated and is only supported for backward compatibility purposes.
11101117 // It should be removed in future releases - use typeAcquisition instead.
11111118 const jsonOptions = json [ "typeAcquisition" ] || json [ "typingOptions" ] ;
11121119 const typeAcquisition : TypeAcquisition = convertTypeAcquisitionFromJsonWorker ( jsonOptions , basePath , errors , configFileName ) ;
11131120
1114- let baseCompileOnSave : boolean ;
1115- if ( json [ "extends" ] ) {
1116- let [ include , exclude , files , baseOptions ] : [ string [ ] , string [ ] , string [ ] , CompilerOptions ] = [ undefined , undefined , undefined , { } ] ;
1117- if ( typeof json [ "extends" ] === "string" ) {
1118- [ include , exclude , files , baseCompileOnSave , baseOptions ] = ( tryExtendsName ( json [ "extends" ] ) || [ include , exclude , files , baseCompileOnSave , baseOptions ] ) ;
1119- }
1120- else {
1121- errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "extends" , "string" ) ) ;
1122- }
1123- if ( include && ! json [ "include" ] ) {
1124- json [ "include" ] = include ;
1125- }
1126- if ( exclude && ! json [ "exclude" ] ) {
1127- json [ "exclude" ] = exclude ;
1128- }
1129- if ( files && ! json [ "files" ] ) {
1130- json [ "files" ] = files ;
1131- }
1132- options = assign ( { } , baseOptions , options ) ;
1133- }
1134-
1135- options = extend ( existingOptions , options ) ;
1136- options . configFilePath = configFileName ;
1137-
1138- const { fileNames, wildcardDirectories } = getFileNames ( errors ) ;
1139- let compileOnSave = convertCompileOnSaveOptionFromJson ( json , basePath , errors ) ;
1140- if ( baseCompileOnSave && json [ compileOnSaveCommandLineOption . name ] === undefined ) {
1141- compileOnSave = baseCompileOnSave ;
1142- }
1121+ const { fileNames, wildcardDirectories } = getFileNames ( ) ;
1122+ const compileOnSave = convertCompileOnSaveOptionFromJson ( json , basePath , errors ) ;
11431123
11441124 return {
11451125 options,
@@ -1151,40 +1131,7 @@ namespace ts {
11511131 compileOnSave
11521132 } ;
11531133
1154- function tryExtendsName ( extendedConfig : string ) : [ string [ ] , string [ ] , string [ ] , boolean , CompilerOptions ] {
1155- // If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
1156- if ( ! ( isRootedDiskPath ( extendedConfig ) || startsWith ( normalizeSlashes ( extendedConfig ) , "./" ) || startsWith ( normalizeSlashes ( extendedConfig ) , "../" ) ) ) {
1157- errors . push ( createCompilerDiagnostic ( Diagnostics . A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not , extendedConfig ) ) ;
1158- return ;
1159- }
1160- let extendedConfigPath = toPath ( extendedConfig , basePath , getCanonicalFileName ) ;
1161- if ( ! host . fileExists ( extendedConfigPath ) && ! endsWith ( extendedConfigPath , ".json" ) ) {
1162- extendedConfigPath = `${ extendedConfigPath } .json` as Path ;
1163- if ( ! host . fileExists ( extendedConfigPath ) ) {
1164- errors . push ( createCompilerDiagnostic ( Diagnostics . File_0_does_not_exist , extendedConfig ) ) ;
1165- return ;
1166- }
1167- }
1168- const extendedResult = readConfigFile ( extendedConfigPath , path => host . readFile ( path ) ) ;
1169- if ( extendedResult . error ) {
1170- errors . push ( extendedResult . error ) ;
1171- return ;
1172- }
1173- const extendedDirname = getDirectoryPath ( extendedConfigPath ) ;
1174- const relativeDifference = convertToRelativePath ( extendedDirname , basePath , getCanonicalFileName ) ;
1175- const updatePath : ( path : string ) => string = path => isRootedDiskPath ( path ) ? path : combinePaths ( relativeDifference , path ) ;
1176- // Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
1177- const result = parseJsonConfigFileContent ( extendedResult . config , host , extendedDirname , /*existingOptions*/ undefined , getBaseFileName ( extendedConfigPath ) , resolutionStack . concat ( [ resolvedPath ] ) ) ;
1178- errors . push ( ...result . errors ) ;
1179- const [ include , exclude , files ] = map ( [ "include" , "exclude" , "files" ] , key => {
1180- if ( ! json [ key ] && extendedResult . config [ key ] ) {
1181- return map ( extendedResult . config [ key ] , updatePath ) ;
1182- }
1183- } ) ;
1184- return [ include , exclude , files , result . compileOnSave , result . options ] ;
1185- }
1186-
1187- function getFileNames ( errors : Diagnostic [ ] ) : ExpandResult {
1134+ function getFileNames ( ) : ExpandResult {
11881135 let fileNames : string [ ] ;
11891136 if ( hasProperty ( json , "files" ) ) {
11901137 if ( isArray ( json [ "files" ] ) ) {
@@ -1217,9 +1164,6 @@ namespace ts {
12171164 errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "exclude" , "Array" ) ) ;
12181165 }
12191166 }
1220- else if ( hasProperty ( json , "excludes" ) ) {
1221- errors . push ( createCompilerDiagnostic ( Diagnostics . Unknown_option_excludes_Did_you_mean_exclude ) ) ;
1222- }
12231167 else {
12241168 // If no includes were specified, exclude common package folders and the outDir
12251169 excludeSpecs = includeSpecs ? [ ] : [ "node_modules" , "bower_components" , "jspm_packages" ] ;
@@ -1249,7 +1193,106 @@ namespace ts {
12491193 }
12501194 }
12511195
1252- export function convertCompileOnSaveOptionFromJson ( jsonOption : any , basePath : string , errors : Diagnostic [ ] ) : boolean | undefined {
1196+ interface ParsedTsconfig {
1197+ include : string [ ] | undefined ;
1198+ exclude : string [ ] | undefined ;
1199+ files : string [ ] | undefined ;
1200+ options : CompilerOptions ;
1201+ compileOnSave : boolean | undefined ;
1202+ }
1203+
1204+ /**
1205+ * This *just* extracts options/include/exclude/files out of a config file.
1206+ * It does *not* resolve the included files.
1207+ */
1208+ function parseConfig (
1209+ json : any ,
1210+ host : ParseConfigHost ,
1211+ basePath : string ,
1212+ configFileName : string ,
1213+ resolutionStack : Path [ ] ,
1214+ errors : Diagnostic [ ] ,
1215+ ) : ParsedTsconfig {
1216+
1217+ basePath = normalizeSlashes ( basePath ) ;
1218+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
1219+ const resolvedPath = toPath ( configFileName || "" , basePath , getCanonicalFileName ) ;
1220+
1221+ if ( resolutionStack . indexOf ( resolvedPath ) >= 0 ) {
1222+ errors . push ( createCompilerDiagnostic ( Diagnostics . Circularity_detected_while_resolving_configuration_Colon_0 , [ ...resolutionStack , resolvedPath ] . join ( " -> " ) ) ) ;
1223+ return { include : undefined , exclude : undefined , files : undefined , options : { } , compileOnSave : undefined } ;
1224+ }
1225+
1226+ if ( hasProperty ( json , "excludes" ) ) {
1227+ errors . push ( createCompilerDiagnostic ( Diagnostics . Unknown_option_excludes_Did_you_mean_exclude ) ) ;
1228+ }
1229+
1230+ let options : CompilerOptions = convertCompilerOptionsFromJsonWorker ( json . compilerOptions , basePath , errors , configFileName ) ;
1231+ let include : string [ ] | undefined = json . include , exclude : string [ ] | undefined = json . exclude , files : string [ ] | undefined = json . files ;
1232+ let compileOnSave : boolean | undefined = json . compileOnSave ;
1233+
1234+ if ( json . extends ) {
1235+ // copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
1236+ resolutionStack = resolutionStack . concat ( [ resolvedPath ] ) ;
1237+ const base = getExtendedConfig ( json . extends , host , basePath , getCanonicalFileName , resolutionStack , errors ) ;
1238+ if ( base ) {
1239+ include = include || base . include ;
1240+ exclude = exclude || base . exclude ;
1241+ files = files || base . files ;
1242+ if ( compileOnSave === undefined ) {
1243+ compileOnSave = base . compileOnSave ;
1244+ }
1245+ options = assign ( { } , base . options , options ) ;
1246+ }
1247+ }
1248+
1249+ return { include, exclude, files, options, compileOnSave } ;
1250+ }
1251+
1252+ function getExtendedConfig (
1253+ extended : any , // Usually a string.
1254+ host : ts . ParseConfigHost ,
1255+ basePath : string ,
1256+ getCanonicalFileName : ( fileName : string ) => string ,
1257+ resolutionStack : Path [ ] ,
1258+ errors : Diagnostic [ ] ,
1259+ ) : ParsedTsconfig | undefined {
1260+ if ( typeof extended !== "string" ) {
1261+ errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "extends" , "string" ) ) ;
1262+ return undefined ;
1263+ }
1264+
1265+ extended = normalizeSlashes ( extended ) ;
1266+
1267+ // If the path isn't a rooted or relative path, don't try to resolve it (we reserve the right to special case module-id like paths in the future)
1268+ if ( ! ( isRootedDiskPath ( extended ) || startsWith ( extended , "./" ) || startsWith ( extended , "../" ) ) ) {
1269+ errors . push ( createCompilerDiagnostic ( Diagnostics . A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not , extended ) ) ;
1270+ return undefined ;
1271+ }
1272+
1273+ let extendedConfigPath = toPath ( extended , basePath , getCanonicalFileName ) ;
1274+ if ( ! host . fileExists ( extendedConfigPath ) && ! endsWith ( extendedConfigPath , ".json" ) ) {
1275+ extendedConfigPath = extendedConfigPath + ".json" as Path ;
1276+ if ( ! host . fileExists ( extendedConfigPath ) ) {
1277+ errors . push ( createCompilerDiagnostic ( Diagnostics . File_0_does_not_exist , extended ) ) ;
1278+ return undefined ;
1279+ }
1280+ }
1281+
1282+ const extendedResult = readConfigFile ( extendedConfigPath , path => host . readFile ( path ) ) ;
1283+ if ( extendedResult . error ) {
1284+ errors . push ( extendedResult . error ) ;
1285+ return undefined ;
1286+ }
1287+
1288+ const extendedDirname = getDirectoryPath ( extendedConfigPath ) ;
1289+ const relativeDifference = convertToRelativePath ( extendedDirname , basePath , getCanonicalFileName ) ;
1290+ const updatePath : ( path : string ) => string = path => isRootedDiskPath ( path ) ? path : combinePaths ( relativeDifference , path ) ;
1291+ const { include, exclude, files, options, compileOnSave } = parseConfig ( extendedResult . config , host , extendedDirname , getBaseFileName ( extendedConfigPath ) , resolutionStack , errors ) ;
1292+ return { include : map ( include , updatePath ) , exclude : map ( exclude , updatePath ) , files : map ( files , updatePath ) , compileOnSave, options } ;
1293+ }
1294+
1295+ export function convertCompileOnSaveOptionFromJson ( jsonOption : any , basePath : string , errors : Diagnostic [ ] ) : boolean {
12531296 if ( ! hasProperty ( jsonOption , compileOnSaveCommandLineOption . name ) ) {
12541297 return false ;
12551298 }
0 commit comments