1namespace ts { 2 describe("unittests:: config:: convertCompilerOptionsFromJson", () => { 3 const formatDiagnosticHost: FormatDiagnosticsHost = { 4 getCurrentDirectory: () => "/apath/", 5 getCanonicalFileName: createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ true), 6 getNewLine: () => "\n" 7 }; 8 9 interface ExpectedResultWithParsingSuccess { 10 compilerOptions: CompilerOptions; 11 errors: readonly Diagnostic[]; 12 } 13 14 interface ExpectedResultWithParsingFailure { 15 compilerOptions: CompilerOptions; 16 hasParseErrors: true; 17 } 18 19 type ExpectedResult = ExpectedResultWithParsingSuccess | ExpectedResultWithParsingFailure; 20 21 function isExpectedResultWithParsingFailure(expectedResult: ExpectedResult): expectedResult is ExpectedResultWithParsingFailure { 22 return !!(expectedResult as ExpectedResultWithParsingFailure).hasParseErrors; 23 } 24 25 function assertCompilerOptions(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) { 26 assertCompilerOptionsWithJson(json, configFileName, expectedResult); 27 assertCompilerOptionsWithJsonNode(json, configFileName, expectedResult); 28 } 29 30 function assertCompilerOptionsWithJson(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) { 31 const { options: actualCompilerOptions, errors: actualErrors } = convertCompilerOptionsFromJson(json.compilerOptions, "/apath/", configFileName); 32 33 const parsedCompilerOptions = JSON.stringify(actualCompilerOptions); 34 const expectedCompilerOptions = JSON.stringify({ ...expectedResult.compilerOptions, configFilePath: configFileName }); 35 assert.equal(parsedCompilerOptions, expectedCompilerOptions); 36 37 verifyErrors(actualErrors, expectedResult.errors, /*ignoreLocation*/ true); 38 } 39 40 function assertCompilerOptionsWithJsonNode(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) { 41 assertCompilerOptionsWithJsonText(JSON.stringify(json), configFileName, expectedResult); 42 } 43 44 function assertCompilerOptionsWithJsonText(fileText: string, configFileName: string, expectedResult: ExpectedResult) { 45 const result = parseJsonText(configFileName, fileText); 46 assert(!!result.endOfFileToken); 47 assert.equal(!!result.parseDiagnostics.length, isExpectedResultWithParsingFailure(expectedResult)); 48 const host: ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" })); 49 const { options: actualCompilerOptions, errors: actualParseErrors } = parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName); 50 expectedResult.compilerOptions.configFilePath = configFileName; 51 52 const parsedCompilerOptions = JSON.stringify(actualCompilerOptions); 53 const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions); 54 assert.equal(parsedCompilerOptions, expectedCompilerOptions); 55 assert.equal(actualCompilerOptions.configFile, result); 56 57 if (!isExpectedResultWithParsingFailure(expectedResult)) { 58 verifyErrors(actualParseErrors.filter(error => error.code !== Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code), expectedResult.errors); 59 } 60 } 61 62 function verifyErrors(actualErrors: Diagnostic[], expectedErrors: readonly Diagnostic[], ignoreLocation?: boolean) { 63 assert.isTrue(expectedErrors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedErrors.map(getDiagnosticString), undefined, " ")}. Actual error: ${JSON.stringify(actualErrors.map(getDiagnosticString), undefined, " ")}.`); 64 for (let i = 0; i < actualErrors.length; i++) { 65 const actualError = actualErrors[i]; 66 const expectedError = expectedErrors[i]; 67 68 assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`); 69 assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`); 70 if (!ignoreLocation) { 71 assert(actualError.file); 72 assert(actualError.start); 73 assert(actualError.length); 74 } 75 } 76 77 function getDiagnosticString(diagnostic: Diagnostic) { 78 if (ignoreLocation) { 79 const { file, ...rest } = diagnostic; 80 diagnostic = { file: undefined, ...rest }; 81 } 82 return formatDiagnostic(diagnostic, formatDiagnosticHost); 83 } 84 } 85 86 // tsconfig.json tests 87 it("Convert correctly format tsconfig.json to compiler-options ", () => { 88 assertCompilerOptions( 89 { 90 compilerOptions: { 91 module: "commonjs", 92 target: "es5", 93 noImplicitAny: false, 94 sourceMap: false, 95 lib: ["es5", "es2015.core", "es2015.symbol"] 96 } 97 }, "tsconfig.json", 98 { 99 compilerOptions: { 100 module: ModuleKind.CommonJS, 101 target: ScriptTarget.ES5, 102 noImplicitAny: false, 103 sourceMap: false, 104 lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] 105 }, 106 errors: [] 107 } 108 ); 109 }); 110 111 it("Convert correctly format tsconfig.json with allowJs is false to compiler-options ", () => { 112 assertCompilerOptions( 113 { 114 compilerOptions: { 115 module: "commonjs", 116 target: "es5", 117 noImplicitAny: false, 118 sourceMap: false, 119 allowJs: false, 120 lib: ["es5", "es2015.core", "es2015.symbol"] 121 } 122 }, "tsconfig.json", 123 { 124 compilerOptions: { 125 module: ModuleKind.CommonJS, 126 target: ScriptTarget.ES5, 127 noImplicitAny: false, 128 sourceMap: false, 129 allowJs: false, 130 lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] 131 }, 132 errors: [] 133 } 134 ); 135 }); 136 137 it("Convert incorrect option of jsx to compiler-options ", () => { 138 assertCompilerOptions( 139 { 140 compilerOptions: { 141 module: "commonjs", 142 target: "es5", 143 noImplicitAny: false, 144 sourceMap: false, 145 jsx: "" 146 } 147 }, "tsconfig.json", 148 { 149 compilerOptions: { 150 module: ModuleKind.CommonJS, 151 target: ScriptTarget.ES5, 152 noImplicitAny: false, 153 sourceMap: false, 154 }, 155 errors: [{ 156 file: undefined, 157 start: 0, 158 length: 0, 159 messageText: "Argument for '--jsx' option must be: 'preserve', 'react-native', 'react'.", 160 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 161 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 162 }] 163 } 164 ); 165 }); 166 167 it("Convert incorrect option of module to compiler-options ", () => { 168 assertCompilerOptions( 169 { 170 compilerOptions: { 171 module: "", 172 target: "es5", 173 noImplicitAny: false, 174 sourceMap: false, 175 } 176 }, "tsconfig.json", 177 { 178 compilerOptions: { 179 target: ScriptTarget.ES5, 180 noImplicitAny: false, 181 sourceMap: false, 182 }, 183 errors: [{ 184 file: undefined, 185 start: 0, 186 length: 0, 187 messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'esnext'.", 188 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 189 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 190 }] 191 } 192 ); 193 }); 194 195 it("Convert incorrect option of newLine to compiler-options ", () => { 196 assertCompilerOptions( 197 { 198 compilerOptions: { 199 newLine: "", 200 target: "es5", 201 noImplicitAny: false, 202 sourceMap: false, 203 } 204 }, "tsconfig.json", 205 { 206 compilerOptions: { 207 target: ScriptTarget.ES5, 208 noImplicitAny: false, 209 sourceMap: false, 210 }, 211 errors: [{ 212 file: undefined, 213 start: 0, 214 length: 0, 215 messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'.", 216 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 217 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 218 }] 219 } 220 ); 221 }); 222 223 it("Convert incorrect option of target to compiler-options ", () => { 224 assertCompilerOptions( 225 { 226 compilerOptions: { 227 target: "", 228 noImplicitAny: false, 229 sourceMap: false, 230 } 231 }, "tsconfig.json", 232 { 233 compilerOptions: { 234 noImplicitAny: false, 235 sourceMap: false, 236 }, 237 errors: [{ 238 file: undefined, 239 start: 0, 240 length: 0, 241 messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'esnext'.", 242 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 243 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 244 }] 245 } 246 ); 247 }); 248 249 it("Convert incorrect option of module-resolution to compiler-options ", () => { 250 assertCompilerOptions( 251 { 252 compilerOptions: { 253 moduleResolution: "", 254 noImplicitAny: false, 255 sourceMap: false, 256 } 257 }, "tsconfig.json", 258 { 259 compilerOptions: { 260 noImplicitAny: false, 261 sourceMap: false, 262 }, 263 errors: [{ 264 file: undefined, 265 start: 0, 266 length: 0, 267 messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'.", 268 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 269 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 270 }] 271 } 272 ); 273 }); 274 275 it("Convert incorrect option of libs to compiler-options ", () => { 276 assertCompilerOptions( 277 { 278 compilerOptions: { 279 module: "commonjs", 280 target: "es5", 281 noImplicitAny: false, 282 sourceMap: false, 283 lib: ["es5", "es2015.core", "incorrectLib"] 284 } 285 }, "tsconfig.json", 286 { 287 compilerOptions: { 288 module: ModuleKind.CommonJS, 289 target: ScriptTarget.ES5, 290 noImplicitAny: false, 291 sourceMap: false, 292 lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts"] 293 }, 294 errors: [{ 295 file: undefined, 296 start: 0, 297 length: 0, 298 messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.bigint', 'esnext.string', 'esnext.promise'.", 299 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 300 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 301 }] 302 } 303 ); 304 }); 305 306 it("Convert empty string option of libs to compiler-options ", () => { 307 assertCompilerOptions( 308 { 309 compilerOptions: { 310 module: "commonjs", 311 target: "es5", 312 noImplicitAny: false, 313 sourceMap: false, 314 lib: ["es5", ""] 315 } 316 }, "tsconfig.json", 317 { 318 compilerOptions: { 319 module: ModuleKind.CommonJS, 320 target: ScriptTarget.ES5, 321 noImplicitAny: false, 322 sourceMap: false, 323 lib: ["lib.es5.d.ts"] 324 }, 325 errors: [{ 326 file: undefined, 327 start: 0, 328 length: 0, 329 messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.", 330 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 331 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 332 }] 333 } 334 ); 335 }); 336 337 it("Convert empty string option of libs to compiler-options ", () => { 338 assertCompilerOptions( 339 { 340 compilerOptions: { 341 module: "commonjs", 342 target: "es5", 343 noImplicitAny: false, 344 sourceMap: false, 345 lib: [""] 346 } 347 }, "tsconfig.json", 348 { 349 compilerOptions: { 350 module: ModuleKind.CommonJS, 351 target: ScriptTarget.ES5, 352 noImplicitAny: false, 353 sourceMap: false, 354 lib: [] 355 }, 356 errors: [{ 357 file: undefined, 358 start: 0, 359 length: 0, 360 messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.", 361 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 362 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 363 }] 364 } 365 ); 366 }); 367 368 it("Convert trailing-whitespace string option of libs to compiler-options ", () => { 369 assertCompilerOptions( 370 { 371 compilerOptions: { 372 module: "commonjs", 373 target: "es5", 374 noImplicitAny: false, 375 sourceMap: false, 376 lib: [" "] 377 } 378 }, "tsconfig.json", 379 { 380 compilerOptions: { 381 module: ModuleKind.CommonJS, 382 target: ScriptTarget.ES5, 383 noImplicitAny: false, 384 sourceMap: false, 385 lib: [] 386 }, 387 errors: [{ 388 file: undefined, 389 start: 0, 390 length: 0, 391 messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.", 392 code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code, 393 category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category 394 }] 395 } 396 ); 397 }); 398 399 it("Convert empty option of libs to compiler-options ", () => { 400 assertCompilerOptions( 401 { 402 compilerOptions: { 403 module: "commonjs", 404 target: "es5", 405 noImplicitAny: false, 406 sourceMap: false, 407 lib: [] 408 } 409 }, "tsconfig.json", 410 { 411 compilerOptions: { 412 module: ModuleKind.CommonJS, 413 target: ScriptTarget.ES5, 414 noImplicitAny: false, 415 sourceMap: false, 416 lib: [] 417 }, 418 errors: [] 419 } 420 ); 421 }); 422 423 it("Convert incorrectly format tsconfig.json to compiler-options ", () => { 424 assertCompilerOptions( 425 { 426 compilerOptions: { 427 modu: "commonjs", 428 } 429 }, "tsconfig.json", 430 { 431 compilerOptions: {}, 432 errors: [{ 433 file: undefined, 434 start: 0, 435 length: 0, 436 messageText: "Unknown compiler option 'modu'.", 437 code: Diagnostics.Unknown_compiler_option_0.code, 438 category: Diagnostics.Unknown_compiler_option_0.category 439 }] 440 } 441 ); 442 }); 443 444 it("Convert default tsconfig.json to compiler-options ", () => { 445 assertCompilerOptions({}, "tsconfig.json", 446 { 447 compilerOptions: {}, 448 errors: [] 449 } 450 ); 451 }); 452 453 it("Convert negative numbers in tsconfig.json ", () => { 454 assertCompilerOptions( 455 { 456 compilerOptions: { 457 allowJs: true, 458 maxNodeModuleJsDepth: -1 459 } 460 }, "tsconfig.json", 461 { 462 compilerOptions: { 463 allowJs: true, 464 maxNodeModuleJsDepth: -1 465 }, 466 errors: [] 467 } 468 ); 469 }); 470 471 // jsconfig.json 472 it("Convert correctly format jsconfig.json to compiler-options ", () => { 473 assertCompilerOptions( 474 { 475 compilerOptions: { 476 module: "commonjs", 477 target: "es5", 478 noImplicitAny: false, 479 sourceMap: false, 480 lib: ["es5", "es2015.core", "es2015.symbol"] 481 } 482 }, "jsconfig.json", 483 { 484 compilerOptions: { 485 allowJs: true, 486 maxNodeModuleJsDepth: 2, 487 allowSyntheticDefaultImports: true, 488 skipLibCheck: true, 489 noEmit: true, 490 module: ModuleKind.CommonJS, 491 target: ScriptTarget.ES5, 492 noImplicitAny: false, 493 sourceMap: false, 494 lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] 495 }, 496 errors: [] 497 } 498 ); 499 }); 500 501 it("Convert correctly format jsconfig.json with allowJs is false to compiler-options ", () => { 502 assertCompilerOptions( 503 { 504 compilerOptions: { 505 module: "commonjs", 506 target: "es5", 507 noImplicitAny: false, 508 sourceMap: false, 509 allowJs: false, 510 lib: ["es5", "es2015.core", "es2015.symbol"] 511 } 512 }, "jsconfig.json", 513 { 514 compilerOptions: { 515 allowJs: false, 516 maxNodeModuleJsDepth: 2, 517 allowSyntheticDefaultImports: true, 518 skipLibCheck: true, 519 noEmit: true, 520 module: ModuleKind.CommonJS, 521 target: ScriptTarget.ES5, 522 noImplicitAny: false, 523 sourceMap: false, 524 lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"] 525 }, 526 errors: [] 527 } 528 ); 529 }); 530 531 it("Convert incorrectly format jsconfig.json to compiler-options ", () => { 532 assertCompilerOptions( 533 { 534 compilerOptions: { 535 modu: "commonjs", 536 } 537 }, "jsconfig.json", 538 { 539 compilerOptions: 540 { 541 allowJs: true, 542 maxNodeModuleJsDepth: 2, 543 allowSyntheticDefaultImports: true, 544 skipLibCheck: true, 545 noEmit: true 546 }, 547 errors: [{ 548 file: undefined, 549 start: 0, 550 length: 0, 551 messageText: "Unknown compiler option 'modu'.", 552 code: Diagnostics.Unknown_compiler_option_0.code, 553 category: Diagnostics.Unknown_compiler_option_0.category 554 }] 555 } 556 ); 557 }); 558 559 it("Convert default jsconfig.json to compiler-options ", () => { 560 assertCompilerOptions({}, "jsconfig.json", 561 { 562 compilerOptions: 563 { 564 allowJs: true, 565 maxNodeModuleJsDepth: 2, 566 allowSyntheticDefaultImports: true, 567 skipLibCheck: true, 568 noEmit: true 569 }, 570 errors: [] 571 } 572 ); 573 }); 574 575 it("Convert tsconfig options when there are multiple invalid strings", () => { 576 assertCompilerOptionsWithJsonText(`{ 577 "compilerOptions": { 578 "target": "<%- options.useTsWithBabel ? 'esnext' : 'es5' %>", 579 "module": "esnext", 580 <%_ if (options.classComponent) { _%> 581 "experimentalDecorators": true, 582 <%_ } _%> 583 "sourceMap": true, 584 "types": [ 585 "webpack-env"<% if (hasMocha || hasJest) { %>,<% } %> 586 <%_ if (hasMocha) { _%> 587 "mocha", 588 "chai" 589 <%_ } else if (hasJest) { _%> 590 "jest" 591 <%_ } _%> 592 ] 593 } 594} 595`, 596 "tsconfig.json", 597 { 598 compilerOptions: { 599 target: undefined, 600 module: ModuleKind.ESNext, 601 experimentalDecorators: true, 602 }, 603 hasParseErrors: true 604 }); 605 }); 606 }); 607} 608