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