1namespace ts { 2 /* @internal */ 3 export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" }; 4 5 const jsxOptionMap = new Map(getEntries({ 6 "preserve": JsxEmit.Preserve, 7 "react-native": JsxEmit.ReactNative, 8 "react": JsxEmit.React, 9 "react-jsx": JsxEmit.ReactJSX, 10 "react-jsxdev": JsxEmit.ReactJSXDev, 11 })); 12 13 /* @internal */ 14 export const inverseJsxOptionMap = new Map(arrayFrom(mapIterator(jsxOptionMap.entries(), ([key, value]: [string, JsxEmit]) => ["" + value, key] as const))); 15 16 // NOTE: The order here is important to default lib ordering as entries will have the same 17 // order in the generated program (see `getDefaultLibPriority` in program.ts). This 18 // order also affects overload resolution when a type declared in one lib is 19 // augmented in another lib. 20 const libEntries: [string, string][] = [ 21 // JavaScript only 22 ["es5", "lib.es5.d.ts"], 23 ["es6", "lib.es2015.d.ts"], 24 ["es2015", "lib.es2015.d.ts"], 25 ["es7", "lib.es2016.d.ts"], 26 ["es2016", "lib.es2016.d.ts"], 27 ["es2017", "lib.es2017.d.ts"], 28 ["es2018", "lib.es2018.d.ts"], 29 ["es2019", "lib.es2019.d.ts"], 30 ["es2020", "lib.es2020.d.ts"], 31 ["esnext", "lib.esnext.d.ts"], 32 // Host only 33 ["dom", "lib.dom.d.ts"], 34 ["dom.iterable", "lib.dom.iterable.d.ts"], 35 ["webworker", "lib.webworker.d.ts"], 36 ["webworker.importscripts", "lib.webworker.importscripts.d.ts"], 37 ["webworker.iterable", "lib.webworker.iterable.d.ts"], 38 ["scripthost", "lib.scripthost.d.ts"], 39 // ES2015 Or ESNext By-feature options 40 ["es2015.core", "lib.es2015.core.d.ts"], 41 ["es2015.collection", "lib.es2015.collection.d.ts"], 42 ["es2015.generator", "lib.es2015.generator.d.ts"], 43 ["es2015.iterable", "lib.es2015.iterable.d.ts"], 44 ["es2015.promise", "lib.es2015.promise.d.ts"], 45 ["es2015.proxy", "lib.es2015.proxy.d.ts"], 46 ["es2015.reflect", "lib.es2015.reflect.d.ts"], 47 ["es2015.symbol", "lib.es2015.symbol.d.ts"], 48 ["es2015.symbol.wellknown", "lib.es2015.symbol.wellknown.d.ts"], 49 ["es2016.array.include", "lib.es2016.array.include.d.ts"], 50 ["es2017.object", "lib.es2017.object.d.ts"], 51 ["es2017.sharedmemory", "lib.es2017.sharedmemory.d.ts"], 52 ["es2017.string", "lib.es2017.string.d.ts"], 53 ["es2017.intl", "lib.es2017.intl.d.ts"], 54 ["es2017.typedarrays", "lib.es2017.typedarrays.d.ts"], 55 ["es2018.asyncgenerator", "lib.es2018.asyncgenerator.d.ts"], 56 ["es2018.asynciterable", "lib.es2018.asynciterable.d.ts"], 57 ["es2018.intl", "lib.es2018.intl.d.ts"], 58 ["es2018.promise", "lib.es2018.promise.d.ts"], 59 ["es2018.regexp", "lib.es2018.regexp.d.ts"], 60 ["es2019.array", "lib.es2019.array.d.ts"], 61 ["es2019.object", "lib.es2019.object.d.ts"], 62 ["es2019.string", "lib.es2019.string.d.ts"], 63 ["es2019.symbol", "lib.es2019.symbol.d.ts"], 64 ["es2020.bigint", "lib.es2020.bigint.d.ts"], 65 ["es2020.promise", "lib.es2020.promise.d.ts"], 66 ["es2020.sharedmemory", "lib.es2020.sharedmemory.d.ts"], 67 ["es2020.string", "lib.es2020.string.d.ts"], 68 ["es2020.symbol.wellknown", "lib.es2020.symbol.wellknown.d.ts"], 69 ["es2020.intl", "lib.es2020.intl.d.ts"], 70 ["esnext.array", "lib.es2019.array.d.ts"], 71 ["esnext.symbol", "lib.es2019.symbol.d.ts"], 72 ["esnext.asynciterable", "lib.es2018.asynciterable.d.ts"], 73 ["esnext.intl", "lib.esnext.intl.d.ts"], 74 ["esnext.bigint", "lib.es2020.bigint.d.ts"], 75 ["esnext.string", "lib.esnext.string.d.ts"], 76 ["esnext.promise", "lib.esnext.promise.d.ts"], 77 ["esnext.weakref", "lib.esnext.weakref.d.ts"] 78 ]; 79 80 /** 81 * An array of supported "lib" reference file names used to determine the order for inclusion 82 * when referenced, as well as for spelling suggestions. This ensures the correct ordering for 83 * overload resolution when a type declared in one lib is extended by another. 84 */ 85 /* @internal */ 86 export const libs = libEntries.map(entry => entry[0]); 87 88 /** 89 * A map of lib names to lib files. This map is used both for parsing the "lib" command line 90 * option as well as for resolving lib reference directives. 91 */ 92 /* @internal */ 93 export const libMap = new Map(libEntries); 94 95 // Watch related options 96 /* @internal */ 97 export const optionsForWatch: CommandLineOption[] = [ 98 { 99 name: "watchFile", 100 type: new Map(getEntries({ 101 fixedpollinginterval: WatchFileKind.FixedPollingInterval, 102 prioritypollinginterval: WatchFileKind.PriorityPollingInterval, 103 dynamicprioritypolling: WatchFileKind.DynamicPriorityPolling, 104 usefsevents: WatchFileKind.UseFsEvents, 105 usefseventsonparentdirectory: WatchFileKind.UseFsEventsOnParentDirectory, 106 })), 107 category: Diagnostics.Advanced_Options, 108 description: Diagnostics.Specify_strategy_for_watching_file_Colon_FixedPollingInterval_default_PriorityPollingInterval_DynamicPriorityPolling_UseFsEvents_UseFsEventsOnParentDirectory, 109 }, 110 { 111 name: "watchDirectory", 112 type: new Map(getEntries({ 113 usefsevents: WatchDirectoryKind.UseFsEvents, 114 fixedpollinginterval: WatchDirectoryKind.FixedPollingInterval, 115 dynamicprioritypolling: WatchDirectoryKind.DynamicPriorityPolling, 116 })), 117 category: Diagnostics.Advanced_Options, 118 description: Diagnostics.Specify_strategy_for_watching_directory_on_platforms_that_don_t_support_recursive_watching_natively_Colon_UseFsEvents_default_FixedPollingInterval_DynamicPriorityPolling, 119 }, 120 { 121 name: "fallbackPolling", 122 type: new Map(getEntries({ 123 fixedinterval: PollingWatchKind.FixedInterval, 124 priorityinterval: PollingWatchKind.PriorityInterval, 125 dynamicpriority: PollingWatchKind.DynamicPriority, 126 })), 127 category: Diagnostics.Advanced_Options, 128 description: Diagnostics.Specify_strategy_for_creating_a_polling_watch_when_it_fails_to_create_using_file_system_events_Colon_FixedInterval_default_PriorityInterval_DynamicPriority, 129 }, 130 { 131 name: "synchronousWatchDirectory", 132 type: "boolean", 133 category: Diagnostics.Advanced_Options, 134 description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, 135 }, 136 { 137 name: "excludeDirectories", 138 type: "list", 139 element: { 140 name: "excludeDirectory", 141 type: "string", 142 isFilePath: true, 143 extraValidation: specToDiagnostic 144 }, 145 category: Diagnostics.Advanced_Options, 146 description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, 147 }, 148 { 149 name: "excludeFiles", 150 type: "list", 151 element: { 152 name: "excludeFile", 153 type: "string", 154 isFilePath: true, 155 extraValidation: specToDiagnostic 156 }, 157 category: Diagnostics.Advanced_Options, 158 description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively, 159 }, 160 ]; 161 162 /* @internal */ 163 export const commonOptionsWithBuild: CommandLineOption[] = [ 164 { 165 name: "help", 166 shortName: "h", 167 type: "boolean", 168 showInSimplifiedHelpView: true, 169 category: Diagnostics.Command_line_Options, 170 description: Diagnostics.Print_this_message, 171 }, 172 { 173 name: "help", 174 shortName: "?", 175 type: "boolean" 176 }, 177 { 178 name: "watch", 179 shortName: "w", 180 type: "boolean", 181 showInSimplifiedHelpView: true, 182 category: Diagnostics.Command_line_Options, 183 description: Diagnostics.Watch_input_files, 184 }, 185 { 186 name: "preserveWatchOutput", 187 type: "boolean", 188 showInSimplifiedHelpView: false, 189 category: Diagnostics.Command_line_Options, 190 description: Diagnostics.Whether_to_keep_outdated_console_output_in_watch_mode_instead_of_clearing_the_screen, 191 }, 192 { 193 name: "listFiles", 194 type: "boolean", 195 category: Diagnostics.Advanced_Options, 196 description: Diagnostics.Print_names_of_files_part_of_the_compilation 197 }, 198 { 199 name: "explainFiles", 200 type: "boolean", 201 category: Diagnostics.Advanced_Options, 202 description: Diagnostics.Print_names_of_files_and_the_reason_they_are_part_of_the_compilation 203 }, { 204 name: "listEmittedFiles", 205 type: "boolean", 206 category: Diagnostics.Advanced_Options, 207 description: Diagnostics.Print_names_of_generated_files_part_of_the_compilation 208 }, 209 { 210 name: "pretty", 211 type: "boolean", 212 showInSimplifiedHelpView: true, 213 category: Diagnostics.Command_line_Options, 214 description: Diagnostics.Stylize_errors_and_messages_using_color_and_context_experimental 215 }, 216 217 { 218 name: "traceResolution", 219 type: "boolean", 220 category: Diagnostics.Advanced_Options, 221 description: Diagnostics.Enable_tracing_of_the_name_resolution_process 222 }, 223 { 224 name: "diagnostics", 225 type: "boolean", 226 category: Diagnostics.Advanced_Options, 227 description: Diagnostics.Show_diagnostic_information 228 }, 229 { 230 name: "extendedDiagnostics", 231 type: "boolean", 232 category: Diagnostics.Advanced_Options, 233 description: Diagnostics.Show_verbose_diagnostic_information 234 }, 235 { 236 name: "generateCpuProfile", 237 type: "string", 238 isFilePath: true, 239 paramType: Diagnostics.FILE_OR_DIRECTORY, 240 category: Diagnostics.Advanced_Options, 241 description: Diagnostics.Generates_a_CPU_profile 242 }, 243 { 244 name: "generateTrace", 245 type: "string", 246 isFilePath: true, 247 isCommandLineOnly: true, 248 paramType: Diagnostics.DIRECTORY, 249 category: Diagnostics.Advanced_Options, 250 description: Diagnostics.Generates_an_event_trace_and_a_list_of_types 251 }, 252 { 253 name: "incremental", 254 shortName: "i", 255 type: "boolean", 256 category: Diagnostics.Basic_Options, 257 description: Diagnostics.Enable_incremental_compilation, 258 transpileOptionValue: undefined 259 }, 260 { 261 name: "assumeChangesOnlyAffectDirectDependencies", 262 type: "boolean", 263 affectsSemanticDiagnostics: true, 264 affectsEmit: true, 265 category: Diagnostics.Advanced_Options, 266 description: Diagnostics.Have_recompiles_in_incremental_and_watch_assume_that_changes_within_a_file_will_only_affect_files_directly_depending_on_it 267 }, 268 { 269 name: "locale", 270 type: "string", 271 category: Diagnostics.Advanced_Options, 272 description: Diagnostics.The_locale_used_when_displaying_messages_to_the_user_e_g_en_us 273 }, 274 ]; 275 276 /* @internal */ 277 export const targetOptionDeclaration: CommandLineOptionOfCustomType = { 278 name: "target", 279 shortName: "t", 280 type: new Map(getEntries({ 281 es3: ScriptTarget.ES3, 282 es5: ScriptTarget.ES5, 283 es6: ScriptTarget.ES2015, 284 es2015: ScriptTarget.ES2015, 285 es2016: ScriptTarget.ES2016, 286 es2017: ScriptTarget.ES2017, 287 es2018: ScriptTarget.ES2018, 288 es2019: ScriptTarget.ES2019, 289 es2020: ScriptTarget.ES2020, 290 esnext: ScriptTarget.ESNext, 291 })), 292 affectsSourceFile: true, 293 affectsModuleResolution: true, 294 affectsEmit: true, 295 paramType: Diagnostics.VERSION, 296 showInSimplifiedHelpView: true, 297 category: Diagnostics.Basic_Options, 298 description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_ES2018_ES2019_ES2020_or_ESNEXT, 299 }; 300 301 /* @internal */ 302 export const optionDeclarations: CommandLineOption[] = [ 303 // CommandLine only options 304 ...commonOptionsWithBuild, 305 { 306 name: "all", 307 type: "boolean", 308 showInSimplifiedHelpView: true, 309 category: Diagnostics.Command_line_Options, 310 description: Diagnostics.Show_all_compiler_options, 311 }, 312 { 313 name: "version", 314 shortName: "v", 315 type: "boolean", 316 showInSimplifiedHelpView: true, 317 category: Diagnostics.Command_line_Options, 318 description: Diagnostics.Print_the_compiler_s_version, 319 }, 320 { 321 name: "init", 322 type: "boolean", 323 showInSimplifiedHelpView: true, 324 category: Diagnostics.Command_line_Options, 325 description: Diagnostics.Initializes_a_TypeScript_project_and_creates_a_tsconfig_json_file, 326 }, 327 { 328 name: "project", 329 shortName: "p", 330 type: "string", 331 isFilePath: true, 332 showInSimplifiedHelpView: true, 333 category: Diagnostics.Command_line_Options, 334 paramType: Diagnostics.FILE_OR_DIRECTORY, 335 description: Diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json, 336 }, 337 { 338 name: "build", 339 type: "boolean", 340 shortName: "b", 341 showInSimplifiedHelpView: true, 342 category: Diagnostics.Command_line_Options, 343 description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date 344 }, 345 { 346 name: "showConfig", 347 type: "boolean", 348 category: Diagnostics.Command_line_Options, 349 isCommandLineOnly: true, 350 description: Diagnostics.Print_the_final_configuration_instead_of_building 351 }, 352 { 353 name: "listFilesOnly", 354 type: "boolean", 355 category: Diagnostics.Command_line_Options, 356 affectsSemanticDiagnostics: true, 357 affectsEmit: true, 358 isCommandLineOnly: true, 359 description: Diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing 360 }, 361 362 // Basic 363 targetOptionDeclaration, 364 { 365 name: "module", 366 shortName: "m", 367 type: new Map(getEntries({ 368 none: ModuleKind.None, 369 commonjs: ModuleKind.CommonJS, 370 amd: ModuleKind.AMD, 371 system: ModuleKind.System, 372 umd: ModuleKind.UMD, 373 es6: ModuleKind.ES2015, 374 es2015: ModuleKind.ES2015, 375 es2020: ModuleKind.ES2020, 376 esnext: ModuleKind.ESNext 377 })), 378 affectsModuleResolution: true, 379 affectsEmit: true, 380 paramType: Diagnostics.KIND, 381 showInSimplifiedHelpView: true, 382 category: Diagnostics.Basic_Options, 383 description: Diagnostics.Specify_module_code_generation_Colon_none_commonjs_amd_system_umd_es2015_es2020_or_ESNext, 384 }, 385 { 386 name: "lib", 387 type: "list", 388 element: { 389 name: "lib", 390 type: libMap 391 }, 392 affectsModuleResolution: true, 393 showInSimplifiedHelpView: true, 394 category: Diagnostics.Basic_Options, 395 description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation, 396 transpileOptionValue: undefined 397 }, 398 { 399 name: "allowJs", 400 type: "boolean", 401 affectsModuleResolution: true, 402 showInSimplifiedHelpView: true, 403 category: Diagnostics.Basic_Options, 404 description: Diagnostics.Allow_javascript_files_to_be_compiled 405 }, 406 { 407 name: "checkJs", 408 type: "boolean", 409 category: Diagnostics.Basic_Options, 410 description: Diagnostics.Report_errors_in_js_files 411 }, 412 { 413 name: "jsx", 414 type: jsxOptionMap, 415 affectsSourceFile: true, 416 affectsEmit: true, 417 affectsModuleResolution: true, 418 paramType: Diagnostics.KIND, 419 showInSimplifiedHelpView: true, 420 category: Diagnostics.Basic_Options, 421 description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_react_native_react_react_jsx_or_react_jsxdev, 422 }, 423 { 424 name: "declaration", 425 shortName: "d", 426 type: "boolean", 427 affectsEmit: true, 428 showInSimplifiedHelpView: true, 429 category: Diagnostics.Basic_Options, 430 description: Diagnostics.Generates_corresponding_d_ts_file, 431 transpileOptionValue: undefined 432 }, 433 { 434 name: "declarationMap", 435 type: "boolean", 436 affectsEmit: true, 437 showInSimplifiedHelpView: true, 438 category: Diagnostics.Basic_Options, 439 description: Diagnostics.Generates_a_sourcemap_for_each_corresponding_d_ts_file, 440 transpileOptionValue: undefined 441 }, 442 { 443 name: "emitDeclarationOnly", 444 type: "boolean", 445 affectsEmit: true, 446 category: Diagnostics.Advanced_Options, 447 description: Diagnostics.Only_emit_d_ts_declaration_files, 448 transpileOptionValue: undefined 449 }, 450 { 451 name: "sourceMap", 452 type: "boolean", 453 affectsEmit: true, 454 showInSimplifiedHelpView: true, 455 category: Diagnostics.Basic_Options, 456 description: Diagnostics.Generates_corresponding_map_file, 457 }, 458 { 459 name: "outFile", 460 type: "string", 461 affectsEmit: true, 462 isFilePath: true, 463 paramType: Diagnostics.FILE, 464 showInSimplifiedHelpView: true, 465 category: Diagnostics.Basic_Options, 466 description: Diagnostics.Concatenate_and_emit_output_to_single_file, 467 transpileOptionValue: undefined 468 }, 469 { 470 name: "outDir", 471 type: "string", 472 affectsEmit: true, 473 isFilePath: true, 474 paramType: Diagnostics.DIRECTORY, 475 showInSimplifiedHelpView: true, 476 category: Diagnostics.Basic_Options, 477 description: Diagnostics.Redirect_output_structure_to_the_directory, 478 }, 479 { 480 name: "rootDir", 481 type: "string", 482 affectsEmit: true, 483 isFilePath: true, 484 paramType: Diagnostics.LOCATION, 485 category: Diagnostics.Basic_Options, 486 description: Diagnostics.Specify_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir, 487 }, 488 { 489 name: "composite", 490 type: "boolean", 491 affectsEmit: true, 492 isTSConfigOnly: true, 493 category: Diagnostics.Basic_Options, 494 description: Diagnostics.Enable_project_compilation, 495 transpileOptionValue: undefined 496 }, 497 { 498 name: "tsBuildInfoFile", 499 type: "string", 500 affectsEmit: true, 501 isFilePath: true, 502 paramType: Diagnostics.FILE, 503 category: Diagnostics.Basic_Options, 504 description: Diagnostics.Specify_file_to_store_incremental_compilation_information, 505 transpileOptionValue: undefined 506 }, 507 { 508 name: "removeComments", 509 type: "boolean", 510 affectsEmit: true, 511 showInSimplifiedHelpView: true, 512 category: Diagnostics.Basic_Options, 513 description: Diagnostics.Do_not_emit_comments_to_output, 514 }, 515 { 516 name: "noEmit", 517 type: "boolean", 518 showInSimplifiedHelpView: true, 519 category: Diagnostics.Basic_Options, 520 description: Diagnostics.Do_not_emit_outputs, 521 transpileOptionValue: undefined 522 }, 523 { 524 name: "importHelpers", 525 type: "boolean", 526 affectsEmit: true, 527 category: Diagnostics.Basic_Options, 528 description: Diagnostics.Import_emit_helpers_from_tslib 529 }, 530 { 531 name: "importsNotUsedAsValues", 532 type: new Map(getEntries({ 533 remove: ImportsNotUsedAsValues.Remove, 534 preserve: ImportsNotUsedAsValues.Preserve, 535 error: ImportsNotUsedAsValues.Error 536 })), 537 affectsEmit: true, 538 affectsSemanticDiagnostics: true, 539 category: Diagnostics.Advanced_Options, 540 description: Diagnostics.Specify_emit_Slashchecking_behavior_for_imports_that_are_only_used_for_types 541 }, 542 { 543 name: "downlevelIteration", 544 type: "boolean", 545 affectsEmit: true, 546 category: Diagnostics.Basic_Options, 547 description: Diagnostics.Provide_full_support_for_iterables_in_for_of_spread_and_destructuring_when_targeting_ES5_or_ES3 548 }, 549 { 550 name: "isolatedModules", 551 type: "boolean", 552 category: Diagnostics.Basic_Options, 553 description: Diagnostics.Transpile_each_file_as_a_separate_module_similar_to_ts_transpileModule, 554 transpileOptionValue: true 555 }, 556 557 // Strict Type Checks 558 { 559 name: "strict", 560 type: "boolean", 561 showInSimplifiedHelpView: true, 562 category: Diagnostics.Strict_Type_Checking_Options, 563 description: Diagnostics.Enable_all_strict_type_checking_options 564 }, 565 { 566 name: "noImplicitAny", 567 type: "boolean", 568 affectsSemanticDiagnostics: true, 569 strictFlag: true, 570 showInSimplifiedHelpView: true, 571 category: Diagnostics.Strict_Type_Checking_Options, 572 description: Diagnostics.Raise_error_on_expressions_and_declarations_with_an_implied_any_type 573 }, 574 { 575 name: "strictNullChecks", 576 type: "boolean", 577 affectsSemanticDiagnostics: true, 578 strictFlag: true, 579 showInSimplifiedHelpView: true, 580 category: Diagnostics.Strict_Type_Checking_Options, 581 description: Diagnostics.Enable_strict_null_checks 582 }, 583 { 584 name: "strictFunctionTypes", 585 type: "boolean", 586 affectsSemanticDiagnostics: true, 587 strictFlag: true, 588 showInSimplifiedHelpView: true, 589 category: Diagnostics.Strict_Type_Checking_Options, 590 description: Diagnostics.Enable_strict_checking_of_function_types 591 }, 592 { 593 name: "strictBindCallApply", 594 type: "boolean", 595 strictFlag: true, 596 showInSimplifiedHelpView: true, 597 category: Diagnostics.Strict_Type_Checking_Options, 598 description: Diagnostics.Enable_strict_bind_call_and_apply_methods_on_functions 599 }, 600 { 601 name: "strictPropertyInitialization", 602 type: "boolean", 603 affectsSemanticDiagnostics: true, 604 strictFlag: true, 605 showInSimplifiedHelpView: true, 606 category: Diagnostics.Strict_Type_Checking_Options, 607 description: Diagnostics.Enable_strict_checking_of_property_initialization_in_classes 608 }, 609 { 610 name: "noImplicitThis", 611 type: "boolean", 612 affectsSemanticDiagnostics: true, 613 strictFlag: true, 614 showInSimplifiedHelpView: true, 615 category: Diagnostics.Strict_Type_Checking_Options, 616 description: Diagnostics.Raise_error_on_this_expressions_with_an_implied_any_type, 617 }, 618 { 619 name: "alwaysStrict", 620 type: "boolean", 621 affectsSourceFile: true, 622 strictFlag: true, 623 showInSimplifiedHelpView: true, 624 category: Diagnostics.Strict_Type_Checking_Options, 625 description: Diagnostics.Parse_in_strict_mode_and_emit_use_strict_for_each_source_file 626 }, 627 628 // Additional Checks 629 { 630 name: "noUnusedLocals", 631 type: "boolean", 632 affectsSemanticDiagnostics: true, 633 showInSimplifiedHelpView: true, 634 category: Diagnostics.Additional_Checks, 635 description: Diagnostics.Report_errors_on_unused_locals, 636 }, 637 { 638 name: "noUnusedParameters", 639 type: "boolean", 640 affectsSemanticDiagnostics: true, 641 showInSimplifiedHelpView: true, 642 category: Diagnostics.Additional_Checks, 643 description: Diagnostics.Report_errors_on_unused_parameters, 644 }, 645 { 646 name: "noImplicitReturns", 647 type: "boolean", 648 affectsSemanticDiagnostics: true, 649 showInSimplifiedHelpView: true, 650 category: Diagnostics.Additional_Checks, 651 description: Diagnostics.Report_error_when_not_all_code_paths_in_function_return_a_value 652 }, 653 { 654 name: "noFallthroughCasesInSwitch", 655 type: "boolean", 656 affectsBindDiagnostics: true, 657 affectsSemanticDiagnostics: true, 658 showInSimplifiedHelpView: true, 659 category: Diagnostics.Additional_Checks, 660 description: Diagnostics.Report_errors_for_fallthrough_cases_in_switch_statement 661 }, 662 { 663 name: "noUncheckedIndexedAccess", 664 type: "boolean", 665 affectsSemanticDiagnostics: true, 666 showInSimplifiedHelpView: false, 667 category: Diagnostics.Additional_Checks, 668 description: Diagnostics.Include_undefined_in_index_signature_results 669 }, 670 { 671 name: "noPropertyAccessFromIndexSignature", 672 type: "boolean", 673 showInSimplifiedHelpView: false, 674 category: Diagnostics.Additional_Checks, 675 description: Diagnostics.Require_undeclared_properties_from_index_signatures_to_use_element_accesses 676 }, 677 678 // Module Resolution 679 { 680 name: "moduleResolution", 681 type: new Map(getEntries({ 682 node: ModuleResolutionKind.NodeJs, 683 classic: ModuleResolutionKind.Classic, 684 })), 685 affectsModuleResolution: true, 686 paramType: Diagnostics.STRATEGY, 687 category: Diagnostics.Module_Resolution_Options, 688 description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6, 689 }, 690 { 691 name: "baseUrl", 692 type: "string", 693 affectsModuleResolution: true, 694 isFilePath: true, 695 category: Diagnostics.Module_Resolution_Options, 696 description: Diagnostics.Base_directory_to_resolve_non_absolute_module_names 697 }, 698 { 699 // this option can only be specified in tsconfig.json 700 // use type = object to copy the value as-is 701 name: "paths", 702 type: "object", 703 affectsModuleResolution: true, 704 isTSConfigOnly: true, 705 category: Diagnostics.Module_Resolution_Options, 706 description: Diagnostics.A_series_of_entries_which_re_map_imports_to_lookup_locations_relative_to_the_baseUrl, 707 transpileOptionValue: undefined 708 }, 709 { 710 // this option can only be specified in tsconfig.json 711 // use type = object to copy the value as-is 712 name: "rootDirs", 713 type: "list", 714 isTSConfigOnly: true, 715 element: { 716 name: "rootDirs", 717 type: "string", 718 isFilePath: true 719 }, 720 affectsModuleResolution: true, 721 category: Diagnostics.Module_Resolution_Options, 722 description: Diagnostics.List_of_root_folders_whose_combined_content_represents_the_structure_of_the_project_at_runtime, 723 transpileOptionValue: undefined 724 }, 725 { 726 name: "typeRoots", 727 type: "list", 728 element: { 729 name: "typeRoots", 730 type: "string", 731 isFilePath: true 732 }, 733 affectsModuleResolution: true, 734 category: Diagnostics.Module_Resolution_Options, 735 description: Diagnostics.List_of_folders_to_include_type_definitions_from 736 }, 737 { 738 name: "types", 739 type: "list", 740 element: { 741 name: "types", 742 type: "string" 743 }, 744 affectsModuleResolution: true, 745 showInSimplifiedHelpView: true, 746 category: Diagnostics.Module_Resolution_Options, 747 description: Diagnostics.Type_declaration_files_to_be_included_in_compilation, 748 transpileOptionValue: undefined 749 }, 750 { 751 name: "allowSyntheticDefaultImports", 752 type: "boolean", 753 affectsSemanticDiagnostics: true, 754 category: Diagnostics.Module_Resolution_Options, 755 description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking 756 }, 757 { 758 name: "esModuleInterop", 759 type: "boolean", 760 affectsSemanticDiagnostics: true, 761 affectsEmit: true, 762 showInSimplifiedHelpView: true, 763 category: Diagnostics.Module_Resolution_Options, 764 description: Diagnostics.Enables_emit_interoperability_between_CommonJS_and_ES_Modules_via_creation_of_namespace_objects_for_all_imports_Implies_allowSyntheticDefaultImports 765 }, 766 { 767 name: "preserveSymlinks", 768 type: "boolean", 769 category: Diagnostics.Module_Resolution_Options, 770 description: Diagnostics.Do_not_resolve_the_real_path_of_symlinks, 771 }, 772 { 773 name: "allowUmdGlobalAccess", 774 type: "boolean", 775 affectsSemanticDiagnostics: true, 776 category: Diagnostics.Module_Resolution_Options, 777 description: Diagnostics.Allow_accessing_UMD_globals_from_modules, 778 }, 779 780 // Source Maps 781 { 782 name: "sourceRoot", 783 type: "string", 784 affectsEmit: true, 785 paramType: Diagnostics.LOCATION, 786 category: Diagnostics.Source_Map_Options, 787 description: Diagnostics.Specify_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations, 788 }, 789 { 790 name: "mapRoot", 791 type: "string", 792 affectsEmit: true, 793 paramType: Diagnostics.LOCATION, 794 category: Diagnostics.Source_Map_Options, 795 description: Diagnostics.Specify_the_location_where_debugger_should_locate_map_files_instead_of_generated_locations, 796 }, 797 { 798 name: "inlineSourceMap", 799 type: "boolean", 800 affectsEmit: true, 801 category: Diagnostics.Source_Map_Options, 802 description: Diagnostics.Emit_a_single_file_with_source_maps_instead_of_having_a_separate_file 803 }, 804 { 805 name: "inlineSources", 806 type: "boolean", 807 affectsEmit: true, 808 category: Diagnostics.Source_Map_Options, 809 description: Diagnostics.Emit_the_source_alongside_the_sourcemaps_within_a_single_file_requires_inlineSourceMap_or_sourceMap_to_be_set 810 }, 811 812 // Experimental 813 { 814 name: "experimentalDecorators", 815 type: "boolean", 816 affectsSemanticDiagnostics: true, 817 category: Diagnostics.Experimental_Options, 818 description: Diagnostics.Enables_experimental_support_for_ES7_decorators 819 }, 820 { 821 name: "emitDecoratorMetadata", 822 type: "boolean", 823 affectsSemanticDiagnostics: true, 824 affectsEmit: true, 825 category: Diagnostics.Experimental_Options, 826 description: Diagnostics.Enables_experimental_support_for_emitting_type_metadata_for_decorators 827 }, 828 829 // Advanced 830 { 831 name: "jsxFactory", 832 type: "string", 833 category: Diagnostics.Advanced_Options, 834 description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h 835 }, 836 { 837 name: "jsxFragmentFactory", 838 type: "string", 839 category: Diagnostics.Advanced_Options, 840 description: Diagnostics.Specify_the_JSX_fragment_factory_function_to_use_when_targeting_react_JSX_emit_with_jsxFactory_compiler_option_is_specified_e_g_Fragment 841 }, 842 { 843 name: "jsxImportSource", 844 type: "string", 845 affectsSemanticDiagnostics: true, 846 affectsEmit: true, 847 affectsModuleResolution: true, 848 category: Diagnostics.Advanced_Options, 849 description: Diagnostics.Specify_the_module_specifier_to_be_used_to_import_the_jsx_and_jsxs_factory_functions_from_eg_react 850 }, 851 { 852 name: "ets", 853 type: "object", 854 affectsSourceFile: true, 855 affectsEmit: true, 856 affectsModuleResolution: true, 857 category: Diagnostics.Advanced_Options, 858 description: Diagnostics.Unknown_build_option_0, 859 }, 860 { 861 name: "packageManagerType", 862 type: "string", 863 affectsSourceFile: true, 864 affectsEmit: true, 865 affectsModuleResolution: true, 866 category: Diagnostics.Advanced_Options, 867 description: Diagnostics.Unknown_build_option_0, 868 }, 869 { 870 name: "emitNodeModulesFiles", 871 type: "boolean", 872 category: Diagnostics.Advanced_Options, 873 description: Diagnostics.Unknown_build_option_0, 874 }, 875 { 876 name: "resolveJsonModule", 877 type: "boolean", 878 affectsModuleResolution: true, 879 category: Diagnostics.Advanced_Options, 880 description: Diagnostics.Include_modules_imported_with_json_extension 881 }, 882 883 { 884 name: "out", 885 type: "string", 886 affectsEmit: true, 887 isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files 888 // for correct behaviour, please use outFile 889 category: Diagnostics.Advanced_Options, 890 paramType: Diagnostics.FILE, 891 description: Diagnostics.Deprecated_Use_outFile_instead_Concatenate_and_emit_output_to_single_file, 892 transpileOptionValue: undefined 893 }, 894 { 895 name: "reactNamespace", 896 type: "string", 897 affectsEmit: true, 898 category: Diagnostics.Advanced_Options, 899 description: Diagnostics.Deprecated_Use_jsxFactory_instead_Specify_the_object_invoked_for_createElement_when_targeting_react_JSX_emit 900 }, 901 { 902 name: "skipDefaultLibCheck", 903 type: "boolean", 904 category: Diagnostics.Advanced_Options, 905 description: Diagnostics.Deprecated_Use_skipLibCheck_instead_Skip_type_checking_of_default_library_declaration_files 906 }, 907 { 908 name: "charset", 909 type: "string", 910 category: Diagnostics.Advanced_Options, 911 description: Diagnostics.The_character_set_of_the_input_files 912 }, 913 { 914 name: "emitBOM", 915 type: "boolean", 916 affectsEmit: true, 917 category: Diagnostics.Advanced_Options, 918 description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files 919 }, 920 { 921 name: "newLine", 922 type: new Map(getEntries({ 923 crlf: NewLineKind.CarriageReturnLineFeed, 924 lf: NewLineKind.LineFeed 925 })), 926 affectsEmit: true, 927 paramType: Diagnostics.NEWLINE, 928 category: Diagnostics.Advanced_Options, 929 description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix, 930 }, 931 { 932 name: "noErrorTruncation", 933 type: "boolean", 934 affectsSemanticDiagnostics: true, 935 category: Diagnostics.Advanced_Options, 936 description: Diagnostics.Do_not_truncate_error_messages 937 }, 938 { 939 name: "noLib", 940 type: "boolean", 941 affectsModuleResolution: true, 942 category: Diagnostics.Advanced_Options, 943 description: Diagnostics.Do_not_include_the_default_library_file_lib_d_ts, 944 // We are not returning a sourceFile for lib file when asked by the program, 945 // so pass --noLib to avoid reporting a file not found error. 946 transpileOptionValue: true 947 }, 948 { 949 name: "noResolve", 950 type: "boolean", 951 affectsModuleResolution: true, 952 category: Diagnostics.Advanced_Options, 953 description: Diagnostics.Do_not_add_triple_slash_references_or_imported_modules_to_the_list_of_compiled_files, 954 // We are not doing a full typecheck, we are not resolving the whole context, 955 // so pass --noResolve to avoid reporting missing file errors. 956 transpileOptionValue: true 957 }, 958 { 959 name: "stripInternal", 960 type: "boolean", 961 affectsEmit: true, 962 category: Diagnostics.Advanced_Options, 963 description: Diagnostics.Do_not_emit_declarations_for_code_that_has_an_internal_annotation, 964 }, 965 { 966 name: "disableSizeLimit", 967 type: "boolean", 968 affectsSourceFile: true, 969 category: Diagnostics.Advanced_Options, 970 description: Diagnostics.Disable_size_limitations_on_JavaScript_projects 971 }, 972 { 973 name: "disableSourceOfProjectReferenceRedirect", 974 type: "boolean", 975 isTSConfigOnly: true, 976 category: Diagnostics.Advanced_Options, 977 description: Diagnostics.Disable_use_of_source_files_instead_of_declaration_files_from_referenced_projects 978 }, 979 { 980 name: "disableSolutionSearching", 981 type: "boolean", 982 isTSConfigOnly: true, 983 category: Diagnostics.Advanced_Options, 984 description: Diagnostics.Disable_solution_searching_for_this_project 985 }, 986 { 987 name: "disableReferencedProjectLoad", 988 type: "boolean", 989 isTSConfigOnly: true, 990 category: Diagnostics.Advanced_Options, 991 description: Diagnostics.Disable_loading_referenced_projects 992 }, 993 { 994 name: "noImplicitUseStrict", 995 type: "boolean", 996 affectsSemanticDiagnostics: true, 997 category: Diagnostics.Advanced_Options, 998 description: Diagnostics.Do_not_emit_use_strict_directives_in_module_output 999 }, 1000 { 1001 name: "noEmitHelpers", 1002 type: "boolean", 1003 affectsEmit: true, 1004 category: Diagnostics.Advanced_Options, 1005 description: Diagnostics.Do_not_generate_custom_helper_functions_like_extends_in_compiled_output 1006 }, 1007 { 1008 name: "noEmitOnError", 1009 type: "boolean", 1010 affectsEmit: true, 1011 category: Diagnostics.Advanced_Options, 1012 description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported, 1013 transpileOptionValue: undefined 1014 }, 1015 { 1016 name: "preserveConstEnums", 1017 type: "boolean", 1018 affectsEmit: true, 1019 category: Diagnostics.Advanced_Options, 1020 description: Diagnostics.Do_not_erase_const_enum_declarations_in_generated_code 1021 }, 1022 { 1023 name: "declarationDir", 1024 type: "string", 1025 affectsEmit: true, 1026 isFilePath: true, 1027 paramType: Diagnostics.DIRECTORY, 1028 category: Diagnostics.Advanced_Options, 1029 description: Diagnostics.Output_directory_for_generated_declaration_files, 1030 transpileOptionValue: undefined 1031 }, 1032 { 1033 name: "skipLibCheck", 1034 type: "boolean", 1035 category: Diagnostics.Advanced_Options, 1036 description: Diagnostics.Skip_type_checking_of_declaration_files, 1037 }, 1038 { 1039 name: "allowUnusedLabels", 1040 type: "boolean", 1041 affectsBindDiagnostics: true, 1042 affectsSemanticDiagnostics: true, 1043 category: Diagnostics.Advanced_Options, 1044 description: Diagnostics.Do_not_report_errors_on_unused_labels 1045 }, 1046 { 1047 name: "allowUnreachableCode", 1048 type: "boolean", 1049 affectsBindDiagnostics: true, 1050 affectsSemanticDiagnostics: true, 1051 category: Diagnostics.Advanced_Options, 1052 description: Diagnostics.Do_not_report_errors_on_unreachable_code 1053 }, 1054 { 1055 name: "suppressExcessPropertyErrors", 1056 type: "boolean", 1057 affectsSemanticDiagnostics: true, 1058 category: Diagnostics.Advanced_Options, 1059 description: Diagnostics.Suppress_excess_property_checks_for_object_literals, 1060 }, 1061 { 1062 name: "suppressImplicitAnyIndexErrors", 1063 type: "boolean", 1064 affectsSemanticDiagnostics: true, 1065 category: Diagnostics.Advanced_Options, 1066 description: Diagnostics.Suppress_noImplicitAny_errors_for_indexing_objects_lacking_index_signatures, 1067 }, 1068 { 1069 name: "forceConsistentCasingInFileNames", 1070 type: "boolean", 1071 affectsModuleResolution: true, 1072 category: Diagnostics.Advanced_Options, 1073 description: Diagnostics.Disallow_inconsistently_cased_references_to_the_same_file 1074 }, 1075 { 1076 name: "maxNodeModuleJsDepth", 1077 type: "number", 1078 affectsModuleResolution: true, 1079 category: Diagnostics.Advanced_Options, 1080 description: Diagnostics.The_maximum_dependency_depth_to_search_under_node_modules_and_load_JavaScript_files 1081 }, 1082 { 1083 name: "noStrictGenericChecks", 1084 type: "boolean", 1085 affectsSemanticDiagnostics: true, 1086 category: Diagnostics.Advanced_Options, 1087 description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types, 1088 }, 1089 { 1090 name: "useDefineForClassFields", 1091 type: "boolean", 1092 affectsSemanticDiagnostics: true, 1093 affectsEmit: true, 1094 category: Diagnostics.Advanced_Options, 1095 description: Diagnostics.Emit_class_fields_with_Define_instead_of_Set, 1096 }, 1097 1098 { 1099 name: "keyofStringsOnly", 1100 type: "boolean", 1101 category: Diagnostics.Advanced_Options, 1102 description: Diagnostics.Resolve_keyof_to_string_valued_property_names_only_no_numbers_or_symbols, 1103 }, 1104 { 1105 // A list of plugins to load in the language service 1106 name: "plugins", 1107 type: "list", 1108 isTSConfigOnly: true, 1109 element: { 1110 name: "plugin", 1111 type: "object" 1112 }, 1113 description: Diagnostics.List_of_language_service_plugins 1114 }, 1115 ]; 1116 1117 /* @internal */ 1118 export const semanticDiagnosticsOptionDeclarations: readonly CommandLineOption[] = 1119 optionDeclarations.filter(option => !!option.affectsSemanticDiagnostics); 1120 1121 /* @internal */ 1122 export const affectsEmitOptionDeclarations: readonly CommandLineOption[] = 1123 optionDeclarations.filter(option => !!option.affectsEmit); 1124 1125 /* @internal */ 1126 export const moduleResolutionOptionDeclarations: readonly CommandLineOption[] = 1127 optionDeclarations.filter(option => !!option.affectsModuleResolution); 1128 1129 /* @internal */ 1130 export const sourceFileAffectingCompilerOptions: readonly CommandLineOption[] = optionDeclarations.filter(option => 1131 !!option.affectsSourceFile || !!option.affectsModuleResolution || !!option.affectsBindDiagnostics); 1132 1133 /* @internal */ 1134 export const transpileOptionValueCompilerOptions: readonly CommandLineOption[] = optionDeclarations.filter(option => 1135 hasProperty(option, "transpileOptionValue")); 1136 1137 /* @internal */ 1138 export const buildOpts: CommandLineOption[] = [ 1139 ...commonOptionsWithBuild, 1140 { 1141 name: "verbose", 1142 shortName: "v", 1143 category: Diagnostics.Command_line_Options, 1144 description: Diagnostics.Enable_verbose_logging, 1145 type: "boolean" 1146 }, 1147 { 1148 name: "dry", 1149 shortName: "d", 1150 category: Diagnostics.Command_line_Options, 1151 description: Diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean, 1152 type: "boolean" 1153 }, 1154 { 1155 name: "force", 1156 shortName: "f", 1157 category: Diagnostics.Command_line_Options, 1158 description: Diagnostics.Build_all_projects_including_those_that_appear_to_be_up_to_date, 1159 type: "boolean" 1160 }, 1161 { 1162 name: "clean", 1163 category: Diagnostics.Command_line_Options, 1164 description: Diagnostics.Delete_the_outputs_of_all_projects, 1165 type: "boolean" 1166 } 1167 ]; 1168 1169 /* @internal */ 1170 export const typeAcquisitionDeclarations: CommandLineOption[] = [ 1171 { 1172 /* @deprecated typingOptions.enableAutoDiscovery 1173 * Use typeAcquisition.enable instead. 1174 */ 1175 name: "enableAutoDiscovery", 1176 type: "boolean", 1177 }, 1178 { 1179 name: "enable", 1180 type: "boolean", 1181 }, 1182 { 1183 name: "include", 1184 type: "list", 1185 element: { 1186 name: "include", 1187 type: "string" 1188 } 1189 }, 1190 { 1191 name: "exclude", 1192 type: "list", 1193 element: { 1194 name: "exclude", 1195 type: "string" 1196 } 1197 }, 1198 { 1199 name: "disableFilenameBasedTypeAcquisition", 1200 type: "boolean", 1201 }, 1202 ]; 1203 1204 /* @internal */ 1205 export interface OptionsNameMap { 1206 optionsNameMap: ESMap<string, CommandLineOption>; 1207 shortOptionNames: ESMap<string, string>; 1208 } 1209 1210 /*@internal*/ 1211 export function createOptionNameMap(optionDeclarations: readonly CommandLineOption[]): OptionsNameMap { 1212 const optionsNameMap = new Map<string, CommandLineOption>(); 1213 const shortOptionNames = new Map<string, string>(); 1214 forEach(optionDeclarations, option => { 1215 optionsNameMap.set(option.name.toLowerCase(), option); 1216 if (option.shortName) { 1217 shortOptionNames.set(option.shortName, option.name); 1218 } 1219 }); 1220 1221 return { optionsNameMap, shortOptionNames }; 1222 } 1223 1224 let optionsNameMapCache: OptionsNameMap; 1225 1226 /* @internal */ 1227 export function getOptionsNameMap(): OptionsNameMap { 1228 return optionsNameMapCache || (optionsNameMapCache = createOptionNameMap(optionDeclarations)); 1229 } 1230 1231 /* @internal */ 1232 export const defaultInitCompilerOptions: CompilerOptions = { 1233 module: ModuleKind.CommonJS, 1234 target: ScriptTarget.ES5, 1235 strict: true, 1236 esModuleInterop: true, 1237 forceConsistentCasingInFileNames: true, 1238 skipLibCheck: true 1239 }; 1240 1241 /* @internal */ 1242 export function convertEnableAutoDiscoveryToEnable(typeAcquisition: TypeAcquisition): TypeAcquisition { 1243 // Convert deprecated typingOptions.enableAutoDiscovery to typeAcquisition.enable 1244 if (typeAcquisition && typeAcquisition.enableAutoDiscovery !== undefined && typeAcquisition.enable === undefined) { 1245 return { 1246 enable: typeAcquisition.enableAutoDiscovery, 1247 include: typeAcquisition.include || [], 1248 exclude: typeAcquisition.exclude || [] 1249 }; 1250 } 1251 return typeAcquisition; 1252 } 1253 1254 /* @internal */ 1255 export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { 1256 return createDiagnosticForInvalidCustomType(opt, createCompilerDiagnostic); 1257 } 1258 1259 function createDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType, createDiagnostic: (message: DiagnosticMessage, arg0: string, arg1: string) => Diagnostic): Diagnostic { 1260 const namesOfType = arrayFrom(opt.type.keys()).map(key => `'${key}'`).join(", "); 1261 return createDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); 1262 } 1263 1264 /* @internal */ 1265 export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) { 1266 return convertJsonOptionOfCustomType(opt, trimString(value || ""), errors); 1267 } 1268 1269 /* @internal */ 1270 export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Push<Diagnostic>): (string | number)[] | undefined { 1271 value = trimString(value); 1272 if (startsWith(value, "-")) { 1273 return undefined; 1274 } 1275 if (value === "") { 1276 return []; 1277 } 1278 const values = value.split(","); 1279 switch (opt.element.type) { 1280 case "number": 1281 return mapDefined(values, v => validateJsonOptionValue(opt.element, parseInt(v), errors)); 1282 case "string": 1283 return mapDefined(values, v => validateJsonOptionValue(opt.element, v || "", errors)); 1284 default: 1285 return mapDefined(values, v => parseCustomTypeOption(<CommandLineOptionOfCustomType>opt.element, v, errors)); 1286 } 1287 } 1288 1289 /*@internal*/ 1290 export interface OptionsBase { 1291 [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; 1292 } 1293 1294 /*@internal*/ 1295 export interface ParseCommandLineWorkerDiagnostics extends DidYouMeanOptionsDiagnostics { 1296 getOptionsNameMap: () => OptionsNameMap; 1297 optionTypeMismatchDiagnostic: DiagnosticMessage; 1298 } 1299 1300 function getOptionName(option: CommandLineOption) { 1301 return option.name; 1302 } 1303 1304 function createUnknownOptionError( 1305 unknownOption: string, 1306 diagnostics: DidYouMeanOptionsDiagnostics, 1307 createDiagnostics: (message: DiagnosticMessage, arg0: string, arg1?: string) => Diagnostic, 1308 unknownOptionErrorText?: string 1309 ) { 1310 const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName); 1311 return possibleOption ? 1312 createDiagnostics(diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : 1313 createDiagnostics(diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption); 1314 } 1315 1316 /*@internal*/ 1317 export function parseCommandLineWorker( 1318 diagnostics: ParseCommandLineWorkerDiagnostics, 1319 commandLine: readonly string[], 1320 readFile?: (path: string) => string | undefined) { 1321 const options = {} as OptionsBase; 1322 let watchOptions: WatchOptions | undefined; 1323 const fileNames: string[] = []; 1324 const errors: Diagnostic[] = []; 1325 1326 parseStrings(commandLine); 1327 return { 1328 options, 1329 watchOptions, 1330 fileNames, 1331 errors 1332 }; 1333 1334 function parseStrings(args: readonly string[]) { 1335 let i = 0; 1336 while (i < args.length) { 1337 const s = args[i]; 1338 i++; 1339 if (s.charCodeAt(0) === CharacterCodes.at) { 1340 parseResponseFile(s.slice(1)); 1341 } 1342 else if (s.charCodeAt(0) === CharacterCodes.minus) { 1343 const inputOptionName = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1); 1344 const opt = getOptionDeclarationFromName(diagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true); 1345 if (opt) { 1346 i = parseOptionValue(args, i, diagnostics, opt, options, errors); 1347 } 1348 else { 1349 const watchOpt = getOptionDeclarationFromName(watchOptionsDidYouMeanDiagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true); 1350 if (watchOpt) { 1351 i = parseOptionValue(args, i, watchOptionsDidYouMeanDiagnostics, watchOpt, watchOptions || (watchOptions = {}), errors); 1352 } 1353 else { 1354 errors.push(createUnknownOptionError(inputOptionName, diagnostics, createCompilerDiagnostic, s)); 1355 } 1356 } 1357 } 1358 else { 1359 fileNames.push(s); 1360 } 1361 } 1362 } 1363 1364 function parseResponseFile(fileName: string) { 1365 const text = tryReadFile(fileName, readFile || (fileName => sys.readFile(fileName))); 1366 if (!isString(text)) { 1367 errors.push(text); 1368 return; 1369 } 1370 1371 const args: string[] = []; 1372 let pos = 0; 1373 while (true) { 1374 while (pos < text.length && text.charCodeAt(pos) <= CharacterCodes.space) pos++; 1375 if (pos >= text.length) break; 1376 const start = pos; 1377 if (text.charCodeAt(start) === CharacterCodes.doubleQuote) { 1378 pos++; 1379 while (pos < text.length && text.charCodeAt(pos) !== CharacterCodes.doubleQuote) pos++; 1380 if (pos < text.length) { 1381 args.push(text.substring(start + 1, pos)); 1382 pos++; 1383 } 1384 else { 1385 errors.push(createCompilerDiagnostic(Diagnostics.Unterminated_quoted_string_in_response_file_0, fileName)); 1386 } 1387 } 1388 else { 1389 while (text.charCodeAt(pos) > CharacterCodes.space) pos++; 1390 args.push(text.substring(start, pos)); 1391 } 1392 } 1393 parseStrings(args); 1394 } 1395 } 1396 1397 function parseOptionValue( 1398 args: readonly string[], 1399 i: number, 1400 diagnostics: ParseCommandLineWorkerDiagnostics, 1401 opt: CommandLineOption, 1402 options: OptionsBase, 1403 errors: Diagnostic[] 1404 ) { 1405 if (opt.isTSConfigOnly) { 1406 const optValue = args[i]; 1407 if (optValue === "null") { 1408 options[opt.name] = undefined; 1409 i++; 1410 } 1411 else if (opt.type === "boolean") { 1412 if (optValue === "false") { 1413 options[opt.name] = validateJsonOptionValue(opt, /*value*/ false, errors); 1414 i++; 1415 } 1416 else { 1417 if (optValue === "true") i++; 1418 errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_null_on_command_line, opt.name)); 1419 } 1420 } 1421 else { 1422 errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_null_on_command_line, opt.name)); 1423 if (optValue && !startsWith(optValue, "-")) i++; 1424 } 1425 } 1426 else { 1427 // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). 1428 if (!args[i] && opt.type !== "boolean") { 1429 errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt))); 1430 } 1431 1432 if (args[i] !== "null") { 1433 switch (opt.type) { 1434 case "number": 1435 options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors); 1436 i++; 1437 break; 1438 case "boolean": 1439 // boolean flag has optional value true, false, others 1440 const optValue = args[i]; 1441 options[opt.name] = validateJsonOptionValue(opt, optValue !== "false", errors); 1442 // consume next argument as boolean flag value 1443 if (optValue === "false" || optValue === "true") { 1444 i++; 1445 } 1446 break; 1447 case "string": 1448 options[opt.name] = validateJsonOptionValue(opt, args[i] || "", errors); 1449 i++; 1450 break; 1451 case "list": 1452 const result = parseListTypeOption(opt, args[i], errors); 1453 options[opt.name] = result || []; 1454 if (result) { 1455 i++; 1456 } 1457 break; 1458 // If not a primitive, the possible types are specified in what is effectively a map of options. 1459 default: 1460 options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors); 1461 i++; 1462 break; 1463 } 1464 } 1465 else { 1466 options[opt.name] = undefined; 1467 i++; 1468 } 1469 } 1470 return i; 1471 } 1472 1473 /*@internal*/ 1474 export const compilerOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1475 getOptionsNameMap, 1476 optionDeclarations, 1477 unknownOptionDiagnostic: Diagnostics.Unknown_compiler_option_0, 1478 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1, 1479 optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument 1480 }; 1481 export function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine { 1482 return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile); 1483 } 1484 1485 /** @internal */ 1486 export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined { 1487 return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort); 1488 } 1489 1490 function getOptionDeclarationFromName(getOptionNameMap: () => OptionsNameMap, optionName: string, allowShort = false): CommandLineOption | undefined { 1491 optionName = optionName.toLowerCase(); 1492 const { optionsNameMap, shortOptionNames } = getOptionNameMap(); 1493 // Try to translate short option names to their full equivalents. 1494 if (allowShort) { 1495 const short = shortOptionNames.get(optionName); 1496 if (short !== undefined) { 1497 optionName = short; 1498 } 1499 } 1500 return optionsNameMap.get(optionName); 1501 } 1502 1503 /*@internal*/ 1504 export interface ParsedBuildCommand { 1505 buildOptions: BuildOptions; 1506 watchOptions: WatchOptions | undefined; 1507 projects: string[]; 1508 errors: Diagnostic[]; 1509 } 1510 1511 let buildOptionsNameMapCache: OptionsNameMap; 1512 function getBuildOptionsNameMap(): OptionsNameMap { 1513 return buildOptionsNameMapCache || (buildOptionsNameMapCache = createOptionNameMap(buildOpts)); 1514 } 1515 1516 const buildOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1517 getOptionsNameMap: getBuildOptionsNameMap, 1518 optionDeclarations: buildOpts, 1519 unknownOptionDiagnostic: Diagnostics.Unknown_build_option_0, 1520 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_build_option_0_Did_you_mean_1, 1521 optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1 1522 }; 1523 1524 /*@internal*/ 1525 export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand { 1526 const { options, watchOptions, fileNames: projects, errors } = parseCommandLineWorker( 1527 buildOptionsDidYouMeanDiagnostics, 1528 args 1529 ); 1530 const buildOptions = options as BuildOptions; 1531 1532 if (projects.length === 0) { 1533 // tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ." 1534 projects.push("."); 1535 } 1536 1537 // Nonsensical combinations 1538 if (buildOptions.clean && buildOptions.force) { 1539 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force")); 1540 } 1541 if (buildOptions.clean && buildOptions.verbose) { 1542 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose")); 1543 } 1544 if (buildOptions.clean && buildOptions.watch) { 1545 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch")); 1546 } 1547 if (buildOptions.watch && buildOptions.dry) { 1548 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry")); 1549 } 1550 1551 return { buildOptions, watchOptions, projects, errors }; 1552 } 1553 1554 /* @internal */ 1555 export function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string { 1556 const diagnostic = createCompilerDiagnostic.apply(undefined, arguments); 1557 return <string>diagnostic.messageText; 1558 } 1559 1560 export type DiagnosticReporter = (diagnostic: Diagnostic) => void; 1561 /** 1562 * Reports config file diagnostics 1563 */ 1564 export interface ConfigFileDiagnosticsReporter { 1565 /** 1566 * Reports unrecoverable error when parsing config file 1567 */ 1568 onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; 1569 } 1570 1571 /** 1572 * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors 1573 */ 1574 export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { 1575 getCurrentDirectory(): string; 1576 } 1577 1578 /** 1579 * Reads the config file, reports errors if any and exits if the config file cannot be found 1580 */ 1581 export function getParsedCommandLineOfConfigFile( 1582 configFileName: string, 1583 optionsToExtend: CompilerOptions, 1584 host: ParseConfigFileHost, 1585 extendedConfigCache?: Map<ExtendedConfigCacheEntry>, 1586 watchOptionsToExtend?: WatchOptions, 1587 extraFileExtensions?: readonly FileExtensionInfo[], 1588 ): ParsedCommandLine | undefined { 1589 const configFileText = tryReadFile(configFileName, fileName => host.readFile(fileName)); 1590 if (!isString(configFileText)) { 1591 host.onUnRecoverableConfigFileDiagnostic(configFileText); 1592 return undefined; 1593 } 1594 1595 const result = parseJsonText(configFileName, configFileText); 1596 const cwd = host.getCurrentDirectory(); 1597 result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames)); 1598 result.resolvedPath = result.path; 1599 result.originalFileName = result.fileName; 1600 return parseJsonSourceFileConfigFileContent( 1601 result, 1602 host, 1603 getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), 1604 optionsToExtend, 1605 getNormalizedAbsolutePath(configFileName, cwd), 1606 /*resolutionStack*/ undefined, 1607 extraFileExtensions, 1608 extendedConfigCache, 1609 watchOptionsToExtend 1610 ); 1611 } 1612 1613 /** 1614 * Read tsconfig.json file 1615 * @param fileName The path to the config file 1616 */ 1617 export function readConfigFile(fileName: string, readFile: (path: string) => string | undefined): { config?: any; error?: Diagnostic } { 1618 const textOrDiagnostic = tryReadFile(fileName, readFile); 1619 return isString(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic }; 1620 } 1621 1622 /** 1623 * Parse the text of the tsconfig.json file 1624 * @param fileName The path to the config file 1625 * @param jsonText The text of the config file 1626 */ 1627 export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { 1628 const jsonSourceFile = parseJsonText(fileName, jsonText); 1629 return { 1630 config: convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics), 1631 error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined 1632 }; 1633 } 1634 1635 /** 1636 * Read tsconfig.json file 1637 * @param fileName The path to the config file 1638 */ 1639 export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile { 1640 const textOrDiagnostic = tryReadFile(fileName, readFile); 1641 return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : <TsConfigSourceFile>{ fileName, parseDiagnostics: [textOrDiagnostic] }; 1642 } 1643 1644 /*@internal*/ 1645 export function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { 1646 let text: string | undefined; 1647 try { 1648 text = readFile(fileName); 1649 } 1650 catch (e) { 1651 return createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message); 1652 } 1653 return text === undefined ? createCompilerDiagnostic(Diagnostics.Cannot_read_file_0, fileName) : text; 1654 } 1655 1656 function commandLineOptionsToMap(options: readonly CommandLineOption[]) { 1657 return arrayToMap(options, getOptionName); 1658 } 1659 1660 const typeAcquisitionDidYouMeanDiagnostics: DidYouMeanOptionsDiagnostics = { 1661 optionDeclarations: typeAcquisitionDeclarations, 1662 unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0, 1663 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1, 1664 }; 1665 1666 let watchOptionsNameMapCache: OptionsNameMap; 1667 function getWatchOptionsNameMap(): OptionsNameMap { 1668 return watchOptionsNameMapCache || (watchOptionsNameMapCache = createOptionNameMap(optionsForWatch)); 1669 } 1670 const watchOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1671 getOptionsNameMap: getWatchOptionsNameMap, 1672 optionDeclarations: optionsForWatch, 1673 unknownOptionDiagnostic: Diagnostics.Unknown_watch_option_0, 1674 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_watch_option_0_Did_you_mean_1, 1675 optionTypeMismatchDiagnostic: Diagnostics.Watch_option_0_requires_a_value_of_type_1 1676 }; 1677 1678 let commandLineCompilerOptionsMapCache: ESMap<string, CommandLineOption>; 1679 function getCommandLineCompilerOptionsMap() { 1680 return commandLineCompilerOptionsMapCache || (commandLineCompilerOptionsMapCache = commandLineOptionsToMap(optionDeclarations)); 1681 } 1682 let commandLineWatchOptionsMapCache: ESMap<string, CommandLineOption>; 1683 function getCommandLineWatchOptionsMap() { 1684 return commandLineWatchOptionsMapCache || (commandLineWatchOptionsMapCache = commandLineOptionsToMap(optionsForWatch)); 1685 } 1686 let commandLineTypeAcquisitionMapCache: ESMap<string, CommandLineOption>; 1687 function getCommandLineTypeAcquisitionMap() { 1688 return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations)); 1689 } 1690 1691 let _tsconfigRootOptions: TsConfigOnlyOption; 1692 function getTsconfigRootOptionsMap() { 1693 if (_tsconfigRootOptions === undefined) { 1694 _tsconfigRootOptions = { 1695 name: undefined!, // should never be needed since this is root 1696 type: "object", 1697 elementOptions: commandLineOptionsToMap([ 1698 { 1699 name: "compilerOptions", 1700 type: "object", 1701 elementOptions: getCommandLineCompilerOptionsMap(), 1702 extraKeyDiagnostics: compilerOptionsDidYouMeanDiagnostics, 1703 }, 1704 { 1705 name: "watchOptions", 1706 type: "object", 1707 elementOptions: getCommandLineWatchOptionsMap(), 1708 extraKeyDiagnostics: watchOptionsDidYouMeanDiagnostics, 1709 }, 1710 { 1711 name: "typingOptions", 1712 type: "object", 1713 elementOptions: getCommandLineTypeAcquisitionMap(), 1714 extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics, 1715 }, 1716 { 1717 name: "typeAcquisition", 1718 type: "object", 1719 elementOptions: getCommandLineTypeAcquisitionMap(), 1720 extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics 1721 }, 1722 { 1723 name: "extends", 1724 type: "string" 1725 }, 1726 { 1727 name: "references", 1728 type: "list", 1729 element: { 1730 name: "references", 1731 type: "object" 1732 } 1733 }, 1734 { 1735 name: "files", 1736 type: "list", 1737 element: { 1738 name: "files", 1739 type: "string" 1740 } 1741 }, 1742 { 1743 name: "include", 1744 type: "list", 1745 element: { 1746 name: "include", 1747 type: "string" 1748 } 1749 }, 1750 { 1751 name: "exclude", 1752 type: "list", 1753 element: { 1754 name: "exclude", 1755 type: "string" 1756 } 1757 }, 1758 compileOnSaveCommandLineOption 1759 ]) 1760 }; 1761 } 1762 return _tsconfigRootOptions; 1763 } 1764 1765 /*@internal*/ 1766 interface JsonConversionNotifier { 1767 /** 1768 * Notifies parent option object is being set with the optionKey and a valid optionValue 1769 * Currently it notifies only if there is element with type object (parentOption) and 1770 * has element's option declarations map associated with it 1771 * @param parentOption parent option name in which the option and value are being set 1772 * @param option option declaration which is being set with the value 1773 * @param value value of the option 1774 */ 1775 onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue): void; 1776 /** 1777 * Notify when valid root key value option is being set 1778 * @param key option key 1779 * @param keyNode node corresponding to node in the source file 1780 * @param value computed value of the key 1781 * @param ValueNode node corresponding to value in the source file 1782 */ 1783 onSetValidOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void; 1784 /** 1785 * Notify when unknown root key value option is being set 1786 * @param key option key 1787 * @param keyNode node corresponding to node in the source file 1788 * @param value computed value of the key 1789 * @param ValueNode node corresponding to value in the source file 1790 */ 1791 onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void; 1792 } 1793 1794 /** 1795 * Convert the json syntax tree into the json value 1796 */ 1797 export function convertToObject(sourceFile: JsonSourceFile, errors: Push<Diagnostic>): any { 1798 return convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); 1799 } 1800 1801 /** 1802 * Convert the json syntax tree into the json value and report errors 1803 * This returns the json value (apart from checking errors) only if returnValue provided is true. 1804 * Otherwise it just checks the errors and returns undefined 1805 */ 1806 /*@internal*/ 1807 export function convertToObjectWorker( 1808 sourceFile: JsonSourceFile, 1809 errors: Push<Diagnostic>, 1810 returnValue: boolean, 1811 knownRootOptions: CommandLineOption | undefined, 1812 jsonConversionNotifier: JsonConversionNotifier | undefined): any { 1813 if (!sourceFile.statements.length) { 1814 return returnValue ? {} : undefined; 1815 } 1816 1817 return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); 1818 1819 function isRootOptionMap(knownOptions: ESMap<string, CommandLineOption> | undefined) { 1820 return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; 1821 } 1822 1823 function convertObjectLiteralExpressionToJson( 1824 node: ObjectLiteralExpression, 1825 knownOptions: ESMap<string, CommandLineOption> | undefined, 1826 extraKeyDiagnostics: DidYouMeanOptionsDiagnostics | undefined, 1827 parentOption: string | undefined 1828 ): any { 1829 const result: any = returnValue ? {} : undefined; 1830 for (const element of node.properties) { 1831 if (element.kind !== SyntaxKind.PropertyAssignment) { 1832 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element, Diagnostics.Property_assignment_expected)); 1833 continue; 1834 } 1835 1836 if (element.questionToken) { 1837 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.questionToken, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?")); 1838 } 1839 if (!isDoubleQuotedString(element.name)) { 1840 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, Diagnostics.String_literal_with_double_quotes_expected)); 1841 } 1842 1843 const textOfKey = isComputedNonLiteralName(element.name) ? undefined : getTextOfPropertyName(element.name); 1844 const keyText = textOfKey && unescapeLeadingUnderscores(textOfKey); 1845 const option = keyText && knownOptions ? knownOptions.get(keyText) : undefined; 1846 if (keyText && extraKeyDiagnostics && !option) { 1847 if (knownOptions) { 1848 errors.push(createUnknownOptionError( 1849 keyText, 1850 extraKeyDiagnostics, 1851 (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, element.name, message, arg0, arg1) 1852 )); 1853 } 1854 else { 1855 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnostics.unknownOptionDiagnostic, keyText)); 1856 } 1857 } 1858 const value = convertPropertyValueToJson(element.initializer, option); 1859 if (typeof keyText !== "undefined") { 1860 if (returnValue) { 1861 result[keyText] = value; 1862 } 1863 // Notify key value set, if user asked for it 1864 if (jsonConversionNotifier && 1865 // Current callbacks are only on known parent option or if we are setting values in the root 1866 (parentOption || isRootOptionMap(knownOptions))) { 1867 const isValidOptionValue = isCompilerOptionsValue(option, value); 1868 if (parentOption) { 1869 if (isValidOptionValue) { 1870 // Notify option set in the parent if its a valid option value 1871 jsonConversionNotifier.onSetValidOptionKeyValueInParent(parentOption, option!, value); 1872 } 1873 } 1874 else if (isRootOptionMap(knownOptions)) { 1875 if (isValidOptionValue) { 1876 // Notify about the valid root key value being set 1877 jsonConversionNotifier.onSetValidOptionKeyValueInRoot(keyText, element.name, value, element.initializer); 1878 } 1879 else if (!option) { 1880 // Notify about the unknown root key value being set 1881 jsonConversionNotifier.onSetUnknownOptionKeyValueInRoot(keyText, element.name, value, element.initializer); 1882 } 1883 } 1884 } 1885 } 1886 } 1887 return result; 1888 } 1889 1890 function convertArrayLiteralExpressionToJson( 1891 elements: NodeArray<Expression>, 1892 elementOption: CommandLineOption | undefined 1893 ) { 1894 if (!returnValue) { 1895 elements.forEach(element => convertPropertyValueToJson(element, elementOption)); 1896 return undefined; 1897 } 1898 1899 // Filter out invalid values 1900 return filter(elements.map(element => convertPropertyValueToJson(element, elementOption)), v => v !== undefined); 1901 } 1902 1903 function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption | undefined): any { 1904 let invalidReported: boolean | undefined; 1905 switch (valueExpression.kind) { 1906 case SyntaxKind.TrueKeyword: 1907 reportInvalidOptionValue(option && option.type !== "boolean"); 1908 return validateValue(/*value*/ true); 1909 1910 case SyntaxKind.FalseKeyword: 1911 reportInvalidOptionValue(option && option.type !== "boolean"); 1912 return validateValue(/*value*/ false); 1913 1914 case SyntaxKind.NullKeyword: 1915 reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for 1916 return validateValue(/*value*/ null); // eslint-disable-line no-null/no-null 1917 1918 case SyntaxKind.StringLiteral: 1919 if (!isDoubleQuotedString(valueExpression)) { 1920 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.String_literal_with_double_quotes_expected)); 1921 } 1922 reportInvalidOptionValue(option && (isString(option.type) && option.type !== "string")); 1923 const text = (<StringLiteral>valueExpression).text; 1924 if (option && !isString(option.type)) { 1925 const customOption = <CommandLineOptionOfCustomType>option; 1926 // Validate custom option type 1927 if (!customOption.type.has(text.toLowerCase())) { 1928 errors.push( 1929 createDiagnosticForInvalidCustomType( 1930 customOption, 1931 (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1) 1932 ) 1933 ); 1934 invalidReported = true; 1935 } 1936 } 1937 return validateValue(text); 1938 1939 case SyntaxKind.NumericLiteral: 1940 reportInvalidOptionValue(option && option.type !== "number"); 1941 return validateValue(Number((<NumericLiteral>valueExpression).text)); 1942 1943 case SyntaxKind.PrefixUnaryExpression: 1944 if ((<PrefixUnaryExpression>valueExpression).operator !== SyntaxKind.MinusToken || (<PrefixUnaryExpression>valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { 1945 break; // not valid JSON syntax 1946 } 1947 reportInvalidOptionValue(option && option.type !== "number"); 1948 return validateValue(-Number((<NumericLiteral>(<PrefixUnaryExpression>valueExpression).operand).text)); 1949 1950 case SyntaxKind.ObjectLiteralExpression: 1951 reportInvalidOptionValue(option && option.type !== "object"); 1952 const objectLiteralExpression = <ObjectLiteralExpression>valueExpression; 1953 1954 // Currently having element option declaration in the tsconfig with type "object" 1955 // determines if it needs onSetValidOptionKeyValueInParent callback or not 1956 // At moment there are only "compilerOptions", "typeAcquisition" and "typingOptions" 1957 // that satifies it and need it to modify options set in them (for normalizing file paths) 1958 // vs what we set in the json 1959 // If need arises, we can modify this interface and callbacks as needed 1960 if (option) { 1961 const { elementOptions, extraKeyDiagnostics, name: optionName } = <TsConfigOnlyOption>option; 1962 return validateValue(convertObjectLiteralExpressionToJson(objectLiteralExpression, 1963 elementOptions, extraKeyDiagnostics, optionName)); 1964 } 1965 else { 1966 return validateValue(convertObjectLiteralExpressionToJson( 1967 objectLiteralExpression, /* knownOptions*/ undefined, 1968 /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined)); 1969 } 1970 1971 case SyntaxKind.ArrayLiteralExpression: 1972 reportInvalidOptionValue(option && option.type !== "list"); 1973 return validateValue(convertArrayLiteralExpressionToJson( 1974 (<ArrayLiteralExpression>valueExpression).elements, 1975 option && (<CommandLineOptionOfListType>option).element)); 1976 } 1977 1978 // Not in expected format 1979 if (option) { 1980 reportInvalidOptionValue(/*isError*/ true); 1981 } 1982 else { 1983 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal)); 1984 } 1985 1986 return undefined; 1987 1988 function validateValue(value: CompilerOptionsValue) { 1989 if (!invalidReported) { 1990 const diagnostic = option?.extraValidation?.(value); 1991 if (diagnostic) { 1992 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, ...diagnostic)); 1993 return undefined; 1994 } 1995 } 1996 return value; 1997 } 1998 1999 function reportInvalidOptionValue(isError: boolean | undefined) { 2000 if (isError) { 2001 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option!.name, getCompilerOptionValueTypeString(option!))); 2002 invalidReported = true; 2003 } 2004 } 2005 } 2006 2007 function isDoubleQuotedString(node: Node): boolean { 2008 return isStringLiteral(node) && isStringDoubleQuoted(node, sourceFile); 2009 } 2010 } 2011 2012 function getCompilerOptionValueTypeString(option: CommandLineOption) { 2013 return option.type === "list" ? 2014 "Array" : 2015 isString(option.type) ? option.type : "string"; 2016 } 2017 2018 function isCompilerOptionsValue(option: CommandLineOption | undefined, value: any): value is CompilerOptionsValue { 2019 if (option) { 2020 if (isNullOrUndefined(value)) return true; // All options are undefinable/nullable 2021 if (option.type === "list") { 2022 return isArray(value); 2023 } 2024 const expectedType = isString(option.type) ? option.type : "string"; 2025 return typeof value === expectedType; 2026 } 2027 return false; 2028 } 2029 2030 /** @internal */ 2031 export interface TSConfig { 2032 compilerOptions: CompilerOptions; 2033 compileOnSave: boolean | undefined; 2034 exclude?: readonly string[]; 2035 files: readonly string[] | undefined; 2036 include?: readonly string[]; 2037 references: readonly ProjectReference[] | undefined; 2038 } 2039 2040 /** @internal */ 2041 export interface ConvertToTSConfigHost { 2042 getCurrentDirectory(): string; 2043 useCaseSensitiveFileNames: boolean; 2044 } 2045 2046 /** 2047 * Generate an uncommented, complete tsconfig for use with "--showConfig" 2048 * @param configParseResult options to be generated into tsconfig.json 2049 * @param configFileName name of the parsed config file - output paths will be generated relative to this 2050 * @param host provides current directory and case sensitivity services 2051 */ 2052 /** @internal */ 2053 export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: ConvertToTSConfigHost): TSConfig { 2054 const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); 2055 const files = map( 2056 filter( 2057 configParseResult.fileNames, 2058 !configParseResult.options.configFile?.configFileSpecs?.validatedIncludeSpecs ? returnTrue : matchesSpecs( 2059 configFileName, 2060 configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs, 2061 configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs, 2062 host, 2063 ) 2064 ), 2065 f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName) 2066 ); 2067 const optionMap = serializeCompilerOptions(configParseResult.options, { configFilePath: getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames }); 2068 const watchOptionMap = configParseResult.watchOptions && serializeWatchOptions(configParseResult.watchOptions); 2069 const config = { 2070 compilerOptions: { 2071 ...optionMapToObject(optionMap), 2072 showConfig: undefined, 2073 configFile: undefined, 2074 configFilePath: undefined, 2075 help: undefined, 2076 init: undefined, 2077 listFiles: undefined, 2078 listEmittedFiles: undefined, 2079 project: undefined, 2080 build: undefined, 2081 version: undefined, 2082 }, 2083 watchOptions: watchOptionMap && optionMapToObject(watchOptionMap), 2084 references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })), 2085 files: length(files) ? files : undefined, 2086 ...(configParseResult.options.configFile?.configFileSpecs ? { 2087 include: filterSameAsDefaultInclude(configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs), 2088 exclude: configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs 2089 } : {}), 2090 compileOnSave: !!configParseResult.compileOnSave ? true : undefined 2091 }; 2092 return config; 2093 } 2094 2095 function optionMapToObject(optionMap: ESMap<string, CompilerOptionsValue>): object { 2096 return { 2097 ...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}), 2098 }; 2099 } 2100 2101 function filterSameAsDefaultInclude(specs: readonly string[] | undefined) { 2102 if (!length(specs)) return undefined; 2103 if (length(specs) !== 1) return specs; 2104 if (specs![0] === "**/*") return undefined; 2105 return specs; 2106 } 2107 2108 function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean { 2109 if (!includeSpecs) return returnTrue; 2110 const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory()); 2111 const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames); 2112 const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames); 2113 if (includeRe) { 2114 if (excludeRe) { 2115 return path => !(includeRe.test(path) && !excludeRe.test(path)); 2116 } 2117 return path => !includeRe.test(path); 2118 } 2119 if (excludeRe) { 2120 return path => excludeRe.test(path); 2121 } 2122 return returnTrue; 2123 } 2124 2125 function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): ESMap<string, string | number> | undefined { 2126 if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") { 2127 // this is of a type CommandLineOptionOfPrimitiveType 2128 return undefined; 2129 } 2130 else if (optionDefinition.type === "list") { 2131 return getCustomTypeMapOfCommandLineOption(optionDefinition.element); 2132 } 2133 else { 2134 return (<CommandLineOptionOfCustomType>optionDefinition).type; 2135 } 2136 } 2137 2138 function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: ESMap<string, string | number>): string | undefined { 2139 // There is a typeMap associated with this command-line option so use it to map value back to its name 2140 return forEachEntry(customTypeMap, (mapValue, key) => { 2141 if (mapValue === value) { 2142 return key; 2143 } 2144 }); 2145 } 2146 2147 function serializeCompilerOptions( 2148 options: CompilerOptions, 2149 pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } 2150 ): ESMap<string, CompilerOptionsValue> { 2151 return serializeOptionBaseObject(options, getOptionsNameMap(), pathOptions); 2152 } 2153 2154 function serializeWatchOptions(options: WatchOptions) { 2155 return serializeOptionBaseObject(options, getWatchOptionsNameMap()); 2156 } 2157 2158 function serializeOptionBaseObject( 2159 options: OptionsBase, 2160 { optionsNameMap }: OptionsNameMap, 2161 pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } 2162 ): ESMap<string, CompilerOptionsValue> { 2163 const result = new Map<string, CompilerOptionsValue>(); 2164 const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames); 2165 2166 for (const name in options) { 2167 if (hasProperty(options, name)) { 2168 // tsconfig only options cannot be specified via command line, 2169 // so we can assume that only types that can appear here string | number | boolean 2170 if (optionsNameMap.has(name) && optionsNameMap.get(name)!.category === Diagnostics.Command_line_Options) { 2171 continue; 2172 } 2173 const value = <CompilerOptionsValue>options[name]; 2174 const optionDefinition = optionsNameMap.get(name.toLowerCase()); 2175 if (optionDefinition) { 2176 const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); 2177 if (!customTypeMap) { 2178 // There is no map associated with this compiler option then use the value as-is 2179 // This is the case if the value is expect to be string, number, boolean or list of string 2180 if (pathOptions && optionDefinition.isFilePath) { 2181 result.set(name, getRelativePathFromFile(pathOptions.configFilePath, getNormalizedAbsolutePath(value as string, getDirectoryPath(pathOptions.configFilePath)), getCanonicalFileName!)); 2182 } 2183 else { 2184 result.set(name, value); 2185 } 2186 } 2187 else { 2188 if (optionDefinition.type === "list") { 2189 result.set(name, (value as readonly (string | number)[]).map(element => getNameOfCompilerOptionValue(element, customTypeMap)!)); // TODO: GH#18217 2190 } 2191 else { 2192 // There is a typeMap associated with this command-line option so use it to map value back to its name 2193 result.set(name, getNameOfCompilerOptionValue(value, customTypeMap)); 2194 } 2195 } 2196 } 2197 } 2198 } 2199 return result; 2200 } 2201 2202 /** 2203 * Generate tsconfig configuration when running command line "--init" 2204 * @param options commandlineOptions to be generated into tsconfig.json 2205 * @param fileNames array of filenames to be generated into tsconfig.json 2206 */ 2207 /* @internal */ 2208 export function generateTSConfig(options: CompilerOptions, fileNames: readonly string[], newLine: string): string { 2209 const compilerOptions = extend(options, defaultInitCompilerOptions); 2210 const compilerOptionsMap = serializeCompilerOptions(compilerOptions); 2211 return writeConfigurations(); 2212 2213 function getDefaultValueForOption(option: CommandLineOption) { 2214 switch (option.type) { 2215 case "number": 2216 return 1; 2217 case "boolean": 2218 return true; 2219 case "string": 2220 return option.isFilePath ? "./" : ""; 2221 case "list": 2222 return []; 2223 case "object": 2224 return {}; 2225 default: 2226 const iterResult = option.type.keys().next(); 2227 if (!iterResult.done) return iterResult.value; 2228 return Debug.fail("Expected 'option.type' to have entries."); 2229 } 2230 } 2231 2232 function makePadding(paddingLength: number): string { 2233 return Array(paddingLength + 1).join(" "); 2234 } 2235 2236 function isAllowedOption({ category, name }: CommandLineOption): boolean { 2237 // Skip options which do not have a category or have category `Command_line_Options` 2238 // Exclude all possible `Advanced_Options` in tsconfig.json which were NOT defined in command line 2239 return category !== undefined 2240 && category !== Diagnostics.Command_line_Options 2241 && (category !== Diagnostics.Advanced_Options || compilerOptionsMap.has(name)); 2242 } 2243 2244 function writeConfigurations() { 2245 // Filter applicable options to place in the file 2246 const categorizedOptions = createMultiMap<CommandLineOption>(); 2247 for (const option of optionDeclarations) { 2248 const { category } = option; 2249 2250 if (isAllowedOption(option)) { 2251 categorizedOptions.add(getLocaleSpecificMessage(category!), option); 2252 } 2253 } 2254 2255 // Serialize all options and their descriptions 2256 let marginLength = 0; 2257 let seenKnownKeys = 0; 2258 const entries: { value: string, description?: string }[] = []; 2259 categorizedOptions.forEach((options, category) => { 2260 if (entries.length !== 0) { 2261 entries.push({ value: "" }); 2262 } 2263 entries.push({ value: `/* ${category} */` }); 2264 for (const option of options) { 2265 let optionName; 2266 if (compilerOptionsMap.has(option.name)) { 2267 optionName = `"${option.name}": ${JSON.stringify(compilerOptionsMap.get(option.name))}${(seenKnownKeys += 1) === compilerOptionsMap.size ? "" : ","}`; 2268 } 2269 else { 2270 optionName = `// "${option.name}": ${JSON.stringify(getDefaultValueForOption(option))},`; 2271 } 2272 entries.push({ 2273 value: optionName, 2274 description: `/* ${option.description && getLocaleSpecificMessage(option.description) || option.name} */` 2275 }); 2276 marginLength = Math.max(optionName.length, marginLength); 2277 } 2278 }); 2279 2280 // Write the output 2281 const tab = makePadding(2); 2282 const result: string[] = []; 2283 result.push(`{`); 2284 result.push(`${tab}"compilerOptions": {`); 2285 result.push(`${tab}${tab}/* ${getLocaleSpecificMessage(Diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashtsconfig_json_to_read_more_about_this_file)} */`); 2286 result.push(""); 2287 // Print out each row, aligning all the descriptions on the same column. 2288 for (const entry of entries) { 2289 const { value, description = "" } = entry; 2290 result.push(value && `${tab}${tab}${value}${description && (makePadding(marginLength - value.length + 2) + description)}`); 2291 } 2292 if (fileNames.length) { 2293 result.push(`${tab}},`); 2294 result.push(`${tab}"files": [`); 2295 for (let i = 0; i < fileNames.length; i++) { 2296 result.push(`${tab}${tab}${JSON.stringify(fileNames[i])}${i === fileNames.length - 1 ? "" : ","}`); 2297 } 2298 result.push(`${tab}]`); 2299 } 2300 else { 2301 result.push(`${tab}}`); 2302 } 2303 result.push(`}`); 2304 2305 return result.join(newLine) + newLine; 2306 } 2307 } 2308 2309 /* @internal */ 2310 export function convertToOptionsWithAbsolutePaths(options: CompilerOptions, toAbsolutePath: (path: string) => string) { 2311 const result: CompilerOptions = {}; 2312 const optionsNameMap = getOptionsNameMap().optionsNameMap; 2313 2314 for (const name in options) { 2315 if (hasProperty(options, name)) { 2316 result[name] = convertToOptionValueWithAbsolutePaths( 2317 optionsNameMap.get(name.toLowerCase()), 2318 options[name] as CompilerOptionsValue, 2319 toAbsolutePath 2320 ); 2321 } 2322 } 2323 if (result.configFilePath) { 2324 result.configFilePath = toAbsolutePath(result.configFilePath); 2325 } 2326 return result; 2327 } 2328 2329 function convertToOptionValueWithAbsolutePaths(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) { 2330 if (option && !isNullOrUndefined(value)) { 2331 if (option.type === "list") { 2332 const values = value as readonly (string | number)[]; 2333 if (option.element.isFilePath && values.length) { 2334 return values.map(toAbsolutePath); 2335 } 2336 } 2337 else if (option.isFilePath) { 2338 return toAbsolutePath(value as string); 2339 } 2340 } 2341 return value; 2342 } 2343 2344 /** 2345 * Parse the contents of a config file (tsconfig.json). 2346 * @param json The contents of the config file to parse 2347 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2348 * @param basePath A root directory to resolve relative path entries in the config 2349 * file to. e.g. outDir 2350 */ 2351 export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine { 2352 return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); 2353 } 2354 2355 /** 2356 * Parse the contents of a config file (tsconfig.json). 2357 * @param jsonNode The contents of the config file to parse 2358 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2359 * @param basePath A root directory to resolve relative path entries in the config 2360 * file to. e.g. outDir 2361 */ 2362 export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine { 2363 return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); 2364 } 2365 2366 /*@internal*/ 2367 export function setConfigFileInOptions(options: CompilerOptions, configFile: TsConfigSourceFile | undefined) { 2368 if (configFile) { 2369 Object.defineProperty(options, "configFile", { enumerable: false, writable: false, value: configFile }); 2370 } 2371 } 2372 2373 function isNullOrUndefined(x: any): x is null | undefined { 2374 return x === undefined || x === null; // eslint-disable-line no-null/no-null 2375 } 2376 2377 function directoryOfCombinedPath(fileName: string, basePath: string) { 2378 // Use the `getNormalizedAbsolutePath` function to avoid canonicalizing the path, as it must remain noncanonical 2379 // until consistent casing errors are reported 2380 return getDirectoryPath(getNormalizedAbsolutePath(fileName, basePath)); 2381 } 2382 2383 /** 2384 * Parse the contents of a config file from json or json source file (tsconfig.json). 2385 * @param json The contents of the config file to parse 2386 * @param sourceFile sourceFile corresponding to the Json 2387 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2388 * @param basePath A root directory to resolve relative path entries in the config 2389 * file to. e.g. outDir 2390 * @param resolutionStack Only present for backwards-compatibility. Should be empty. 2391 */ 2392 function parseJsonConfigFileContentWorker( 2393 json: any, 2394 sourceFile: TsConfigSourceFile | undefined, 2395 host: ParseConfigHost, 2396 basePath: string, 2397 existingOptions: CompilerOptions = {}, 2398 existingWatchOptions: WatchOptions | undefined, 2399 configFileName?: string, 2400 resolutionStack: Path[] = [], 2401 extraFileExtensions: readonly FileExtensionInfo[] = [], 2402 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2403 ): ParsedCommandLine { 2404 Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); 2405 const errors: Diagnostic[] = []; 2406 2407 const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors, extendedConfigCache); 2408 const { raw } = parsedConfig; 2409 const options = extend(existingOptions, parsedConfig.options || {}); 2410 const watchOptions = existingWatchOptions && parsedConfig.watchOptions ? 2411 extend(existingWatchOptions, parsedConfig.watchOptions) : 2412 parsedConfig.watchOptions || existingWatchOptions; 2413 2414 options.configFilePath = configFileName && normalizeSlashes(configFileName); 2415 const configFileSpecs = getConfigFileSpecs(); 2416 if (sourceFile) sourceFile.configFileSpecs = configFileSpecs; 2417 setConfigFileInOptions(options, sourceFile); 2418 2419 const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath); 2420 return { 2421 options, 2422 watchOptions, 2423 fileNames: getFileNames(basePathForFileNames), 2424 projectReferences: getProjectReferences(basePathForFileNames), 2425 typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), 2426 raw, 2427 errors, 2428 // Wildcard directories (provided as part of a wildcard path) are stored in a 2429 // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), 2430 // or a recursive directory. This information is used by filesystem watchers to monitor for 2431 // new entries in these paths. 2432 wildcardDirectories: getWildcardDirectories(configFileSpecs, basePathForFileNames, host.useCaseSensitiveFileNames), 2433 compileOnSave: !!raw.compileOnSave, 2434 }; 2435 2436 function getConfigFileSpecs(): ConfigFileSpecs { 2437 const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object"); 2438 const filesSpecs = toPropValue(getSpecsFromRaw("files")); 2439 if (filesSpecs) { 2440 const hasZeroOrNoReferences = referencesOfRaw === "no-prop" || isArray(referencesOfRaw) && referencesOfRaw.length === 0; 2441 const hasExtends = hasProperty(raw, "extends"); 2442 if (filesSpecs.length === 0 && hasZeroOrNoReferences && !hasExtends) { 2443 if (sourceFile) { 2444 const fileName = configFileName || "tsconfig.json"; 2445 const diagnosticMessage = Diagnostics.The_files_list_in_config_file_0_is_empty; 2446 const nodeValue = firstDefined(getTsConfigPropArray(sourceFile, "files"), property => property.initializer); 2447 const error = nodeValue 2448 ? createDiagnosticForNodeInSourceFile(sourceFile, nodeValue, diagnosticMessage, fileName) 2449 : createCompilerDiagnostic(diagnosticMessage, fileName); 2450 errors.push(error); 2451 } 2452 else { 2453 createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"); 2454 } 2455 } 2456 } 2457 2458 let includeSpecs = toPropValue(getSpecsFromRaw("include")); 2459 2460 const excludeOfRaw = getSpecsFromRaw("exclude"); 2461 let excludeSpecs = toPropValue(excludeOfRaw); 2462 if (excludeOfRaw === "no-prop" && raw.compilerOptions) { 2463 const outDir = raw.compilerOptions.outDir; 2464 const declarationDir = raw.compilerOptions.declarationDir; 2465 2466 if (outDir || declarationDir) { 2467 excludeSpecs = [outDir, declarationDir].filter(d => !!d); 2468 } 2469 } 2470 2471 if (filesSpecs === undefined && includeSpecs === undefined) { 2472 includeSpecs = ["**/*"]; 2473 } 2474 let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined; 2475 2476 // The exclude spec list is converted into a regular expression, which allows us to quickly 2477 // test whether a file or directory should be excluded before recursively traversing the 2478 // file system. 2479 2480 if (includeSpecs) { 2481 validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include"); 2482 } 2483 2484 if (excludeSpecs) { 2485 validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude"); 2486 } 2487 2488 return { 2489 filesSpecs, 2490 includeSpecs, 2491 excludeSpecs, 2492 validatedFilesSpec: filter(filesSpecs, isString), 2493 validatedIncludeSpecs, 2494 validatedExcludeSpecs, 2495 }; 2496 } 2497 2498 function getFileNames(basePath: string): string[] { 2499 const fileNames = getFileNamesFromConfigSpecs(configFileSpecs, basePath, options, host, extraFileExtensions); 2500 if (shouldReportNoInputFiles(fileNames, canJsonReportNoInputFiles(raw), resolutionStack)) { 2501 errors.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); 2502 } 2503 return fileNames; 2504 } 2505 2506 function getProjectReferences(basePath: string): readonly ProjectReference[] | undefined { 2507 let projectReferences: ProjectReference[] | undefined; 2508 const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object"); 2509 if (isArray(referencesOfRaw)) { 2510 for (const ref of referencesOfRaw) { 2511 if (typeof ref.path !== "string") { 2512 createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); 2513 } 2514 else { 2515 (projectReferences || (projectReferences = [])).push({ 2516 path: getNormalizedAbsolutePath(ref.path, basePath), 2517 originalPath: ref.path, 2518 prepend: ref.prepend, 2519 circular: ref.circular 2520 }); 2521 } 2522 } 2523 } 2524 return projectReferences; 2525 } 2526 2527 type PropOfRaw<T> = readonly T[] | "not-array" | "no-prop"; 2528 function toPropValue<T>(specResult: PropOfRaw<T>) { 2529 return isArray(specResult) ? specResult : undefined; 2530 } 2531 2532 function getSpecsFromRaw(prop: "files" | "include" | "exclude"): PropOfRaw<string> { 2533 return getPropFromRaw(prop, isString, "string"); 2534 } 2535 2536 function getPropFromRaw<T>(prop: "files" | "include" | "exclude" | "references", validateElement: (value: unknown) => boolean, elementTypeName: string): PropOfRaw<T> { 2537 if (hasProperty(raw, prop) && !isNullOrUndefined(raw[prop])) { 2538 if (isArray(raw[prop])) { 2539 const result = raw[prop]; 2540 if (!sourceFile && !every(result, validateElement)) { 2541 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, elementTypeName)); 2542 } 2543 return result; 2544 } 2545 else { 2546 createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, "Array"); 2547 return "not-array"; 2548 } 2549 } 2550 return "no-prop"; 2551 } 2552 2553 function createCompilerDiagnosticOnlyIfJson(message: DiagnosticMessage, arg0?: string, arg1?: string) { 2554 if (!sourceFile) { 2555 errors.push(createCompilerDiagnostic(message, arg0, arg1)); 2556 } 2557 } 2558 } 2559 2560 function isErrorNoInputFiles(error: Diagnostic) { 2561 return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code; 2562 } 2563 2564 function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) { 2565 return createCompilerDiagnostic( 2566 Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, 2567 configFileName || "tsconfig.json", 2568 JSON.stringify(includeSpecs || []), 2569 JSON.stringify(excludeSpecs || [])); 2570 } 2571 2572 function shouldReportNoInputFiles(fileNames: string[], canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) { 2573 return fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0); 2574 } 2575 2576 /*@internal*/ 2577 export function canJsonReportNoInputFiles(raw: any) { 2578 return !hasProperty(raw, "files") && !hasProperty(raw, "references"); 2579 } 2580 2581 /*@internal*/ 2582 export function updateErrorForNoInputFiles(fileNames: string[], configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) { 2583 const existingErrors = configParseDiagnostics.length; 2584 if (shouldReportNoInputFiles(fileNames, canJsonReportNoInutFiles)) { 2585 configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); 2586 } 2587 else { 2588 filterMutate(configParseDiagnostics, error => !isErrorNoInputFiles(error)); 2589 } 2590 return existingErrors !== configParseDiagnostics.length; 2591 } 2592 2593 export interface ParsedTsconfig { 2594 raw: any; 2595 options?: CompilerOptions; 2596 watchOptions?: WatchOptions; 2597 typeAcquisition?: TypeAcquisition; 2598 /** 2599 * Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet 2600 */ 2601 extendedConfigPath?: string; 2602 } 2603 2604 function isSuccessfulParsedTsconfig(value: ParsedTsconfig) { 2605 return !!value.options; 2606 } 2607 2608 /** 2609 * This *just* extracts options/include/exclude/files out of a config file. 2610 * It does *not* resolve the included files. 2611 */ 2612 function parseConfig( 2613 json: any, 2614 sourceFile: TsConfigSourceFile | undefined, 2615 host: ParseConfigHost, 2616 basePath: string, 2617 configFileName: string | undefined, 2618 resolutionStack: string[], 2619 errors: Push<Diagnostic>, 2620 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2621 ): ParsedTsconfig { 2622 basePath = normalizeSlashes(basePath); 2623 const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); 2624 2625 if (resolutionStack.indexOf(resolvedPath) >= 0) { 2626 errors.push(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))); 2627 return { raw: json || convertToObject(sourceFile!, errors) }; 2628 } 2629 2630 const ownConfig = json ? 2631 parseOwnConfigOfJson(json, host, basePath, configFileName, errors) : 2632 parseOwnConfigOfJsonSourceFile(sourceFile!, host, basePath, configFileName, errors); 2633 2634 if (ownConfig.options?.paths) { 2635 // If we end up needing to resolve relative paths from 'paths' relative to 2636 // the config file location, we'll need to know where that config file was. 2637 // Since 'paths' can be inherited from an extended config in another directory, 2638 // we wouldn't know which directory to use unless we store it here. 2639 ownConfig.options.pathsBasePath = basePath; 2640 } 2641 if (ownConfig.extendedConfigPath) { 2642 // copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios. 2643 resolutionStack = resolutionStack.concat([resolvedPath]); 2644 const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, resolutionStack, errors, extendedConfigCache); 2645 if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) { 2646 const baseRaw = extendedConfig.raw; 2647 const raw = ownConfig.raw; 2648 let relativeDifference: string | undefined ; 2649 const setPropertyInRawIfNotUndefined = (propertyName: string) => { 2650 if (!raw[propertyName] && baseRaw[propertyName]) { 2651 raw[propertyName] = map(baseRaw[propertyName], (path: string) => isRootedDiskPath(path) ? path : combinePaths( 2652 relativeDifference ||= convertToRelativePath(getDirectoryPath(ownConfig.extendedConfigPath!), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)), 2653 path 2654 )); 2655 } 2656 }; 2657 setPropertyInRawIfNotUndefined("include"); 2658 setPropertyInRawIfNotUndefined("exclude"); 2659 setPropertyInRawIfNotUndefined("files"); 2660 if (raw.compileOnSave === undefined) { 2661 raw.compileOnSave = baseRaw.compileOnSave; 2662 } 2663 ownConfig.options = assign({}, extendedConfig.options, ownConfig.options); 2664 ownConfig.watchOptions = ownConfig.watchOptions && extendedConfig.watchOptions ? 2665 assign({}, extendedConfig.watchOptions, ownConfig.watchOptions) : 2666 ownConfig.watchOptions || extendedConfig.watchOptions; 2667 // TODO extend type typeAcquisition 2668 } 2669 } 2670 2671 return ownConfig; 2672 } 2673 2674 function parseOwnConfigOfJson( 2675 json: any, 2676 host: ParseConfigHost, 2677 basePath: string, 2678 configFileName: string | undefined, 2679 errors: Push<Diagnostic> 2680 ): ParsedTsconfig { 2681 if (hasProperty(json, "excludes")) { 2682 errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); 2683 } 2684 2685 const options = convertCompilerOptionsFromJsonWorker(json.compilerOptions, basePath, errors, configFileName); 2686 // typingOptions has been deprecated and is only supported for backward compatibility purposes. 2687 // It should be removed in future releases - use typeAcquisition instead. 2688 const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName); 2689 const watchOptions = convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors); 2690 json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors); 2691 let extendedConfigPath: string | undefined; 2692 2693 if (json.extends) { 2694 if (!isString(json.extends)) { 2695 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string")); 2696 } 2697 else { 2698 const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath; 2699 extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic); 2700 } 2701 } 2702 return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath }; 2703 } 2704 2705 function parseOwnConfigOfJsonSourceFile( 2706 sourceFile: TsConfigSourceFile, 2707 host: ParseConfigHost, 2708 basePath: string, 2709 configFileName: string | undefined, 2710 errors: Push<Diagnostic> 2711 ): ParsedTsconfig { 2712 const options = getDefaultCompilerOptions(configFileName); 2713 let typeAcquisition: TypeAcquisition | undefined, typingOptionstypeAcquisition: TypeAcquisition | undefined; 2714 let watchOptions: WatchOptions | undefined; 2715 let extendedConfigPath: string | undefined; 2716 2717 const optionsIterator: JsonConversionNotifier = { 2718 onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue) { 2719 let currentOption; 2720 switch (parentOption) { 2721 case "compilerOptions": 2722 currentOption = options; 2723 break; 2724 case "watchOptions": 2725 currentOption = (watchOptions || (watchOptions = {})); 2726 break; 2727 case "typeAcquisition": 2728 currentOption = (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))); 2729 break; 2730 case "typingOptions": 2731 currentOption = (typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName))); 2732 break; 2733 default: 2734 Debug.fail("Unknown option"); 2735 } 2736 2737 currentOption[option.name] = normalizeOptionValue(option, basePath, value); 2738 }, 2739 onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) { 2740 switch (key) { 2741 case "extends": 2742 const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath; 2743 extendedConfigPath = getExtendsConfigPath( 2744 <string>value, 2745 host, 2746 newBase, 2747 errors, 2748 (message, arg0) => 2749 createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0) 2750 ); 2751 return; 2752 } 2753 }, 2754 onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, _value: CompilerOptionsValue, _valueNode: Expression) { 2755 if (key === "excludes") { 2756 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, keyNode, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); 2757 } 2758 } 2759 }; 2760 const json = convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, getTsconfigRootOptionsMap(), optionsIterator); 2761 2762 if (!typeAcquisition) { 2763 if (typingOptionstypeAcquisition) { 2764 typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ? 2765 { 2766 enable: typingOptionstypeAcquisition.enableAutoDiscovery, 2767 include: typingOptionstypeAcquisition.include, 2768 exclude: typingOptionstypeAcquisition.exclude 2769 } : 2770 typingOptionstypeAcquisition; 2771 } 2772 else { 2773 typeAcquisition = getDefaultTypeAcquisition(configFileName); 2774 } 2775 } 2776 2777 return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath }; 2778 } 2779 2780 function getExtendsConfigPath( 2781 extendedConfig: string, 2782 host: ParseConfigHost, 2783 basePath: string, 2784 errors: Push<Diagnostic>, 2785 createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) { 2786 extendedConfig = normalizeSlashes(extendedConfig); 2787 if (isRootedDiskPath(extendedConfig) || startsWith(extendedConfig, "./") || startsWith(extendedConfig, "../")) { 2788 let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath); 2789 if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) { 2790 extendedConfigPath = `${extendedConfigPath}.json`; 2791 if (!host.fileExists(extendedConfigPath)) { 2792 errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig)); 2793 return undefined; 2794 } 2795 } 2796 return extendedConfigPath; 2797 } 2798 // If the path isn't a rooted or relative path, resolve like a module 2799 const resolved = nodeModuleNameResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), { moduleResolution: ModuleResolutionKind.NodeJs }, host, /*cache*/ undefined, /*projectRefs*/ undefined, /*lookupConfig*/ true); 2800 if (resolved.resolvedModule) { 2801 return resolved.resolvedModule.resolvedFileName; 2802 } 2803 errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig)); 2804 return undefined; 2805 } 2806 2807 export interface ExtendedConfigCacheEntry { 2808 extendedResult: TsConfigSourceFile; 2809 extendedConfig: ParsedTsconfig | undefined; 2810 } 2811 2812 function getExtendedConfig( 2813 sourceFile: TsConfigSourceFile | undefined, 2814 extendedConfigPath: string, 2815 host: ParseConfigHost, 2816 resolutionStack: string[], 2817 errors: Push<Diagnostic>, 2818 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2819 ): ParsedTsconfig | undefined { 2820 const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toFileNameLowerCase(extendedConfigPath); 2821 let value: ExtendedConfigCacheEntry | undefined; 2822 let extendedResult: TsConfigSourceFile; 2823 let extendedConfig: ParsedTsconfig | undefined; 2824 if (extendedConfigCache && (value = extendedConfigCache.get(path))) { 2825 ({ extendedResult, extendedConfig } = value); 2826 } 2827 else { 2828 extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path)); 2829 if (!extendedResult.parseDiagnostics.length) { 2830 extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, getDirectoryPath(extendedConfigPath), 2831 getBaseFileName(extendedConfigPath), resolutionStack, errors, extendedConfigCache); 2832 } 2833 if (extendedConfigCache) { 2834 extendedConfigCache.set(path, { extendedResult, extendedConfig }); 2835 } 2836 } 2837 if (sourceFile) { 2838 sourceFile.extendedSourceFiles = [extendedResult.fileName]; 2839 if (extendedResult.extendedSourceFiles) { 2840 sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles); 2841 } 2842 } 2843 if (extendedResult.parseDiagnostics.length) { 2844 errors.push(...extendedResult.parseDiagnostics); 2845 return undefined; 2846 } 2847 return extendedConfig!; 2848 } 2849 2850 function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Push<Diagnostic>): boolean { 2851 if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) { 2852 return false; 2853 } 2854 const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption.compileOnSave, basePath, errors); 2855 return typeof result === "boolean" && result; 2856 } 2857 2858 export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } { 2859 const errors: Diagnostic[] = []; 2860 const options = convertCompilerOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName); 2861 return { options, errors }; 2862 } 2863 2864 export function convertTypeAcquisitionFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypeAcquisition, errors: Diagnostic[] } { 2865 const errors: Diagnostic[] = []; 2866 const options = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName); 2867 return { options, errors }; 2868 } 2869 2870 function getDefaultCompilerOptions(configFileName?: string) { 2871 const options: CompilerOptions = configFileName && getBaseFileName(configFileName) === "jsconfig.json" 2872 ? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, skipLibCheck: true, noEmit: true } 2873 : {}; 2874 return options; 2875 } 2876 2877 function convertCompilerOptionsFromJsonWorker(jsonOptions: any, 2878 basePath: string, errors: Push<Diagnostic>, configFileName?: string): CompilerOptions { 2879 2880 const options = getDefaultCompilerOptions(configFileName); 2881 convertOptionsFromJson(getCommandLineCompilerOptionsMap(), jsonOptions, basePath, options, compilerOptionsDidYouMeanDiagnostics, errors); 2882 if (configFileName) { 2883 options.configFilePath = normalizeSlashes(configFileName); 2884 } 2885 return options; 2886 } 2887 2888 function getDefaultTypeAcquisition(configFileName?: string): TypeAcquisition { 2889 return { enable: !!configFileName && getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] }; 2890 } 2891 2892 function convertTypeAcquisitionFromJsonWorker(jsonOptions: any, 2893 basePath: string, errors: Push<Diagnostic>, configFileName?: string): TypeAcquisition { 2894 2895 const options = getDefaultTypeAcquisition(configFileName); 2896 const typeAcquisition = convertEnableAutoDiscoveryToEnable(jsonOptions); 2897 2898 convertOptionsFromJson(getCommandLineTypeAcquisitionMap(), typeAcquisition, basePath, options, typeAcquisitionDidYouMeanDiagnostics, errors); 2899 return options; 2900 } 2901 2902 function convertWatchOptionsFromJsonWorker(jsonOptions: any, basePath: string, errors: Push<Diagnostic>): WatchOptions | undefined { 2903 return convertOptionsFromJson(getCommandLineWatchOptionsMap(), jsonOptions, basePath, /*defaultOptions*/ undefined, watchOptionsDidYouMeanDiagnostics, errors); 2904 } 2905 2906 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2907 defaultOptions: undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): WatchOptions | undefined; 2908 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2909 defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): CompilerOptions | TypeAcquisition; 2910 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2911 defaultOptions: CompilerOptions | TypeAcquisition | WatchOptions | undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>) { 2912 2913 if (!jsonOptions) { 2914 return; 2915 } 2916 2917 for (const id in jsonOptions) { 2918 const opt = optionsNameMap.get(id); 2919 if (opt) { 2920 (defaultOptions || (defaultOptions = {}))[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); 2921 } 2922 else { 2923 errors.push(createUnknownOptionError(id, diagnostics, createCompilerDiagnostic)); 2924 } 2925 } 2926 return defaultOptions; 2927 } 2928 2929 /*@internal*/ 2930 export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue { 2931 if (isCompilerOptionsValue(opt, value)) { 2932 const optType = opt.type; 2933 if (optType === "list" && isArray(value)) { 2934 return convertJsonOptionOfListType(<CommandLineOptionOfListType>opt, value, basePath, errors); 2935 } 2936 else if (!isString(optType)) { 2937 return convertJsonOptionOfCustomType(<CommandLineOptionOfCustomType>opt, <string>value, errors); 2938 } 2939 const validatedValue = validateJsonOptionValue(opt, value, errors); 2940 return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue); 2941 } 2942 else { 2943 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, getCompilerOptionValueTypeString(opt))); 2944 } 2945 } 2946 2947 function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { 2948 if (isNullOrUndefined(value)) return undefined; 2949 if (option.type === "list") { 2950 const listOption = option; 2951 if (listOption.element.isFilePath || !isString(listOption.element.type)) { 2952 return <CompilerOptionsValue>filter(map(value, v => normalizeOptionValue(listOption.element, basePath, v)), v => !!v); 2953 } 2954 return value; 2955 } 2956 else if (!isString(option.type)) { 2957 return option.type.get(isString(value) ? value.toLowerCase() : value); 2958 } 2959 return normalizeNonListOptionValue(option, basePath, value); 2960 } 2961 2962 function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { 2963 if (option.isFilePath) { 2964 value = getNormalizedAbsolutePath(value, basePath); 2965 if (value === "") { 2966 value = "."; 2967 } 2968 } 2969 return value; 2970 } 2971 2972 function validateJsonOptionValue<T extends CompilerOptionsValue>(opt: CommandLineOption, value: T, errors: Push<Diagnostic>): T | undefined { 2973 if (isNullOrUndefined(value)) return undefined; 2974 const d = opt.extraValidation?.(value); 2975 if (!d) return value; 2976 errors.push(createCompilerDiagnostic(...d)); 2977 return undefined; 2978 } 2979 2980 function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) { 2981 if (isNullOrUndefined(value)) return undefined; 2982 const key = value.toLowerCase(); 2983 const val = opt.type.get(key); 2984 if (val !== undefined) { 2985 return validateJsonOptionValue(opt, val, errors); 2986 } 2987 else { 2988 errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); 2989 } 2990 } 2991 2992 function convertJsonOptionOfListType(option: CommandLineOptionOfListType, values: readonly any[], basePath: string, errors: Push<Diagnostic>): any[] { 2993 return filter(map(values, v => convertJsonOption(option.element, v, basePath, errors)), v => !!v); 2994 } 2995 2996 function trimString(s: string) { 2997 return typeof s.trim === "function" ? s.trim() : s.replace(/^[\s]+|[\s]+$/g, ""); 2998 } 2999 3000 /** 3001 * Tests for a path that ends in a recursive directory wildcard. 3002 * Matches **, \**, **\, and \**\, but not a**b. 3003 * 3004 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3005 * 3006 * Breakdown: 3007 * (^|\/) # matches either the beginning of the string or a directory separator. 3008 * \*\* # matches the recursive directory wildcard "**". 3009 * \/?$ # matches an optional trailing directory separator at the end of the string. 3010 */ 3011 const invalidTrailingRecursionPattern = /(^|\/)\*\*\/?$/; 3012 3013 /** 3014 * Tests for a path where .. appears after a recursive directory wildcard. 3015 * Matches **\..\*, **\a\..\*, and **\.., but not ..\**\* 3016 * 3017 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3018 * 3019 * Breakdown: 3020 * (^|\/) # matches either the beginning of the string or a directory separator. 3021 * \*\*\/ # matches a recursive directory wildcard "**" followed by a directory separator. 3022 * (.*\/)? # optionally matches any number of characters followed by a directory separator. 3023 * \.\. # matches a parent directory path component ".." 3024 * ($|\/) # matches either the end of the string or a directory separator. 3025 */ 3026 const invalidDotDotAfterRecursiveWildcardPattern = /(^|\/)\*\*\/(.*\/)?\.\.($|\/)/; 3027 3028 /** 3029 * Tests for a path containing a wildcard character in a directory component of the path. 3030 * Matches \*\, \?\, and \a*b\, but not \a\ or \a\*. 3031 * 3032 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3033 * 3034 * Breakdown: 3035 * \/ # matches a directory separator. 3036 * [^/]*? # matches any number of characters excluding directory separators (non-greedy). 3037 * [*?] # matches either a wildcard character (* or ?) 3038 * [^/]* # matches any number of characters excluding directory separators (greedy). 3039 * \/ # matches a directory separator. 3040 */ 3041 const watchRecursivePattern = /\/[^/]*?[*?][^/]*\//; 3042 3043 /** 3044 * Matches the portion of a wildcard path that does not contain wildcards. 3045 * Matches \a of \a\*, or \a\b\c of \a\b\c\?\d. 3046 * 3047 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3048 * 3049 * Breakdown: 3050 * ^ # matches the beginning of the string 3051 * [^*?]* # matches any number of non-wildcard characters 3052 * (?=\/[^/]*[*?]) # lookahead that matches a directory separator followed by 3053 * # a path component that contains at least one wildcard character (* or ?). 3054 */ 3055 const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/; 3056 3057 /** 3058 * Gets the file names from the provided config file specs that contain, files, include, exclude and 3059 * other properties needed to resolve the file names 3060 * @param configFileSpecs The config file specs extracted with file names to include, wildcards to include/exclude and other details 3061 * @param basePath The base path for any relative file specifications. 3062 * @param options Compiler options. 3063 * @param host The host used to resolve files and directories. 3064 * @param extraFileExtensions optionaly file extra file extension information from host 3065 */ 3066 /* @internal */ 3067 export function getFileNamesFromConfigSpecs( 3068 configFileSpecs: ConfigFileSpecs, 3069 basePath: string, 3070 options: CompilerOptions, 3071 host: ParseConfigHost, 3072 extraFileExtensions: readonly FileExtensionInfo[] = emptyArray 3073 ): string[] { 3074 basePath = normalizePath(basePath); 3075 3076 const keyMapper = createGetCanonicalFileName(host.useCaseSensitiveFileNames); 3077 3078 // Literal file names (provided via the "files" array in tsconfig.json) are stored in a 3079 // file map with a possibly case insensitive key. We use this map later when when including 3080 // wildcard paths. 3081 const literalFileMap = new Map<string, string>(); 3082 3083 // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a 3084 // file map with a possibly case insensitive key. We use this map to store paths matched 3085 // via wildcard, and to handle extension priority. 3086 const wildcardFileMap = new Map<string, string>(); 3087 3088 // Wildcard paths of json files (provided via the "includes" array in tsconfig.json) are stored in a 3089 // file map with a possibly case insensitive key. We use this map to store paths matched 3090 // via wildcard of *.json kind 3091 const wildCardJsonFileMap = new Map<string, string>(); 3092 const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = configFileSpecs; 3093 3094 // Rather than requery this for each file and filespec, we query the supported extensions 3095 // once and store it on the expansion context. 3096 const supportedExtensions = getSupportedExtensions(options, extraFileExtensions); 3097 const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions); 3098 3099 // Literal files are always included verbatim. An "include" or "exclude" specification cannot 3100 // remove a literal file. 3101 if (validatedFilesSpec) { 3102 for (const fileName of validatedFilesSpec) { 3103 const file = getNormalizedAbsolutePath(fileName, basePath); 3104 literalFileMap.set(keyMapper(file), file); 3105 } 3106 } 3107 3108 let jsonOnlyIncludeRegexes: readonly RegExp[] | undefined; 3109 if (validatedIncludeSpecs && validatedIncludeSpecs.length > 0) { 3110 for (const file of host.readDirectory(basePath, supportedExtensionsWithJsonIfResolveJsonModule, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) { 3111 if (fileExtensionIs(file, Extension.Json)) { 3112 // Valid only if *.json specified 3113 if (!jsonOnlyIncludeRegexes) { 3114 const includes = validatedIncludeSpecs.filter(s => endsWith(s, Extension.Json)); 3115 const includeFilePatterns = map(getRegularExpressionsForWildcards(includes, basePath, "files"), pattern => `^${pattern}$`); 3116 jsonOnlyIncludeRegexes = includeFilePatterns ? includeFilePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames)) : emptyArray; 3117 } 3118 const includeIndex = findIndex(jsonOnlyIncludeRegexes, re => re.test(file)); 3119 if (includeIndex !== -1) { 3120 const key = keyMapper(file); 3121 if (!literalFileMap.has(key) && !wildCardJsonFileMap.has(key)) { 3122 wildCardJsonFileMap.set(key, file); 3123 } 3124 } 3125 continue; 3126 } 3127 // If we have already included a literal or wildcard path with a 3128 // higher priority extension, we should skip this file. 3129 // 3130 // This handles cases where we may encounter both <file>.ts and 3131 // <file>.d.ts (or <file>.js if "allowJs" is enabled) in the same 3132 // directory when they are compilation outputs. 3133 if (hasFileWithHigherPriorityExtension(file, literalFileMap, wildcardFileMap, supportedExtensions, keyMapper)) { 3134 continue; 3135 } 3136 3137 // We may have included a wildcard path with a lower priority 3138 // extension due to the user-defined order of entries in the 3139 // "include" array. If there is a lower priority extension in the 3140 // same directory, we should remove it. 3141 removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); 3142 3143 const key = keyMapper(file); 3144 if (!literalFileMap.has(key) && !wildcardFileMap.has(key)) { 3145 wildcardFileMap.set(key, file); 3146 } 3147 } 3148 } 3149 3150 const literalFiles = arrayFrom(literalFileMap.values()); 3151 const wildcardFiles = arrayFrom(wildcardFileMap.values()); 3152 3153 return literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())); 3154 } 3155 3156 /* @internal */ 3157 export function isExcludedFile( 3158 pathToCheck: string, 3159 spec: ConfigFileSpecs, 3160 basePath: string, 3161 useCaseSensitiveFileNames: boolean, 3162 currentDirectory: string 3163 ): boolean { 3164 const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = spec; 3165 if (!length(validatedIncludeSpecs) || !length(validatedExcludeSpecs)) return false; 3166 3167 basePath = normalizePath(basePath); 3168 3169 const keyMapper = createGetCanonicalFileName(useCaseSensitiveFileNames); 3170 if (validatedFilesSpec) { 3171 for (const fileName of validatedFilesSpec) { 3172 if (keyMapper(getNormalizedAbsolutePath(fileName, basePath)) === pathToCheck) return false; 3173 } 3174 } 3175 3176 return matchesExcludeWorker(pathToCheck, validatedExcludeSpecs, useCaseSensitiveFileNames, currentDirectory, basePath); 3177 } 3178 3179 /* @internal */ 3180 export function matchesExclude( 3181 pathToCheck: string, 3182 excludeSpecs: readonly string[] | undefined, 3183 useCaseSensitiveFileNames: boolean, 3184 currentDirectory: string 3185 ) { 3186 return matchesExcludeWorker( 3187 pathToCheck, 3188 filter(excludeSpecs, spec => !invalidDotDotAfterRecursiveWildcardPattern.test(spec)), 3189 useCaseSensitiveFileNames, 3190 currentDirectory 3191 ); 3192 } 3193 3194 function matchesExcludeWorker( 3195 pathToCheck: string, 3196 excludeSpecs: readonly string[] | undefined, 3197 useCaseSensitiveFileNames: boolean, 3198 currentDirectory: string, 3199 basePath?: string 3200 ) { 3201 const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude"); 3202 const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames); 3203 if (!excludeRegex) return false; 3204 if (excludeRegex.test(pathToCheck)) return true; 3205 return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck)); 3206 } 3207 3208 function validateSpecs(specs: readonly string[], errors: Push<Diagnostic>, disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { 3209 return specs.filter(spec => { 3210 if (!isString(spec)) return false; 3211 const diag = specToDiagnostic(spec, disallowTrailingRecursion); 3212 if (diag !== undefined) { 3213 errors.push(createDiagnostic(...diag)); 3214 } 3215 return diag === undefined; 3216 }); 3217 3218 function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { 3219 const element = getTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec); 3220 return element ? 3221 createDiagnosticForNodeInSourceFile(jsonSourceFile!, element, message, spec) : 3222 createCompilerDiagnostic(message, spec); 3223 } 3224 } 3225 3226 function specToDiagnostic(spec: string, disallowTrailingRecursion?: boolean): [DiagnosticMessage, string] | undefined { 3227 if (disallowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { 3228 return [Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; 3229 } 3230 else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { 3231 return [Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; 3232 } 3233 } 3234 3235 /** 3236 * Gets directories in a set of include patterns that should be watched for changes. 3237 */ 3238 function getWildcardDirectories({ validatedIncludeSpecs: include, validatedExcludeSpecs: exclude }: ConfigFileSpecs, path: string, useCaseSensitiveFileNames: boolean): MapLike<WatchDirectoryFlags> { 3239 // We watch a directory recursively if it contains a wildcard anywhere in a directory segment 3240 // of the pattern: 3241 // 3242 // /a/b/**/d - Watch /a/b recursively to catch changes to any d in any subfolder recursively 3243 // /a/b/*/d - Watch /a/b recursively to catch any d in any immediate subfolder, even if a new subfolder is added 3244 // /a/b - Watch /a/b recursively to catch changes to anything in any recursive subfoler 3245 // 3246 // We watch a directory without recursion if it contains a wildcard in the file segment of 3247 // the pattern: 3248 // 3249 // /a/b/* - Watch /a/b directly to catch any new file 3250 // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z 3251 const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); 3252 const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); 3253 const wildcardDirectories: MapLike<WatchDirectoryFlags> = {}; 3254 if (include !== undefined) { 3255 const recursiveKeys: string[] = []; 3256 for (const file of include) { 3257 const spec = normalizePath(combinePaths(path, file)); 3258 if (excludeRegex && excludeRegex.test(spec)) { 3259 continue; 3260 } 3261 3262 const match = getWildcardDirectoryFromSpec(spec, useCaseSensitiveFileNames); 3263 if (match) { 3264 const { key, flags } = match; 3265 const existingFlags = wildcardDirectories[key]; 3266 if (existingFlags === undefined || existingFlags < flags) { 3267 wildcardDirectories[key] = flags; 3268 if (flags === WatchDirectoryFlags.Recursive) { 3269 recursiveKeys.push(key); 3270 } 3271 } 3272 } 3273 } 3274 3275 // Remove any subpaths under an existing recursively watched directory. 3276 for (const key in wildcardDirectories) { 3277 if (hasProperty(wildcardDirectories, key)) { 3278 for (const recursiveKey of recursiveKeys) { 3279 if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { 3280 delete wildcardDirectories[key]; 3281 } 3282 } 3283 } 3284 } 3285 } 3286 3287 return wildcardDirectories; 3288 } 3289 3290 function getWildcardDirectoryFromSpec(spec: string, useCaseSensitiveFileNames: boolean): { key: string, flags: WatchDirectoryFlags } | undefined { 3291 const match = wildcardDirectoryPattern.exec(spec); 3292 if (match) { 3293 return { 3294 key: useCaseSensitiveFileNames ? match[0] : toFileNameLowerCase(match[0]), 3295 flags: watchRecursivePattern.test(spec) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None 3296 }; 3297 } 3298 if (isImplicitGlob(spec)) { 3299 return { 3300 key: useCaseSensitiveFileNames ? spec : toFileNameLowerCase(spec), 3301 flags: WatchDirectoryFlags.Recursive 3302 }; 3303 } 3304 return undefined; 3305 } 3306 3307 /** 3308 * Determines whether a literal or wildcard file has already been included that has a higher 3309 * extension priority. 3310 * 3311 * @param file The path to the file. 3312 * @param extensionPriority The priority of the extension. 3313 * @param context The expansion context. 3314 */ 3315 function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap<string, string>, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) { 3316 const extensionPriority = getExtensionPriority(file, extensions); 3317 const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); 3318 for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { 3319 const higherPriorityExtension = extensions[i]; 3320 const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); 3321 if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) { 3322 return true; 3323 } 3324 } 3325 3326 return false; 3327 } 3328 3329 /** 3330 * Removes files included via wildcard expansion with a lower extension priority that have 3331 * already been included. 3332 * 3333 * @param file The path to the file. 3334 * @param extensionPriority The priority of the extension. 3335 * @param context The expansion context. 3336 */ 3337 function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) { 3338 const extensionPriority = getExtensionPriority(file, extensions); 3339 const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); 3340 for (let i = nextExtensionPriority; i < extensions.length; i++) { 3341 const lowerPriorityExtension = extensions[i]; 3342 const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); 3343 wildcardFiles.delete(lowerPriorityPath); 3344 } 3345 } 3346 3347 /** 3348 * Produces a cleaned version of compiler options with personally identifying info (aka, paths) removed. 3349 * Also converts enum values back to strings. 3350 */ 3351 /* @internal */ 3352 export function convertCompilerOptionsForTelemetry(opts: CompilerOptions): CompilerOptions { 3353 const out: CompilerOptions = {}; 3354 for (const key in opts) { 3355 if (opts.hasOwnProperty(key)) { 3356 const type = getOptionFromName(key); 3357 if (type !== undefined) { // Ignore unknown options 3358 out[key] = getOptionValueWithEmptyStrings(opts[key], type); 3359 } 3360 } 3361 } 3362 return out; 3363 } 3364 3365 function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption): {} { 3366 switch (option.type) { 3367 case "object": // "paths". Can't get any useful information from the value since we blank out strings, so just return "". 3368 return ""; 3369 case "string": // Could be any arbitrary string -- use empty string instead. 3370 return ""; 3371 case "number": // Allow numbers, but be sure to check it's actually a number. 3372 return typeof value === "number" ? value : ""; 3373 case "boolean": 3374 return typeof value === "boolean" ? value : ""; 3375 case "list": 3376 const elementType = option.element; 3377 return isArray(value) ? value.map(v => getOptionValueWithEmptyStrings(v, elementType)) : ""; 3378 default: 3379 return forEachEntry(option.type, (optionEnumValue, optionStringValue) => { 3380 if (optionEnumValue === value) { 3381 return optionStringValue; 3382 } 3383 })!; // TODO: GH#18217 3384 } 3385 } 3386} 3387