• 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) || (sourceFile.isDeclarationFile && !!options.needDoArkTsLinter)) {
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                // Only check and block .d.ts import .ets behavior when it is called by "ets-loader" and scanned.
1905                const filterFlag = !!options.needDoArkTsLinter;
1906
1907                if (filterFlag) {
1908                    options.skipLibCheck = false;
1909                }
1910
1911                if (skipTypeChecking(sourceFile, options, program)) {
1912                    return emptyArray;
1913                }
1914
1915                const typeChecker = getDiagnosticsProducingTypeChecker();
1916
1917                Debug.assert(!!sourceFile.bindDiagnostics);
1918
1919                const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
1920                const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
1921                // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
1922                const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX ||
1923                    sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred || sourceFile.scriptKind === ScriptKind.ETS);
1924                const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
1925                const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
1926
1927                return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, filterFlag ?
1928                    filterDiagnostics(checkDiagnostics) : checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
1929            });
1930        }
1931
1932        function filterDiagnostics(allDiagnostics: (readonly Diagnostic[] | undefined)): readonly Diagnostic[] | undefined {
1933            if (allDiagnostics) {
1934                const diagnosticsAfterFilter = allDiagnostics.filter((item) => {
1935                    const isOhModule = (item.file !== undefined) && (normalizePath(item.file.fileName).indexOf("/oh_modules/") !== -1);
1936                    const isNotForbiddenImportDiag = item.messageText !== (options.isCompatibleVersion ?
1937                        Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_about_to_be_forbidden.message :
1938                        Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_forbidden.message);
1939
1940                    if (isOhModule) {
1941                        if (options.skipTscOhModuleCheck) {
1942                            return false;
1943                        }
1944                        if (item.file?.isDeclarationFile) {
1945                            return false;
1946                        }
1947                        return isNotForbiddenImportDiag;
1948                    }
1949
1950                    if (item.file?.scriptKind === ScriptKind.TS && item.file?.isDeclarationFile) {
1951                        return !isNotForbiddenImportDiag;
1952                    }
1953                    return true;
1954                });
1955                return diagnosticsAfterFilter;
1956            }
1957            return emptyArray;
1958        }
1959
1960        function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) {
1961            const flatDiagnostics = flatten(allDiagnostics);
1962            if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) {
1963                return flatDiagnostics;
1964            }
1965
1966            const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics);
1967
1968            for (const errorExpectation of directives.getUnusedExpectations()) {
1969                diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive));
1970            }
1971
1972            return diagnostics;
1973        }
1974
1975        /**
1976         * Creates a map of comment directives along with the diagnostics immediately preceded by one of them.
1977         * Comments that match to any of those diagnostics are marked as used.
1978         */
1979        function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) {
1980            // Diagnostics are only reported if there is no comment directive preceding them
1981            // This will modify the directives map by marking "used" ones with a corresponding diagnostic
1982            const directives = createCommentDirectivesMap(sourceFile, commentDirectives);
1983            const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1);
1984
1985            return { diagnostics, directives };
1986        }
1987
1988        function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
1989            return runWithCancellationToken(() => {
1990                return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
1991            });
1992        }
1993
1994        /**
1995         * @returns The line index marked as preceding the diagnostic, or -1 if none was.
1996         */
1997        function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) {
1998            const { file, start } = diagnostic;
1999            if (!file) {
2000                return -1;
2001            }
2002
2003            // Start out with the line just before the text
2004            const lineStarts = getLineStarts(file);
2005            let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217
2006            while (line >= 0) {
2007                // As soon as that line is known to have a comment directive, use that
2008                if (directives.markUsed(line)) {
2009                    return line;
2010                }
2011
2012                // Stop searching if the line is not empty and not a comment
2013                const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim();
2014                if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) {
2015                    return -1;
2016                }
2017
2018                line--;
2019            }
2020
2021            return -1;
2022        }
2023
2024        function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
2025            return runWithCancellationToken(() => {
2026                const diagnostics: DiagnosticWithLocation[] = [];
2027                walk(sourceFile, sourceFile);
2028                forEachChildRecursively(sourceFile, walk, walkArray);
2029
2030                return diagnostics;
2031
2032                function walk(node: Node, parent: Node) {
2033                    // Return directly from the case if the given node doesnt want to visit each child
2034                    // Otherwise break to visit each child
2035
2036                    switch (parent.kind) {
2037                        case SyntaxKind.Parameter:
2038                        case SyntaxKind.PropertyDeclaration:
2039                        case SyntaxKind.MethodDeclaration:
2040                            if ((<ParameterDeclaration | PropertyDeclaration | MethodDeclaration>parent).questionToken === node) {
2041                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?"));
2042                                return "skip";
2043                            }
2044                        // falls through
2045                        case SyntaxKind.MethodSignature:
2046                        case SyntaxKind.Constructor:
2047                        case SyntaxKind.GetAccessor:
2048                        case SyntaxKind.SetAccessor:
2049                        case SyntaxKind.FunctionExpression:
2050                        case SyntaxKind.FunctionDeclaration:
2051                        case SyntaxKind.ArrowFunction:
2052                        case SyntaxKind.VariableDeclaration:
2053                            // type annotation
2054                            if ((<FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration>parent).type === node) {
2055                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files));
2056                                return "skip";
2057                            }
2058                    }
2059
2060                    switch (node.kind) {
2061                        case SyntaxKind.ImportClause:
2062                            if ((node as ImportClause).isTypeOnly) {
2063                                diagnostics.push(createDiagnosticForNode(parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type"));
2064                                return "skip";
2065                            }
2066                            break;
2067                        case SyntaxKind.ExportDeclaration:
2068                            if ((node as ExportDeclaration).isTypeOnly) {
2069                                diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type"));
2070                                return "skip";
2071                            }
2072                            break;
2073                        case SyntaxKind.ImportEqualsDeclaration:
2074                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files));
2075                            return "skip";
2076                        case SyntaxKind.ExportAssignment:
2077                            if ((<ExportAssignment>node).isExportEquals) {
2078                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files));
2079                                return "skip";
2080                            }
2081                            break;
2082                        case SyntaxKind.HeritageClause:
2083                            const heritageClause = <HeritageClause>node;
2084                            if (heritageClause.token === SyntaxKind.ImplementsKeyword) {
2085                                diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files));
2086                                return "skip";
2087                            }
2088                            break;
2089                        case SyntaxKind.InterfaceDeclaration:
2090                            const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword);
2091                            Debug.assertIsDefined(interfaceKeyword);
2092                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword));
2093                            return "skip";
2094                        case SyntaxKind.ModuleDeclaration:
2095                            const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword);
2096                            Debug.assertIsDefined(moduleKeyword);
2097                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword));
2098                            return "skip";
2099                        case SyntaxKind.TypeAliasDeclaration:
2100                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files));
2101                            return "skip";
2102                        case SyntaxKind.EnumDeclaration:
2103                            const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword));
2104                            diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword));
2105                            return "skip";
2106                        case SyntaxKind.NonNullExpression:
2107                            diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files));
2108                            return "skip";
2109                        case SyntaxKind.AsExpression:
2110                            diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files));
2111                            return "skip";
2112                        case SyntaxKind.TypeAssertionExpression:
2113                            Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
2114                    }
2115                }
2116
2117                function walkArray(nodes: NodeArray<Node>, parent: Node) {
2118                    if (parent.decorators === nodes && !options.experimentalDecorators) {
2119                        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));
2120                    }
2121
2122                    switch (parent.kind) {
2123                        case SyntaxKind.ClassDeclaration:
2124                        case SyntaxKind.ClassExpression:
2125                        case SyntaxKind.StructDeclaration:
2126                        case SyntaxKind.MethodDeclaration:
2127                        case SyntaxKind.Constructor:
2128                        case SyntaxKind.GetAccessor:
2129                        case SyntaxKind.SetAccessor:
2130                        case SyntaxKind.FunctionExpression:
2131                        case SyntaxKind.FunctionDeclaration:
2132                        case SyntaxKind.ArrowFunction:
2133                            // Check type parameters
2134                            if (nodes === (<DeclarationWithTypeParameterChildren>parent).typeParameters) {
2135                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files));
2136                                return "skip";
2137                            }
2138                        // falls through
2139
2140                        case SyntaxKind.VariableStatement:
2141                            // Check modifiers
2142                            if (nodes === parent.modifiers) {
2143                                checkModifiers(parent.modifiers, parent.kind === SyntaxKind.VariableStatement);
2144                                return "skip";
2145                            }
2146                            break;
2147                        case SyntaxKind.PropertyDeclaration:
2148                            // Check modifiers of property declaration
2149                            if (nodes === (<PropertyDeclaration>parent).modifiers) {
2150                                for (const modifier of <NodeArray<Modifier>>nodes) {
2151                                    if (modifier.kind !== SyntaxKind.StaticKeyword) {
2152                                        diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2153                                    }
2154                                }
2155                                return "skip";
2156                            }
2157                            break;
2158                        case SyntaxKind.Parameter:
2159                            // Check modifiers of parameter declaration
2160                            if (nodes === (<ParameterDeclaration>parent).modifiers) {
2161                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files));
2162                                return "skip";
2163                            }
2164                            break;
2165                        case SyntaxKind.CallExpression:
2166                        case SyntaxKind.NewExpression:
2167                        case SyntaxKind.ExpressionWithTypeArguments:
2168                        case SyntaxKind.JsxSelfClosingElement:
2169                        case SyntaxKind.JsxOpeningElement:
2170                        case SyntaxKind.TaggedTemplateExpression:
2171                            // Check type arguments
2172                            if (nodes === (<NodeWithTypeArguments>parent).typeArguments) {
2173                                diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files));
2174                                return "skip";
2175                            }
2176                            break;
2177                    }
2178                }
2179
2180                function checkModifiers(modifiers: NodeArray<Modifier>, isConstValid: boolean) {
2181                    for (const modifier of modifiers) {
2182                        switch (modifier.kind) {
2183                            case SyntaxKind.ConstKeyword:
2184                                if (isConstValid) {
2185                                    continue;
2186                                }
2187                            // to report error,
2188                            // falls through
2189                            case SyntaxKind.PublicKeyword:
2190                            case SyntaxKind.PrivateKeyword:
2191                            case SyntaxKind.ProtectedKeyword:
2192                            case SyntaxKind.ReadonlyKeyword:
2193                            case SyntaxKind.DeclareKeyword:
2194                            case SyntaxKind.AbstractKeyword:
2195                                diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind)));
2196                                break;
2197
2198                            // These are all legal modifiers.
2199                            case SyntaxKind.StaticKeyword:
2200                            case SyntaxKind.ExportKeyword:
2201                            case SyntaxKind.DefaultKeyword:
2202                        }
2203                    }
2204                }
2205
2206                function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2207                    const start = nodes.pos;
2208                    return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2);
2209                }
2210
2211                // Since these are syntactic diagnostics, parent might not have been set
2212                // this means the sourceFile cannot be infered from the node
2213                function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation {
2214                    return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2);
2215                }
2216            });
2217        }
2218
2219        function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2220            return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache);
2221        }
2222
2223        function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] {
2224            return runWithCancellationToken(() => {
2225                const resolver = getDiagnosticsProducingTypeChecker().getEmitResolver(sourceFile, cancellationToken);
2226                // Don't actually write any files since we're just getting diagnostics.
2227                return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray;
2228            });
2229        }
2230
2231        function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>(
2232            sourceFile: T,
2233            cancellationToken: CancellationToken | undefined,
2234            cache: DiagnosticCache<U>,
2235            getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined) => readonly U[],
2236        ): readonly U[] {
2237
2238            const cachedResult = sourceFile
2239                ? cache.perFile?.get(sourceFile.path)
2240                : cache.allDiagnostics;
2241
2242            if (cachedResult) {
2243                return cachedResult;
2244            }
2245            const result = getDiagnostics(sourceFile, cancellationToken);
2246            if (sourceFile) {
2247                (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result);
2248            }
2249            else {
2250                cache.allDiagnostics = result;
2251            }
2252            return result;
2253        }
2254
2255        function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
2256            return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken);
2257        }
2258
2259        function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> {
2260            return sortAndDeduplicateDiagnostics(concatenate(
2261                programDiagnostics.getGlobalDiagnostics(),
2262                getOptionsDiagnosticsOfConfigFile()
2263            ));
2264        }
2265
2266        function getOptionsDiagnosticsOfConfigFile() {
2267            if (!options.configFile) { return emptyArray; }
2268            let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName);
2269            forEachResolvedProjectReference(resolvedRef => {
2270                diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName));
2271            });
2272            return diagnostics;
2273        }
2274
2275        function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> {
2276            return rootNames.length ? sortAndDeduplicateDiagnostics(getDiagnosticsProducingTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>;
2277        }
2278
2279        function getConfigFileParsingDiagnostics(): readonly Diagnostic[] {
2280            return configFileParsingDiagnostics || emptyArray;
2281        }
2282
2283        function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) {
2284            processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason);
2285        }
2286
2287        function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
2288            return a.fileName === b.fileName;
2289        }
2290
2291        function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean {
2292            return a.kind === SyntaxKind.Identifier
2293                ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText
2294                : b.kind === SyntaxKind.StringLiteral && a.text === b.text;
2295        }
2296
2297        function createSyntheticImport(text: string, file: SourceFile) {
2298            const externalHelpersModuleReference = factory.createStringLiteral(text);
2299            const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
2300            addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
2301            setParent(externalHelpersModuleReference, importDecl);
2302            setParent(importDecl, file);
2303            // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them
2304            // (which is required to build the dependency graph for incremental emit)
2305            (externalHelpersModuleReference as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2306            (importDecl as Mutable<Node>).flags &= ~NodeFlags.Synthesized;
2307            return externalHelpersModuleReference;
2308        }
2309
2310        function collectExternalModuleReferences(file: SourceFile): void {
2311            if (file.imports) {
2312                return;
2313            }
2314
2315            const isJavaScriptFile = isSourceFileJS(file);
2316            const isExternalModuleFile = isExternalModule(file);
2317
2318            // file.imports may not be undefined if there exists dynamic import
2319            let imports: StringLiteralLike[] | undefined;
2320            let moduleAugmentations: (StringLiteral | Identifier)[] | undefined;
2321            let ambientModules: string[] | undefined;
2322
2323            // If we are importing helpers, we need to add a synthetic reference to resolve the
2324            // helpers library.
2325            if ((options.isolatedModules || isExternalModuleFile)
2326                && !file.isDeclarationFile) {
2327                if (options.importHelpers) {
2328                    // synthesize 'import "tslib"' declaration
2329                    imports = [createSyntheticImport(externalHelpersModuleNameText, file)];
2330                }
2331                const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options);
2332                if (jsxImport) {
2333                    // synthesize `import "base/jsx-runtime"` declaration
2334                    (imports ||= []).push(createSyntheticImport(jsxImport, file));
2335                }
2336            }
2337
2338            for (const node of file.statements) {
2339                collectModuleReferences(node, /*inAmbientModule*/ false);
2340            }
2341            if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) {
2342                collectDynamicImportOrRequireCalls(file);
2343            }
2344
2345            file.imports = imports || emptyArray;
2346            file.moduleAugmentations = moduleAugmentations || emptyArray;
2347            file.ambientModuleNames = ambientModules || emptyArray;
2348
2349            return;
2350
2351            function collectModuleReferences(node: Statement, inAmbientModule: boolean): void {
2352                if (isAnyImportOrReExport(node)) {
2353                    const moduleNameExpr = getExternalModuleName(node);
2354                    // TypeScript 1.0 spec (April 2014): 12.1.6
2355                    // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
2356                    // only through top - level external module names. Relative external module names are not permitted.
2357                    if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
2358                        imports = append(imports, moduleNameExpr);
2359                    }
2360                }
2361                else if (isModuleDeclaration(node)) {
2362                    if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
2363                        const nameText = getTextOfIdentifierOrLiteral(node.name);
2364                        // Ambient module declarations can be interpreted as augmentations for some existing external modules.
2365                        // This will happen in two cases:
2366                        // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope
2367                        // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name
2368                        //   immediately nested in top level ambient module declaration .
2369                        if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) {
2370                            (moduleAugmentations || (moduleAugmentations = [])).push(node.name);
2371                        }
2372                        else if (!inAmbientModule) {
2373                            if (file.isDeclarationFile) {
2374                                // for global .d.ts files record name of ambient module
2375                                (ambientModules || (ambientModules = [])).push(nameText);
2376                            }
2377                            // An AmbientExternalModuleDeclaration declares an external module.
2378                            // This type of declaration is permitted only in the global module.
2379                            // The StringLiteral must specify a top - level external module name.
2380                            // Relative external module names are not permitted
2381
2382                            // NOTE: body of ambient module is always a module block, if it exists
2383                            const body = <ModuleBlock>(<ModuleDeclaration>node).body;
2384                            if (body) {
2385                                for (const statement of body.statements) {
2386                                    collectModuleReferences(statement, /*inAmbientModule*/ true);
2387                                }
2388                            }
2389                        }
2390                    }
2391                }
2392            }
2393
2394            function collectDynamicImportOrRequireCalls(file: SourceFile) {
2395                const r = /import|require/g;
2396                while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null
2397                    const node = getNodeAtPosition(file, r.lastIndex);
2398                    if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) {
2399                        imports = append(imports, node.arguments[0]);
2400                    }
2401                    // we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
2402                    else if (isImportCall(node) && node.arguments.length === 1 && isStringLiteralLike(node.arguments[0])) {
2403                        imports = append(imports, node.arguments[0] as StringLiteralLike);
2404                    }
2405                    else if (isLiteralImportTypeNode(node)) {
2406                        imports = append(imports, node.argument.literal);
2407                    }
2408                }
2409            }
2410
2411            /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */
2412            function getNodeAtPosition(sourceFile: SourceFile, position: number): Node {
2413                let current: Node = sourceFile;
2414                const getContainingChild = (child: Node) => {
2415                    if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) {
2416                        return child;
2417                    }
2418                };
2419                while (true) {
2420                    const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild);
2421                    if (!child) {
2422                        return current;
2423                    }
2424                    current = child;
2425                }
2426            }
2427        }
2428
2429        function getLibFileFromReference(ref: FileReference) {
2430            const libName = toFileNameLowerCase(ref.fileName);
2431            const libFileName = libMap.get(libName);
2432            if (libFileName) {
2433                return getSourceFile(combinePaths(defaultLibraryPath, libFileName));
2434            }
2435        }
2436
2437        /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */
2438        function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined {
2439            return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile);
2440        }
2441
2442        function getSourceFileFromReferenceWorker(
2443            fileName: string,
2444            getSourceFile: (fileName: string) => SourceFile | undefined,
2445            fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void,
2446            reason?: FileIncludeReason): SourceFile | undefined {
2447
2448            if (hasExtension(fileName)) {
2449                const canonicalFileName = host.getCanonicalFileName(fileName);
2450                if (!options.allowNonTsExtensions && !forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(canonicalFileName, extension)) && !fileExtensionIs(canonicalFileName, Extension.Ets)) {
2451                    if (fail) {
2452                        if (hasJSFileExtension(canonicalFileName)) {
2453                            fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName);
2454                        }
2455                        else {
2456                            fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2457                        }
2458                    }
2459                    return undefined;
2460                }
2461
2462                const sourceFile = getSourceFile(fileName);
2463                if (fail) {
2464                    if (!sourceFile) {
2465                        const redirect = getProjectReferenceRedirect(fileName);
2466                        if (redirect) {
2467                            fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName);
2468                        }
2469                        else {
2470                            fail(Diagnostics.File_0_not_found, fileName);
2471                        }
2472                    }
2473                    else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) {
2474                        fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
2475                    }
2476                }
2477                return sourceFile;
2478            }
2479            else {
2480                const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName);
2481                if (sourceFileNoExtension) return sourceFileNoExtension;
2482
2483                if (fail && options.allowNonTsExtensions) {
2484                    fail(Diagnostics.File_0_not_found, fileName);
2485                    return undefined;
2486                }
2487
2488                const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
2489                if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + supportedExtensions.join("', '") + "'");
2490                return sourceFileWithAddedExtension;
2491            }
2492        }
2493
2494        /** This has side effects through `findSourceFile`. */
2495        function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void {
2496            getSourceFileFromReferenceWorker(
2497                fileName,
2498                fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217
2499                (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args),
2500                reason
2501            );
2502        }
2503
2504        function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) {
2505            return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason);
2506        }
2507
2508        function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void {
2509            const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile);
2510            if (hasExistingReasonToReportErrorOn) {
2511                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]);
2512            }
2513            else {
2514                addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]);
2515            }
2516        }
2517
2518        function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string): SourceFile {
2519            const redirect: SourceFile = Object.create(redirectTarget);
2520            redirect.fileName = fileName;
2521            redirect.path = path;
2522            redirect.resolvedPath = resolvedPath;
2523            redirect.originalFileName = originalFileName;
2524            redirect.redirectInfo = { redirectTarget, unredirected };
2525            sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2526            Object.defineProperties(redirect, {
2527                id: {
2528                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; },
2529                    set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; },
2530                },
2531                symbol: {
2532                    get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; },
2533                    set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; },
2534                },
2535            });
2536            return redirect;
2537        }
2538
2539        // Get source file from normalized fileName
2540        function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2541            tracing?.push(tracing.Phase.Program, "findSourceFile", {
2542                fileName,
2543                isDefaultLib: isDefaultLib || undefined,
2544                fileIncludeKind: (FileIncludeKind as any)[reason.kind],
2545            });
2546            const result = findSourceFileWorker(fileName, path, isDefaultLib, ignoreNoDefaultLib, reason, packageId);
2547            tracing?.pop();
2548            return result;
2549        }
2550
2551        function findSourceFileWorker(fileName: string, path: Path, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined {
2552            if (useSourceOfProjectReferenceRedirect) {
2553                let source = getSourceOfProjectReferenceRedirect(fileName);
2554                // If preserveSymlinks is true, module resolution wont jump the symlink
2555                // but the resolved real path may be the .d.ts from project reference
2556                // Note:: Currently we try the real path only if the
2557                // file is from node_modules or oh_modules to avoid having to run real path on all file paths
2558                const modulesPathPart: string = isOhpm(options.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
2559                if (!source &&
2560                    host.realpath &&
2561                    options.preserveSymlinks &&
2562                    isDeclarationFileName(fileName) &&
2563                    stringContains(fileName, modulesPathPart)) {
2564                    const realPath = host.realpath(fileName);
2565                    if (realPath !== fileName) source = getSourceOfProjectReferenceRedirect(realPath);
2566                }
2567                if (source) {
2568                    const file = isString(source) ?
2569                        findSourceFile(source, toPath(source), isDefaultLib, ignoreNoDefaultLib, reason, packageId) :
2570                        undefined;
2571                    if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined);
2572                    return file;
2573                }
2574            }
2575            const originalFileName = fileName;
2576            if (filesByName.has(path)) {
2577                const file = filesByName.get(path);
2578                addFileIncludeReason(file || undefined, reason);
2579                // try to check if we've already seen this file but with a different casing in path
2580                // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected
2581                if (file && options.forceConsistentCasingInFileNames) {
2582                    const checkedName = file.fileName;
2583                    const isRedirect = toPath(checkedName) !== toPath(fileName);
2584                    if (isRedirect) {
2585                        fileName = getProjectReferenceRedirect(fileName) || fileName;
2586                    }
2587                    // Check if it differs only in drive letters its ok to ignore that error:
2588                    const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory);
2589                    const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory);
2590                    if (checkedAbsolutePath !== inputAbsolutePath) {
2591                        reportFileNamesDifferOnlyInCasingError(fileName, file, reason);
2592                    }
2593                }
2594
2595                // If the file was previously found via a node_modules or oh_modules search, but is now being processed as a root file,
2596                // then everything it sucks in may also be marked incorrectly, and needs to be checked again.
2597                if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) {
2598                    sourceFilesFoundSearchingNodeModules.set(file.path, false);
2599                    if (!options.noResolve) {
2600                        processReferencedFiles(file, isDefaultLib);
2601                        processTypeReferenceDirectives(file);
2602                    }
2603                    if (!options.noLib) {
2604                        processLibReferenceDirectives(file);
2605                    }
2606
2607                    modulesWithElidedImports.set(file.path, false);
2608                    processImportedModules(file);
2609                }
2610                // See if we need to reprocess the imports due to prior skipped imports
2611                else if (file && modulesWithElidedImports.get(file.path)) {
2612                    if (currentNodeModulesDepth < maxNodeModuleJsDepth) {
2613                        modulesWithElidedImports.set(file.path, false);
2614                        processImportedModules(file);
2615                    }
2616                }
2617
2618                return file || undefined;
2619            }
2620
2621            let redirectedPath: Path | undefined;
2622            if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) {
2623                const redirectProject = getProjectReferenceRedirectProject(fileName);
2624                if (redirectProject) {
2625                    if (outFile(redirectProject.commandLine.options)) {
2626                        // Shouldnt create many to 1 mapping file in --out scenario
2627                        return undefined;
2628                    }
2629                    const redirect = getProjectReferenceOutputName(redirectProject, fileName);
2630                    fileName = redirect;
2631                    // Once we start redirecting to a file, we can potentially come back to it
2632                    // via a back-reference from another file in the .d.ts folder. If that happens we'll
2633                    // end up trying to add it to the program *again* because we were tracking it via its
2634                    // original (un-redirected) name. So we have to map both the original path and the redirected path
2635                    // to the source file we're about to find/create
2636                    redirectedPath = toPath(redirect);
2637                }
2638            }
2639
2640            // We haven't looked for this file, do so now and cache result
2641            const file = host.getSourceFile(
2642                fileName,
2643                options.target!,
2644                hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]),
2645                shouldCreateNewSourceFile
2646            );
2647
2648            if (packageId) {
2649                const packageIdKey = packageIdToString(packageId);
2650                const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
2651                if (fileFromPackageId) {
2652                    // Some other SourceFile already exists with this package name and version.
2653                    // Instead of creating a duplicate, just redirect to the existing one.
2654                    const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName); // TODO: GH#18217
2655                    redirectTargetsMap.add(fileFromPackageId.path, fileName);
2656                    addFileToFilesByName(dupFile, path, redirectedPath);
2657                    addFileIncludeReason(dupFile, reason);
2658                    sourceFileToPackageName.set(path, packageId.name);
2659                    processingOtherFiles!.push(dupFile);
2660                    return dupFile;
2661                }
2662                else if (file) {
2663                    // This is the first source file to have this packageId.
2664                    packageIdToSourceFile.set(packageIdKey, file);
2665                    sourceFileToPackageName.set(path, packageId.name);
2666                }
2667            }
2668            addFileToFilesByName(file, path, redirectedPath);
2669
2670            if (file) {
2671                sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
2672                file.fileName = fileName; // Ensure that source file has same name as what we were looking for
2673                file.path = path;
2674                file.resolvedPath = toPath(fileName);
2675                file.originalFileName = originalFileName;
2676                addFileIncludeReason(file, reason);
2677
2678                if (host.useCaseSensitiveFileNames()) {
2679                    const pathLowerCase = toFileNameLowerCase(path);
2680                    // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
2681                    const existingFile = filesByNameIgnoreCase!.get(pathLowerCase);
2682                    if (existingFile) {
2683                        reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason);
2684                    }
2685                    else {
2686                        filesByNameIgnoreCase!.set(pathLowerCase, file);
2687                    }
2688                }
2689
2690                skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib);
2691
2692                if (!options.noResolve) {
2693                    processReferencedFiles(file, isDefaultLib);
2694                    processTypeReferenceDirectives(file);
2695                }
2696                if (!options.noLib) {
2697                    processLibReferenceDirectives(file);
2698                }
2699
2700
2701                // always process imported modules to record module name resolutions
2702                processImportedModules(file);
2703
2704                if (isDefaultLib) {
2705                    processingDefaultLibFiles!.push(file);
2706                }
2707                else {
2708                    processingOtherFiles!.push(file);
2709                }
2710            }
2711            return file;
2712        }
2713
2714        function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) {
2715            if (file) fileReasons.add(file.path, reason);
2716        }
2717
2718        function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) {
2719            if (redirectedPath) {
2720                filesByName.set(redirectedPath, file);
2721                filesByName.set(path, file || false);
2722            }
2723            else {
2724                filesByName.set(path, file);
2725            }
2726        }
2727
2728        function getProjectReferenceRedirect(fileName: string): string | undefined {
2729            const referencedProject = getProjectReferenceRedirectProject(fileName);
2730            return referencedProject && getProjectReferenceOutputName(referencedProject, fileName);
2731        }
2732
2733        function getProjectReferenceRedirectProject(fileName: string) {
2734            // Ignore dts or any json files
2735            if (!resolvedProjectReferences || !resolvedProjectReferences.length || isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json)) {
2736                return undefined;
2737            }
2738
2739            // If this file is produced by a referenced project, we need to rewrite it to
2740            // look in the output folder of the referenced project rather than the input
2741            return getResolvedProjectReferenceToRedirect(fileName);
2742        }
2743
2744
2745        function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) {
2746            const out = outFile(referencedProject.commandLine.options);
2747            return out ?
2748                changeExtension(out, Extension.Dts) :
2749                getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames());
2750        }
2751
2752        /**
2753         * Get the referenced project if the file is input file from that reference project
2754         */
2755        function getResolvedProjectReferenceToRedirect(fileName: string) {
2756            if (mapFromFileToProjectReferenceRedirects === undefined) {
2757                mapFromFileToProjectReferenceRedirects = new Map();
2758                forEachResolvedProjectReference(referencedProject => {
2759                    // not input file from the referenced project, ignore
2760                    if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) {
2761                        referencedProject.commandLine.fileNames.forEach(f =>
2762                            mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path));
2763                    }
2764                });
2765            }
2766
2767            const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName));
2768            return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath);
2769        }
2770
2771        function forEachResolvedProjectReference<T>(
2772            cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined
2773        ): T | undefined {
2774            return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb);
2775        }
2776
2777        function getSourceOfProjectReferenceRedirect(file: string) {
2778            if (!isDeclarationFileName(file)) return undefined;
2779            if (mapFromToProjectReferenceRedirectSource === undefined) {
2780                mapFromToProjectReferenceRedirectSource = new Map();
2781                forEachResolvedProjectReference(resolvedRef => {
2782                    const out = outFile(resolvedRef.commandLine.options);
2783                    if (out) {
2784                        // Dont know which source file it means so return true?
2785                        const outputDts = changeExtension(out, Extension.Dts);
2786                        mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true);
2787                    }
2788                    else {
2789                        const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames()));
2790                        forEach(resolvedRef.commandLine.fileNames, fileName => {
2791                            if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) {
2792                                const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory);
2793                                mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName);
2794                            }
2795                        });
2796                    }
2797                });
2798            }
2799            return mapFromToProjectReferenceRedirectSource.get(toPath(file));
2800        }
2801
2802        function isSourceOfProjectReferenceRedirect(fileName: string) {
2803            return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName);
2804        }
2805
2806        function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined {
2807            if (!projectReferenceRedirects) {
2808                return undefined;
2809            }
2810
2811            return projectReferenceRedirects.get(projectReferencePath) || undefined;
2812        }
2813
2814        function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
2815            forEach(file.referencedFiles, (ref, index) => {
2816                processSourceFile(
2817                    resolveTripleslashReference(ref.fileName, file.fileName),
2818                    isDefaultLib,
2819                    /*ignoreNoDefaultLib*/ false,
2820                    /*packageId*/ undefined,
2821                    { kind: FileIncludeKind.ReferenceFile, file: file.path, index, }
2822                );
2823            });
2824        }
2825
2826        function processTypeReferenceDirectives(file: SourceFile) {
2827            // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
2828            const typeDirectives = map(file.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
2829            if (!typeDirectives) {
2830                return;
2831            }
2832
2833            const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file);
2834            for (let index = 0; index < typeDirectives.length; index++) {
2835                const ref = file.typeReferenceDirectives[index];
2836                const resolvedTypeReferenceDirective = resolutions[index];
2837                // store resolved type directive on the file
2838                const fileName = toFileNameLowerCase(ref.fileName);
2839                setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
2840                processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
2841            }
2842        }
2843
2844        function processTypeReferenceDirective(
2845            typeReferenceDirective: string,
2846            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2847            reason: FileIncludeReason
2848        ): void {
2849            tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
2850            processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason);
2851            tracing?.pop();
2852        }
2853
2854        function processTypeReferenceDirectiveWorker(
2855            typeReferenceDirective: string,
2856            resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
2857            reason: FileIncludeReason
2858        ): void {
2859
2860            // If we already found this library as a primary reference - nothing to do
2861            const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
2862            if (previousResolution && previousResolution.primary) {
2863                return;
2864            }
2865            let saveResolution = true;
2866            if (resolvedTypeReferenceDirective) {
2867                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++;
2868
2869                if (resolvedTypeReferenceDirective.primary) {
2870                    // resolved from the primary path
2871                    processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217
2872                }
2873                else {
2874                    // If we already resolved to this file, it must have been a secondary reference. Check file contents
2875                    // for sameness and possibly issue an error
2876                    if (previousResolution) {
2877                        // Don't bother reading the file again if it's the same file.
2878                        if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) {
2879                            const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!);
2880                            const existingFile = getSourceFile(previousResolution.resolvedFileName!)!;
2881                            if (otherFileText !== existingFile.text) {
2882                                addFilePreprocessingFileExplainingDiagnostic(
2883                                    existingFile,
2884                                    reason,
2885                                    Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict,
2886                                    [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName]
2887                                );
2888                            }
2889                        }
2890                        // don't overwrite previous resolution result
2891                        saveResolution = false;
2892                    }
2893                    else {
2894                        // First resolution of this library
2895                        processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason);
2896                    }
2897                }
2898
2899                if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--;
2900            }
2901            else {
2902                addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]);
2903            }
2904
2905            if (saveResolution) {
2906                resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
2907            }
2908        }
2909
2910        function processLibReferenceDirectives(file: SourceFile) {
2911            forEach(file.libReferenceDirectives, (libReference, index) => {
2912                const libName = toFileNameLowerCase(libReference.fileName);
2913                const libFileName = libMap.get(libName);
2914                if (libFileName) {
2915                    // we ignore any 'no-default-lib' reference set on this file.
2916                    processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
2917                }
2918                else {
2919                    const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
2920                    const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity);
2921                    const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0;
2922                    (fileProcessingDiagnostics ||= []).push({
2923                        kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic,
2924                        reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, },
2925                        diagnostic,
2926                        args: [libName, suggestion]
2927                    });
2928                }
2929            });
2930        }
2931
2932        function getCanonicalFileName(fileName: string): string {
2933            return host.getCanonicalFileName(fileName);
2934        }
2935
2936        function processImportedModules(file: SourceFile) {
2937            collectExternalModuleReferences(file);
2938            if (file.imports.length || file.moduleAugmentations.length) {
2939                // Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
2940                const moduleNames = getModuleNames(file);
2941                const resolutions = resolveModuleNamesReusingOldState(moduleNames, file);
2942                Debug.assert(resolutions.length === moduleNames.length);
2943                for (let index = 0; index < moduleNames.length; index++) {
2944                    const resolution = resolutions[index];
2945                    setResolvedModule(file, moduleNames[index], resolution);
2946
2947                    if (!resolution) {
2948                        continue;
2949                    }
2950
2951                    const isFromNodeModulesSearch = resolution.isExternalLibraryImport;
2952                    const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension);
2953                    const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile;
2954                    const resolvedFileName = resolution.resolvedFileName;
2955
2956                    if (isFromNodeModulesSearch) {
2957                        currentNodeModulesDepth++;
2958                    }
2959
2960                    // add file to program only if:
2961                    // - resolution was successful
2962                    // - noResolve is falsy
2963                    // - module name comes from the list of imports
2964                    // - it's not a top level JavaScript module that exceeded the search max
2965                    const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
2966                    // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
2967                    // This may still end up being an untyped module -- the file won't be included but imports will be allowed.
2968                    const shouldAddFile = resolvedFileName
2969                        && !getResolutionDiagnostic(options, resolution)
2970                        && !options.noResolve
2971                        && index < file.imports.length
2972                        && !elideImport
2973                        && !(isJsFile && !getAllowJSCompilerOption(options))
2974                        && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc));
2975
2976                    if (elideImport) {
2977                        modulesWithElidedImports.set(file.path, true);
2978                    }
2979                    else if (shouldAddFile) {
2980                        const path = toPath(resolvedFileName);
2981                        findSourceFile(
2982                            resolvedFileName,
2983                            path,
2984                            /*isDefaultLib*/ false,
2985                            /*ignoreNoDefaultLib*/ false,
2986                            { kind: FileIncludeKind.Import, file: file.path, index, },
2987                            resolution.packageId,
2988                        );
2989                    }
2990
2991                    if (isFromNodeModulesSearch) {
2992                        currentNodeModulesDepth--;
2993                    }
2994                }
2995            }
2996            else {
2997                // no imports - drop cached module resolutions
2998                file.resolvedModules = undefined;
2999            }
3000        }
3001
3002        function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean {
3003            let allFilesBelongToPath = true;
3004            const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
3005            for (const sourceFile of sourceFiles) {
3006                if (!sourceFile.isDeclarationFile) {
3007                    const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
3008                    if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
3009                        addProgramDiagnosticExplainingFile(
3010                            sourceFile,
3011                            Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files,
3012                            [sourceFile.fileName, rootDirectory]
3013                        );
3014                        allFilesBelongToPath = false;
3015                    }
3016                }
3017            }
3018
3019            return allFilesBelongToPath;
3020        }
3021
3022        function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined {
3023            if (!projectReferenceRedirects) {
3024                projectReferenceRedirects = new Map();
3025            }
3026
3027            // The actual filename (i.e. add "/tsconfig.json" if necessary)
3028            const refPath = resolveProjectReferencePath(ref);
3029            const sourceFilePath = toPath(refPath);
3030            const fromCache = projectReferenceRedirects.get(sourceFilePath);
3031            if (fromCache !== undefined) {
3032                return fromCache || undefined;
3033            }
3034
3035            let commandLine: ParsedCommandLine | undefined;
3036            let sourceFile: JsonSourceFile | undefined;
3037            if (host.getParsedCommandLine) {
3038                commandLine = host.getParsedCommandLine(refPath);
3039                if (!commandLine) {
3040                    addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined);
3041                    projectReferenceRedirects.set(sourceFilePath, false);
3042                    return undefined;
3043                }
3044                sourceFile = Debug.checkDefined(commandLine.options.configFile);
3045                Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath);
3046                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3047            }
3048            else {
3049                // An absolute path pointing to the containing directory of the config file
3050                const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory());
3051                sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined;
3052                addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined);
3053                if (sourceFile === undefined) {
3054                    projectReferenceRedirects.set(sourceFilePath, false);
3055                    return undefined;
3056                }
3057                commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath);
3058            }
3059            sourceFile.fileName = refPath;
3060            sourceFile.path = sourceFilePath;
3061            sourceFile.resolvedPath = sourceFilePath;
3062            sourceFile.originalFileName = refPath;
3063
3064            const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile };
3065            projectReferenceRedirects.set(sourceFilePath, resolvedRef);
3066            if (commandLine.projectReferences) {
3067                resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile);
3068            }
3069            return resolvedRef;
3070        }
3071
3072        function verifyCompilerOptions() {
3073            if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
3074                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
3075            }
3076
3077            if (options.isolatedModules) {
3078                if (options.out) {
3079                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules");
3080                }
3081
3082                if (options.outFile) {
3083                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules");
3084                }
3085            }
3086
3087            if (options.inlineSourceMap) {
3088                if (options.sourceMap) {
3089                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap");
3090                }
3091                if (options.mapRoot) {
3092                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap");
3093                }
3094            }
3095
3096            if (options.composite) {
3097                if (options.declaration === false) {
3098                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration");
3099                }
3100                if (options.incremental === false) {
3101                    createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration");
3102                }
3103            }
3104
3105            const outputFile = outFile(options);
3106            if (options.tsBuildInfoFile) {
3107                if (!isIncrementalCompilation(options)) {
3108                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite");
3109                }
3110            }
3111            else if (options.incremental && !outputFile && !options.configFilePath) {
3112                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified));
3113            }
3114
3115            verifyProjectReferences();
3116
3117            // List of collected files is complete; validate exhautiveness if this is a project with a file list
3118            if (options.composite) {
3119                const rootPaths = new Set(rootNames.map(toPath));
3120                for (const file of files) {
3121                    // Ignore file that is not emitted
3122                    if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) {
3123                        addProgramDiagnosticExplainingFile(
3124                            file,
3125                            Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern,
3126                            [file.fileName, options.configFilePath || ""]
3127                        );
3128                    }
3129                }
3130            }
3131
3132            if (options.paths) {
3133                for (const key in options.paths) {
3134                    if (!hasProperty(options.paths, key)) {
3135                        continue;
3136                    }
3137                    if (!hasZeroOrOneAsteriskCharacter(key)) {
3138                        createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key);
3139                    }
3140                    if (isArray(options.paths[key])) {
3141                        const len = options.paths[key].length;
3142                        if (len === 0) {
3143                            createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key);
3144                        }
3145                        for (let i = 0; i < len; i++) {
3146                            const subst = options.paths[key][i];
3147                            const typeOfSubst = typeof subst;
3148                            if (typeOfSubst === "string") {
3149                                if (!hasZeroOrOneAsteriskCharacter(subst)) {
3150                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key);
3151                                }
3152                                if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) {
3153                                    createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash);
3154                                }
3155                            }
3156                            else {
3157                                createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst);
3158                            }
3159                        }
3160                    }
3161                    else {
3162                        createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key);
3163                    }
3164                }
3165            }
3166
3167            if (!options.sourceMap && !options.inlineSourceMap) {
3168                if (options.inlineSources) {
3169                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources");
3170                }
3171                if (options.sourceRoot) {
3172                    createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot");
3173                }
3174            }
3175
3176            if (options.out && options.outFile) {
3177                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile");
3178            }
3179
3180            if (options.mapRoot && !(options.sourceMap || options.declarationMap)) {
3181                // Error to specify --mapRoot without --sourcemap
3182                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap");
3183            }
3184
3185            if (options.declarationDir) {
3186                if (!getEmitDeclarations(options)) {
3187                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite");
3188                }
3189                if (outputFile) {
3190                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile");
3191                }
3192            }
3193
3194            if (options.declarationMap && !getEmitDeclarations(options)) {
3195                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite");
3196            }
3197
3198            if (options.lib && options.noLib) {
3199                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib");
3200            }
3201
3202            if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) {
3203                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
3204            }
3205
3206            const languageVersion = options.target || ScriptTarget.ES3;
3207
3208            const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
3209            if (options.isolatedModules) {
3210                if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) {
3211                    createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target");
3212                }
3213
3214                if (options.preserveConstEnums === false) {
3215                    createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules");
3216                }
3217
3218                const firstNonExternalModuleSourceFile = find(files, f => !isExternalModule(f) && !isSourceFileJS(f) && !f.isDeclarationFile && f.scriptKind !== ScriptKind.JSON);
3219                if (firstNonExternalModuleSourceFile) {
3220                    const span = getErrorSpanForNode(firstNonExternalModuleSourceFile, firstNonExternalModuleSourceFile);
3221                    programDiagnostics.add(createFileDiagnostic(firstNonExternalModuleSourceFile, span.start, span.length,
3222                        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)));
3223                }
3224            }
3225            else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) {
3226                // We cannot use createDiagnosticFromNode because nodes do not have parents yet
3227                const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3228                programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none));
3229            }
3230
3231            // Cannot specify module gen that isn't amd or system with --out
3232            if (outputFile && !options.emitDeclarationOnly) {
3233                if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) {
3234                    createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module");
3235                }
3236                else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) {
3237                    const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!);
3238                    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"));
3239                }
3240            }
3241
3242            if (options.resolveJsonModule) {
3243                if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
3244                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
3245                }
3246                // Any emit other than common js, amd, es2015 or esnext is error
3247                else if (!hasJsonModuleEmitEnabled(options)) {
3248                    createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module");
3249                }
3250            }
3251
3252            // there has to be common source directory if user specified --outdir || --sourceRoot
3253            // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
3254            if (options.outDir || // there is --outDir specified
3255                options.sourceRoot || // there is --sourceRoot specified
3256                options.mapRoot) { // there is --mapRoot specified
3257
3258                // Precalculate and cache the common source directory
3259                const dir = getCommonSourceDirectory();
3260
3261                // 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
3262                if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) {
3263                    createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir");
3264                }
3265            }
3266
3267            if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) {
3268                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields");
3269            }
3270
3271            if (options.checkJs && !getAllowJSCompilerOption(options)) {
3272                programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs"));
3273            }
3274
3275            if (options.emitDeclarationOnly) {
3276                if (!getEmitDeclarations(options)) {
3277                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite");
3278                }
3279
3280                if (options.noEmit) {
3281                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit");
3282                }
3283            }
3284
3285            if (options.emitDecoratorMetadata &&
3286                !options.experimentalDecorators) {
3287                createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators");
3288            }
3289
3290            if (options.jsxFactory) {
3291                if (options.reactNamespace) {
3292                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory");
3293                }
3294                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3295                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx));
3296                }
3297                if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) {
3298                    createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory);
3299                }
3300            }
3301            else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
3302                createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace);
3303            }
3304
3305            if (options.jsxFragmentFactory) {
3306                if (!options.jsxFactory) {
3307                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory");
3308                }
3309                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3310                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx));
3311                }
3312                if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) {
3313                    createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory);
3314                }
3315            }
3316
3317            if (options.reactNamespace) {
3318                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
3319                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx));
3320                }
3321            }
3322
3323            if (options.jsxImportSource) {
3324                if (options.jsx === JsxEmit.React) {
3325                    createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx));
3326                }
3327            }
3328
3329            // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files
3330            if (!options.noEmit && !options.suppressOutputPathCheck) {
3331                const emitHost = getEmitHost();
3332                const emitFilesSeen = new Set<string>();
3333                forEachEmittedFile(emitHost, (emitFileNames) => {
3334                    if (!options.emitDeclarationOnly) {
3335                        verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen);
3336                    }
3337                    verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen);
3338                });
3339            }
3340
3341            // Verify that all the emit files are unique and don't overwrite input files
3342            function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) {
3343                if (emitFileName) {
3344                    const emitFilePath = toPath(emitFileName);
3345                    // Report error if the output overwrites input file
3346                    if (filesByName.has(emitFilePath)) {
3347                        let chain: DiagnosticMessageChain | undefined;
3348                        if (!options.configFilePath) {
3349                            // The program is from either an inferred project or an external project
3350                            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);
3351                        }
3352                        chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName);
3353                        blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain));
3354                    }
3355
3356                    const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath;
3357                    // Report error if multiple files write into same file
3358                    if (emitFilesSeen.has(emitFileKey)) {
3359                        // Already seen the same emit file - report error
3360                        blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName));
3361                    }
3362                    else {
3363                        emitFilesSeen.add(emitFileKey);
3364                    }
3365                }
3366            }
3367        }
3368
3369        function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic {
3370            let fileIncludeReasons: DiagnosticMessageChain[] | undefined;
3371            let relatedInfo: Diagnostic[] | undefined;
3372            let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined;
3373            if (file) fileReasons.get(file.path)?.forEach(processReason);
3374            if (fileProcessingReason) processReason(fileProcessingReason);
3375            // If we have location and there is only one reason file is in which is the location, dont add details for file include
3376            if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined;
3377            const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason);
3378            const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon);
3379            const redirectInfo = file && explainIfFileIsRedirect(file);
3380            const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray);
3381            return location && isReferenceFileLocation(location) ?
3382                createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) :
3383                createCompilerDiagnosticFromMessageChain(chain, relatedInfo);
3384
3385            function processReason(reason: FileIncludeReason) {
3386                (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason));
3387                if (!locationReason && isReferencedFile(reason)) {
3388                    // Report error at first reference file or file currently in processing and dont report in related information
3389                    locationReason = reason;
3390                }
3391                else if (locationReason !== reason) {
3392                    relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason));
3393                }
3394                // Remove fileProcessingReason if its already included in fileReasons of the program
3395                if (reason === fileProcessingReason) fileProcessingReason = undefined;
3396            }
3397        }
3398
3399        function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3400            (fileProcessingDiagnostics ||= []).push({
3401                kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic,
3402                file: file && file.path,
3403                fileProcessingReason,
3404                diagnostic,
3405                args
3406            });
3407        }
3408
3409        function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) {
3410            programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args));
3411        }
3412
3413        function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined {
3414            if (isReferencedFile(reason)) {
3415                const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason);
3416                let message: DiagnosticMessage;
3417                switch (reason.kind) {
3418                    case FileIncludeKind.Import:
3419                        message = Diagnostics.File_is_included_via_import_here;
3420                        break;
3421                    case FileIncludeKind.ReferenceFile:
3422                        message = Diagnostics.File_is_included_via_reference_here;
3423                        break;
3424                    case FileIncludeKind.TypeReferenceDirective:
3425                        message = Diagnostics.File_is_included_via_type_library_reference_here;
3426                        break;
3427                    case FileIncludeKind.LibReferenceDirective:
3428                        message = Diagnostics.File_is_included_via_library_reference_here;
3429                        break;
3430                    default:
3431                        Debug.assertNever(reason);
3432                }
3433                return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic(
3434                    referenceLocation.file,
3435                    referenceLocation.pos,
3436                    referenceLocation.end - referenceLocation.pos,
3437                    message,
3438                ) : undefined;
3439            }
3440
3441            if (!options.configFile) return undefined;
3442            let configFileNode: Node | undefined;
3443            let message: DiagnosticMessage;
3444            switch (reason.kind) {
3445                case FileIncludeKind.RootFile:
3446                    if (!options.configFile.configFileSpecs) return undefined;
3447                    const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory);
3448                    const matchedByFiles = getMatchedFileSpec(program, fileName);
3449                    if (matchedByFiles) {
3450                        configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles);
3451                        message = Diagnostics.File_is_matched_by_files_list_specified_here;
3452                        break;
3453                    }
3454                    const matchedByInclude = getMatchedIncludeSpec(program, fileName);
3455                    // Could be additional files specified as roots
3456                    if (!matchedByInclude) return undefined;
3457                    configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude);
3458                    message = Diagnostics.File_is_matched_by_include_pattern_specified_here;
3459                    break;
3460                case FileIncludeKind.SourceFromProjectReference:
3461                case FileIncludeKind.OutputFromProjectReference:
3462                    const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]);
3463                    const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) =>
3464                        resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined
3465                    );
3466                    if (!referenceInfo) return undefined;
3467                    const { sourceFile, index } = referenceInfo;
3468                    const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"),
3469                        property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3470                    return referencesSyntax && referencesSyntax.elements.length > index ?
3471                        createDiagnosticForNodeInSourceFile(
3472                            sourceFile,
3473                            referencesSyntax.elements[index],
3474                            reason.kind === FileIncludeKind.OutputFromProjectReference ?
3475                                Diagnostics.File_is_output_from_referenced_project_specified_here :
3476                                Diagnostics.File_is_source_from_referenced_project_specified_here,
3477                        ) :
3478                        undefined;
3479                case FileIncludeKind.AutomaticTypeDirectiveFile:
3480                    if (!options.types) return undefined;
3481                    configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference);
3482                    message = Diagnostics.File_is_entry_point_of_type_library_specified_here;
3483                    break;
3484                case FileIncludeKind.LibFile:
3485                    if (reason.index !== undefined) {
3486                        configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]);
3487                        message = Diagnostics.File_is_library_specified_here;
3488                        break;
3489                    }
3490                    const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
3491                    configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
3492                    message = Diagnostics.File_is_default_library_for_target_specified_here;
3493                    break;
3494                default:
3495                    Debug.assertNever(reason);
3496            }
3497            return configFileNode && createDiagnosticForNodeInSourceFile(
3498                options.configFile,
3499                configFileNode,
3500                message,
3501            );
3502        }
3503
3504        function verifyProjectReferences() {
3505            const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined;
3506            forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => {
3507                const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index];
3508                const parentFile = parent && parent.sourceFile as JsonSourceFile;
3509                if (!resolvedRef) {
3510                    createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path);
3511                    return;
3512                }
3513                const options = resolvedRef.commandLine.options;
3514                if (!options.composite || options.noEmit) {
3515                    // ok to not have composite if the current program is container only
3516                    const inputs = parent ? parent.commandLine.fileNames : rootNames;
3517                    if (inputs.length) {
3518                        if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path);
3519                        if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path);
3520                    }
3521                }
3522                if (ref.prepend) {
3523                    const out = outFile(options);
3524                    if (out) {
3525                        if (!host.fileExists(out)) {
3526                            createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path);
3527                        }
3528                    }
3529                    else {
3530                        createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path);
3531                    }
3532                }
3533                if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) {
3534                    createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path);
3535                    hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true);
3536                }
3537            });
3538        }
3539
3540        function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) {
3541            let needCompilerDiagnostic = true;
3542            const pathsSyntax = getOptionPathsSyntax();
3543            for (const pathProp of pathsSyntax) {
3544                if (isObjectLiteralExpression(pathProp.initializer)) {
3545                    for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) {
3546                        const initializer = keyProps.initializer;
3547                        if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) {
3548                            programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2));
3549                            needCompilerDiagnostic = false;
3550                        }
3551                    }
3552                }
3553            }
3554
3555            if (needCompilerDiagnostic) {
3556                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3557            }
3558        }
3559
3560        function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) {
3561            let needCompilerDiagnostic = true;
3562            const pathsSyntax = getOptionPathsSyntax();
3563            for (const pathProp of pathsSyntax) {
3564                if (isObjectLiteralExpression(pathProp.initializer) &&
3565                    createOptionDiagnosticInObjectLiteralSyntax(
3566                        pathProp.initializer, onKey, key, /*key2*/ undefined,
3567                        message, arg0)) {
3568                    needCompilerDiagnostic = false;
3569                }
3570            }
3571            if (needCompilerDiagnostic) {
3572                programDiagnostics.add(createCompilerDiagnostic(message, arg0));
3573            }
3574        }
3575
3576        function getOptionsSyntaxByName(name: string) {
3577            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3578            return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
3579        }
3580
3581        function getOptionPathsSyntax() {
3582            return getOptionsSyntaxByName("paths") || emptyArray;
3583        }
3584
3585        function getOptionsSyntaxByValue(name: string, value: string) {
3586            const syntaxByName = getOptionsSyntaxByName(name);
3587            return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined);
3588        }
3589
3590        function getOptionsSyntaxByArrayElementValue(name: string, value: string) {
3591            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3592            return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value);
3593        }
3594
3595        function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) {
3596            createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
3597        }
3598
3599        function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0: string) {
3600            createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
3601        }
3602
3603        function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
3604            const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"),
3605                property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined);
3606            if (referencesSyntax && referencesSyntax.elements.length > index) {
3607                programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1));
3608            }
3609            else {
3610                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1));
3611            }
3612        }
3613
3614        function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number) {
3615            const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
3616            const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
3617                !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);
3618
3619            if (needCompilerDiagnostic) {
3620                programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
3621            }
3622        }
3623
3624        function getCompilerOptionsObjectLiteralSyntax() {
3625            if (_compilerOptionsObjectLiteralSyntax === undefined) {
3626                _compilerOptionsObjectLiteralSyntax = false;
3627                const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile);
3628                if (jsonObjectLiteral) {
3629                    for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) {
3630                        if (isObjectLiteralExpression(prop.initializer)) {
3631                            _compilerOptionsObjectLiteralSyntax = prop.initializer;
3632                            break;
3633                        }
3634                    }
3635                }
3636            }
3637            return _compilerOptionsObjectLiteralSyntax || undefined;
3638        }
3639
3640        function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0: string | number, arg1?: string | number, arg2?: string | number): boolean {
3641            const props = getPropertyAssignment(objectLiteral, key1, key2);
3642            for (const prop of props) {
3643                programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
3644            }
3645            return !!props.length;
3646        }
3647
3648        function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) {
3649            hasEmitBlockingDiagnostics.set(toPath(emitFileName), true);
3650            programDiagnostics.add(diag);
3651        }
3652
3653        function isEmittedFile(file: string): boolean {
3654            if (options.noEmit) {
3655                return false;
3656            }
3657
3658            // If this is source file, its not emitted file
3659            const filePath = toPath(file);
3660            if (getSourceFileByPath(filePath)) {
3661                return false;
3662            }
3663
3664            // If options have --outFile or --out just check that
3665            const out = outFile(options);
3666            if (out) {
3667                return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts);
3668            }
3669
3670            // If declarationDir is specified, return if its a file in that directory
3671            if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) {
3672                return true;
3673            }
3674
3675            // If --outDir, check if file is in that directory
3676            if (options.outDir) {
3677                return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
3678            }
3679
3680            if (fileExtensionIsOneOf(filePath, supportedJSExtensions) || isDeclarationFileName(filePath)) {
3681                // Otherwise just check if sourceFile with the name exists
3682                const filePathWithoutExtension = removeFileExtension(filePath);
3683                return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) ||
3684                    !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path);
3685            }
3686            return false;
3687        }
3688
3689        function isSameFile(file1: string, file2: string) {
3690            return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
3691        }
3692
3693        function getSymlinkCache(): SymlinkCache {
3694            if (host.getSymlinkCache) {
3695                return host.getSymlinkCache();
3696            }
3697            return symlinks || (symlinks = discoverProbableSymlinks(
3698                files,
3699                getCanonicalFileName,
3700                host.getCurrentDirectory(),
3701                isOhpm(options.packageManagerType)));
3702        }
3703    }
3704
3705    interface HostForUseSourceOfProjectReferenceRedirect {
3706        compilerHost: CompilerHost;
3707        getSymlinkCache: () => SymlinkCache;
3708        useSourceOfProjectReferenceRedirect: boolean;
3709        toPath(fileName: string): Path;
3710        getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined;
3711        getSourceOfProjectReferenceRedirect(fileName: string): SourceOfProjectReferenceRedirect | undefined;
3712        forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined;
3713        options?: CompilerOptions;
3714    }
3715
3716    function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) {
3717        let setOfDeclarationDirectories: Set<Path> | undefined;
3718        const originalFileExists = host.compilerHost.fileExists;
3719        const originalDirectoryExists = host.compilerHost.directoryExists;
3720        const originalGetDirectories = host.compilerHost.getDirectories;
3721        const originalRealpath = host.compilerHost.realpath;
3722
3723        if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists };
3724
3725        host.compilerHost.fileExists = fileExists;
3726
3727        let directoryExists;
3728        if (originalDirectoryExists) {
3729            // This implementation of directoryExists checks if the directory being requested is
3730            // directory of .d.ts file for the referenced Project.
3731            // If it is it returns true irrespective of whether that directory exists on host
3732            directoryExists = host.compilerHost.directoryExists = path => {
3733                if (originalDirectoryExists.call(host.compilerHost, path)) {
3734                    handleDirectoryCouldBeSymlink(path);
3735                    return true;
3736                }
3737
3738                if (!host.getResolvedProjectReferences()) return false;
3739
3740                if (!setOfDeclarationDirectories) {
3741                    setOfDeclarationDirectories = new Set();
3742                    host.forEachResolvedProjectReference(ref => {
3743                        const out = outFile(ref.commandLine.options);
3744                        if (out) {
3745                            setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out)));
3746                        }
3747                        else {
3748                            // Set declaration's in different locations only, if they are next to source the directory present doesnt change
3749                            const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir;
3750                            if (declarationDir) {
3751                                setOfDeclarationDirectories!.add(host.toPath(declarationDir));
3752                            }
3753                        }
3754                    });
3755                }
3756
3757                return fileOrDirectoryExistsUsingSource(path, /*isFile*/ false);
3758            };
3759        }
3760
3761        if (originalGetDirectories) {
3762            // Call getDirectories only if directory actually present on the host
3763            // This is needed to ensure that we arent getting directories that we fake about presence for
3764            host.compilerHost.getDirectories = path =>
3765                !host.getResolvedProjectReferences() || (originalDirectoryExists && originalDirectoryExists.call(host.compilerHost, path)) ?
3766                    originalGetDirectories.call(host.compilerHost, path) :
3767                    [];
3768        }
3769
3770        // This is something we keep for life time of the host
3771        if (originalRealpath) {
3772            host.compilerHost.realpath = s =>
3773                host.getSymlinkCache().getSymlinkedFiles()?.get(host.toPath(s)) ||
3774                originalRealpath.call(host.compilerHost, s);
3775        }
3776
3777        return { onProgramCreateComplete, fileExists, directoryExists };
3778
3779        function onProgramCreateComplete() {
3780            host.compilerHost.fileExists = originalFileExists;
3781            host.compilerHost.directoryExists = originalDirectoryExists;
3782            host.compilerHost.getDirectories = originalGetDirectories;
3783            // DO not revert realpath as it could be used later
3784        }
3785
3786        // This implementation of fileExists checks if the file being requested is
3787        // .d.ts file for the referenced Project.
3788        // If it is it returns true irrespective of whether that file exists on host
3789        function fileExists(file: string) {
3790            if (originalFileExists.call(host.compilerHost, file)) return true;
3791            if (!host.getResolvedProjectReferences()) return false;
3792            if (!isDeclarationFileName(file)) return false;
3793
3794            // Project references go to source file instead of .d.ts file
3795            return fileOrDirectoryExistsUsingSource(file, /*isFile*/ true);
3796        }
3797
3798        function fileExistsIfProjectReferenceDts(file: string) {
3799            const source = host.getSourceOfProjectReferenceRedirect(file);
3800            return source !== undefined ?
3801                isString(source) ? originalFileExists.call(host.compilerHost, source) : true :
3802                undefined;
3803        }
3804
3805        function directoryExistsIfProjectReferenceDeclDir(dir: string) {
3806            const dirPath = host.toPath(dir);
3807            const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
3808            return forEachKey(
3809                setOfDeclarationDirectories!,
3810                declDirPath => dirPath === declDirPath ||
3811                    // Any parent directory of declaration dir
3812                    startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) ||
3813                    // Any directory inside declaration dir
3814                    startsWith(dirPath, `${declDirPath}/`)
3815            );
3816        }
3817
3818        function handleDirectoryCouldBeSymlink(directory: string) {
3819            if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return;
3820
3821            // Because we already watch node_modules or oh_modules, handle symlinks in there
3822            const modulesPathPart = isOhpm(host.options?.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
3823            if (!originalRealpath || !stringContains(directory, modulesPathPart)) return;
3824            const symlinkCache = host.getSymlinkCache();
3825            const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory));
3826            if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return;
3827
3828            const real = normalizePath(originalRealpath.call(host.compilerHost, directory));
3829            let realPath: Path;
3830            if (real === directory ||
3831                (realPath = ensureTrailingDirectorySeparator(host.toPath(real))) === directoryPath) {
3832                // not symlinked
3833                symlinkCache.setSymlinkedDirectory(directoryPath, false);
3834                return;
3835            }
3836
3837            symlinkCache.setSymlinkedDirectory(directory, {
3838                real: ensureTrailingDirectorySeparator(real),
3839                realPath
3840            });
3841        }
3842
3843        function fileOrDirectoryExistsUsingSource(fileOrDirectory: string, isFile: boolean): boolean {
3844            const fileOrDirectoryExistsUsingSource = isFile ?
3845                (file: string) => fileExistsIfProjectReferenceDts(file) :
3846                (dir: string) => directoryExistsIfProjectReferenceDeclDir(dir);
3847            // Check current directory or file
3848            const result = fileOrDirectoryExistsUsingSource(fileOrDirectory);
3849            if (result !== undefined) return result;
3850
3851            const symlinkCache = host.getSymlinkCache();
3852            const symlinkedDirectories = symlinkCache.getSymlinkedDirectories();
3853            if (!symlinkedDirectories) return false;
3854            const fileOrDirectoryPath = host.toPath(fileOrDirectory);
3855            const modulesPathPart = isOhpm(host.options?.packageManagerType) ? ohModulesPathPart : nodeModulesPathPart;
3856            if (!stringContains(fileOrDirectoryPath, modulesPathPart)) return false;
3857            if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true;
3858
3859            // If it contains node_modules or oh_modules check if its one of the symlinked path we know of
3860            return firstDefinedIterator(
3861                symlinkedDirectories.entries(),
3862                ([directoryPath, symlinkedDirectory]) => {
3863                    if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined;
3864                    const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath));
3865                    if (isFile && result) {
3866                        // Store the real path for the file'
3867                        const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory());
3868                        symlinkCache.setSymlinkedFile(
3869                            fileOrDirectoryPath,
3870                            `${symlinkedDirectory.real}${absolutePath.replace(new RegExp(directoryPath, "i"), "")}`
3871                        );
3872                    }
3873                    return result;
3874                }
3875            ) || false;
3876        }
3877    }
3878
3879    /*@internal*/
3880    export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true };
3881
3882    /*@internal*/
3883    export function handleNoEmitOptions<T extends BuilderProgram>(
3884        program: Program | T,
3885        sourceFile: SourceFile | undefined,
3886        writeFile: WriteFileCallback | undefined,
3887        cancellationToken: CancellationToken | undefined
3888    ): EmitResult | undefined {
3889        const options = program.getCompilerOptions();
3890        if (options.noEmit) {
3891            // Cache the semantic diagnostics
3892            program.getSemanticDiagnostics(sourceFile, cancellationToken);
3893            return sourceFile || outFile(options) ?
3894                emitSkippedWithNoDiagnostics :
3895                program.emitBuildInfo(writeFile, cancellationToken);
3896        }
3897
3898        // If the noEmitOnError flag is set, then check if we have any errors so far.  If so,
3899        // immediately bail out.  Note that we pass 'undefined' for 'sourceFile' so that we
3900        // get any preEmit diagnostics, not just the ones
3901        if (!options.noEmitOnError) return undefined;
3902        let diagnostics: readonly Diagnostic[] = [
3903            ...program.getOptionsDiagnostics(cancellationToken),
3904            ...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
3905            ...program.getGlobalDiagnostics(cancellationToken),
3906            ...program.getSemanticDiagnostics(sourceFile, cancellationToken)
3907        ];
3908
3909        if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) {
3910            diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken);
3911        }
3912
3913        if (!diagnostics.length) return undefined;
3914        let emittedFiles: string[] | undefined;
3915        if (!sourceFile && !outFile(options)) {
3916            const emitResult = program.emitBuildInfo(writeFile, cancellationToken);
3917            if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics];
3918            emittedFiles = emitResult.emittedFiles;
3919        }
3920        return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true };
3921    }
3922
3923    /*@internal*/
3924    export function filterSemanticDiagnotics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] {
3925        return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]);
3926    }
3927
3928    /*@internal*/
3929    interface CompilerHostLike {
3930        useCaseSensitiveFileNames(): boolean;
3931        getCurrentDirectory(): string;
3932        fileExists(fileName: string): boolean;
3933        readFile(fileName: string): string | undefined;
3934        readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[];
3935        trace?(s: string): void;
3936        onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter;
3937    }
3938
3939    /* @internal */
3940    export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost {
3941        return {
3942            fileExists: f => directoryStructureHost.fileExists(f),
3943            readDirectory(root, extensions, excludes, includes, depth) {
3944                Debug.assertIsDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'");
3945                return directoryStructureHost.readDirectory(root, extensions, excludes, includes, depth);
3946            },
3947            readFile: f => directoryStructureHost.readFile(f),
3948            useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(),
3949            getCurrentDirectory: () => host.getCurrentDirectory(),
3950            onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined,
3951            trace: host.trace ? (s) => host.trace!(s) : undefined
3952        };
3953    }
3954
3955    // For backward compatibility
3956    /** @deprecated */ export interface ResolveProjectReferencePathHost {
3957        fileExists(fileName: string): boolean;
3958    }
3959
3960    /* @internal */
3961    export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) {
3962        if (!projectReferences) return emptyArray;
3963        let nodes: InputFiles[] | undefined;
3964        for (let i = 0; i < projectReferences.length; i++) {
3965            const ref = projectReferences[i];
3966            const resolvedRefOpts = getCommandLine(ref, i);
3967            if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) {
3968                const out = outFile(resolvedRefOpts.options);
3969                // Upstream project didn't have outFile set -- skip (error will have been issued earlier)
3970                if (!out) continue;
3971
3972                const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true);
3973                const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath);
3974                (nodes || (nodes = [])).push(node);
3975            }
3976        }
3977        return nodes || emptyArray;
3978    }
3979    /**
3980     * Returns the target config filename of a project reference.
3981     * Note: The file might not exist.
3982     */
3983    export function resolveProjectReferencePath(ref: ProjectReference): ResolvedConfigFileName;
3984    /** @deprecated */ export function resolveProjectReferencePath(host: ResolveProjectReferencePathHost, ref: ProjectReference): ResolvedConfigFileName;
3985    export function resolveProjectReferencePath(hostOrRef: ResolveProjectReferencePathHost | ProjectReference, ref?: ProjectReference): ResolvedConfigFileName {
3986        const passedInRef = ref ? ref : hostOrRef as ProjectReference;
3987        return resolveConfigFileProjectName(passedInRef.path);
3988    }
3989
3990    /* @internal */
3991    /**
3992     * Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
3993     * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
3994     * This returns a diagnostic even if the module will be an untyped module.
3995     */
3996    export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined {
3997        switch (extension) {
3998            case Extension.Ts:
3999            case Extension.Dts:
4000            case Extension.Ets:
4001            case Extension.Dets:
4002                // These are always allowed.
4003                return undefined;
4004            case Extension.Tsx:
4005                return needJsx();
4006            case Extension.Jsx:
4007                return needJsx() || needAllowJs();
4008            case Extension.Js:
4009                return needAllowJs();
4010            case Extension.Json:
4011                return needResolveJsonModule();
4012        }
4013
4014        function needJsx() {
4015            return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set;
4016        }
4017        function needAllowJs() {
4018            return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type;
4019        }
4020        function needResolveJsonModule() {
4021            return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used;
4022        }
4023    }
4024
4025    function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] {
4026        const res = imports.map(i => i.text);
4027        for (const aug of moduleAugmentations) {
4028            if (aug.kind === SyntaxKind.StringLiteral) {
4029                res.push(aug.text);
4030            }
4031            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
4032        }
4033        return res;
4034    }
4035
4036    /* @internal */
4037    export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike {
4038        if (index < imports.length) return imports[index];
4039        let augIndex = imports.length;
4040        for (const aug of moduleAugmentations) {
4041            if (aug.kind === SyntaxKind.StringLiteral) {
4042                if (index === augIndex) return aug;
4043                augIndex++;
4044            }
4045            // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
4046        }
4047        Debug.fail("should never ask for module name at index higher than possible module name");
4048    }
4049
4050    export function getTypeExportImportAndConstEnumTransformer(context: TransformationContext): (node: SourceFile) => SourceFile {
4051        return transformTypeExportImportAndConstEnumInTypeScript(context);
4052    }
4053
4054    export function hasTsNoCheckOrTsIgnoreFlag(node: SourceFile): boolean {
4055        // check @ts-nocheck flag
4056        if (!!node.checkJsDirective && node.checkJsDirective.enabled === false) {
4057            return true;
4058        }
4059        // check @ts-ignore flag
4060        if (node.commentDirectives !== undefined) {
4061            for (const commentDirective of node.commentDirectives) {
4062                if (commentDirective.type === CommentDirectiveType.Ignore) {
4063                    return true;
4064                }
4065            }
4066        }
4067        return false;
4068    }
4069}
4070