1/*@internal*/ 2namespace ts { 3 const sysFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { 4 getCurrentDirectory: () => sys.getCurrentDirectory(), 5 getNewLine: () => sys.newLine, 6 getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames) 7 } : undefined!; // TODO: GH#18217 8 9 /** 10 * Create a function that reports error by writing to the system and handles the formating of the diagnostic 11 */ 12 export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter { 13 const host: FormatDiagnosticsHost = system === sys ? sysFormatDiagnosticsHost : { 14 getCurrentDirectory: () => system.getCurrentDirectory(), 15 getNewLine: () => system.newLine, 16 getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), 17 }; 18 if (!pretty) { 19 return diagnostic => system.write(formatDiagnostic(diagnostic, host)); 20 } 21 22 const diagnostics: Diagnostic[] = new Array(1); 23 return diagnostic => { 24 diagnostics[0] = diagnostic; 25 system.write(formatDiagnosticsWithColorAndContext(diagnostics, host) + host.getNewLine()); 26 diagnostics[0] = undefined!; // TODO: GH#18217 27 }; 28 } 29 30 /** 31 * @returns Whether the screen was cleared. 32 */ 33 function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic, options: CompilerOptions): boolean { 34 if (system.clearScreen && 35 !options.preserveWatchOutput && 36 !options.extendedDiagnostics && 37 !options.diagnostics && 38 contains(screenStartingMessageCodes, diagnostic.code)) { 39 system.clearScreen(); 40 return true; 41 } 42 43 return false; 44 } 45 46 export const screenStartingMessageCodes: number[] = [ 47 Diagnostics.Starting_compilation_in_watch_mode.code, 48 Diagnostics.File_change_detected_Starting_incremental_compilation.code, 49 ]; 50 51 function getPlainDiagnosticFollowingNewLines(diagnostic: Diagnostic, newLine: string): string { 52 return contains(screenStartingMessageCodes, diagnostic.code) 53 ? newLine + newLine 54 : newLine; 55 } 56 57 /** 58 * Get locale specific time based on whether we are in test mode 59 */ 60 export function getLocaleTimeString(system: System) { 61 return !system.now ? 62 new Date().toLocaleTimeString() : 63 system.now().toLocaleTimeString("en-US", { timeZone: "UTC" }); 64 } 65 66 /** 67 * Create a function that reports watch status by writing to the system and handles the formating of the diagnostic 68 */ 69 export function createWatchStatusReporter(system: System, pretty?: boolean): WatchStatusReporter { 70 return pretty ? 71 (diagnostic, newLine, options) => { 72 clearScreenIfNotWatchingForFileChanges(system, diagnostic, options); 73 let output = `[${formatColorAndReset(getLocaleTimeString(system), ForegroundColorEscapeSequences.Grey)}] `; 74 output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine}`; 75 system.write(output); 76 } : 77 (diagnostic, newLine, options) => { 78 let output = ""; 79 80 if (!clearScreenIfNotWatchingForFileChanges(system, diagnostic, options)) { 81 output += newLine; 82 } 83 84 output += `${getLocaleTimeString(system)} - `; 85 output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${getPlainDiagnosticFollowingNewLines(diagnostic, newLine)}`; 86 87 system.write(output); 88 }; 89 } 90 91 /** Parses config file using System interface */ 92 export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, watchOptionsToExtend: WatchOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter) { 93 const host: ParseConfigFileHost = <any>system; 94 host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); 95 const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, /*extendedConfigCache*/ undefined, watchOptionsToExtend); 96 host.onUnRecoverableConfigFileDiagnostic = undefined!; // TODO: GH#18217 97 return result; 98 } 99 100 export function getErrorCountForSummary(diagnostics: readonly Diagnostic[]) { 101 return countWhere(diagnostics, diagnostic => diagnostic.category === DiagnosticCategory.Error); 102 } 103 104 export function getWatchErrorSummaryDiagnosticMessage(errorCount: number) { 105 return errorCount === 1 ? 106 Diagnostics.Found_1_error_Watching_for_file_changes : 107 Diagnostics.Found_0_errors_Watching_for_file_changes; 108 } 109 110 export function getErrorSummaryText(errorCount: number, newLine: string) { 111 if (errorCount === 0) return ""; 112 const d = createCompilerDiagnostic(errorCount === 1 ? Diagnostics.Found_1_error : Diagnostics.Found_0_errors, errorCount); 113 return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`; 114 } 115 116 export function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T { 117 return !!(program as T).getState; 118 } 119 120 export function listFiles<T extends BuilderProgram>(program: Program | T, write: (s: string) => void) { 121 const options = program.getCompilerOptions(); 122 if (options.explainFiles) { 123 explainFiles(isBuilderProgram(program) ? program.getProgram() : program, write); 124 } 125 else if (options.listFiles || options.listFilesOnly) { 126 forEach(program.getSourceFiles(), file => { 127 write(file.fileName); 128 }); 129 } 130 } 131 132 export function explainFiles(program: Program, write: (s: string) => void) { 133 const reasons = program.getFileIncludeReasons(); 134 const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); 135 const relativeFileName = (fileName: string) => convertToRelativePath(fileName, program.getCurrentDirectory(), getCanonicalFileName); 136 for (const file of program.getSourceFiles()) { 137 write(`${toFileName(file, relativeFileName)}`); 138 reasons.get(file.path)?.forEach(reason => write(` ${fileIncludeReasonToDiagnostics(program, reason, relativeFileName).messageText}`)); 139 explainIfFileIsRedirect(file, relativeFileName)?.forEach(d => write(` ${d.messageText}`)); 140 } 141 } 142 143 export function explainIfFileIsRedirect(file: SourceFile, fileNameConvertor?: (fileName: string) => string): DiagnosticMessageChain[] | undefined { 144 let result: DiagnosticMessageChain[] | undefined; 145 if (file.path !== file.resolvedPath) { 146 (result ||= []).push(chainDiagnosticMessages( 147 /*details*/ undefined, 148 Diagnostics.File_is_output_of_project_reference_source_0, 149 toFileName(file.originalFileName, fileNameConvertor) 150 )); 151 } 152 if (file.redirectInfo) { 153 (result ||= []).push(chainDiagnosticMessages( 154 /*details*/ undefined, 155 Diagnostics.File_redirects_to_file_0, 156 toFileName(file.redirectInfo.redirectTarget, fileNameConvertor) 157 )); 158 } 159 return result; 160 } 161 162 export function getMatchedFileSpec(program: Program, fileName: string) { 163 const configFile = program.getCompilerOptions().configFile; 164 if (!configFile?.configFileSpecs?.validatedFilesSpec) return undefined; 165 166 const getCanonicalFileName = createGetCanonicalFileName(program.useCaseSensitiveFileNames()); 167 const filePath = getCanonicalFileName(fileName); 168 const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); 169 return find(configFile.configFileSpecs.validatedFilesSpec, fileSpec => getCanonicalFileName(getNormalizedAbsolutePath(fileSpec, basePath)) === filePath); 170 } 171 172 export function getMatchedIncludeSpec(program: Program, fileName: string) { 173 const configFile = program.getCompilerOptions().configFile; 174 if (!configFile?.configFileSpecs?.validatedIncludeSpecs) return undefined; 175 176 const isJsonFile = fileExtensionIs(fileName, Extension.Json); 177 const basePath = getDirectoryPath(getNormalizedAbsolutePath(configFile.fileName, program.getCurrentDirectory())); 178 const useCaseSensitiveFileNames = program.useCaseSensitiveFileNames(); 179 return find(configFile?.configFileSpecs?.validatedIncludeSpecs, includeSpec => { 180 if (isJsonFile && !endsWith(includeSpec, Extension.Json)) return false; 181 const pattern = getPatternFromSpec(includeSpec, basePath, "files"); 182 return !!pattern && getRegexFromPattern(`(${pattern})$`, useCaseSensitiveFileNames).test(fileName); 183 }); 184 } 185 186 export function fileIncludeReasonToDiagnostics(program: Program, reason: FileIncludeReason, fileNameConvertor?: (fileName: string) => string,): DiagnosticMessageChain { 187 const options = program.getCompilerOptions(); 188 if (isReferencedFile(reason)) { 189 const referenceLocation = getReferencedFileLocation(path => program.getSourceFileByPath(path), reason); 190 const referenceText = isReferenceFileLocation(referenceLocation) ? referenceLocation.file.text.substring(referenceLocation.pos, referenceLocation.end) : `"${referenceLocation.text}"`; 191 let message: DiagnosticMessage; 192 Debug.assert(isReferenceFileLocation(referenceLocation) || reason.kind === FileIncludeKind.Import, "Only synthetic references are imports"); 193 switch (reason.kind) { 194 case FileIncludeKind.Import: 195 if (isReferenceFileLocation(referenceLocation)) { 196 message = referenceLocation.packageId ? 197 Diagnostics.Imported_via_0_from_file_1_with_packageId_2 : 198 Diagnostics.Imported_via_0_from_file_1; 199 } 200 else if (referenceLocation.text === externalHelpersModuleNameText) { 201 message = referenceLocation.packageId ? 202 Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions : 203 Diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions; 204 } 205 else { 206 message = referenceLocation.packageId ? 207 Diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions : 208 Diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions; 209 } 210 break; 211 case FileIncludeKind.ReferenceFile: 212 Debug.assert(!referenceLocation.packageId); 213 message = Diagnostics.Referenced_via_0_from_file_1; 214 break; 215 case FileIncludeKind.TypeReferenceDirective: 216 message = referenceLocation.packageId ? 217 Diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2 : 218 Diagnostics.Type_library_referenced_via_0_from_file_1; 219 break; 220 case FileIncludeKind.LibReferenceDirective: 221 Debug.assert(!referenceLocation.packageId); 222 message = Diagnostics.Library_referenced_via_0_from_file_1; 223 break; 224 default: 225 Debug.assertNever(reason); 226 } 227 return chainDiagnosticMessages( 228 /*details*/ undefined, 229 message, 230 referenceText, 231 toFileName(referenceLocation.file, fileNameConvertor), 232 referenceLocation.packageId && packageIdToString(referenceLocation.packageId) 233 ); 234 } 235 switch (reason.kind) { 236 case FileIncludeKind.RootFile: 237 if (!options.configFile?.configFileSpecs) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation); 238 const fileName = getNormalizedAbsolutePath(program.getRootFileNames()[reason.index], program.getCurrentDirectory()); 239 const matchedByFiles = getMatchedFileSpec(program, fileName); 240 if (matchedByFiles) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Part_of_files_list_in_tsconfig_json); 241 const matchedByInclude = getMatchedIncludeSpec(program, fileName); 242 return matchedByInclude ? 243 chainDiagnosticMessages( 244 /*details*/ undefined, 245 Diagnostics.Matched_by_include_pattern_0_in_1, 246 matchedByInclude, 247 toFileName(options.configFile, fileNameConvertor) 248 ) : 249 // Could be additional files specified as roots 250 chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Root_file_specified_for_compilation); 251 case FileIncludeKind.SourceFromProjectReference: 252 case FileIncludeKind.OutputFromProjectReference: 253 const isOutput = reason.kind === FileIncludeKind.OutputFromProjectReference; 254 const referencedResolvedRef = Debug.checkDefined(program.getResolvedProjectReferences()?.[reason.index]); 255 return chainDiagnosticMessages( 256 /*details*/ undefined, 257 outFile(options) ? 258 isOutput ? 259 Diagnostics.Output_from_referenced_project_0_included_because_1_specified : 260 Diagnostics.Source_from_referenced_project_0_included_because_1_specified : 261 isOutput ? 262 Diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none : 263 Diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, 264 toFileName(referencedResolvedRef.sourceFile.fileName, fileNameConvertor), 265 options.outFile ? "--outFile" : "--out", 266 ); 267 case FileIncludeKind.AutomaticTypeDirectiveFile: 268 return chainDiagnosticMessages( 269 /*details*/ undefined, 270 options.types ? 271 reason.packageId ? 272 Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1 : 273 Diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions : 274 reason.packageId ? 275 Diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1 : 276 Diagnostics.Entry_point_for_implicit_type_library_0, 277 reason.typeReference, 278 reason.packageId && packageIdToString(reason.packageId), 279 ); 280 case FileIncludeKind.LibFile: 281 if (reason.index !== undefined) return chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Library_0_specified_in_compilerOptions, options.lib![reason.index]); 282 const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined); 283 return chainDiagnosticMessages( 284 /*details*/ undefined, 285 target ? 286 Diagnostics.Default_library_for_target_0 : 287 Diagnostics.Default_library, 288 target, 289 ); 290 default: 291 Debug.assertNever(reason); 292 } 293 } 294 295 function toFileName(file: SourceFile | string, fileNameConvertor?: (fileName: string) => string) { 296 const fileName = isString(file) ? file : file.fileName; 297 return fileNameConvertor ? fileNameConvertor(fileName) : fileName; 298 } 299 300 /** 301 * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options 302 */ 303 export function emitFilesAndReportErrors<T extends BuilderProgram>( 304 program: Program | T, 305 reportDiagnostic: DiagnosticReporter, 306 write?: (s: string) => void, 307 reportSummary?: ReportEmitErrorSummary, 308 writeFile?: WriteFileCallback, 309 cancellationToken?: CancellationToken, 310 emitOnlyDtsFiles?: boolean, 311 customTransformers?: CustomTransformers 312 ) { 313 const isListFilesOnly = !!program.getCompilerOptions().listFilesOnly; 314 315 // First get and report any syntactic errors. 316 const allDiagnostics = program.getConfigFileParsingDiagnostics().slice(); 317 const configFileParsingDiagnosticsLength = allDiagnostics.length; 318 addRange(allDiagnostics, program.getSyntacticDiagnostics(/*sourceFile*/ undefined, cancellationToken)); 319 320 // If we didn't have any syntactic errors, then also try getting the global and 321 // semantic errors. 322 if (allDiagnostics.length === configFileParsingDiagnosticsLength) { 323 addRange(allDiagnostics, program.getOptionsDiagnostics(cancellationToken)); 324 325 if (!isListFilesOnly) { 326 addRange(allDiagnostics, program.getGlobalDiagnostics(cancellationToken)); 327 328 if (allDiagnostics.length === configFileParsingDiagnosticsLength) { 329 addRange(allDiagnostics, program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken)); 330 } 331 } 332 } 333 334 // Emit and report any errors we ran into. 335 const emitResult = isListFilesOnly 336 ? { emitSkipped: true, diagnostics: emptyArray } 337 : program.emit(/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); 338 const { emittedFiles, diagnostics: emitDiagnostics } = emitResult; 339 addRange(allDiagnostics, emitDiagnostics); 340 341 const diagnostics = sortAndDeduplicateDiagnostics(allDiagnostics); 342 diagnostics.forEach(reportDiagnostic); 343 if (write) { 344 const currentDir = program.getCurrentDirectory(); 345 forEach(emittedFiles, file => { 346 const filepath = getNormalizedAbsolutePath(file, currentDir); 347 write(`TSFILE: ${filepath}`); 348 }); 349 listFiles(program, write); 350 } 351 352 if (reportSummary) { 353 reportSummary(getErrorCountForSummary(diagnostics)); 354 } 355 356 return { 357 emitResult, 358 diagnostics, 359 }; 360 } 361 362 export function emitFilesAndReportErrorsAndGetExitStatus<T extends BuilderProgram>( 363 program: Program | T, 364 reportDiagnostic: DiagnosticReporter, 365 write?: (s: string) => void, 366 reportSummary?: ReportEmitErrorSummary, 367 writeFile?: WriteFileCallback, 368 cancellationToken?: CancellationToken, 369 emitOnlyDtsFiles?: boolean, 370 customTransformers?: CustomTransformers 371 ) { 372 const { emitResult, diagnostics } = emitFilesAndReportErrors( 373 program, 374 reportDiagnostic, 375 write, 376 reportSummary, 377 writeFile, 378 cancellationToken, 379 emitOnlyDtsFiles, 380 customTransformers 381 ); 382 383 if (emitResult.emitSkipped && diagnostics.length > 0) { 384 // If the emitter didn't emit anything, then pass that value along. 385 return ExitStatus.DiagnosticsPresent_OutputsSkipped; 386 } 387 else if (diagnostics.length > 0) { 388 // The emitter emitted something, inform the caller if that happened in the presence 389 // of diagnostics or not. 390 return ExitStatus.DiagnosticsPresent_OutputsGenerated; 391 } 392 return ExitStatus.Success; 393 } 394 395 export const noopFileWatcher: FileWatcher = { close: noop }; 396 export const returnNoopFileWatcher = () => noopFileWatcher; 397 398 export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost { 399 const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system); 400 return { 401 onWatchStatusChange, 402 watchFile: maybeBind(system, system.watchFile) || returnNoopFileWatcher, 403 watchDirectory: maybeBind(system, system.watchDirectory) || returnNoopFileWatcher, 404 setTimeout: maybeBind(system, system.setTimeout) || noop, 405 clearTimeout: maybeBind(system, system.clearTimeout) || noop 406 }; 407 } 408 409 export type WatchType = WatchTypeRegistry[keyof WatchTypeRegistry]; 410 export const WatchType: WatchTypeRegistry = { 411 ConfigFile: "Config file", 412 ExtendedConfigFile: "Extended config file", 413 SourceFile: "Source file", 414 MissingFile: "Missing file", 415 WildcardDirectory: "Wild card directory", 416 FailedLookupLocations: "Failed Lookup Locations", 417 TypeRoots: "Type roots" 418 }; 419 420 export interface WatchTypeRegistry { 421 ConfigFile: "Config file", 422 ExtendedConfigFile: "Extended config file", 423 SourceFile: "Source file", 424 MissingFile: "Missing file", 425 WildcardDirectory: "Wild card directory", 426 FailedLookupLocations: "Failed Lookup Locations", 427 TypeRoots: "Type roots" 428 } 429 430 interface WatchFactory<X, Y = undefined> extends ts.WatchFactory<X, Y> { 431 writeLog: (s: string) => void; 432 } 433 434 export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { 435 const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None; 436 const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop; 437 const result = getWatchFactory<WatchType, Y>(host, watchLogLevel, writeLog) as WatchFactory<WatchType, Y>; 438 result.writeLog = writeLog; 439 return result; 440 } 441 442 export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCompilerOptions: () => CompilerOptions, directoryStructureHost: DirectoryStructureHost = host): CompilerHost { 443 const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); 444 const hostGetNewLine = memoize(() => host.getNewLine()); 445 return { 446 getSourceFile: (fileName, languageVersion, onError) => { 447 const options = getCompilerOptions(); 448 let text: string | undefined; 449 try { 450 performance.mark("beforeIORead"); 451 text = host.readFile(fileName, options.charset); 452 performance.mark("afterIORead"); 453 performance.measure("I/O Read", "beforeIORead", "afterIORead"); 454 } 455 catch (e) { 456 if (onError) { 457 onError(e.message); 458 } 459 text = ""; 460 } 461 462 return text !== undefined ? createSourceFile(fileName, text, languageVersion, /*setParentNodes*/ undefined, /*scriptKind*/ undefined, options) : undefined; 463 }, 464 getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation), 465 getDefaultLibFileName: options => host.getDefaultLibFileName(options), 466 writeFile, 467 getCurrentDirectory: memoize(() => host.getCurrentDirectory()), 468 useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, 469 getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames), 470 getNewLine: () => getNewLineCharacter(getCompilerOptions(), hostGetNewLine), 471 fileExists: f => host.fileExists(f), 472 readFile: f => host.readFile(f), 473 trace: maybeBind(host, host.trace), 474 directoryExists: maybeBind(directoryStructureHost, directoryStructureHost.directoryExists), 475 getDirectories: maybeBind(directoryStructureHost, directoryStructureHost.getDirectories), 476 realpath: maybeBind(host, host.realpath), 477 getEnvironmentVariable: maybeBind(host, host.getEnvironmentVariable) || (() => ""), 478 createHash: maybeBind(host, host.createHash), 479 readDirectory: maybeBind(host, host.readDirectory), 480 }; 481 482 function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { 483 try { 484 performance.mark("beforeIOWrite"); 485 486 // NOTE: If patchWriteFileEnsuringDirectory has been called, 487 // the host.writeFile will do its own directory creation and 488 // the ensureDirectoriesExist call will always be redundant. 489 writeFileEnsuringDirectories( 490 fileName, 491 text, 492 writeByteOrderMark, 493 (path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark), 494 path => host.createDirectory!(path), 495 path => host.directoryExists!(path)); 496 497 performance.mark("afterIOWrite"); 498 performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); 499 } 500 catch (e) { 501 if (onError) { 502 onError(e.message); 503 } 504 } 505 } 506 } 507 508 export function setGetSourceFileAsHashVersioned(compilerHost: CompilerHost, host: { createHash?(data: string): string; }) { 509 const originalGetSourceFile = compilerHost.getSourceFile; 510 const computeHash = maybeBind(host, host.createHash) || generateDjb2Hash; 511 compilerHost.getSourceFile = (...args) => { 512 const result = originalGetSourceFile.call(compilerHost, ...args); 513 if (result) { 514 result.version = computeHash(result.text); 515 } 516 return result; 517 }; 518 } 519 520 /** 521 * Creates the watch compiler host that can be extended with config file or root file names and options host 522 */ 523 export function createProgramHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system: System, createProgram: CreateProgram<T> | undefined): ProgramHost<T> { 524 const getDefaultLibLocation = memoize(() => getDirectoryPath(normalizePath(system.getExecutingFilePath()))); 525 return { 526 useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, 527 getNewLine: () => system.newLine, 528 getCurrentDirectory: memoize(() => system.getCurrentDirectory()), 529 getDefaultLibLocation, 530 getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), 531 fileExists: path => system.fileExists(path), 532 readFile: (path, encoding) => system.readFile(path, encoding), 533 directoryExists: path => system.directoryExists(path), 534 getDirectories: path => system.getDirectories(path), 535 readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth), 536 realpath: maybeBind(system, system.realpath), 537 getEnvironmentVariable: maybeBind(system, system.getEnvironmentVariable), 538 trace: s => system.write(s + system.newLine), 539 createDirectory: path => system.createDirectory(path), 540 writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), 541 createHash: maybeBind(system, system.createHash), 542 createProgram: createProgram || createEmitAndSemanticDiagnosticsBuilderProgram as any as CreateProgram<T> 543 }; 544 } 545 546 /** 547 * Creates the watch compiler host that can be extended with config file or root file names and options host 548 */ 549 function createWatchCompilerHost<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>(system = sys, createProgram: CreateProgram<T> | undefined, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost<T> { 550 const write = (s: string) => system.write(s + system.newLine); 551 const result = createProgramHost(system, createProgram) as WatchCompilerHost<T>; 552 copyProperties(result, createWatchHost(system, reportWatchStatus)); 553 result.afterProgramCreate = builderProgram => { 554 const compilerOptions = builderProgram.getCompilerOptions(); 555 const newLine = getNewLineCharacter(compilerOptions, () => system.newLine); 556 557 emitFilesAndReportErrors( 558 builderProgram, 559 reportDiagnostic, 560 write, 561 errorCount => result.onWatchStatusChange!( 562 createCompilerDiagnostic(getWatchErrorSummaryDiagnosticMessage(errorCount), errorCount), 563 newLine, 564 compilerOptions, 565 errorCount 566 ) 567 ); 568 }; 569 return result; 570 } 571 572 /** 573 * Report error and exit 574 */ 575 function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { 576 reportDiagnostic(diagnostic); 577 system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); 578 } 579 580 export interface CreateWatchCompilerHostInput<T extends BuilderProgram> { 581 system: System; 582 createProgram?: CreateProgram<T>; 583 reportDiagnostic?: DiagnosticReporter; 584 reportWatchStatus?: WatchStatusReporter; 585 } 586 587 export interface CreateWatchCompilerHostOfConfigFileInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> { 588 configFileName: string; 589 optionsToExtend?: CompilerOptions; 590 watchOptionsToExtend?: WatchOptions; 591 extraFileExtensions?: readonly FileExtensionInfo[]; 592 } 593 /** 594 * Creates the watch compiler host from system for config file in watch mode 595 */ 596 export function createWatchCompilerHostOfConfigFile<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({ 597 configFileName, optionsToExtend, watchOptionsToExtend, extraFileExtensions, 598 system, createProgram, reportDiagnostic, reportWatchStatus 599 }: CreateWatchCompilerHostOfConfigFileInput<T>): WatchCompilerHostOfConfigFile<T> { 600 const diagnosticReporter = reportDiagnostic || createDiagnosticReporter(system); 601 const host = createWatchCompilerHost(system, createProgram, diagnosticReporter, reportWatchStatus) as WatchCompilerHostOfConfigFile<T>; 602 host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, diagnosticReporter, diagnostic); 603 host.configFileName = configFileName; 604 host.optionsToExtend = optionsToExtend; 605 host.watchOptionsToExtend = watchOptionsToExtend; 606 host.extraFileExtensions = extraFileExtensions; 607 return host; 608 } 609 610 export interface CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T extends BuilderProgram> extends CreateWatchCompilerHostInput<T> { 611 rootFiles: string[]; 612 options: CompilerOptions; 613 watchOptions: WatchOptions | undefined; 614 projectReferences?: readonly ProjectReference[]; 615 } 616 /** 617 * Creates the watch compiler host from system for compiling root files and options in watch mode 618 */ 619 export function createWatchCompilerHostOfFilesAndCompilerOptions<T extends BuilderProgram = EmitAndSemanticDiagnosticsBuilderProgram>({ 620 rootFiles, options, watchOptions, projectReferences, 621 system, createProgram, reportDiagnostic, reportWatchStatus 622 }: CreateWatchCompilerHostOfFilesAndCompilerOptionsInput<T>): WatchCompilerHostOfFilesAndCompilerOptions<T> { 623 const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions<T>; 624 host.rootFiles = rootFiles; 625 host.options = options; 626 host.watchOptions = watchOptions; 627 host.projectReferences = projectReferences; 628 return host; 629 } 630 631 export interface IncrementalCompilationOptions { 632 rootNames: readonly string[]; 633 options: CompilerOptions; 634 configFileParsingDiagnostics?: readonly Diagnostic[]; 635 projectReferences?: readonly ProjectReference[]; 636 host?: CompilerHost; 637 reportDiagnostic?: DiagnosticReporter; 638 reportErrorSummary?: ReportEmitErrorSummary; 639 afterProgramEmitAndDiagnostics?(program: EmitAndSemanticDiagnosticsBuilderProgram): void; 640 system?: System; 641 } 642 export function performIncrementalCompilation(input: IncrementalCompilationOptions) { 643 const system = input.system || sys; 644 const host = input.host || (input.host = createIncrementalCompilerHost(input.options, system)); 645 const builderProgram = createIncrementalProgram(input); 646 const exitStatus = emitFilesAndReportErrorsAndGetExitStatus( 647 builderProgram, 648 input.reportDiagnostic || createDiagnosticReporter(system), 649 s => host.trace && host.trace(s), 650 input.reportErrorSummary || input.options.pretty ? errorCount => system.write(getErrorSummaryText(errorCount, system.newLine)) : undefined 651 ); 652 if (input.afterProgramEmitAndDiagnostics) input.afterProgramEmitAndDiagnostics(builderProgram); 653 return exitStatus; 654 } 655} 656