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