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