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: "resolveJsonModule", 862 type: "boolean", 863 affectsModuleResolution: true, 864 category: Diagnostics.Advanced_Options, 865 description: Diagnostics.Include_modules_imported_with_json_extension 866 }, 867 868 { 869 name: "out", 870 type: "string", 871 affectsEmit: true, 872 isFilePath: false, // This is intentionally broken to support compatability with existing tsconfig files 873 // for correct behaviour, please use outFile 874 category: Diagnostics.Advanced_Options, 875 paramType: Diagnostics.FILE, 876 description: Diagnostics.Deprecated_Use_outFile_instead_Concatenate_and_emit_output_to_single_file, 877 transpileOptionValue: undefined 878 }, 879 { 880 name: "reactNamespace", 881 type: "string", 882 affectsEmit: true, 883 category: Diagnostics.Advanced_Options, 884 description: Diagnostics.Deprecated_Use_jsxFactory_instead_Specify_the_object_invoked_for_createElement_when_targeting_react_JSX_emit 885 }, 886 { 887 name: "skipDefaultLibCheck", 888 type: "boolean", 889 category: Diagnostics.Advanced_Options, 890 description: Diagnostics.Deprecated_Use_skipLibCheck_instead_Skip_type_checking_of_default_library_declaration_files 891 }, 892 { 893 name: "charset", 894 type: "string", 895 category: Diagnostics.Advanced_Options, 896 description: Diagnostics.The_character_set_of_the_input_files 897 }, 898 { 899 name: "emitBOM", 900 type: "boolean", 901 affectsEmit: true, 902 category: Diagnostics.Advanced_Options, 903 description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files 904 }, 905 { 906 name: "newLine", 907 type: new Map(getEntries({ 908 crlf: NewLineKind.CarriageReturnLineFeed, 909 lf: NewLineKind.LineFeed 910 })), 911 affectsEmit: true, 912 paramType: Diagnostics.NEWLINE, 913 category: Diagnostics.Advanced_Options, 914 description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix, 915 }, 916 { 917 name: "noErrorTruncation", 918 type: "boolean", 919 affectsSemanticDiagnostics: true, 920 category: Diagnostics.Advanced_Options, 921 description: Diagnostics.Do_not_truncate_error_messages 922 }, 923 { 924 name: "noLib", 925 type: "boolean", 926 affectsModuleResolution: true, 927 category: Diagnostics.Advanced_Options, 928 description: Diagnostics.Do_not_include_the_default_library_file_lib_d_ts, 929 // We are not returning a sourceFile for lib file when asked by the program, 930 // so pass --noLib to avoid reporting a file not found error. 931 transpileOptionValue: true 932 }, 933 { 934 name: "noResolve", 935 type: "boolean", 936 affectsModuleResolution: true, 937 category: Diagnostics.Advanced_Options, 938 description: Diagnostics.Do_not_add_triple_slash_references_or_imported_modules_to_the_list_of_compiled_files, 939 // We are not doing a full typecheck, we are not resolving the whole context, 940 // so pass --noResolve to avoid reporting missing file errors. 941 transpileOptionValue: true 942 }, 943 { 944 name: "stripInternal", 945 type: "boolean", 946 affectsEmit: true, 947 category: Diagnostics.Advanced_Options, 948 description: Diagnostics.Do_not_emit_declarations_for_code_that_has_an_internal_annotation, 949 }, 950 { 951 name: "disableSizeLimit", 952 type: "boolean", 953 affectsSourceFile: true, 954 category: Diagnostics.Advanced_Options, 955 description: Diagnostics.Disable_size_limitations_on_JavaScript_projects 956 }, 957 { 958 name: "disableSourceOfProjectReferenceRedirect", 959 type: "boolean", 960 isTSConfigOnly: true, 961 category: Diagnostics.Advanced_Options, 962 description: Diagnostics.Disable_use_of_source_files_instead_of_declaration_files_from_referenced_projects 963 }, 964 { 965 name: "disableSolutionSearching", 966 type: "boolean", 967 isTSConfigOnly: true, 968 category: Diagnostics.Advanced_Options, 969 description: Diagnostics.Disable_solution_searching_for_this_project 970 }, 971 { 972 name: "disableReferencedProjectLoad", 973 type: "boolean", 974 isTSConfigOnly: true, 975 category: Diagnostics.Advanced_Options, 976 description: Diagnostics.Disable_loading_referenced_projects 977 }, 978 { 979 name: "noImplicitUseStrict", 980 type: "boolean", 981 affectsSemanticDiagnostics: true, 982 category: Diagnostics.Advanced_Options, 983 description: Diagnostics.Do_not_emit_use_strict_directives_in_module_output 984 }, 985 { 986 name: "noEmitHelpers", 987 type: "boolean", 988 affectsEmit: true, 989 category: Diagnostics.Advanced_Options, 990 description: Diagnostics.Do_not_generate_custom_helper_functions_like_extends_in_compiled_output 991 }, 992 { 993 name: "noEmitOnError", 994 type: "boolean", 995 affectsEmit: true, 996 category: Diagnostics.Advanced_Options, 997 description: Diagnostics.Do_not_emit_outputs_if_any_errors_were_reported, 998 transpileOptionValue: undefined 999 }, 1000 { 1001 name: "preserveConstEnums", 1002 type: "boolean", 1003 affectsEmit: true, 1004 category: Diagnostics.Advanced_Options, 1005 description: Diagnostics.Do_not_erase_const_enum_declarations_in_generated_code 1006 }, 1007 { 1008 name: "declarationDir", 1009 type: "string", 1010 affectsEmit: true, 1011 isFilePath: true, 1012 paramType: Diagnostics.DIRECTORY, 1013 category: Diagnostics.Advanced_Options, 1014 description: Diagnostics.Output_directory_for_generated_declaration_files, 1015 transpileOptionValue: undefined 1016 }, 1017 { 1018 name: "skipLibCheck", 1019 type: "boolean", 1020 category: Diagnostics.Advanced_Options, 1021 description: Diagnostics.Skip_type_checking_of_declaration_files, 1022 }, 1023 { 1024 name: "allowUnusedLabels", 1025 type: "boolean", 1026 affectsBindDiagnostics: true, 1027 affectsSemanticDiagnostics: true, 1028 category: Diagnostics.Advanced_Options, 1029 description: Diagnostics.Do_not_report_errors_on_unused_labels 1030 }, 1031 { 1032 name: "allowUnreachableCode", 1033 type: "boolean", 1034 affectsBindDiagnostics: true, 1035 affectsSemanticDiagnostics: true, 1036 category: Diagnostics.Advanced_Options, 1037 description: Diagnostics.Do_not_report_errors_on_unreachable_code 1038 }, 1039 { 1040 name: "suppressExcessPropertyErrors", 1041 type: "boolean", 1042 affectsSemanticDiagnostics: true, 1043 category: Diagnostics.Advanced_Options, 1044 description: Diagnostics.Suppress_excess_property_checks_for_object_literals, 1045 }, 1046 { 1047 name: "suppressImplicitAnyIndexErrors", 1048 type: "boolean", 1049 affectsSemanticDiagnostics: true, 1050 category: Diagnostics.Advanced_Options, 1051 description: Diagnostics.Suppress_noImplicitAny_errors_for_indexing_objects_lacking_index_signatures, 1052 }, 1053 { 1054 name: "forceConsistentCasingInFileNames", 1055 type: "boolean", 1056 affectsModuleResolution: true, 1057 category: Diagnostics.Advanced_Options, 1058 description: Diagnostics.Disallow_inconsistently_cased_references_to_the_same_file 1059 }, 1060 { 1061 name: "maxNodeModuleJsDepth", 1062 type: "number", 1063 affectsModuleResolution: true, 1064 category: Diagnostics.Advanced_Options, 1065 description: Diagnostics.The_maximum_dependency_depth_to_search_under_node_modules_and_load_JavaScript_files 1066 }, 1067 { 1068 name: "noStrictGenericChecks", 1069 type: "boolean", 1070 affectsSemanticDiagnostics: true, 1071 category: Diagnostics.Advanced_Options, 1072 description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types, 1073 }, 1074 { 1075 name: "useDefineForClassFields", 1076 type: "boolean", 1077 affectsSemanticDiagnostics: true, 1078 affectsEmit: true, 1079 category: Diagnostics.Advanced_Options, 1080 description: Diagnostics.Emit_class_fields_with_Define_instead_of_Set, 1081 }, 1082 1083 { 1084 name: "keyofStringsOnly", 1085 type: "boolean", 1086 category: Diagnostics.Advanced_Options, 1087 description: Diagnostics.Resolve_keyof_to_string_valued_property_names_only_no_numbers_or_symbols, 1088 }, 1089 { 1090 // A list of plugins to load in the language service 1091 name: "plugins", 1092 type: "list", 1093 isTSConfigOnly: true, 1094 element: { 1095 name: "plugin", 1096 type: "object" 1097 }, 1098 description: Diagnostics.List_of_language_service_plugins 1099 }, 1100 ]; 1101 1102 /* @internal */ 1103 export const semanticDiagnosticsOptionDeclarations: readonly CommandLineOption[] = 1104 optionDeclarations.filter(option => !!option.affectsSemanticDiagnostics); 1105 1106 /* @internal */ 1107 export const affectsEmitOptionDeclarations: readonly CommandLineOption[] = 1108 optionDeclarations.filter(option => !!option.affectsEmit); 1109 1110 /* @internal */ 1111 export const moduleResolutionOptionDeclarations: readonly CommandLineOption[] = 1112 optionDeclarations.filter(option => !!option.affectsModuleResolution); 1113 1114 /* @internal */ 1115 export const sourceFileAffectingCompilerOptions: readonly CommandLineOption[] = optionDeclarations.filter(option => 1116 !!option.affectsSourceFile || !!option.affectsModuleResolution || !!option.affectsBindDiagnostics); 1117 1118 /* @internal */ 1119 export const transpileOptionValueCompilerOptions: readonly CommandLineOption[] = optionDeclarations.filter(option => 1120 hasProperty(option, "transpileOptionValue")); 1121 1122 /* @internal */ 1123 export const buildOpts: CommandLineOption[] = [ 1124 ...commonOptionsWithBuild, 1125 { 1126 name: "verbose", 1127 shortName: "v", 1128 category: Diagnostics.Command_line_Options, 1129 description: Diagnostics.Enable_verbose_logging, 1130 type: "boolean" 1131 }, 1132 { 1133 name: "dry", 1134 shortName: "d", 1135 category: Diagnostics.Command_line_Options, 1136 description: Diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean, 1137 type: "boolean" 1138 }, 1139 { 1140 name: "force", 1141 shortName: "f", 1142 category: Diagnostics.Command_line_Options, 1143 description: Diagnostics.Build_all_projects_including_those_that_appear_to_be_up_to_date, 1144 type: "boolean" 1145 }, 1146 { 1147 name: "clean", 1148 category: Diagnostics.Command_line_Options, 1149 description: Diagnostics.Delete_the_outputs_of_all_projects, 1150 type: "boolean" 1151 } 1152 ]; 1153 1154 /* @internal */ 1155 export const typeAcquisitionDeclarations: CommandLineOption[] = [ 1156 { 1157 /* @deprecated typingOptions.enableAutoDiscovery 1158 * Use typeAcquisition.enable instead. 1159 */ 1160 name: "enableAutoDiscovery", 1161 type: "boolean", 1162 }, 1163 { 1164 name: "enable", 1165 type: "boolean", 1166 }, 1167 { 1168 name: "include", 1169 type: "list", 1170 element: { 1171 name: "include", 1172 type: "string" 1173 } 1174 }, 1175 { 1176 name: "exclude", 1177 type: "list", 1178 element: { 1179 name: "exclude", 1180 type: "string" 1181 } 1182 }, 1183 { 1184 name: "disableFilenameBasedTypeAcquisition", 1185 type: "boolean", 1186 }, 1187 ]; 1188 1189 /* @internal */ 1190 export interface OptionsNameMap { 1191 optionsNameMap: ESMap<string, CommandLineOption>; 1192 shortOptionNames: ESMap<string, string>; 1193 } 1194 1195 /*@internal*/ 1196 export function createOptionNameMap(optionDeclarations: readonly CommandLineOption[]): OptionsNameMap { 1197 const optionsNameMap = new Map<string, CommandLineOption>(); 1198 const shortOptionNames = new Map<string, string>(); 1199 forEach(optionDeclarations, option => { 1200 optionsNameMap.set(option.name.toLowerCase(), option); 1201 if (option.shortName) { 1202 shortOptionNames.set(option.shortName, option.name); 1203 } 1204 }); 1205 1206 return { optionsNameMap, shortOptionNames }; 1207 } 1208 1209 let optionsNameMapCache: OptionsNameMap; 1210 1211 /* @internal */ 1212 export function getOptionsNameMap(): OptionsNameMap { 1213 return optionsNameMapCache || (optionsNameMapCache = createOptionNameMap(optionDeclarations)); 1214 } 1215 1216 /* @internal */ 1217 export const defaultInitCompilerOptions: CompilerOptions = { 1218 module: ModuleKind.CommonJS, 1219 target: ScriptTarget.ES5, 1220 strict: true, 1221 esModuleInterop: true, 1222 forceConsistentCasingInFileNames: true, 1223 skipLibCheck: true 1224 }; 1225 1226 /* @internal */ 1227 export function convertEnableAutoDiscoveryToEnable(typeAcquisition: TypeAcquisition): TypeAcquisition { 1228 // Convert deprecated typingOptions.enableAutoDiscovery to typeAcquisition.enable 1229 if (typeAcquisition && typeAcquisition.enableAutoDiscovery !== undefined && typeAcquisition.enable === undefined) { 1230 return { 1231 enable: typeAcquisition.enableAutoDiscovery, 1232 include: typeAcquisition.include || [], 1233 exclude: typeAcquisition.exclude || [] 1234 }; 1235 } 1236 return typeAcquisition; 1237 } 1238 1239 /* @internal */ 1240 export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { 1241 return createDiagnosticForInvalidCustomType(opt, createCompilerDiagnostic); 1242 } 1243 1244 function createDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType, createDiagnostic: (message: DiagnosticMessage, arg0: string, arg1: string) => Diagnostic): Diagnostic { 1245 const namesOfType = arrayFrom(opt.type.keys()).map(key => `'${key}'`).join(", "); 1246 return createDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); 1247 } 1248 1249 /* @internal */ 1250 export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) { 1251 return convertJsonOptionOfCustomType(opt, trimString(value || ""), errors); 1252 } 1253 1254 /* @internal */ 1255 export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Push<Diagnostic>): (string | number)[] | undefined { 1256 value = trimString(value); 1257 if (startsWith(value, "-")) { 1258 return undefined; 1259 } 1260 if (value === "") { 1261 return []; 1262 } 1263 const values = value.split(","); 1264 switch (opt.element.type) { 1265 case "number": 1266 return mapDefined(values, v => validateJsonOptionValue(opt.element, parseInt(v), errors)); 1267 case "string": 1268 return mapDefined(values, v => validateJsonOptionValue(opt.element, v || "", errors)); 1269 default: 1270 return mapDefined(values, v => parseCustomTypeOption(<CommandLineOptionOfCustomType>opt.element, v, errors)); 1271 } 1272 } 1273 1274 /*@internal*/ 1275 export interface OptionsBase { 1276 [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; 1277 } 1278 1279 /*@internal*/ 1280 export interface ParseCommandLineWorkerDiagnostics extends DidYouMeanOptionsDiagnostics { 1281 getOptionsNameMap: () => OptionsNameMap; 1282 optionTypeMismatchDiagnostic: DiagnosticMessage; 1283 } 1284 1285 function getOptionName(option: CommandLineOption) { 1286 return option.name; 1287 } 1288 1289 function createUnknownOptionError( 1290 unknownOption: string, 1291 diagnostics: DidYouMeanOptionsDiagnostics, 1292 createDiagnostics: (message: DiagnosticMessage, arg0: string, arg1?: string) => Diagnostic, 1293 unknownOptionErrorText?: string 1294 ) { 1295 const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName); 1296 return possibleOption ? 1297 createDiagnostics(diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : 1298 createDiagnostics(diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption); 1299 } 1300 1301 /*@internal*/ 1302 export function parseCommandLineWorker( 1303 diagnostics: ParseCommandLineWorkerDiagnostics, 1304 commandLine: readonly string[], 1305 readFile?: (path: string) => string | undefined) { 1306 const options = {} as OptionsBase; 1307 let watchOptions: WatchOptions | undefined; 1308 const fileNames: string[] = []; 1309 const errors: Diagnostic[] = []; 1310 1311 parseStrings(commandLine); 1312 return { 1313 options, 1314 watchOptions, 1315 fileNames, 1316 errors 1317 }; 1318 1319 function parseStrings(args: readonly string[]) { 1320 let i = 0; 1321 while (i < args.length) { 1322 const s = args[i]; 1323 i++; 1324 if (s.charCodeAt(0) === CharacterCodes.at) { 1325 parseResponseFile(s.slice(1)); 1326 } 1327 else if (s.charCodeAt(0) === CharacterCodes.minus) { 1328 const inputOptionName = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1); 1329 const opt = getOptionDeclarationFromName(diagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true); 1330 if (opt) { 1331 i = parseOptionValue(args, i, diagnostics, opt, options, errors); 1332 } 1333 else { 1334 const watchOpt = getOptionDeclarationFromName(watchOptionsDidYouMeanDiagnostics.getOptionsNameMap, inputOptionName, /*allowShort*/ true); 1335 if (watchOpt) { 1336 i = parseOptionValue(args, i, watchOptionsDidYouMeanDiagnostics, watchOpt, watchOptions || (watchOptions = {}), errors); 1337 } 1338 else { 1339 errors.push(createUnknownOptionError(inputOptionName, diagnostics, createCompilerDiagnostic, s)); 1340 } 1341 } 1342 } 1343 else { 1344 fileNames.push(s); 1345 } 1346 } 1347 } 1348 1349 function parseResponseFile(fileName: string) { 1350 const text = tryReadFile(fileName, readFile || (fileName => sys.readFile(fileName))); 1351 if (!isString(text)) { 1352 errors.push(text); 1353 return; 1354 } 1355 1356 const args: string[] = []; 1357 let pos = 0; 1358 while (true) { 1359 while (pos < text.length && text.charCodeAt(pos) <= CharacterCodes.space) pos++; 1360 if (pos >= text.length) break; 1361 const start = pos; 1362 if (text.charCodeAt(start) === CharacterCodes.doubleQuote) { 1363 pos++; 1364 while (pos < text.length && text.charCodeAt(pos) !== CharacterCodes.doubleQuote) pos++; 1365 if (pos < text.length) { 1366 args.push(text.substring(start + 1, pos)); 1367 pos++; 1368 } 1369 else { 1370 errors.push(createCompilerDiagnostic(Diagnostics.Unterminated_quoted_string_in_response_file_0, fileName)); 1371 } 1372 } 1373 else { 1374 while (text.charCodeAt(pos) > CharacterCodes.space) pos++; 1375 args.push(text.substring(start, pos)); 1376 } 1377 } 1378 parseStrings(args); 1379 } 1380 } 1381 1382 function parseOptionValue( 1383 args: readonly string[], 1384 i: number, 1385 diagnostics: ParseCommandLineWorkerDiagnostics, 1386 opt: CommandLineOption, 1387 options: OptionsBase, 1388 errors: Diagnostic[] 1389 ) { 1390 if (opt.isTSConfigOnly) { 1391 const optValue = args[i]; 1392 if (optValue === "null") { 1393 options[opt.name] = undefined; 1394 i++; 1395 } 1396 else if (opt.type === "boolean") { 1397 if (optValue === "false") { 1398 options[opt.name] = validateJsonOptionValue(opt, /*value*/ false, errors); 1399 i++; 1400 } 1401 else { 1402 if (optValue === "true") i++; 1403 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)); 1404 } 1405 } 1406 else { 1407 errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_null_on_command_line, opt.name)); 1408 if (optValue && !startsWith(optValue, "-")) i++; 1409 } 1410 } 1411 else { 1412 // Check to see if no argument was provided (e.g. "--locale" is the last command-line argument). 1413 if (!args[i] && opt.type !== "boolean") { 1414 errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt))); 1415 } 1416 1417 if (args[i] !== "null") { 1418 switch (opt.type) { 1419 case "number": 1420 options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors); 1421 i++; 1422 break; 1423 case "boolean": 1424 // boolean flag has optional value true, false, others 1425 const optValue = args[i]; 1426 options[opt.name] = validateJsonOptionValue(opt, optValue !== "false", errors); 1427 // consume next argument as boolean flag value 1428 if (optValue === "false" || optValue === "true") { 1429 i++; 1430 } 1431 break; 1432 case "string": 1433 options[opt.name] = validateJsonOptionValue(opt, args[i] || "", errors); 1434 i++; 1435 break; 1436 case "list": 1437 const result = parseListTypeOption(opt, args[i], errors); 1438 options[opt.name] = result || []; 1439 if (result) { 1440 i++; 1441 } 1442 break; 1443 // If not a primitive, the possible types are specified in what is effectively a map of options. 1444 default: 1445 options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors); 1446 i++; 1447 break; 1448 } 1449 } 1450 else { 1451 options[opt.name] = undefined; 1452 i++; 1453 } 1454 } 1455 return i; 1456 } 1457 1458 /*@internal*/ 1459 export const compilerOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1460 getOptionsNameMap, 1461 optionDeclarations, 1462 unknownOptionDiagnostic: Diagnostics.Unknown_compiler_option_0, 1463 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1, 1464 optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument 1465 }; 1466 export function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine { 1467 return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile); 1468 } 1469 1470 /** @internal */ 1471 export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined { 1472 return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort); 1473 } 1474 1475 function getOptionDeclarationFromName(getOptionNameMap: () => OptionsNameMap, optionName: string, allowShort = false): CommandLineOption | undefined { 1476 optionName = optionName.toLowerCase(); 1477 const { optionsNameMap, shortOptionNames } = getOptionNameMap(); 1478 // Try to translate short option names to their full equivalents. 1479 if (allowShort) { 1480 const short = shortOptionNames.get(optionName); 1481 if (short !== undefined) { 1482 optionName = short; 1483 } 1484 } 1485 return optionsNameMap.get(optionName); 1486 } 1487 1488 /*@internal*/ 1489 export interface ParsedBuildCommand { 1490 buildOptions: BuildOptions; 1491 watchOptions: WatchOptions | undefined; 1492 projects: string[]; 1493 errors: Diagnostic[]; 1494 } 1495 1496 let buildOptionsNameMapCache: OptionsNameMap; 1497 function getBuildOptionsNameMap(): OptionsNameMap { 1498 return buildOptionsNameMapCache || (buildOptionsNameMapCache = createOptionNameMap(buildOpts)); 1499 } 1500 1501 const buildOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1502 getOptionsNameMap: getBuildOptionsNameMap, 1503 optionDeclarations: buildOpts, 1504 unknownOptionDiagnostic: Diagnostics.Unknown_build_option_0, 1505 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_build_option_0_Did_you_mean_1, 1506 optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1 1507 }; 1508 1509 /*@internal*/ 1510 export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand { 1511 const { options, watchOptions, fileNames: projects, errors } = parseCommandLineWorker( 1512 buildOptionsDidYouMeanDiagnostics, 1513 args 1514 ); 1515 const buildOptions = options as BuildOptions; 1516 1517 if (projects.length === 0) { 1518 // tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ." 1519 projects.push("."); 1520 } 1521 1522 // Nonsensical combinations 1523 if (buildOptions.clean && buildOptions.force) { 1524 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force")); 1525 } 1526 if (buildOptions.clean && buildOptions.verbose) { 1527 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose")); 1528 } 1529 if (buildOptions.clean && buildOptions.watch) { 1530 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch")); 1531 } 1532 if (buildOptions.watch && buildOptions.dry) { 1533 errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry")); 1534 } 1535 1536 return { buildOptions, watchOptions, projects, errors }; 1537 } 1538 1539 /* @internal */ 1540 export function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string { 1541 const diagnostic = createCompilerDiagnostic.apply(undefined, arguments); 1542 return <string>diagnostic.messageText; 1543 } 1544 1545 export type DiagnosticReporter = (diagnostic: Diagnostic) => void; 1546 /** 1547 * Reports config file diagnostics 1548 */ 1549 export interface ConfigFileDiagnosticsReporter { 1550 /** 1551 * Reports unrecoverable error when parsing config file 1552 */ 1553 onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; 1554 } 1555 1556 /** 1557 * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors 1558 */ 1559 export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { 1560 getCurrentDirectory(): string; 1561 } 1562 1563 /** 1564 * Reads the config file, reports errors if any and exits if the config file cannot be found 1565 */ 1566 export function getParsedCommandLineOfConfigFile( 1567 configFileName: string, 1568 optionsToExtend: CompilerOptions, 1569 host: ParseConfigFileHost, 1570 extendedConfigCache?: Map<ExtendedConfigCacheEntry>, 1571 watchOptionsToExtend?: WatchOptions, 1572 extraFileExtensions?: readonly FileExtensionInfo[], 1573 ): ParsedCommandLine | undefined { 1574 const configFileText = tryReadFile(configFileName, fileName => host.readFile(fileName)); 1575 if (!isString(configFileText)) { 1576 host.onUnRecoverableConfigFileDiagnostic(configFileText); 1577 return undefined; 1578 } 1579 1580 const result = parseJsonText(configFileName, configFileText); 1581 const cwd = host.getCurrentDirectory(); 1582 result.path = toPath(configFileName, cwd, createGetCanonicalFileName(host.useCaseSensitiveFileNames)); 1583 result.resolvedPath = result.path; 1584 result.originalFileName = result.fileName; 1585 return parseJsonSourceFileConfigFileContent( 1586 result, 1587 host, 1588 getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), 1589 optionsToExtend, 1590 getNormalizedAbsolutePath(configFileName, cwd), 1591 /*resolutionStack*/ undefined, 1592 extraFileExtensions, 1593 extendedConfigCache, 1594 watchOptionsToExtend 1595 ); 1596 } 1597 1598 /** 1599 * Read tsconfig.json file 1600 * @param fileName The path to the config file 1601 */ 1602 export function readConfigFile(fileName: string, readFile: (path: string) => string | undefined): { config?: any; error?: Diagnostic } { 1603 const textOrDiagnostic = tryReadFile(fileName, readFile); 1604 return isString(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic }; 1605 } 1606 1607 /** 1608 * Parse the text of the tsconfig.json file 1609 * @param fileName The path to the config file 1610 * @param jsonText The text of the config file 1611 */ 1612 export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } { 1613 const jsonSourceFile = parseJsonText(fileName, jsonText); 1614 return { 1615 config: convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics), 1616 error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined 1617 }; 1618 } 1619 1620 /** 1621 * Read tsconfig.json file 1622 * @param fileName The path to the config file 1623 */ 1624 export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile { 1625 const textOrDiagnostic = tryReadFile(fileName, readFile); 1626 return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : <TsConfigSourceFile>{ fileName, parseDiagnostics: [textOrDiagnostic] }; 1627 } 1628 1629 /*@internal*/ 1630 export function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { 1631 let text: string | undefined; 1632 try { 1633 text = readFile(fileName); 1634 } 1635 catch (e) { 1636 return createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message); 1637 } 1638 return text === undefined ? createCompilerDiagnostic(Diagnostics.Cannot_read_file_0, fileName) : text; 1639 } 1640 1641 function commandLineOptionsToMap(options: readonly CommandLineOption[]) { 1642 return arrayToMap(options, getOptionName); 1643 } 1644 1645 const typeAcquisitionDidYouMeanDiagnostics: DidYouMeanOptionsDiagnostics = { 1646 optionDeclarations: typeAcquisitionDeclarations, 1647 unknownOptionDiagnostic: Diagnostics.Unknown_type_acquisition_option_0, 1648 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_type_acquisition_option_0_Did_you_mean_1, 1649 }; 1650 1651 let watchOptionsNameMapCache: OptionsNameMap; 1652 function getWatchOptionsNameMap(): OptionsNameMap { 1653 return watchOptionsNameMapCache || (watchOptionsNameMapCache = createOptionNameMap(optionsForWatch)); 1654 } 1655 const watchOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = { 1656 getOptionsNameMap: getWatchOptionsNameMap, 1657 optionDeclarations: optionsForWatch, 1658 unknownOptionDiagnostic: Diagnostics.Unknown_watch_option_0, 1659 unknownDidYouMeanDiagnostic: Diagnostics.Unknown_watch_option_0_Did_you_mean_1, 1660 optionTypeMismatchDiagnostic: Diagnostics.Watch_option_0_requires_a_value_of_type_1 1661 }; 1662 1663 let commandLineCompilerOptionsMapCache: ESMap<string, CommandLineOption>; 1664 function getCommandLineCompilerOptionsMap() { 1665 return commandLineCompilerOptionsMapCache || (commandLineCompilerOptionsMapCache = commandLineOptionsToMap(optionDeclarations)); 1666 } 1667 let commandLineWatchOptionsMapCache: ESMap<string, CommandLineOption>; 1668 function getCommandLineWatchOptionsMap() { 1669 return commandLineWatchOptionsMapCache || (commandLineWatchOptionsMapCache = commandLineOptionsToMap(optionsForWatch)); 1670 } 1671 let commandLineTypeAcquisitionMapCache: ESMap<string, CommandLineOption>; 1672 function getCommandLineTypeAcquisitionMap() { 1673 return commandLineTypeAcquisitionMapCache || (commandLineTypeAcquisitionMapCache = commandLineOptionsToMap(typeAcquisitionDeclarations)); 1674 } 1675 1676 let _tsconfigRootOptions: TsConfigOnlyOption; 1677 function getTsconfigRootOptionsMap() { 1678 if (_tsconfigRootOptions === undefined) { 1679 _tsconfigRootOptions = { 1680 name: undefined!, // should never be needed since this is root 1681 type: "object", 1682 elementOptions: commandLineOptionsToMap([ 1683 { 1684 name: "compilerOptions", 1685 type: "object", 1686 elementOptions: getCommandLineCompilerOptionsMap(), 1687 extraKeyDiagnostics: compilerOptionsDidYouMeanDiagnostics, 1688 }, 1689 { 1690 name: "watchOptions", 1691 type: "object", 1692 elementOptions: getCommandLineWatchOptionsMap(), 1693 extraKeyDiagnostics: watchOptionsDidYouMeanDiagnostics, 1694 }, 1695 { 1696 name: "typingOptions", 1697 type: "object", 1698 elementOptions: getCommandLineTypeAcquisitionMap(), 1699 extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics, 1700 }, 1701 { 1702 name: "typeAcquisition", 1703 type: "object", 1704 elementOptions: getCommandLineTypeAcquisitionMap(), 1705 extraKeyDiagnostics: typeAcquisitionDidYouMeanDiagnostics 1706 }, 1707 { 1708 name: "extends", 1709 type: "string" 1710 }, 1711 { 1712 name: "references", 1713 type: "list", 1714 element: { 1715 name: "references", 1716 type: "object" 1717 } 1718 }, 1719 { 1720 name: "files", 1721 type: "list", 1722 element: { 1723 name: "files", 1724 type: "string" 1725 } 1726 }, 1727 { 1728 name: "include", 1729 type: "list", 1730 element: { 1731 name: "include", 1732 type: "string" 1733 } 1734 }, 1735 { 1736 name: "exclude", 1737 type: "list", 1738 element: { 1739 name: "exclude", 1740 type: "string" 1741 } 1742 }, 1743 compileOnSaveCommandLineOption 1744 ]) 1745 }; 1746 } 1747 return _tsconfigRootOptions; 1748 } 1749 1750 /*@internal*/ 1751 interface JsonConversionNotifier { 1752 /** 1753 * Notifies parent option object is being set with the optionKey and a valid optionValue 1754 * Currently it notifies only if there is element with type object (parentOption) and 1755 * has element's option declarations map associated with it 1756 * @param parentOption parent option name in which the option and value are being set 1757 * @param option option declaration which is being set with the value 1758 * @param value value of the option 1759 */ 1760 onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue): void; 1761 /** 1762 * Notify when valid root key value option is being set 1763 * @param key option key 1764 * @param keyNode node corresponding to node in the source file 1765 * @param value computed value of the key 1766 * @param ValueNode node corresponding to value in the source file 1767 */ 1768 onSetValidOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void; 1769 /** 1770 * Notify when unknown root key value option is being set 1771 * @param key option key 1772 * @param keyNode node corresponding to node in the source file 1773 * @param value computed value of the key 1774 * @param ValueNode node corresponding to value in the source file 1775 */ 1776 onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression): void; 1777 } 1778 1779 /** 1780 * Convert the json syntax tree into the json value 1781 */ 1782 export function convertToObject(sourceFile: JsonSourceFile, errors: Push<Diagnostic>): any { 1783 return convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined); 1784 } 1785 1786 /** 1787 * Convert the json syntax tree into the json value and report errors 1788 * This returns the json value (apart from checking errors) only if returnValue provided is true. 1789 * Otherwise it just checks the errors and returns undefined 1790 */ 1791 /*@internal*/ 1792 export function convertToObjectWorker( 1793 sourceFile: JsonSourceFile, 1794 errors: Push<Diagnostic>, 1795 returnValue: boolean, 1796 knownRootOptions: CommandLineOption | undefined, 1797 jsonConversionNotifier: JsonConversionNotifier | undefined): any { 1798 if (!sourceFile.statements.length) { 1799 return returnValue ? {} : undefined; 1800 } 1801 1802 return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); 1803 1804 function isRootOptionMap(knownOptions: ESMap<string, CommandLineOption> | undefined) { 1805 return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; 1806 } 1807 1808 function convertObjectLiteralExpressionToJson( 1809 node: ObjectLiteralExpression, 1810 knownOptions: ESMap<string, CommandLineOption> | undefined, 1811 extraKeyDiagnostics: DidYouMeanOptionsDiagnostics | undefined, 1812 parentOption: string | undefined 1813 ): any { 1814 const result: any = returnValue ? {} : undefined; 1815 for (const element of node.properties) { 1816 if (element.kind !== SyntaxKind.PropertyAssignment) { 1817 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element, Diagnostics.Property_assignment_expected)); 1818 continue; 1819 } 1820 1821 if (element.questionToken) { 1822 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.questionToken, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?")); 1823 } 1824 if (!isDoubleQuotedString(element.name)) { 1825 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, Diagnostics.String_literal_with_double_quotes_expected)); 1826 } 1827 1828 const textOfKey = isComputedNonLiteralName(element.name) ? undefined : getTextOfPropertyName(element.name); 1829 const keyText = textOfKey && unescapeLeadingUnderscores(textOfKey); 1830 const option = keyText && knownOptions ? knownOptions.get(keyText) : undefined; 1831 if (keyText && extraKeyDiagnostics && !option) { 1832 if (knownOptions) { 1833 errors.push(createUnknownOptionError( 1834 keyText, 1835 extraKeyDiagnostics, 1836 (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, element.name, message, arg0, arg1) 1837 )); 1838 } 1839 else { 1840 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, element.name, extraKeyDiagnostics.unknownOptionDiagnostic, keyText)); 1841 } 1842 } 1843 const value = convertPropertyValueToJson(element.initializer, option); 1844 if (typeof keyText !== "undefined") { 1845 if (returnValue) { 1846 result[keyText] = value; 1847 } 1848 // Notify key value set, if user asked for it 1849 if (jsonConversionNotifier && 1850 // Current callbacks are only on known parent option or if we are setting values in the root 1851 (parentOption || isRootOptionMap(knownOptions))) { 1852 const isValidOptionValue = isCompilerOptionsValue(option, value); 1853 if (parentOption) { 1854 if (isValidOptionValue) { 1855 // Notify option set in the parent if its a valid option value 1856 jsonConversionNotifier.onSetValidOptionKeyValueInParent(parentOption, option!, value); 1857 } 1858 } 1859 else if (isRootOptionMap(knownOptions)) { 1860 if (isValidOptionValue) { 1861 // Notify about the valid root key value being set 1862 jsonConversionNotifier.onSetValidOptionKeyValueInRoot(keyText, element.name, value, element.initializer); 1863 } 1864 else if (!option) { 1865 // Notify about the unknown root key value being set 1866 jsonConversionNotifier.onSetUnknownOptionKeyValueInRoot(keyText, element.name, value, element.initializer); 1867 } 1868 } 1869 } 1870 } 1871 } 1872 return result; 1873 } 1874 1875 function convertArrayLiteralExpressionToJson( 1876 elements: NodeArray<Expression>, 1877 elementOption: CommandLineOption | undefined 1878 ) { 1879 if (!returnValue) { 1880 elements.forEach(element => convertPropertyValueToJson(element, elementOption)); 1881 return undefined; 1882 } 1883 1884 // Filter out invalid values 1885 return filter(elements.map(element => convertPropertyValueToJson(element, elementOption)), v => v !== undefined); 1886 } 1887 1888 function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption | undefined): any { 1889 let invalidReported: boolean | undefined; 1890 switch (valueExpression.kind) { 1891 case SyntaxKind.TrueKeyword: 1892 reportInvalidOptionValue(option && option.type !== "boolean"); 1893 return validateValue(/*value*/ true); 1894 1895 case SyntaxKind.FalseKeyword: 1896 reportInvalidOptionValue(option && option.type !== "boolean"); 1897 return validateValue(/*value*/ false); 1898 1899 case SyntaxKind.NullKeyword: 1900 reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for 1901 return validateValue(/*value*/ null); // eslint-disable-line no-null/no-null 1902 1903 case SyntaxKind.StringLiteral: 1904 if (!isDoubleQuotedString(valueExpression)) { 1905 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.String_literal_with_double_quotes_expected)); 1906 } 1907 reportInvalidOptionValue(option && (isString(option.type) && option.type !== "string")); 1908 const text = (<StringLiteral>valueExpression).text; 1909 if (option && !isString(option.type)) { 1910 const customOption = <CommandLineOptionOfCustomType>option; 1911 // Validate custom option type 1912 if (!customOption.type.has(text.toLowerCase())) { 1913 errors.push( 1914 createDiagnosticForInvalidCustomType( 1915 customOption, 1916 (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1) 1917 ) 1918 ); 1919 invalidReported = true; 1920 } 1921 } 1922 return validateValue(text); 1923 1924 case SyntaxKind.NumericLiteral: 1925 reportInvalidOptionValue(option && option.type !== "number"); 1926 return validateValue(Number((<NumericLiteral>valueExpression).text)); 1927 1928 case SyntaxKind.PrefixUnaryExpression: 1929 if ((<PrefixUnaryExpression>valueExpression).operator !== SyntaxKind.MinusToken || (<PrefixUnaryExpression>valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { 1930 break; // not valid JSON syntax 1931 } 1932 reportInvalidOptionValue(option && option.type !== "number"); 1933 return validateValue(-Number((<NumericLiteral>(<PrefixUnaryExpression>valueExpression).operand).text)); 1934 1935 case SyntaxKind.ObjectLiteralExpression: 1936 reportInvalidOptionValue(option && option.type !== "object"); 1937 const objectLiteralExpression = <ObjectLiteralExpression>valueExpression; 1938 1939 // Currently having element option declaration in the tsconfig with type "object" 1940 // determines if it needs onSetValidOptionKeyValueInParent callback or not 1941 // At moment there are only "compilerOptions", "typeAcquisition" and "typingOptions" 1942 // that satifies it and need it to modify options set in them (for normalizing file paths) 1943 // vs what we set in the json 1944 // If need arises, we can modify this interface and callbacks as needed 1945 if (option) { 1946 const { elementOptions, extraKeyDiagnostics, name: optionName } = <TsConfigOnlyOption>option; 1947 return validateValue(convertObjectLiteralExpressionToJson(objectLiteralExpression, 1948 elementOptions, extraKeyDiagnostics, optionName)); 1949 } 1950 else { 1951 return validateValue(convertObjectLiteralExpressionToJson( 1952 objectLiteralExpression, /* knownOptions*/ undefined, 1953 /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined)); 1954 } 1955 1956 case SyntaxKind.ArrayLiteralExpression: 1957 reportInvalidOptionValue(option && option.type !== "list"); 1958 return validateValue(convertArrayLiteralExpressionToJson( 1959 (<ArrayLiteralExpression>valueExpression).elements, 1960 option && (<CommandLineOptionOfListType>option).element)); 1961 } 1962 1963 // Not in expected format 1964 if (option) { 1965 reportInvalidOptionValue(/*isError*/ true); 1966 } 1967 else { 1968 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Property_value_can_only_be_string_literal_numeric_literal_true_false_null_object_literal_or_array_literal)); 1969 } 1970 1971 return undefined; 1972 1973 function validateValue(value: CompilerOptionsValue) { 1974 if (!invalidReported) { 1975 const diagnostic = option?.extraValidation?.(value); 1976 if (diagnostic) { 1977 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, ...diagnostic)); 1978 return undefined; 1979 } 1980 } 1981 return value; 1982 } 1983 1984 function reportInvalidOptionValue(isError: boolean | undefined) { 1985 if (isError) { 1986 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option!.name, getCompilerOptionValueTypeString(option!))); 1987 invalidReported = true; 1988 } 1989 } 1990 } 1991 1992 function isDoubleQuotedString(node: Node): boolean { 1993 return isStringLiteral(node) && isStringDoubleQuoted(node, sourceFile); 1994 } 1995 } 1996 1997 function getCompilerOptionValueTypeString(option: CommandLineOption) { 1998 return option.type === "list" ? 1999 "Array" : 2000 isString(option.type) ? option.type : "string"; 2001 } 2002 2003 function isCompilerOptionsValue(option: CommandLineOption | undefined, value: any): value is CompilerOptionsValue { 2004 if (option) { 2005 if (isNullOrUndefined(value)) return true; // All options are undefinable/nullable 2006 if (option.type === "list") { 2007 return isArray(value); 2008 } 2009 const expectedType = isString(option.type) ? option.type : "string"; 2010 return typeof value === expectedType; 2011 } 2012 return false; 2013 } 2014 2015 /** @internal */ 2016 export interface TSConfig { 2017 compilerOptions: CompilerOptions; 2018 compileOnSave: boolean | undefined; 2019 exclude?: readonly string[]; 2020 files: readonly string[] | undefined; 2021 include?: readonly string[]; 2022 references: readonly ProjectReference[] | undefined; 2023 } 2024 2025 /** @internal */ 2026 export interface ConvertToTSConfigHost { 2027 getCurrentDirectory(): string; 2028 useCaseSensitiveFileNames: boolean; 2029 } 2030 2031 /** 2032 * Generate an uncommented, complete tsconfig for use with "--showConfig" 2033 * @param configParseResult options to be generated into tsconfig.json 2034 * @param configFileName name of the parsed config file - output paths will be generated relative to this 2035 * @param host provides current directory and case sensitivity services 2036 */ 2037 /** @internal */ 2038 export function convertToTSConfig(configParseResult: ParsedCommandLine, configFileName: string, host: ConvertToTSConfigHost): TSConfig { 2039 const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); 2040 const files = map( 2041 filter( 2042 configParseResult.fileNames, 2043 !configParseResult.options.configFile?.configFileSpecs?.validatedIncludeSpecs ? returnTrue : matchesSpecs( 2044 configFileName, 2045 configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs, 2046 configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs, 2047 host, 2048 ) 2049 ), 2050 f => getRelativePathFromFile(getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), getNormalizedAbsolutePath(f, host.getCurrentDirectory()), getCanonicalFileName) 2051 ); 2052 const optionMap = serializeCompilerOptions(configParseResult.options, { configFilePath: getNormalizedAbsolutePath(configFileName, host.getCurrentDirectory()), useCaseSensitiveFileNames: host.useCaseSensitiveFileNames }); 2053 const watchOptionMap = configParseResult.watchOptions && serializeWatchOptions(configParseResult.watchOptions); 2054 const config = { 2055 compilerOptions: { 2056 ...optionMapToObject(optionMap), 2057 showConfig: undefined, 2058 configFile: undefined, 2059 configFilePath: undefined, 2060 help: undefined, 2061 init: undefined, 2062 listFiles: undefined, 2063 listEmittedFiles: undefined, 2064 project: undefined, 2065 build: undefined, 2066 version: undefined, 2067 }, 2068 watchOptions: watchOptionMap && optionMapToObject(watchOptionMap), 2069 references: map(configParseResult.projectReferences, r => ({ ...r, path: r.originalPath ? r.originalPath : "", originalPath: undefined })), 2070 files: length(files) ? files : undefined, 2071 ...(configParseResult.options.configFile?.configFileSpecs ? { 2072 include: filterSameAsDefaultInclude(configParseResult.options.configFile.configFileSpecs.validatedIncludeSpecs), 2073 exclude: configParseResult.options.configFile.configFileSpecs.validatedExcludeSpecs 2074 } : {}), 2075 compileOnSave: !!configParseResult.compileOnSave ? true : undefined 2076 }; 2077 return config; 2078 } 2079 2080 function optionMapToObject(optionMap: ESMap<string, CompilerOptionsValue>): object { 2081 return { 2082 ...arrayFrom(optionMap.entries()).reduce((prev, cur) => ({ ...prev, [cur[0]]: cur[1] }), {}), 2083 }; 2084 } 2085 2086 function filterSameAsDefaultInclude(specs: readonly string[] | undefined) { 2087 if (!length(specs)) return undefined; 2088 if (length(specs) !== 1) return specs; 2089 if (specs![0] === "**/*") return undefined; 2090 return specs; 2091 } 2092 2093 function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean { 2094 if (!includeSpecs) return returnTrue; 2095 const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory()); 2096 const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames); 2097 const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames); 2098 if (includeRe) { 2099 if (excludeRe) { 2100 return path => !(includeRe.test(path) && !excludeRe.test(path)); 2101 } 2102 return path => !includeRe.test(path); 2103 } 2104 if (excludeRe) { 2105 return path => excludeRe.test(path); 2106 } 2107 return returnTrue; 2108 } 2109 2110 function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): ESMap<string, string | number> | undefined { 2111 if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean" || optionDefinition.type === "object") { 2112 // this is of a type CommandLineOptionOfPrimitiveType 2113 return undefined; 2114 } 2115 else if (optionDefinition.type === "list") { 2116 return getCustomTypeMapOfCommandLineOption(optionDefinition.element); 2117 } 2118 else { 2119 return (<CommandLineOptionOfCustomType>optionDefinition).type; 2120 } 2121 } 2122 2123 function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: ESMap<string, string | number>): string | undefined { 2124 // There is a typeMap associated with this command-line option so use it to map value back to its name 2125 return forEachEntry(customTypeMap, (mapValue, key) => { 2126 if (mapValue === value) { 2127 return key; 2128 } 2129 }); 2130 } 2131 2132 function serializeCompilerOptions( 2133 options: CompilerOptions, 2134 pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } 2135 ): ESMap<string, CompilerOptionsValue> { 2136 return serializeOptionBaseObject(options, getOptionsNameMap(), pathOptions); 2137 } 2138 2139 function serializeWatchOptions(options: WatchOptions) { 2140 return serializeOptionBaseObject(options, getWatchOptionsNameMap()); 2141 } 2142 2143 function serializeOptionBaseObject( 2144 options: OptionsBase, 2145 { optionsNameMap }: OptionsNameMap, 2146 pathOptions?: { configFilePath: string, useCaseSensitiveFileNames: boolean } 2147 ): ESMap<string, CompilerOptionsValue> { 2148 const result = new Map<string, CompilerOptionsValue>(); 2149 const getCanonicalFileName = pathOptions && createGetCanonicalFileName(pathOptions.useCaseSensitiveFileNames); 2150 2151 for (const name in options) { 2152 if (hasProperty(options, name)) { 2153 // tsconfig only options cannot be specified via command line, 2154 // so we can assume that only types that can appear here string | number | boolean 2155 if (optionsNameMap.has(name) && optionsNameMap.get(name)!.category === Diagnostics.Command_line_Options) { 2156 continue; 2157 } 2158 const value = <CompilerOptionsValue>options[name]; 2159 const optionDefinition = optionsNameMap.get(name.toLowerCase()); 2160 if (optionDefinition) { 2161 const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); 2162 if (!customTypeMap) { 2163 // There is no map associated with this compiler option then use the value as-is 2164 // This is the case if the value is expect to be string, number, boolean or list of string 2165 if (pathOptions && optionDefinition.isFilePath) { 2166 result.set(name, getRelativePathFromFile(pathOptions.configFilePath, getNormalizedAbsolutePath(value as string, getDirectoryPath(pathOptions.configFilePath)), getCanonicalFileName!)); 2167 } 2168 else { 2169 result.set(name, value); 2170 } 2171 } 2172 else { 2173 if (optionDefinition.type === "list") { 2174 result.set(name, (value as readonly (string | number)[]).map(element => getNameOfCompilerOptionValue(element, customTypeMap)!)); // TODO: GH#18217 2175 } 2176 else { 2177 // There is a typeMap associated with this command-line option so use it to map value back to its name 2178 result.set(name, getNameOfCompilerOptionValue(value, customTypeMap)); 2179 } 2180 } 2181 } 2182 } 2183 } 2184 return result; 2185 } 2186 2187 /** 2188 * Generate tsconfig configuration when running command line "--init" 2189 * @param options commandlineOptions to be generated into tsconfig.json 2190 * @param fileNames array of filenames to be generated into tsconfig.json 2191 */ 2192 /* @internal */ 2193 export function generateTSConfig(options: CompilerOptions, fileNames: readonly string[], newLine: string): string { 2194 const compilerOptions = extend(options, defaultInitCompilerOptions); 2195 const compilerOptionsMap = serializeCompilerOptions(compilerOptions); 2196 return writeConfigurations(); 2197 2198 function getDefaultValueForOption(option: CommandLineOption) { 2199 switch (option.type) { 2200 case "number": 2201 return 1; 2202 case "boolean": 2203 return true; 2204 case "string": 2205 return option.isFilePath ? "./" : ""; 2206 case "list": 2207 return []; 2208 case "object": 2209 return {}; 2210 default: 2211 const iterResult = option.type.keys().next(); 2212 if (!iterResult.done) return iterResult.value; 2213 return Debug.fail("Expected 'option.type' to have entries."); 2214 } 2215 } 2216 2217 function makePadding(paddingLength: number): string { 2218 return Array(paddingLength + 1).join(" "); 2219 } 2220 2221 function isAllowedOption({ category, name }: CommandLineOption): boolean { 2222 // Skip options which do not have a category or have category `Command_line_Options` 2223 // Exclude all possible `Advanced_Options` in tsconfig.json which were NOT defined in command line 2224 return category !== undefined 2225 && category !== Diagnostics.Command_line_Options 2226 && (category !== Diagnostics.Advanced_Options || compilerOptionsMap.has(name)); 2227 } 2228 2229 function writeConfigurations() { 2230 // Filter applicable options to place in the file 2231 const categorizedOptions = createMultiMap<CommandLineOption>(); 2232 for (const option of optionDeclarations) { 2233 const { category } = option; 2234 2235 if (isAllowedOption(option)) { 2236 categorizedOptions.add(getLocaleSpecificMessage(category!), option); 2237 } 2238 } 2239 2240 // Serialize all options and their descriptions 2241 let marginLength = 0; 2242 let seenKnownKeys = 0; 2243 const entries: { value: string, description?: string }[] = []; 2244 categorizedOptions.forEach((options, category) => { 2245 if (entries.length !== 0) { 2246 entries.push({ value: "" }); 2247 } 2248 entries.push({ value: `/* ${category} */` }); 2249 for (const option of options) { 2250 let optionName; 2251 if (compilerOptionsMap.has(option.name)) { 2252 optionName = `"${option.name}": ${JSON.stringify(compilerOptionsMap.get(option.name))}${(seenKnownKeys += 1) === compilerOptionsMap.size ? "" : ","}`; 2253 } 2254 else { 2255 optionName = `// "${option.name}": ${JSON.stringify(getDefaultValueForOption(option))},`; 2256 } 2257 entries.push({ 2258 value: optionName, 2259 description: `/* ${option.description && getLocaleSpecificMessage(option.description) || option.name} */` 2260 }); 2261 marginLength = Math.max(optionName.length, marginLength); 2262 } 2263 }); 2264 2265 // Write the output 2266 const tab = makePadding(2); 2267 const result: string[] = []; 2268 result.push(`{`); 2269 result.push(`${tab}"compilerOptions": {`); 2270 result.push(`${tab}${tab}/* ${getLocaleSpecificMessage(Diagnostics.Visit_https_Colon_Slash_Slashaka_ms_Slashtsconfig_json_to_read_more_about_this_file)} */`); 2271 result.push(""); 2272 // Print out each row, aligning all the descriptions on the same column. 2273 for (const entry of entries) { 2274 const { value, description = "" } = entry; 2275 result.push(value && `${tab}${tab}${value}${description && (makePadding(marginLength - value.length + 2) + description)}`); 2276 } 2277 if (fileNames.length) { 2278 result.push(`${tab}},`); 2279 result.push(`${tab}"files": [`); 2280 for (let i = 0; i < fileNames.length; i++) { 2281 result.push(`${tab}${tab}${JSON.stringify(fileNames[i])}${i === fileNames.length - 1 ? "" : ","}`); 2282 } 2283 result.push(`${tab}]`); 2284 } 2285 else { 2286 result.push(`${tab}}`); 2287 } 2288 result.push(`}`); 2289 2290 return result.join(newLine) + newLine; 2291 } 2292 } 2293 2294 /* @internal */ 2295 export function convertToOptionsWithAbsolutePaths(options: CompilerOptions, toAbsolutePath: (path: string) => string) { 2296 const result: CompilerOptions = {}; 2297 const optionsNameMap = getOptionsNameMap().optionsNameMap; 2298 2299 for (const name in options) { 2300 if (hasProperty(options, name)) { 2301 result[name] = convertToOptionValueWithAbsolutePaths( 2302 optionsNameMap.get(name.toLowerCase()), 2303 options[name] as CompilerOptionsValue, 2304 toAbsolutePath 2305 ); 2306 } 2307 } 2308 if (result.configFilePath) { 2309 result.configFilePath = toAbsolutePath(result.configFilePath); 2310 } 2311 return result; 2312 } 2313 2314 function convertToOptionValueWithAbsolutePaths(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) { 2315 if (option && !isNullOrUndefined(value)) { 2316 if (option.type === "list") { 2317 const values = value as readonly (string | number)[]; 2318 if (option.element.isFilePath && values.length) { 2319 return values.map(toAbsolutePath); 2320 } 2321 } 2322 else if (option.isFilePath) { 2323 return toAbsolutePath(value as string); 2324 } 2325 } 2326 return value; 2327 } 2328 2329 /** 2330 * Parse the contents of a config file (tsconfig.json). 2331 * @param json The contents of the config file to parse 2332 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2333 * @param basePath A root directory to resolve relative path entries in the config 2334 * file to. e.g. outDir 2335 */ 2336 export function parseJsonConfigFileContent(json: any, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine { 2337 return parseJsonConfigFileContentWorker(json, /*sourceFile*/ undefined, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); 2338 } 2339 2340 /** 2341 * Parse the contents of a config file (tsconfig.json). 2342 * @param jsonNode The contents of the config file to parse 2343 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2344 * @param basePath A root directory to resolve relative path entries in the config 2345 * file to. e.g. outDir 2346 */ 2347 export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: readonly FileExtensionInfo[], extendedConfigCache?: Map<ExtendedConfigCacheEntry>, existingWatchOptions?: WatchOptions): ParsedCommandLine { 2348 return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, existingWatchOptions, configFileName, resolutionStack, extraFileExtensions, extendedConfigCache); 2349 } 2350 2351 /*@internal*/ 2352 export function setConfigFileInOptions(options: CompilerOptions, configFile: TsConfigSourceFile | undefined) { 2353 if (configFile) { 2354 Object.defineProperty(options, "configFile", { enumerable: false, writable: false, value: configFile }); 2355 } 2356 } 2357 2358 function isNullOrUndefined(x: any): x is null | undefined { 2359 return x === undefined || x === null; // eslint-disable-line no-null/no-null 2360 } 2361 2362 function directoryOfCombinedPath(fileName: string, basePath: string) { 2363 // Use the `getNormalizedAbsolutePath` function to avoid canonicalizing the path, as it must remain noncanonical 2364 // until consistent casing errors are reported 2365 return getDirectoryPath(getNormalizedAbsolutePath(fileName, basePath)); 2366 } 2367 2368 /** 2369 * Parse the contents of a config file from json or json source file (tsconfig.json). 2370 * @param json The contents of the config file to parse 2371 * @param sourceFile sourceFile corresponding to the Json 2372 * @param host Instance of ParseConfigHost used to enumerate files in folder. 2373 * @param basePath A root directory to resolve relative path entries in the config 2374 * file to. e.g. outDir 2375 * @param resolutionStack Only present for backwards-compatibility. Should be empty. 2376 */ 2377 function parseJsonConfigFileContentWorker( 2378 json: any, 2379 sourceFile: TsConfigSourceFile | undefined, 2380 host: ParseConfigHost, 2381 basePath: string, 2382 existingOptions: CompilerOptions = {}, 2383 existingWatchOptions: WatchOptions | undefined, 2384 configFileName?: string, 2385 resolutionStack: Path[] = [], 2386 extraFileExtensions: readonly FileExtensionInfo[] = [], 2387 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2388 ): ParsedCommandLine { 2389 Debug.assert((json === undefined && sourceFile !== undefined) || (json !== undefined && sourceFile === undefined)); 2390 const errors: Diagnostic[] = []; 2391 2392 const parsedConfig = parseConfig(json, sourceFile, host, basePath, configFileName, resolutionStack, errors, extendedConfigCache); 2393 const { raw } = parsedConfig; 2394 const options = extend(existingOptions, parsedConfig.options || {}); 2395 const watchOptions = existingWatchOptions && parsedConfig.watchOptions ? 2396 extend(existingWatchOptions, parsedConfig.watchOptions) : 2397 parsedConfig.watchOptions || existingWatchOptions; 2398 2399 options.configFilePath = configFileName && normalizeSlashes(configFileName); 2400 const configFileSpecs = getConfigFileSpecs(); 2401 if (sourceFile) sourceFile.configFileSpecs = configFileSpecs; 2402 setConfigFileInOptions(options, sourceFile); 2403 2404 const basePathForFileNames = normalizePath(configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath); 2405 return { 2406 options, 2407 watchOptions, 2408 fileNames: getFileNames(basePathForFileNames), 2409 projectReferences: getProjectReferences(basePathForFileNames), 2410 typeAcquisition: parsedConfig.typeAcquisition || getDefaultTypeAcquisition(), 2411 raw, 2412 errors, 2413 // Wildcard directories (provided as part of a wildcard path) are stored in a 2414 // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), 2415 // or a recursive directory. This information is used by filesystem watchers to monitor for 2416 // new entries in these paths. 2417 wildcardDirectories: getWildcardDirectories(configFileSpecs, basePathForFileNames, host.useCaseSensitiveFileNames), 2418 compileOnSave: !!raw.compileOnSave, 2419 }; 2420 2421 function getConfigFileSpecs(): ConfigFileSpecs { 2422 const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object"); 2423 const filesSpecs = toPropValue(getSpecsFromRaw("files")); 2424 if (filesSpecs) { 2425 const hasZeroOrNoReferences = referencesOfRaw === "no-prop" || isArray(referencesOfRaw) && referencesOfRaw.length === 0; 2426 const hasExtends = hasProperty(raw, "extends"); 2427 if (filesSpecs.length === 0 && hasZeroOrNoReferences && !hasExtends) { 2428 if (sourceFile) { 2429 const fileName = configFileName || "tsconfig.json"; 2430 const diagnosticMessage = Diagnostics.The_files_list_in_config_file_0_is_empty; 2431 const nodeValue = firstDefined(getTsConfigPropArray(sourceFile, "files"), property => property.initializer); 2432 const error = nodeValue 2433 ? createDiagnosticForNodeInSourceFile(sourceFile, nodeValue, diagnosticMessage, fileName) 2434 : createCompilerDiagnostic(diagnosticMessage, fileName); 2435 errors.push(error); 2436 } 2437 else { 2438 createCompilerDiagnosticOnlyIfJson(Diagnostics.The_files_list_in_config_file_0_is_empty, configFileName || "tsconfig.json"); 2439 } 2440 } 2441 } 2442 2443 let includeSpecs = toPropValue(getSpecsFromRaw("include")); 2444 2445 const excludeOfRaw = getSpecsFromRaw("exclude"); 2446 let excludeSpecs = toPropValue(excludeOfRaw); 2447 if (excludeOfRaw === "no-prop" && raw.compilerOptions) { 2448 const outDir = raw.compilerOptions.outDir; 2449 const declarationDir = raw.compilerOptions.declarationDir; 2450 2451 if (outDir || declarationDir) { 2452 excludeSpecs = [outDir, declarationDir].filter(d => !!d); 2453 } 2454 } 2455 2456 if (filesSpecs === undefined && includeSpecs === undefined) { 2457 includeSpecs = ["**/*"]; 2458 } 2459 let validatedIncludeSpecs: readonly string[] | undefined, validatedExcludeSpecs: readonly string[] | undefined; 2460 2461 // The exclude spec list is converted into a regular expression, which allows us to quickly 2462 // test whether a file or directory should be excluded before recursively traversing the 2463 // file system. 2464 2465 if (includeSpecs) { 2466 validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, sourceFile, "include"); 2467 } 2468 2469 if (excludeSpecs) { 2470 validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, sourceFile, "exclude"); 2471 } 2472 2473 return { 2474 filesSpecs, 2475 includeSpecs, 2476 excludeSpecs, 2477 validatedFilesSpec: filter(filesSpecs, isString), 2478 validatedIncludeSpecs, 2479 validatedExcludeSpecs, 2480 }; 2481 } 2482 2483 function getFileNames(basePath: string): string[] { 2484 const fileNames = getFileNamesFromConfigSpecs(configFileSpecs, basePath, options, host, extraFileExtensions); 2485 if (shouldReportNoInputFiles(fileNames, canJsonReportNoInputFiles(raw), resolutionStack)) { 2486 errors.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); 2487 } 2488 return fileNames; 2489 } 2490 2491 function getProjectReferences(basePath: string): readonly ProjectReference[] | undefined { 2492 let projectReferences: ProjectReference[] | undefined; 2493 const referencesOfRaw = getPropFromRaw<ProjectReference>("references", element => typeof element === "object", "object"); 2494 if (isArray(referencesOfRaw)) { 2495 for (const ref of referencesOfRaw) { 2496 if (typeof ref.path !== "string") { 2497 createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "reference.path", "string"); 2498 } 2499 else { 2500 (projectReferences || (projectReferences = [])).push({ 2501 path: getNormalizedAbsolutePath(ref.path, basePath), 2502 originalPath: ref.path, 2503 prepend: ref.prepend, 2504 circular: ref.circular 2505 }); 2506 } 2507 } 2508 } 2509 return projectReferences; 2510 } 2511 2512 type PropOfRaw<T> = readonly T[] | "not-array" | "no-prop"; 2513 function toPropValue<T>(specResult: PropOfRaw<T>) { 2514 return isArray(specResult) ? specResult : undefined; 2515 } 2516 2517 function getSpecsFromRaw(prop: "files" | "include" | "exclude"): PropOfRaw<string> { 2518 return getPropFromRaw(prop, isString, "string"); 2519 } 2520 2521 function getPropFromRaw<T>(prop: "files" | "include" | "exclude" | "references", validateElement: (value: unknown) => boolean, elementTypeName: string): PropOfRaw<T> { 2522 if (hasProperty(raw, prop) && !isNullOrUndefined(raw[prop])) { 2523 if (isArray(raw[prop])) { 2524 const result = raw[prop]; 2525 if (!sourceFile && !every(result, validateElement)) { 2526 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, elementTypeName)); 2527 } 2528 return result; 2529 } 2530 else { 2531 createCompilerDiagnosticOnlyIfJson(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, prop, "Array"); 2532 return "not-array"; 2533 } 2534 } 2535 return "no-prop"; 2536 } 2537 2538 function createCompilerDiagnosticOnlyIfJson(message: DiagnosticMessage, arg0?: string, arg1?: string) { 2539 if (!sourceFile) { 2540 errors.push(createCompilerDiagnostic(message, arg0, arg1)); 2541 } 2542 } 2543 } 2544 2545 function isErrorNoInputFiles(error: Diagnostic) { 2546 return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code; 2547 } 2548 2549 function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) { 2550 return createCompilerDiagnostic( 2551 Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, 2552 configFileName || "tsconfig.json", 2553 JSON.stringify(includeSpecs || []), 2554 JSON.stringify(excludeSpecs || [])); 2555 } 2556 2557 function shouldReportNoInputFiles(fileNames: string[], canJsonReportNoInutFiles: boolean, resolutionStack?: Path[]) { 2558 return fileNames.length === 0 && canJsonReportNoInutFiles && (!resolutionStack || resolutionStack.length === 0); 2559 } 2560 2561 /*@internal*/ 2562 export function canJsonReportNoInputFiles(raw: any) { 2563 return !hasProperty(raw, "files") && !hasProperty(raw, "references"); 2564 } 2565 2566 /*@internal*/ 2567 export function updateErrorForNoInputFiles(fileNames: string[], configFileName: string, configFileSpecs: ConfigFileSpecs, configParseDiagnostics: Diagnostic[], canJsonReportNoInutFiles: boolean) { 2568 const existingErrors = configParseDiagnostics.length; 2569 if (shouldReportNoInputFiles(fileNames, canJsonReportNoInutFiles)) { 2570 configParseDiagnostics.push(getErrorForNoInputFiles(configFileSpecs, configFileName)); 2571 } 2572 else { 2573 filterMutate(configParseDiagnostics, error => !isErrorNoInputFiles(error)); 2574 } 2575 return existingErrors !== configParseDiagnostics.length; 2576 } 2577 2578 export interface ParsedTsconfig { 2579 raw: any; 2580 options?: CompilerOptions; 2581 watchOptions?: WatchOptions; 2582 typeAcquisition?: TypeAcquisition; 2583 /** 2584 * Note that the case of the config path has not yet been normalized, as no files have been imported into the project yet 2585 */ 2586 extendedConfigPath?: string; 2587 } 2588 2589 function isSuccessfulParsedTsconfig(value: ParsedTsconfig) { 2590 return !!value.options; 2591 } 2592 2593 /** 2594 * This *just* extracts options/include/exclude/files out of a config file. 2595 * It does *not* resolve the included files. 2596 */ 2597 function parseConfig( 2598 json: any, 2599 sourceFile: TsConfigSourceFile | undefined, 2600 host: ParseConfigHost, 2601 basePath: string, 2602 configFileName: string | undefined, 2603 resolutionStack: string[], 2604 errors: Push<Diagnostic>, 2605 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2606 ): ParsedTsconfig { 2607 basePath = normalizeSlashes(basePath); 2608 const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); 2609 2610 if (resolutionStack.indexOf(resolvedPath) >= 0) { 2611 errors.push(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))); 2612 return { raw: json || convertToObject(sourceFile!, errors) }; 2613 } 2614 2615 const ownConfig = json ? 2616 parseOwnConfigOfJson(json, host, basePath, configFileName, errors) : 2617 parseOwnConfigOfJsonSourceFile(sourceFile!, host, basePath, configFileName, errors); 2618 2619 if (ownConfig.options?.paths) { 2620 // If we end up needing to resolve relative paths from 'paths' relative to 2621 // the config file location, we'll need to know where that config file was. 2622 // Since 'paths' can be inherited from an extended config in another directory, 2623 // we wouldn't know which directory to use unless we store it here. 2624 ownConfig.options.pathsBasePath = basePath; 2625 } 2626 if (ownConfig.extendedConfigPath) { 2627 // copy the resolution stack so it is never reused between branches in potential diamond-problem scenarios. 2628 resolutionStack = resolutionStack.concat([resolvedPath]); 2629 const extendedConfig = getExtendedConfig(sourceFile, ownConfig.extendedConfigPath, host, resolutionStack, errors, extendedConfigCache); 2630 if (extendedConfig && isSuccessfulParsedTsconfig(extendedConfig)) { 2631 const baseRaw = extendedConfig.raw; 2632 const raw = ownConfig.raw; 2633 let relativeDifference: string | undefined ; 2634 const setPropertyInRawIfNotUndefined = (propertyName: string) => { 2635 if (!raw[propertyName] && baseRaw[propertyName]) { 2636 raw[propertyName] = map(baseRaw[propertyName], (path: string) => isRootedDiskPath(path) ? path : combinePaths( 2637 relativeDifference ||= convertToRelativePath(getDirectoryPath(ownConfig.extendedConfigPath!), basePath, createGetCanonicalFileName(host.useCaseSensitiveFileNames)), 2638 path 2639 )); 2640 } 2641 }; 2642 setPropertyInRawIfNotUndefined("include"); 2643 setPropertyInRawIfNotUndefined("exclude"); 2644 setPropertyInRawIfNotUndefined("files"); 2645 if (raw.compileOnSave === undefined) { 2646 raw.compileOnSave = baseRaw.compileOnSave; 2647 } 2648 ownConfig.options = assign({}, extendedConfig.options, ownConfig.options); 2649 ownConfig.watchOptions = ownConfig.watchOptions && extendedConfig.watchOptions ? 2650 assign({}, extendedConfig.watchOptions, ownConfig.watchOptions) : 2651 ownConfig.watchOptions || extendedConfig.watchOptions; 2652 // TODO extend type typeAcquisition 2653 } 2654 } 2655 2656 return ownConfig; 2657 } 2658 2659 function parseOwnConfigOfJson( 2660 json: any, 2661 host: ParseConfigHost, 2662 basePath: string, 2663 configFileName: string | undefined, 2664 errors: Push<Diagnostic> 2665 ): ParsedTsconfig { 2666 if (hasProperty(json, "excludes")) { 2667 errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); 2668 } 2669 2670 const options = convertCompilerOptionsFromJsonWorker(json.compilerOptions, basePath, errors, configFileName); 2671 // typingOptions has been deprecated and is only supported for backward compatibility purposes. 2672 // It should be removed in future releases - use typeAcquisition instead. 2673 const typeAcquisition = convertTypeAcquisitionFromJsonWorker(json.typeAcquisition || json.typingOptions, basePath, errors, configFileName); 2674 const watchOptions = convertWatchOptionsFromJsonWorker(json.watchOptions, basePath, errors); 2675 json.compileOnSave = convertCompileOnSaveOptionFromJson(json, basePath, errors); 2676 let extendedConfigPath: string | undefined; 2677 2678 if (json.extends) { 2679 if (!isString(json.extends)) { 2680 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "extends", "string")); 2681 } 2682 else { 2683 const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath; 2684 extendedConfigPath = getExtendsConfigPath(json.extends, host, newBase, errors, createCompilerDiagnostic); 2685 } 2686 } 2687 return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath }; 2688 } 2689 2690 function parseOwnConfigOfJsonSourceFile( 2691 sourceFile: TsConfigSourceFile, 2692 host: ParseConfigHost, 2693 basePath: string, 2694 configFileName: string | undefined, 2695 errors: Push<Diagnostic> 2696 ): ParsedTsconfig { 2697 const options = getDefaultCompilerOptions(configFileName); 2698 let typeAcquisition: TypeAcquisition | undefined, typingOptionstypeAcquisition: TypeAcquisition | undefined; 2699 let watchOptions: WatchOptions | undefined; 2700 let extendedConfigPath: string | undefined; 2701 2702 const optionsIterator: JsonConversionNotifier = { 2703 onSetValidOptionKeyValueInParent(parentOption: string, option: CommandLineOption, value: CompilerOptionsValue) { 2704 let currentOption; 2705 switch (parentOption) { 2706 case "compilerOptions": 2707 currentOption = options; 2708 break; 2709 case "watchOptions": 2710 currentOption = (watchOptions || (watchOptions = {})); 2711 break; 2712 case "typeAcquisition": 2713 currentOption = (typeAcquisition || (typeAcquisition = getDefaultTypeAcquisition(configFileName))); 2714 break; 2715 case "typingOptions": 2716 currentOption = (typingOptionstypeAcquisition || (typingOptionstypeAcquisition = getDefaultTypeAcquisition(configFileName))); 2717 break; 2718 default: 2719 Debug.fail("Unknown option"); 2720 } 2721 2722 currentOption[option.name] = normalizeOptionValue(option, basePath, value); 2723 }, 2724 onSetValidOptionKeyValueInRoot(key: string, _keyNode: PropertyName, value: CompilerOptionsValue, valueNode: Expression) { 2725 switch (key) { 2726 case "extends": 2727 const newBase = configFileName ? directoryOfCombinedPath(configFileName, basePath) : basePath; 2728 extendedConfigPath = getExtendsConfigPath( 2729 <string>value, 2730 host, 2731 newBase, 2732 errors, 2733 (message, arg0) => 2734 createDiagnosticForNodeInSourceFile(sourceFile, valueNode, message, arg0) 2735 ); 2736 return; 2737 } 2738 }, 2739 onSetUnknownOptionKeyValueInRoot(key: string, keyNode: PropertyName, _value: CompilerOptionsValue, _valueNode: Expression) { 2740 if (key === "excludes") { 2741 errors.push(createDiagnosticForNodeInSourceFile(sourceFile, keyNode, Diagnostics.Unknown_option_excludes_Did_you_mean_exclude)); 2742 } 2743 } 2744 }; 2745 const json = convertToObjectWorker(sourceFile, errors, /*returnValue*/ true, getTsconfigRootOptionsMap(), optionsIterator); 2746 2747 if (!typeAcquisition) { 2748 if (typingOptionstypeAcquisition) { 2749 typeAcquisition = (typingOptionstypeAcquisition.enableAutoDiscovery !== undefined) ? 2750 { 2751 enable: typingOptionstypeAcquisition.enableAutoDiscovery, 2752 include: typingOptionstypeAcquisition.include, 2753 exclude: typingOptionstypeAcquisition.exclude 2754 } : 2755 typingOptionstypeAcquisition; 2756 } 2757 else { 2758 typeAcquisition = getDefaultTypeAcquisition(configFileName); 2759 } 2760 } 2761 2762 return { raw: json, options, watchOptions, typeAcquisition, extendedConfigPath }; 2763 } 2764 2765 function getExtendsConfigPath( 2766 extendedConfig: string, 2767 host: ParseConfigHost, 2768 basePath: string, 2769 errors: Push<Diagnostic>, 2770 createDiagnostic: (message: DiagnosticMessage, arg1?: string) => Diagnostic) { 2771 extendedConfig = normalizeSlashes(extendedConfig); 2772 if (isRootedDiskPath(extendedConfig) || startsWith(extendedConfig, "./") || startsWith(extendedConfig, "../")) { 2773 let extendedConfigPath = getNormalizedAbsolutePath(extendedConfig, basePath); 2774 if (!host.fileExists(extendedConfigPath) && !endsWith(extendedConfigPath, Extension.Json)) { 2775 extendedConfigPath = `${extendedConfigPath}.json`; 2776 if (!host.fileExists(extendedConfigPath)) { 2777 errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig)); 2778 return undefined; 2779 } 2780 } 2781 return extendedConfigPath; 2782 } 2783 // If the path isn't a rooted or relative path, resolve like a module 2784 const resolved = nodeModuleNameResolver(extendedConfig, combinePaths(basePath, "tsconfig.json"), { moduleResolution: ModuleResolutionKind.NodeJs }, host, /*cache*/ undefined, /*projectRefs*/ undefined, /*lookupConfig*/ true); 2785 if (resolved.resolvedModule) { 2786 return resolved.resolvedModule.resolvedFileName; 2787 } 2788 errors.push(createDiagnostic(Diagnostics.File_0_not_found, extendedConfig)); 2789 return undefined; 2790 } 2791 2792 export interface ExtendedConfigCacheEntry { 2793 extendedResult: TsConfigSourceFile; 2794 extendedConfig: ParsedTsconfig | undefined; 2795 } 2796 2797 function getExtendedConfig( 2798 sourceFile: TsConfigSourceFile | undefined, 2799 extendedConfigPath: string, 2800 host: ParseConfigHost, 2801 resolutionStack: string[], 2802 errors: Push<Diagnostic>, 2803 extendedConfigCache?: ESMap<string, ExtendedConfigCacheEntry> 2804 ): ParsedTsconfig | undefined { 2805 const path = host.useCaseSensitiveFileNames ? extendedConfigPath : toFileNameLowerCase(extendedConfigPath); 2806 let value: ExtendedConfigCacheEntry | undefined; 2807 let extendedResult: TsConfigSourceFile; 2808 let extendedConfig: ParsedTsconfig | undefined; 2809 if (extendedConfigCache && (value = extendedConfigCache.get(path))) { 2810 ({ extendedResult, extendedConfig } = value); 2811 } 2812 else { 2813 extendedResult = readJsonConfigFile(extendedConfigPath, path => host.readFile(path)); 2814 if (!extendedResult.parseDiagnostics.length) { 2815 extendedConfig = parseConfig(/*json*/ undefined, extendedResult, host, getDirectoryPath(extendedConfigPath), 2816 getBaseFileName(extendedConfigPath), resolutionStack, errors, extendedConfigCache); 2817 } 2818 if (extendedConfigCache) { 2819 extendedConfigCache.set(path, { extendedResult, extendedConfig }); 2820 } 2821 } 2822 if (sourceFile) { 2823 sourceFile.extendedSourceFiles = [extendedResult.fileName]; 2824 if (extendedResult.extendedSourceFiles) { 2825 sourceFile.extendedSourceFiles.push(...extendedResult.extendedSourceFiles); 2826 } 2827 } 2828 if (extendedResult.parseDiagnostics.length) { 2829 errors.push(...extendedResult.parseDiagnostics); 2830 return undefined; 2831 } 2832 return extendedConfig!; 2833 } 2834 2835 function convertCompileOnSaveOptionFromJson(jsonOption: any, basePath: string, errors: Push<Diagnostic>): boolean { 2836 if (!hasProperty(jsonOption, compileOnSaveCommandLineOption.name)) { 2837 return false; 2838 } 2839 const result = convertJsonOption(compileOnSaveCommandLineOption, jsonOption.compileOnSave, basePath, errors); 2840 return typeof result === "boolean" && result; 2841 } 2842 2843 export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } { 2844 const errors: Diagnostic[] = []; 2845 const options = convertCompilerOptionsFromJsonWorker(jsonOptions, basePath, errors, configFileName); 2846 return { options, errors }; 2847 } 2848 2849 export function convertTypeAcquisitionFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: TypeAcquisition, errors: Diagnostic[] } { 2850 const errors: Diagnostic[] = []; 2851 const options = convertTypeAcquisitionFromJsonWorker(jsonOptions, basePath, errors, configFileName); 2852 return { options, errors }; 2853 } 2854 2855 function getDefaultCompilerOptions(configFileName?: string) { 2856 const options: CompilerOptions = configFileName && getBaseFileName(configFileName) === "jsconfig.json" 2857 ? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, skipLibCheck: true, noEmit: true } 2858 : {}; 2859 return options; 2860 } 2861 2862 function convertCompilerOptionsFromJsonWorker(jsonOptions: any, 2863 basePath: string, errors: Push<Diagnostic>, configFileName?: string): CompilerOptions { 2864 2865 const options = getDefaultCompilerOptions(configFileName); 2866 convertOptionsFromJson(getCommandLineCompilerOptionsMap(), jsonOptions, basePath, options, compilerOptionsDidYouMeanDiagnostics, errors); 2867 if (configFileName) { 2868 options.configFilePath = normalizeSlashes(configFileName); 2869 } 2870 return options; 2871 } 2872 2873 function getDefaultTypeAcquisition(configFileName?: string): TypeAcquisition { 2874 return { enable: !!configFileName && getBaseFileName(configFileName) === "jsconfig.json", include: [], exclude: [] }; 2875 } 2876 2877 function convertTypeAcquisitionFromJsonWorker(jsonOptions: any, 2878 basePath: string, errors: Push<Diagnostic>, configFileName?: string): TypeAcquisition { 2879 2880 const options = getDefaultTypeAcquisition(configFileName); 2881 const typeAcquisition = convertEnableAutoDiscoveryToEnable(jsonOptions); 2882 2883 convertOptionsFromJson(getCommandLineTypeAcquisitionMap(), typeAcquisition, basePath, options, typeAcquisitionDidYouMeanDiagnostics, errors); 2884 return options; 2885 } 2886 2887 function convertWatchOptionsFromJsonWorker(jsonOptions: any, basePath: string, errors: Push<Diagnostic>): WatchOptions | undefined { 2888 return convertOptionsFromJson(getCommandLineWatchOptionsMap(), jsonOptions, basePath, /*defaultOptions*/ undefined, watchOptionsDidYouMeanDiagnostics, errors); 2889 } 2890 2891 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2892 defaultOptions: undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): WatchOptions | undefined; 2893 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2894 defaultOptions: CompilerOptions | TypeAcquisition, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>): CompilerOptions | TypeAcquisition; 2895 function convertOptionsFromJson(optionsNameMap: ESMap<string, CommandLineOption>, jsonOptions: any, basePath: string, 2896 defaultOptions: CompilerOptions | TypeAcquisition | WatchOptions | undefined, diagnostics: DidYouMeanOptionsDiagnostics, errors: Push<Diagnostic>) { 2897 2898 if (!jsonOptions) { 2899 return; 2900 } 2901 2902 for (const id in jsonOptions) { 2903 const opt = optionsNameMap.get(id); 2904 if (opt) { 2905 (defaultOptions || (defaultOptions = {}))[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); 2906 } 2907 else { 2908 errors.push(createUnknownOptionError(id, diagnostics, createCompilerDiagnostic)); 2909 } 2910 } 2911 return defaultOptions; 2912 } 2913 2914 /*@internal*/ 2915 export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue { 2916 if (isCompilerOptionsValue(opt, value)) { 2917 const optType = opt.type; 2918 if (optType === "list" && isArray(value)) { 2919 return convertJsonOptionOfListType(<CommandLineOptionOfListType>opt, value, basePath, errors); 2920 } 2921 else if (!isString(optType)) { 2922 return convertJsonOptionOfCustomType(<CommandLineOptionOfCustomType>opt, <string>value, errors); 2923 } 2924 const validatedValue = validateJsonOptionValue(opt, value, errors); 2925 return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue); 2926 } 2927 else { 2928 errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, getCompilerOptionValueTypeString(opt))); 2929 } 2930 } 2931 2932 function normalizeOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { 2933 if (isNullOrUndefined(value)) return undefined; 2934 if (option.type === "list") { 2935 const listOption = option; 2936 if (listOption.element.isFilePath || !isString(listOption.element.type)) { 2937 return <CompilerOptionsValue>filter(map(value, v => normalizeOptionValue(listOption.element, basePath, v)), v => !!v); 2938 } 2939 return value; 2940 } 2941 else if (!isString(option.type)) { 2942 return option.type.get(isString(value) ? value.toLowerCase() : value); 2943 } 2944 return normalizeNonListOptionValue(option, basePath, value); 2945 } 2946 2947 function normalizeNonListOptionValue(option: CommandLineOption, basePath: string, value: any): CompilerOptionsValue { 2948 if (option.isFilePath) { 2949 value = getNormalizedAbsolutePath(value, basePath); 2950 if (value === "") { 2951 value = "."; 2952 } 2953 } 2954 return value; 2955 } 2956 2957 function validateJsonOptionValue<T extends CompilerOptionsValue>(opt: CommandLineOption, value: T, errors: Push<Diagnostic>): T | undefined { 2958 if (isNullOrUndefined(value)) return undefined; 2959 const d = opt.extraValidation?.(value); 2960 if (!d) return value; 2961 errors.push(createCompilerDiagnostic(...d)); 2962 return undefined; 2963 } 2964 2965 function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) { 2966 if (isNullOrUndefined(value)) return undefined; 2967 const key = value.toLowerCase(); 2968 const val = opt.type.get(key); 2969 if (val !== undefined) { 2970 return validateJsonOptionValue(opt, val, errors); 2971 } 2972 else { 2973 errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); 2974 } 2975 } 2976 2977 function convertJsonOptionOfListType(option: CommandLineOptionOfListType, values: readonly any[], basePath: string, errors: Push<Diagnostic>): any[] { 2978 return filter(map(values, v => convertJsonOption(option.element, v, basePath, errors)), v => !!v); 2979 } 2980 2981 function trimString(s: string) { 2982 return typeof s.trim === "function" ? s.trim() : s.replace(/^[\s]+|[\s]+$/g, ""); 2983 } 2984 2985 /** 2986 * Tests for a path that ends in a recursive directory wildcard. 2987 * Matches **, \**, **\, and \**\, but not a**b. 2988 * 2989 * NOTE: used \ in place of / above to avoid issues with multiline comments. 2990 * 2991 * Breakdown: 2992 * (^|\/) # matches either the beginning of the string or a directory separator. 2993 * \*\* # matches the recursive directory wildcard "**". 2994 * \/?$ # matches an optional trailing directory separator at the end of the string. 2995 */ 2996 const invalidTrailingRecursionPattern = /(^|\/)\*\*\/?$/; 2997 2998 /** 2999 * Tests for a path where .. appears after a recursive directory wildcard. 3000 * Matches **\..\*, **\a\..\*, and **\.., but not ..\**\* 3001 * 3002 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3003 * 3004 * Breakdown: 3005 * (^|\/) # matches either the beginning of the string or a directory separator. 3006 * \*\*\/ # matches a recursive directory wildcard "**" followed by a directory separator. 3007 * (.*\/)? # optionally matches any number of characters followed by a directory separator. 3008 * \.\. # matches a parent directory path component ".." 3009 * ($|\/) # matches either the end of the string or a directory separator. 3010 */ 3011 const invalidDotDotAfterRecursiveWildcardPattern = /(^|\/)\*\*\/(.*\/)?\.\.($|\/)/; 3012 3013 /** 3014 * Tests for a path containing a wildcard character in a directory component of the path. 3015 * Matches \*\, \?\, and \a*b\, but not \a\ or \a\*. 3016 * 3017 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3018 * 3019 * Breakdown: 3020 * \/ # matches a directory separator. 3021 * [^/]*? # matches any number of characters excluding directory separators (non-greedy). 3022 * [*?] # matches either a wildcard character (* or ?) 3023 * [^/]* # matches any number of characters excluding directory separators (greedy). 3024 * \/ # matches a directory separator. 3025 */ 3026 const watchRecursivePattern = /\/[^/]*?[*?][^/]*\//; 3027 3028 /** 3029 * Matches the portion of a wildcard path that does not contain wildcards. 3030 * Matches \a of \a\*, or \a\b\c of \a\b\c\?\d. 3031 * 3032 * NOTE: used \ in place of / above to avoid issues with multiline comments. 3033 * 3034 * Breakdown: 3035 * ^ # matches the beginning of the string 3036 * [^*?]* # matches any number of non-wildcard characters 3037 * (?=\/[^/]*[*?]) # lookahead that matches a directory separator followed by 3038 * # a path component that contains at least one wildcard character (* or ?). 3039 */ 3040 const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/; 3041 3042 /** 3043 * Gets the file names from the provided config file specs that contain, files, include, exclude and 3044 * other properties needed to resolve the file names 3045 * @param configFileSpecs The config file specs extracted with file names to include, wildcards to include/exclude and other details 3046 * @param basePath The base path for any relative file specifications. 3047 * @param options Compiler options. 3048 * @param host The host used to resolve files and directories. 3049 * @param extraFileExtensions optionaly file extra file extension information from host 3050 */ 3051 /* @internal */ 3052 export function getFileNamesFromConfigSpecs( 3053 configFileSpecs: ConfigFileSpecs, 3054 basePath: string, 3055 options: CompilerOptions, 3056 host: ParseConfigHost, 3057 extraFileExtensions: readonly FileExtensionInfo[] = emptyArray 3058 ): string[] { 3059 basePath = normalizePath(basePath); 3060 3061 const keyMapper = createGetCanonicalFileName(host.useCaseSensitiveFileNames); 3062 3063 // Literal file names (provided via the "files" array in tsconfig.json) are stored in a 3064 // file map with a possibly case insensitive key. We use this map later when when including 3065 // wildcard paths. 3066 const literalFileMap = new Map<string, string>(); 3067 3068 // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a 3069 // file map with a possibly case insensitive key. We use this map to store paths matched 3070 // via wildcard, and to handle extension priority. 3071 const wildcardFileMap = new Map<string, string>(); 3072 3073 // Wildcard paths of json files (provided via the "includes" array in tsconfig.json) are stored in a 3074 // file map with a possibly case insensitive key. We use this map to store paths matched 3075 // via wildcard of *.json kind 3076 const wildCardJsonFileMap = new Map<string, string>(); 3077 const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = configFileSpecs; 3078 3079 // Rather than requery this for each file and filespec, we query the supported extensions 3080 // once and store it on the expansion context. 3081 const supportedExtensions = getSupportedExtensions(options, extraFileExtensions); 3082 const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions); 3083 3084 // Literal files are always included verbatim. An "include" or "exclude" specification cannot 3085 // remove a literal file. 3086 if (validatedFilesSpec) { 3087 for (const fileName of validatedFilesSpec) { 3088 const file = getNormalizedAbsolutePath(fileName, basePath); 3089 literalFileMap.set(keyMapper(file), file); 3090 } 3091 } 3092 3093 let jsonOnlyIncludeRegexes: readonly RegExp[] | undefined; 3094 if (validatedIncludeSpecs && validatedIncludeSpecs.length > 0) { 3095 for (const file of host.readDirectory(basePath, supportedExtensionsWithJsonIfResolveJsonModule, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) { 3096 if (fileExtensionIs(file, Extension.Json)) { 3097 // Valid only if *.json specified 3098 if (!jsonOnlyIncludeRegexes) { 3099 const includes = validatedIncludeSpecs.filter(s => endsWith(s, Extension.Json)); 3100 const includeFilePatterns = map(getRegularExpressionsForWildcards(includes, basePath, "files"), pattern => `^${pattern}$`); 3101 jsonOnlyIncludeRegexes = includeFilePatterns ? includeFilePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames)) : emptyArray; 3102 } 3103 const includeIndex = findIndex(jsonOnlyIncludeRegexes, re => re.test(file)); 3104 if (includeIndex !== -1) { 3105 const key = keyMapper(file); 3106 if (!literalFileMap.has(key) && !wildCardJsonFileMap.has(key)) { 3107 wildCardJsonFileMap.set(key, file); 3108 } 3109 } 3110 continue; 3111 } 3112 // If we have already included a literal or wildcard path with a 3113 // higher priority extension, we should skip this file. 3114 // 3115 // This handles cases where we may encounter both <file>.ts and 3116 // <file>.d.ts (or <file>.js if "allowJs" is enabled) in the same 3117 // directory when they are compilation outputs. 3118 if (hasFileWithHigherPriorityExtension(file, literalFileMap, wildcardFileMap, supportedExtensions, keyMapper)) { 3119 continue; 3120 } 3121 3122 // We may have included a wildcard path with a lower priority 3123 // extension due to the user-defined order of entries in the 3124 // "include" array. If there is a lower priority extension in the 3125 // same directory, we should remove it. 3126 removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); 3127 3128 const key = keyMapper(file); 3129 if (!literalFileMap.has(key) && !wildcardFileMap.has(key)) { 3130 wildcardFileMap.set(key, file); 3131 } 3132 } 3133 } 3134 3135 const literalFiles = arrayFrom(literalFileMap.values()); 3136 const wildcardFiles = arrayFrom(wildcardFileMap.values()); 3137 3138 return literalFiles.concat(wildcardFiles, arrayFrom(wildCardJsonFileMap.values())); 3139 } 3140 3141 /* @internal */ 3142 export function isExcludedFile( 3143 pathToCheck: string, 3144 spec: ConfigFileSpecs, 3145 basePath: string, 3146 useCaseSensitiveFileNames: boolean, 3147 currentDirectory: string 3148 ): boolean { 3149 const { validatedFilesSpec, validatedIncludeSpecs, validatedExcludeSpecs } = spec; 3150 if (!length(validatedIncludeSpecs) || !length(validatedExcludeSpecs)) return false; 3151 3152 basePath = normalizePath(basePath); 3153 3154 const keyMapper = createGetCanonicalFileName(useCaseSensitiveFileNames); 3155 if (validatedFilesSpec) { 3156 for (const fileName of validatedFilesSpec) { 3157 if (keyMapper(getNormalizedAbsolutePath(fileName, basePath)) === pathToCheck) return false; 3158 } 3159 } 3160 3161 return matchesExcludeWorker(pathToCheck, validatedExcludeSpecs, useCaseSensitiveFileNames, currentDirectory, basePath); 3162 } 3163 3164 /* @internal */ 3165 export function matchesExclude( 3166 pathToCheck: string, 3167 excludeSpecs: readonly string[] | undefined, 3168 useCaseSensitiveFileNames: boolean, 3169 currentDirectory: string 3170 ) { 3171 return matchesExcludeWorker( 3172 pathToCheck, 3173 filter(excludeSpecs, spec => !invalidDotDotAfterRecursiveWildcardPattern.test(spec)), 3174 useCaseSensitiveFileNames, 3175 currentDirectory 3176 ); 3177 } 3178 3179 function matchesExcludeWorker( 3180 pathToCheck: string, 3181 excludeSpecs: readonly string[] | undefined, 3182 useCaseSensitiveFileNames: boolean, 3183 currentDirectory: string, 3184 basePath?: string 3185 ) { 3186 const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude"); 3187 const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames); 3188 if (!excludeRegex) return false; 3189 if (excludeRegex.test(pathToCheck)) return true; 3190 return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck)); 3191 } 3192 3193 function validateSpecs(specs: readonly string[], errors: Push<Diagnostic>, disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { 3194 return specs.filter(spec => { 3195 if (!isString(spec)) return false; 3196 const diag = specToDiagnostic(spec, disallowTrailingRecursion); 3197 if (diag !== undefined) { 3198 errors.push(createDiagnostic(...diag)); 3199 } 3200 return diag === undefined; 3201 }); 3202 3203 function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { 3204 const element = getTsConfigPropArrayElementValue(jsonSourceFile, specKey, spec); 3205 return element ? 3206 createDiagnosticForNodeInSourceFile(jsonSourceFile!, element, message, spec) : 3207 createCompilerDiagnostic(message, spec); 3208 } 3209 } 3210 3211 function specToDiagnostic(spec: string, disallowTrailingRecursion?: boolean): [DiagnosticMessage, string] | undefined { 3212 if (disallowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { 3213 return [Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; 3214 } 3215 else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { 3216 return [Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec]; 3217 } 3218 } 3219 3220 /** 3221 * Gets directories in a set of include patterns that should be watched for changes. 3222 */ 3223 function getWildcardDirectories({ validatedIncludeSpecs: include, validatedExcludeSpecs: exclude }: ConfigFileSpecs, path: string, useCaseSensitiveFileNames: boolean): MapLike<WatchDirectoryFlags> { 3224 // We watch a directory recursively if it contains a wildcard anywhere in a directory segment 3225 // of the pattern: 3226 // 3227 // /a/b/**/d - Watch /a/b recursively to catch changes to any d in any subfolder recursively 3228 // /a/b/*/d - Watch /a/b recursively to catch any d in any immediate subfolder, even if a new subfolder is added 3229 // /a/b - Watch /a/b recursively to catch changes to anything in any recursive subfoler 3230 // 3231 // We watch a directory without recursion if it contains a wildcard in the file segment of 3232 // the pattern: 3233 // 3234 // /a/b/* - Watch /a/b directly to catch any new file 3235 // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z 3236 const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); 3237 const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); 3238 const wildcardDirectories: MapLike<WatchDirectoryFlags> = {}; 3239 if (include !== undefined) { 3240 const recursiveKeys: string[] = []; 3241 for (const file of include) { 3242 const spec = normalizePath(combinePaths(path, file)); 3243 if (excludeRegex && excludeRegex.test(spec)) { 3244 continue; 3245 } 3246 3247 const match = getWildcardDirectoryFromSpec(spec, useCaseSensitiveFileNames); 3248 if (match) { 3249 const { key, flags } = match; 3250 const existingFlags = wildcardDirectories[key]; 3251 if (existingFlags === undefined || existingFlags < flags) { 3252 wildcardDirectories[key] = flags; 3253 if (flags === WatchDirectoryFlags.Recursive) { 3254 recursiveKeys.push(key); 3255 } 3256 } 3257 } 3258 } 3259 3260 // Remove any subpaths under an existing recursively watched directory. 3261 for (const key in wildcardDirectories) { 3262 if (hasProperty(wildcardDirectories, key)) { 3263 for (const recursiveKey of recursiveKeys) { 3264 if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { 3265 delete wildcardDirectories[key]; 3266 } 3267 } 3268 } 3269 } 3270 } 3271 3272 return wildcardDirectories; 3273 } 3274 3275 function getWildcardDirectoryFromSpec(spec: string, useCaseSensitiveFileNames: boolean): { key: string, flags: WatchDirectoryFlags } | undefined { 3276 const match = wildcardDirectoryPattern.exec(spec); 3277 if (match) { 3278 return { 3279 key: useCaseSensitiveFileNames ? match[0] : toFileNameLowerCase(match[0]), 3280 flags: watchRecursivePattern.test(spec) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None 3281 }; 3282 } 3283 if (isImplicitGlob(spec)) { 3284 return { 3285 key: useCaseSensitiveFileNames ? spec : toFileNameLowerCase(spec), 3286 flags: WatchDirectoryFlags.Recursive 3287 }; 3288 } 3289 return undefined; 3290 } 3291 3292 /** 3293 * Determines whether a literal or wildcard file has already been included that has a higher 3294 * extension priority. 3295 * 3296 * @param file The path to the file. 3297 * @param extensionPriority The priority of the extension. 3298 * @param context The expansion context. 3299 */ 3300 function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap<string, string>, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) { 3301 const extensionPriority = getExtensionPriority(file, extensions); 3302 const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); 3303 for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { 3304 const higherPriorityExtension = extensions[i]; 3305 const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); 3306 if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) { 3307 return true; 3308 } 3309 } 3310 3311 return false; 3312 } 3313 3314 /** 3315 * Removes files included via wildcard expansion with a lower extension priority that have 3316 * already been included. 3317 * 3318 * @param file The path to the file. 3319 * @param extensionPriority The priority of the extension. 3320 * @param context The expansion context. 3321 */ 3322 function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) { 3323 const extensionPriority = getExtensionPriority(file, extensions); 3324 const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); 3325 for (let i = nextExtensionPriority; i < extensions.length; i++) { 3326 const lowerPriorityExtension = extensions[i]; 3327 const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); 3328 wildcardFiles.delete(lowerPriorityPath); 3329 } 3330 } 3331 3332 /** 3333 * Produces a cleaned version of compiler options with personally identifying info (aka, paths) removed. 3334 * Also converts enum values back to strings. 3335 */ 3336 /* @internal */ 3337 export function convertCompilerOptionsForTelemetry(opts: CompilerOptions): CompilerOptions { 3338 const out: CompilerOptions = {}; 3339 for (const key in opts) { 3340 if (opts.hasOwnProperty(key)) { 3341 const type = getOptionFromName(key); 3342 if (type !== undefined) { // Ignore unknown options 3343 out[key] = getOptionValueWithEmptyStrings(opts[key], type); 3344 } 3345 } 3346 } 3347 return out; 3348 } 3349 3350 function getOptionValueWithEmptyStrings(value: any, option: CommandLineOption): {} { 3351 switch (option.type) { 3352 case "object": // "paths". Can't get any useful information from the value since we blank out strings, so just return "". 3353 return ""; 3354 case "string": // Could be any arbitrary string -- use empty string instead. 3355 return ""; 3356 case "number": // Allow numbers, but be sure to check it's actually a number. 3357 return typeof value === "number" ? value : ""; 3358 case "boolean": 3359 return typeof value === "boolean" ? value : ""; 3360 case "list": 3361 const elementType = option.element; 3362 return isArray(value) ? value.map(v => getOptionValueWithEmptyStrings(v, elementType)) : ""; 3363 default: 3364 return forEachEntry(option.type, (optionEnumValue, optionStringValue) => { 3365 if (optionEnumValue === value) { 3366 return optionStringValue; 3367 } 3368 })!; // TODO: GH#18217 3369 } 3370 } 3371} 3372