• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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