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