1import { 2 addRange, BuilderProgram, CancellationToken, chainDiagnosticMessages, combinePaths, CompilerHost, CompilerOptions, 3 contains, convertToRelativePath, copyProperties, countWhere, createCompilerDiagnostic, 4 createEmitAndSemanticDiagnosticsBuilderProgram, createGetCanonicalFileName, createIncrementalCompilerHost, 5 createIncrementalProgram, CreateProgram, createSourceFile, CustomTransformers, Debug, Diagnostic, 6 DiagnosticCategory, DiagnosticMessage, DiagnosticMessageChain, DiagnosticReporter, Diagnostics, 7 DirectoryStructureHost, EmitAndSemanticDiagnosticsBuilderProgram, EmitResult, emptyArray, endsWith, ExitStatus, 8 ExtendedConfigCacheEntry, Extension, externalHelpersModuleNameText, FileExtensionInfo, fileExtensionIs, 9 FileIncludeKind, FileIncludeReason, FileWatcher, filter, find, flattenDiagnosticMessageText, forEach, forEachEntry, 10 ForegroundColorEscapeSequences, formatColorAndReset, formatDiagnostic, FormatDiagnosticsHost, 11 formatDiagnosticsWithColorAndContext, generateDjb2Hash, getDefaultLibFileName, getDirectoryPath, 12 getEmitScriptTarget, getLineAndCharacterOfPosition, getNewLineCharacter, getNormalizedAbsolutePath, 13 getParsedCommandLineOfConfigFile, getPatternFromSpec, getReferencedFileLocation, getRegexFromPattern, 14 getRelativePathFromDirectory, getWatchFactory, HasCurrentDirectory, isExternalOrCommonJsModule, isReferencedFile, 15 isReferenceFileLocation, isString, last, Map, maybeBind, memoize, ModuleKind, noop, normalizePath, outFile, 16 packageIdToString, ParseConfigFileHost, ParsedCommandLine, pathIsAbsolute, Program, ProgramHost, ProjectReference, 17 ReportEmitErrorSummary, ReportFileInError, sortAndDeduplicateDiagnostics, SortedReadonlyArray, SourceFile, sys, System, 18 targetOptionDeclaration, WatchCompilerHost, WatchCompilerHostOfConfigFile, 19 WatchCompilerHostOfFilesAndCompilerOptions, WatchFactory, WatchFactoryHost, WatchHost, WatchLogLevel, WatchOptions, 20 WatchStatusReporter, WriteFileCallback, writeFileEnsuringDirectories, 21} from "./_namespaces/ts"; 22 23const sysFormatDiagnosticsHost: FormatDiagnosticsHost | undefined = sys ? { 24 getCurrentDirectory: () => sys.getCurrentDirectory(), 25 getNewLine: () => sys.newLine, 26 getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames) 27} : undefined; 28 29/** 30 * Create a function that reports error by writing to the system and handles the formatting of the diagnostic 31 * 32 * @internal 33 */ 34export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter { 35 const host: FormatDiagnosticsHost = system === sys && sysFormatDiagnosticsHost ? sysFormatDiagnosticsHost : { 36 getCurrentDirectory: () => system.getCurrentDirectory(), 37 getNewLine: () => system.newLine, 38 getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), 39 }; 40 if (!pretty) { 41 return diagnostic => system.write(formatDiagnostic(diagnostic, host)); 42 } 43 44 const diagnostics: Diagnostic[] = new Array(1); 45 return diagnostic => { 46 diagnostics[0] = diagnostic; 47 system.write(formatDiagnosticsWithColorAndContext(diagnostics, host) + host.getNewLine()); 48 diagnostics[0] = undefined!; // TODO: GH#18217 49 }; 50} 51 52/** 53 * @returns Whether the screen was cleared. 54 */ 55function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic, options: CompilerOptions): boolean { 56 if (system.clearScreen && 57 !options.preserveWatchOutput && 58 !options.extendedDiagnostics && 59 !options.diagnostics && 60 contains(screenStartingMessageCodes, diagnostic.code)) { 61 system.clearScreen(); 62 return true; 63 } 64 65 return false; 66} 67 68/** @internal */ 69export const screenStartingMessageCodes: number[] = [ 70 Diagnostics.Starting_compilation_in_watch_mode.code, 71 Diagnostics.File_change_detected_Starting_incremental_compilation.code, 72]; 73 74function getPlainDiagnosticFollowingNewLines(diagnostic: Diagnostic, newLine: string): string { 75 return contains(screenStartingMessageCodes, diagnostic.code) 76 ? newLine + newLine 77 : newLine; 78} 79 80/** 81 * Get locale specific time based on whether we are in test mode 82 * 83 * @internal 84 */ 85export function getLocaleTimeString(system: System) { 86 return !system.now ? 87 new Date().toLocaleTimeString() : 88 // On some systems / builds of Node, there's a non-breaking space between the time and AM/PM. 89 // This branch is solely for testing, so just switch it to a normal space for baseline stability. 90 // See: 91 // - https://github.com/nodejs/node/issues/45171 92 // - https://github.com/nodejs/node/issues/45753 93 system.now().toLocaleTimeString("en-US", { timeZone: "UTC" }).replace("\u202f", " "); 94} 95 96/** 97 * Create a function that reports watch status by writing to the system and handles the formatting of the diagnostic 98 * 99 * @internal 100 */ 101export function createWatchStatusReporter(system: System, pretty?: boolean): WatchStatusReporter { 102 return pretty ? 103 (diagnostic, newLine, options) => { 104 clearScreenIfNotWatchingForFileChanges(system, diagnostic, options); 105 let output = `[${formatColorAndReset(getLocaleTimeString(system), ForegroundColorEscapeSequences.Grey)}] `; 106 output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine}`; 107 system.write(output); 108 } : 109 (diagnostic, newLine, options) => { 110 let output = ""; 111 112 if (!clearScreenIfNotWatchingForFileChanges(system, diagnostic, options)) { 113 output += newLine; 114 } 115 116 output += `${getLocaleTimeString(system)} - `; 117 output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${getPlainDiagnosticFollowingNewLines(diagnostic, newLine)}`; 118 119 system.write(output); 120 }; 121} 122 123/** 124 * Parses config file using System interface 125 * 126 * @internal 127 */ 128export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, extendedConfigCache: Map<ExtendedConfigCacheEntry> | undefined, watchOptionsToExtend: WatchOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter): ParsedCommandLine | undefined { 129 const host: ParseConfigFileHost = system as any; 130 host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); 131 const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, extendedConfigCache, watchOptionsToExtend); 132 host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217 133 return result; 134} 135 136/** @internal */ 137export function getErrorCountForSummary(diagnostics: readonly Diagnostic[]) { 138 return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error); 139} 140 141/** @internal */ 142export function getFilesInErrorForSummary(diagnostics: readonly Diagnostic[]): (ReportFileInError | undefined)[] { 143 const filesInError = 144 filter(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error) 145 .map( 146 errorDiagnostic => { 147 if(errorDiagnostic.file === undefined) return; 148 return `${errorDiagnostic.file.fileName}`; 149 }); 150 return filesInError.map((fileName: string) => { 151 const diagnosticForFileName = find(diagnostics, diagnostic => 152 diagnostic.file !== undefined && diagnostic.file.fileName === fileName 153 ); 154 155 if(diagnosticForFileName !== undefined) { 156 const { line } = getLineAndCharacterOfPosition(diagnosticForFileName.file!, diagnosticForFileName.start!); 157 return { 158 fileName, 159 line: line + 1, 160 }; 161 } 162 }); 163} 164 165/** @internal */ 166export function getWatchErrorSummaryDiagnosticMessage(errorCount: number) { 167 return errorCount === 1 ? 168 Diagnostics.Found_1_error_Watching_for_file_changes : 169 Diagnostics.Found_0_errors_Watching_for_file_changes; 170} 171 172function prettyPathForFileError(error: ReportFileInError, cwd: string) { 173 const line = formatColorAndReset(":" + error.line, ForegroundColorEscapeSequences.Grey); 174 if (pathIsAbsolute(error.fileName) && pathIsAbsolute(cwd)) { 175 return getRelativePathFromDirectory(cwd, error.fileName, /* ignoreCase */ false) + line; 176 } 177 178 return error.fileName + line; 179} 180 181/** @internal */ 182export function getErrorSummaryText( 183 errorCount: number, 184 filesInError: readonly (ReportFileInError | undefined)[], 185 newLine: string, 186 host: HasCurrentDirectory 187) { 188 if (errorCount === 0) return ""; 189 const nonNilFiles = filesInError.filter(fileInError => fileInError !== undefined); 190 const distinctFileNamesWithLines = nonNilFiles.map(fileInError => `${fileInError!.fileName}:${fileInError!.line}`) 191 .filter((value, index, self) => self.indexOf(value) === index); 192 193 const firstFileReference = nonNilFiles[0] && prettyPathForFileError(nonNilFiles[0], host.getCurrentDirectory()); 194 195 const d = errorCount === 1 ? 196 createCompilerDiagnostic( 197 filesInError[0] !== undefined ? 198 Diagnostics.Found_1_error_in_1 : 199 Diagnostics.Found_1_error, 200 errorCount, 201 firstFileReference) : 202 createCompilerDiagnostic( 203 distinctFileNamesWithLines.length === 0 ? 204 Diagnostics.Found_0_errors : 205 distinctFileNamesWithLines.length === 1 ? 206 Diagnostics.Found_0_errors_in_the_same_file_starting_at_Colon_1 : 207 Diagnostics.Found_0_errors_in_1_files, 208 errorCount, 209 distinctFileNamesWithLines.length === 1 ? firstFileReference : distinctFileNamesWithLines.length); 210 211 const suffix = distinctFileNamesWithLines.length > 1 ? createTabularErrorsDisplay(nonNilFiles, host) : ""; 212 return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}${suffix}`; 213} 214 215function createTabularErrorsDisplay(filesInError: (ReportFileInError | undefined)[], host: HasCurrentDirectory) { 216 const distinctFiles = filesInError.filter((value, index, self) => index === self.findIndex(file => file?.fileName === value?.fileName)); 217 if (distinctFiles.length === 0) return ""; 218 219 const numberLength = (num: number) => Math.log(num) * Math.LOG10E + 1; 220 const fileToErrorCount = distinctFiles.map(file => ([file, countWhere(filesInError, fileInError => fileInError!.fileName === file!.fileName)] as const)); 221 const maxErrors = fileToErrorCount.reduce((acc, value) => Math.max(acc, value[1] || 0), 0); 222 223 const headerRow = Diagnostics.Errors_Files.message; 224 const leftColumnHeadingLength = headerRow.split(" ")[0].length; 225 const leftPaddingGoal = Math.max(leftColumnHeadingLength, numberLength(maxErrors)); 226 const headerPadding = Math.max(numberLength(maxErrors) - leftColumnHeadingLength, 0); 227 228 let tabularData = ""; 229 tabularData += " ".repeat(headerPadding) + headerRow + "\n"; 230 fileToErrorCount.forEach((row) => { 231 const [file, errorCount] = row; 232 const errorCountDigitsLength = Math.log(errorCount) * Math.LOG10E + 1 | 0; 233 const leftPadding = errorCountDigitsLength < leftPaddingGoal ? 234 " ".repeat(leftPaddingGoal - errorCountDigitsLength) 235 : ""; 236 237 const fileRef = prettyPathForFileError(file!, host.getCurrentDirectory()); 238 tabularData += `${leftPadding}${errorCount} ${fileRef}\n`; 239 }); 240 241 return tabularData; 242} 243 244/** @internal */ 245export function isBuilderProgram(program: Program | BuilderProgram): program is BuilderProgram { 246 return !!(program as BuilderProgram).getState; 247} 248 249/** @internal */ 250export function listFiles<T extends BuilderProgram>(program: Program | T, write: (s: string) => void) { 251 const options = program.getCompilerOptions(); 252 if (options.explainFiles) { 253 explainFiles(isBuilderProgram(program) ? program.getProgram() : program, write); 254 } 255 else if (options.listFiles || options.listFilesOnly) { 256 forEach(program.getSourceFiles(), file => { 257 write(file.fileName); 258 }); 259 } 260} 261 262/** @internal */ 263export function explainFiles(program: Program, write: (s: string) => void) { 264 const reasons = program.getFileIncludeReasons(); 265 const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); 266 const relativeFileName = (fileName: string) => convertToRelativePath(fileName, program.getCurrentDirectory(), getCanonicalFileName); 267 for (const file of program.getSourceFiles()) { 268 write(`${toFileName(file, relativeFileName)}`); 269 reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`)); 270 explainIfFileIsRedirectAndImpliedFormat(file, relativeFileName)?.forEach(d => write(` ${d.messageText}`)); 271 } 272} 273 274/** @internal */ 275export function explainIfFileIsRedirectAndImpliedFormat( 276 file: SourceFile, 277 fileNameConvertor?: (fileName: string) => string, 278): DiagnosticMessageChain[] | undefined { 279 let result: DiagnosticMessageChain[] | undefined; 280 if (file.path !== file.resolvedPath) { 281 (result ??= []).push(chainDiagnosticMessages( 282 /*details*/ undefined, 283 Diagnostics.File_is_output_of_project_reference_source_0, 284 toFileName(file.originalFileName, fileNameConvertor) 285 )); 286 } 287 if (file.redirectInfo) { 288 (result ??= []).push(chainDiagnosticMessages( 289 /*details*/ undefined, 290 Diagnostics.File_redirects_to_file_0, 291 toFileName(file.redirectInfo.redirectTarget, fileNameConvertor) 292 )); 293 } 294 if (isExternalOrCommonJsModule(file)) { 295 switch (file.impliedNodeFormat) { 296 case ModuleKind.ESNext: 297 if (file.packageJsonScope) { 298 (result ??= []).push(chainDiagnosticMessages( 299 /*details*/ undefined, 300 Diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module, 301 toFileName(last(file.packageJsonLocations!), fileNameConvertor) 302 )); 303 } 304 break; 305 case ModuleKind.CommonJS: 306 if (file.packageJsonScope) { 307 (result ??= []).push(chainDiagnosticMessages( 308 /*details*/ undefined, 309 file.packageJsonScope.contents.packageJsonContent.type ? 310 Diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module : 311 Diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type, 312 toFileName(last(file.packageJsonLocations!), fileNameConvertor) 313 )); 314 } 315 else if (file.packageJsonLocations?.length) { 316 (result ??= []).push(chainDiagnosticMessages( 317 /*details*/ undefined, 318 Diagnostics.File_is_CommonJS_module_because_package_json_was_not_found, 319 )); 320 } 321 break; 322 } 323 } 324 return result; 325} 326 327/** @internal */ 328export function getMatchedFileSpec(program: Program, fileName: string) { 329 const configFile = program.getCompilerOptions().configFile; 330 if (!configFile?.configFileSpecs?.validatedFilesSpec) return undefined; 331 332 const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); 333 const filePath = getCanonicalFileName(fileName); 334 const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); 335 return find(configFile.configFileSpecs.validatedFilesSpec, fileSpec => getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath); 336} 337 338/** @internal */ 339export function getMatchedIncludeSpec(program: Program, fileName: string) { 340 const configFile = program.getCompilerOptions().configFile; 341 if (!configFile?.configFileSpecs?.validatedIncludeSpecs) return undefined; 342 343 // Return true if its default include spec 344 if (configFile.configFileSpecs.isDefaultIncludeSpec) return true; 345 346 const isJsonFile = fileExtensionIs(fileName, Extension.Json); 347 const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); 348 const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames(); 349 return find(configFile?.configFileSpecs?.validatedIncludeSpecs, includeSpec => { 350 if (isJsonFile && !endsWith(includeSpec, Extension.Json)) return false; 351 const pattern = getPatternFromSpec(includeSpec, basePath, "files"); 352 return !!pattern && getRegexFromPattern(`(${pattern})$`, useCaseSensitiveFileNames).test(fileName); 353 }); 354} 355 356/** @internal */ 357export function fileIncludeReasonToDiagnostics(program: Program, reason: FileIncludeReason, fileNameConvertor?: (fileName: string) => string,): DiagnosticMessageChain { 358 const options = program.getCompilerOptions(); 359 if (isReferencedFile(reason)) { 360 const referenceLocation = getReferencedFileLocation(path => program.getSourceFileByPath(path), reason); 361 const referenceText = isReferenceFileLocation(referenceLocation) ? referenceLocation.file.text.substring(referenceLocation.pos, referenceLocation.end) : `"${referenceLocation.text}"`; 362 let message: DiagnosticMessage; 363 Debug.assert(isReferenceFileLocation(referenceLocation) || reason.kind === FileIncludeKind.Import, "Only synthetic references are imports"); 364 switch (reason.kind) { 365 case FileIncludeKind.Import: 366 if (isReferenceFileLocation(referenceLocation)) { 367 message = referenceLocation.packageId ? 368 Diagnostics.Imported_via_0_from_file_1_with_packageId_2 : 369 Diagnostics.Imported_via_0_from_file_1; 370 } 371 else if (referenceLocation.text === externalHelpersModuleNameText) { 372 message = referenceLocation.packageId ? 373 Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions : 374 Diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions; 375 } 376 else { 377 message = referenceLocation.packageId ? 378 Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions : 379 Diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions; 380 } 381 break; 382 case FileIncludeKind.ReferenceFile: 383 Debug.assert(!referenceLocation.packageId); 384 message = Diagnostics.Referenced_via_0_from_file_1; 385 break; 386 case FileIncludeKind.TypeReferenceDirective: 387 message = referenceLocation.packageId ? 388 Diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2 : 389 Diagnostics.Type_library_referenced_via_0_from_file_1; 390 break; 391 case FileIncludeKind.LibReferenceDirective: 392 Debug.assert(!referenceLocation.packageId); 393 message = Diagnostics.Library_referenced_via_0_from_file_1; 394 break; 395 default: 396 Debug.assertNever(reason); 397 } 398 return chainDiagnosticMessages( 399 /*details*/ undefined, 400 message, 401 referenceText, 402 toFileName(referenceLocation.file, fileNameConvertor), 403 referenceLocation.packageId && packageIdToString(referenceLocation.packageId) 404 ); 405 } 406 switch (reason.kind) { 407 case FileIncludeKind.RootFile: 408 if (!options.configFile?.configFileSpecs) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation); 409 const fileName = getNormalizedAbsolutePath(program.getRootFileNames()[reason.index], program.getCurrentDirectory()); 410 const matchedByFiles = getMatchedFileSpec(program, fileName); 411 if (matchedByFiles) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Part_of_files_list_in_tsconfig_json); 412 const matchedByInclude = getMatchedIncludeSpec(program, fileName); 413 return isString(matchedByInclude) ? 414 chainDiagnosticMessages( 415 /*details*/ undefined, 416 Diagnostics.Matched_by_include_pattern_0_in_1, 417 matchedByInclude, 418 toFileName(options.configFile, fileNameConvertor) 419 ) : 420 // Could be additional files specified as roots or matched by default include 421 chainDiagnosticMessages(/*details*/ undefined, matchedByInclude ? 422 Diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk : 423 Diagnostics.Root_file_specified_for_compilation 424 ); 425 case FileIncludeKind.SourceFromProjectReference: 426 case FileIncludeKind.OutputFromProjectReference: 427 const isOutput = reason.kind === FileIncludeKind.OutputFromProjectReference; 428 const referencedResolvedRef = Debug.checkDefined(program.getResolvedProjectReferences()?.[reason.index]); 429 return chainDiagnosticMessages( 430 /*details*/ undefined, 431 outFile(options) ? 432 isOutput ? 433 Diagnostics.Output_from_referenced_project_0_included_because_1_specified : 434 Diagnostics.Source_from_referenced_project_0_included_because_1_specified : 435 isOutput ? 436 Diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none : 437 Diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, 438 toFileName(referencedResolvedRef.sourceFile.fileName, fileNameConvertor), 439 options.outFile ? "--outFile" : "--out", 440 ); 441 case FileIncludeKind.AutomaticTypeDirectiveFile: 442 return chainDiagnosticMessages( 443 /*details*/ undefined, 444 options.types ? 445 reason.packageId ? 446 Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1 : 447 Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions : 448 reason.packageId ? 449 Diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1 : 450 Diagnostics.Entry_point_for_implicit_type_library_0, 451 reason.typeReference, 452 reason.packageId && packageIdToString(reason.packageId), 453 ); 454 case FileIncludeKind.LibFile: 455 if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]); 456 const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined); 457 return chainDiagnosticMessages( 458 /*details*/ undefined, 459 target ? 460 Diagnostics.Default_library_for_target_0 : 461 Diagnostics.Default_library, 462 target, 463 ); 464 default: 465 Debug.assertNever(reason); 466 } 467} 468 469function toFileName(file: SourceFile | string, fileNameConvertor?: (fileName: string) => string) { 470 const fileName = isString(file) ? file : file.fileName; 471 return fileNameConvertor ? fileNameConvertor(fileName) : fileName; 472} 473 474/** 475 * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options 476 * 477 * @internal 478 */ 479export function emitFilesAndReportErrors<T extends BuilderProgram>( 480 program: Program | T, 481 reportDiagnostic: DiagnosticReporter, 482 write?: (s: string) => void, 483 reportSummary?: ReportEmitErrorSummary, 484 writeFile?: WriteFileCallback, 485 cancellationToken?: CancellationToken, 486 emitOnlyDtsFiles?: boolean, 487 customTransformers?: CustomTransformers 488) : { 489 emitResult: EmitResult; 490 diagnostics: SortedReadonlyArray<Diagnostic>; 491 } { 492 const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly; 493 494 // First get and report any syntactic errors. 495 const allDiagnostics = program.getConfigFileParsingDiagnostics().slice(); 496 const configFileParsingDiagnosticsLength = allDiagnostics.length; 497 addRange(allDiagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken)); 498 499 // If we didn't have any syntactic errors, then also try getting the global and 500 // semantic errors. 501 if (allDiagnostics.length === configFileParsingDiagnosticsLength) { 502 addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken)); 503 504 if (!isListFilesOnly) { 505 addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken)); 506 507 if (allDiagnostics.length === configFileParsingDiagnosticsLength) { 508 addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken)); 509 } 510 } 511 } 512 513 // Emit and report any errors we ran into. 514 const emitResult = isListFilesOnly 515 ? { emitSkipped: true, diagnostics: emptyArray } 516 : program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); 517 const { emittedFiles, diagnostics: emitDiagnostics } = emitResult; 518 addRange(allDiagnostics, emitDiagnostics); 519 520 const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics); 521 diagnostics.forEach(reportDiagnostic); 522 if (write) { 523 const currentDir = program.getCurrentDirectory(); 524 forEach(emittedFiles, file => { 525 const filepath = getNormalizedAbsolutePath(file, currentDir); 526 write(`TSFILE: ${filepath}`); 527 }); 528 listFiles(program, write); 529 } 530 531 if (reportSummary) { 532 reportSummary(getErrorCountForSummary(diagnostics), getFilesInErrorForSummary(diagnostics)); 533 } 534 535 return { 536 emitResult, 537 diagnostics, 538 }; 539} 540 541/** @internal */ 542export function emitFilesAndReportErrorsAndGetExitStatus<T extends BuilderProgram>( 543 program: Program | T, 544 reportDiagnostic: DiagnosticReporter, 545 write?: (s: string) => void, 546 reportSummary?: ReportEmitErrorSummary, 547 writeFile?: WriteFileCallback, 548 cancellationToken?: CancellationToken, 549 emitOnlyDtsFiles?: boolean, 550 customTransformers?: CustomTransformers 551) { 552 const { emitResult, diagnostics } = emitFilesAndReportErrors( 553 program, 554 reportDiagnostic, 555 write, 556 reportSummary, 557 writeFile, 558 cancellationToken, 559 emitOnlyDtsFiles, 560 customTransformers 561 ); 562 563 if (emitResult.emitSkipped && diagnostics.length > 0) { 564 // If the emitter didn't emit anything, then pass that value along. 565 return ExitStatus.DiagnosticsPresent_OutputsSkipped; 566 } 567 else if (diagnostics.length > 0) { 568 // The emitter emitted something, inform the caller if that happened in the presence 569 // of diagnostics or not. 570 return ExitStatus.DiagnosticsPresent_OutputsGenerated; 571 } 572 return ExitStatus.Success; 573} 574 575/** @internal */ 576export const noopFileWatcher: FileWatcher = { close: noop }; 577/** @internal */ 578export const returnNoopFileWatcher = () => noopFileWatcher; 579 580/** @internal */ 581export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost { 582 const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system); 583 return { 584 onWatchStatusChange, 585 watchFile: maybeBind(system, system.watchFile) || returnNoopFileWatcher, 586 watchDirectory: maybeBind(system, system.watchDirectory) || returnNoopFileWatcher, 587 setTimeout: maybeBind(system, system.setTimeout) || noop, 588 clearTimeout: maybeBind(system, system.clearTimeout) || noop 589 }; 590} 591 592/** @internal */ 593export type WatchType = WatchTypeRegistry[keyof WatchTypeRegistry]; 594/** @internal */ 595export const WatchType: WatchTypeRegistry = { 596 ConfigFile: "Config file", 597 ExtendedConfigFile: "Extended config file", 598 SourceFile: "Source file", 599 MissingFile: "Missing file", 600 WildcardDirectory: "Wild card directory", 601 FailedLookupLocations: "Failed Lookup Locations", 602 AffectingFileLocation: "File location affecting resolution", 603 TypeRoots: "Type roots", 604 ConfigFileOfReferencedProject: "Config file of referened project", 605 ExtendedConfigOfReferencedProject: "Extended config file of referenced project", 606 WildcardDirectoryOfReferencedProject: "Wild card directory of referenced project", 607 PackageJson: "package.json file", 608 ClosedScriptInfo: "Closed Script info", 609 ConfigFileForInferredRoot: "Config file for the inferred project root", 610 NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache", 611 MissingSourceMapFile: "Missing source map file", 612 NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root", 613 MissingGeneratedFile: "Missing generated file", 614 NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation", 615}; 616 617/** @internal */ 618export interface WatchTypeRegistry { 619 ConfigFile: "Config file", 620 ExtendedConfigFile: "Extended config file", 621 SourceFile: "Source file", 622 MissingFile: "Missing file", 623 WildcardDirectory: "Wild card directory", 624 FailedLookupLocations: "Failed Lookup Locations", 625 AffectingFileLocation: "File location affecting resolution", 626 TypeRoots: "Type roots", 627 ConfigFileOfReferencedProject: "Config file of referened project", 628 ExtendedConfigOfReferencedProject: "Extended config file of referenced project", 629 WildcardDirectoryOfReferencedProject: "Wild card directory of referenced project", 630 PackageJson: "package.json file", 631 632 // Additional tsserver specific watch information 633 ClosedScriptInfo: "Closed Script info", 634 ConfigFileForInferredRoot: "Config file for the inferred project root", 635 NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache", 636 MissingSourceMapFile: "Missing source map file", 637 NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root", 638 MissingGeneratedFile: "Missing generated file", 639 NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation", 640} 641 642/** @internal */ 643export interface WatchFactoryWithLog<X, Y = undefined> extends WatchFactory<X, Y> { 644 writeLog: (s: string) => void; 645} 646 647/** @internal */ 648export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { 649 const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None; 650 const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop; 651 const result = getWatchFactory<WatchType, Y>(host, watchLogLevel, writeLog) as WatchFactoryWithLog<WatchType, Y>; 652 result.writeLog = writeLog; 653 return result; 654} 655 656/** @internal */ 657export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost { 658 const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); 659 const hostGetNewLine = memoize(() => host.getNewLine()); 660 const compilerHost: CompilerHost = { 661 getSourceFile: (fileName, languageVersionOrOptions, onError) => { 662 const options = getCompilerOptions(); 663 let text: string | undefined; 664 try { 665 performance.mark("beforeIORead"); 666 const encoding = getCompilerOptions().charset; 667 text = !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding); 668 performance.mark("afterIORead"); 669 performance.measure("I/O Read", "beforeIORead", "afterIORead"); 670 } 671 catch (e) { 672 if (onError) { 673 onError(e.message); 674 } 675 text = ""; 676 } 677 678 return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, /*setParentNodes*/ undefined, /*scriptKind*/ undefined, options) : undefined; 679 }, 680 getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation), 681 getDefaultLibFileName: options => host.getDefaultLibFileName(options), 682 writeFile, 683 getCurrentDirectory: memoize(() => host.getCurrentDirectory()), 684 useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, 685 getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames), 686 getNewLine: () => getNewLineCharacter(getCompilerOptions(), hostGetNewLine), 687 fileExists: f => host.fileExists(f), 688 readFile: f => host.readFile(f), 689 trace: maybeBind(host, host.trace), 690 directoryExists: maybeBind(directoryStructureHost, directoryStructureHost.directoryExists), 691 getDirectories: maybeBind(directoryStructureHost, directoryStructureHost.getDirectories), 692 realpath: maybeBind(host, host.realpath), 693 getEnvironmentVariable: maybeBind(host, host.getEnvironmentVariable) || (() => ""), 694 createHash: maybeBind(host, host.createHash), 695 readDirectory: maybeBind(host, host.readDirectory), 696 disableUseFileVersionAsSignature: host.disableUseFileVersionAsSignature, 697 storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit, 698 }; 699 return compilerHost; 700 701 function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { 702 try { 703 performance.mark("beforeIOWrite"); 704 705 // NOTE: If patchWriteFileEnsuringDirectory has been called, 706 // the host.writeFile will do its own directory creation and 707 // the ensureDirectoriesExist call will always be redundant. 708 writeFileEnsuringDirectories( 709 fileName, 710 text, 711 writeByteOrderMark, 712 (path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark), 713 path => host.createDirectory!(path), 714 path => host.directoryExists!(path)); 715 716 performance.mark("afterIOWrite"); 717 performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); 718 } 719 catch (e) { 720 if (onError) { 721 onError(e.message); 722 } 723 } 724 } 725} 726 727/** @internal */ 728export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost) { 729 const originalGetSourceFile = compilerHost.getSourceFile; 730 const computeHash = maybeBind(compilerHost, compilerHost.createHash) || generateDjb2Hash; 731 compilerHost.getSourceFile = (...args) => { 732 const result = originalGetSourceFile.call(compilerHost, ...args); 733 if (result) { 734 result.version = computeHash(result.text); 735 } 736 return result; 737 }; 738} 739 740/** 741 * Creates the watch compiler host that can be extended with config file or root file names and options host 742 * 743 * @internal 744 */ 745export function createProgramHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system: System, createProgram: CreateProgram<T> | undefined): ProgramHost<T> { 746 const getDefaultLibLocation = memoize(() => getDirectoryPath(normalizePath(system.getExecutingFilePath()))); 747 return { 748 useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, 749 getNewLine: () => system.newLine, 750 getCurrentDirectory: memoize(() => system.getCurrentDirectory()), 751 getDefaultLibLocation, 752 getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), 753 fileExists: path => system.fileExists(path), 754 readFile: (path, encoding) => system.readFile(path, encoding), 755 directoryExists: path => system.directoryExists(path), 756 getDirectories: path => system.getDirectories(path), 757 readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth), 758 realpath: maybeBind(system, system.realpath), 759 getEnvironmentVariable: maybeBind(system, system.getEnvironmentVariable), 760 trace: s => system.write(s + system.newLine), 761 createDirectory: path => system.createDirectory(path), 762 writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), 763 createHash: maybeBind(system, system.createHash), 764 createProgram: createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T>, 765 disableUseFileVersionAsSignature: system.disableUseFileVersionAsSignature, 766 storeFilesChangingSignatureDuringEmit: system.storeFilesChangingSignatureDuringEmit, 767 now: maybeBind(system, system.now), 768 }; 769} 770 771/** 772 * Creates the watch compiler host that can be extended with config file or root file names and options host 773 */ 774function createWatchCompilerHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system = sys, createProgram: CreateProgram<T> | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost<T> { 775 const write = (s: string) => system.write(s + system.newLine); 776 const result = createProgramHost(system, createProgram) as WatchCompilerHost<T>; 777 copyProperties(result, createWatchHost(system, reportWatchStatus)); 778 result.afterProgramCreate = builderProgram => { 779 const compilerOptions = builderProgram.getCompilerOptions(); 780 const newLine = getNewLineCharacter(compilerOptions, () => system.newLine); 781 782 emitFilesAndReportErrors( 783 builderProgram, 784 reportDiagnostic, 785 write, 786 errorCount => result.onWatchStatusChange!( 787 createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount), 788 newLine, 789 compilerOptions, 790 errorCount 791 ) 792 ); 793 }; 794 return result; 795} 796 797/** 798 * Report error and exit 799 */ 800function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { 801 reportDiagnostic(diagnostic); 802 system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); 803} 804 805/** @internal */ 806export interface CreateWatchCompilerHostInput<T extends BuilderProgram> { 807 system: System; 808 createProgram?: CreateProgram<T>; 809 reportDiagnostic?: DiagnosticReporter; 810 reportWatchStatus?: WatchStatusReporter; 811} 812 813/** @internal */ 814export interface CreateWatchCompilerHostOfConfigFileInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> { 815 configFileName: string; 816 optionsToExtend?: CompilerOptions; 817 watchOptionsToExtend?: WatchOptions; 818 extraFileExtensions?: readonly FileExtensionInfo[]; 819} 820/** 821 * Creates the watch compiler host from system for config file in watch mode 822 * 823 * @internal 824 */ 825export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({ 826 configFileName, optionsToExtend, watchOptionsToExtend, extraFileExtensions, 827 system, createProgram, reportDiagnostic, reportWatchStatus 828}: CreateWatchCompilerHostOfConfigFileInput<T>): WatchCompilerHostOfConfigFile<T> { 829 const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system); 830 const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>; 831 host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic); 832 host.configFileName = configFileName; 833 host.optionsToExtend = optionsToExtend; 834 host.watchOptionsToExtend = watchOptionsToExtend; 835 host.extraFileExtensions = extraFileExtensions; 836 return host; 837} 838 839/** @internal */ 840export interface CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> { 841 rootFiles: string[]; 842 options: CompilerOptions; 843 watchOptions: WatchOptions | undefined; 844 projectReferences?: readonly ProjectReference[]; 845} 846/** 847 * Creates the watch compiler host from system for compiling root files and options in watch mode 848 * 849 * @internal 850 */ 851export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({ 852 rootFiles, options, watchOptions, projectReferences, 853 system, createProgram, reportDiagnostic, reportWatchStatus 854}: CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T>): WatchCompilerHostOfFilesAndCompilerOptions<T> { 855 const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>; 856 host.rootFiles = rootFiles; 857 host.options = options; 858 host.watchOptions = watchOptions; 859 host.projectReferences = projectReferences; 860 return host; 861} 862 863/** @internal */ 864export interface IncrementalCompilationOptions { 865 rootNames: readonly string[]; 866 options: CompilerOptions; 867 configFileParsingDiagnostics?: readonly Diagnostic[]; 868 projectReferences?: readonly ProjectReference[]; 869 host?: CompilerHost; 870 reportDiagnostic?: DiagnosticReporter; 871 reportErrorSummary?: ReportEmitErrorSummary; 872 afterProgramEmitAndDiagnostics?(program: EmitAndSemanticDiagnosticsBuilderProgram): void; 873 system?: System; 874} 875/** @internal */ 876export function performIncrementalCompilation(input: IncrementalCompilationOptions) { 877 const system = input.system || sys; 878 const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system)); 879 const builderProgram = createIncrementalProgram(input); 880 const exitStatus = emitFilesAndReportErrorsAndGetExitStatus( 881 builderProgram, 882 input.reportDiagnostic || createDiagnosticReporter(system), 883 s => host.trace && host.trace(s), 884 input.reportErrorSummary || input.options.pretty ? (errorCount, filesInError) => system.write(getErrorSummaryText(errorCount, filesInError, system.newLine, host)) : undefined 885 ); 886 if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram); 887 return exitStatus; 888} 889