@@ -1088,53 +1088,36 @@ namespace ts {
1088
1088
* @param host Instance of ParseConfigHost used to enumerate files in folder.
1089
1089
* @param basePath A root directory to resolve relative path entries in the config
1090
1090
* file to. e.g. outDir
1091
+ * @param resolutionStack Only present for backwards-compatibility. Should be empty.
1091
1092
*/
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 {
1093
1102
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
- }
1107
1103
1108
- let options : CompilerOptions = convertCompilerOptionsFromJsonWorker ( json [ "compilerOptions" ] , basePath , errors , configFileName ) ;
1104
+ let options = ( ( ) => {
1105
+ const { include, exclude, files, options } = 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
+ return options ;
1110
+ } ) ( ) ;
1111
+
1112
+ options = extend ( existingOptions , options ) ;
1113
+ options . configFilePath = configFileName ;
1114
+
1109
1115
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
1110
1116
// It should be removed in future releases - use typeAcquisition instead.
1111
1117
const jsonOptions = json [ "typeAcquisition" ] || json [ "typingOptions" ] ;
1112
1118
const typeAcquisition : TypeAcquisition = convertTypeAcquisitionFromJsonWorker ( jsonOptions , basePath , errors , configFileName ) ;
1113
1119
1114
- if ( json [ "extends" ] ) {
1115
- let [ include , exclude , files , baseOptions ] : [ string [ ] , string [ ] , string [ ] , CompilerOptions ] = [ undefined , undefined , undefined , { } ] ;
1116
- if ( typeof json [ "extends" ] === "string" ) {
1117
- [ include , exclude , files , baseOptions ] = ( tryExtendsName ( json [ "extends" ] ) || [ include , exclude , files , baseOptions ] ) ;
1118
- }
1119
- else {
1120
- errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "extends" , "string" ) ) ;
1121
- }
1122
- if ( include && ! json [ "include" ] ) {
1123
- json [ "include" ] = include ;
1124
- }
1125
- if ( exclude && ! json [ "exclude" ] ) {
1126
- json [ "exclude" ] = exclude ;
1127
- }
1128
- if ( files && ! json [ "files" ] ) {
1129
- json [ "files" ] = files ;
1130
- }
1131
- options = assign ( { } , baseOptions , options ) ;
1132
- }
1133
-
1134
- options = extend ( existingOptions , options ) ;
1135
- options . configFilePath = configFileName ;
1136
-
1137
- const { fileNames, wildcardDirectories } = getFileNames ( errors ) ;
1120
+ const { fileNames, wildcardDirectories } = getFileNames ( ) ;
1138
1121
const compileOnSave = convertCompileOnSaveOptionFromJson ( json , basePath , errors ) ;
1139
1122
1140
1123
return {
@@ -1147,40 +1130,7 @@ namespace ts {
1147
1130
compileOnSave
1148
1131
} ;
1149
1132
1150
- function tryExtendsName ( extendedConfig : string ) : [ string [ ] , string [ ] , string [ ] , CompilerOptions ] {
1151
- // 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)
1152
- if ( ! ( isRootedDiskPath ( extendedConfig ) || startsWith ( normalizeSlashes ( extendedConfig ) , "./" ) || startsWith ( normalizeSlashes ( extendedConfig ) , "../" ) ) ) {
1153
- errors . push ( createCompilerDiagnostic ( Diagnostics . A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not , extendedConfig ) ) ;
1154
- return ;
1155
- }
1156
- let extendedConfigPath = toPath ( extendedConfig , basePath , getCanonicalFileName ) ;
1157
- if ( ! host . fileExists ( extendedConfigPath ) && ! endsWith ( extendedConfigPath , ".json" ) ) {
1158
- extendedConfigPath = `${ extendedConfigPath } .json` as Path ;
1159
- if ( ! host . fileExists ( extendedConfigPath ) ) {
1160
- errors . push ( createCompilerDiagnostic ( Diagnostics . File_0_does_not_exist , extendedConfig ) ) ;
1161
- return ;
1162
- }
1163
- }
1164
- const extendedResult = readConfigFile ( extendedConfigPath , path => host . readFile ( path ) ) ;
1165
- if ( extendedResult . error ) {
1166
- errors . push ( extendedResult . error ) ;
1167
- return ;
1168
- }
1169
- const extendedDirname = getDirectoryPath ( extendedConfigPath ) ;
1170
- const relativeDifference = convertToRelativePath ( extendedDirname , basePath , getCanonicalFileName ) ;
1171
- const updatePath : ( path : string ) => string = path => isRootedDiskPath ( path ) ? path : combinePaths ( relativeDifference , path ) ;
1172
- // Merge configs (copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios)
1173
- const result = parseJsonConfigFileContent ( extendedResult . config , host , extendedDirname , /*existingOptions*/ undefined , getBaseFileName ( extendedConfigPath ) , resolutionStack . concat ( [ resolvedPath ] ) ) ;
1174
- errors . push ( ...result . errors ) ;
1175
- const [ include , exclude , files ] = map ( [ "include" , "exclude" , "files" ] , key => {
1176
- if ( ! json [ key ] && extendedResult . config [ key ] ) {
1177
- return map ( extendedResult . config [ key ] , updatePath ) ;
1178
- }
1179
- } ) ;
1180
- return [ include , exclude , files , result . options ] ;
1181
- }
1182
-
1183
- function getFileNames ( errors : Diagnostic [ ] ) : ExpandResult {
1133
+ function getFileNames ( ) : ExpandResult {
1184
1134
let fileNames : string [ ] ;
1185
1135
if ( hasProperty ( json , "files" ) ) {
1186
1136
if ( isArray ( json [ "files" ] ) ) {
@@ -1213,9 +1163,6 @@ namespace ts {
1213
1163
errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "exclude" , "Array" ) ) ;
1214
1164
}
1215
1165
}
1216
- else if ( hasProperty ( json , "excludes" ) ) {
1217
- errors . push ( createCompilerDiagnostic ( Diagnostics . Unknown_option_excludes_Did_you_mean_exclude ) ) ;
1218
- }
1219
1166
else {
1220
1167
// If no includes were specified, exclude common package folders and the outDir
1221
1168
excludeSpecs = includeSpecs ? [ ] : [ "node_modules" , "bower_components" , "jspm_packages" ] ;
@@ -1245,6 +1192,93 @@ namespace ts {
1245
1192
}
1246
1193
}
1247
1194
1195
+ type ParsedTsconfig = { include ?: string [ ] , exclude ?: string [ ] , files ?: string [ ] , options : CompilerOptions } ;
1196
+
1197
+ /**
1198
+ * This *just* extracts options/include/exclude/files out of a config file.
1199
+ * It does *not* resolve the included files.
1200
+ */
1201
+ function parseConfig (
1202
+ json : any ,
1203
+ host : ParseConfigHost ,
1204
+ basePath : string ,
1205
+ configFileName : string ,
1206
+ resolutionStack : Path [ ] = [ ] ,
1207
+ errors : Diagnostic [ ] ,
1208
+ ) : ParsedTsconfig {
1209
+
1210
+ basePath = normalizeSlashes ( basePath ) ;
1211
+ const getCanonicalFileName = createGetCanonicalFileName ( host . useCaseSensitiveFileNames ) ;
1212
+ const resolvedPath = toPath ( configFileName || "" , basePath , getCanonicalFileName ) ;
1213
+
1214
+ if ( resolutionStack . indexOf ( resolvedPath ) >= 0 ) {
1215
+ errors . push ( createCompilerDiagnostic ( Diagnostics . Circularity_detected_while_resolving_configuration_Colon_0 , [ ...resolutionStack , resolvedPath ] . join ( " -> " ) ) ) ;
1216
+ return { options : { } } ;
1217
+ }
1218
+
1219
+ if ( hasProperty ( json , "excludes" ) ) {
1220
+ errors . push ( createCompilerDiagnostic ( Diagnostics . Unknown_option_excludes_Did_you_mean_exclude ) ) ;
1221
+ }
1222
+
1223
+ let options : CompilerOptions = convertCompilerOptionsFromJsonWorker ( json . compilerOptions , basePath , errors , configFileName ) ;
1224
+ let include : string [ ] | undefined = json . include , exclude : string [ ] | undefined = json . exclude , files : string [ ] | undefined = json . files ;
1225
+
1226
+ if ( json . extends ) {
1227
+ // copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios.
1228
+ resolutionStack = resolutionStack . concat ( [ resolvedPath ] ) ;
1229
+ const base = getExtendedConfig ( json . extends , host , basePath , getCanonicalFileName , resolutionStack , errors ) ;
1230
+ if ( base ) {
1231
+ include = include || base . include ;
1232
+ exclude = exclude || base . exclude ;
1233
+ files = files || base . files ;
1234
+ options = assign ( { } , base . options , options ) ;
1235
+ }
1236
+ }
1237
+
1238
+ return { include, exclude, files, options } ;
1239
+ }
1240
+
1241
+ function getExtendedConfig (
1242
+ extended : any , // Usually a string.
1243
+ host : ts . ParseConfigHost ,
1244
+ basePath : string ,
1245
+ getCanonicalFileName : ( fileName : string ) => string ,
1246
+ resolutionStack : Path [ ] ,
1247
+ errors : Diagnostic [ ] ,
1248
+ ) : ParsedTsconfig | undefined {
1249
+ if ( typeof extended !== "string" ) {
1250
+ errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "extends" , "string" ) ) ;
1251
+ return undefined ;
1252
+ }
1253
+
1254
+ // 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)
1255
+ if ( ! ( isRootedDiskPath ( extended ) || startsWith ( normalizeSlashes ( extended ) , "./" ) || startsWith ( normalizeSlashes ( extended ) , "../" ) ) ) {
1256
+ errors . push ( createCompilerDiagnostic ( Diagnostics . A_path_in_an_extends_option_must_be_relative_or_rooted_but_0_is_not , extended ) ) ;
1257
+ return undefined ;
1258
+ }
1259
+
1260
+ let extendedConfigPath = toPath ( extended , basePath , getCanonicalFileName ) ;
1261
+ if ( ! host . fileExists ( extendedConfigPath ) && ! endsWith ( extendedConfigPath , ".json" ) ) {
1262
+ extendedConfigPath = extendedConfigPath + ".json" as Path ;
1263
+ if ( ! host . fileExists ( extendedConfigPath ) ) {
1264
+ errors . push ( createCompilerDiagnostic ( Diagnostics . File_0_does_not_exist , extended ) ) ;
1265
+ return undefined ;
1266
+ }
1267
+ }
1268
+
1269
+ const extendedResult = readConfigFile ( extendedConfigPath , path => host . readFile ( path ) ) ;
1270
+ if ( extendedResult . error ) {
1271
+ errors . push ( extendedResult . error ) ;
1272
+ return undefined ;
1273
+ }
1274
+
1275
+ const extendedDirname = getDirectoryPath ( extendedConfigPath ) ;
1276
+ const relativeDifference = convertToRelativePath ( extendedDirname , basePath , getCanonicalFileName ) ;
1277
+ const updatePath : ( path : string ) => string = path => isRootedDiskPath ( path ) ? path : combinePaths ( relativeDifference , path ) ;
1278
+ const { include, exclude, files, options } = parseConfig ( extendedResult . config , host , extendedDirname , getBaseFileName ( extendedConfigPath ) , resolutionStack , errors ) ;
1279
+ return { include : map ( include , updatePath ) , exclude : map ( exclude , updatePath ) , files : map ( files , updatePath ) , options } ;
1280
+ }
1281
+
1248
1282
export function convertCompileOnSaveOptionFromJson ( jsonOption : any , basePath : string , errors : Diagnostic [ ] ) : boolean {
1249
1283
if ( ! hasProperty ( jsonOption , compileOnSaveCommandLineOption . name ) ) {
1250
1284
return false ;
0 commit comments