• 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 or oh_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 or oh_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 or oh_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            options: _options
921        });
922
923        tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
924        const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
925        tracing?.pop();
926        // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks
927        // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`.
928        let structureIsReused: StructureIsReused;
929        tracing?.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {});
930        structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const
931        tracing?.pop();
932        if (structureIsReused !== StructureIsReused.Completely) {
933            processingDefaultLibFiles = [];
934            processingOtherFiles = [];
935
936            if (projectReferences) {
937                if (!resolvedProjectReferences) {
938                    resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
939                }
940                if (rootNames.length) {
941                    resolvedProjectReferences?.forEach((parsedRef, index) => {
942                        if (!parsedRef) return;
943                        const out = outFile(parsedRef.commandLine.options);
944                        if (useSourceOfProjectReferenceRedirect) {
945                            if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
946                                for (const fileName of parsedRef.commandLine.fileNames) {
947                                    processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index });
948                                }
949                            }
950                        }
951                        else {
952                            if (out) {
953                                processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index });
954                            }
955                            else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) {
956                                const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames()));
957                                for (const fileName of parsedRef.commandLine.fileNames) {
958                                    if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) {
959                                        processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index });
960                                    }
961                                }
962                            }
963                        }
964                    });
965                }
966            }
967
968            tracing?.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length });
969            forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index }));
970            tracing?.pop();
971
972            // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders
973            const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray;
974
975            if (typeReferences.length) {
976                tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length });
977                // This containingFilename needs to match with the one used in managed-side
978                const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory();
979                const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
980                const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
981                for (let i = 0; i < typeReferences.length; i++) {
982                    processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
983                }
984                tracing?.pop();
985            }
986
987            // Do not process the default library if:
988            //  - The '--noLib' flag is used.
989            //  - A 'no-default-lib' reference comment is encountered in
990            //      processing the root files.
991            if (rootNames.length && !skipDefaultLib) {
992                // If '--lib' is not specified, include default library file according to '--target'
993                // otherwise, using options specified in '--lib' instead of '--target' default library file
994                const defaultLibraryFileName = getDefaultLibraryFileName();
995                if (!options.lib && defaultLibraryFileName) {
996                    processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile });
997                }
998                else {
999                    forEach(options.lib, (libFileName, index) => {
1000                        processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index });
1001                    });
1002
1003                    const etsComponentsLib = options.ets?.libs ?? [];
1004                    if (etsComponentsLib.length) {
1005                        forEach(etsComponentsLib, libFileName => {
1006                            processRootFile(combinePaths(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile });
1007                        });
1008                    }
1009                }
1010            }
1011
1012            missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined));
1013            files = stableSort(processingDefaultLibFiles, compareDefaultLibFiles).concat(processingOtherFiles);
1014            processingDefaultLibFiles = undefined;
1015            processingOtherFiles = undefined;
1016        }
1017
1018        Debug.assert(!!missingFilePaths);
1019
1020        // Release any files we have acquired in the old program but are
1021        // not part of the new program.
1022        if (oldProgram && host.onReleaseOldSourceFile) {
1023            const oldSourceFiles = oldProgram.getSourceFiles();
1024            for (const oldSourceFile of oldSourceFiles) {
1025                const newFile = getSourceFileByPath(oldSourceFile.resolvedPath);
1026                if (shouldCreateNewSourceFile || !newFile ||
1027                    // old file wasn't redirect but new file is
1028                    (oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) {
1029                    host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path));
1030                }
1031            }
1032            oldProgram.forEachResolvedProjectReference(resolvedProjectReference => {
1033                if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) {
1034                    host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false);
1035                }
1036            });
1037        }
1038
1039        // unconditionally set oldProgram to undefined to prevent it from being captured in closure
1040        oldProgram = undefined;
1041
1042        const program: Program = {
1043            getRootFileNames: () => rootNames,
1044            getSourceFile,
1045            getSourceFileByPath,
1046            getSourceFiles: () => files,
1047            getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217
1048            getFilesByNameMap: () => filesByName,
1049            getCompilerOptions: () => options,
1050            getSyntacticDiagnostics,
1051            getOptionsDiagnostics,
1052            getGlobalDiagnostics,
1053            getSemanticDiagnostics,
1054            getCachedSemanticDiagnostics,
1055            getSuggestionDiagnostics,
1056            getDeclarationDiagnostics,
1057            getBindAndCheckDiagnostics,
1058            getProgramDiagnostics,
1059            getTypeChecker,
1060            getEtsLibSFromProgram,
1061            getClassifiableNames,
1062            getDiagnosticsProducingTypeChecker,
1063            getCommonSourceDirectory,
1064            emit,
1065            getCurrentDirectory: () => currentDirectory,
1066            getNodeCount: () => getDiagnosticsProducingTypeChecker().getNodeCount(),
1067            getIdentifierCount: () => getDiagnosticsProducingTypeChecker().getIdentifierCount(),
1068            getSymbolCount: () => getDiagnosticsProducingTypeChecker().getSymbolCount(),
1069            getTypeCatalog: () => getDiagnosticsProducingTypeChecker().getTypeCatalog(),
1070            getTypeCount: () => getDiagnosticsProducingTypeChecker().getTypeCount(),
1071            getInstantiationCount: () => getDiagnosticsProducingTypeChecker().getInstantiationCount(),
1072            getRelationCacheSizes: () => getDiagnosticsProducingTypeChecker().getRelationCacheSizes(),
1073            getFileProcessingDiagnostics: () => fileProcessingDiagnostics,
1074            getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives,
1075            isSourceFileFromExternalLibrary,
1076            isSourceFileDefaultLibrary,
1077            dropDiagnosticsProducingTypeChecker,
1078            getSourceFileFromReference,
1079            getLibFileFromReference,
1080            sourceFileToPackageName,
1081            redirectTargetsMap,
1082            isEmittedFile,
1083            getConfigFileParsingDiagnostics,
1084            getResolvedModuleWithFailedLookupLocationsFromCache,
1085            getProjectReferences,
1086            getResolvedProjectReferences,
1087            getProjectReferenceRedirect,
1088            getResolvedProjectReferenceToRedirect,
1089            getResolvedProjectReferenceByPath,
1090            forEachResolvedProjectReference,
1091            isSourceOfProjectReferenceRedirect,
1092            emitBuildInfo,
1093            fileExists,
1094            directoryExists,
1095            getSymlinkCache,
1096            realpath: host.realpath?.bind(host),
1097            useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
1098            getFileIncludeReasons: () => fileReasons,
1099            structureIsReused,
1100            getTagNameNeededCheckByFile: host.getTagNameNeededCheckByFile,
1101            getExpressionCheckedResultsByFile: host.getExpressionCheckedResultsByFile
1102        };
1103
1104        onProgramCreateComplete();
1105
1106        // Add file processingDiagnostics
1107        fileProcessingDiagnostics?.forEach(diagnostic => {
1108            switch (diagnostic.kind) {
1109                case FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic:
1110                    return programDiagnostics.add(createDiagnosticExplainingFile(diagnostic.file && getSourceFileByPath(diagnostic.file), diagnostic.fileProcessingReason, diagnostic.diagnostic, diagnostic.args || emptyArray));
1111                case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic:
1112                    const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation;
1113                    return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray));
1114                default:
1115                    Debug.assertNever(diagnostic);
1116            }
1117        });
1118
1119        verifyCompilerOptions();
1120        performance.mark("afterProgram");
1121        performance.measure("Program", "beforeProgram", "afterProgram");
1122        tracing?.pop();
1123
1124        return program;
1125
1126        function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] {
1127            if (!moduleNames.length) return emptyArray;
1128            const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory);
1129            const redirectedReference = getRedirectReferenceForResolution(containingFile);
1130            tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
1131            performance.mark("beforeResolveModule");
1132            const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference);
1133            performance.mark("afterResolveModule");
1134            performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
1135            tracing?.pop();
1136            return result;
1137        }
1138
1139        function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
1140            if (!typeDirectiveNames.length) return [];
1141            const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile;
1142            const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined;
1143            tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName });
1144            performance.mark("beforeResolveTypeReference");
1145            const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference);
1146            performance.mark("afterResolveTypeReference");
1147            performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference");
1148            tracing?.pop();
1149            return result;
1150        }
1151
1152        function getRedirectReferenceForResolution(file: SourceFile) {
1153            const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
1154            if (redirect || !isDeclarationFileName(file.originalFileName)) return redirect;
1155
1156            // The originalFileName could not be actual source file name if file found was d.ts from referecned project
1157            // 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
1158            const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.originalFileName, file.path);
1159            if (resultFromDts) return resultFromDts;
1160
1161            // If preserveSymlinks is true, module resolution wont jump the symlink
1162            // but the resolved real path may be the .d.ts from project reference
1163            // Note:: Currently we try the real path only if the
1164            // file is from node_modules or oh_modules to avoid having to run real path on all file paths
1165            if (!host.realpath || !options.preserveSymlinks || (!stringContains(file.originalFileName, nodeModulesPathPart) &&
1166              !stringContains(file.originalFileName, ohModulesPathPart))) return undefined;
1167            const realDeclarationFileName = host.realpath(file.originalFileName);
1168            const realDeclarationPath = toPath(realDeclarationFileName);
1169            return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationFileName, realDeclarationPath);
1170        }
1171
1172        function getRedirectReferenceForResolutionFromSourceOfProject(fileName: string, filePath: Path) {
1173            const source = getSourceOfProjectReferenceRedirect(fileName);
1174            if (isString(source)) return getResolvedProjectReferenceToRedirect(source);
1175            if (!source) return undefined;
1176            // Output of .d.ts file so return resolved ref that matches the out file name
1177            return forEachResolvedProjectReference(resolvedRef => {
1178                const out = outFile(resolvedRef.commandLine.options);
1179                if (!out) return undefined;
1180                return toPath(out) === filePath ? resolvedRef : undefined;
1181            });
1182        }
1183
1184        function compareDefaultLibFiles(a: SourceFile, b: SourceFile) {
1185            return compareValues(getDefaultLibFilePriority(a), getDefaultLibFilePriority(b));
1186        }
1187
1188        function getDefaultLibFilePriority(a: SourceFile) {
1189            if (containsPath(defaultLibraryPath, a.fileName, /*ignoreCase*/ false)) {
1190                const basename = getBaseFileName(a.fileName);
1191                if (basename === "lib.d.ts" || basename === "lib.es6.d.ts") return 0;
1192                const name = removeSuffix(removePrefix(basename, "lib."), ".d.ts");
1193                const index = libs.indexOf(name);
1194                if (index !== -1) return index + 1;
1195            }
1196            return libs.length + 2;
1197        }
1198
1199        function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
1200            return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache);
1201        }
1202
1203        function toPath(fileName: string): Path {
1204            return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
1205        }
1206
1207        function getCommonSourceDirectory() {
1208            if (commonSourceDirectory === undefined) {
1209                const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program));
1210                commonSourceDirectory = ts.getCommonSourceDirectory(
1211                    options,
1212                    () => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName),
1213                    currentDirectory,
1214                    getCanonicalFileName,
1215                    commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory)
1216                );
1217            }
1218            return commonSourceDirectory;
1219        }
1220
1221        function getClassifiableNames() {
1222            if (!classifiableNames) {
1223                // Initialize a checker so that all our files are bound.
1224                getTypeChecker();
1225                classifiableNames = new Set();
1226
1227                for (const sourceFile of files) {
1228                    sourceFile.classifiableNames?.forEach(value => classifiableNames.add(value));
1229                }
1230            }
1231
1232            return classifiableNames;
1233        }
1234
1235        function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly ResolvedModuleFull[] {
1236            if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
1237                // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
1238                // the best we can do is fallback to the default logic.
1239                return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined);
1240            }
1241
1242            const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
1243            if (oldSourceFile !== file && file.resolvedModules) {
1244                // `file` was created for the new program.
1245                //
1246                // We only set `file.resolvedModules` via work from the current function,
1247                // so it is defined iff we already called the current function on `file`.
1248                // That call happened no later than the creation of the `file` object,
1249                // which per above occurred during the current program creation.
1250                // Since we assume the filesystem does not change during program creation,
1251                // it is safe to reuse resolutions from the earlier call.
1252                const result: ResolvedModuleFull[] = [];
1253                for (const moduleName of moduleNames) {
1254                    const resolvedModule = file.resolvedModules.get(moduleName)!;
1255                    result.push(resolvedModule);
1256                }
1257                return result;
1258            }
1259            // At this point, we know at least one of the following hold:
1260            // - file has local declarations for ambient modules
1261            // - old program state is available
1262            // With this information, we can infer some module resolutions without performing resolution.
1263
1264            /** An ordered list of module names for which we cannot recover the resolution. */
1265            let unknownModuleNames: string[] | undefined;
1266            /**
1267             * The indexing of elements in this list matches that of `moduleNames`.
1268             *
1269             * Before combining results, result[i] is in one of the following states:
1270             * * undefined: needs to be recomputed,
1271             * * predictedToResolveToAmbientModuleMarker: known to be an ambient module.
1272             * Needs to be reset to undefined before returning,
1273             * * ResolvedModuleFull instance: can be reused.
1274             */
1275            let result: ResolvedModuleFull[] | undefined;
1276            let reusedNames: string[] | undefined;
1277            /** A transient placeholder used to mark predicted resolution in the result list. */
1278            const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = <any>{};
1279
1280            for (let i = 0; i < moduleNames.length; i++) {
1281                const moduleName = moduleNames[i];
1282                // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
1283                if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
1284                    const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName);
1285                    if (oldResolvedModule) {
1286                        if (isTraceEnabled(options, host)) {
1287                            trace(host, Diagnostics.Reusing_resolution_of_module_0_to_file_1_from_old_program, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
1288                        }
1289                        (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule;
1290                        (reusedNames || (reusedNames = [])).push(moduleName);
1291                        continue;
1292                    }
1293                }
1294                // We know moduleName resolves to an ambient module provided that moduleName:
1295                // - is in the list of ambient modules locally declared in the current source file.
1296                // - resolved to an ambient module in the old program whose declaration is in an unmodified file
1297                //   (so the same module declaration will land in the new program)
1298                let resolvesToAmbientModuleInNonModifiedFile = false;
1299                if (contains(file.ambientModuleNames, moduleName)) {
1300                    resolvesToAmbientModuleInNonModifiedFile = true;
1301                    if (isTraceEnabled(options, host)) {
1302                        trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory));
1303                    }
1304                }
1305                else {
1306                    resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
1307                }
1308
1309                if (resolvesToAmbientModuleInNonModifiedFile) {
1310                    (result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker;
1311                }
1312                else {
1313                    // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result.
1314                    (unknownModuleNames || (unknownModuleNames = [])).push(moduleName);
1315                }
1316            }
1317
1318            const resolutions = unknownModuleNames && unknownModuleNames.length
1319                ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames)
1320                : emptyArray;
1321
1322            // Combine results of resolutions and predicted results
1323            if (!result) {
1324                // There were no unresolved/ambient resolutions.
1325                Debug.assert(resolutions.length === moduleNames.length);
1326                return resolutions;
1327            }
1328
1329            let j = 0;
1330            for (let i = 0; i < result.length; i++) {
1331                if (result[i]) {
1332                    // `result[i]` is either a `ResolvedModuleFull` or a marker.
1333                    // If it is the former, we can leave it as is.
1334                    if (result[i] === predictedToResolveToAmbientModuleMarker) {
1335                        result[i] = undefined!; // TODO: GH#18217
1336                    }
1337                }
1338                else {
1339                    result[i] = resolutions[j];
1340                    j++;
1341                }
1342            }
1343            Debug.assert(j === resolutions.length);
1344
1345            return result;
1346
1347            // If we change our policy of rechecking failed lookups on each program create,
1348            // we should adjust the value returned here.
1349            function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
1350                const resolutionToFile = getResolvedModule(oldSourceFile, moduleName);
1351                const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
1352                if (resolutionToFile && resolvedFile) {
1353                    // In the old program, we resolved to an ambient module that was in the same
1354                    //   place as we expected to find an actual module file.
1355                    // We actually need to return 'false' here even though this seems like a 'true' case
1356                    //   because the normal module resolution algorithm will find this anyway.
1357                    return false;
1358                }
1359
1360                // at least one of declarations should come from non-modified source file
1361                const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
1362
1363                if (!unmodifiedFile) {
1364                    return false;
1365                }
1366
1367                if (isTraceEnabled(options, host)) {
1368                    trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
1369                }
1370                return true;
1371            }
1372        }
1373
1374        function canReuseProjectReferences(): boolean {
1375            return !forEachProjectReference(
1376                oldProgram!.getProjectReferences(),
1377                oldProgram!.getResolvedProjectReferences(),
1378                (oldResolvedRef, parent, index) => {
1379                    const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
1380                    const newResolvedRef = parseProjectReferenceConfigFile(newRef);
1381                    if (oldResolvedRef) {
1382                        // Resolved project reference has gone missing or changed
1383                        return !newResolvedRef || newResolvedRef.sourceFile !== oldResolvedRef.sourceFile;
1384                    }
1385                    else {
1386                        // A previously-unresolved reference may be resolved now
1387                        return newResolvedRef !== undefined;
1388                    }
1389                },
1390                (oldProjectReferences, parent) => {
1391                    // If array of references is changed, we cant resue old program
1392                    const newReferences = parent ? getResolvedProjectReferenceByPath(parent.sourceFile.path)!.commandLine.projectReferences : projectReferences;
1393                    return !arrayIsEqualTo(oldProjectReferences, newReferences, projectReferenceIsEqualTo);
1394                }
1395            );
1396        }
1397
1398        function tryReuseStructureFromOldProgram(): StructureIsReused {
1399            if (!oldProgram) {
1400                return StructureIsReused.Not;
1401            }
1402
1403            // check properties that can affect structure of the program or module resolution strategy
1404            // if any of these properties has changed - structure cannot be reused
1405            const oldOptions = oldProgram.getCompilerOptions();
1406            if (changesAffectModuleResolution(oldOptions, options)) {
1407                return StructureIsReused.Not;
1408            }
1409
1410            // there is an old program, check if we can reuse its structure
1411            const oldRootNames = oldProgram.getRootFileNames();
1412            if (!arrayIsEqualTo(oldRootNames, rootNames)) {
1413                return StructureIsReused.Not;
1414            }
1415
1416            if (!arrayIsEqualTo(options.types, oldOptions.types)) {
1417                return StructureIsReused.Not;
1418            }
1419
1420            // Check if any referenced project tsconfig files are different
1421            if (!canReuseProjectReferences()) {
1422                return StructureIsReused.Not;
1423            }
1424            if (projectReferences) {
1425                resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile);
1426            }
1427
1428            // check if program source files has changed in the way that can affect structure of the program
1429            const newSourceFiles: SourceFile[] = [];
1430            const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
1431            structureIsReused = StructureIsReused.Completely;
1432
1433            // If the missing file paths are now present, it can change the progam structure,
1434            // and hence cant reuse the structure.
1435            // This is same as how we dont reuse the structure if one of the file from old program is now missing
1436            if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) {
1437                return StructureIsReused.Not;
1438            }
1439
1440            const oldSourceFiles = oldProgram.getSourceFiles();
1441            const enum SeenPackageName { Exists, Modified }
1442            const seenPackageNames = new Map<string, SeenPackageName>();
1443
1444            for (const oldSourceFile of oldSourceFiles) {
1445                let newSourceFile = host.getSourceFileByPath
1446                    ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
1447                    : host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
1448
1449                if (!newSourceFile) {
1450                    return StructureIsReused.Not;
1451                }
1452
1453                Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
1454
1455                let fileChanged: boolean;
1456                if (oldSourceFile.redirectInfo) {
1457                    // We got `newSourceFile` by path, so it is actually for the unredirected file.
1458                    // This lets us know if the unredirected file has changed. If it has we should break the redirect.
1459                    if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
1460                        // Underlying file has changed. Might not redirect anymore. Must rebuild program.
1461                        return StructureIsReused.Not;
1462                    }
1463                    fileChanged = false;
1464                    newSourceFile = oldSourceFile; // Use the redirect.
1465                }
1466                else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) {
1467                    // If a redirected-to source file changes, the redirect may be broken.
1468                    if (newSourceFile !== oldSourceFile) {
1469                        return StructureIsReused.Not;
1470                    }
1471                    fileChanged = false;
1472                }
1473                else {
1474                    fileChanged = newSourceFile !== oldSourceFile;
1475                }
1476
1477                // Since the project references havent changed, its right to set originalFileName and resolvedPath here
1478                newSourceFile.path = oldSourceFile.path;
1479                newSourceFile.originalFileName = oldSourceFile.originalFileName;
1480                newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
1481                newSourceFile.fileName = oldSourceFile.fileName;
1482
1483                const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
1484                if (packageName !== undefined) {
1485                    // If there are 2 different source files for the same package name and at least one of them changes,
1486                    // they might become redirects. So we must rebuild the program.
1487                    const prevKind = seenPackageNames.get(packageName);
1488                    const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
1489                    if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
1490                        return StructureIsReused.Not;
1491                    }
1492                    seenPackageNames.set(packageName, newKind);
1493                }
1494
1495                if (fileChanged) {
1496                    // The `newSourceFile` object was created for the new program.
1497
1498                    if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) {
1499                        // 'lib' references has changed. Matches behavior in changesAffectModuleResolution
1500                        return StructureIsReused.Not;
1501                    }
1502
1503                    if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
1504                        // value of no-default-lib has changed
1505                        // this will affect if default library is injected into the list of files
1506                        structureIsReused = StructureIsReused.SafeModules;
1507                    }
1508
1509                    // check tripleslash references
1510                    if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
1511                        // tripleslash references has changed
1512                        structureIsReused = StructureIsReused.SafeModules;
1513                    }
1514
1515                    // check imports and module augmentations
1516                    collectExternalModuleReferences(newSourceFile);
1517                    if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
1518                        // imports has changed
1519                        structureIsReused = StructureIsReused.SafeModules;
1520                    }
1521                    if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) {
1522                        // moduleAugmentations has changed
1523                        structureIsReused = StructureIsReused.SafeModules;
1524                    }
1525                    if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) {
1526                        // dynamicImport has changed
1527                        structureIsReused = StructureIsReused.SafeModules;
1528                    }
1529
1530                    if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) {
1531                        // 'types' references has changed
1532                        structureIsReused = StructureIsReused.SafeModules;
1533                    }
1534
1535                    // tentatively approve the file
1536                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
1537                }
1538                else if (hasInvalidatedResolution(oldSourceFile.path)) {
1539                    // 'module/types' references could have changed
1540                    structureIsReused = StructureIsReused.SafeModules;
1541
1542                    // add file to the modified list so that we will resolve it later
1543                    modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
1544                }
1545
1546                // if file has passed all checks it should be safe to reuse it
1547                newSourceFiles.push(newSourceFile);
1548            }
1549
1550            if (structureIsReused !== StructureIsReused.Completely) {
1551                return structureIsReused;
1552            }
1553
1554            const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
1555            for (const oldFile of oldSourceFiles) {
1556                if (!contains(modifiedFiles, oldFile)) {
1557                    for (const moduleName of oldFile.ambientModuleNames) {
1558                        ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName);
1559                    }
1560                }
1561            }
1562            // try to verify results of module resolution
1563            for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
1564                const moduleNames = getModuleNames(newSourceFile);
1565                const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
1566                // ensure that module resolution results are still correct
1567                const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
1568                if (resolutionsChanged) {
1569                    structureIsReused = StructureIsReused.SafeModules;
1570                    newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
1571                }
1572                else {
1573                    newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
1574                }
1575                // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
1576                const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
1577                const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile);
1578                // ensure that types resolutions are still correct
1579                const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
1580                if (typeReferenceEesolutionsChanged) {
1581                    structureIsReused = StructureIsReused.SafeModules;
1582                    newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
1583                }
1584                else {
1585                    newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
1586                }
1587            }
1588
1589            if (structureIsReused !== StructureIsReused.Completely) {
1590                return structureIsReused;
1591            }
1592
1593            if (host.hasChangedAutomaticTypeDirectiveNames?.()) {
1594                return StructureIsReused.SafeModules;
1595            }
1596
1597            missingFilePaths = oldProgram.getMissingFilePaths();
1598
1599            // update fileName -> file mapping
1600            Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length);
1601            for (const newSourceFile of newSourceFiles) {
1602                filesByName.set(newSourceFile.path, newSourceFile);
1603            }
1604            const oldFilesByNameMap = oldProgram.getFilesByNameMap();
1605            oldFilesByNameMap.forEach((oldFile, path) => {
1606                if (!oldFile) {
1607                    filesByName.set(path, oldFile);
1608                    return;
1609                }
1610                if (oldFile.path === path) {
1611                    // Set the file as found during node modules search if it was found that way in old progra,
1612                    if (oldProgram!.isSourceFileFromExternalLibrary(oldFile)) {
1613                        sourceFilesFoundSearchingNodeModules.set(oldFile.path, true);
1614                    }
1615                    return;
1616                }
1617                filesByName.set(path, filesByName.get(oldFile.path));
1618            });
1619
1620            files = newSourceFiles;
1621            fileReasons = oldProgram.getFileIncludeReasons();
1622            fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics();
1623            resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
1624
1625            sourceFileToPackageName = oldProgram.sourceFileToPackageName;
1626            redirectTargetsMap = oldProgram.redirectTargetsMap;
1627
1628            return StructureIsReused.Completely;
1629        }
1630
1631        function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
1632            return {
1633                getPrependNodes,
1634                getCanonicalFileName,
1635                getCommonSourceDirectory: program.getCommonSourceDirectory,
1636                getCompilerOptions: program.getCompilerOptions,
1637                getCurrentDirectory: () => currentDirectory,
1638                getNewLine: () => host.getNewLine(),
1639                getSourceFile: program.getSourceFile,
1640                getSourceFileByPath: program.getSourceFileByPath,
1641                getSourceFiles: program.getSourceFiles,
1642                getLibFileFromReference: program.getLibFileFromReference,
1643                isSourceFileFromExternalLibrary,
1644                getResolvedProjectReferenceToRedirect,
1645                getProjectReferenceRedirect,
1646                isSourceOfProjectReferenceRedirect,
1647                getSymlinkCache,
1648                writeFile: writeFileCallback || (
1649                    (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
1650                isEmitBlocked,
1651                readFile: f => host.readFile(f),
1652                fileExists: f => {
1653                    // Use local caches
1654                    const path = toPath(f);
1655                    if (getSourceFileByPath(path)) return true;
1656                    if (contains(missingFilePaths, path)) return false;
1657                    // Before falling back to the host
1658                    return host.fileExists(f);
1659                },
1660                useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
1661                getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(),
1662                getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref),
1663                redirectTargetsMap,
1664                getFileIncludeReasons: program.getFileIncludeReasons,
1665            };
1666        }
1667
1668        function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult {
1669            Debug.assert(!outFile(options));
1670            tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true);
1671            performance.mark("beforeEmit");
1672            const emitResult = emitFiles(
1673                notImplementedResolver,
1674                getEmitHost(writeFileCallback),
1675                /*targetSourceFile*/ undefined,
1676                /*transformers*/ noTransformers,
1677                /*emitOnlyDtsFiles*/ false,
1678                /*onlyBuildInfo*/ true
1679            );
1680
1681            performance.mark("afterEmit");
1682            performance.measure("Emit", "beforeEmit", "afterEmit");
1683            tracing?.pop();
1684            return emitResult;
1685        }
1686
1687        function getResolvedProjectReferences() {
1688            return resolvedProjectReferences;
1689        }
1690
1691        function getProjectReferences() {
1692            return projectReferences;
1693        }
1694
1695        function getPrependNodes() {
1696            return createPrependNodes(
1697                projectReferences,
1698                (_ref, index) => resolvedProjectReferences![index]?.commandLine,
1699                fileName => {
1700                    const path = toPath(fileName);
1701                    const sourceFile = getSourceFileByPath(path);
1702                    return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path);
1703                }
1704            );
1705        }
1706
1707        function isSourceFileFromExternalLibrary(file: SourceFile): boolean {
1708            return !!sourceFilesFoundSearchingNodeModules.get(file.path);
1709        }
1710
1711        function isSourceFileDefaultLibrary(file: SourceFile): boolean {
1712            if (file.hasNoDefaultLib) {
1713                return true;
1714            }
1715
1716            if (!options.noLib) {
1717                return false;
1718            }
1719
1720            // If '--lib' is not specified, include default library file according to '--target'
1721            // otherwise, using options specified in '--lib' instead of '--target' default library file
1722            const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
1723            if (!options.lib) {
1724                return equalityComparer(file.fileName, getDefaultLibraryFileName());
1725            }
1726            else {
1727                return some(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
1728            }
1729        }
1730
1731        function getDiagnosticsProducingTypeChecker() {
1732            return diagnosticsProducingTypeChecker || (diagnosticsProducingTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ true));
1733        }
1734
1735        function dropDiagnosticsProducingTypeChecker() {
1736            diagnosticsProducingTypeChecker = undefined!;
1737        }
1738
1739        function getEtsLibSFromProgram() {
1740            return getEtsLibs(program);
1741        }
1742
1743        function getTypeChecker() {
1744            return noDiagnosticsTypeChecker || (noDiagnosticsTypeChecker = createTypeChecker(program, /*produceDiagnostics:*/ false));
1745        }
1746
1747        function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
1748            tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true);
1749            const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit));
1750            tracing?.pop();
1751            return result;
1752        }
1753
1754        function isEmitBlocked(emitFileName: string): boolean {
1755            return hasEmitBlockingDiagnostics.has(toPath(emitFileName));
1756        }
1757
1758        function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult {
1759            if (!forceDtsEmit) {
1760                const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken);
1761                if (result) return result;
1762            }
1763
1764            // Create the emit resolver outside of the "emitTime" tracking code below.  That way
1765            // any cost associated with it (like type checking) are appropriate associated with
1766            // the type-checking counter.
1767            //
1768            // If the -out option is specified, we should not pass the source file to getEmitResolver.
1769            // This is because in the -out scenario all files need to be emitted, and therefore all
1770            // files need to be type checked. And the way to specify that all files need to be type
1771            // checked is to not pass the file to getEmitResolver.
1772            const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken);
1773
1774            performance.mark("beforeEmit");
1775
1776            const emitResult = emitFiles(
1777                emitResolver,
1778                getEmitHost(writeFileCallback),
1779                sourceFile,
1780                getTransformers(options, customTransformers, emitOnlyDtsFiles),
1781                emitOnlyDtsFiles,
1782                /*onlyBuildInfo*/ false,
1783                forceDtsEmit
1784            );
1785
1786            performance.mark("afterEmit");
1787            performance.measure("Emit", "beforeEmit", "afterEmit");
1788            return emitResult;
1789        }
1790
1791        function getSourceFile(fileName: string): SourceFile | undefined {
1792            return getSourceFileByPath(toPath(fileName));
1793        }
1794
1795        function getSourceFileByPath(path: Path): SourceFile | undefined {
1796            return filesByName.get(path) || undefined;
1797        }
1798
1799        function getDiagnosticsHelper<T extends Diagnostic>(
1800            sourceFile: SourceFile | undefined,
1801            getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[],
1802            cancellationToken: CancellationToken | undefined): readonly T[] {
1803            if (sourceFile) {
1804                return getDiagnostics(sourceFile, cancellationToken);
1805            }
1806            return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => {
1807                if (cancellationToken) {
1808                    cancellationToken.throwIfCancellationRequested();
1809                }
1810                return getDiagnostics(sourceFile, cancellationToken);
1811            }));
1812        }
1813
1814        function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1815            return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken);
1816        }
1817
1818        function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1819            return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken);
1820        }
1821
1822        function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined {
1823           return sourceFile
1824                ? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path)
1825                : cachedBindAndCheckDiagnosticsForFile.allDiagnostics;
1826        }
1827
1828        function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] {
1829            return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken);
1830        }
1831
1832        function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] {
1833            if (skipTypeChecking(sourceFile, options, program)) {
1834                return emptyArray;
1835            }
1836
1837            const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
1838            if (!sourceFile.commentDirectives?.length) {
1839                return programDiagnosticsInFile;
1840            }
1841
1842            return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics;
1843        }
1844
1845        function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
1846            const options = program.getCompilerOptions();
1847            // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit)
1848            if (!sourceFile || outFile(options)) {
1849                return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
1850            }
1851            else {
1852                return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken);
1853            }
1854        }
1855
1856        function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] {
1857            // For JavaScript files, we report semantic errors for using TypeScript-only
1858            // constructs from within a JavaScript file as syntactic errors.
1859            if (isSourceFileJS(sourceFile)) {
1860                if (!sourceFile.additionalSyntacticDiagnostics) {
1861                    sourceFile.additionalSyntacticDiagnostics = getJSSyntacticDiagnosticsForFile(sourceFile);
1862                }
1863                return concatenate(sourceFile.additionalSyntacticDiagnostics, sourceFile.parseDiagnostics);
1864            }
1865            return sourceFile.parseDiagnostics;
1866        }
1867
1868        function runWithCancellationToken<T>(func: () => T): T {
1869            try {
1870                return func();
1871            }
1872            catch (e) {
1873                if (e instanceof OperationCanceledException) {
1874                    // We were canceled while performing the operation.  Because our type checker
1875                    // might be a bad state, we need to throw it away.
1876                    //
1877                    // Note: we are overly aggressive here.  We do not actually *have* to throw away
1878                    // the "noDiagnosticsTypeChecker".  However, for simplicity, i'd like to keep
1879                    // the lifetimes of these two TypeCheckers the same.  Also, we generally only
1880                    // cancel when the user has made a change anyways.  And, in that case, we (the
1881                    // program instance) will get thrown away anyways.  So trying to keep one of
1882                    // these type checkers alive doesn't serve much purpose.
1883                    noDiagnosticsTypeChecker = undefined!;
1884                    diagnosticsProducingTypeChecker = undefined!;
1885                }
1886
1887                throw e;
1888            }
1889        }
1890
1891        function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1892            return concatenate(
1893                filterSemanticDiagnotics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options),
1894                getProgramDiagnostics(sourceFile)
1895            );
1896        }
1897
1898        function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1899            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache);
1900        }
1901
1902        function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] {
1903            return runWithCancellationToken(() => {
1904                if (skipTypeChecking(sourceFile, options, program)) {
1905                    return emptyArray;
1906                }
1907
1908                const typeChecker = getDiagnosticsProducingTypeChecker();
1909
1910                Debug.assert(!!sourceFile.bindDiagnostics);
1911
1912                const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
1913                const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
1914                // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
1915                const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
1916                    sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred || sourceFile.scriptKind === ScriptKind.ETS);
1917                const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
1918                const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
1919
1920                return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
1921            });
1922        }
1923
1924        function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
1925            const flatDiagnostics = flatten(allDiagnostics);
1926            if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) {
1927                return flatDiagnostics;
1928            }
1929
1930            const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics);
1931
1932            for (const errorExpectation of directives.getUnusedExpectations()) {
1933                diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive));
1934            }
1935
1936            return diagnostics;
1937        }
1938
1939        /**
1940         * Creates a map of comment directives along with the diagnostics immediately preceded by one of them.
1941         * Comments that match to any of those diagnostics are marked as used.
1942         */
1943        function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) {
1944            // Diagnostics are only reported if there is no comment directive preceding them
1945            // This will modify the directives map by marking "used" ones with a corresponding diagnostic
1946            const directives = createCommentDirectivesMap(sourceFile, commentDirectives);
1947            const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1);
1948
1949            return { diagnostics, directives };
1950        }
1951
1952        function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
1953            return runWithCancellationToken(() => {
1954                return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
1955            });
1956        }
1957
1958        /**
1959         * @returns The line index marked as preceding the diagnostic, or -1 if none was.
1960         */
1961        function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) {
1962            const { file, start } = diagnostic;
1963            if (!file) {
1964                return -1;
1965            }
1966
1967            // Start out with the line just before the text
1968            const lineStarts = getLineStarts(file);
1969            let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217
1970            while (line >= 0) {
1971                // As soon as that line is known to have a comment directive, use that
1972                if (directives.markUsed(line)) {
1973                    return line;
1974                }
1975
1976                // Stop searching if the line is not empty and not a comment
1977                const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim();
1978                if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) {
1979                    return -1;
1980                }
1981
1982                line--;
1983            }
1984
1985            return -1;
1986        }
1987
1988        function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
1989            return runWithCancellationToken(() => {
1990                const diagnostics: DiagnosticWithLocation[] = [];
1991                walk(sourceFile, sourceFile);
1992                forEachChildRecursively(sourceFile, walk, walkArray);
1993
1994                return diagnostics;
1995
1996                function walk(node: Node, parent: Node) {
1997                    // Return directly from the case if the given node doesnt want to visit each child
1998                    // Otherwise break to visit each child
1999
2000                    switch (parent.kind) {
2001                        case SyntaxKind.Parameter:
2002                        case SyntaxKind.PropertyDeclaration:
2003                        case SyntaxKind.MethodDeclaration:
2004                            if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
2005                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
2006                                return "skip";
2007                            }
2008                        // falls through
2009                        case SyntaxKind.MethodSignature:
2010                        case SyntaxKind.Constructor:
2011                        case SyntaxKind.GetAccessor:
2012                        case SyntaxKind.SetAccessor:
2013                        case SyntaxKind.FunctionExpression:
2014                        case SyntaxKind.FunctionDeclaration:
2015                        case SyntaxKind.ArrowFunction:
2016                        case SyntaxKind.VariableDeclaration:
2017                            // type annotation
2018                            if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
2019                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
2020                                return "skip";
2021                            }
2022                    }
2023
2024                    switch (node.kind) {
2025                        case SyntaxKind.ImportClause:
2026                            if ((node as ImportClause).isTypeOnly) {
2027                                diagnostics.push(createDiagnosticForNode(parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type"));
2028                                return "skip";
2029                            }
2030                            break;
2031                        case SyntaxKind.ExportDeclaration:
2032                            if ((node as ExportDeclaration).isTypeOnly) {
2033                                diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type"));
2034                                return "skip";
2035                            }
2036                            break;
2037                        case SyntaxKind.ImportEqualsDeclaration:
2038                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
2039                            return "skip";
2040                        case SyntaxKind.ExportAssignment:
2041                            if ((<ExportAssignment>node).isExportEquals) {
2042                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
2043                                return "skip";
2044                            }
2045                            break;
2046                        case SyntaxKind.HeritageClause:
2047                            const heritageClause = <HeritageClause>node;
2048                            if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
2049                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
2050                                return "skip";
2051                            }
2052                            break;
2053                        case SyntaxKind.InterfaceDeclaration:
2054                            const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword);
2055                            Debug.assertIsDefined(interfaceKeyword);
2056                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword));
2057                            return "skip";
2058                        case SyntaxKind.ModuleDeclaration:
2059                            const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
2060                            Debug.assertIsDefined(moduleKeyword);
2061                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
2062                            return "skip";
2063                        case SyntaxKind.TypeAliasDeclaration:
2064                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
2065                            return "skip";
2066                        case SyntaxKind.EnumDeclaration:
2067                            const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword));
2068                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
2069                            return "skip";
2070                        case SyntaxKind.NonNullExpression:
2071                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
2072                            return "skip";
2073                        case SyntaxKind.AsExpression:
2074                            diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
2075                            return "skip";
2076                        case SyntaxKind.TypeAssertionExpression:
2077                            Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
2078                    }
2079                }
2080
2081                function walkArray(nodes: NodeArray<Node>, parent: Node) {
2082                    if (parent.decorators === nodes && !options.experimentalDecorators) {
2083                        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));
2084                    }
2085
2086                    switch (parent.kind) {
2087                        case SyntaxKind.ClassDeclaration:
2088                        case SyntaxKind.ClassExpression:
2089                        case SyntaxKind.StructDeclaration:
2090                        case SyntaxKind.MethodDeclaration:
2091                        case SyntaxKind.Constructor:
2092                        case SyntaxKind.GetAccessor:
2093                        case SyntaxKind.SetAccessor:
2094                        case SyntaxKind.FunctionExpression:
2095                        case SyntaxKind.FunctionDeclaration:
2096                        case SyntaxKind.ArrowFunction:
2097                            // Check type parameters
2098                            if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
2099                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
2100                                return "skip";
2101                            }
2102                        // falls through
2103
2104                        case SyntaxKind.VariableStatement:
2105                            // Check modifiers
2106                            if (nodes === parent.modifiers) {
2107                                checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
2108                                return "skip";
2109                            }
2110                            break;
2111                        case SyntaxKind.PropertyDeclaration:
2112                            // Check modifiers of property declaration
2113                            if (nodes === (<PropertyDeclaration>parent).modifiers) {
2114                                for (const modifier of <NodeArray<Modifier>>nodes) {
2115                                    if (modifier.kind !== SyntaxKind.StaticKeyword) {
2116                                        diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2117                                    }
2118                                }
2119                                return "skip";
2120                            }
2121                            break;
2122                        case SyntaxKind.Parameter:
2123                            // Check modifiers of parameter declaration
2124                            if (nodes === (<ParameterDeclaration>parent).modifiers) {
2125                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
2126                                return "skip";
2127                            }
2128                            break;
2129                        case SyntaxKind.CallExpression:
2130                        case SyntaxKind.NewExpression:
2131                        case SyntaxKind.ExpressionWithTypeArguments:
2132                        case SyntaxKind.JsxSelfClosingElement:
2133                        case SyntaxKind.JsxOpeningElement:
2134                        case SyntaxKind.TaggedTemplateExpression:
2135                            // Check type arguments
2136                            if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
2137                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
2138                                return "skip";
2139                            }
2140                            break;
2141                    }
2142                }
2143
2144                function checkModifiers(modifiers: NodeArray<Modifier>, isConstValid: boolean) {
2145                    for (const modifier of modifiers) {
2146                        switch (modifier.kind) {
2147                            case SyntaxKind.ConstKeyword:
2148                                if (isConstValid) {
2149                                    continue;
2150                                }
2151                            // to report error,
2152                            // falls through
2153                            case SyntaxKind.PublicKeyword:
2154                            case SyntaxKind.PrivateKeyword:
2155                            case SyntaxKind.ProtectedKeyword:
2156                            case SyntaxKind.ReadonlyKeyword:
2157                            case SyntaxKind.DeclareKeyword:
2158                            case SyntaxKind.AbstractKeyword:
2159                                diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2160                                break;
2161
2162                            // These are all legal modifiers.
2163                            case SyntaxKind.StaticKeyword:
2164                            case SyntaxKind.ExportKeyword:
2165                            case SyntaxKind.DefaultKeyword:
2166                        }
2167                    }
2168                }
2169
2170                function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2171                    const start = nodes.pos;
2172                    return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2);
2173                }
2174
2175                // Since these are syntactic diagnostics, parent might not have been set
2176                // this means the sourceFile cannot be infered from the node
2177                function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2178                    return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
2179                }
2180            });
2181        }
2182
2183        function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2184            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
2185        }
2186
2187        function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2188            return runWithCancellationToken(() => {
2189                const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
2190                // Don't actually write any files since we're just getting diagnostics.
2191                return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray;
2192            });
2193        }
2194
2195        function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>(
2196            sourceFile: T,
2197            cancellationToken: CancellationToken | undefined,
2198            cache: DiagnosticCache<U>,
2199            getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[],
2200        ): readonly U[] {
2201
2202            const cachedResult = sourceFile
2203                ? cache.perFile?.get(sourceFile.path)
2204                : cache.allDiagnostics;
2205
2206            if (cachedResult) {
2207                return cachedResult;
2208            }
2209            const result = getDiagnostics(sourceFile, cancellationToken);
2210            if (sourceFile) {
2211                (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result);
2212            }
2213            else {
2214                cache.allDiagnostics = result;
2215            }
2216            return result;
2217        }
2218
2219        function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
2220            return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
2221        }
2222
2223        function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
2224            return sortAndDeduplicateDiagnostics(concatenate(
2225                programDiagnostics.getGlobalDiagnostics(),
2226                getOptionsDiagnosticsOfConfigFile()
2227            ));
2228        }
2229
2230        function getOptionsDiagnosticsOfConfigFile() {
2231            if (!options.configFile) { return emptyArray; }
2232            let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
2233            forEachResolvedProjectReference(resolvedRef => {
2234                diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
2235            });
2236            return diagnostics;
2237        }
2238
2239        function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
2240            return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
2241        }
2242
2243        function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
2244            return configFileParsingDiagnostics || emptyArray;
2245        }
2246
2247        function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) {
2248            processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason);
2249        }
2250
2251        function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
2252            return a.fileName === b.fileName;
2253        }
2254
2255        function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean {
2256            return a.kind === SyntaxKind.Identifier
2257                ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText
2258                : b.kind === SyntaxKind.StringLiteral && a.text === b.text;
2259        }
2260
2261        function createSyntheticImport(text: string, file: SourceFile) {
2262            const externalHelpersModuleReference = factory.createStringLiteral(text);
2263            const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
2264            addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
2265            setParent(externalHelpersModuleReference, importDecl);
2266            setParent(importDecl, file);
2267            // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them
2268            // (which is required to build the dependency graph for incremental emit)
2269            (externalHelpersModuleReference as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2270            (importDecl as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2271            return externalHelpersModuleReference;
2272        }
2273
2274        function collectExternalModuleReferences(file: SourceFile): void {
2275            if (file.imports) {
2276                return;
2277            }
2278
2279            const isJavaScriptFile = isSourceFileJS(file);
2280            const isExternalModuleFile = isExternalModule(file);
2281
2282            // file.imports may not be undefined if there exists dynamic import
2283            let imports: StringLiteralLike[] | undefined;
2284            let moduleAugmentations: (StringLiteral | Identifier)[] | undefined;
2285            let ambientModules: string[] | undefined;
2286
2287            // If we are importing helpers, we need to add a synthetic reference to resolve the
2288            // helpers library.
2289            if ((options.isolatedModules || isExternalModuleFile)
2290                && !file.isDeclarationFile) {
2291                if (options.importHelpers) {
2292                    // synthesize 'import "tslib"' declaration
2293                    imports = [createSyntheticImport(externalHelpersModuleNameText, file)];
2294                }
2295                const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options);
2296                if (jsxImport) {
2297                    // synthesize `import "base/jsx-runtime"` declaration
2298                    (imports ||= []).push(createSyntheticImport(jsxImport, file));
2299                }
2300            }
2301
2302            for (const node of file.statements) {
2303                collectModuleReferences(node, /*inAmbientModule*/ false);
2304            }
2305            if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
2306                collectDynamicImportOrRequireCalls(file);
2307            }
2308
2309            file.imports = imports || emptyArray;
2310            file.moduleAugmentations = moduleAugmentations || emptyArray;
2311            file.ambientModuleNames = ambientModules || emptyArray;
2312
2313            return;
2314
2315            function collectModuleReferences(node: Statement, inAmbientModule: boolean): void {
2316                if (isAnyImportOrReExport(node)) {
2317                    const moduleNameExpr = getExternalModuleName(node);
2318                    // TypeScript 1.0 spec (April 2014): 12.1.6
2319                    // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
2320                    // only through top - level external module names. Relative external module names are not permitted.
2321                    if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
2322                        imports = append(imports, moduleNameExpr);
2323                    }
2324                }
2325                else if (isModuleDeclaration(node)) {
2326                    if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
2327                        const nameText = getTextOfIdentifierOrLiteral(node.name);
2328                        // Ambient module declarations can be interpreted as augmentations for some existing external modules.
2329                        // This will happen in two cases:
2330                        // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
2331                        // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
2332                        //   immediately nested in top level ambient module declaration .
2333                        if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
2334                            (moduleAugmentations || (moduleAugmentations = [])).push(node.name);
2335                        }
2336                        else if (!inAmbientModule) {
2337                            if (file.isDeclarationFile) {
2338                                // for global .d.ts files record name of ambient module
2339                                (ambientModules || (ambientModules = [])).push(nameText);
2340                            }
2341                            // An AmbientExternalModuleDeclaration declares an external module.
2342                            // This type of declaration is permitted only in the global module.
2343                            // The StringLiteral must specify a top - level external module name.
2344                            // Relative external module names are not permitted
2345
2346                            // NOTE: body of ambient module is always a module block, if it exists
2347                            const body = <ModuleBlock>(<ModuleDeclaration>node).body;
2348                            if (body) {
2349                                for (const statement of body.statements) {
2350                                    collectModuleReferences(statement, /*inAmbientModule*/ true);
2351                                }
2352                            }
2353                        }
2354                    }
2355                }
2356            }
2357
2358            function collectDynamicImportOrRequireCalls(file: SourceFile) {
2359                const r = /import|require/g;
2360                while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null
2361                    const node = getNodeAtPosition(file, r.lastIndex);
2362                    if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) {
2363                        imports = append(imports, node.arguments[0]);
2364                    }
2365                    // we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
2366                    else if (isImportCall(node) && node.arguments.length === 1 && isStringLiteralLike(node.arguments[0])) {
2367                        imports = append(imports, node.arguments[0] as StringLiteralLike);
2368                    }
2369                    else if (isLiteralImportTypeNode(node)) {
2370                        imports = append(imports, node.argument.literal);
2371                    }
2372                }
2373            }
2374
2375            /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
2376            function getNodeAtPosition(sourceFile: SourceFile, position: number): Node {
2377                let current: Node = sourceFile;
2378                const getContainingChild = (child: Node) => {
2379                    if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) {
2380                        return child;
2381                    }
2382                };
2383                while (true) {
2384                    const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild);
2385                    if (!child) {
2386                        return current;
2387                    }
2388                    current = child;
2389                }
2390            }
2391        }
2392
2393        function getLibFileFromReference(ref: FileReference) {
2394            const libName = toFileNameLowerCase(ref.fileName);
2395            const libFileName = libMap.get(libName);
2396            if (libFileName) {
2397                return getSourceFile(combinePaths(defaultLibraryPath, libFileName));
2398            }
2399        }
2400
2401        /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
2402        function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
2403            return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile);
2404        }
2405
2406        function getSourceFileFromReferenceWorker(
2407            fileName: string,
2408            getSourceFile: (fileName: string) => SourceFile | undefined,
2409            fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
2410            reason?: FileIncludeReason): SourceFile | undefined {
2411
2412            if (hasExtension(fileName)) {
2413                const canonicalFileName = host.getCanonicalFileName(fileName);
2414                if (!options.allowNonTsExtensions && !forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(canonicalFileName, extension)) && !fileExtensionIs(canonicalFileName, Extension.Ets)) {
2415                    if (fail) {
2416                        if (hasJSFileExtension(canonicalFileName)) {
2417                            fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName);
2418                        }
2419                        else {
2420                            fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2421                        }
2422                    }
2423                    return undefined;
2424                }
2425
2426                const sourceFile = getSourceFile(fileName);
2427                if (fail) {
2428                    if (!sourceFile) {
2429                        const redirect = getProjectReferenceRedirect(fileName);
2430                        if (redirect) {
2431                            fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName);
2432                        }
2433                        else {
2434                            fail(Diagnostics.File_0_not_found, fileName);
2435                        }
2436                    }
2437                    else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) {
2438                        fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
2439                    }
2440                }
2441                return sourceFile;
2442            }
2443            else {
2444                const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName);
2445                if (sourceFileNoExtension) return sourceFileNoExtension;
2446
2447                if (fail && options.allowNonTsExtensions) {
2448                    fail(Diagnostics.File_0_not_found, fileName);
2449                    return undefined;
2450                }
2451
2452                const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
2453                if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2454                return sourceFileWithAddedExtension;
2455            }
2456        }
2457
2458        /** This has side effects through `findSourceFile`. */
2459        function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
2460            getSourceFileFromReferenceWorker(
2461                fileName,
2462                fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
2463                (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
2464                reason
2465            );
2466        }
2467
2468        function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) {
2469            return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason);
2470        }
2471
2472        function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void {
2473            const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile);
2474            if (hasExistingReasonToReportErrorOn) {
2475                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]);
2476            }
2477            else {
2478                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]);
2479            }
2480        }
2481
2482        function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
2483            const redirect: SourceFile = Object.create(redirectTarget);
2484            redirect.fileName = fileName;
2485            redirect.path = path;
2486            redirect.resolvedPath = resolvedPath;
2487            redirect.originalFileName = originalFileName;
2488            redirect.redirectInfo = { redirectTarget, unredirected };
2489            sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2490            Object.defineProperties(redirect, {
2491                id: {
2492                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; },
2493                    set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; },
2494                },
2495                symbol: {
2496                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; },
2497                    set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; },
2498                },
2499            });
2500            return redirect;
2501        }
2502
2503        // Get source file from normalized fileName
2504        function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2505            tracing?.push(tracing.Phase.Program, "findSourceFile", {
2506                fileName,
2507                isDefaultLib: isDefaultLib || undefined,
2508                fileIncludeKind: (FileIncludeKind as any)[reason.kind],
2509            });
2510            const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
2511            tracing?.pop();
2512            return result;
2513        }
2514
2515        function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2516            if (useSourceOfProjectReferenceRedirect) {
2517                let source = getSourceOfProjectReferenceRedirect(fileName);
2518                // If preserveSymlinks is true, module resolution wont jump the symlink
2519                // but the resolved real path may be the .d.ts from project reference
2520                // Note:: Currently we try the real path only if the
2521                // file is from node_modules or oh_modules to avoid having to run real path on all file paths
2522                const modulesPathPart: string = isOhpm(options.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
2523                if (!source &&
2524                    host.realpath &&
2525                    options.preserveSymlinks &&
2526                    isDeclarationFileName(fileName) &&
2527                    stringContains(fileName, modulesPathPart)) {
2528                    const realPath = host.realpath(fileName);
2529                    if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
2530                }
2531                if (source) {
2532                    const file = isString(source) ?
2533                        findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
2534                        undefined;
2535                    if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
2536                    return file;
2537                }
2538            }
2539            const originalFileName = fileName;
2540            if (filesByName.has(path)) {
2541                const file = filesByName.get(path);
2542                addFileIncludeReason(file || undefined, reason);
2543                // try to check if we've already seen this file but with a different casing in path
2544                // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
2545                if (file && options.forceConsistentCasingInFileNames) {
2546                    const checkedName = file.fileName;
2547                    const isRedirect = toPath(checkedName) !== toPath(fileName);
2548                    if (isRedirect) {
2549                        fileName = getProjectReferenceRedirect(fileName) || fileName;
2550                    }
2551                    // Check if it differs only in drive letters its ok to ignore that error:
2552                    const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
2553                    const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory);
2554                    if (checkedAbsolutePath !== inputAbsolutePath) {
2555                        reportFileNamesDifferOnlyInCasingError(fileName, file, reason);
2556                    }
2557                }
2558
2559                // If the file was previously found via a node_modules or oh_modules search, but is now being processed as a root file,
2560                // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
2561                if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
2562                    sourceFilesFoundSearchingNodeModules.set(file.path, false);
2563                    if (!options.noResolve) {
2564                        processReferencedFiles(file, isDefaultLib);
2565                        processTypeReferenceDirectives(file);
2566                    }
2567                    if (!options.noLib) {
2568                        processLibReferenceDirectives(file);
2569                    }
2570
2571                    modulesWithElidedImports.set(file.path, false);
2572                    processImportedModules(file);
2573                }
2574                // See if we need to reprocess the imports due to prior skipped imports
2575                else if (file && modulesWithElidedImports.get(file.path)) {
2576                    if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
2577                        modulesWithElidedImports.set(file.path, false);
2578                        processImportedModules(file);
2579                    }
2580                }
2581
2582                return file || undefined;
2583            }
2584
2585            let redirectedPath: Path | undefined;
2586            if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) {
2587                const redirectProject = getProjectReferenceRedirectProject(fileName);
2588                if (redirectProject) {
2589                    if (outFile(redirectProject.commandLine.options)) {
2590                        // Shouldnt create many to 1 mapping file in --out scenario
2591                        return undefined;
2592                    }
2593                    const redirect = getProjectReferenceOutputName(redirectProject, fileName);
2594                    fileName = redirect;
2595                    // Once we start redirecting to a file, we can potentially come back to it
2596                    // via a back-reference from another file in the .d.ts folder. If that happens we'll
2597                    // end up trying to add it to the program *again* because we were tracking it via its
2598                    // original (un-redirected) name. So we have to map both the original path and the redirected path
2599                    // to the source file we're about to find/create
2600                    redirectedPath = toPath(redirect);
2601                }
2602            }
2603
2604            // We haven't looked for this file, do so now and cache result
2605            const file = host.getSourceFile(
2606                fileName,
2607                options.target!,
2608                hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]),
2609                shouldCreateNewSourceFile
2610            );
2611
2612            if (packageId) {
2613                const packageIdKey = packageIdToString(packageId);
2614                const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
2615                if (fileFromPackageId) {
2616                    // Some other SourceFile already exists with this package name and version.
2617                    // Instead of creating a duplicate, just redirect to the existing one.
2618                    const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
2619                    redirectTargetsMap.add(fileFromPackageId.path, fileName);
2620                    addFileToFilesByName(dupFile, path, redirectedPath);
2621                    addFileIncludeReason(dupFile, reason);
2622                    sourceFileToPackageName.set(path, packageId.name);
2623                    processingOtherFiles!.push(dupFile);
2624                    return dupFile;
2625                }
2626                else if (file) {
2627                    // This is the first source file to have this packageId.
2628                    packageIdToSourceFile.set(packageIdKey, file);
2629                    sourceFileToPackageName.set(path, packageId.name);
2630                }
2631            }
2632            addFileToFilesByName(file, path, redirectedPath);
2633
2634            if (file) {
2635                sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2636                file.fileName = fileName; // Ensure that source file has same name as what we were looking for
2637                file.path = path;
2638                file.resolvedPath = toPath(fileName);
2639                file.originalFileName = originalFileName;
2640                addFileIncludeReason(file, reason);
2641
2642                if (host.useCaseSensitiveFileNames()) {
2643                    const pathLowerCase = toFileNameLowerCase(path);
2644                    // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
2645                    const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
2646                    if (existingFile) {
2647                        reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason);
2648                    }
2649                    else {
2650                        filesByNameIgnoreCase!.set(pathLowerCase, file);
2651                    }
2652                }
2653
2654                skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib);
2655
2656                if (!options.noResolve) {
2657                    processReferencedFiles(file, isDefaultLib);
2658                    processTypeReferenceDirectives(file);
2659                }
2660                if (!options.noLib) {
2661                    processLibReferenceDirectives(file);
2662                }
2663
2664
2665                // always process imported modules to record module name resolutions
2666                processImportedModules(file);
2667
2668                if (isDefaultLib) {
2669                    processingDefaultLibFiles!.push(file);
2670                }
2671                else {
2672                    processingOtherFiles!.push(file);
2673                }
2674            }
2675            return file;
2676        }
2677
2678        function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) {
2679            if (file) fileReasons.add(file.path, reason);
2680        }
2681
2682        function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
2683            if (redirectedPath) {
2684                filesByName.set(redirectedPath, file);
2685                filesByName.set(path, file || false);
2686            }
2687            else {
2688                filesByName.set(path, file);
2689            }
2690        }
2691
2692        function getProjectReferenceRedirect(fileName: string): string | undefined {
2693            const referencedProject = getProjectReferenceRedirectProject(fileName);
2694            return referencedProject && getProjectReferenceOutputName(referencedProject, fileName);
2695        }
2696
2697        function getProjectReferenceRedirectProject(fileName: string) {
2698            // Ignore dts or any json files
2699            if (!resolvedProjectReferences || !resolvedProjectReferences.length || isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json)) {
2700                return undefined;
2701            }
2702
2703            // If this file is produced by a referenced project, we need to rewrite it to
2704            // look in the output folder of the referenced project rather than the input
2705            return getResolvedProjectReferenceToRedirect(fileName);
2706        }
2707
2708
2709        function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) {
2710            const out = outFile(referencedProject.commandLine.options);
2711            return out ?
2712                changeExtension(out, Extension.Dts) :
2713                getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames());
2714        }
2715
2716        /**
2717         * Get the referenced project if the file is input file from that reference project
2718         */
2719        function getResolvedProjectReferenceToRedirect(fileName: string) {
2720            if (mapFromFileToProjectReferenceRedirects === undefined) {
2721                mapFromFileToProjectReferenceRedirects = new Map();
2722                forEachResolvedProjectReference(referencedProject => {
2723                    // not input file from the referenced project, ignore
2724                    if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) {
2725                        referencedProject.commandLine.fileNames.forEach(f =>
2726                            mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path));
2727                    }
2728                });
2729            }
2730
2731            const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName));
2732            return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
2733        }
2734
2735        function forEachResolvedProjectReference<T>(
2736            cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
2737        ): T | undefined {
2738            return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
2739        }
2740
2741        function getSourceOfProjectReferenceRedirect(file: string) {
2742            if (!isDeclarationFileName(file)) return undefined;
2743            if (mapFromToProjectReferenceRedirectSource === undefined) {
2744                mapFromToProjectReferenceRedirectSource = new Map();
2745                forEachResolvedProjectReference(resolvedRef => {
2746                    const out = outFile(resolvedRef.commandLine.options);
2747                    if (out) {
2748                        // Dont know which source file it means so return true?
2749                        const outputDts = changeExtension(out, Extension.Dts);
2750                        mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
2751                    }
2752                    else {
2753                        const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames()));
2754                        forEach(resolvedRef.commandLine.fileNames, fileName => {
2755                            if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) {
2756                                const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory);
2757                                mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
2758                            }
2759                        });
2760                    }
2761                });
2762            }
2763            return mapFromToProjectReferenceRedirectSource.get(toPath(file));
2764        }
2765
2766        function isSourceOfProjectReferenceRedirect(fileName: string) {
2767            return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
2768        }
2769
2770        function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
2771            if (!projectReferenceRedirects) {
2772                return undefined;
2773            }
2774
2775            return projectReferenceRedirects.get(projectReferencePath) || undefined;
2776        }
2777
2778        function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
2779            forEach(file.referencedFiles, (ref, index) => {
2780                processSourceFile(
2781                    resolveTripleslashReference(ref.fileName, file.fileName),
2782                    isDefaultLib,
2783                    /*ignoreNoDefaultLib*/ false,
2784                    /*packageId*/ undefined,
2785                    { kind: FileIncludeKind.ReferenceFile, file: file.path, index, }
2786                );
2787            });
2788        }
2789
2790        function processTypeReferenceDirectives(file: SourceFile) {
2791            // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2792            const typeDirectives = map(file.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
2793            if (!typeDirectives) {
2794                return;
2795            }
2796
2797            const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file);
2798            for (let index = 0; index < typeDirectives.length; index++) {
2799                const ref = file.typeReferenceDirectives[index];
2800                const resolvedTypeReferenceDirective = resolutions[index];
2801                // store resolved type directive on the file
2802                const fileName = toFileNameLowerCase(ref.fileName);
2803                setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
2804                processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
2805            }
2806        }
2807
2808        function processTypeReferenceDirective(
2809            typeReferenceDirective: string,
2810            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2811            reason: FileIncludeReason
2812        ): void {
2813            tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
2814            processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason);
2815            tracing?.pop();
2816        }
2817
2818        function processTypeReferenceDirectiveWorker(
2819            typeReferenceDirective: string,
2820            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2821            reason: FileIncludeReason
2822        ): void {
2823
2824            // If we already found this library as a primary reference - nothing to do
2825            const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
2826            if (previousResolution && previousResolution.primary) {
2827                return;
2828            }
2829            let saveResolution = true;
2830            if (resolvedTypeReferenceDirective) {
2831                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++;
2832
2833                if (resolvedTypeReferenceDirective.primary) {
2834                    // resolved from the primary path
2835                    processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217
2836                }
2837                else {
2838                    // If we already resolved to this file, it must have been a secondary reference. Check file contents
2839                    // for sameness and possibly issue an error
2840                    if (previousResolution) {
2841                        // Don't bother reading the file again if it's the same file.
2842                        if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) {
2843                            const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
2844                            const existingFile = getSourceFile(previousResolution.resolvedFileName!)!;
2845                            if (otherFileText !== existingFile.text) {
2846                                addFilePreprocessingFileExplainingDiagnostic(
2847                                    existingFile,
2848                                    reason,
2849                                    Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
2850                                    [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName]
2851                                );
2852                            }
2853                        }
2854                        // don't overwrite previous resolution result
2855                        saveResolution = false;
2856                    }
2857                    else {
2858                        // First resolution of this library
2859                        processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason);
2860                    }
2861                }
2862
2863                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
2864            }
2865            else {
2866                addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]);
2867            }
2868
2869            if (saveResolution) {
2870                resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
2871            }
2872        }
2873
2874        function processLibReferenceDirectives(file: SourceFile) {
2875            forEach(file.libReferenceDirectives, (libReference, index) => {
2876                const libName = toFileNameLowerCase(libReference.fileName);
2877                const libFileName = libMap.get(libName);
2878                if (libFileName) {
2879                    // we ignore any 'no-default-lib' reference set on this file.
2880                    processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
2881                }
2882                else {
2883                    const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
2884                    const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
2885                    const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
2886                    (fileProcessingDiagnostics ||= []).push({
2887                        kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic,
2888                        reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, },
2889                        diagnostic,
2890                        args: [libName, suggestion]
2891                    });
2892                }
2893            });
2894        }
2895
2896        function getCanonicalFileName(fileName: string): string {
2897            return host.getCanonicalFileName(fileName);
2898        }
2899
2900        function processImportedModules(file: SourceFile) {
2901            collectExternalModuleReferences(file);
2902            if (file.imports.length || file.moduleAugmentations.length) {
2903                // Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
2904                const moduleNames = getModuleNames(file);
2905                const resolutions = resolveModuleNamesReusingOldState(moduleNames, file);
2906                Debug.assert(resolutions.length === moduleNames.length);
2907                for (let index = 0; index < moduleNames.length; index++) {
2908                    const resolution = resolutions[index];
2909                    setResolvedModule(file, moduleNames[index], resolution);
2910
2911                    if (!resolution) {
2912                        continue;
2913                    }
2914
2915                    const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
2916                    const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension);
2917                    const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
2918                    const resolvedFileName = resolution.resolvedFileName;
2919
2920                    if (isFromNodeModulesSearch) {
2921                        currentNodeModulesDepth++;
2922                    }
2923
2924                    // add file to program only if:
2925                    // - resolution was successful
2926                    // - noResolve is falsy
2927                    // - module name comes from the list of imports
2928                    // - it's not a top level JavaScript module that exceeded the search max
2929                    const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
2930                    // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
2931                    // This may still end up being an untyped module -- the file won't be included but imports will be allowed.
2932                    const shouldAddFile = resolvedFileName
2933                        && !getResolutionDiagnostic(options, resolution)
2934                        && !options.noResolve
2935                        && index < file.imports.length
2936                        && !elideImport
2937                        && !(isJsFile && !getAllowJSCompilerOption(options))
2938                        && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc));
2939
2940                    if (elideImport) {
2941                        modulesWithElidedImports.set(file.path, true);
2942                    }
2943                    else if (shouldAddFile) {
2944                        const path = toPath(resolvedFileName);
2945                        findSourceFile(
2946                            resolvedFileName,
2947                            path,
2948                            /*isDefaultLib*/ false,
2949                            /*ignoreNoDefaultLib*/ false,
2950                            { kind: FileIncludeKind.Import, file: file.path, index, },
2951                            resolution.packageId,
2952                        );
2953                    }
2954
2955                    if (isFromNodeModulesSearch) {
2956                        currentNodeModulesDepth--;
2957                    }
2958                }
2959            }
2960            else {
2961                // no imports - drop cached module resolutions
2962                file.resolvedModules = undefined;
2963            }
2964        }
2965
2966        function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
2967            let allFilesBelongToPath = true;
2968            const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
2969            for (const sourceFile of sourceFiles) {
2970                if (!sourceFile.isDeclarationFile) {
2971                    const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
2972                    if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
2973                        addProgramDiagnosticExplainingFile(
2974                            sourceFile,
2975                            Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
2976                            [sourceFile.fileName, rootDirectory]
2977                        );
2978                        allFilesBelongToPath = false;
2979                    }
2980                }
2981            }
2982
2983            return allFilesBelongToPath;
2984        }
2985
2986        function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
2987            if (!projectReferenceRedirects) {
2988                projectReferenceRedirects = new Map();
2989            }
2990
2991            // The actual filename (i.e. add "/tsconfig.json" if necessary)
2992            const refPath = resolveProjectReferencePath(ref);
2993            const sourceFilePath = toPath(refPath);
2994            const fromCache = projectReferenceRedirects.get(sourceFilePath);
2995            if (fromCache !== undefined) {
2996                return fromCache || undefined;
2997            }
2998
2999            let commandLine: ParsedCommandLine | undefined;
3000            let sourceFile: JsonSourceFile | undefined;
3001            if (host.getParsedCommandLine) {
3002                commandLine = host.getParsedCommandLine(refPath);
3003                if (!commandLine) {
3004                    addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined);
3005                    projectReferenceRedirects.set(sourceFilePath, false);
3006                    return undefined;
3007                }
3008                sourceFile = Debug.checkDefined(commandLine.options.configFile);
3009                Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath);
3010                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3011            }
3012            else {
3013                // An absolute path pointing to the containing directory of the config file
3014                const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
3015                sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
3016                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3017                if (sourceFile === undefined) {
3018                    projectReferenceRedirects.set(sourceFilePath, false);
3019                    return undefined;
3020                }
3021                commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
3022            }
3023            sourceFile.fileName = refPath;
3024            sourceFile.path = sourceFilePath;
3025            sourceFile.resolvedPath = sourceFilePath;
3026            sourceFile.originalFileName = refPath;
3027
3028            const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
3029            projectReferenceRedirects.set(sourceFilePath, resolvedRef);
3030            if (commandLine.projectReferences) {
3031                resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile);
3032            }
3033            return resolvedRef;
3034        }
3035
3036        function verifyCompilerOptions() {
3037            if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
3038                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
3039            }
3040
3041            if (options.isolatedModules) {
3042                if (options.out) {
3043                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
3044                }
3045
3046                if (options.outFile) {
3047                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules");
3048                }
3049            }
3050
3051            if (options.inlineSourceMap) {
3052                if (options.sourceMap) {
3053                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap");
3054                }
3055                if (options.mapRoot) {
3056                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap");
3057                }
3058            }
3059
3060            if (options.composite) {
3061                if (options.declaration === false) {
3062                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration");
3063                }
3064                if (options.incremental === false) {
3065                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration");
3066                }
3067            }
3068
3069            const outputFile = outFile(options);
3070            if (options.tsBuildInfoFile) {
3071                if (!isIncrementalCompilation(options)) {
3072                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite");
3073                }
3074            }
3075            else if (options.incremental && !outputFile && !options.configFilePath) {
3076                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified));
3077            }
3078
3079            verifyProjectReferences();
3080
3081            // List of collected files is complete; validate exhautiveness if this is a project with a file list
3082            if (options.composite) {
3083                const rootPaths = new Set(rootNames.map(toPath));
3084                for (const file of files) {
3085                    // Ignore file that is not emitted
3086                    if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) {
3087                        addProgramDiagnosticExplainingFile(
3088                            file,
3089                            Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
3090                            [file.fileName, options.configFilePath || ""]
3091                        );
3092                    }
3093                }
3094            }
3095
3096            if (options.paths) {
3097                for (const key in options.paths) {
3098                    if (!hasProperty(options.paths, key)) {
3099                        continue;
3100                    }
3101                    if (!hasZeroOrOneAsteriskCharacter(key)) {
3102                        createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key);
3103                    }
3104                    if (isArray(options.paths[key])) {
3105                        const len = options.paths[key].length;
3106                        if (len === 0) {
3107                            createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key);
3108                        }
3109                        for (let i = 0; i < len; i++) {
3110                            const subst = options.paths[key][i];
3111                            const typeOfSubst = typeof subst;
3112                            if (typeOfSubst === "string") {
3113                                if (!hasZeroOrOneAsteriskCharacter(subst)) {
3114                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key);
3115                                }
3116                                if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) {
3117                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash);
3118                                }
3119                            }
3120                            else {
3121                                createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst);
3122                            }
3123                        }
3124                    }
3125                    else {
3126                        createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key);
3127                    }
3128                }
3129            }
3130
3131            if (!options.sourceMap && !options.inlineSourceMap) {
3132                if (options.inlineSources) {
3133                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources");
3134                }
3135                if (options.sourceRoot) {
3136                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot");
3137                }
3138            }
3139
3140            if (options.out && options.outFile) {
3141                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile");
3142            }
3143
3144            if (options.mapRoot && !(options.sourceMap || options.declarationMap)) {
3145                // Error to specify --mapRoot without --sourcemap
3146                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap");
3147            }
3148
3149            if (options.declarationDir) {
3150                if (!getEmitDeclarations(options)) {
3151                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite");
3152                }
3153                if (outputFile) {
3154                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile");
3155                }
3156            }
3157
3158            if (options.declarationMap && !getEmitDeclarations(options)) {
3159                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite");
3160            }
3161
3162            if (options.lib && options.noLib) {
3163                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
3164            }
3165
3166            if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) {
3167                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
3168            }
3169
3170            const languageVersion = options.target || ScriptTarget.ES3;
3171
3172            const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
3173            if (options.isolatedModules) {
3174                if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
3175                    createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
3176                }
3177
3178                if (options.preserveConstEnums === false) {
3179                    createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules");
3180                }
3181
3182                const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
3183                if (firstNonExternalModuleSourceFile) {
3184                    const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
3185                    programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length,
3186                        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)));
3187                }
3188            }
3189            else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
3190                // We cannot use createDiagnosticFromNode because nodes do not have parents yet
3191                const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3192                programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
3193            }
3194
3195            // Cannot specify module gen that isn't amd or system with --out
3196            if (outputFile && !options.emitDeclarationOnly) {
3197                if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
3198                    createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module");
3199                }
3200                else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) {
3201                    const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3202                    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"));
3203                }
3204            }
3205
3206            if (options.resolveJsonModule) {
3207                if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
3208                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
3209                }
3210                // Any emit other than common js, amd, es2015 or esnext is error
3211                else if (!hasJsonModuleEmitEnabled(options)) {
3212                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module");
3213                }
3214            }
3215
3216            // there has to be common source directory if user specified --outdir || --sourceRoot
3217            // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
3218            if (options.outDir || // there is --outDir specified
3219                options.sourceRoot || // there is --sourceRoot specified
3220                options.mapRoot) { // there is --mapRoot specified
3221
3222                // Precalculate and cache the common source directory
3223                const dir = getCommonSourceDirectory();
3224
3225                // 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
3226                if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) {
3227                    createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir");
3228                }
3229            }
3230
3231            if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) {
3232                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields");
3233            }
3234
3235            if (options.checkJs && !getAllowJSCompilerOption(options)) {
3236                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs"));
3237            }
3238
3239            if (options.emitDeclarationOnly) {
3240                if (!getEmitDeclarations(options)) {
3241                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite");
3242                }
3243
3244                if (options.noEmit) {
3245                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit");
3246                }
3247            }
3248
3249            if (options.emitDecoratorMetadata &&
3250                !options.experimentalDecorators) {
3251                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
3252            }
3253
3254            if (options.jsxFactory) {
3255                if (options.reactNamespace) {
3256                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
3257                }
3258                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3259                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
3260                }
3261                if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
3262                    createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
3263                }
3264            }
3265            else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
3266                createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
3267            }
3268
3269            if (options.jsxFragmentFactory) {
3270                if (!options.jsxFactory) {
3271                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
3272                }
3273                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3274                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
3275                }
3276                if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
3277                    createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
3278                }
3279            }
3280
3281            if (options.reactNamespace) {
3282                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3283                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx));
3284                }
3285            }
3286
3287            if (options.jsxImportSource) {
3288                if (options.jsx === JsxEmit.React) {
3289                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx));
3290                }
3291            }
3292
3293            // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
3294            if (!options.noEmit && !options.suppressOutputPathCheck) {
3295                const emitHost = getEmitHost();
3296                const emitFilesSeen = new Set<string>();
3297                forEachEmittedFile(emitHost, (emitFileNames) => {
3298                    if (!options.emitDeclarationOnly) {
3299                        verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
3300                    }
3301                    verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
3302                });
3303            }
3304
3305            // Verify that all the emit files are unique and don't overwrite input files
3306            function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) {
3307                if (emitFileName) {
3308                    const emitFilePath = toPath(emitFileName);
3309                    // Report error if the output overwrites input file
3310                    if (filesByName.has(emitFilePath)) {
3311                        let chain: DiagnosticMessageChain | undefined;
3312                        if (!options.configFilePath) {
3313                            // The program is from either an inferred project or an external project
3314                            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);
3315                        }
3316                        chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName);
3317                        blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain));
3318                    }
3319
3320                    const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath;
3321                    // Report error if multiple files write into same file
3322                    if (emitFilesSeen.has(emitFileKey)) {
3323                        // Already seen the same emit file - report error
3324                        blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName));
3325                    }
3326                    else {
3327                        emitFilesSeen.add(emitFileKey);
3328                    }
3329                }
3330            }
3331        }
3332
3333        function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic {
3334            let fileIncludeReasons: DiagnosticMessageChain[] | undefined;
3335            let relatedInfo: Diagnostic[] | undefined;
3336            let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined;
3337            if (file) fileReasons.get(file.path)?.forEach(processReason);
3338            if (fileProcessingReason) processReason(fileProcessingReason);
3339            // If we have location and there is only one reason file is in which is the location, dont add details for file include
3340            if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined;
3341            const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason);
3342            const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon);
3343            const redirectInfo = file && explainIfFileIsRedirect(file);
3344            const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray);
3345            return location && isReferenceFileLocation(location) ?
3346                createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) :
3347                createCompilerDiagnosticFromMessageChain(chain, relatedInfo);
3348
3349            function processReason(reason: FileIncludeReason) {
3350                (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason));
3351                if (!locationReason && isReferencedFile(reason)) {
3352                    // Report error at first reference file or file currently in processing and dont report in related information
3353                    locationReason = reason;
3354                }
3355                else if (locationReason !== reason) {
3356                    relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason));
3357                }
3358                // Remove fileProcessingReason if its already included in fileReasons of the program
3359                if (reason === fileProcessingReason) fileProcessingReason = undefined;
3360            }
3361        }
3362
3363        function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3364            (fileProcessingDiagnostics ||= []).push({
3365                kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic,
3366                file: file && file.path,
3367                fileProcessingReason,
3368                diagnostic,
3369                args
3370            });
3371        }
3372
3373        function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3374            programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args));
3375        }
3376
3377        function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined {
3378            if (isReferencedFile(reason)) {
3379                const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason);
3380                let message: DiagnosticMessage;
3381                switch (reason.kind) {
3382                    case FileIncludeKind.Import:
3383                        message = Diagnostics.File_is_included_via_import_here;
3384                        break;
3385                    case FileIncludeKind.ReferenceFile:
3386                        message = Diagnostics.File_is_included_via_reference_here;
3387                        break;
3388                    case FileIncludeKind.TypeReferenceDirective:
3389                        message = Diagnostics.File_is_included_via_type_library_reference_here;
3390                        break;
3391                    case FileIncludeKind.LibReferenceDirective:
3392                        message = Diagnostics.File_is_included_via_library_reference_here;
3393                        break;
3394                    default:
3395                        Debug.assertNever(reason);
3396                }
3397                return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic(
3398                    referenceLocation.file,
3399                    referenceLocation.pos,
3400                    referenceLocation.end - referenceLocation.pos,
3401                    message,
3402                ) : undefined;
3403            }
3404
3405            if (!options.configFile) return undefined;
3406            let configFileNode: Node | undefined;
3407            let message: DiagnosticMessage;
3408            switch (reason.kind) {
3409                case FileIncludeKind.RootFile:
3410                    if (!options.configFile.configFileSpecs) return undefined;
3411                    const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory);
3412                    const matchedByFiles = getMatchedFileSpec(program, fileName);
3413                    if (matchedByFiles) {
3414                        configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles);
3415                        message = Diagnostics.File_is_matched_by_files_list_specified_here;
3416                        break;
3417                    }
3418                    const matchedByInclude = getMatchedIncludeSpec(program, fileName);
3419                    // Could be additional files specified as roots
3420                    if (!matchedByInclude) return undefined;
3421                    configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude);
3422                    message = Diagnostics.File_is_matched_by_include_pattern_specified_here;
3423                    break;
3424                case FileIncludeKind.SourceFromProjectReference:
3425                case FileIncludeKind.OutputFromProjectReference:
3426                    const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]);
3427                    const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) =>
3428                        resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined
3429                    );
3430                    if (!referenceInfo) return undefined;
3431                    const { sourceFile, index } = referenceInfo;
3432                    const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"),
3433                        property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3434                    return referencesSyntax && referencesSyntax.elements.length > index ?
3435                        createDiagnosticForNodeInSourceFile(
3436                            sourceFile,
3437                            referencesSyntax.elements[index],
3438                            reason.kind === FileIncludeKind.OutputFromProjectReference ?
3439                                Diagnostics.File_is_output_from_referenced_project_specified_here :
3440                                Diagnostics.File_is_source_from_referenced_project_specified_here,
3441                        ) :
3442                        undefined;
3443                case FileIncludeKind.AutomaticTypeDirectiveFile:
3444                    if (!options.types) return undefined;
3445                    configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference);
3446                    message = Diagnostics.File_is_entry_point_of_type_library_specified_here;
3447                    break;
3448                case FileIncludeKind.LibFile:
3449                    if (reason.index !== undefined) {
3450                        configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]);
3451                        message = Diagnostics.File_is_library_specified_here;
3452                        break;
3453                    }
3454                    const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
3455                    configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
3456                    message = Diagnostics.File_is_default_library_for_target_specified_here;
3457                    break;
3458                default:
3459                    Debug.assertNever(reason);
3460            }
3461            return configFileNode && createDiagnosticForNodeInSourceFile(
3462                options.configFile,
3463                configFileNode,
3464                message,
3465            );
3466        }
3467
3468        function verifyProjectReferences() {
3469            const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
3470            forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
3471                const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
3472                const parentFile = parent && parent.sourceFile as JsonSourceFile;
3473                if (!resolvedRef) {
3474                    createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
3475                    return;
3476                }
3477                const options = resolvedRef.commandLine.options;
3478                if (!options.composite || options.noEmit) {
3479                    // ok to not have composite if the current program is container only
3480                    const inputs = parent ? parent.commandLine.fileNames : rootNames;
3481                    if (inputs.length) {
3482                        if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
3483                        if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
3484                    }
3485                }
3486                if (ref.prepend) {
3487                    const out = outFile(options);
3488                    if (out) {
3489                        if (!host.fileExists(out)) {
3490                            createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
3491                        }
3492                    }
3493                    else {
3494                        createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
3495                    }
3496                }
3497                if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
3498                    createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
3499                    hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
3500                }
3501            });
3502        }
3503
3504        function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) {
3505            let needCompilerDiagnostic = true;
3506            const pathsSyntax = getOptionPathsSyntax();
3507            for (const pathProp of pathsSyntax) {
3508                if (isObjectLiteralExpression(pathProp.initializer)) {
3509                    for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) {
3510                        const initializer = keyProps.initializer;
3511                        if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) {
3512                            programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2));
3513                            needCompilerDiagnostic = false;
3514                        }
3515                    }
3516                }
3517            }
3518
3519            if (needCompilerDiagnostic) {
3520                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3521            }
3522        }
3523
3524        function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) {
3525            let needCompilerDiagnostic = true;
3526            const pathsSyntax = getOptionPathsSyntax();
3527            for (const pathProp of pathsSyntax) {
3528                if (isObjectLiteralExpression(pathProp.initializer) &&
3529                    createOptionDiagnosticInObjectLiteralSyntax(
3530                        pathProp.initializer, onKey, key, /*key2*/ undefined,
3531                        message, arg0)) {
3532                    needCompilerDiagnostic = false;
3533                }
3534            }
3535            if (needCompilerDiagnostic) {
3536                programDiagnostics.add(createCompilerDiagnostic(message, arg0));
3537            }
3538        }
3539
3540        function getOptionsSyntaxByName(name: string) {
3541            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3542            return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
3543        }
3544
3545        function getOptionPathsSyntax() {
3546            return getOptionsSyntaxByName("paths") || emptyArray;
3547        }
3548
3549        function getOptionsSyntaxByValue(name: string, value: string) {
3550            const syntaxByName = getOptionsSyntaxByName(name);
3551            return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined);
3552        }
3553
3554        function getOptionsSyntaxByArrayElementValue(name: string, value: string) {
3555            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3556            return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value);
3557        }
3558
3559        function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) {
3560            createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
3561        }
3562
3563        function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
3564            createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
3565        }
3566
3567        function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
3568            const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"),
3569                property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3570            if (referencesSyntax && referencesSyntax.elements.length > index) {
3571                programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
3572            }
3573            else {
3574                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
3575            }
3576        }
3577
3578        function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
3579            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3580            const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
3581                !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);
3582
3583            if (needCompilerDiagnostic) {
3584                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3585            }
3586        }
3587
3588        function getCompilerOptionsObjectLiteralSyntax() {
3589            if (_compilerOptionsObjectLiteralSyntax === undefined) {
3590                _compilerOptionsObjectLiteralSyntax = false;
3591                const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile);
3592                if (jsonObjectLiteral) {
3593                    for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) {
3594                        if (isObjectLiteralExpression(prop.initializer)) {
3595                            _compilerOptionsObjectLiteralSyntax = prop.initializer;
3596                            break;
3597                        }
3598                    }
3599                }
3600            }
3601            return _compilerOptionsObjectLiteralSyntax || undefined;
3602        }
3603
3604        function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean {
3605            const props = getPropertyAssignment(objectLiteral, key1, key2);
3606            for (const prop of props) {
3607                programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
3608            }
3609            return !!props.length;
3610        }
3611
3612        function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
3613            hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
3614            programDiagnostics.add(diag);
3615        }
3616
3617        function isEmittedFile(file: string): boolean {
3618            if (options.noEmit) {
3619                return false;
3620            }
3621
3622            // If this is source file, its not emitted file
3623            const filePath = toPath(file);
3624            if (getSourceFileByPath(filePath)) {
3625                return false;
3626            }
3627
3628            // If options have --outFile or --out just check that
3629            const out = outFile(options);
3630            if (out) {
3631                return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts);
3632            }
3633
3634            // If declarationDir is specified, return if its a file in that directory
3635            if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) {
3636                return true;
3637            }
3638
3639            // If --outDir, check if file is in that directory
3640            if (options.outDir) {
3641                return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
3642            }
3643
3644            if (fileExtensionIsOneOf(filePath, supportedJSExtensions) || isDeclarationFileName(filePath)) {
3645                // Otherwise just check if sourceFile with the name exists
3646                const filePathWithoutExtension = removeFileExtension(filePath);
3647                return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) ||
3648                    !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path);
3649            }
3650            return false;
3651        }
3652
3653        function isSameFile(file1: string, file2: string) {
3654            return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
3655        }
3656
3657        function getSymlinkCache(): SymlinkCache {
3658            if (host.getSymlinkCache) {
3659                return host.getSymlinkCache();
3660            }
3661            return symlinks || (symlinks = discoverProbableSymlinks(
3662                files,
3663                getCanonicalFileName,
3664                host.getCurrentDirectory(),
3665                isOhpm(options.packageManagerType)));
3666        }
3667    }
3668
3669    interface HostForUseSourceOfProjectReferenceRedirect {
3670        compilerHost: CompilerHost;
3671        getSymlinkCache: () => SymlinkCache;
3672        useSourceOfProjectReferenceRedirect: boolean;
3673        toPath(fileName: string): Path;
3674        getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
3675        getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
3676        forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
3677        options?: CompilerOptions;
3678    }
3679
3680    function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) {
3681        let setOfDeclarationDirectories: Set<Path> | undefined;
3682        const originalFileExists = host.compilerHost.fileExists;
3683        const originalDirectoryExists = host.compilerHost.directoryExists;
3684        const originalGetDirectories = host.compilerHost.getDirectories;
3685        const originalRealpath = host.compilerHost.realpath;
3686
3687        if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists };
3688
3689        host.compilerHost.fileExists = fileExists;
3690
3691        let directoryExists;
3692        if (originalDirectoryExists) {
3693            // This implementation of directoryExists checks if the directory being requested is
3694            // directory of .d.ts file for the referenced Project.
3695            // If it is it returns true irrespective of whether that directory exists on host
3696            directoryExists = host.compilerHost.directoryExists = path => {
3697                if (originalDirectoryExists.call(host.compilerHost, path)) {
3698                    handleDirectoryCouldBeSymlink(path);
3699                    return true;
3700                }
3701
3702                if (!host.getResolvedProjectReferences()) return false;
3703
3704                if (!setOfDeclarationDirectories) {
3705                    setOfDeclarationDirectories = new Set();
3706                    host.forEachResolvedProjectReference(ref => {
3707                        const out = outFile(ref.commandLine.options);
3708                        if (out) {
3709                            setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out)));
3710                        }
3711                        else {
3712                            // Set declaration's in different locations only, if they are next to source the directory present doesnt change
3713                            const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir;
3714                            if (declarationDir) {
3715                                setOfDeclarationDirectories!.add(host.toPath(declarationDir));
3716                            }
3717                        }
3718                    });
3719                }
3720
3721                return fileOrDirectoryExistsUsingSource(path, /*isFile*/ false);
3722            };
3723        }
3724
3725        if (originalGetDirectories) {
3726            // Call getDirectories only if directory actually present on the host
3727            // This is needed to ensure that we arent getting directories that we fake about presence for
3728            host.compilerHost.getDirectories = path =>
3729                !host.getResolvedProjectReferences() || (originalDirectoryExists && originalDirectoryExists.call(host.compilerHost, path)) ?
3730                    originalGetDirectories.call(host.compilerHost, path) :
3731                    [];
3732        }
3733
3734        // This is something we keep for life time of the host
3735        if (originalRealpath) {
3736            host.compilerHost.realpath = s =>
3737                host.getSymlinkCache().getSymlinkedFiles()?.get(host.toPath(s)) ||
3738                originalRealpath.call(host.compilerHost, s);
3739        }
3740
3741        return { onProgramCreateComplete, fileExists, directoryExists };
3742
3743        function onProgramCreateComplete() {
3744            host.compilerHost.fileExists = originalFileExists;
3745            host.compilerHost.directoryExists = originalDirectoryExists;
3746            host.compilerHost.getDirectories = originalGetDirectories;
3747            // DO not revert realpath as it could be used later
3748        }
3749
3750        // This implementation of fileExists checks if the file being requested is
3751        // .d.ts file for the referenced Project.
3752        // If it is it returns true irrespective of whether that file exists on host
3753        function fileExists(file: string) {
3754            if (originalFileExists.call(host.compilerHost, file)) return true;
3755            if (!host.getResolvedProjectReferences()) return false;
3756            if (!isDeclarationFileName(file)) return false;
3757
3758            // Project references go to source file instead of .d.ts file
3759            return fileOrDirectoryExistsUsingSource(file, /*isFile*/ true);
3760        }
3761
3762        function fileExistsIfProjectReferenceDts(file: string) {
3763            const source = host.getSourceOfProjectReferenceRedirect(file);
3764            return source !== undefined ?
3765                isString(source) ? originalFileExists.call(host.compilerHost, source) : true :
3766                undefined;
3767        }
3768
3769        function directoryExistsIfProjectReferenceDeclDir(dir: string) {
3770            const dirPath = host.toPath(dir);
3771            const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
3772            return forEachKey(
3773                setOfDeclarationDirectories!,
3774                declDirPath => dirPath === declDirPath ||
3775                    // Any parent directory of declaration dir
3776                    startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) ||
3777                    // Any directory inside declaration dir
3778                    startsWith(dirPath, `${declDirPath}/`)
3779            );
3780        }
3781
3782        function handleDirectoryCouldBeSymlink(directory: string) {
3783            if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return;
3784
3785            // Because we already watch node_modules or oh_modules, handle symlinks in there
3786            const modulesPathPart = isOhpm(host.options?.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
3787            if (!originalRealpath || !stringContains(directory, modulesPathPart)) return;
3788            const symlinkCache = host.getSymlinkCache();
3789            const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory));
3790            if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return;
3791
3792            const real = normalizePath(originalRealpath.call(host.compilerHost, directory));
3793            let realPath: Path;
3794            if (real === directory ||
3795                (realPath = ensureTrailingDirectorySeparator(host.toPath(real))) === directoryPath) {
3796                // not symlinked
3797                symlinkCache.setSymlinkedDirectory(directoryPath, false);
3798                return;
3799            }
3800
3801            symlinkCache.setSymlinkedDirectory(directory, {
3802                real: ensureTrailingDirectorySeparator(real),
3803                realPath
3804            });
3805        }
3806
3807        function fileOrDirectoryExistsUsingSource(fileOrDirectory: string, isFile: boolean): boolean {
3808            const fileOrDirectoryExistsUsingSource = isFile ?
3809                (file: string) => fileExistsIfProjectReferenceDts(file) :
3810                (dir: string) => directoryExistsIfProjectReferenceDeclDir(dir);
3811            // Check current directory or file
3812            const result = fileOrDirectoryExistsUsingSource(fileOrDirectory);
3813            if (result !== undefined) return result;
3814
3815            const symlinkCache = host.getSymlinkCache();
3816            const symlinkedDirectories = symlinkCache.getSymlinkedDirectories();
3817            if (!symlinkedDirectories) return false;
3818            const fileOrDirectoryPath = host.toPath(fileOrDirectory);
3819            const modulesPathPart = isOhpm(host.options?.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
3820            if (!stringContains(fileOrDirectoryPath, modulesPathPart)) return false;
3821            if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true;
3822
3823            // If it contains node_modules or oh_modules check if its one of the symlinked path we know of
3824            return firstDefinedIterator(
3825                symlinkedDirectories.entries(),
3826                ([directoryPath, symlinkedDirectory]) => {
3827                    if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined;
3828                    const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath));
3829                    if (isFile && result) {
3830                        // Store the real path for the file'
3831                        const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory());
3832                        symlinkCache.setSymlinkedFile(
3833                            fileOrDirectoryPath,
3834                            `${symlinkedDirectory.real}${absolutePath.replace(new RegExp(directoryPath, "i"), "")}`
3835                        );
3836                    }
3837                    return result;
3838                }
3839            ) || false;
3840        }
3841    }
3842
3843    /*@internal*/
3844    export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3845
3846    /*@internal*/
3847    export function handleNoEmitOptions<T extends BuilderProgram>(
3848        program: Program | T,
3849        sourceFile: SourceFile | undefined,
3850        writeFile: WriteFileCallback | undefined,
3851        cancellationToken: CancellationToken | undefined
3852    ): EmitResult | undefined {
3853        const options = program.getCompilerOptions();
3854        if (options.noEmit) {
3855            // Cache the semantic diagnostics
3856            program.getSemanticDiagnostics(sourceFile, cancellationToken);
3857            return sourceFile || outFile(options) ?
3858                emitSkippedWithNoDiagnostics :
3859                program.emitBuildInfo(writeFile, cancellationToken);
3860        }
3861
3862        // If the noEmitOnError flag is set, then check if we have any errors so far.  If so,
3863        // immediately bail out.  Note that we pass 'undefined' for 'sourceFile' so that we
3864        // get any preEmit diagnostics, not just the ones
3865        if (!options.noEmitOnError) return undefined;
3866        let diagnostics: readonly Diagnostic[] = [
3867            ...program.getOptionsDiagnostics(cancellationToken),
3868            ...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
3869            ...program.getGlobalDiagnostics(cancellationToken),
3870            ...program.getSemanticDiagnostics(sourceFile, cancellationToken)
3871        ];
3872
3873        if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
3874            diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
3875        }
3876
3877        if (!diagnostics.length) return undefined;
3878        let emittedFiles: string[] | undefined;
3879        if (!sourceFile && !outFile(options)) {
3880            const emitResult = program.emitBuildInfo(writeFile, cancellationToken);
3881            if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics];
3882            emittedFiles = emitResult.emittedFiles;
3883        }
3884        return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true };
3885    }
3886
3887    /*@internal*/
3888    export function filterSemanticDiagnotics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] {
3889        return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]);
3890    }
3891
3892    /*@internal*/
3893    interface CompilerHostLike {
3894        useCaseSensitiveFileNames(): boolean;
3895        getCurrentDirectory(): string;
3896        fileExists(fileName: string): boolean;
3897        readFile(fileName: string): string | undefined;
3898        readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
3899        trace?(s: string): void;
3900        onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter;
3901    }
3902
3903    /* @internal */
3904    export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost {
3905        return {
3906            fileExists: f => directoryStructureHost.fileExists(f),
3907            readDirectory(root, extensions, excludes, includes, depth) {
3908                Debug.assertIsDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
3909                return directoryStructureHost.readDirectory(root, extensions, excludes, includes, depth);
3910            },
3911            readFile: f => directoryStructureHost.readFile(f),
3912            useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(),
3913            getCurrentDirectory: () => host.getCurrentDirectory(),
3914            onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined,
3915            trace: host.trace ? (s) => host.trace!(s) : undefined
3916        };
3917    }
3918
3919    // For backward compatibility
3920    /** @deprecated */ export interface ResolveProjectReferencePathHost {
3921        fileExists(fileName: string): boolean;
3922    }
3923
3924    /* @internal */
3925    export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) {
3926        if (!projectReferences) return emptyArray;
3927        let nodes: InputFiles[] | undefined;
3928        for (let i = 0; i < projectReferences.length; i++) {
3929            const ref = projectReferences[i];
3930            const resolvedRefOpts = getCommandLine(ref, i);
3931            if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) {
3932                const out = outFile(resolvedRefOpts.options);
3933                // Upstream project didn't have outFile set -- skip (error will have been issued earlier)
3934                if (!out) continue;
3935
3936                const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true);
3937                const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath);
3938                (nodes || (nodes = [])).push(node);
3939            }
3940        }
3941        return nodes || emptyArray;
3942    }
3943    /**
3944     * Returns the target config filename of a project reference.
3945     * Note: The file might not exist.
3946     */
3947    export function resolveProjectReferencePath(ref: ProjectReference): ResolvedConfigFileName;
3948    /** @deprecated */ export function resolveProjectReferencePath(host: ResolveProjectReferencePathHost, ref: ProjectReference): ResolvedConfigFileName;
3949    export function resolveProjectReferencePath(hostOrRef: ResolveProjectReferencePathHost | ProjectReference, ref?: ProjectReference): ResolvedConfigFileName {
3950        const passedInRef = ref ? ref : hostOrRef as ProjectReference;
3951        return resolveConfigFileProjectName(passedInRef.path);
3952    }
3953
3954    /* @internal */
3955    /**
3956     * Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
3957     * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
3958     * This returns a diagnostic even if the module will be an untyped module.
3959     */
3960    export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
3961        switch (extension) {
3962            case Extension.Ts:
3963            case Extension.Dts:
3964            case Extension.Ets:
3965            case Extension.Dets:
3966                // These are always allowed.
3967                return undefined;
3968            case Extension.Tsx:
3969                return needJsx();
3970            case Extension.Jsx:
3971                return needJsx() || needAllowJs();
3972            case Extension.Js:
3973                return needAllowJs();
3974            case Extension.Json:
3975                return needResolveJsonModule();
3976        }
3977
3978        function needJsx() {
3979            return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
3980        }
3981        function needAllowJs() {
3982            return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
3983        }
3984        function needResolveJsonModule() {
3985            return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used;
3986        }
3987    }
3988
3989    function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
3990        const res = imports.map(i => i.text);
3991        for (const aug of moduleAugmentations) {
3992            if (aug.kind === SyntaxKind.StringLiteral) {
3993                res.push(aug.text);
3994            }
3995            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
3996        }
3997        return res;
3998    }
3999
4000    /* @internal */
4001    export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike {
4002        if (index < imports.length) return imports[index];
4003        let augIndex = imports.length;
4004        for (const aug of moduleAugmentations) {
4005            if (aug.kind === SyntaxKind.StringLiteral) {
4006                if (index === augIndex) return aug;
4007                augIndex++;
4008            }
4009            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
4010        }
4011        Debug.fail("should never ask for module name at index higher than possible module name");
4012    }
4013}
4014