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