@@ -1088,58 +1088,38 @@ 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, 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
+
1109
1116
// typingOptions has been deprecated and is only supported for backward compatibility purposes.
1110
1117
// It should be removed in future releases - use typeAcquisition instead.
1111
1118
const jsonOptions = json [ "typeAcquisition" ] || json [ "typingOptions" ] ;
1112
1119
const typeAcquisition : TypeAcquisition = convertTypeAcquisitionFromJsonWorker ( jsonOptions , basePath , errors , configFileName ) ;
1113
1120
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 ) ;
1143
1123
1144
1124
return {
1145
1125
options,
@@ -1151,40 +1131,7 @@ namespace ts {
1151
1131
compileOnSave
1152
1132
} ;
1153
1133
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 {
1188
1135
let fileNames : string [ ] ;
1189
1136
if ( hasProperty ( json , "files" ) ) {
1190
1137
if ( isArray ( json [ "files" ] ) ) {
@@ -1217,9 +1164,6 @@ namespace ts {
1217
1164
errors . push ( createCompilerDiagnostic ( Diagnostics . Compiler_option_0_requires_a_value_of_type_1 , "exclude" , "Array" ) ) ;
1218
1165
}
1219
1166
}
1220
- else if ( hasProperty ( json , "excludes" ) ) {
1221
- errors . push ( createCompilerDiagnostic ( Diagnostics . Unknown_option_excludes_Did_you_mean_exclude ) ) ;
1222
- }
1223
1167
else {
1224
1168
// If no includes were specified, exclude common package folders and the outDir
1225
1169
excludeSpecs = includeSpecs ? [ ] : [ "node_modules" , "bower_components" , "jspm_packages" ] ;
@@ -1249,7 +1193,106 @@ namespace ts {
1249
1193
}
1250
1194
}
1251
1195
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 {
1253
1296
if ( ! hasProperty ( jsonOption , compileOnSaveCommandLineOption . name ) ) {
1254
1297
return false ;
1255
1298
}
0 commit comments