• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string | undefined {
3        return forEachAncestorDirectory(searchPath, ancestor => {
4            const fileName = combinePaths(ancestor, configName);
5            return fileExists(fileName) ? fileName : undefined;
6        });
7    }
8
9    export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
10        const basePath = getDirectoryPath(containingFile);
11        const referencedFileName = isRootedDiskPath(moduleName) ? moduleName : combinePaths(basePath, moduleName);
12        return normalizePath(referencedFileName);
13    }
14
15    /* @internal */
16    export function computeCommonSourceDirectoryOfFilenames(fileNames: readonly string[], currentDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
17        let commonPathComponents: string[] | undefined;
18        const failed = forEach(fileNames, sourceFile => {
19            // Each file contributes into common source file path
20            const sourcePathComponents = getNormalizedPathComponents(sourceFile, currentDirectory);
21            sourcePathComponents.pop(); // The base file name is not part of the common directory path
22
23            if (!commonPathComponents) {
24                // first file
25                commonPathComponents = sourcePathComponents;
26                return;
27            }
28
29            const n = Math.min(commonPathComponents.length, sourcePathComponents.length);
30            for (let i = 0; i < n; i++) {
31                if (getCanonicalFileName(commonPathComponents[i]) !== getCanonicalFileName(sourcePathComponents[i])) {
32                    if (i === 0) {
33                        // Failed to find any common path component
34                        return true;
35                    }
36
37                    // New common path found that is 0 -> i-1
38                    commonPathComponents.length = i;
39                    break;
40                }
41            }
42
43            // If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
44            if (sourcePathComponents.length < commonPathComponents.length) {
45                commonPathComponents.length = sourcePathComponents.length;
46            }
47        });
48
49        // A common path can not be found when paths span multiple drives on windows, for example
50        if (failed) {
51            return "";
52        }
53
54        if (!commonPathComponents) { // Can happen when all input files are .d.ts files
55            return currentDirectory;
56        }
57
58        return getPathFromPathComponents(commonPathComponents);
59    }
60
61    interface OutputFingerprint {
62        hash: string;
63        byteOrderMark: boolean;
64        mtime: Date;
65    }
66
67    export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
68        return createCompilerHostWorker(options, setParentNodes);
69    }
70
71    /*@internal*/
72    // TODO(shkamat): update this after reworking ts build API
73    export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
74        const existingDirectories = new Map<string, boolean>();
75        const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
76        const computeHash = maybeBind(system, system.createHash) || generateDjb2Hash;
77        function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined {
78            let text: string | undefined;
79            try {
80                performance.mark("beforeIORead");
81                text = compilerHost.readFile(fileName);
82                performance.mark("afterIORead");
83                performance.measure("I/O Read", "beforeIORead", "afterIORead");
84            }
85            catch (e) {
86                if (onError) {
87                    onError(e.message);
88                }
89                text = "";
90            }
91            return text !== undefined ? createSourceFile(fileName, text, languageVersion, setParentNodes, /*scriptKind*/ undefined, options) : undefined;
92        }
93
94        function directoryExists(directoryPath: string): boolean {
95            if (existingDirectories.has(directoryPath)) {
96                return true;
97            }
98            if ((compilerHost.directoryExists || system.directoryExists)(directoryPath)) {
99                existingDirectories.set(directoryPath, true);
100                return true;
101            }
102            return false;
103        }
104
105        function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
106            try {
107                performance.mark("beforeIOWrite");
108
109                // NOTE: If patchWriteFileEnsuringDirectory has been called,
110                // the system.writeFile will do its own directory creation and
111                // the ensureDirectoriesExist call will always be redundant.
112                writeFileEnsuringDirectories(
113                    fileName,
114                    data,
115                    writeByteOrderMark,
116                    (path, data, writeByteOrderMark) => writeFileWorker(path, data, writeByteOrderMark),
117                    path => (compilerHost.createDirectory || system.createDirectory)(path),
118                    path => directoryExists(path));
119
120                performance.mark("afterIOWrite");
121                performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
122            }
123            catch (e) {
124                if (onError) {
125                    onError(e.message);
126                }
127            }
128        }
129
130        let outputFingerprints: ESMap<string, OutputFingerprint>;
131        function writeFileWorker(fileName: string, data: string, writeByteOrderMark: boolean) {
132            if (!isWatchSet(options) || !system.getModifiedTime) {
133                system.writeFile(fileName, data, writeByteOrderMark);
134                return;
135            }
136
137            if (!outputFingerprints) {
138                outputFingerprints = new Map<string, OutputFingerprint>();
139            }
140
141            const hash = computeHash(data);
142            const mtimeBefore = system.getModifiedTime(fileName);
143
144            if (mtimeBefore) {
145                const fingerprint = outputFingerprints.get(fileName);
146                // If output has not been changed, and the file has no external modification
147                if (fingerprint &&
148                    fingerprint.byteOrderMark === writeByteOrderMark &&
149                    fingerprint.hash === hash &&
150                    fingerprint.mtime.getTime() === mtimeBefore.getTime()) {
151                    return;
152                }
153            }
154
155            system.writeFile(fileName, data, writeByteOrderMark);
156
157            const mtimeAfter = system.getModifiedTime(fileName) || missingFileModifiedTime;
158
159            outputFingerprints.set(fileName, {
160                hash,
161                byteOrderMark: writeByteOrderMark,
162                mtime: mtimeAfter
163            });
164        }
165
166        function getDefaultLibLocation(): string {
167            return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
168        }
169
170        const newLine = getNewLineCharacter(options, () => system.newLine);
171        const realpath = system.realpath && ((path: string) => system.realpath!(path));
172        const compilerHost: CompilerHost = {
173            getSourceFile,
174            getDefaultLibLocation,
175            getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
176            writeFile,
177            getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
178            useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
179            getCanonicalFileName,
180            getNewLine: () => newLine,
181            fileExists: fileName => system.fileExists(fileName),
182            readFile: fileName => system.readFile(fileName),
183            trace: (s: string) => system.write(s + newLine),
184            directoryExists: directoryName => system.directoryExists(directoryName),
185            getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
186            getDirectories: (path: string) => system.getDirectories(path),
187            realpath,
188            readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth),
189            createDirectory: d => system.createDirectory(d),
190            createHash: maybeBind(system, system.createHash)
191        };
192        return compilerHost;
193    }
194
195    /*@internal*/
196    interface CompilerHostLikeForCache {
197        fileExists(fileName: string): boolean;
198        readFile(fileName: string, encoding?: string): string | undefined;
199        directoryExists?(directory: string): boolean;
200        createDirectory?(directory: string): void;
201        writeFile?: WriteFileCallback;
202    }
203
204    /*@internal*/
205    export function changeCompilerHostLikeToUseCache(
206        host: CompilerHostLikeForCache,
207        toPath: (fileName: string) => Path,
208        getSourceFile?: CompilerHost["getSourceFile"]
209    ) {
210        const originalReadFile = host.readFile;
211        const originalFileExists = host.fileExists;
212        const originalDirectoryExists = host.directoryExists;
213        const originalCreateDirectory = host.createDirectory;
214        const originalWriteFile = host.writeFile;
215        const readFileCache = new Map<string, string | false>();
216        const fileExistsCache = new Map<string, boolean>();
217        const directoryExistsCache = new Map<string, boolean>();
218        const sourceFileCache = new Map<string, SourceFile>();
219
220        const readFileWithCache = (fileName: string): string | undefined => {
221            const key = toPath(fileName);
222            const value = readFileCache.get(key);
223            if (value !== undefined) return value !== false ? value : undefined;
224            return setReadFileCache(key, fileName);
225        };
226        const setReadFileCache = (key: Path, fileName: string) => {
227            const newValue = originalReadFile.call(host, fileName);
228            readFileCache.set(key, newValue !== undefined ? newValue : false);
229            return newValue;
230        };
231        host.readFile = fileName => {
232            const key = toPath(fileName);
233            const value = readFileCache.get(key);
234            if (value !== undefined) return value !== false ? value : undefined; // could be .d.ts from output
235            // Cache json or buildInfo
236            if (!fileExtensionIs(fileName, Extension.Json) && !isBuildInfoFile(fileName)) {
237                return originalReadFile.call(host, fileName);
238            }
239
240            return setReadFileCache(key, fileName);
241        };
242
243        const getSourceFileWithCache: CompilerHost["getSourceFile"] | undefined = getSourceFile ? (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
244            const key = toPath(fileName);
245            const value = sourceFileCache.get(key);
246            if (value) return value;
247
248            const sourceFile = getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
249            if (sourceFile && (isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json))) {
250                sourceFileCache.set(key, sourceFile);
251            }
252            return sourceFile;
253        } : undefined;
254
255        // fileExists for any kind of extension
256        host.fileExists = fileName => {
257            const key = toPath(fileName);
258            const value = fileExistsCache.get(key);
259            if (value !== undefined) return value;
260            const newValue = originalFileExists.call(host, fileName);
261            fileExistsCache.set(key, !!newValue);
262            return newValue;
263        };
264        if (originalWriteFile) {
265            host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
266                const key = toPath(fileName);
267                fileExistsCache.delete(key);
268
269                const value = readFileCache.get(key);
270                if (value !== undefined && value !== data) {
271                    readFileCache.delete(key);
272                    sourceFileCache.delete(key);
273                }
274                else if (getSourceFileWithCache) {
275                    const sourceFile = sourceFileCache.get(key);
276                    if (sourceFile && sourceFile.text !== data) {
277                        sourceFileCache.delete(key);
278                    }
279                }
280                originalWriteFile.call(host, fileName, data, writeByteOrderMark, onError, sourceFiles);
281            };
282        }
283
284        // directoryExists
285        if (originalDirectoryExists && originalCreateDirectory) {
286            host.directoryExists = directory => {
287                const key = toPath(directory);
288                const value = directoryExistsCache.get(key);
289                if (value !== undefined) return value;
290                const newValue = originalDirectoryExists.call(host, directory);
291                directoryExistsCache.set(key, !!newValue);
292                return newValue;
293            };
294            host.createDirectory = directory => {
295                const key = toPath(directory);
296                directoryExistsCache.delete(key);
297                originalCreateDirectory.call(host, directory);
298            };
299        }
300
301        return {
302            originalReadFile,
303            originalFileExists,
304            originalDirectoryExists,
305            originalCreateDirectory,
306            originalWriteFile,
307            getSourceFileWithCache,
308            readFileWithCache
309        };
310    }
311
312    export function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
313    /*@internal*/ export function getPreEmitDiagnostics(program: BuilderProgram, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[]; // eslint-disable-line @typescript-eslint/unified-signatures
314    export function getPreEmitDiagnostics(program: Program | BuilderProgram, sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
315        let diagnostics: Diagnostic[] | undefined;
316        diagnostics = addRange(diagnostics, program.getConfigFileParsingDiagnostics());
317        diagnostics = addRange(diagnostics, program.getOptionsDiagnostics(cancellationToken));
318        diagnostics = addRange(diagnostics, program.getSyntacticDiagnostics(sourceFile, cancellationToken));
319        diagnostics = addRange(diagnostics, program.getGlobalDiagnostics(cancellationToken));
320        diagnostics = addRange(diagnostics, program.getSemanticDiagnostics(sourceFile, cancellationToken));
321
322        if (getEmitDeclarations(program.getCompilerOptions())) {
323            diagnostics = addRange(diagnostics, program.getDeclarationDiagnostics(sourceFile, cancellationToken));
324        }
325
326        return sortAndDeduplicateDiagnostics(diagnostics || emptyArray);
327    }
328
329    export interface FormatDiagnosticsHost {
330        getCurrentDirectory(): string;
331        getCanonicalFileName(fileName: string): string;
332        getNewLine(): string;
333    }
334
335    export function formatDiagnostics(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string {
336        let output = "";
337
338        for (const diagnostic of diagnostics) {
339            output += formatDiagnostic(diagnostic, host);
340        }
341        return output;
342    }
343
344    export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string {
345        const errorMessage = `${diagnosticCategoryName(diagnostic)} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}${host.getNewLine()}`;
346
347        if (diagnostic.file) {
348            const { line, character } = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); // TODO: GH#18217
349            const fileName = diagnostic.file.fileName;
350            const relativeFileName = convertToRelativePath(fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName));
351            return `${relativeFileName}(${line + 1},${character + 1}): ` + errorMessage;
352        }
353
354        return errorMessage;
355    }
356
357    /** @internal */
358    export enum ForegroundColorEscapeSequences {
359        Grey = "\u001b[90m",
360        Red = "\u001b[91m",
361        Yellow = "\u001b[93m",
362        Blue = "\u001b[94m",
363        Cyan = "\u001b[96m"
364    }
365    const gutterStyleSequence = "\u001b[7m";
366    const gutterSeparator = " ";
367    const resetEscapeSequence = "\u001b[0m";
368    const ellipsis = "...";
369    const halfIndent = "  ";
370    const indent = "    ";
371    function getCategoryFormat(category: DiagnosticCategory): ForegroundColorEscapeSequences {
372        switch (category) {
373            case DiagnosticCategory.Error: return ForegroundColorEscapeSequences.Red;
374            case DiagnosticCategory.Warning: return ForegroundColorEscapeSequences.Yellow;
375            case DiagnosticCategory.Suggestion: return Debug.fail("Should never get an Info diagnostic on the command line.");
376            case DiagnosticCategory.Message: return ForegroundColorEscapeSequences.Blue;
377        }
378    }
379
380    /** @internal */
381    export function formatColorAndReset(text: string, formatStyle: string) {
382        return formatStyle + text + resetEscapeSequence;
383    }
384
385    function formatCodeSpan(file: SourceFile, start: number, length: number, indent: string, squiggleColor: ForegroundColorEscapeSequences, host: FormatDiagnosticsHost) {
386        const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start);
387        const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length);
388        const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line;
389
390        const hasMoreThanFiveLines = (lastLine - firstLine) >= 4;
391        let gutterWidth = (lastLine + 1 + "").length;
392        if (hasMoreThanFiveLines) {
393            gutterWidth = Math.max(ellipsis.length, gutterWidth);
394        }
395
396        let context = "";
397        for (let i = firstLine; i <= lastLine; i++) {
398            context += host.getNewLine();
399            // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
400            // so we'll skip ahead to the second-to-last line.
401            if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
402                context += indent + formatColorAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + host.getNewLine();
403                i = lastLine - 1;
404            }
405
406            const lineStart = getPositionOfLineAndCharacter(file, i, 0);
407            const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length;
408            let lineContent = file.text.slice(lineStart, lineEnd);
409            lineContent = lineContent.replace(/\s+$/g, "");  // trim from end
410            lineContent = lineContent.replace(/\t/g, " ");   // convert tabs to single spaces
411
412            // Output the gutter and the actual contents of the line.
413            context += indent + formatColorAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator;
414            context += lineContent + host.getNewLine();
415
416            // Output the gutter and the error span for the line using tildes.
417            context += indent + formatColorAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator;
418            context += squiggleColor;
419            if (i === firstLine) {
420                // If we're on the last line, then limit it to the last character of the last line.
421                // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
422                const lastCharForLine = i === lastLine ? lastLineChar : undefined;
423
424                context += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
425                context += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
426            }
427            else if (i === lastLine) {
428                context += lineContent.slice(0, lastLineChar).replace(/./g, "~");
429            }
430            else {
431                // Squiggle the entire line.
432                context += lineContent.replace(/./g, "~");
433            }
434            context += resetEscapeSequence;
435        }
436        return context;
437    }
438
439    /* @internal */
440    export function formatLocation(file: SourceFile, start: number, host: FormatDiagnosticsHost, color = formatColorAndReset) {
441        const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start); // TODO: GH#18217
442        const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName;
443
444        let output = "";
445        output += color(relativeFileName, ForegroundColorEscapeSequences.Cyan);
446        output += ":";
447        output += color(`${firstLine + 1}`, ForegroundColorEscapeSequences.Yellow);
448        output += ":";
449        output += color(`${firstLineChar + 1}`, ForegroundColorEscapeSequences.Yellow);
450        return output;
451    }
452
453    export function formatDiagnosticsWithColorAndContext(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string {
454        let output = "";
455        for (const diagnostic of diagnostics) {
456            if (diagnostic.file) {
457                const { file, start } = diagnostic;
458                output += formatLocation(file, start!, host); // TODO: GH#18217
459                output += " - ";
460            }
461
462            output += formatColorAndReset(diagnosticCategoryName(diagnostic), getCategoryFormat(diagnostic.category));
463            output += formatColorAndReset(` TS${diagnostic.code}: `, ForegroundColorEscapeSequences.Grey);
464            output += flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine());
465
466            if (diagnostic.file) {
467                output += host.getNewLine();
468                output += formatCodeSpan(diagnostic.file, diagnostic.start!, diagnostic.length!, "", getCategoryFormat(diagnostic.category), host); // TODO: GH#18217
469            }
470            if (diagnostic.relatedInformation) {
471                output += host.getNewLine();
472                for (const { file, start, length, messageText } of diagnostic.relatedInformation) {
473                    if (file) {
474                        output += host.getNewLine();
475                        output += halfIndent + formatLocation(file, start!, host); // TODO: GH#18217
476                        output += formatCodeSpan(file, start!, length!, indent, ForegroundColorEscapeSequences.Cyan, host); // TODO: GH#18217
477                    }
478                    output += host.getNewLine();
479                    output += indent + flattenDiagnosticMessageText(messageText, host.getNewLine());
480                }
481            }
482            output += host.getNewLine();
483        }
484        return output;
485    }
486
487    export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent = 0): string {
488        if (isString(diag)) {
489            return diag;
490        }
491        else if (diag === undefined) {
492            return "";
493        }
494        let result = "";
495        if (indent) {
496            result += newLine;
497
498            for (let i = 0; i < indent; i++) {
499                result += "  ";
500            }
501        }
502        result += diag.messageText;
503        indent++;
504        if (diag.next) {
505            for (const kid of diag.next) {
506                result += flattenDiagnosticMessageText(kid, newLine, indent);
507            }
508        }
509        return result;
510    }
511
512    /* @internal */
513    export function loadWithLocalCache<T>(names: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
514        if (names.length === 0) {
515            return [];
516        }
517        const resolutions: T[] = [];
518        const cache = new Map<string, T>();
519        for (const name of names) {
520            let result: T;
521            if (cache.has(name)) {
522                result = cache.get(name)!;
523            }
524            else {
525                cache.set(name, result = loader(name, containingFile, redirectedReference));
526            }
527            resolutions.push(result);
528        }
529        return resolutions;
530    }
531
532    /* @internal */
533    export function forEachResolvedProjectReference<T>(
534        resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
535        cb: (resolvedProjectReference: ResolvedProjectReference, parent: ResolvedProjectReference | undefined) => T | undefined
536    ): T | undefined {
537        return forEachProjectReference(/*projectReferences*/ undefined, resolvedProjectReferences, (resolvedRef, parent) => resolvedRef && cb(resolvedRef, parent));
538    }
539
540    function forEachProjectReference<T>(
541        projectReferences: readonly ProjectReference[] | undefined,
542        resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
543        cbResolvedRef: (resolvedRef: ResolvedProjectReference | undefined, parent: ResolvedProjectReference | undefined, index: number) => T | undefined,
544        cbRef?: (projectReferences: readonly ProjectReference[] | undefined, parent: ResolvedProjectReference | undefined) => T | undefined
545    ): T | undefined {
546        let seenResolvedRefs: Set<Path> | undefined;
547
548        return worker(projectReferences, resolvedProjectReferences, /*parent*/ undefined);
549
550        function worker(
551            projectReferences: readonly ProjectReference[] | undefined,
552            resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined,
553            parent: ResolvedProjectReference | undefined,
554        ): T | undefined {
555
556            // Visit project references first
557            if (cbRef) {
558                const result = cbRef(projectReferences, parent);
559                if (result) { return result; }
560            }
561
562            return forEach(resolvedProjectReferences, (resolvedRef, index) => {
563                if (resolvedRef && seenResolvedRefs?.has(resolvedRef.sourceFile.path)) {
564                    // ignore recursives
565                    return undefined;
566                }
567
568                const result = cbResolvedRef(resolvedRef, parent, index);
569                if (result || !resolvedRef) return result;
570
571                (seenResolvedRefs ||= new Set()).add(resolvedRef.sourceFile.path);
572                return worker(resolvedRef.commandLine.projectReferences, resolvedRef.references, resolvedRef);
573            });
574        }
575    }
576
577    /* @internal */
578    export const inferredTypesContainingFile = "__inferred type names__.ts";
579
580    interface DiagnosticCache<T extends Diagnostic> {
581        perFile?: ESMap<Path, readonly T[]>;
582        allDiagnostics?: readonly T[];
583    }
584
585    /*@internal*/
586    export function isReferencedFile(reason: FileIncludeReason | undefined): reason is ReferencedFile {
587        switch (reason?.kind) {
588            case FileIncludeKind.Import:
589            case FileIncludeKind.ReferenceFile:
590            case FileIncludeKind.TypeReferenceDirective:
591            case FileIncludeKind.LibReferenceDirective:
592                return true;
593            default:
594                return false;
595        }
596    }
597
598    /*@internal*/
599    export interface ReferenceFileLocation {
600        file: SourceFile;
601        pos: number;
602        end: number;
603        packageId: PackageId | undefined;
604    }
605
606    /*@internal*/
607    export interface SyntheticReferenceFileLocation {
608        file: SourceFile;
609        packageId: PackageId | undefined;
610        text: string;
611    }
612
613    /*@internal*/
614    export function isReferenceFileLocation(location: ReferenceFileLocation | SyntheticReferenceFileLocation): location is ReferenceFileLocation {
615        return (location as ReferenceFileLocation).pos !== undefined;
616    }
617
618    /*@internal*/
619    export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => SourceFile | undefined, ref: ReferencedFile): ReferenceFileLocation | SyntheticReferenceFileLocation {
620        const file = Debug.checkDefined(getSourceFileByPath(ref.file));
621        const { kind, index } = ref;
622        let pos: number | undefined, end: number | undefined, packageId: PackageId | undefined;
623        switch (kind) {
624            case FileIncludeKind.Import:
625                const importLiteral = getModuleNameStringLiteralAt(file, index);
626                packageId = file.resolvedModules?.get(importLiteral.text)?.packageId;
627                if (importLiteral.pos === -1) return { file, packageId, text: importLiteral.text };
628                pos = skipTrivia(file.text, importLiteral.pos);
629                end = importLiteral.end;
630                break;
631            case FileIncludeKind.ReferenceFile:
632                ({ pos, end } = file.referencedFiles[index]);
633                break;
634            case FileIncludeKind.TypeReferenceDirective:
635                ({ pos, end } = file.typeReferenceDirectives[index]);
636                packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName))?.packageId;
637                break;
638            case FileIncludeKind.LibReferenceDirective:
639                ({ pos, end } = file.libReferenceDirectives[index]);
640                break;
641            default:
642                return Debug.assertNever(kind);
643        }
644        return { file, pos, end, packageId };
645    }
646
647    /**
648     * Determines if program structure is upto date or needs to be recreated
649     */
650    /* @internal */
651    export function isProgramUptoDate(
652        program: Program | undefined,
653        rootFileNames: string[],
654        newOptions: CompilerOptions,
655        getSourceVersion: (path: Path, fileName: string) => string | undefined,
656        fileExists: (fileName: string) => boolean,
657        hasInvalidatedResolution: HasInvalidatedResolution,
658        hasChangedAutomaticTypeDirectiveNames: HasChangedAutomaticTypeDirectiveNames | undefined,
659        projectReferences: readonly ProjectReference[] | undefined
660    ): boolean {
661        // If we haven't created a program yet or have changed automatic type directives, then it is not up-to-date
662        if (!program || hasChangedAutomaticTypeDirectiveNames?.()) {
663            return false;
664        }
665
666        // If root file names don't match
667        if (!arrayIsEqualTo(program.getRootFileNames(), rootFileNames)) {
668            return false;
669        }
670
671        let seenResolvedRefs: ResolvedProjectReference[] | undefined;
672
673        // If project references don't match
674        if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences, projectReferenceUptoDate)) {
675            return false;
676        }
677
678        // If any file is not up-to-date, then the whole program is not up-to-date
679        if (program.getSourceFiles().some(sourceFileNotUptoDate)) {
680            return false;
681        }
682
683        // If any of the missing file paths are now created
684        if (program.getMissingFilePaths().some(fileExists)) {
685            return false;
686        }
687
688        const currentOptions = program.getCompilerOptions();
689        // If the compilation settings do no match, then the program is not up-to-date
690        if (!compareDataObjects(currentOptions, newOptions)) {
691            return false;
692        }
693
694        // If everything matches but the text of config file is changed,
695        // error locations can change for program options, so update the program
696        if (currentOptions.configFile && newOptions.configFile) {
697            return currentOptions.configFile.text === newOptions.configFile.text;
698        }
699
700        return true;
701
702        function sourceFileNotUptoDate(sourceFile: SourceFile) {
703            return !sourceFileVersionUptoDate(sourceFile) ||
704                hasInvalidatedResolution(sourceFile.path);
705        }
706
707        function sourceFileVersionUptoDate(sourceFile: SourceFile) {
708            return sourceFile.version === getSourceVersion(sourceFile.resolvedPath, sourceFile.fileName);
709        }
710
711        function projectReferenceUptoDate(oldRef: ProjectReference, newRef: ProjectReference, index: number) {
712            if (!projectReferenceIsEqualTo(oldRef, newRef)) {
713                return false;
714            }
715            return resolvedProjectReferenceUptoDate(program!.getResolvedProjectReferences()![index], oldRef);
716        }
717
718        function resolvedProjectReferenceUptoDate(oldResolvedRef: ResolvedProjectReference | undefined, oldRef: ProjectReference): boolean {
719            if (oldResolvedRef) {
720                if (contains(seenResolvedRefs, oldResolvedRef)) {
721                    // Assume true
722                    return true;
723                }
724
725                // If sourceFile for the oldResolvedRef existed, check the version for uptodate
726                if (!sourceFileVersionUptoDate(oldResolvedRef.sourceFile)) {
727                    return false;
728                }
729
730                // Add to seen before checking the referenced paths of this config file
731                (seenResolvedRefs || (seenResolvedRefs = [])).push(oldResolvedRef);
732
733                // If child project references are upto date, this project reference is uptodate
734                return !forEach(oldResolvedRef.references, (childResolvedRef, index) =>
735                    !resolvedProjectReferenceUptoDate(childResolvedRef, oldResolvedRef.commandLine.projectReferences![index]));
736            }
737
738            // In old program, not able to resolve project reference path,
739            // so if config file doesnt exist, it is uptodate.
740            return !fileExists(resolveProjectReferencePath(oldRef));
741        }
742    }
743
744    export function getConfigFileParsingDiagnostics(configFileParseResult: ParsedCommandLine): readonly Diagnostic[] {
745        return configFileParseResult.options.configFile ?
746            [...configFileParseResult.options.configFile.parseDiagnostics, ...configFileParseResult.errors] :
747            configFileParseResult.errors;
748    }
749
750    /**
751     * Determine if source file needs to be re-created even if its text hasn't changed
752     */
753    function shouldProgramCreateNewSourceFiles(program: Program | undefined, newOptions: CompilerOptions): boolean {
754        if (!program) return false;
755        // If any compiler options change, we can't reuse old source file even if version match
756        // The change in options like these could result in change in syntax tree or `sourceFile.bindDiagnostics`.
757        const oldOptions = program.getCompilerOptions();
758        return !!sourceFileAffectingCompilerOptions.some(option =>
759            !isJsonEqual(getCompilerOptionValue(oldOptions, option), getCompilerOptionValue(newOptions, option)));
760    }
761
762    function createCreateProgramOptions(rootNames: readonly string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: readonly Diagnostic[]): CreateProgramOptions {
763        return {
764            rootNames,
765            options,
766            host,
767            oldProgram,
768            configFileParsingDiagnostics
769        };
770    }
771
772    /**
773     * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
774     * that represent a compilation unit.
775     *
776     * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and
777     * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in.
778     *
779     * @param createProgramOptions - The options for creating a program.
780     * @returns A 'Program' object.
781     */
782    export function createProgram(createProgramOptions: CreateProgramOptions): Program;
783    /**
784     * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions'
785     * that represent a compilation unit.
786     *
787     * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and
788     * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in.
789     *
790     * @param rootNames - A set of root files.
791     * @param options - The compiler options which should be used.
792     * @param host - The host interacts with the underlying file system.
793     * @param oldProgram - Reuses an old program structure.
794     * @param configFileParsingDiagnostics - error during config file parsing
795     * @returns A 'Program' object.
796     */
797    export function createProgram(rootNames: readonly string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program, configFileParsingDiagnostics?: readonly Diagnostic[]): Program;
798    export function createProgram(rootNamesOrOptions: readonly string[] | CreateProgramOptions, _options?: CompilerOptions, _host?: CompilerHost, _oldProgram?: Program, _configFileParsingDiagnostics?: readonly Diagnostic[]): Program {
799        const createProgramOptions = isArray(rootNamesOrOptions) ? createCreateProgramOptions(rootNamesOrOptions, _options!, _host, _oldProgram, _configFileParsingDiagnostics) : rootNamesOrOptions; // TODO: GH#18217
800        const { rootNames, options, configFileParsingDiagnostics, projectReferences } = createProgramOptions;
801        let { oldProgram } = createProgramOptions;
802
803        let processingDefaultLibFiles: SourceFile[] | undefined;
804        let processingOtherFiles: SourceFile[] | undefined;
805        let files: SourceFile[];
806        let symlinks: SymlinkCache | undefined;
807        let commonSourceDirectory: string;
808        let diagnosticsProducingTypeChecker: TypeChecker;
809        let noDiagnosticsTypeChecker: TypeChecker;
810        let classifiableNames: Set<__String>;
811        const ambientModuleNameToUnmodifiedFileName = new Map<string, string>();
812        let fileReasons = createMultiMap<Path, FileIncludeReason>();
813        const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
814        const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
815
816        let resolvedTypeReferenceDirectives = new Map<string, ResolvedTypeReferenceDirective | undefined>();
817        let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined;
818
819        // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
820        // This works as imported modules are discovered recursively in a depth first manner, specifically:
821        // - For each root file, findSourceFile is called.
822        // - This calls processImportedModules for each module imported in the source file.
823        // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
824        // As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
825        // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
826        const maxNodeModuleJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0;
827        let currentNodeModulesDepth = 0;
828
829        // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
830        // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
831        const modulesWithElidedImports = new Map<string, boolean>();
832
833        // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled.
834        const sourceFilesFoundSearchingNodeModules = new Map<string, boolean>();
835
836        tracing?.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true);
837        performance.mark("beforeProgram");
838
839        const host = createProgramOptions.host || createCompilerHost(options);
840        const configParsingHost = parseConfigHostFromCompilerHostLike(host);
841
842        let skipDefaultLib = options.noLib;
843        const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options));
844        const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName());
845        const programDiagnostics = createDiagnosticCollection();
846        const currentDirectory = host.getCurrentDirectory();
847        const supportedExtensions = getSupportedExtensions(options);
848        const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
849
850        // Map storing if there is emit blocking diagnostics for given input
851        const hasEmitBlockingDiagnostics = new Map<string, boolean>();
852        let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined;
853
854        let moduleResolutionCache: ModuleResolutionCache | undefined;
855        let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
856        const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
857        if (host.resolveModuleNames) {
858            actualResolveModuleNamesWorker = (moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFile, reusedNames, redirectedReference, options).map(resolved => {
859                // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
860                if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
861                    return resolved as ResolvedModuleFull;
862                }
863                const withExtension = clone(resolved) as ResolvedModuleFull;
864                withExtension.extension = extensionFromPath(resolved.resolvedFileName);
865                return withExtension;
866            });
867        }
868        else {
869            moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x), options);
870            const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
871            actualResolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
872        }
873
874        let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference) => (ResolvedTypeReferenceDirective | undefined)[];
875        if (host.resolveTypeReferenceDirectives) {
876            actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options);
877        }
878        else {
879            const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(typesRef, containingFile, options, host, redirectedReference).resolvedTypeReferenceDirective!; // TODO: GH#18217
880            actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference) => loadWithLocalCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
881        }
882
883        // Map from a stringified PackageId to the source file with that id.
884        // Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
885        // `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
886        const packageIdToSourceFile = new Map<string, SourceFile>();
887        // Maps from a SourceFile's `.path` to the name of the package it was imported with.
888        let sourceFileToPackageName = new Map<string, string>();
889        // Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it.
890        let redirectTargetsMap = createMultiMap<string>();
891
892        /**
893         * map with
894         * - SourceFile if present
895         * - false if sourceFile missing for source of project reference redirect
896         * - undefined otherwise
897         */
898        const filesByName = new Map<string, SourceFile | false | undefined>();
899        let missingFilePaths: readonly Path[] | undefined;
900        // stores 'filename -> file association' ignoring case
901        // used to track cases when two file names differ only in casing
902        const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? new Map<string, SourceFile>() : undefined;
903
904        // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files
905        let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined;
906        let projectReferenceRedirects: ESMap<Path, ResolvedProjectReference | false> | undefined;
907        let mapFromFileToProjectReferenceRedirects: ESMap<Path, Path> | undefined;
908        let mapFromToProjectReferenceRedirectSource: ESMap<Path, SourceOfProjectReferenceRedirect> | undefined;
909
910        const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
911            !options.disableSourceOfProjectReferenceRedirect;
912        const { onProgramCreateComplete, fileExists, directoryExists } = updateHostForUseSourceOfProjectReferenceRedirect({
913            compilerHost: host,
914            getSymlinkCache,
915            useSourceOfProjectReferenceRedirect,
916            toPath,
917            getResolvedProjectReferences,
918            getSourceOfProjectReferenceRedirect,
919            forEachResolvedProjectReference
920        });
921
922        tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
923        const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
924        tracing?.pop();
925        // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
926        // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
927        let structureIsReused: StructureIsReused;
928        tracing?.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {});
929        structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
930        tracing?.pop();
931        if (structureIsReused !== StructureIsReused.Completely) {
932            processingDefaultLibFiles = [];
933            processingOtherFiles = [];
934
935            if (projectReferences) {
936                if (!resolvedProjectReferences) {
937                    resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
938                }
939                if (rootNames.length) {
940                    resolvedProjectReferences?.forEach((parsedRef, index) => {
941                        if (!parsedRef) return;
942                        const out = outFile(parsedRef.commandLine.options);
943                        if (useSourceOfProjectReferenceRedirect) {
944                            if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
945                                for (const fileName of parsedRef.commandLine.fileNames) {
946                                    processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index });
947                                }
948                            }
949                        }
950                        else {
951                            if (out) {
952                                processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index });
953                            }
954                            else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
955                                const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames()));
956                                for (const fileName of parsedRef.commandLine.fileNames) {
957                                    if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
958                                        processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index });
959                                    }
960                                }
961                            }
962                        }
963                    });
964                }
965            }
966
967            tracing?.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length });
968            forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index }));
969            tracing?.pop();
970
971            // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
972            const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray;
973
974            if (typeReferences.length) {
975                tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length });
976                // This containingFilename needs to match with the one used in managed-side
977                const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
978                const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
979                const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
980                for (let i = 0; i < typeReferences.length; i++) {
981                    processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
982                }
983                tracing?.pop();
984            }
985
986            // Do not process the default library if:
987            //  - The '--noLib' flag is used.
988            //  - A 'no-default-lib' reference comment is encountered in
989            //      processing the root files.
990            if (rootNames.length && !skipDefaultLib) {
991                // If '--lib' is not specified, include default library file according to '--target'
992                // otherwise, using options specified in '--lib' instead of '--target' default library file
993                const defaultLibraryFileName = getDefaultLibraryFileName();
994                if (!options.lib && defaultLibraryFileName) {
995                    processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile });
996                }
997                else {
998                    forEach(options.lib, (libFileName, index) => {
999                        processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index });
1000                    });
1001
1002                    const etsComponentsLib = options.ets?.libs ?? [];
1003                    if (etsComponentsLib.length) {
1004                        forEach(etsComponentsLib, libFileName => {
1005                            processRootFile(combinePaths(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile });
1006                        });
1007                    }
1008                }
1009            }
1010
1011            missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined));
1012            files = stableSort(processingDefaultLibFiles, compareDefaultLibFiles).concat(processingOtherFiles);
1013            processingDefaultLibFiles = undefined;
1014            processingOtherFiles = undefined;
1015        }
1016
1017        Debug.assert(!!missingFilePaths);
1018
1019        // Release any files we have acquired in the old program but are
1020        // not part of the new program.
1021        if (oldProgram && host.onReleaseOldSourceFile) {
1022            const oldSourceFiles = oldProgram.getSourceFiles();
1023            for (const oldSourceFile of oldSourceFiles) {
1024                const newFile = getSourceFileByPath(oldSourceFile.resolvedPath);
1025                if (shouldCreateNewSourceFile || !newFile ||
1026                    // old file wasn't redirect but new file is
1027                    (oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) {
1028                    host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
1029                }
1030            }
1031            oldProgram.forEachResolvedProjectReference(resolvedProjectReference => {
1032                if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) {
1033                    host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false);
1034                }
1035            });
1036        }
1037
1038        // unconditionally set oldProgram to undefined to prevent it from being captured in closure
1039        oldProgram = undefined;
1040
1041        const program: Program = {
1042            getRootFileNames: () => rootNames,
1043            getSourceFile,
1044            getSourceFileByPath,
1045            getSourceFiles: () => files,
1046            getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
1047            getFilesByNameMap: () => filesByName,
1048            getCompilerOptions: () => options,
1049            getSyntacticDiagnostics,
1050            getOptionsDiagnostics,
1051            getGlobalDiagnostics,
1052            getSemanticDiagnostics,
1053            getCachedSemanticDiagnostics,
1054            getSuggestionDiagnostics,
1055            getDeclarationDiagnostics,
1056            getBindAndCheckDiagnostics,
1057            getProgramDiagnostics,
1058            getTypeChecker,
1059            getEtsLibSFromProgram,
1060            getClassifiableNames,
1061            getDiagnosticsProducingTypeChecker,
1062            getCommonSourceDirectory,
1063            emit,
1064            getCurrentDirectory: () => currentDirectory,
1065            getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
1066            getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
1067            getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
1068            getTypeCatalog: () => getDiagnosticsProducingTypeChecker().getTypeCatalog(),
1069            getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
1070            getInstantiationCount: () => getDiagnosticsProducingTypeChecker().getInstantiationCount(),
1071            getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(),
1072            getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
1073            getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
1074            isSourceFileFromExternalLibrary,
1075            isSourceFileDefaultLibrary,
1076            dropDiagnosticsProducingTypeChecker,
1077            getSourceFileFromReference,
1078            getLibFileFromReference,
1079            sourceFileToPackageName,
1080            redirectTargetsMap,
1081            isEmittedFile,
1082            getConfigFileParsingDiagnostics,
1083            getResolvedModuleWithFailedLookupLocationsFromCache,
1084            getProjectReferences,
1085            getResolvedProjectReferences,
1086            getProjectReferenceRedirect,
1087            getResolvedProjectReferenceToRedirect,
1088            getResolvedProjectReferenceByPath,
1089            forEachResolvedProjectReference,
1090            isSourceOfProjectReferenceRedirect,
1091            emitBuildInfo,
1092            fileExists,
1093            directoryExists,
1094            getSymlinkCache,
1095            realpath: host.realpath?.bind(host),
1096            useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
1097            getFileIncludeReasons: () => fileReasons,
1098            structureIsReused,
1099            getTagNameNeededCheckByFile: host.getTagNameNeededCheckByFile,
1100            getExpressionCheckedResultsByFile: host.getExpressionCheckedResultsByFile
1101        };
1102
1103        onProgramCreateComplete();
1104
1105        // Add file processingDiagnostics
1106        fileProcessingDiagnostics?.forEach(diagnostic => {
1107            switch (diagnostic.kind) {
1108                case FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic:
1109                    return programDiagnostics.add(createDiagnosticExplainingFile(diagnostic.file && getSourceFileByPath(diagnostic.file), diagnostic.fileProcessingReason, diagnostic.diagnostic, diagnostic.args || emptyArray));
1110                case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic:
1111                    const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation;
1112                    return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray));
1113                default:
1114                    Debug.assertNever(diagnostic);
1115            }
1116        });
1117
1118        verifyCompilerOptions();
1119        performance.mark("afterProgram");
1120        performance.measure("Program", "beforeProgram", "afterProgram");
1121        tracing?.pop();
1122
1123        return program;
1124
1125        function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] {
1126            if (!moduleNames.length) return emptyArray;
1127            const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
1128            const redirectedReference = getRedirectReferenceForResolution(containingFile);
1129            tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
1130            performance.mark("beforeResolveModule");
1131            const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference);
1132            performance.mark("afterResolveModule");
1133            performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
1134            tracing?.pop();
1135            return result;
1136        }
1137
1138        function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
1139            if (!typeDirectiveNames.length) return [];
1140            const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile;
1141            const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined;
1142            tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName });
1143            performance.mark("beforeResolveTypeReference");
1144            const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference);
1145            performance.mark("afterResolveTypeReference");
1146            performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference");
1147            tracing?.pop();
1148            return result;
1149        }
1150
1151        function getRedirectReferenceForResolution(file: SourceFile) {
1152            const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
1153            if (redirect || !fileExtensionIs(file.originalFileName, Extension.Dts)) return redirect;
1154
1155            // The originalFileName could not be actual source file name if file found was d.ts from referecned project
1156            // So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
1157            const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path);
1158            if (resultFromDts) return resultFromDts;
1159
1160            // If preserveSymlinks is true, module resolution wont jump the symlink
1161            // but the resolved real path may be the .d.ts from project reference
1162            // Note:: Currently we try the real path only if the
1163            // file is from node_modules to avoid having to run real path on all file paths
1164            if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined;
1165            const realDeclarationFileName = host.realpath(file.originalFileName);
1166            const realDeclarationPath = toPath(realDeclarationFileName);
1167            return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationFileName, realDeclarationPath);
1168        }
1169
1170        function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) {
1171            const source = getSourceOfProjectReferenceRedirect(fileName);
1172            if (isString(source)) return getResolvedProjectReferenceToRedirect(source);
1173            if (!source) return undefined;
1174            // Output of .d.ts file so return resolved ref that matches the out file name
1175            return forEachResolvedProjectReference(resolvedRef => {
1176                const out = outFile(resolvedRef.commandLine.options);
1177                if (!out) return undefined;
1178                return toPath(out) === filePath ? resolvedRef : undefined;
1179            });
1180        }
1181
1182        function compareDefaultLibFiles(a: SourceFile, b: SourceFile) {
1183            return compareValues(getDefaultLibFilePriority(a), getDefaultLibFilePriority(b));
1184        }
1185
1186        function getDefaultLibFilePriority(a: SourceFile) {
1187            if (containsPath(defaultLibraryPath, a.fileName, /*ignoreCase*/ false)) {
1188                const basename = getBaseFileName(a.fileName);
1189                if (basename === "lib.d.ts" || basename === "lib.es6.d.ts") return 0;
1190                const name = removeSuffix(removePrefix(basename, "lib."), ".d.ts");
1191                const index = libs.indexOf(name);
1192                if (index !== -1) return index + 1;
1193            }
1194            return libs.length + 2;
1195        }
1196
1197        function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
1198            return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache);
1199        }
1200
1201        function toPath(fileName: string): Path {
1202            return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
1203        }
1204
1205        function getCommonSourceDirectory() {
1206            if (commonSourceDirectory === undefined) {
1207                const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program));
1208                commonSourceDirectory = ts.getCommonSourceDirectory(
1209                    options,
1210                    () => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName),
1211                    currentDirectory,
1212                    getCanonicalFileName,
1213                    commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory)
1214                );
1215            }
1216            return commonSourceDirectory;
1217        }
1218
1219        function getClassifiableNames() {
1220            if (!classifiableNames) {
1221                // Initialize a checker so that all our files are bound.
1222                getTypeChecker();
1223                classifiableNames = new Set();
1224
1225                for (const sourceFile of files) {
1226                    sourceFile.classifiableNames?.forEach(value => classifiableNames.add(value));
1227                }
1228            }
1229
1230            return classifiableNames;
1231        }
1232
1233        function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] {
1234            if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
1235                // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
1236                // the best we can do is fallback to the default logic.
1237                return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
1238            }
1239
1240            const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
1241            if (oldSourceFile !== file && file.resolvedModules) {
1242                // `file` was created for the new program.
1243                //
1244                // We only set `file.resolvedModules` via work from the current function,
1245                // so it is defined iff we already called the current function on `file`.
1246                // That call happened no later than the creation of the `file` object,
1247                // which per above occurred during the current program creation.
1248                // Since we assume the filesystem does not change during program creation,
1249                // it is safe to reuse resolutions from the earlier call.
1250                const result: ResolvedModuleFull[] = [];
1251                for (const moduleName of moduleNames) {
1252                    const resolvedModule = file.resolvedModules.get(moduleName)!;
1253                    result.push(resolvedModule);
1254                }
1255                return result;
1256            }
1257            // At this point, we know at least one of the following hold:
1258            // - file has local declarations for ambient modules
1259            // - old program state is available
1260            // With this information, we can infer some module resolutions without performing resolution.
1261
1262            /** An ordered list of module names for which we cannot recover the resolution. */
1263            let unknownModuleNames: string[] | undefined;
1264            /**
1265             * The indexing of elements in this list matches that of `moduleNames`.
1266             *
1267             * Before combining results, result[i] is in one of the following states:
1268             * * undefined: needs to be recomputed,
1269             * * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
1270             * Needs to be reset to undefined before returning,
1271             * * ResolvedModuleFull instance: can be reused.
1272             */
1273            let result: ResolvedModuleFull[] | undefined;
1274            let reusedNames: string[] | undefined;
1275            /** A transient placeholder used to mark predicted resolution in the result list. */
1276            const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
1277
1278            for (let i = 0; i < moduleNames.length; i++) {
1279                const moduleName = moduleNames[i];
1280                // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
1281                if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
1282                    const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName);
1283                    if (oldResolvedModule) {
1284                        if (isTraceEnabled(options, host)) {
1285                            trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
1286                        }
1287                        (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
1288                        (reusedNames || (reusedNames = [])).push(moduleName);
1289                        continue;
1290                    }
1291                }
1292                // We know moduleName resolves to an ambient module provided that moduleName:
1293                // - is in the list of ambient modules locally declared in the current source file.
1294                // - resolved to an ambient module in the old program whose declaration is in an unmodified file
1295                //   (so the same module declaration will land in the new program)
1296                let resolvesToAmbientModuleInNonModifiedFile = false;
1297                if (contains(file.ambientModuleNames, moduleName)) {
1298                    resolvesToAmbientModuleInNonModifiedFile = true;
1299                    if (isTraceEnabled(options, host)) {
1300                        trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
1301                    }
1302                }
1303                else {
1304                    resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
1305                }
1306
1307                if (resolvesToAmbientModuleInNonModifiedFile) {
1308                    (result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
1309                }
1310                else {
1311                    // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
1312                    (unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
1313                }
1314            }
1315
1316            const resolutions = unknownModuleNames && unknownModuleNames.length
1317                ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames)
1318                : emptyArray;
1319
1320            // Combine results of resolutions and predicted results
1321            if (!result) {
1322                // There were no unresolved/ambient resolutions.
1323                Debug.assert(resolutions.length === moduleNames.length);
1324                return resolutions;
1325            }
1326
1327            let j = 0;
1328            for (let i = 0; i < result.length; i++) {
1329                if (result[i]) {
1330                    // `result[i]` is either a `ResolvedModuleFull` or a marker.
1331                    // If it is the former, we can leave it as is.
1332                    if (result[i] === predictedToResolveToAmbientModuleMarker) {
1333                        result[i] = undefined!; // TODO: GH#18217
1334                    }
1335                }
1336                else {
1337                    result[i] = resolutions[j];
1338                    j++;
1339                }
1340            }
1341            Debug.assert(j === resolutions.length);
1342
1343            return result;
1344
1345            // If we change our policy of rechecking failed lookups on each program create,
1346            // we should adjust the value returned here.
1347            function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
1348                const resolutionToFile = getResolvedModule(oldSourceFile, moduleName);
1349                const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
1350                if (resolutionToFile && resolvedFile) {
1351                    // In the old program, we resolved to an ambient module that was in the same
1352                    //   place as we expected to find an actual module file.
1353                    // We actually need to return 'false' here even though this seems like a 'true' case
1354                    //   because the normal module resolution algorithm will find this anyway.
1355                    return false;
1356                }
1357
1358                // at least one of declarations should come from non-modified source file
1359                const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
1360
1361                if (!unmodifiedFile) {
1362                    return false;
1363                }
1364
1365                if (isTraceEnabled(options, host)) {
1366                    trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
1367                }
1368                return true;
1369            }
1370        }
1371
1372        function canReuseProjectReferences(): boolean {
1373            return !forEachProjectReference(
1374                oldProgram!.getProjectReferences(),
1375                oldProgram!.getResolvedProjectReferences(),
1376                (oldResolvedRef, parent, index) => {
1377                    const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
1378                    const newResolvedRef = parseProjectReferenceConfigFile(newRef);
1379                    if (oldResolvedRef) {
1380                        // Resolved project reference has gone missing or changed
1381                        return !newResolvedRef || newResolvedRef.sourceFile !== oldResolvedRef.sourceFile;
1382                    }
1383                    else {
1384                        // A previously-unresolved reference may be resolved now
1385                        return newResolvedRef !== undefined;
1386                    }
1387                },
1388                (oldProjectReferences, parent) => {
1389                    // If array of references is changed, we cant resue old program
1390                    const newReferences = parent ? getResolvedProjectReferenceByPath(parent.sourceFile.path)!.commandLine.projectReferences : projectReferences;
1391                    return !arrayIsEqualTo(oldProjectReferences, newReferences, projectReferenceIsEqualTo);
1392                }
1393            );
1394        }
1395
1396        function tryReuseStructureFromOldProgram(): StructureIsReused {
1397            if (!oldProgram) {
1398                return StructureIsReused.Not;
1399            }
1400
1401            // check properties that can affect structure of the program or module resolution strategy
1402            // if any of these properties has changed - structure cannot be reused
1403            const oldOptions = oldProgram.getCompilerOptions();
1404            if (changesAffectModuleResolution(oldOptions, options)) {
1405                return StructureIsReused.Not;
1406            }
1407
1408            // there is an old program, check if we can reuse its structure
1409            const oldRootNames = oldProgram.getRootFileNames();
1410            if (!arrayIsEqualTo(oldRootNames, rootNames)) {
1411                return StructureIsReused.Not;
1412            }
1413
1414            if (!arrayIsEqualTo(options.types, oldOptions.types)) {
1415                return StructureIsReused.Not;
1416            }
1417
1418            // Check if any referenced project tsconfig files are different
1419            if (!canReuseProjectReferences()) {
1420                return StructureIsReused.Not;
1421            }
1422            if (projectReferences) {
1423                resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
1424            }
1425
1426            // check if program source files has changed in the way that can affect structure of the program
1427            const newSourceFiles: SourceFile[] = [];
1428            const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
1429            structureIsReused = StructureIsReused.Completely;
1430
1431            // If the missing file paths are now present, it can change the progam structure,
1432            // and hence cant reuse the structure.
1433            // This is same as how we dont reuse the structure if one of the file from old program is now missing
1434            if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
1435                return StructureIsReused.Not;
1436            }
1437
1438            const oldSourceFiles = oldProgram.getSourceFiles();
1439            const enum SeenPackageName { Exists, Modified }
1440            const seenPackageNames = new Map<string, SeenPackageName>();
1441
1442            for (const oldSourceFile of oldSourceFiles) {
1443                let newSourceFile = host.getSourceFileByPath
1444                    ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
1445                    : host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
1446
1447                if (!newSourceFile) {
1448                    return StructureIsReused.Not;
1449                }
1450
1451                Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
1452
1453                let fileChanged: boolean;
1454                if (oldSourceFile.redirectInfo) {
1455                    // We got `newSourceFile` by path, so it is actually for the unredirected file.
1456                    // This lets us know if the unredirected file has changed. If it has we should break the redirect.
1457                    if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
1458                        // Underlying file has changed. Might not redirect anymore. Must rebuild program.
1459                        return StructureIsReused.Not;
1460                    }
1461                    fileChanged = false;
1462                    newSourceFile = oldSourceFile; // Use the redirect.
1463                }
1464                else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
1465                    // If a redirected-to source file changes, the redirect may be broken.
1466                    if (newSourceFile !== oldSourceFile) {
1467                        return StructureIsReused.Not;
1468                    }
1469                    fileChanged = false;
1470                }
1471                else {
1472                    fileChanged = newSourceFile !== oldSourceFile;
1473                }
1474
1475                // Since the project references havent changed, its right to set originalFileName and resolvedPath here
1476                newSourceFile.path = oldSourceFile.path;
1477                newSourceFile.originalFileName = oldSourceFile.originalFileName;
1478                newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
1479                newSourceFile.fileName = oldSourceFile.fileName;
1480
1481                const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
1482                if (packageName !== undefined) {
1483                    // If there are 2 different source files for the same package name and at least one of them changes,
1484                    // they might become redirects. So we must rebuild the program.
1485                    const prevKind = seenPackageNames.get(packageName);
1486                    const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
1487                    if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
1488                        return StructureIsReused.Not;
1489                    }
1490                    seenPackageNames.set(packageName, newKind);
1491                }
1492
1493                if (fileChanged) {
1494                    // The `newSourceFile` object was created for the new program.
1495
1496                    if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) {
1497                        // 'lib' references has changed. Matches behavior in changesAffectModuleResolution
1498                        return StructureIsReused.Not;
1499                    }
1500
1501                    if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
1502                        // value of no-default-lib has changed
1503                        // this will affect if default library is injected into the list of files
1504                        structureIsReused = StructureIsReused.SafeModules;
1505                    }
1506
1507                    // check tripleslash references
1508                    if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
1509                        // tripleslash references has changed
1510                        structureIsReused = StructureIsReused.SafeModules;
1511                    }
1512
1513                    // check imports and module augmentations
1514                    collectExternalModuleReferences(newSourceFile);
1515                    if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
1516                        // imports has changed
1517                        structureIsReused = StructureIsReused.SafeModules;
1518                    }
1519                    if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
1520                        // moduleAugmentations has changed
1521                        structureIsReused = StructureIsReused.SafeModules;
1522                    }
1523                    if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
1524                        // dynamicImport has changed
1525                        structureIsReused = StructureIsReused.SafeModules;
1526                    }
1527
1528                    if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
1529                        // 'types' references has changed
1530                        structureIsReused = StructureIsReused.SafeModules;
1531                    }
1532
1533                    // tentatively approve the file
1534                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
1535                }
1536                else if (hasInvalidatedResolution(oldSourceFile.path)) {
1537                    // 'module/types' references could have changed
1538                    structureIsReused = StructureIsReused.SafeModules;
1539
1540                    // add file to the modified list so that we will resolve it later
1541                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
1542                }
1543
1544                // if file has passed all checks it should be safe to reuse it
1545                newSourceFiles.push(newSourceFile);
1546            }
1547
1548            if (structureIsReused !== StructureIsReused.Completely) {
1549                return structureIsReused;
1550            }
1551
1552            const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
1553            for (const oldFile of oldSourceFiles) {
1554                if (!contains(modifiedFiles, oldFile)) {
1555                    for (const moduleName of oldFile.ambientModuleNames) {
1556                        ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName);
1557                    }
1558                }
1559            }
1560            // try to verify results of module resolution
1561            for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
1562                const moduleNames = getModuleNames(newSourceFile);
1563                const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
1564                // ensure that module resolution results are still correct
1565                const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
1566                if (resolutionsChanged) {
1567                    structureIsReused = StructureIsReused.SafeModules;
1568                    newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
1569                }
1570                else {
1571                    newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
1572                }
1573                // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
1574                const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
1575                const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile);
1576                // ensure that types resolutions are still correct
1577                const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
1578                if (typeReferenceEesolutionsChanged) {
1579                    structureIsReused = StructureIsReused.SafeModules;
1580                    newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
1581                }
1582                else {
1583                    newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
1584                }
1585            }
1586
1587            if (structureIsReused !== StructureIsReused.Completely) {
1588                return structureIsReused;
1589            }
1590
1591            if (host.hasChangedAutomaticTypeDirectiveNames?.()) {
1592                return StructureIsReused.SafeModules;
1593            }
1594
1595            missingFilePaths = oldProgram.getMissingFilePaths();
1596
1597            // update fileName -> file mapping
1598            Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
1599            for (const newSourceFile of newSourceFiles) {
1600                filesByName.set(newSourceFile.path, newSourceFile);
1601            }
1602            const oldFilesByNameMap = oldProgram.getFilesByNameMap();
1603            oldFilesByNameMap.forEach((oldFile, path) => {
1604                if (!oldFile) {
1605                    filesByName.set(path, oldFile);
1606                    return;
1607                }
1608                if (oldFile.path === path) {
1609                    // Set the file as found during node modules search if it was found that way in old progra,
1610                    if (oldProgram!.isSourceFileFromExternalLibrary(oldFile)) {
1611                        sourceFilesFoundSearchingNodeModules.set(oldFile.path, true);
1612                    }
1613                    return;
1614                }
1615                filesByName.set(path, filesByName.get(oldFile.path));
1616            });
1617
1618            files = newSourceFiles;
1619            fileReasons = oldProgram.getFileIncludeReasons();
1620            fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
1621            resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
1622
1623            sourceFileToPackageName = oldProgram.sourceFileToPackageName;
1624            redirectTargetsMap = oldProgram.redirectTargetsMap;
1625
1626            return StructureIsReused.Completely;
1627        }
1628
1629        function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
1630            return {
1631                getPrependNodes,
1632                getCanonicalFileName,
1633                getCommonSourceDirectory: program.getCommonSourceDirectory,
1634                getCompilerOptions: program.getCompilerOptions,
1635                getCurrentDirectory: () => currentDirectory,
1636                getNewLine: () => host.getNewLine(),
1637                getSourceFile: program.getSourceFile,
1638                getSourceFileByPath: program.getSourceFileByPath,
1639                getSourceFiles: program.getSourceFiles,
1640                getLibFileFromReference: program.getLibFileFromReference,
1641                isSourceFileFromExternalLibrary,
1642                getResolvedProjectReferenceToRedirect,
1643                getProjectReferenceRedirect,
1644                isSourceOfProjectReferenceRedirect,
1645                getSymlinkCache,
1646                writeFile: writeFileCallback || (
1647                    (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
1648                isEmitBlocked,
1649                readFile: f => host.readFile(f),
1650                fileExists: f => {
1651                    // Use local caches
1652                    const path = toPath(f);
1653                    if (getSourceFileByPath(path)) return true;
1654                    if (contains(missingFilePaths, path)) return false;
1655                    // Before falling back to the host
1656                    return host.fileExists(f);
1657                },
1658                useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
1659                getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
1660                getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
1661                redirectTargetsMap,
1662                getFileIncludeReasons: program.getFileIncludeReasons,
1663            };
1664        }
1665
1666        function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
1667            Debug.assert(!outFile(options));
1668            tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true);
1669            performance.mark("beforeEmit");
1670            const emitResult = emitFiles(
1671                notImplementedResolver,
1672                getEmitHost(writeFileCallback),
1673                /*targetSourceFile*/ undefined,
1674                /*transformers*/ noTransformers,
1675                /*emitOnlyDtsFiles*/ false,
1676                /*onlyBuildInfo*/ true
1677            );
1678
1679            performance.mark("afterEmit");
1680            performance.measure("Emit", "beforeEmit", "afterEmit");
1681            tracing?.pop();
1682            return emitResult;
1683        }
1684
1685        function getResolvedProjectReferences() {
1686            return resolvedProjectReferences;
1687        }
1688
1689        function getProjectReferences() {
1690            return projectReferences;
1691        }
1692
1693        function getPrependNodes() {
1694            return createPrependNodes(
1695                projectReferences,
1696                (_ref, index) => resolvedProjectReferences![index]?.commandLine,
1697                fileName => {
1698                    const path = toPath(fileName);
1699                    const sourceFile = getSourceFileByPath(path);
1700                    return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path);
1701                }
1702            );
1703        }
1704
1705        function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
1706            return !!sourceFilesFoundSearchingNodeModules.get(file.path);
1707        }
1708
1709        function isSourceFileDefaultLibrary(file: SourceFile): boolean {
1710            if (file.hasNoDefaultLib) {
1711                return true;
1712            }
1713
1714            if (!options.noLib) {
1715                return false;
1716            }
1717
1718            // If '--lib' is not specified, include default library file according to '--target'
1719            // otherwise, using options specified in '--lib' instead of '--target' default library file
1720            const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
1721            if (!options.lib) {
1722                return equalityComparer(file.fileName, getDefaultLibraryFileName());
1723            }
1724            else {
1725                return some(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
1726            }
1727        }
1728
1729        function getDiagnosticsProducingTypeChecker() {
1730            return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
1731        }
1732
1733        function dropDiagnosticsProducingTypeChecker() {
1734            diagnosticsProducingTypeChecker = undefined!;
1735        }
1736
1737        function getEtsLibSFromProgram() {
1738            return getEtsLibs(program);
1739        }
1740
1741        function getTypeChecker() {
1742            return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
1743        }
1744
1745        function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
1746            tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true);
1747            const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
1748            tracing?.pop();
1749            return result;
1750        }
1751
1752        function isEmitBlocked(emitFileName: string): boolean {
1753            return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
1754        }
1755
1756        function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
1757            if (!forceDtsEmit) {
1758                const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
1759                if (result) return result;
1760            }
1761
1762            // Create the emit resolver outside of the "emitTime" tracking code below.  That way
1763            // any cost associated with it (like type checking) are appropriate associated with
1764            // the type-checking counter.
1765            //
1766            // If the -out option is specified, we should not pass the source file to getEmitResolver.
1767            // This is because in the -out scenario all files need to be emitted, and therefore all
1768            // files need to be type checked. And the way to specify that all files need to be type
1769            // checked is to not pass the file to getEmitResolver.
1770            const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken);
1771
1772            performance.mark("beforeEmit");
1773
1774            const emitResult = emitFiles(
1775                emitResolver,
1776                getEmitHost(writeFileCallback),
1777                sourceFile,
1778                getTransformers(options, customTransformers, emitOnlyDtsFiles),
1779                emitOnlyDtsFiles,
1780                /*onlyBuildInfo*/ false,
1781                forceDtsEmit
1782            );
1783
1784            performance.mark("afterEmit");
1785            performance.measure("Emit", "beforeEmit", "afterEmit");
1786            return emitResult;
1787        }
1788
1789        function getSourceFile(fileName: string): SourceFile | undefined {
1790            return getSourceFileByPath(toPath(fileName));
1791        }
1792
1793        function getSourceFileByPath(path: Path): SourceFile | undefined {
1794            return filesByName.get(path) || undefined;
1795        }
1796
1797        function getDiagnosticsHelper<T extends Diagnostic>(
1798            sourceFile: SourceFile | undefined,
1799            getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[],
1800            cancellationToken: CancellationToken | undefined): readonly T[] {
1801            if (sourceFile) {
1802                return getDiagnostics(sourceFile, cancellationToken);
1803            }
1804            return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => {
1805                if (cancellationToken) {
1806                    cancellationToken.throwIfCancellationRequested();
1807                }
1808                return getDiagnostics(sourceFile, cancellationToken);
1809            }));
1810        }
1811
1812        function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1813            return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
1814        }
1815
1816        function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1817            return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
1818        }
1819
1820        function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined {
1821           return sourceFile
1822                ? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path)
1823                : cachedBindAndCheckDiagnosticsForFile.allDiagnostics;
1824        }
1825
1826        function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1827            return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken);
1828        }
1829
1830        function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] {
1831            if (skipTypeChecking(sourceFile, options, program)) {
1832                return emptyArray;
1833            }
1834
1835            const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
1836            if (!sourceFile.commentDirectives?.length) {
1837                return programDiagnosticsInFile;
1838            }
1839
1840            return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics;
1841        }
1842
1843        function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1844            const options = program.getCompilerOptions();
1845            // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
1846            if (!sourceFile || outFile(options)) {
1847                return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
1848            }
1849            else {
1850                return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
1851            }
1852        }
1853
1854        function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] {
1855            // For JavaScript files, we report semantic errors for using TypeScript-only
1856            // constructs from within a JavaScript file as syntactic errors.
1857            if (isSourceFileJS(sourceFile)) {
1858                if (!sourceFile.additionalSyntacticDiagnostics) {
1859                    sourceFile.additionalSyntacticDiagnostics = getJSSyntacticDiagnosticsForFile(sourceFile);
1860                }
1861                return concatenate(sourceFile.additionalSyntacticDiagnostics, sourceFile.parseDiagnostics);
1862            }
1863            return sourceFile.parseDiagnostics;
1864        }
1865
1866        function runWithCancellationToken<T>(func: () => T): T {
1867            try {
1868                return func();
1869            }
1870            catch (e) {
1871                if (e instanceof OperationCanceledException) {
1872                    // We were canceled while performing the operation.  Because our type checker
1873                    // might be a bad state, we need to throw it away.
1874                    //
1875                    // Note: we are overly aggressive here.  We do not actually *have* to throw away
1876                    // the "noDiagnosticsTypeChecker".  However, for simplicity, i'd like to keep
1877                    // the lifetimes of these two TypeCheckers the same.  Also, we generally only
1878                    // cancel when the user has made a change anyways.  And, in that case, we (the
1879                    // program instance) will get thrown away anyways.  So trying to keep one of
1880                    // these type checkers alive doesn't serve much purpose.
1881                    noDiagnosticsTypeChecker = undefined!;
1882                    diagnosticsProducingTypeChecker = undefined!;
1883                }
1884
1885                throw e;
1886            }
1887        }
1888
1889        function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1890            return concatenate(
1891                filterSemanticDiagnotics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options),
1892                getProgramDiagnostics(sourceFile)
1893            );
1894        }
1895
1896        function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1897            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache);
1898        }
1899
1900        function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1901            return runWithCancellationToken(() => {
1902                if (skipTypeChecking(sourceFile, options, program)) {
1903                    return emptyArray;
1904                }
1905
1906                const typeChecker = getDiagnosticsProducingTypeChecker();
1907
1908                Debug.assert(!!sourceFile.bindDiagnostics);
1909
1910                const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
1911                const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
1912                // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
1913                const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
1914                    sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred || sourceFile.scriptKind === ScriptKind.ETS);
1915                const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
1916                const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
1917
1918                return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
1919            });
1920        }
1921
1922        function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
1923            const flatDiagnostics = flatten(allDiagnostics);
1924            if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) {
1925                return flatDiagnostics;
1926            }
1927
1928            const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics);
1929
1930            for (const errorExpectation of directives.getUnusedExpectations()) {
1931                diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive));
1932            }
1933
1934            return diagnostics;
1935        }
1936
1937        /**
1938         * Creates a map of comment directives along with the diagnostics immediately preceded by one of them.
1939         * Comments that match to any of those diagnostics are marked as used.
1940         */
1941        function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) {
1942            // Diagnostics are only reported if there is no comment directive preceding them
1943            // This will modify the directives map by marking "used" ones with a corresponding diagnostic
1944            const directives = createCommentDirectivesMap(sourceFile, commentDirectives);
1945            const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1);
1946
1947            return { diagnostics, directives };
1948        }
1949
1950        function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
1951            return runWithCancellationToken(() => {
1952                return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
1953            });
1954        }
1955
1956        /**
1957         * @returns The line index marked as preceding the diagnostic, or -1 if none was.
1958         */
1959        function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) {
1960            const { file, start } = diagnostic;
1961            if (!file) {
1962                return -1;
1963            }
1964
1965            // Start out with the line just before the text
1966            const lineStarts = getLineStarts(file);
1967            let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217
1968            while (line >= 0) {
1969                // As soon as that line is known to have a comment directive, use that
1970                if (directives.markUsed(line)) {
1971                    return line;
1972                }
1973
1974                // Stop searching if the line is not empty and not a comment
1975                const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim();
1976                if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) {
1977                    return -1;
1978                }
1979
1980                line--;
1981            }
1982
1983            return -1;
1984        }
1985
1986        function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
1987            return runWithCancellationToken(() => {
1988                const diagnostics: DiagnosticWithLocation[] = [];
1989                walk(sourceFile, sourceFile);
1990                forEachChildRecursively(sourceFile, walk, walkArray);
1991
1992                return diagnostics;
1993
1994                function walk(node: Node, parent: Node) {
1995                    // Return directly from the case if the given node doesnt want to visit each child
1996                    // Otherwise break to visit each child
1997
1998                    switch (parent.kind) {
1999                        case SyntaxKind.Parameter:
2000                        case SyntaxKind.PropertyDeclaration:
2001                        case SyntaxKind.MethodDeclaration:
2002                            if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
2003                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
2004                                return "skip";
2005                            }
2006                        // falls through
2007                        case SyntaxKind.MethodSignature:
2008                        case SyntaxKind.Constructor:
2009                        case SyntaxKind.GetAccessor:
2010                        case SyntaxKind.SetAccessor:
2011                        case SyntaxKind.FunctionExpression:
2012                        case SyntaxKind.FunctionDeclaration:
2013                        case SyntaxKind.ArrowFunction:
2014                        case SyntaxKind.VariableDeclaration:
2015                            // type annotation
2016                            if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
2017                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
2018                                return "skip";
2019                            }
2020                    }
2021
2022                    switch (node.kind) {
2023                        case SyntaxKind.ImportClause:
2024                            if ((node as ImportClause).isTypeOnly) {
2025                                diagnostics.push(createDiagnosticForNode(parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type"));
2026                                return "skip";
2027                            }
2028                            break;
2029                        case SyntaxKind.ExportDeclaration:
2030                            if ((node as ExportDeclaration).isTypeOnly) {
2031                                diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type"));
2032                                return "skip";
2033                            }
2034                            break;
2035                        case SyntaxKind.ImportEqualsDeclaration:
2036                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
2037                            return "skip";
2038                        case SyntaxKind.ExportAssignment:
2039                            if ((<ExportAssignment>node).isExportEquals) {
2040                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
2041                                return "skip";
2042                            }
2043                            break;
2044                        case SyntaxKind.HeritageClause:
2045                            const heritageClause = <HeritageClause>node;
2046                            if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
2047                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
2048                                return "skip";
2049                            }
2050                            break;
2051                        case SyntaxKind.InterfaceDeclaration:
2052                            const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword);
2053                            Debug.assertIsDefined(interfaceKeyword);
2054                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword));
2055                            return "skip";
2056                        case SyntaxKind.ModuleDeclaration:
2057                            const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
2058                            Debug.assertIsDefined(moduleKeyword);
2059                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
2060                            return "skip";
2061                        case SyntaxKind.TypeAliasDeclaration:
2062                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
2063                            return "skip";
2064                        case SyntaxKind.EnumDeclaration:
2065                            const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword));
2066                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
2067                            return "skip";
2068                        case SyntaxKind.NonNullExpression:
2069                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
2070                            return "skip";
2071                        case SyntaxKind.AsExpression:
2072                            diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
2073                            return "skip";
2074                        case SyntaxKind.TypeAssertionExpression:
2075                            Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
2076                    }
2077                }
2078
2079                function walkArray(nodes: NodeArray<Node>, parent: Node) {
2080                    if (parent.decorators === nodes && !options.experimentalDecorators) {
2081                        diagnostics.push(createDiagnosticForNode(parent, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_in_your_tsconfig_or_jsconfig_to_remove_this_warning));
2082                    }
2083
2084                    switch (parent.kind) {
2085                        case SyntaxKind.ClassDeclaration:
2086                        case SyntaxKind.ClassExpression:
2087                        case SyntaxKind.StructDeclaration:
2088                        case SyntaxKind.MethodDeclaration:
2089                        case SyntaxKind.Constructor:
2090                        case SyntaxKind.GetAccessor:
2091                        case SyntaxKind.SetAccessor:
2092                        case SyntaxKind.FunctionExpression:
2093                        case SyntaxKind.FunctionDeclaration:
2094                        case SyntaxKind.ArrowFunction:
2095                            // Check type parameters
2096                            if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
2097                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
2098                                return "skip";
2099                            }
2100                        // falls through
2101
2102                        case SyntaxKind.VariableStatement:
2103                            // Check modifiers
2104                            if (nodes === parent.modifiers) {
2105                                checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
2106                                return "skip";
2107                            }
2108                            break;
2109                        case SyntaxKind.PropertyDeclaration:
2110                            // Check modifiers of property declaration
2111                            if (nodes === (<PropertyDeclaration>parent).modifiers) {
2112                                for (const modifier of <NodeArray<Modifier>>nodes) {
2113                                    if (modifier.kind !== SyntaxKind.StaticKeyword) {
2114                                        diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2115                                    }
2116                                }
2117                                return "skip";
2118                            }
2119                            break;
2120                        case SyntaxKind.Parameter:
2121                            // Check modifiers of parameter declaration
2122                            if (nodes === (<ParameterDeclaration>parent).modifiers) {
2123                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
2124                                return "skip";
2125                            }
2126                            break;
2127                        case SyntaxKind.CallExpression:
2128                        case SyntaxKind.NewExpression:
2129                        case SyntaxKind.ExpressionWithTypeArguments:
2130                        case SyntaxKind.JsxSelfClosingElement:
2131                        case SyntaxKind.JsxOpeningElement:
2132                        case SyntaxKind.TaggedTemplateExpression:
2133                            // Check type arguments
2134                            if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
2135                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
2136                                return "skip";
2137                            }
2138                            break;
2139                    }
2140                }
2141
2142                function checkModifiers(modifiers: NodeArray<Modifier>, isConstValid: boolean) {
2143                    for (const modifier of modifiers) {
2144                        switch (modifier.kind) {
2145                            case SyntaxKind.ConstKeyword:
2146                                if (isConstValid) {
2147                                    continue;
2148                                }
2149                            // to report error,
2150                            // falls through
2151                            case SyntaxKind.PublicKeyword:
2152                            case SyntaxKind.PrivateKeyword:
2153                            case SyntaxKind.ProtectedKeyword:
2154                            case SyntaxKind.ReadonlyKeyword:
2155                            case SyntaxKind.DeclareKeyword:
2156                            case SyntaxKind.AbstractKeyword:
2157                                diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2158                                break;
2159
2160                            // These are all legal modifiers.
2161                            case SyntaxKind.StaticKeyword:
2162                            case SyntaxKind.ExportKeyword:
2163                            case SyntaxKind.DefaultKeyword:
2164                        }
2165                    }
2166                }
2167
2168                function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2169                    const start = nodes.pos;
2170                    return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2);
2171                }
2172
2173                // Since these are syntactic diagnostics, parent might not have been set
2174                // this means the sourceFile cannot be infered from the node
2175                function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2176                    return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
2177                }
2178            });
2179        }
2180
2181        function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2182            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
2183        }
2184
2185        function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2186            return runWithCancellationToken(() => {
2187                const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
2188                // Don't actually write any files since we're just getting diagnostics.
2189                return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray;
2190            });
2191        }
2192
2193        function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>(
2194            sourceFile: T,
2195            cancellationToken: CancellationToken | undefined,
2196            cache: DiagnosticCache<U>,
2197            getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[],
2198        ): readonly U[] {
2199
2200            const cachedResult = sourceFile
2201                ? cache.perFile?.get(sourceFile.path)
2202                : cache.allDiagnostics;
2203
2204            if (cachedResult) {
2205                return cachedResult;
2206            }
2207            const result = getDiagnostics(sourceFile, cancellationToken);
2208            if (sourceFile) {
2209                (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result);
2210            }
2211            else {
2212                cache.allDiagnostics = result;
2213            }
2214            return result;
2215        }
2216
2217        function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
2218            return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
2219        }
2220
2221        function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
2222            return sortAndDeduplicateDiagnostics(concatenate(
2223                programDiagnostics.getGlobalDiagnostics(),
2224                getOptionsDiagnosticsOfConfigFile()
2225            ));
2226        }
2227
2228        function getOptionsDiagnosticsOfConfigFile() {
2229            if (!options.configFile) { return emptyArray; }
2230            let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
2231            forEachResolvedProjectReference(resolvedRef => {
2232                diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
2233            });
2234            return diagnostics;
2235        }
2236
2237        function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
2238            return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
2239        }
2240
2241        function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
2242            return configFileParsingDiagnostics || emptyArray;
2243        }
2244
2245        function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) {
2246            processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason);
2247        }
2248
2249        function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
2250            return a.fileName === b.fileName;
2251        }
2252
2253        function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean {
2254            return a.kind === SyntaxKind.Identifier
2255                ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText
2256                : b.kind === SyntaxKind.StringLiteral && a.text === b.text;
2257        }
2258
2259        function createSyntheticImport(text: string, file: SourceFile) {
2260            const externalHelpersModuleReference = factory.createStringLiteral(text);
2261            const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
2262            addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
2263            setParent(externalHelpersModuleReference, importDecl);
2264            setParent(importDecl, file);
2265            // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them
2266            // (which is required to build the dependency graph for incremental emit)
2267            (externalHelpersModuleReference as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2268            (importDecl as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2269            return externalHelpersModuleReference;
2270        }
2271
2272        function collectExternalModuleReferences(file: SourceFile): void {
2273            if (file.imports) {
2274                return;
2275            }
2276
2277            const isJavaScriptFile = isSourceFileJS(file);
2278            const isExternalModuleFile = isExternalModule(file);
2279
2280            // file.imports may not be undefined if there exists dynamic import
2281            let imports: StringLiteralLike[] | undefined;
2282            let moduleAugmentations: (StringLiteral | Identifier)[] | undefined;
2283            let ambientModules: string[] | undefined;
2284
2285            // If we are importing helpers, we need to add a synthetic reference to resolve the
2286            // helpers library.
2287            if ((options.isolatedModules || isExternalModuleFile)
2288                && !file.isDeclarationFile) {
2289                if (options.importHelpers) {
2290                    // synthesize 'import "tslib"' declaration
2291                    imports = [createSyntheticImport(externalHelpersModuleNameText, file)];
2292                }
2293                const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options);
2294                if (jsxImport) {
2295                    // synthesize `import "base/jsx-runtime"` declaration
2296                    (imports ||= []).push(createSyntheticImport(jsxImport, file));
2297                }
2298            }
2299
2300            for (const node of file.statements) {
2301                collectModuleReferences(node, /*inAmbientModule*/ false);
2302            }
2303            if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
2304                collectDynamicImportOrRequireCalls(file);
2305            }
2306
2307            file.imports = imports || emptyArray;
2308            file.moduleAugmentations = moduleAugmentations || emptyArray;
2309            file.ambientModuleNames = ambientModules || emptyArray;
2310
2311            return;
2312
2313            function collectModuleReferences(node: Statement, inAmbientModule: boolean): void {
2314                if (isAnyImportOrReExport(node)) {
2315                    const moduleNameExpr = getExternalModuleName(node);
2316                    // TypeScript 1.0 spec (April 2014): 12.1.6
2317                    // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
2318                    // only through top - level external module names. Relative external module names are not permitted.
2319                    if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
2320                        imports = append(imports, moduleNameExpr);
2321                    }
2322                }
2323                else if (isModuleDeclaration(node)) {
2324                    if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
2325                        const nameText = getTextOfIdentifierOrLiteral(node.name);
2326                        // Ambient module declarations can be interpreted as augmentations for some existing external modules.
2327                        // This will happen in two cases:
2328                        // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
2329                        // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
2330                        //   immediately nested in top level ambient module declaration .
2331                        if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
2332                            (moduleAugmentations || (moduleAugmentations = [])).push(node.name);
2333                        }
2334                        else if (!inAmbientModule) {
2335                            if (file.isDeclarationFile) {
2336                                // for global .d.ts files record name of ambient module
2337                                (ambientModules || (ambientModules = [])).push(nameText);
2338                            }
2339                            // An AmbientExternalModuleDeclaration declares an external module.
2340                            // This type of declaration is permitted only in the global module.
2341                            // The StringLiteral must specify a top - level external module name.
2342                            // Relative external module names are not permitted
2343
2344                            // NOTE: body of ambient module is always a module block, if it exists
2345                            const body = <ModuleBlock>(<ModuleDeclaration>node).body;
2346                            if (body) {
2347                                for (const statement of body.statements) {
2348                                    collectModuleReferences(statement, /*inAmbientModule*/ true);
2349                                }
2350                            }
2351                        }
2352                    }
2353                }
2354            }
2355
2356            function collectDynamicImportOrRequireCalls(file: SourceFile) {
2357                const r = /import|require/g;
2358                while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null
2359                    const node = getNodeAtPosition(file, r.lastIndex);
2360                    if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) {
2361                        imports = append(imports, node.arguments[0]);
2362                    }
2363                    // we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
2364                    else if (isImportCall(node) && node.arguments.length === 1 && isStringLiteralLike(node.arguments[0])) {
2365                        imports = append(imports, node.arguments[0] as StringLiteralLike);
2366                    }
2367                    else if (isLiteralImportTypeNode(node)) {
2368                        imports = append(imports, node.argument.literal);
2369                    }
2370                }
2371            }
2372
2373            /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
2374            function getNodeAtPosition(sourceFile: SourceFile, position: number): Node {
2375                let current: Node = sourceFile;
2376                const getContainingChild = (child: Node) => {
2377                    if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) {
2378                        return child;
2379                    }
2380                };
2381                while (true) {
2382                    const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild);
2383                    if (!child) {
2384                        return current;
2385                    }
2386                    current = child;
2387                }
2388            }
2389        }
2390
2391        function getLibFileFromReference(ref: FileReference) {
2392            const libName = toFileNameLowerCase(ref.fileName);
2393            const libFileName = libMap.get(libName);
2394            if (libFileName) {
2395                return getSourceFile(combinePaths(defaultLibraryPath, libFileName));
2396            }
2397        }
2398
2399        /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
2400        function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
2401            return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile);
2402        }
2403
2404        function getSourceFileFromReferenceWorker(
2405            fileName: string,
2406            getSourceFile: (fileName: string) => SourceFile | undefined,
2407            fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
2408            reason?: FileIncludeReason): SourceFile | undefined {
2409
2410            if (hasExtension(fileName)) {
2411                const canonicalFileName = host.getCanonicalFileName(fileName);
2412                if (!options.allowNonTsExtensions && !forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(canonicalFileName, extension)) && !fileExtensionIs(canonicalFileName, Extension.Ets)) {
2413                    if (fail) {
2414                        if (hasJSFileExtension(canonicalFileName)) {
2415                            fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName);
2416                        }
2417                        else {
2418                            fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2419                        }
2420                    }
2421                    return undefined;
2422                }
2423
2424                const sourceFile = getSourceFile(fileName);
2425                if (fail) {
2426                    if (!sourceFile) {
2427                        const redirect = getProjectReferenceRedirect(fileName);
2428                        if (redirect) {
2429                            fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName);
2430                        }
2431                        else {
2432                            fail(Diagnostics.File_0_not_found, fileName);
2433                        }
2434                    }
2435                    else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) {
2436                        fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
2437                    }
2438                }
2439                return sourceFile;
2440            }
2441            else {
2442                const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName);
2443                if (sourceFileNoExtension) return sourceFileNoExtension;
2444
2445                if (fail && options.allowNonTsExtensions) {
2446                    fail(Diagnostics.File_0_not_found, fileName);
2447                    return undefined;
2448                }
2449
2450                const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
2451                if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2452                return sourceFileWithAddedExtension;
2453            }
2454        }
2455
2456        /** This has side effects through `findSourceFile`. */
2457        function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
2458            getSourceFileFromReferenceWorker(
2459                fileName,
2460                fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
2461                (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
2462                reason
2463            );
2464        }
2465
2466        function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) {
2467            return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason);
2468        }
2469
2470        function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void {
2471            const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile);
2472            if (hasExistingReasonToReportErrorOn) {
2473                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]);
2474            }
2475            else {
2476                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]);
2477            }
2478        }
2479
2480        function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
2481            const redirect: SourceFile = Object.create(redirectTarget);
2482            redirect.fileName = fileName;
2483            redirect.path = path;
2484            redirect.resolvedPath = resolvedPath;
2485            redirect.originalFileName = originalFileName;
2486            redirect.redirectInfo = { redirectTarget, unredirected };
2487            sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2488            Object.defineProperties(redirect, {
2489                id: {
2490                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; },
2491                    set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; },
2492                },
2493                symbol: {
2494                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; },
2495                    set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; },
2496                },
2497            });
2498            return redirect;
2499        }
2500
2501        // Get source file from normalized fileName
2502        function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2503            tracing?.push(tracing.Phase.Program, "findSourceFile", {
2504                fileName,
2505                isDefaultLib: isDefaultLib || undefined,
2506                fileIncludeKind: (FileIncludeKind as any)[reason.kind],
2507            });
2508            const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
2509            tracing?.pop();
2510            return result;
2511        }
2512
2513        function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2514            if (useSourceOfProjectReferenceRedirect) {
2515                let source = getSourceOfProjectReferenceRedirect(fileName);
2516                // If preserveSymlinks is true, module resolution wont jump the symlink
2517                // but the resolved real path may be the .d.ts from project reference
2518                // Note:: Currently we try the real path only if the
2519                // file is from node_modules to avoid having to run real path on all file paths
2520                if (!source &&
2521                    host.realpath &&
2522                    options.preserveSymlinks &&
2523                    isDeclarationFileName(fileName) &&
2524                    stringContains(fileName, nodeModulesPathPart)) {
2525                    const realPath = host.realpath(fileName);
2526                    if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
2527                }
2528                if (source) {
2529                    const file = isString(source) ?
2530                        findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
2531                        undefined;
2532                    if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
2533                    return file;
2534                }
2535            }
2536            const originalFileName = fileName;
2537            if (filesByName.has(path)) {
2538                const file = filesByName.get(path);
2539                addFileIncludeReason(file || undefined, reason);
2540                // try to check if we've already seen this file but with a different casing in path
2541                // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
2542                if (file && options.forceConsistentCasingInFileNames) {
2543                    const checkedName = file.fileName;
2544                    const isRedirect = toPath(checkedName) !== toPath(fileName);
2545                    if (isRedirect) {
2546                        fileName = getProjectReferenceRedirect(fileName) || fileName;
2547                    }
2548                    // Check if it differs only in drive letters its ok to ignore that error:
2549                    const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
2550                    const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory);
2551                    if (checkedAbsolutePath !== inputAbsolutePath) {
2552                        reportFileNamesDifferOnlyInCasingError(fileName, file, reason);
2553                    }
2554                }
2555
2556                // If the file was previously found via a node_modules search, but is now being processed as a root file,
2557                // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
2558                if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
2559                    sourceFilesFoundSearchingNodeModules.set(file.path, false);
2560                    if (!options.noResolve) {
2561                        processReferencedFiles(file, isDefaultLib);
2562                        processTypeReferenceDirectives(file);
2563                    }
2564                    if (!options.noLib) {
2565                        processLibReferenceDirectives(file);
2566                    }
2567
2568                    modulesWithElidedImports.set(file.path, false);
2569                    processImportedModules(file);
2570                }
2571                // See if we need to reprocess the imports due to prior skipped imports
2572                else if (file && modulesWithElidedImports.get(file.path)) {
2573                    if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
2574                        modulesWithElidedImports.set(file.path, false);
2575                        processImportedModules(file);
2576                    }
2577                }
2578
2579                return file || undefined;
2580            }
2581
2582            let redirectedPath: Path | undefined;
2583            if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) {
2584                const redirectProject = getProjectReferenceRedirectProject(fileName);
2585                if (redirectProject) {
2586                    if (outFile(redirectProject.commandLine.options)) {
2587                        // Shouldnt create many to 1 mapping file in --out scenario
2588                        return undefined;
2589                    }
2590                    const redirect = getProjectReferenceOutputName(redirectProject, fileName);
2591                    fileName = redirect;
2592                    // Once we start redirecting to a file, we can potentially come back to it
2593                    // via a back-reference from another file in the .d.ts folder. If that happens we'll
2594                    // end up trying to add it to the program *again* because we were tracking it via its
2595                    // original (un-redirected) name. So we have to map both the original path and the redirected path
2596                    // to the source file we're about to find/create
2597                    redirectedPath = toPath(redirect);
2598                }
2599            }
2600
2601            // We haven't looked for this file, do so now and cache result
2602            const file = host.getSourceFile(
2603                fileName,
2604                options.target!,
2605                hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]),
2606                shouldCreateNewSourceFile
2607            );
2608
2609            if (packageId) {
2610                const packageIdKey = packageIdToString(packageId);
2611                const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
2612                if (fileFromPackageId) {
2613                    // Some other SourceFile already exists with this package name and version.
2614                    // Instead of creating a duplicate, just redirect to the existing one.
2615                    const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
2616                    redirectTargetsMap.add(fileFromPackageId.path, fileName);
2617                    addFileToFilesByName(dupFile, path, redirectedPath);
2618                    addFileIncludeReason(dupFile, reason);
2619                    sourceFileToPackageName.set(path, packageId.name);
2620                    processingOtherFiles!.push(dupFile);
2621                    return dupFile;
2622                }
2623                else if (file) {
2624                    // This is the first source file to have this packageId.
2625                    packageIdToSourceFile.set(packageIdKey, file);
2626                    sourceFileToPackageName.set(path, packageId.name);
2627                }
2628            }
2629            addFileToFilesByName(file, path, redirectedPath);
2630
2631            if (file) {
2632                sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2633                file.fileName = fileName; // Ensure that source file has same name as what we were looking for
2634                file.path = path;
2635                file.resolvedPath = toPath(fileName);
2636                file.originalFileName = originalFileName;
2637                addFileIncludeReason(file, reason);
2638
2639                if (host.useCaseSensitiveFileNames()) {
2640                    const pathLowerCase = toFileNameLowerCase(path);
2641                    // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
2642                    const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
2643                    if (existingFile) {
2644                        reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason);
2645                    }
2646                    else {
2647                        filesByNameIgnoreCase!.set(pathLowerCase, file);
2648                    }
2649                }
2650
2651                skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib);
2652
2653                if (!options.noResolve) {
2654                    processReferencedFiles(file, isDefaultLib);
2655                    processTypeReferenceDirectives(file);
2656                }
2657                if (!options.noLib) {
2658                    processLibReferenceDirectives(file);
2659                }
2660
2661
2662                // always process imported modules to record module name resolutions
2663                processImportedModules(file);
2664
2665                if (isDefaultLib) {
2666                    processingDefaultLibFiles!.push(file);
2667                }
2668                else {
2669                    processingOtherFiles!.push(file);
2670                }
2671            }
2672            return file;
2673        }
2674
2675        function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) {
2676            if (file) fileReasons.add(file.path, reason);
2677        }
2678
2679        function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
2680            if (redirectedPath) {
2681                filesByName.set(redirectedPath, file);
2682                filesByName.set(path, file || false);
2683            }
2684            else {
2685                filesByName.set(path, file);
2686            }
2687        }
2688
2689        function getProjectReferenceRedirect(fileName: string): string | undefined {
2690            const referencedProject = getProjectReferenceRedirectProject(fileName);
2691            return referencedProject && getProjectReferenceOutputName(referencedProject, fileName);
2692        }
2693
2694        function getProjectReferenceRedirectProject(fileName: string) {
2695            // Ignore dts or any json files
2696            if (!resolvedProjectReferences || !resolvedProjectReferences.length || fileExtensionIs(fileName, Extension.Dts) || fileExtensionIs(fileName, Extension.Json)) {
2697                return undefined;
2698            }
2699
2700            // If this file is produced by a referenced project, we need to rewrite it to
2701            // look in the output folder of the referenced project rather than the input
2702            return getResolvedProjectReferenceToRedirect(fileName);
2703        }
2704
2705
2706        function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) {
2707            const out = outFile(referencedProject.commandLine.options);
2708            return out ?
2709                changeExtension(out, Extension.Dts) :
2710                getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames());
2711        }
2712
2713        /**
2714         * Get the referenced project if the file is input file from that reference project
2715         */
2716        function getResolvedProjectReferenceToRedirect(fileName: string) {
2717            if (mapFromFileToProjectReferenceRedirects === undefined) {
2718                mapFromFileToProjectReferenceRedirects = new Map();
2719                forEachResolvedProjectReference(referencedProject => {
2720                    // not input file from the referenced project, ignore
2721                    if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) {
2722                        referencedProject.commandLine.fileNames.forEach(f =>
2723                            mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path));
2724                    }
2725                });
2726            }
2727
2728            const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName));
2729            return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
2730        }
2731
2732        function forEachResolvedProjectReference<T>(
2733            cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
2734        ): T | undefined {
2735            return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
2736        }
2737
2738        function getSourceOfProjectReferenceRedirect(file: string) {
2739            if (!isDeclarationFileName(file)) return undefined;
2740            if (mapFromToProjectReferenceRedirectSource === undefined) {
2741                mapFromToProjectReferenceRedirectSource = new Map();
2742                forEachResolvedProjectReference(resolvedRef => {
2743                    const out = outFile(resolvedRef.commandLine.options);
2744                    if (out) {
2745                        // Dont know which source file it means so return true?
2746                        const outputDts = changeExtension(out, Extension.Dts);
2747                        mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
2748                    }
2749                    else {
2750                        const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames()));
2751                        forEach(resolvedRef.commandLine.fileNames, fileName => {
2752                            if (!fileExtensionIs(fileName, Extension.Dts) && !fileExtensionIs(fileName, Extension.Json)) {
2753                                const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory);
2754                                mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
2755                            }
2756                        });
2757                    }
2758                });
2759            }
2760            return mapFromToProjectReferenceRedirectSource.get(toPath(file));
2761        }
2762
2763        function isSourceOfProjectReferenceRedirect(fileName: string) {
2764            return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
2765        }
2766
2767        function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
2768            if (!projectReferenceRedirects) {
2769                return undefined;
2770            }
2771
2772            return projectReferenceRedirects.get(projectReferencePath) || undefined;
2773        }
2774
2775        function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
2776            forEach(file.referencedFiles, (ref, index) => {
2777                processSourceFile(
2778                    resolveTripleslashReference(ref.fileName, file.fileName),
2779                    isDefaultLib,
2780                    /*ignoreNoDefaultLib*/ false,
2781                    /*packageId*/ undefined,
2782                    { kind: FileIncludeKind.ReferenceFile, file: file.path, index, }
2783                );
2784            });
2785        }
2786
2787        function processTypeReferenceDirectives(file: SourceFile) {
2788            // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2789            const typeDirectives = map(file.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
2790            if (!typeDirectives) {
2791                return;
2792            }
2793
2794            const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file);
2795            for (let index = 0; index < typeDirectives.length; index++) {
2796                const ref = file.typeReferenceDirectives[index];
2797                const resolvedTypeReferenceDirective = resolutions[index];
2798                // store resolved type directive on the file
2799                const fileName = toFileNameLowerCase(ref.fileName);
2800                setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
2801                processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
2802            }
2803        }
2804
2805        function processTypeReferenceDirective(
2806            typeReferenceDirective: string,
2807            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2808            reason: FileIncludeReason
2809        ): void {
2810            tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
2811            processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason);
2812            tracing?.pop();
2813        }
2814
2815        function processTypeReferenceDirectiveWorker(
2816            typeReferenceDirective: string,
2817            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2818            reason: FileIncludeReason
2819        ): void {
2820
2821            // If we already found this library as a primary reference - nothing to do
2822            const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
2823            if (previousResolution && previousResolution.primary) {
2824                return;
2825            }
2826            let saveResolution = true;
2827            if (resolvedTypeReferenceDirective) {
2828                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++;
2829
2830                if (resolvedTypeReferenceDirective.primary) {
2831                    // resolved from the primary path
2832                    processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217
2833                }
2834                else {
2835                    // If we already resolved to this file, it must have been a secondary reference. Check file contents
2836                    // for sameness and possibly issue an error
2837                    if (previousResolution) {
2838                        // Don't bother reading the file again if it's the same file.
2839                        if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) {
2840                            const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
2841                            const existingFile = getSourceFile(previousResolution.resolvedFileName!)!;
2842                            if (otherFileText !== existingFile.text) {
2843                                addFilePreprocessingFileExplainingDiagnostic(
2844                                    existingFile,
2845                                    reason,
2846                                    Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
2847                                    [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName]
2848                                );
2849                            }
2850                        }
2851                        // don't overwrite previous resolution result
2852                        saveResolution = false;
2853                    }
2854                    else {
2855                        // First resolution of this library
2856                        processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason);
2857                    }
2858                }
2859
2860                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
2861            }
2862            else {
2863                addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]);
2864            }
2865
2866            if (saveResolution) {
2867                resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
2868            }
2869        }
2870
2871        function processLibReferenceDirectives(file: SourceFile) {
2872            forEach(file.libReferenceDirectives, (libReference, index) => {
2873                const libName = toFileNameLowerCase(libReference.fileName);
2874                const libFileName = libMap.get(libName);
2875                if (libFileName) {
2876                    // we ignore any 'no-default-lib' reference set on this file.
2877                    processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
2878                }
2879                else {
2880                    const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
2881                    const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
2882                    const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
2883                    (fileProcessingDiagnostics ||= []).push({
2884                        kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic,
2885                        reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, },
2886                        diagnostic,
2887                        args: [libName, suggestion]
2888                    });
2889                }
2890            });
2891        }
2892
2893        function getCanonicalFileName(fileName: string): string {
2894            return host.getCanonicalFileName(fileName);
2895        }
2896
2897        function processImportedModules(file: SourceFile) {
2898            collectExternalModuleReferences(file);
2899            if (file.imports.length || file.moduleAugmentations.length) {
2900                // Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
2901                const moduleNames = getModuleNames(file);
2902                const resolutions = resolveModuleNamesReusingOldState(moduleNames, file);
2903                Debug.assert(resolutions.length === moduleNames.length);
2904                for (let index = 0; index < moduleNames.length; index++) {
2905                    const resolution = resolutions[index];
2906                    setResolvedModule(file, moduleNames[index], resolution);
2907
2908                    if (!resolution) {
2909                        continue;
2910                    }
2911
2912                    const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
2913                    const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension);
2914                    const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
2915                    const resolvedFileName = resolution.resolvedFileName;
2916
2917                    if (isFromNodeModulesSearch) {
2918                        currentNodeModulesDepth++;
2919                    }
2920
2921                    // add file to program only if:
2922                    // - resolution was successful
2923                    // - noResolve is falsy
2924                    // - module name comes from the list of imports
2925                    // - it's not a top level JavaScript module that exceeded the search max
2926                    const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
2927                    // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
2928                    // This may still end up being an untyped module -- the file won't be included but imports will be allowed.
2929                    const shouldAddFile = resolvedFileName
2930                        && !getResolutionDiagnostic(options, resolution)
2931                        && !options.noResolve
2932                        && index < file.imports.length
2933                        && !elideImport
2934                        && !(isJsFile && !getAllowJSCompilerOption(options))
2935                        && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc));
2936
2937                    if (elideImport) {
2938                        modulesWithElidedImports.set(file.path, true);
2939                    }
2940                    else if (shouldAddFile) {
2941                        const path = toPath(resolvedFileName);
2942                        findSourceFile(
2943                            resolvedFileName,
2944                            path,
2945                            /*isDefaultLib*/ false,
2946                            /*ignoreNoDefaultLib*/ false,
2947                            { kind: FileIncludeKind.Import, file: file.path, index, },
2948                            resolution.packageId,
2949                        );
2950                    }
2951
2952                    if (isFromNodeModulesSearch) {
2953                        currentNodeModulesDepth--;
2954                    }
2955                }
2956            }
2957            else {
2958                // no imports - drop cached module resolutions
2959                file.resolvedModules = undefined;
2960            }
2961        }
2962
2963        function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
2964            let allFilesBelongToPath = true;
2965            const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
2966            for (const sourceFile of sourceFiles) {
2967                if (!sourceFile.isDeclarationFile) {
2968                    const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
2969                    if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
2970                        addProgramDiagnosticExplainingFile(
2971                            sourceFile,
2972                            Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
2973                            [sourceFile.fileName, rootDirectory]
2974                        );
2975                        allFilesBelongToPath = false;
2976                    }
2977                }
2978            }
2979
2980            return allFilesBelongToPath;
2981        }
2982
2983        function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
2984            if (!projectReferenceRedirects) {
2985                projectReferenceRedirects = new Map();
2986            }
2987
2988            // The actual filename (i.e. add "/tsconfig.json" if necessary)
2989            const refPath = resolveProjectReferencePath(ref);
2990            const sourceFilePath = toPath(refPath);
2991            const fromCache = projectReferenceRedirects.get(sourceFilePath);
2992            if (fromCache !== undefined) {
2993                return fromCache || undefined;
2994            }
2995
2996            let commandLine: ParsedCommandLine | undefined;
2997            let sourceFile: JsonSourceFile | undefined;
2998            if (host.getParsedCommandLine) {
2999                commandLine = host.getParsedCommandLine(refPath);
3000                if (!commandLine) {
3001                    addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined);
3002                    projectReferenceRedirects.set(sourceFilePath, false);
3003                    return undefined;
3004                }
3005                sourceFile = Debug.checkDefined(commandLine.options.configFile);
3006                Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath);
3007                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3008            }
3009            else {
3010                // An absolute path pointing to the containing directory of the config file
3011                const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
3012                sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
3013                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3014                if (sourceFile === undefined) {
3015                    projectReferenceRedirects.set(sourceFilePath, false);
3016                    return undefined;
3017                }
3018                commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
3019            }
3020            sourceFile.fileName = refPath;
3021            sourceFile.path = sourceFilePath;
3022            sourceFile.resolvedPath = sourceFilePath;
3023            sourceFile.originalFileName = refPath;
3024
3025            const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
3026            projectReferenceRedirects.set(sourceFilePath, resolvedRef);
3027            if (commandLine.projectReferences) {
3028                resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile);
3029            }
3030            return resolvedRef;
3031        }
3032
3033        function verifyCompilerOptions() {
3034            if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
3035                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
3036            }
3037
3038            if (options.isolatedModules) {
3039                if (options.out) {
3040                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
3041                }
3042
3043                if (options.outFile) {
3044                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules");
3045                }
3046            }
3047
3048            if (options.inlineSourceMap) {
3049                if (options.sourceMap) {
3050                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap");
3051                }
3052                if (options.mapRoot) {
3053                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap");
3054                }
3055            }
3056
3057            if (options.composite) {
3058                if (options.declaration === false) {
3059                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration");
3060                }
3061                if (options.incremental === false) {
3062                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration");
3063                }
3064            }
3065
3066            const outputFile = outFile(options);
3067            if (options.tsBuildInfoFile) {
3068                if (!isIncrementalCompilation(options)) {
3069                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite");
3070                }
3071            }
3072            else if (options.incremental && !outputFile && !options.configFilePath) {
3073                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified));
3074            }
3075
3076            verifyProjectReferences();
3077
3078            // List of collected files is complete; validate exhautiveness if this is a project with a file list
3079            if (options.composite) {
3080                const rootPaths = new Set(rootNames.map(toPath));
3081                for (const file of files) {
3082                    // Ignore file that is not emitted
3083                    if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) {
3084                        addProgramDiagnosticExplainingFile(
3085                            file,
3086                            Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
3087                            [file.fileName, options.configFilePath || ""]
3088                        );
3089                    }
3090                }
3091            }
3092
3093            if (options.paths) {
3094                for (const key in options.paths) {
3095                    if (!hasProperty(options.paths, key)) {
3096                        continue;
3097                    }
3098                    if (!hasZeroOrOneAsteriskCharacter(key)) {
3099                        createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key);
3100                    }
3101                    if (isArray(options.paths[key])) {
3102                        const len = options.paths[key].length;
3103                        if (len === 0) {
3104                            createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key);
3105                        }
3106                        for (let i = 0; i < len; i++) {
3107                            const subst = options.paths[key][i];
3108                            const typeOfSubst = typeof subst;
3109                            if (typeOfSubst === "string") {
3110                                if (!hasZeroOrOneAsteriskCharacter(subst)) {
3111                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key);
3112                                }
3113                                if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) {
3114                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash);
3115                                }
3116                            }
3117                            else {
3118                                createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst);
3119                            }
3120                        }
3121                    }
3122                    else {
3123                        createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key);
3124                    }
3125                }
3126            }
3127
3128            if (!options.sourceMap && !options.inlineSourceMap) {
3129                if (options.inlineSources) {
3130                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources");
3131                }
3132                if (options.sourceRoot) {
3133                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot");
3134                }
3135            }
3136
3137            if (options.out && options.outFile) {
3138                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile");
3139            }
3140
3141            if (options.mapRoot && !(options.sourceMap || options.declarationMap)) {
3142                // Error to specify --mapRoot without --sourcemap
3143                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap");
3144            }
3145
3146            if (options.declarationDir) {
3147                if (!getEmitDeclarations(options)) {
3148                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite");
3149                }
3150                if (outputFile) {
3151                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile");
3152                }
3153            }
3154
3155            if (options.declarationMap && !getEmitDeclarations(options)) {
3156                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite");
3157            }
3158
3159            if (options.lib && options.noLib) {
3160                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
3161            }
3162
3163            if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) {
3164                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
3165            }
3166
3167            const languageVersion = options.target || ScriptTarget.ES3;
3168
3169            const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
3170            if (options.isolatedModules) {
3171                if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
3172                    createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
3173                }
3174
3175                if (options.preserveConstEnums === false) {
3176                    createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules");
3177                }
3178
3179                const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
3180                if (firstNonExternalModuleSourceFile) {
3181                    const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
3182                    programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length,
3183                        Diagnostics._0_cannot_be_compiled_under_isolatedModules_because_it_is_considered_a_global_script_file_Add_an_import_export_or_an_empty_export_statement_to_make_it_a_module, getBaseFileName(firstNonExternalModuleSourceFile.fileName)));
3184                }
3185            }
3186            else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
3187                // We cannot use createDiagnosticFromNode because nodes do not have parents yet
3188                const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3189                programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
3190            }
3191
3192            // Cannot specify module gen that isn't amd or system with --out
3193            if (outputFile && !options.emitDeclarationOnly) {
3194                if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
3195                    createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module");
3196                }
3197                else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) {
3198                    const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3199                    programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_compile_modules_using_option_0_unless_the_module_flag_is_amd_or_system, options.out ? "out" : "outFile"));
3200                }
3201            }
3202
3203            if (options.resolveJsonModule) {
3204                if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
3205                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
3206                }
3207                // Any emit other than common js, amd, es2015 or esnext is error
3208                else if (!hasJsonModuleEmitEnabled(options)) {
3209                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module");
3210                }
3211            }
3212
3213            // there has to be common source directory if user specified --outdir || --sourceRoot
3214            // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
3215            if (options.outDir || // there is --outDir specified
3216                options.sourceRoot || // there is --sourceRoot specified
3217                options.mapRoot) { // there is --mapRoot specified
3218
3219                // Precalculate and cache the common source directory
3220                const dir = getCommonSourceDirectory();
3221
3222                // If we failed to find a good common directory, but outDir is specified and at least one of our files is on a windows drive/URL/other resource, add a failure
3223                if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) {
3224                    createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir");
3225                }
3226            }
3227
3228            if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) {
3229                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields");
3230            }
3231
3232            if (options.checkJs && !getAllowJSCompilerOption(options)) {
3233                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs"));
3234            }
3235
3236            if (options.emitDeclarationOnly) {
3237                if (!getEmitDeclarations(options)) {
3238                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite");
3239                }
3240
3241                if (options.noEmit) {
3242                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit");
3243                }
3244            }
3245
3246            if (options.emitDecoratorMetadata &&
3247                !options.experimentalDecorators) {
3248                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
3249            }
3250
3251            if (options.jsxFactory) {
3252                if (options.reactNamespace) {
3253                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
3254                }
3255                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3256                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
3257                }
3258                if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
3259                    createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
3260                }
3261            }
3262            else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
3263                createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
3264            }
3265
3266            if (options.jsxFragmentFactory) {
3267                if (!options.jsxFactory) {
3268                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
3269                }
3270                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3271                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
3272                }
3273                if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
3274                    createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
3275                }
3276            }
3277
3278            if (options.reactNamespace) {
3279                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3280                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx));
3281                }
3282            }
3283
3284            if (options.jsxImportSource) {
3285                if (options.jsx === JsxEmit.React) {
3286                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx));
3287                }
3288            }
3289
3290            // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
3291            if (!options.noEmit && !options.suppressOutputPathCheck) {
3292                const emitHost = getEmitHost();
3293                const emitFilesSeen = new Set<string>();
3294                forEachEmittedFile(emitHost, (emitFileNames) => {
3295                    if (!options.emitDeclarationOnly) {
3296                        verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
3297                    }
3298                    verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
3299                });
3300            }
3301
3302            // Verify that all the emit files are unique and don't overwrite input files
3303            function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) {
3304                if (emitFileName) {
3305                    const emitFilePath = toPath(emitFileName);
3306                    // Report error if the output overwrites input file
3307                    if (filesByName.has(emitFilePath)) {
3308                        let chain: DiagnosticMessageChain | undefined;
3309                        if (!options.configFilePath) {
3310                            // The program is from either an inferred project or an external project
3311                            chain = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig);
3312                        }
3313                        chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName);
3314                        blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain));
3315                    }
3316
3317                    const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath;
3318                    // Report error if multiple files write into same file
3319                    if (emitFilesSeen.has(emitFileKey)) {
3320                        // Already seen the same emit file - report error
3321                        blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName));
3322                    }
3323                    else {
3324                        emitFilesSeen.add(emitFileKey);
3325                    }
3326                }
3327            }
3328        }
3329
3330        function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic {
3331            let fileIncludeReasons: DiagnosticMessageChain[] | undefined;
3332            let relatedInfo: Diagnostic[] | undefined;
3333            let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined;
3334            if (file) fileReasons.get(file.path)?.forEach(processReason);
3335            if (fileProcessingReason) processReason(fileProcessingReason);
3336            // If we have location and there is only one reason file is in which is the location, dont add details for file include
3337            if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined;
3338            const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason);
3339            const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon);
3340            const redirectInfo = file && explainIfFileIsRedirect(file);
3341            const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray);
3342            return location && isReferenceFileLocation(location) ?
3343                createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) :
3344                createCompilerDiagnosticFromMessageChain(chain, relatedInfo);
3345
3346            function processReason(reason: FileIncludeReason) {
3347                (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason));
3348                if (!locationReason && isReferencedFile(reason)) {
3349                    // Report error at first reference file or file currently in processing and dont report in related information
3350                    locationReason = reason;
3351                }
3352                else if (locationReason !== reason) {
3353                    relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason));
3354                }
3355                // Remove fileProcessingReason if its already included in fileReasons of the program
3356                if (reason === fileProcessingReason) fileProcessingReason = undefined;
3357            }
3358        }
3359
3360        function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3361            (fileProcessingDiagnostics ||= []).push({
3362                kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic,
3363                file: file && file.path,
3364                fileProcessingReason,
3365                diagnostic,
3366                args
3367            });
3368        }
3369
3370        function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3371            programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args));
3372        }
3373
3374        function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined {
3375            if (isReferencedFile(reason)) {
3376                const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason);
3377                let message: DiagnosticMessage;
3378                switch (reason.kind) {
3379                    case FileIncludeKind.Import:
3380                        message = Diagnostics.File_is_included_via_import_here;
3381                        break;
3382                    case FileIncludeKind.ReferenceFile:
3383                        message = Diagnostics.File_is_included_via_reference_here;
3384                        break;
3385                    case FileIncludeKind.TypeReferenceDirective:
3386                        message = Diagnostics.File_is_included_via_type_library_reference_here;
3387                        break;
3388                    case FileIncludeKind.LibReferenceDirective:
3389                        message = Diagnostics.File_is_included_via_library_reference_here;
3390                        break;
3391                    default:
3392                        Debug.assertNever(reason);
3393                }
3394                return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic(
3395                    referenceLocation.file,
3396                    referenceLocation.pos,
3397                    referenceLocation.end - referenceLocation.pos,
3398                    message,
3399                ) : undefined;
3400            }
3401
3402            if (!options.configFile) return undefined;
3403            let configFileNode: Node | undefined;
3404            let message: DiagnosticMessage;
3405            switch (reason.kind) {
3406                case FileIncludeKind.RootFile:
3407                    if (!options.configFile.configFileSpecs) return undefined;
3408                    const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory);
3409                    const matchedByFiles = getMatchedFileSpec(program, fileName);
3410                    if (matchedByFiles) {
3411                        configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles);
3412                        message = Diagnostics.File_is_matched_by_files_list_specified_here;
3413                        break;
3414                    }
3415                    const matchedByInclude = getMatchedIncludeSpec(program, fileName);
3416                    // Could be additional files specified as roots
3417                    if (!matchedByInclude) return undefined;
3418                    configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude);
3419                    message = Diagnostics.File_is_matched_by_include_pattern_specified_here;
3420                    break;
3421                case FileIncludeKind.SourceFromProjectReference:
3422                case FileIncludeKind.OutputFromProjectReference:
3423                    const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]);
3424                    const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) =>
3425                        resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined
3426                    );
3427                    if (!referenceInfo) return undefined;
3428                    const { sourceFile, index } = referenceInfo;
3429                    const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"),
3430                        property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3431                    return referencesSyntax && referencesSyntax.elements.length > index ?
3432                        createDiagnosticForNodeInSourceFile(
3433                            sourceFile,
3434                            referencesSyntax.elements[index],
3435                            reason.kind === FileIncludeKind.OutputFromProjectReference ?
3436                                Diagnostics.File_is_output_from_referenced_project_specified_here :
3437                                Diagnostics.File_is_source_from_referenced_project_specified_here,
3438                        ) :
3439                        undefined;
3440                case FileIncludeKind.AutomaticTypeDirectiveFile:
3441                    if (!options.types) return undefined;
3442                    configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference);
3443                    message = Diagnostics.File_is_entry_point_of_type_library_specified_here;
3444                    break;
3445                case FileIncludeKind.LibFile:
3446                    if (reason.index !== undefined) {
3447                        configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]);
3448                        message = Diagnostics.File_is_library_specified_here;
3449                        break;
3450                    }
3451                    const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
3452                    configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
3453                    message = Diagnostics.File_is_default_library_for_target_specified_here;
3454                    break;
3455                default:
3456                    Debug.assertNever(reason);
3457            }
3458            return configFileNode && createDiagnosticForNodeInSourceFile(
3459                options.configFile,
3460                configFileNode,
3461                message,
3462            );
3463        }
3464
3465        function verifyProjectReferences() {
3466            const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
3467            forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
3468                const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
3469                const parentFile = parent && parent.sourceFile as JsonSourceFile;
3470                if (!resolvedRef) {
3471                    createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
3472                    return;
3473                }
3474                const options = resolvedRef.commandLine.options;
3475                if (!options.composite || options.noEmit) {
3476                    // ok to not have composite if the current program is container only
3477                    const inputs = parent ? parent.commandLine.fileNames : rootNames;
3478                    if (inputs.length) {
3479                        if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
3480                        if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
3481                    }
3482                }
3483                if (ref.prepend) {
3484                    const out = outFile(options);
3485                    if (out) {
3486                        if (!host.fileExists(out)) {
3487                            createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
3488                        }
3489                    }
3490                    else {
3491                        createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
3492                    }
3493                }
3494                if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
3495                    createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
3496                    hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
3497                }
3498            });
3499        }
3500
3501        function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) {
3502            let needCompilerDiagnostic = true;
3503            const pathsSyntax = getOptionPathsSyntax();
3504            for (const pathProp of pathsSyntax) {
3505                if (isObjectLiteralExpression(pathProp.initializer)) {
3506                    for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) {
3507                        const initializer = keyProps.initializer;
3508                        if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) {
3509                            programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2));
3510                            needCompilerDiagnostic = false;
3511                        }
3512                    }
3513                }
3514            }
3515
3516            if (needCompilerDiagnostic) {
3517                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3518            }
3519        }
3520
3521        function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) {
3522            let needCompilerDiagnostic = true;
3523            const pathsSyntax = getOptionPathsSyntax();
3524            for (const pathProp of pathsSyntax) {
3525                if (isObjectLiteralExpression(pathProp.initializer) &&
3526                    createOptionDiagnosticInObjectLiteralSyntax(
3527                        pathProp.initializer, onKey, key, /*key2*/ undefined,
3528                        message, arg0)) {
3529                    needCompilerDiagnostic = false;
3530                }
3531            }
3532            if (needCompilerDiagnostic) {
3533                programDiagnostics.add(createCompilerDiagnostic(message, arg0));
3534            }
3535        }
3536
3537        function getOptionsSyntaxByName(name: string) {
3538            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3539            return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
3540        }
3541
3542        function getOptionPathsSyntax() {
3543            return getOptionsSyntaxByName("paths") || emptyArray;
3544        }
3545
3546        function getOptionsSyntaxByValue(name: string, value: string) {
3547            const syntaxByName = getOptionsSyntaxByName(name);
3548            return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined);
3549        }
3550
3551        function getOptionsSyntaxByArrayElementValue(name: string, value: string) {
3552            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3553            return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value);
3554        }
3555
3556        function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) {
3557            createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
3558        }
3559
3560        function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
3561            createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
3562        }
3563
3564        function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
3565            const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"),
3566                property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3567            if (referencesSyntax && referencesSyntax.elements.length > index) {
3568                programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
3569            }
3570            else {
3571                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
3572            }
3573        }
3574
3575        function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
3576            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3577            const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
3578                !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);
3579
3580            if (needCompilerDiagnostic) {
3581                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3582            }
3583        }
3584
3585        function getCompilerOptionsObjectLiteralSyntax() {
3586            if (_compilerOptionsObjectLiteralSyntax === undefined) {
3587                _compilerOptionsObjectLiteralSyntax = false;
3588                const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile);
3589                if (jsonObjectLiteral) {
3590                    for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) {
3591                        if (isObjectLiteralExpression(prop.initializer)) {
3592                            _compilerOptionsObjectLiteralSyntax = prop.initializer;
3593                            break;
3594                        }
3595                    }
3596                }
3597            }
3598            return _compilerOptionsObjectLiteralSyntax || undefined;
3599        }
3600
3601        function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean {
3602            const props = getPropertyAssignment(objectLiteral, key1, key2);
3603            for (const prop of props) {
3604                programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
3605            }
3606            return !!props.length;
3607        }
3608
3609        function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
3610            hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
3611            programDiagnostics.add(diag);
3612        }
3613
3614        function isEmittedFile(file: string): boolean {
3615            if (options.noEmit) {
3616                return false;
3617            }
3618
3619            // If this is source file, its not emitted file
3620            const filePath = toPath(file);
3621            if (getSourceFileByPath(filePath)) {
3622                return false;
3623            }
3624
3625            // If options have --outFile or --out just check that
3626            const out = outFile(options);
3627            if (out) {
3628                return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts);
3629            }
3630
3631            // If declarationDir is specified, return if its a file in that directory
3632            if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) {
3633                return true;
3634            }
3635
3636            // If --outDir, check if file is in that directory
3637            if (options.outDir) {
3638                return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
3639            }
3640
3641            if (fileExtensionIsOneOf(filePath, supportedJSExtensions) || fileExtensionIs(filePath, Extension.Dts)) {
3642                // Otherwise just check if sourceFile with the name exists
3643                const filePathWithoutExtension = removeFileExtension(filePath);
3644                return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) ||
3645                    !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path);
3646            }
3647            return false;
3648        }
3649
3650        function isSameFile(file1: string, file2: string) {
3651            return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
3652        }
3653
3654        function getSymlinkCache(): SymlinkCache {
3655            if (host.getSymlinkCache) {
3656                return host.getSymlinkCache();
3657            }
3658            return symlinks || (symlinks = discoverProbableSymlinks(
3659                files,
3660                getCanonicalFileName,
3661                host.getCurrentDirectory()));
3662        }
3663    }
3664
3665    interface HostForUseSourceOfProjectReferenceRedirect {
3666        compilerHost: CompilerHost;
3667        getSymlinkCache: () => SymlinkCache;
3668        useSourceOfProjectReferenceRedirect: boolean;
3669        toPath(fileName: string): Path;
3670        getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
3671        getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
3672        forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
3673    }
3674
3675    function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) {
3676        let setOfDeclarationDirectories: Set<Path> | undefined;
3677        const originalFileExists = host.compilerHost.fileExists;
3678        const originalDirectoryExists = host.compilerHost.directoryExists;
3679        const originalGetDirectories = host.compilerHost.getDirectories;
3680        const originalRealpath = host.compilerHost.realpath;
3681
3682        if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists };
3683
3684        host.compilerHost.fileExists = fileExists;
3685
3686        let directoryExists;
3687        if (originalDirectoryExists) {
3688            // This implementation of directoryExists checks if the directory being requested is
3689            // directory of .d.ts file for the referenced Project.
3690            // If it is it returns true irrespective of whether that directory exists on host
3691            directoryExists = host.compilerHost.directoryExists = path => {
3692                if (originalDirectoryExists.call(host.compilerHost, path)) {
3693                    handleDirectoryCouldBeSymlink(path);
3694                    return true;
3695                }
3696
3697                if (!host.getResolvedProjectReferences()) return false;
3698
3699                if (!setOfDeclarationDirectories) {
3700                    setOfDeclarationDirectories = new Set();
3701                    host.forEachResolvedProjectReference(ref => {
3702                        const out = outFile(ref.commandLine.options);
3703                        if (out) {
3704                            setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out)));
3705                        }
3706                        else {
3707                            // Set declaration's in different locations only, if they are next to source the directory present doesnt change
3708                            const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir;
3709                            if (declarationDir) {
3710                                setOfDeclarationDirectories!.add(host.toPath(declarationDir));
3711                            }
3712                        }
3713                    });
3714                }
3715
3716                return fileOrDirectoryExistsUsingSource(path, /*isFile*/ false);
3717            };
3718        }
3719
3720        if (originalGetDirectories) {
3721            // Call getDirectories only if directory actually present on the host
3722            // This is needed to ensure that we arent getting directories that we fake about presence for
3723            host.compilerHost.getDirectories = path =>
3724                !host.getResolvedProjectReferences() || (originalDirectoryExists && originalDirectoryExists.call(host.compilerHost, path)) ?
3725                    originalGetDirectories.call(host.compilerHost, path) :
3726                    [];
3727        }
3728
3729        // This is something we keep for life time of the host
3730        if (originalRealpath) {
3731            host.compilerHost.realpath = s =>
3732                host.getSymlinkCache().getSymlinkedFiles()?.get(host.toPath(s)) ||
3733                originalRealpath.call(host.compilerHost, s);
3734        }
3735
3736        return { onProgramCreateComplete, fileExists, directoryExists };
3737
3738        function onProgramCreateComplete() {
3739            host.compilerHost.fileExists = originalFileExists;
3740            host.compilerHost.directoryExists = originalDirectoryExists;
3741            host.compilerHost.getDirectories = originalGetDirectories;
3742            // DO not revert realpath as it could be used later
3743        }
3744
3745        // This implementation of fileExists checks if the file being requested is
3746        // .d.ts file for the referenced Project.
3747        // If it is it returns true irrespective of whether that file exists on host
3748        function fileExists(file: string) {
3749            if (originalFileExists.call(host.compilerHost, file)) return true;
3750            if (!host.getResolvedProjectReferences()) return false;
3751            if (!isDeclarationFileName(file)) return false;
3752
3753            // Project references go to source file instead of .d.ts file
3754            return fileOrDirectoryExistsUsingSource(file, /*isFile*/ true);
3755        }
3756
3757        function fileExistsIfProjectReferenceDts(file: string) {
3758            const source = host.getSourceOfProjectReferenceRedirect(file);
3759            return source !== undefined ?
3760                isString(source) ? originalFileExists.call(host.compilerHost, source) : true :
3761                undefined;
3762        }
3763
3764        function directoryExistsIfProjectReferenceDeclDir(dir: string) {
3765            const dirPath = host.toPath(dir);
3766            const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
3767            return forEachKey(
3768                setOfDeclarationDirectories!,
3769                declDirPath => dirPath === declDirPath ||
3770                    // Any parent directory of declaration dir
3771                    startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) ||
3772                    // Any directory inside declaration dir
3773                    startsWith(dirPath, `${declDirPath}/`)
3774            );
3775        }
3776
3777        function handleDirectoryCouldBeSymlink(directory: string) {
3778            if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return;
3779
3780            // Because we already watch node_modules, handle symlinks in there
3781            if (!originalRealpath || !stringContains(directory, nodeModulesPathPart)) return;
3782            const symlinkCache = host.getSymlinkCache();
3783            const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory));
3784            if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return;
3785
3786            const real = normalizePath(originalRealpath.call(host.compilerHost, directory));
3787            let realPath: Path;
3788            if (real === directory ||
3789                (realPath = ensureTrailingDirectorySeparator(host.toPath(real))) === directoryPath) {
3790                // not symlinked
3791                symlinkCache.setSymlinkedDirectory(directoryPath, false);
3792                return;
3793            }
3794
3795            symlinkCache.setSymlinkedDirectory(directory, {
3796                real: ensureTrailingDirectorySeparator(real),
3797                realPath
3798            });
3799        }
3800
3801        function fileOrDirectoryExistsUsingSource(fileOrDirectory: string, isFile: boolean): boolean {
3802            const fileOrDirectoryExistsUsingSource = isFile ?
3803                (file: string) => fileExistsIfProjectReferenceDts(file) :
3804                (dir: string) => directoryExistsIfProjectReferenceDeclDir(dir);
3805            // Check current directory or file
3806            const result = fileOrDirectoryExistsUsingSource(fileOrDirectory);
3807            if (result !== undefined) return result;
3808
3809            const symlinkCache = host.getSymlinkCache();
3810            const symlinkedDirectories = symlinkCache.getSymlinkedDirectories();
3811            if (!symlinkedDirectories) return false;
3812            const fileOrDirectoryPath = host.toPath(fileOrDirectory);
3813            if (!stringContains(fileOrDirectoryPath, nodeModulesPathPart)) return false;
3814            if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true;
3815
3816            // If it contains node_modules check if its one of the symlinked path we know of
3817            return firstDefinedIterator(
3818                symlinkedDirectories.entries(),
3819                ([directoryPath, symlinkedDirectory]) => {
3820                    if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined;
3821                    const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath));
3822                    if (isFile && result) {
3823                        // Store the real path for the file'
3824                        const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory());
3825                        symlinkCache.setSymlinkedFile(
3826                            fileOrDirectoryPath,
3827                            `${symlinkedDirectory.real}${absolutePath.replace(new RegExp(directoryPath, "i"), "")}`
3828                        );
3829                    }
3830                    return result;
3831                }
3832            ) || false;
3833        }
3834    }
3835
3836    /*@internal*/
3837    export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3838
3839    /*@internal*/
3840    export function handleNoEmitOptions<T extends BuilderProgram>(
3841        program: Program | T,
3842        sourceFile: SourceFile | undefined,
3843        writeFile: WriteFileCallback | undefined,
3844        cancellationToken: CancellationToken | undefined
3845    ): EmitResult | undefined {
3846        const options = program.getCompilerOptions();
3847        if (options.noEmit) {
3848            // Cache the semantic diagnostics
3849            program.getSemanticDiagnostics(sourceFile, cancellationToken);
3850            return sourceFile || outFile(options) ?
3851                emitSkippedWithNoDiagnostics :
3852                program.emitBuildInfo(writeFile, cancellationToken);
3853        }
3854
3855        // If the noEmitOnError flag is set, then check if we have any errors so far.  If so,
3856        // immediately bail out.  Note that we pass 'undefined' for 'sourceFile' so that we
3857        // get any preEmit diagnostics, not just the ones
3858        if (!options.noEmitOnError) return undefined;
3859        let diagnostics: readonly Diagnostic[] = [
3860            ...program.getOptionsDiagnostics(cancellationToken),
3861            ...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
3862            ...program.getGlobalDiagnostics(cancellationToken),
3863            ...program.getSemanticDiagnostics(sourceFile, cancellationToken)
3864        ];
3865
3866        if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
3867            diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
3868        }
3869
3870        if (!diagnostics.length) return undefined;
3871        let emittedFiles: string[] | undefined;
3872        if (!sourceFile && !outFile(options)) {
3873            const emitResult = program.emitBuildInfo(writeFile, cancellationToken);
3874            if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics];
3875            emittedFiles = emitResult.emittedFiles;
3876        }
3877        return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true };
3878    }
3879
3880    /*@internal*/
3881    export function filterSemanticDiagnotics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] {
3882        return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]);
3883    }
3884
3885    /*@internal*/
3886    interface CompilerHostLike {
3887        useCaseSensitiveFileNames(): boolean;
3888        getCurrentDirectory(): string;
3889        fileExists(fileName: string): boolean;
3890        readFile(fileName: string): string | undefined;
3891        readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
3892        trace?(s: string): void;
3893        onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter;
3894    }
3895
3896    /* @internal */
3897    export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost {
3898        return {
3899            fileExists: f => directoryStructureHost.fileExists(f),
3900            readDirectory(root, extensions, excludes, includes, depth) {
3901                Debug.assertIsDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
3902                return directoryStructureHost.readDirectory(root, extensions, excludes, includes, depth);
3903            },
3904            readFile: f => directoryStructureHost.readFile(f),
3905            useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(),
3906            getCurrentDirectory: () => host.getCurrentDirectory(),
3907            onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined,
3908            trace: host.trace ? (s) => host.trace!(s) : undefined
3909        };
3910    }
3911
3912    // For backward compatibility
3913    /** @deprecated */ export interface ResolveProjectReferencePathHost {
3914        fileExists(fileName: string): boolean;
3915    }
3916
3917    /* @internal */
3918    export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) {
3919        if (!projectReferences) return emptyArray;
3920        let nodes: InputFiles[] | undefined;
3921        for (let i = 0; i < projectReferences.length; i++) {
3922            const ref = projectReferences[i];
3923            const resolvedRefOpts = getCommandLine(ref, i);
3924            if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) {
3925                const out = outFile(resolvedRefOpts.options);
3926                // Upstream project didn't have outFile set -- skip (error will have been issued earlier)
3927                if (!out) continue;
3928
3929                const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true);
3930                const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath);
3931                (nodes || (nodes = [])).push(node);
3932            }
3933        }
3934        return nodes || emptyArray;
3935    }
3936    /**
3937     * Returns the target config filename of a project reference.
3938     * Note: The file might not exist.
3939     */
3940    export function resolveProjectReferencePath(ref: ProjectReference): ResolvedConfigFileName;
3941    /** @deprecated */ export function resolveProjectReferencePath(host: ResolveProjectReferencePathHost, ref: ProjectReference): ResolvedConfigFileName;
3942    export function resolveProjectReferencePath(hostOrRef: ResolveProjectReferencePathHost | ProjectReference, ref?: ProjectReference): ResolvedConfigFileName {
3943        const passedInRef = ref ? ref : hostOrRef as ProjectReference;
3944        return resolveConfigFileProjectName(passedInRef.path);
3945    }
3946
3947    /* @internal */
3948    /**
3949     * Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
3950     * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
3951     * This returns a diagnostic even if the module will be an untyped module.
3952     */
3953    export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
3954        switch (extension) {
3955            case Extension.Ts:
3956            case Extension.Dts:
3957            case Extension.Ets:
3958                // These are always allowed.
3959                return undefined;
3960            case Extension.Tsx:
3961                return needJsx();
3962            case Extension.Jsx:
3963                return needJsx() || needAllowJs();
3964            case Extension.Js:
3965                return needAllowJs();
3966            case Extension.Json:
3967                return needResolveJsonModule();
3968        }
3969
3970        function needJsx() {
3971            return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
3972        }
3973        function needAllowJs() {
3974            return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
3975        }
3976        function needResolveJsonModule() {
3977            return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used;
3978        }
3979    }
3980
3981    function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
3982        const res = imports.map(i => i.text);
3983        for (const aug of moduleAugmentations) {
3984            if (aug.kind === SyntaxKind.StringLiteral) {
3985                res.push(aug.text);
3986            }
3987            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
3988        }
3989        return res;
3990    }
3991
3992    /* @internal */
3993    export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike {
3994        if (index < imports.length) return imports[index];
3995        let augIndex = imports.length;
3996        for (const aug of moduleAugmentations) {
3997            if (aug.kind === SyntaxKind.StringLiteral) {
3998                if (index === augIndex) return aug;
3999                augIndex++;
4000            }
4001            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
4002        }
4003        Debug.fail("should never ask for module name at index higher than possible module name");
4004    }
4005}
4006