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