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 | undefined; 1036 let linterTypeChecker: TypeChecker | undefined; 1037 let classifiableNames: Set<__String>; 1038 const ambientModuleNameToUnmodifiedFileName = new Map<string, string>(); 1039 let fileReasons = createMultiMap<Path, FileIncludeReason>(); 1040 const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache<Diagnostic> = {}; 1041 const cachedBindAndCheckDiagnosticsForFileForLinter: DiagnosticCache<Diagnostic> = {}; 1042 const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {}; 1043 1044 let resolvedTypeReferenceDirectives = createModeAwareCache<ResolvedTypeReferenceDirective | undefined>(); 1045 let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined; 1046 1047 // 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. 1048 // This works as imported modules are discovered recursively in a depth first manner, specifically: 1049 // - For each root file, findSourceFile is called. 1050 // - This calls processImportedModules for each module imported in the source file. 1051 // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module. 1052 // As all these operations happen - and are nested - within the createProgram call, they close over the below variables. 1053 // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses. 1054 const maxNodeModuleJsDepth = typeof options.maxNodeModuleJsDepth === "number" ? options.maxNodeModuleJsDepth : 0; 1055 let currentNodeModulesDepth = 0; 1056 1057 // If a module has some of its imports skipped due to being at the depth limit under node_modules or oh_modules, then track 1058 // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. 1059 const modulesWithElidedImports = new Map<string, boolean>(); 1060 1061 // Track source files that are source files found by searching under node_modules or oh_modules, as these shouldn't be compiled. 1062 const sourceFilesFoundSearchingNodeModules = new Map<string, boolean>(); 1063 1064 tracing?.push(tracing.Phase.Program, "createProgram", { configFilePath: options.configFilePath, rootDir: options.rootDir }, /*separateBeginAndEnd*/ true); 1065 performance.mark("beforeProgram"); 1066 1067 const host = createProgramOptions.host || createCompilerHost(options); 1068 const configParsingHost = parseConfigHostFromCompilerHostLike(host); 1069 1070 let skipDefaultLib = options.noLib; 1071 const getDefaultLibraryFileName = memoize(() => host.getDefaultLibFileName(options)); 1072 const defaultLibraryPath = host.getDefaultLibLocation ? host.getDefaultLibLocation() : getDirectoryPath(getDefaultLibraryFileName()); 1073 const programDiagnostics = createDiagnosticCollection(); 1074 const currentDirectory = host.getCurrentDirectory(); 1075 const supportedExtensions = getSupportedExtensions(options); 1076 const supportedExtensionsWithJsonIfResolveJsonModule = getSupportedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions); 1077 1078 // Map storing if there is emit blocking diagnostics for given input 1079 const hasEmitBlockingDiagnostics = new Map<string, boolean>(); 1080 let _compilerOptionsObjectLiteralSyntax: ObjectLiteralExpression | false | undefined; 1081 1082 let moduleResolutionCache: ModuleResolutionCache | undefined; 1083 let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined; 1084 let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: SourceFile, containingFileName: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[]; 1085 const hasInvalidatedResolutions = host.hasInvalidatedResolutions || returnFalse; 1086 if (host.resolveModuleNames) { 1087 actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFileName, reusedNames, redirectedReference, options, containingFile).map(resolved => { 1088 // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. 1089 if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { 1090 return resolved as ResolvedModuleFull; 1091 } 1092 const withExtension = clone(resolved) as ResolvedModuleFull; 1093 withExtension.extension = extensionFromPath(resolved.resolvedFileName); 1094 return withExtension; 1095 }); 1096 moduleResolutionCache = host.getModuleResolutionCache?.(); 1097 } 1098 else { 1099 moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options); 1100 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 1101 actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, _reusedNames, redirectedReference) => loadWithModeAwareCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, loader); 1102 } 1103 1104 let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[]; 1105 if (host.resolveTypeReferenceDirectives) { 1106 actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, containingFileMode) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options, containingFileMode); 1107 } 1108 else { 1109 typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache()); 1110 const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, resolutionMode: SourceFile["impliedNodeFormat"] | undefined) => resolveTypeReferenceDirective( 1111 typesRef, 1112 containingFile, 1113 options, 1114 host, 1115 redirectedReference, 1116 typeReferenceDirectiveResolutionCache, 1117 resolutionMode, 1118 ).resolvedTypeReferenceDirective!; // TODO: GH#18217 1119 actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference, containingFileMode) => loadWithTypeDirectiveCache<ResolvedTypeReferenceDirective>(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, containingFileMode, loader); 1120 } 1121 1122 // Map from a stringified PackageId to the source file with that id. 1123 // Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile). 1124 // `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around. 1125 const packageIdToSourceFile = new Map<string, SourceFile>(); 1126 // Maps from a SourceFile's `.path` to the name of the package it was imported with. 1127 let sourceFileToPackageName = new Map<Path, string>(); 1128 // Key is a file name. Value is the (non-empty, or undefined) list of files that redirect to it. 1129 let redirectTargetsMap = createMultiMap<Path, string>(); 1130 let usesUriStyleNodeCoreModules = false; 1131 1132 /** 1133 * map with 1134 * - SourceFile if present 1135 * - false if sourceFile missing for source of project reference redirect 1136 * - undefined otherwise 1137 */ 1138 const filesByName = new Map<string, SourceFile | false | undefined>(); 1139 let missingFilePaths: readonly Path[] | undefined; 1140 // stores 'filename -> file association' ignoring case 1141 // used to track cases when two file names differ only in casing 1142 const filesByNameIgnoreCase = host.useCaseSensitiveFileNames() ? new Map<string, SourceFile>() : undefined; 1143 1144 // A parallel array to projectReferences storing the results of reading in the referenced tsconfig files 1145 let resolvedProjectReferences: readonly (ResolvedProjectReference | undefined)[] | undefined; 1146 let projectReferenceRedirects: ESMap<Path, ResolvedProjectReference | false> | undefined; 1147 let mapFromFileToProjectReferenceRedirects: ESMap<Path, Path> | undefined; 1148 let mapFromToProjectReferenceRedirectSource: ESMap<Path, SourceOfProjectReferenceRedirect> | undefined; 1149 1150 const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() && 1151 !options.disableSourceOfProjectReferenceRedirect; 1152 const { onProgramCreateComplete, fileExists, directoryExists } = updateHostForUseSourceOfProjectReferenceRedirect({ 1153 compilerHost: host, 1154 getSymlinkCache, 1155 useSourceOfProjectReferenceRedirect, 1156 toPath, 1157 getResolvedProjectReferences, 1158 getSourceOfProjectReferenceRedirect, 1159 forEachResolvedProjectReference, 1160 options: _options 1161 }); 1162 const readFile = host.readFile.bind(host) as typeof host.readFile; 1163 1164 tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram }); 1165 const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options); 1166 tracing?.pop(); 1167 // We set `structuralIsReused` to `undefined` because `tryReuseStructureFromOldProgram` calls `tryReuseStructureFromOldProgram` which checks 1168 // `structuralIsReused`, which would be a TDZ violation if it was not set in advance to `undefined`. 1169 let structureIsReused: StructureIsReused; 1170 tracing?.push(tracing.Phase.Program, "tryReuseStructureFromOldProgram", {}); 1171 structureIsReused = tryReuseStructureFromOldProgram(); // eslint-disable-line prefer-const 1172 tracing?.pop(); 1173 if (structureIsReused !== StructureIsReused.Completely) { 1174 processingDefaultLibFiles = []; 1175 processingOtherFiles = []; 1176 1177 if (projectReferences) { 1178 if (!resolvedProjectReferences) { 1179 resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile); 1180 } 1181 if (rootNames.length) { 1182 resolvedProjectReferences?.forEach((parsedRef, index) => { 1183 if (!parsedRef) return; 1184 const out = outFile(parsedRef.commandLine.options); 1185 if (useSourceOfProjectReferenceRedirect) { 1186 if (out || getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { 1187 for (const fileName of parsedRef.commandLine.fileNames) { 1188 processProjectReferenceFile(fileName, { kind: FileIncludeKind.SourceFromProjectReference, index }); 1189 } 1190 } 1191 } 1192 else { 1193 if (out) { 1194 processProjectReferenceFile(changeExtension(out, ".d.ts"), { kind: FileIncludeKind.OutputFromProjectReference, index }); 1195 } 1196 else if (getEmitModuleKind(parsedRef.commandLine.options) === ModuleKind.None) { 1197 const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(parsedRef.commandLine, !host.useCaseSensitiveFileNames())); 1198 for (const fileName of parsedRef.commandLine.fileNames) { 1199 if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { 1200 processProjectReferenceFile(getOutputDeclarationFileName(fileName, parsedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory), { kind: FileIncludeKind.OutputFromProjectReference, index }); 1201 } 1202 } 1203 } 1204 } 1205 }); 1206 } 1207 } 1208 1209 tracing?.push(tracing.Phase.Program, "processRootFiles", { count: rootNames.length }); 1210 forEach(rootNames, (name, index) => processRootFile(name, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.RootFile, index })); 1211 tracing?.pop(); 1212 1213 // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders 1214 const typeReferences: string[] = rootNames.length ? getAutomaticTypeDirectiveNames(options, host) : emptyArray; 1215 1216 if (typeReferences.length) { 1217 tracing?.push(tracing.Phase.Program, "processTypeReferences", { count: typeReferences.length }); 1218 // This containingFilename needs to match with the one used in managed-side 1219 const containingDirectory = options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(); 1220 const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile); 1221 const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename); 1222 for (let i = 0; i < typeReferences.length; i++) { 1223 // under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode 1224 processTypeReferenceDirective(typeReferences[i], /*mode*/ undefined, resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId }); 1225 } 1226 tracing?.pop(); 1227 } 1228 1229 // Do not process the default library if: 1230 // - The '--noLib' flag is used. 1231 // - A 'no-default-lib' reference comment is encountered in 1232 // processing the root files. 1233 if (rootNames.length && !skipDefaultLib) { 1234 // If '--lib' is not specified, include default library file according to '--target' 1235 // otherwise, using options specified in '--lib' instead of '--target' default library file 1236 const defaultLibraryFileName = getDefaultLibraryFileName(); 1237 if (!options.lib && defaultLibraryFileName) { 1238 processRootFile(defaultLibraryFileName, /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }); 1239 } 1240 else { 1241 forEach(options.lib, (libFileName, index) => { 1242 processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index }); 1243 }); 1244 1245 const etsComponentsLib = options.ets?.libs ?? []; 1246 if (etsComponentsLib.length) { 1247 forEach(etsComponentsLib, libFileName => { 1248 processRootFile(combinePaths(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile }); 1249 }); 1250 } 1251 } 1252 } 1253 1254 missingFilePaths = arrayFrom(mapDefinedIterator(filesByName.entries(), ([path, file]) => file === undefined ? path as Path : undefined)); 1255 files = stableSort(processingDefaultLibFiles, compareDefaultLibFiles).concat(processingOtherFiles); 1256 processingDefaultLibFiles = undefined; 1257 processingOtherFiles = undefined; 1258 } 1259 1260 Debug.assert(!!missingFilePaths); 1261 1262 // Release any files we have acquired in the old program but are 1263 // not part of the new program. 1264 if (oldProgram && host.onReleaseOldSourceFile) { 1265 const oldSourceFiles = oldProgram.getSourceFiles(); 1266 for (const oldSourceFile of oldSourceFiles) { 1267 const newFile = getSourceFileByPath(oldSourceFile.resolvedPath); 1268 if (shouldCreateNewSourceFile || !newFile || newFile.impliedNodeFormat !== oldSourceFile.impliedNodeFormat || 1269 // old file wasn't redirect but new file is 1270 (oldSourceFile.resolvedPath === oldSourceFile.path && newFile.resolvedPath !== oldSourceFile.path)) { 1271 host.onReleaseOldSourceFile(oldSourceFile, oldProgram.getCompilerOptions(), !!getSourceFileByPath(oldSourceFile.path)); 1272 } 1273 } 1274 if (!host.getParsedCommandLine) { 1275 oldProgram.forEachResolvedProjectReference(resolvedProjectReference => { 1276 if (!getResolvedProjectReferenceByPath(resolvedProjectReference.sourceFile.path)) { 1277 host.onReleaseOldSourceFile!(resolvedProjectReference.sourceFile, oldProgram!.getCompilerOptions(), /*hasSourceFileByPath*/ false); 1278 } 1279 }); 1280 } 1281 } 1282 1283 // Release commandlines that new program does not use 1284 if (oldProgram && host.onReleaseParsedCommandLine) { 1285 forEachProjectReference( 1286 oldProgram.getProjectReferences(), 1287 oldProgram.getResolvedProjectReferences(), 1288 (oldResolvedRef, parent, index) => { 1289 const oldReference = parent?.commandLine.projectReferences![index] || oldProgram!.getProjectReferences()![index]; 1290 const oldRefPath = resolveProjectReferencePath(oldReference); 1291 if (!projectReferenceRedirects?.has(toPath(oldRefPath))) { 1292 host.onReleaseParsedCommandLine!(oldRefPath, oldResolvedRef, oldProgram!.getCompilerOptions()); 1293 } 1294 } 1295 ); 1296 } 1297 1298 typeReferenceDirectiveResolutionCache = undefined; 1299 1300 // unconditionally set oldProgram to undefined to prevent it from being captured in closure 1301 oldProgram = undefined; 1302 1303 const program: Program = { 1304 getRootFileNames: () => rootNames, 1305 getSourceFile, 1306 getSourceFileByPath, 1307 getSourceFiles: () => files, 1308 getMissingFilePaths: () => missingFilePaths!, // TODO: GH#18217 1309 getModuleResolutionCache: () => moduleResolutionCache, 1310 getFilesByNameMap: () => filesByName, 1311 getCompilerOptions: () => options, 1312 getSyntacticDiagnostics, 1313 getOptionsDiagnostics, 1314 getGlobalDiagnostics, 1315 getSemanticDiagnostics, 1316 getSemanticDiagnosticsForLinter, 1317 getCachedSemanticDiagnostics, 1318 getSuggestionDiagnostics, 1319 getDeclarationDiagnostics, 1320 getBindAndCheckDiagnostics, 1321 getProgramDiagnostics, 1322 getTypeChecker, 1323 getLinterTypeChecker, 1324 getEtsLibSFromProgram, 1325 getClassifiableNames, 1326 getCommonSourceDirectory, 1327 emit, 1328 getCurrentDirectory: () => currentDirectory, 1329 getNodeCount: () => getTypeChecker().getNodeCount(), 1330 getIdentifierCount: () => getTypeChecker().getIdentifierCount(), 1331 getSymbolCount: () => getTypeChecker().getSymbolCount(), 1332 getTypeCount: () => getTypeChecker().getTypeCount(), 1333 getInstantiationCount: () => getTypeChecker().getInstantiationCount(), 1334 getRelationCacheSizes: () => getTypeChecker().getRelationCacheSizes(), 1335 getFileProcessingDiagnostics: () => fileProcessingDiagnostics, 1336 getResolvedTypeReferenceDirectives: () => resolvedTypeReferenceDirectives, 1337 isSourceFileFromExternalLibrary, 1338 isSourceFileDefaultLibrary, 1339 getSourceFileFromReference, 1340 getLibFileFromReference, 1341 sourceFileToPackageName, 1342 redirectTargetsMap, 1343 usesUriStyleNodeCoreModules, 1344 isEmittedFile, 1345 getConfigFileParsingDiagnostics, 1346 getResolvedModuleWithFailedLookupLocationsFromCache, 1347 getProjectReferences, 1348 getResolvedProjectReferences, 1349 getProjectReferenceRedirect, 1350 getResolvedProjectReferenceToRedirect, 1351 getResolvedProjectReferenceByPath, 1352 forEachResolvedProjectReference, 1353 isSourceOfProjectReferenceRedirect, 1354 emitBuildInfo, 1355 fileExists, 1356 readFile, 1357 directoryExists, 1358 getSymlinkCache, 1359 realpath: host.realpath?.bind(host), 1360 useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), 1361 getFileIncludeReasons: () => fileReasons, 1362 structureIsReused, 1363 writeFile, 1364 getJsDocNodeCheckedConfig: host.getJsDocNodeCheckedConfig, 1365 getJsDocNodeConditionCheckedResult: host. getJsDocNodeConditionCheckedResult, 1366 getFileCheckedModuleInfo: host.getFileCheckedModuleInfo, 1367 releaseTypeChecker: () => { typeChecker = undefined; linterTypeChecker = undefined; }, 1368 getEmitHost 1369 }; 1370 1371 onProgramCreateComplete(); 1372 1373 // Add file processingDiagnostics 1374 fileProcessingDiagnostics?.forEach(diagnostic => { 1375 switch (diagnostic.kind) { 1376 case FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic: 1377 return programDiagnostics.add(createDiagnosticExplainingFile(diagnostic.file && getSourceFileByPath(diagnostic.file), diagnostic.fileProcessingReason, diagnostic.diagnostic, diagnostic.args || emptyArray)); 1378 case FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic: 1379 const { file, pos, end } = getReferencedFileLocation(getSourceFileByPath, diagnostic.reason) as ReferenceFileLocation; 1380 return programDiagnostics.add(createFileDiagnostic(file, Debug.checkDefined(pos), Debug.checkDefined(end) - pos, diagnostic.diagnostic, ...diagnostic.args || emptyArray)); 1381 default: 1382 Debug.assertNever(diagnostic); 1383 } 1384 }); 1385 1386 verifyCompilerOptions(); 1387 performance.mark("afterProgram"); 1388 performance.measure("Program", "beforeProgram", "afterProgram"); 1389 tracing?.pop(); 1390 1391 return program; 1392 1393 function addResolutionDiagnostics(list: Diagnostic[] | undefined) { 1394 if (!list) return; 1395 for (const elem of list) { 1396 programDiagnostics.add(elem); 1397 } 1398 } 1399 1400 function pullDiagnosticsFromCache(names: string[] | readonly FileReference[], containingFile: SourceFile) { 1401 if (!moduleResolutionCache) return; 1402 const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); 1403 const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined; 1404 const containingDir = getDirectoryPath(containingFileName); 1405 const redirectedReference = getRedirectReferenceForResolution(containingFile); 1406 let i = 0; 1407 for (const n of names) { 1408 // 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 1409 const mode = typeof n === "string" ? getModeForResolutionAtIndex(containingFile, i) : getModeForFileReference(n, containingFileMode); 1410 const name = typeof n === "string" ? n : n.fileName; 1411 i++; 1412 // only nonrelative names hit the cache, and, at least as of right now, only nonrelative names can issue diagnostics 1413 // (Since diagnostics are only issued via import or export map lookup) 1414 // This may totally change if/when the issue of output paths not mapping to input files is fixed in a broader context 1415 // When it is, how we extract diagnostics from the module name resolver will have the be refined - the current cache 1416 // APIs wrapping the underlying resolver make it almost impossible to smuggle the diagnostics out in a generalized way 1417 if (isExternalModuleNameRelative(name)) continue; 1418 const diags = moduleResolutionCache.getOrCreateCacheForModuleName(name, mode, redirectedReference).get(containingDir)?.resolutionDiagnostics; 1419 addResolutionDiagnostics(diags); 1420 } 1421 } 1422 1423 function resolveModuleNamesWorker(moduleNames: string[], containingFile: SourceFile, reusedNames: string[] | undefined): readonly ResolvedModuleFull[] { 1424 if (!moduleNames.length) return emptyArray; 1425 const containingFileName = getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory); 1426 const redirectedReference = getRedirectReferenceForResolution(containingFile); 1427 tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName }); 1428 performance.mark("beforeResolveModule"); 1429 const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, reusedNames, redirectedReference); 1430 performance.mark("afterResolveModule"); 1431 performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule"); 1432 tracing?.pop(); 1433 pullDiagnosticsFromCache(moduleNames, containingFile); 1434 return result; 1435 } 1436 1437 function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] { 1438 if (!typeDirectiveNames.length) return []; 1439 const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile; 1440 const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined; 1441 const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined; 1442 tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName }); 1443 performance.mark("beforeResolveTypeReference"); 1444 const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, containingFileMode); 1445 performance.mark("afterResolveTypeReference"); 1446 performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference"); 1447 tracing?.pop(); 1448 return result; 1449 } 1450 1451 function getRedirectReferenceForResolution(file: SourceFile) { 1452 const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName); 1453 if (redirect || !isDeclarationFileName(file.originalFileName)) return redirect; 1454 1455 // The originalFileName could not be actual source file name if file found was d.ts from referecned project 1456 // 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 1457 const resultFromDts = getRedirectReferenceForResolutionFromSourceOfProject(file.path); 1458 if (resultFromDts) return resultFromDts; 1459 1460 // If preserveSymlinks is true, module resolution wont jump the symlink 1461 // but the resolved real path may be the .d.ts from project reference 1462 // Note:: Currently we try the real path only if the 1463 // file is from node_modules to avoid having to run real path on all file paths 1464 if (!host.realpath || !options.preserveSymlinks || (!stringContains(file.originalFileName, nodeModulesPathPart) && !stringContains(file.originalFileName, ohModulesPathPart))) return undefined; 1465 const realDeclarationPath = toPath(host.realpath(file.originalFileName)); 1466 return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationPath); 1467 } 1468 1469 function getRedirectReferenceForResolutionFromSourceOfProject(filePath: Path) { 1470 const source = getSourceOfProjectReferenceRedirect(filePath); 1471 if (isString(source)) return getResolvedProjectReferenceToRedirect(source); 1472 if (!source) return undefined; 1473 // Output of .d.ts file so return resolved ref that matches the out file name 1474 return forEachResolvedProjectReference(resolvedRef => { 1475 const out = outFile(resolvedRef.commandLine.options); 1476 if (!out) return undefined; 1477 return toPath(out) === filePath ? resolvedRef : undefined; 1478 }); 1479 } 1480 1481 function compareDefaultLibFiles(a: SourceFile, b: SourceFile) { 1482 return compareValues(getDefaultLibFilePriority(a), getDefaultLibFilePriority(b)); 1483 } 1484 1485 function getDefaultLibFilePriority(a: SourceFile) { 1486 if (containsPath(defaultLibraryPath, a.fileName, /*ignoreCase*/ false)) { 1487 const basename = getBaseFileName(a.fileName); 1488 if (basename === "lib.d.ts" || basename === "lib.es6.d.ts") return 0; 1489 const name = removeSuffix(removePrefix(basename, "lib."), ".d.ts"); 1490 const index = libs.indexOf(name); 1491 if (index !== -1) return index + 1; 1492 } 1493 return libs.length + 2; 1494 } 1495 1496 function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, mode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined { 1497 return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache, mode); 1498 } 1499 1500 function toPath(fileName: string): Path { 1501 return ts.toPath(fileName, currentDirectory, getCanonicalFileName); 1502 } 1503 1504 function getCommonSourceDirectory() { 1505 if (commonSourceDirectory === undefined) { 1506 const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, program)); 1507 commonSourceDirectory = ts.getCommonSourceDirectory( 1508 options, 1509 () => mapDefined(emittedFiles, file => file.isDeclarationFile ? undefined : file.fileName), 1510 currentDirectory, 1511 getCanonicalFileName, 1512 commonSourceDirectory => checkSourceFilesBelongToPath(emittedFiles, commonSourceDirectory) 1513 ); 1514 } 1515 return commonSourceDirectory; 1516 } 1517 1518 function getClassifiableNames() { 1519 if (!classifiableNames) { 1520 // Initialize a checker so that all our files are bound. 1521 getTypeChecker(); 1522 classifiableNames = new Set(); 1523 1524 for (const sourceFile of files) { 1525 sourceFile.classifiableNames?.forEach(value => classifiableNames.add(value)); 1526 } 1527 } 1528 1529 return classifiableNames; 1530 } 1531 1532 function resolveModuleNamesReusingOldState(moduleNames: string[], file: SourceFile): readonly (ResolvedModuleFull | undefined)[] { 1533 if (structureIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) { 1534 // If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules, 1535 // the best we can do is fallback to the default logic. 1536 return resolveModuleNamesWorker(moduleNames, file, /*reusedNames*/ undefined); 1537 } 1538 1539 const oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName); 1540 if (oldSourceFile !== file && file.resolvedModules) { 1541 // `file` was created for the new program. 1542 // 1543 // We only set `file.resolvedModules` via work from the current function, 1544 // so it is defined iff we already called the current function on `file`. 1545 // That call happened no later than the creation of the `file` object, 1546 // which per above occurred during the current program creation. 1547 // Since we assume the filesystem does not change during program creation, 1548 // it is safe to reuse resolutions from the earlier call. 1549 const result: (ResolvedModuleFull | undefined)[] = []; 1550 let i = 0; 1551 for (const moduleName of moduleNames) { 1552 const resolvedModule = file.resolvedModules.get(moduleName, getModeForResolutionAtIndex(file, i)); 1553 i++; 1554 result.push(resolvedModule); 1555 } 1556 return result; 1557 } 1558 // At this point, we know at least one of the following hold: 1559 // - file has local declarations for ambient modules 1560 // - old program state is available 1561 // With this information, we can infer some module resolutions without performing resolution. 1562 1563 /** An ordered list of module names for which we cannot recover the resolution. */ 1564 let unknownModuleNames: string[] | undefined; 1565 /** 1566 * The indexing of elements in this list matches that of `moduleNames`. 1567 * 1568 * Before combining results, result[i] is in one of the following states: 1569 * * undefined: needs to be recomputed, 1570 * * predictedToResolveToAmbientModuleMarker: known to be an ambient module. 1571 * Needs to be reset to undefined before returning, 1572 * * ResolvedModuleFull instance: can be reused. 1573 */ 1574 let result: (ResolvedModuleFull | undefined)[] | undefined; 1575 let reusedNames: string[] | undefined; 1576 /** A transient placeholder used to mark predicted resolution in the result list. */ 1577 const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {} as any; 1578 1579 for (let i = 0; i < moduleNames.length; i++) { 1580 const moduleName = moduleNames[i]; 1581 // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions 1582 if (file === oldSourceFile && !hasInvalidatedResolutions(oldSourceFile.path)) { 1583 const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, getModeForResolutionAtIndex(oldSourceFile, i)); 1584 if (oldResolvedModule) { 1585 if (isTraceEnabled(options, host)) { 1586 trace(host, 1587 oldResolvedModule.packageId ? 1588 Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2_with_Package_ID_3 : 1589 Diagnostics.Reusing_resolution_of_module_0_from_1_of_old_program_it_was_successfully_resolved_to_2, 1590 moduleName, 1591 getNormalizedAbsolutePath(file.originalFileName, currentDirectory), 1592 oldResolvedModule.resolvedFileName, 1593 oldResolvedModule.packageId && packageIdToString(oldResolvedModule.packageId) 1594 ); 1595 } 1596 (result || (result = new Array(moduleNames.length)))[i] = oldResolvedModule; 1597 (reusedNames || (reusedNames = [])).push(moduleName); 1598 continue; 1599 } 1600 } 1601 // We know moduleName resolves to an ambient module provided that moduleName: 1602 // - is in the list of ambient modules locally declared in the current source file. 1603 // - resolved to an ambient module in the old program whose declaration is in an unmodified file 1604 // (so the same module declaration will land in the new program) 1605 let resolvesToAmbientModuleInNonModifiedFile = false; 1606 if (contains(file.ambientModuleNames, moduleName)) { 1607 resolvesToAmbientModuleInNonModifiedFile = true; 1608 if (isTraceEnabled(options, host)) { 1609 trace(host, Diagnostics.Module_0_was_resolved_as_locally_declared_ambient_module_in_file_1, moduleName, getNormalizedAbsolutePath(file.originalFileName, currentDirectory)); 1610 } 1611 } 1612 else { 1613 resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, i); 1614 } 1615 1616 if (resolvesToAmbientModuleInNonModifiedFile) { 1617 (result || (result = new Array(moduleNames.length)))[i] = predictedToResolveToAmbientModuleMarker; 1618 } 1619 else { 1620 // Resolution failed in the old program, or resolved to an ambient module for which we can't reuse the result. 1621 (unknownModuleNames || (unknownModuleNames = [])).push(moduleName); 1622 } 1623 } 1624 1625 const resolutions = unknownModuleNames && unknownModuleNames.length 1626 ? resolveModuleNamesWorker(unknownModuleNames, file, reusedNames) 1627 : emptyArray; 1628 1629 // Combine results of resolutions and predicted results 1630 if (!result) { 1631 // There were no unresolved/ambient resolutions. 1632 Debug.assert(resolutions.length === moduleNames.length); 1633 return resolutions; 1634 } 1635 1636 let j = 0; 1637 for (let i = 0; i < result.length; i++) { 1638 if (result[i]) { 1639 // `result[i]` is either a `ResolvedModuleFull` or a marker. 1640 // If it is the former, we can leave it as is. 1641 if (result[i] === predictedToResolveToAmbientModuleMarker) { 1642 result[i] = undefined; 1643 } 1644 } 1645 else { 1646 result[i] = resolutions[j]; 1647 j++; 1648 } 1649 } 1650 Debug.assert(j === resolutions.length); 1651 1652 return result; 1653 1654 // If we change our policy of rechecking failed lookups on each program create, 1655 // we should adjust the value returned here. 1656 function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, index: number): boolean { 1657 if (index >= length(oldSourceFile?.imports) + length(oldSourceFile?.moduleAugmentations)) return false; // mode index out of bounds, don't reuse resolution 1658 const resolutionToFile = getResolvedModule(oldSourceFile, moduleName, oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, index)); 1659 const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName); 1660 if (resolutionToFile && resolvedFile) { 1661 // In the old program, we resolved to an ambient module that was in the same 1662 // place as we expected to find an actual module file. 1663 // We actually need to return 'false' here even though this seems like a 'true' case 1664 // because the normal module resolution algorithm will find this anyway. 1665 return false; 1666 } 1667 1668 // at least one of declarations should come from non-modified source file 1669 const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName); 1670 1671 if (!unmodifiedFile) { 1672 return false; 1673 } 1674 1675 if (isTraceEnabled(options, host)) { 1676 trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile); 1677 } 1678 return true; 1679 } 1680 } 1681 1682 function canReuseProjectReferences(): boolean { 1683 return !forEachProjectReference( 1684 oldProgram!.getProjectReferences(), 1685 oldProgram!.getResolvedProjectReferences(), 1686 (oldResolvedRef, parent, index) => { 1687 const newRef = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; 1688 const newResolvedRef = parseProjectReferenceConfigFile(newRef); 1689 if (oldResolvedRef) { 1690 // Resolved project reference has gone missing or changed 1691 return !newResolvedRef || 1692 newResolvedRef.sourceFile !== oldResolvedRef.sourceFile || 1693 !arrayIsEqualTo(oldResolvedRef.commandLine.fileNames, newResolvedRef.commandLine.fileNames); 1694 } 1695 else { 1696 // A previously-unresolved reference may be resolved now 1697 return newResolvedRef !== undefined; 1698 } 1699 }, 1700 (oldProjectReferences, parent) => { 1701 // If array of references is changed, we cant resue old program 1702 const newReferences = parent ? getResolvedProjectReferenceByPath(parent.sourceFile.path)!.commandLine.projectReferences : projectReferences; 1703 return !arrayIsEqualTo(oldProjectReferences, newReferences, projectReferenceIsEqualTo); 1704 } 1705 ); 1706 } 1707 1708 function tryReuseStructureFromOldProgram(): StructureIsReused { 1709 if (!oldProgram) { 1710 return StructureIsReused.Not; 1711 } 1712 1713 // check properties that can affect structure of the program or module resolution strategy 1714 // if any of these properties has changed - structure cannot be reused 1715 const oldOptions = oldProgram.getCompilerOptions(); 1716 if (changesAffectModuleResolution(oldOptions, options)) { 1717 return StructureIsReused.Not; 1718 } 1719 1720 // there is an old program, check if we can reuse its structure 1721 const oldRootNames = oldProgram.getRootFileNames(); 1722 if (!arrayIsEqualTo(oldRootNames, rootNames)) { 1723 return StructureIsReused.Not; 1724 } 1725 1726 // Check if any referenced project tsconfig files are different 1727 if (!canReuseProjectReferences()) { 1728 return StructureIsReused.Not; 1729 } 1730 if (projectReferences) { 1731 resolvedProjectReferences = projectReferences.map(parseProjectReferenceConfigFile); 1732 } 1733 1734 // check if program source files has changed in the way that can affect structure of the program 1735 const newSourceFiles: SourceFile[] = []; 1736 const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = []; 1737 structureIsReused = StructureIsReused.Completely; 1738 1739 // If the missing file paths are now present, it can change the progam structure, 1740 // and hence cant reuse the structure. 1741 // This is same as how we dont reuse the structure if one of the file from old program is now missing 1742 if (oldProgram.getMissingFilePaths().some(missingFilePath => host.fileExists(missingFilePath))) { 1743 return StructureIsReused.Not; 1744 } 1745 1746 const oldSourceFiles = oldProgram.getSourceFiles(); 1747 const enum SeenPackageName { Exists, Modified } 1748 const seenPackageNames = new Map<string, SeenPackageName>(); 1749 1750 for (const oldSourceFile of oldSourceFiles) { 1751 const sourceFileOptions = getCreateSourceFileOptions(oldSourceFile.fileName, moduleResolutionCache, host, options); 1752 let newSourceFile = host.getSourceFileByPath 1753 ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, sourceFileOptions, /*onError*/ undefined, shouldCreateNewSourceFile || sourceFileOptions.impliedNodeFormat !== oldSourceFile.impliedNodeFormat) 1754 : host.getSourceFile(oldSourceFile.fileName, sourceFileOptions, /*onError*/ undefined, shouldCreateNewSourceFile || sourceFileOptions.impliedNodeFormat !== oldSourceFile.impliedNodeFormat); // TODO: GH#18217 1755 1756 if (!newSourceFile) { 1757 return StructureIsReused.Not; 1758 } 1759 newSourceFile.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined; 1760 newSourceFile.packageJsonScope = sourceFileOptions.packageJsonScope; 1761 1762 Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`"); 1763 1764 let fileChanged: boolean; 1765 if (oldSourceFile.redirectInfo) { 1766 // We got `newSourceFile` by path, so it is actually for the unredirected file. 1767 // This lets us know if the unredirected file has changed. If it has we should break the redirect. 1768 if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) { 1769 // Underlying file has changed. Might not redirect anymore. Must rebuild program. 1770 return StructureIsReused.Not; 1771 } 1772 fileChanged = false; 1773 newSourceFile = oldSourceFile; // Use the redirect. 1774 } 1775 else if (oldProgram.redirectTargetsMap.has(oldSourceFile.path)) { 1776 // If a redirected-to source file changes, the redirect may be broken. 1777 if (newSourceFile !== oldSourceFile) { 1778 return StructureIsReused.Not; 1779 } 1780 fileChanged = false; 1781 } 1782 else { 1783 fileChanged = newSourceFile !== oldSourceFile; 1784 } 1785 1786 // Since the project references havent changed, its right to set originalFileName and resolvedPath here 1787 newSourceFile.path = oldSourceFile.path; 1788 newSourceFile.originalFileName = oldSourceFile.originalFileName; 1789 newSourceFile.resolvedPath = oldSourceFile.resolvedPath; 1790 newSourceFile.fileName = oldSourceFile.fileName; 1791 1792 const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path); 1793 if (packageName !== undefined) { 1794 // If there are 2 different source files for the same package name and at least one of them changes, 1795 // they might become redirects. So we must rebuild the program. 1796 const prevKind = seenPackageNames.get(packageName); 1797 const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists; 1798 if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) { 1799 return StructureIsReused.Not; 1800 } 1801 seenPackageNames.set(packageName, newKind); 1802 } 1803 1804 if (fileChanged) { 1805 if (oldSourceFile.impliedNodeFormat !== newSourceFile.impliedNodeFormat) { 1806 structureIsReused = StructureIsReused.SafeModules; 1807 } 1808 // The `newSourceFile` object was created for the new program. 1809 else if (!arrayIsEqualTo(oldSourceFile.libReferenceDirectives, newSourceFile.libReferenceDirectives, fileReferenceIsEqualTo)) { 1810 // 'lib' references has changed. Matches behavior in changesAffectModuleResolution 1811 structureIsReused = StructureIsReused.SafeModules; 1812 } 1813 else if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) { 1814 // value of no-default-lib has changed 1815 // this will affect if default library is injected into the list of files 1816 structureIsReused = StructureIsReused.SafeModules; 1817 } 1818 // check tripleslash references 1819 else if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) { 1820 // tripleslash references has changed 1821 structureIsReused = StructureIsReused.SafeModules; 1822 } 1823 else { 1824 // check imports and module augmentations 1825 collectExternalModuleReferences(newSourceFile); 1826 if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) { 1827 // imports has changed 1828 structureIsReused = StructureIsReused.SafeModules; 1829 } 1830 else if (!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations, moduleNameIsEqualTo)) { 1831 // moduleAugmentations has changed 1832 structureIsReused = StructureIsReused.SafeModules; 1833 } 1834 else if ((oldSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags) !== (newSourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags)) { 1835 // dynamicImport has changed 1836 structureIsReused = StructureIsReused.SafeModules; 1837 } 1838 else if (!arrayIsEqualTo(oldSourceFile.typeReferenceDirectives, newSourceFile.typeReferenceDirectives, fileReferenceIsEqualTo)) { 1839 // 'types' references has changed 1840 structureIsReused = StructureIsReused.SafeModules; 1841 } 1842 } 1843 1844 // tentatively approve the file 1845 modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile }); 1846 } 1847 else if (hasInvalidatedResolutions(oldSourceFile.path)) { 1848 // 'module/types' references could have changed 1849 structureIsReused = StructureIsReused.SafeModules; 1850 1851 // add file to the modified list so that we will resolve it later 1852 modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile }); 1853 } 1854 1855 // if file has passed all checks it should be safe to reuse it 1856 newSourceFiles.push(newSourceFile); 1857 } 1858 1859 if (structureIsReused !== StructureIsReused.Completely) { 1860 return structureIsReused; 1861 } 1862 1863 const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile); 1864 for (const oldFile of oldSourceFiles) { 1865 if (!contains(modifiedFiles, oldFile)) { 1866 for (const moduleName of oldFile.ambientModuleNames) { 1867 ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName); 1868 } 1869 } 1870 } 1871 // try to verify results of module resolution 1872 for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) { 1873 const moduleNames = getModuleNames(newSourceFile); 1874 const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile); 1875 // ensure that module resolution results are still correct 1876 const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, oldSourceFile, moduleResolutionIsEqualTo); 1877 if (resolutionsChanged) { 1878 structureIsReused = StructureIsReused.SafeModules; 1879 newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions); 1880 } 1881 else { 1882 newSourceFile.resolvedModules = oldSourceFile.resolvedModules; 1883 } 1884 const typesReferenceDirectives = newSourceFile.typeReferenceDirectives; 1885 const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile); 1886 // ensure that types resolutions are still correct 1887 const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, oldSourceFile, typeDirectiveIsEqualTo); 1888 if (typeReferenceResolutionsChanged) { 1889 structureIsReused = StructureIsReused.SafeModules; 1890 newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions); 1891 } 1892 else { 1893 newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames; 1894 } 1895 } 1896 1897 if (structureIsReused !== StructureIsReused.Completely) { 1898 return structureIsReused; 1899 } 1900 1901 if (changesAffectingProgramStructure(oldOptions, options) || host.hasChangedAutomaticTypeDirectiveNames?.()) { 1902 return StructureIsReused.SafeModules; 1903 } 1904 1905 missingFilePaths = oldProgram.getMissingFilePaths(); 1906 1907 // update fileName -> file mapping 1908 Debug.assert(newSourceFiles.length === oldProgram.getSourceFiles().length); 1909 for (const newSourceFile of newSourceFiles) { 1910 filesByName.set(newSourceFile.path, newSourceFile); 1911 } 1912 const oldFilesByNameMap = oldProgram.getFilesByNameMap(); 1913 oldFilesByNameMap.forEach((oldFile, path) => { 1914 if (!oldFile) { 1915 filesByName.set(path, oldFile); 1916 return; 1917 } 1918 if (oldFile.path === path) { 1919 // Set the file as found during node modules search if it was found that way in old progra, 1920 if (oldProgram!.isSourceFileFromExternalLibrary(oldFile)) { 1921 sourceFilesFoundSearchingNodeModules.set(oldFile.path, true); 1922 } 1923 return; 1924 } 1925 filesByName.set(path, filesByName.get(oldFile.path)); 1926 }); 1927 1928 files = newSourceFiles; 1929 fileReasons = oldProgram.getFileIncludeReasons(); 1930 fileProcessingDiagnostics = oldProgram.getFileProcessingDiagnostics(); 1931 resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives(); 1932 1933 sourceFileToPackageName = oldProgram.sourceFileToPackageName; 1934 redirectTargetsMap = oldProgram.redirectTargetsMap; 1935 usesUriStyleNodeCoreModules = oldProgram.usesUriStyleNodeCoreModules; 1936 1937 return StructureIsReused.Completely; 1938 } 1939 1940 function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost { 1941 return { 1942 getPrependNodes, 1943 getCanonicalFileName, 1944 getCommonSourceDirectory: program.getCommonSourceDirectory, 1945 getCompilerOptions: program.getCompilerOptions, 1946 getCurrentDirectory: () => currentDirectory, 1947 getNewLine: () => host.getNewLine(), 1948 getSourceFile: program.getSourceFile, 1949 getSourceFileByPath: program.getSourceFileByPath, 1950 getSourceFiles: program.getSourceFiles, 1951 getLibFileFromReference: program.getLibFileFromReference, 1952 isSourceFileFromExternalLibrary, 1953 getResolvedProjectReferenceToRedirect, 1954 getProjectReferenceRedirect, 1955 isSourceOfProjectReferenceRedirect, 1956 getSymlinkCache, 1957 writeFile: writeFileCallback || writeFile, 1958 isEmitBlocked, 1959 readFile: f => host.readFile(f), 1960 fileExists: f => { 1961 // Use local caches 1962 const path = toPath(f); 1963 if (getSourceFileByPath(path)) return true; 1964 if (contains(missingFilePaths, path)) return false; 1965 // Before falling back to the host 1966 return host.fileExists(f); 1967 }, 1968 useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(), 1969 getProgramBuildInfo: () => program.getProgramBuildInfo && program.getProgramBuildInfo(), 1970 getSourceFileFromReference: (file, ref) => program.getSourceFileFromReference(file, ref), 1971 redirectTargetsMap, 1972 getFileIncludeReasons: program.getFileIncludeReasons, 1973 createHash: maybeBind(host, host.createHash), 1974 getProgramBuildInfoForLinter: () => program.getProgramBuildInfoForLinter && program.getProgramBuildInfoForLinter(), 1975 }; 1976 } 1977 1978 function writeFile( 1979 fileName: string, 1980 text: string, 1981 writeByteOrderMark: boolean, 1982 onError?: (message: string) => void, 1983 sourceFiles?: readonly SourceFile[], 1984 data?: WriteFileCallbackData 1985 ) { 1986 host.writeFile(fileName, text, writeByteOrderMark, onError, sourceFiles, data); 1987 } 1988 1989 function emitBuildInfo(writeFileCallback?: WriteFileCallback): EmitResult { 1990 Debug.assert(!outFile(options)); 1991 tracing?.push(tracing.Phase.Emit, "emitBuildInfo", {}, /*separateBeginAndEnd*/ true); 1992 performance.mark("beforeEmit"); 1993 const emitResult = emitFiles( 1994 notImplementedResolver, 1995 getEmitHost(writeFileCallback), 1996 /*targetSourceFile*/ undefined, 1997 /*transformers*/ noTransformers, 1998 /*emitOnlyDtsFiles*/ false, 1999 /*onlyBuildInfo*/ true 2000 ); 2001 2002 performance.mark("afterEmit"); 2003 performance.measure("Emit", "beforeEmit", "afterEmit"); 2004 tracing?.pop(); 2005 return emitResult; 2006 } 2007 2008 function getResolvedProjectReferences() { 2009 return resolvedProjectReferences; 2010 } 2011 2012 function getProjectReferences() { 2013 return projectReferences; 2014 } 2015 2016 function getPrependNodes() { 2017 return createPrependNodes( 2018 projectReferences, 2019 (_ref, index) => resolvedProjectReferences![index]?.commandLine, 2020 fileName => { 2021 const path = toPath(fileName); 2022 const sourceFile = getSourceFileByPath(path); 2023 return sourceFile ? sourceFile.text : filesByName.has(path) ? undefined : host.readFile(path); 2024 } 2025 ); 2026 } 2027 2028 function isSourceFileFromExternalLibrary(file: SourceFile): boolean { 2029 return !!sourceFilesFoundSearchingNodeModules.get(file.path); 2030 } 2031 2032 function isSourceFileDefaultLibrary(file: SourceFile): boolean { 2033 if (!file.isDeclarationFile) { 2034 return false; 2035 } 2036 2037 if (file.hasNoDefaultLib) { 2038 return true; 2039 } 2040 2041 if (!options.noLib) { 2042 return false; 2043 } 2044 2045 // If '--lib' is not specified, include default library file according to '--target' 2046 // otherwise, using options specified in '--lib' instead of '--target' default library file 2047 const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive; 2048 if (!options.lib) { 2049 return equalityComparer(file.fileName, getDefaultLibraryFileName()); 2050 } 2051 else { 2052 return some(options.lib, libFileName => equalityComparer(file.fileName, pathForLibFile(libFileName))); 2053 } 2054 } 2055 2056 function getEtsLibSFromProgram() { 2057 return getEtsLibs(program); 2058 } 2059 2060 function getTypeChecker() { 2061 return typeChecker || (typeChecker = createTypeChecker(program)); 2062 } 2063 2064 function getLinterTypeChecker() { 2065 return linterTypeChecker || (linterTypeChecker = createTypeChecker(program, true)); 2066 } 2067 2068 function emit(sourceFile?: SourceFile, writeFileCallback?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, transformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { 2069 tracing?.push(tracing.Phase.Emit, "emit", { path: sourceFile?.path }, /*separateBeginAndEnd*/ true); 2070 const result = runWithCancellationToken(() => emitWorker(program, sourceFile, writeFileCallback, cancellationToken, emitOnlyDtsFiles, transformers, forceDtsEmit)); 2071 tracing?.pop(); 2072 return result; 2073 } 2074 2075 function isEmitBlocked(emitFileName: string): boolean { 2076 return hasEmitBlockingDiagnostics.has(toPath(emitFileName)); 2077 } 2078 2079 function emitWorker(program: Program, sourceFile: SourceFile | undefined, writeFileCallback: WriteFileCallback | undefined, cancellationToken: CancellationToken | undefined, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitResult { 2080 if (!forceDtsEmit) { 2081 const result = handleNoEmitOptions(program, sourceFile, writeFileCallback, cancellationToken); 2082 if (result) return result; 2083 } 2084 2085 // Create the emit resolver outside of the "emitTime" tracking code below. That way 2086 // any cost associated with it (like type checking) are appropriate associated with 2087 // the type-checking counter. 2088 // 2089 // If the -out option is specified, we should not pass the source file to getEmitResolver. 2090 // This is because in the -out scenario all files need to be emitted, and therefore all 2091 // files need to be type checked. And the way to specify that all files need to be type 2092 // checked is to not pass the file to getEmitResolver. 2093 const emitResolver = getTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken); 2094 2095 performance.mark("beforeEmit"); 2096 2097 const emitResult = emitFiles( 2098 emitResolver, 2099 getEmitHost(writeFileCallback), 2100 sourceFile, 2101 getTransformers(options, customTransformers, emitOnlyDtsFiles), 2102 emitOnlyDtsFiles, 2103 /*onlyBuildInfo*/ false, 2104 forceDtsEmit 2105 ); 2106 2107 performance.mark("afterEmit"); 2108 performance.measure("Emit", "beforeEmit", "afterEmit"); 2109 return emitResult; 2110 } 2111 2112 function getSourceFile(fileName: string): SourceFile | undefined { 2113 return getSourceFileByPath(toPath(fileName)); 2114 } 2115 2116 function getSourceFileByPath(path: Path): SourceFile | undefined { 2117 return filesByName.get(path) || undefined; 2118 } 2119 2120 function getDiagnosticsHelper<T extends Diagnostic>( 2121 sourceFile: SourceFile | undefined, 2122 getDiagnostics: (sourceFile: SourceFile, cancellationToken: CancellationToken | undefined) => readonly T[], 2123 cancellationToken: CancellationToken | undefined): readonly T[] { 2124 if (sourceFile) { 2125 return getDiagnostics(sourceFile, cancellationToken); 2126 } 2127 return sortAndDeduplicateDiagnostics(flatMap(program.getSourceFiles(), sourceFile => { 2128 if (cancellationToken) { 2129 cancellationToken.throwIfCancellationRequested(); 2130 } 2131 return getDiagnostics(sourceFile, cancellationToken); 2132 })); 2133 } 2134 2135 function getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { 2136 return getDiagnosticsHelper(sourceFile, getSyntacticDiagnosticsForFile, cancellationToken); 2137 } 2138 2139 function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { 2140 return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFile, cancellationToken); 2141 } 2142 2143 function getSemanticDiagnosticsForLinter(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[] { 2144 return getDiagnosticsHelper(sourceFile, getSemanticDiagnosticsForFileForLinter, cancellationToken); 2145 } 2146 2147 function getSemanticDiagnosticsForFileForLinter(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { 2148 return concatenate( 2149 filterSemanticDiagnostics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, true), options), 2150 getProgramDiagnostics(sourceFile) 2151 ); 2152 } 2153 2154 function getCachedSemanticDiagnostics(sourceFile?: SourceFile): readonly Diagnostic[] | undefined { 2155 return sourceFile 2156 ? cachedBindAndCheckDiagnosticsForFile.perFile?.get(sourceFile.path) 2157 : cachedBindAndCheckDiagnosticsForFile.allDiagnostics; 2158 } 2159 2160 function getBindAndCheckDiagnostics(sourceFile: SourceFile, cancellationToken?: CancellationToken, isForLinter?: boolean): readonly Diagnostic[] { 2161 return getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken, isForLinter); 2162 } 2163 2164 function getProgramDiagnostics(sourceFile: SourceFile): readonly Diagnostic[] { 2165 if (skipTypeChecking(sourceFile, options, program) || (sourceFile.isDeclarationFile && !!options.needDoArkTsLinter)) { 2166 return emptyArray; 2167 } 2168 2169 const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName); 2170 if (!sourceFile.commentDirectives?.length) { 2171 return programDiagnosticsInFile; 2172 } 2173 2174 return getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, programDiagnosticsInFile).diagnostics; 2175 } 2176 2177 function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] { 2178 const options = program.getCompilerOptions(); 2179 // collect diagnostics from the program only once if either no source file was specified or out/outFile is set (bundled emit) 2180 if (!sourceFile || outFile(options)) { 2181 return getDeclarationDiagnosticsWorker(sourceFile, cancellationToken); 2182 } 2183 else { 2184 return getDiagnosticsHelper(sourceFile, getDeclarationDiagnosticsForFile, cancellationToken); 2185 } 2186 } 2187 2188 function getSyntacticDiagnosticsForFile(sourceFile: SourceFile): readonly DiagnosticWithLocation[] { 2189 // For JavaScript files, we report semantic errors for using TypeScript-only 2190 // constructs from within a JavaScript file as syntactic errors. 2191 if (isSourceFileJS(sourceFile)) { 2192 if (!sourceFile.additionalSyntacticDiagnostics) { 2193 sourceFile.additionalSyntacticDiagnostics = getJSSyntacticDiagnosticsForFile(sourceFile); 2194 } 2195 return concatenate(sourceFile.additionalSyntacticDiagnostics, sourceFile.parseDiagnostics); 2196 } 2197 return sourceFile.parseDiagnostics; 2198 } 2199 2200 function runWithCancellationToken<T>(func: () => T): T { 2201 try { 2202 return func(); 2203 } 2204 catch (e) { 2205 if (e instanceof OperationCanceledException) { 2206 // We were canceled while performing the operation. Because our type checker 2207 // might be a bad state, we need to throw it away. 2208 typeChecker = undefined!; 2209 } 2210 2211 throw e; 2212 } 2213 } 2214 2215 function getSemanticDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined): readonly Diagnostic[] { 2216 return concatenate( 2217 filterSemanticDiagnostics(getBindAndCheckDiagnosticsForFile(sourceFile, cancellationToken), options), 2218 getProgramDiagnostics(sourceFile) 2219 ); 2220 } 2221 2222 function getBindAndCheckDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, 2223 isForLinter: boolean = false): readonly Diagnostic[] { 2224 if (!isForLinter) { 2225 return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFile, getBindAndCheckDiagnosticsForFileNoCache); 2226 } 2227 return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedBindAndCheckDiagnosticsForFileForLinter, 2228 getBindAndCheckDiagnosticsForFileNoCache, true); 2229 } 2230 2231 function getBindAndCheckDiagnosticsForFileNoCache(sourceFile: SourceFile, cancellationToken: CancellationToken | undefined, 2232 isForLinter: boolean = false): readonly Diagnostic[] { 2233 return runWithCancellationToken(() => { 2234 // Only check and block .d.ts import .ets behavior when it is called by "ets-loader" and scanned. 2235 const filterFlag = !!options.needDoArkTsLinter; 2236 2237 if (filterFlag) { 2238 options.skipLibCheck = false; 2239 } 2240 2241 if (skipTypeChecking(sourceFile, options, program)) { 2242 return emptyArray; 2243 } 2244 2245 const typeChecker = isForLinter ? getLinterTypeChecker() : getTypeChecker(); 2246 2247 Debug.assert(!!sourceFile.bindDiagnostics); 2248 2249 const isJs = sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX; 2250 const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options); 2251 const isPlainJs = isPlainJsFile(sourceFile, options.checkJs); 2252 const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false; 2253 2254 // By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External 2255 // - plain JS: .js files with no // ts-check and checkJs: undefined 2256 // - check JS: .js files with either // ts-check or checkJs: true 2257 // - external: files that are added by plugins 2258 const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX 2259 || sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred || sourceFile.scriptKind === ScriptKind.ETS); 2260 let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray; 2261 let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray; 2262 if (isPlainJs) { 2263 bindDiagnostics = filter(bindDiagnostics, d => plainJSErrors.has(d.code)); 2264 checkDiagnostics = filter(checkDiagnostics, d => plainJSErrors.has(d.code)); 2265 } 2266 // skip ts-expect-error errors in plain JS files, and skip JSDoc errors except in checked JS 2267 return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics && !isPlainJs, bindDiagnostics, 2268 filterFlag ? filterDiagnostics(checkDiagnostics) : checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined); 2269 }); 2270 } 2271 2272 function filterDiagnostics(allDiagnostics: (readonly Diagnostic[] | undefined)): readonly Diagnostic[] | undefined { 2273 if (allDiagnostics) { 2274 const diagnosticsAfterFilter = allDiagnostics.filter((item) => { 2275 /* Diagnostics suppression scheme: 2276 * 1.if `file` comes from `oh_modules`: 2277 * skip all the diagnostics in declaration files and importing ArkTS errors from TS files 2278 * done. 2279 * 2.if `file` is a d.ts: 2280 * if d.ts is a kit declaration which being named with `@kit.` prefix: 2281 * skip all the diagnostics. 2282 * else: 2283 * skip all the diagnostics other than forbidden imports. 2284 * done. 2285 */ 2286 const isOhModule = (item.file !== undefined) && (normalizePath(item.file.fileName).indexOf("/oh_modules/") !== -1); 2287 const isNotForbiddenImportDiag = item.messageText !== (options.isCompatibleVersion ? 2288 Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_about_to_be_forbidden.message : 2289 Diagnostics.Importing_ArkTS_files_in_JS_and_TS_files_is_forbidden.message); 2290 2291 if (isOhModule) { 2292 if (options.skipTscOhModuleCheck) { 2293 return false; 2294 } 2295 if (item.file?.isDeclarationFile) { 2296 return false; 2297 } 2298 return isNotForbiddenImportDiag; 2299 } 2300 2301 if (item.file?.scriptKind === ScriptKind.TS && item.file?.isDeclarationFile) { 2302 if (item.file !== undefined && getBaseFileName(item.file.fileName).indexOf('@kit.') === 0) { 2303 // kit declaration 2304 return false; 2305 } 2306 return !isNotForbiddenImportDiag; 2307 } 2308 return true; 2309 }); 2310 return diagnosticsAfterFilter; 2311 } 2312 return emptyArray; 2313 } 2314 2315 function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, includeBindAndCheckDiagnostics: boolean, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) { 2316 const flatDiagnostics = flatten(allDiagnostics); 2317 if (!includeBindAndCheckDiagnostics || !sourceFile.commentDirectives?.length) { 2318 return flatDiagnostics; 2319 } 2320 2321 const { diagnostics, directives } = getDiagnosticsWithPrecedingDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics); 2322 2323 for (const errorExpectation of directives.getUnusedExpectations()) { 2324 diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive)); 2325 } 2326 2327 return diagnostics; 2328 } 2329 2330 /** 2331 * Creates a map of comment directives along with the diagnostics immediately preceded by one of them. 2332 * Comments that match to any of those diagnostics are marked as used. 2333 */ 2334 function getDiagnosticsWithPrecedingDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) { 2335 // Diagnostics are only reported if there is no comment directive preceding them 2336 // This will modify the directives map by marking "used" ones with a corresponding diagnostic 2337 const directives = createCommentDirectivesMap(sourceFile, commentDirectives); 2338 const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1); 2339 2340 return { diagnostics, directives }; 2341 } 2342 2343 function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] { 2344 return runWithCancellationToken(() => { 2345 return getTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken); 2346 }); 2347 } 2348 2349 /** 2350 * @returns The line index marked as preceding the diagnostic, or -1 if none was. 2351 */ 2352 function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) { 2353 const { file, start } = diagnostic; 2354 if (!file) { 2355 return -1; 2356 } 2357 2358 // Start out with the line just before the text 2359 const lineStarts = getLineStarts(file); 2360 let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217 2361 while (line >= 0) { 2362 // As soon as that line is known to have a comment directive, use that 2363 if (directives.markUsed(line)) { 2364 return line; 2365 } 2366 2367 // Stop searching if the line is not empty and not a comment 2368 const lineText = file.text.slice(lineStarts[line], lineStarts[line + 1]).trim(); 2369 if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) { 2370 return -1; 2371 } 2372 2373 line--; 2374 } 2375 2376 return -1; 2377 } 2378 2379 function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] { 2380 return runWithCancellationToken(() => { 2381 const diagnostics: DiagnosticWithLocation[] = []; 2382 walk(sourceFile, sourceFile); 2383 forEachChildRecursively(sourceFile, walk, walkArray); 2384 2385 return diagnostics; 2386 2387 function walk(node: Node, parent: Node) { 2388 // Return directly from the case if the given node doesnt want to visit each child 2389 // Otherwise break to visit each child 2390 2391 switch (parent.kind) { 2392 case SyntaxKind.Parameter: 2393 case SyntaxKind.PropertyDeclaration: 2394 case SyntaxKind.MethodDeclaration: 2395 if ((parent as ParameterDeclaration | PropertyDeclaration | MethodDeclaration).questionToken === node) { 2396 diagnostics.push(createDiagnosticForNode(node, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, "?")); 2397 return "skip"; 2398 } 2399 // falls through 2400 case SyntaxKind.MethodSignature: 2401 case SyntaxKind.Constructor: 2402 case SyntaxKind.GetAccessor: 2403 case SyntaxKind.SetAccessor: 2404 case SyntaxKind.FunctionExpression: 2405 case SyntaxKind.FunctionDeclaration: 2406 case SyntaxKind.ArrowFunction: 2407 case SyntaxKind.VariableDeclaration: 2408 // type annotation 2409 if ((parent as FunctionLikeDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration).type === node) { 2410 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_annotations_can_only_be_used_in_TypeScript_files)); 2411 return "skip"; 2412 } 2413 } 2414 2415 switch (node.kind) { 2416 case SyntaxKind.ImportClause: 2417 if ((node as ImportClause).isTypeOnly) { 2418 diagnostics.push(createDiagnosticForNode(parent, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "import type")); 2419 return "skip"; 2420 } 2421 break; 2422 case SyntaxKind.ExportDeclaration: 2423 if ((node as ExportDeclaration).isTypeOnly) { 2424 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, "export type")); 2425 return "skip"; 2426 } 2427 break; 2428 case SyntaxKind.ImportSpecifier: 2429 case SyntaxKind.ExportSpecifier: 2430 if ((node as ImportOrExportSpecifier).isTypeOnly) { 2431 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, isImportSpecifier(node) ? "import...type" : "export...type")); 2432 return "skip"; 2433 } 2434 break; 2435 case SyntaxKind.ImportEqualsDeclaration: 2436 diagnostics.push(createDiagnosticForNode(node, Diagnostics.import_can_only_be_used_in_TypeScript_files)); 2437 return "skip"; 2438 case SyntaxKind.ExportAssignment: 2439 if ((node as ExportAssignment).isExportEquals) { 2440 diagnostics.push(createDiagnosticForNode(node, Diagnostics.export_can_only_be_used_in_TypeScript_files)); 2441 return "skip"; 2442 } 2443 break; 2444 case SyntaxKind.HeritageClause: 2445 const heritageClause = node as HeritageClause; 2446 if (heritageClause.token === SyntaxKind.ImplementsKeyword) { 2447 diagnostics.push(createDiagnosticForNode(node, Diagnostics.implements_clauses_can_only_be_used_in_TypeScript_files)); 2448 return "skip"; 2449 } 2450 break; 2451 case SyntaxKind.InterfaceDeclaration: 2452 const interfaceKeyword = tokenToString(SyntaxKind.InterfaceKeyword); 2453 Debug.assertIsDefined(interfaceKeyword); 2454 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, interfaceKeyword)); 2455 return "skip"; 2456 case SyntaxKind.ModuleDeclaration: 2457 const moduleKeyword = node.flags & NodeFlags.Namespace ? tokenToString(SyntaxKind.NamespaceKeyword) : tokenToString(SyntaxKind.ModuleKeyword); 2458 Debug.assertIsDefined(moduleKeyword); 2459 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, moduleKeyword)); 2460 return "skip"; 2461 case SyntaxKind.TypeAliasDeclaration: 2462 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Type_aliases_can_only_be_used_in_TypeScript_files)); 2463 return "skip"; 2464 case SyntaxKind.EnumDeclaration: 2465 const enumKeyword = Debug.checkDefined(tokenToString(SyntaxKind.EnumKeyword)); 2466 diagnostics.push(createDiagnosticForNode(node, Diagnostics._0_declarations_can_only_be_used_in_TypeScript_files, enumKeyword)); 2467 return "skip"; 2468 case SyntaxKind.NonNullExpression: 2469 diagnostics.push(createDiagnosticForNode(node, Diagnostics.Non_null_assertions_can_only_be_used_in_TypeScript_files)); 2470 return "skip"; 2471 case SyntaxKind.AsExpression: 2472 diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.Type_assertion_expressions_can_only_be_used_in_TypeScript_files)); 2473 return "skip"; 2474 case SyntaxKind.SatisfiesExpression: 2475 diagnostics.push(createDiagnosticForNode((node as SatisfiesExpression).type, Diagnostics.Type_satisfaction_expressions_can_only_be_used_in_TypeScript_files)); 2476 return "skip"; 2477 case SyntaxKind.TypeAssertionExpression: 2478 Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX. 2479 } 2480 } 2481 2482 function walkArray(nodes: NodeArray<Node>, parent: Node) { 2483 if (canHaveModifiers(parent) && parent.modifiers === nodes && some(nodes, isDecorator) && !options.experimentalDecorators) { 2484 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)); 2485 } 2486 2487 switch (parent.kind) { 2488 case SyntaxKind.ClassDeclaration: 2489 case SyntaxKind.ClassExpression: 2490 case SyntaxKind.StructDeclaration: 2491 case SyntaxKind.MethodDeclaration: 2492 case SyntaxKind.Constructor: 2493 case SyntaxKind.GetAccessor: 2494 case SyntaxKind.SetAccessor: 2495 case SyntaxKind.FunctionExpression: 2496 case SyntaxKind.FunctionDeclaration: 2497 case SyntaxKind.ArrowFunction: 2498 // Check type parameters 2499 if (nodes === (parent as DeclarationWithTypeParameterChildren).typeParameters) { 2500 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_parameter_declarations_can_only_be_used_in_TypeScript_files)); 2501 return "skip"; 2502 } 2503 // falls through 2504 2505 case SyntaxKind.VariableStatement: 2506 // Check modifiers 2507 if (nodes === (parent as VariableStatement).modifiers) { 2508 checkModifiers((parent as VariableStatement).modifiers!, parent.kind === SyntaxKind.VariableStatement); 2509 return "skip"; 2510 } 2511 break; 2512 case SyntaxKind.PropertyDeclaration: 2513 // Check modifiers of property declaration 2514 if (nodes === (parent as PropertyDeclaration).modifiers) { 2515 for (const modifier of nodes as NodeArray<ModifierLike>) { 2516 if (isModifier(modifier) 2517 && modifier.kind !== SyntaxKind.StaticKeyword 2518 && modifier.kind !== SyntaxKind.AccessorKeyword) { 2519 diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind))); 2520 } 2521 } 2522 return "skip"; 2523 } 2524 break; 2525 case SyntaxKind.Parameter: 2526 // Check modifiers of parameter declaration 2527 if (nodes === (parent as ParameterDeclaration).modifiers && some(nodes, isModifier)) { 2528 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Parameter_modifiers_can_only_be_used_in_TypeScript_files)); 2529 return "skip"; 2530 } 2531 break; 2532 case SyntaxKind.CallExpression: 2533 case SyntaxKind.NewExpression: 2534 case SyntaxKind.ExpressionWithTypeArguments: 2535 case SyntaxKind.JsxSelfClosingElement: 2536 case SyntaxKind.JsxOpeningElement: 2537 case SyntaxKind.TaggedTemplateExpression: 2538 // Check type arguments 2539 if (nodes === (parent as NodeWithTypeArguments).typeArguments) { 2540 diagnostics.push(createDiagnosticForNodeArray(nodes, Diagnostics.Type_arguments_can_only_be_used_in_TypeScript_files)); 2541 return "skip"; 2542 } 2543 break; 2544 } 2545 } 2546 2547 function checkModifiers(modifiers: NodeArray<ModifierLike>, isConstValid: boolean) { 2548 for (const modifier of modifiers) { 2549 switch (modifier.kind) { 2550 case SyntaxKind.ConstKeyword: 2551 if (isConstValid) { 2552 continue; 2553 } 2554 // to report error, 2555 // falls through 2556 case SyntaxKind.PublicKeyword: 2557 case SyntaxKind.PrivateKeyword: 2558 case SyntaxKind.ProtectedKeyword: 2559 case SyntaxKind.ReadonlyKeyword: 2560 case SyntaxKind.DeclareKeyword: 2561 case SyntaxKind.AbstractKeyword: 2562 case SyntaxKind.OverrideKeyword: 2563 case SyntaxKind.InKeyword: 2564 case SyntaxKind.OutKeyword: 2565 diagnostics.push(createDiagnosticForNode(modifier, Diagnostics.The_0_modifier_can_only_be_used_in_TypeScript_files, tokenToString(modifier.kind))); 2566 break; 2567 2568 // These are all legal modifiers. 2569 case SyntaxKind.StaticKeyword: 2570 case SyntaxKind.ExportKeyword: 2571 case SyntaxKind.DefaultKeyword: 2572 case SyntaxKind.AccessorKeyword: 2573 } 2574 } 2575 } 2576 2577 function createDiagnosticForNodeArray(nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation { 2578 const start = nodes.pos; 2579 return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2); 2580 } 2581 2582 // Since these are syntactic diagnostics, parent might not have been set 2583 // this means the sourceFile cannot be infered from the node 2584 function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation { 2585 return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2); 2586 } 2587 }); 2588 } 2589 2590 function getDeclarationDiagnosticsWorker(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { 2591 return getAndCacheDiagnostics(sourceFile, cancellationToken, cachedDeclarationDiagnosticsForFile, getDeclarationDiagnosticsForFileNoCache); 2592 } 2593 2594 function getDeclarationDiagnosticsForFileNoCache(sourceFile: SourceFile | undefined, cancellationToken: CancellationToken | undefined): readonly DiagnosticWithLocation[] { 2595 return runWithCancellationToken(() => { 2596 const resolver = getTypeChecker().getEmitResolver(sourceFile, cancellationToken); 2597 // Don't actually write any files since we're just getting diagnostics. 2598 return ts.getDeclarationDiagnostics(getEmitHost(noop), resolver, sourceFile) || emptyArray; 2599 }); 2600 } 2601 2602 function getAndCacheDiagnostics<T extends SourceFile | undefined, U extends Diagnostic>( 2603 sourceFile: T, 2604 cancellationToken: CancellationToken | undefined, 2605 cache: DiagnosticCache<U>, 2606 getDiagnostics: (sourceFile: T, cancellationToken: CancellationToken | undefined, isForLinter?: boolean) => readonly U[], 2607 isForLinter?: boolean 2608 ): readonly U[] { 2609 2610 const cachedResult = sourceFile 2611 ? cache.perFile?.get(sourceFile.path) 2612 : cache.allDiagnostics; 2613 2614 if (cachedResult) { 2615 return cachedResult; 2616 } 2617 2618 let result; 2619 if (isForLinter !== undefined) { 2620 result = getDiagnostics(sourceFile, cancellationToken, isForLinter); 2621 } else { 2622 result = getDiagnostics(sourceFile, cancellationToken); 2623 } 2624 2625 if (sourceFile) { 2626 (cache.perFile || (cache.perFile = new Map())).set(sourceFile.path, result); 2627 } 2628 else { 2629 cache.allDiagnostics = result; 2630 } 2631 return result; 2632 } 2633 2634 function getDeclarationDiagnosticsForFile(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] { 2635 return sourceFile.isDeclarationFile ? [] : getDeclarationDiagnosticsWorker(sourceFile, cancellationToken); 2636 } 2637 2638 function getOptionsDiagnostics(): SortedReadonlyArray<Diagnostic> { 2639 return sortAndDeduplicateDiagnostics(concatenate( 2640 programDiagnostics.getGlobalDiagnostics(), 2641 getOptionsDiagnosticsOfConfigFile() 2642 )); 2643 } 2644 2645 function getOptionsDiagnosticsOfConfigFile() { 2646 if (!options.configFile) return emptyArray; 2647 let diagnostics = programDiagnostics.getDiagnostics(options.configFile.fileName); 2648 forEachResolvedProjectReference(resolvedRef => { 2649 diagnostics = concatenate(diagnostics, programDiagnostics.getDiagnostics(resolvedRef.sourceFile.fileName)); 2650 }); 2651 return diagnostics; 2652 } 2653 2654 function getGlobalDiagnostics(): SortedReadonlyArray<Diagnostic> { 2655 return rootNames.length ? sortAndDeduplicateDiagnostics(getTypeChecker().getGlobalDiagnostics().slice()) : emptyArray as any as SortedReadonlyArray<Diagnostic>; 2656 } 2657 2658 function getConfigFileParsingDiagnostics(): readonly Diagnostic[] { 2659 return configFileParsingDiagnostics || emptyArray; 2660 } 2661 2662 function processRootFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason) { 2663 processSourceFile(normalizePath(fileName), isDefaultLib, ignoreNoDefaultLib, /*packageId*/ undefined, reason); 2664 } 2665 2666 function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean { 2667 return a.fileName === b.fileName; 2668 } 2669 2670 function moduleNameIsEqualTo(a: StringLiteralLike | Identifier, b: StringLiteralLike | Identifier): boolean { 2671 return a.kind === SyntaxKind.Identifier 2672 ? b.kind === SyntaxKind.Identifier && a.escapedText === b.escapedText 2673 : b.kind === SyntaxKind.StringLiteral && a.text === b.text; 2674 } 2675 2676 function createSyntheticImport(text: string, file: SourceFile) { 2677 const externalHelpersModuleReference = factory.createStringLiteral(text); 2678 const importDecl = factory.createImportDeclaration(/*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference, /*assertClause*/ undefined); 2679 addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper); 2680 setParent(externalHelpersModuleReference, importDecl); 2681 setParent(importDecl, file); 2682 // explicitly unset the synthesized flag on these declarations so the checker API will answer questions about them 2683 // (which is required to build the dependency graph for incremental emit) 2684 (externalHelpersModuleReference as Mutable<Node>).flags &= ~NodeFlags.Synthesized; 2685 (importDecl as Mutable<Node>).flags &= ~NodeFlags.Synthesized; 2686 return externalHelpersModuleReference; 2687 } 2688 2689 function collectExternalModuleReferences(file: SourceFile): void { 2690 if (file.imports) { 2691 return; 2692 } 2693 2694 const isJavaScriptFile = isSourceFileJS(file); 2695 const isExternalModuleFile = isExternalModule(file); 2696 2697 // file.imports may not be undefined if there exists dynamic import 2698 let imports: StringLiteralLike[] | undefined; 2699 let moduleAugmentations: (StringLiteral | Identifier)[] | undefined; 2700 let ambientModules: string[] | undefined; 2701 2702 // If we are importing helpers, we need to add a synthetic reference to resolve the 2703 // helpers library. 2704 if ((options.isolatedModules || isExternalModuleFile) 2705 && !file.isDeclarationFile) { 2706 if (options.importHelpers) { 2707 // synthesize 'import "tslib"' declaration 2708 imports = [createSyntheticImport(externalHelpersModuleNameText, file)]; 2709 } 2710 const jsxImport = getJSXRuntimeImport(getJSXImplicitImportBase(options, file), options); 2711 if (jsxImport) { 2712 // synthesize `import "base/jsx-runtime"` declaration 2713 (imports ||= []).push(createSyntheticImport(jsxImport, file)); 2714 } 2715 } 2716 2717 for (const node of file.statements) { 2718 collectModuleReferences(node, /*inAmbientModule*/ false); 2719 } 2720 if ((file.flags & NodeFlags.PossiblyContainsDynamicImport) || isJavaScriptFile) { 2721 collectDynamicImportOrRequireCalls(file); 2722 } 2723 2724 file.imports = imports || emptyArray; 2725 file.moduleAugmentations = moduleAugmentations || emptyArray; 2726 file.ambientModuleNames = ambientModules || emptyArray; 2727 2728 return; 2729 2730 function collectModuleReferences(node: Statement, inAmbientModule: boolean): void { 2731 if (isAnyImportOrReExport(node)) { 2732 const moduleNameExpr = getExternalModuleName(node); 2733 // TypeScript 1.0 spec (April 2014): 12.1.6 2734 // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules 2735 // only through top - level external module names. Relative external module names are not permitted. 2736 if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) { 2737 setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here 2738 imports = append(imports, moduleNameExpr); 2739 if (!usesUriStyleNodeCoreModules && currentNodeModulesDepth === 0 && !file.isDeclarationFile) { 2740 usesUriStyleNodeCoreModules = startsWith(moduleNameExpr.text, "node:"); 2741 } 2742 } 2743 } 2744 else if (isModuleDeclaration(node)) { 2745 if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) { 2746 (node.name as Mutable<Node>).parent = node; 2747 const nameText = getTextOfIdentifierOrLiteral(node.name); 2748 // Ambient module declarations can be interpreted as augmentations for some existing external modules. 2749 // This will happen in two cases: 2750 // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope 2751 // - if current file is not external module then module augmentation is an ambient module declaration with non-relative module name 2752 // immediately nested in top level ambient module declaration . 2753 if (isExternalModuleFile || (inAmbientModule && !isExternalModuleNameRelative(nameText))) { 2754 (moduleAugmentations || (moduleAugmentations = [])).push(node.name); 2755 } 2756 else if (!inAmbientModule) { 2757 if (file.isDeclarationFile) { 2758 // for global .d.ts files record name of ambient module 2759 (ambientModules || (ambientModules = [])).push(nameText); 2760 } 2761 // An AmbientExternalModuleDeclaration declares an external module. 2762 // This type of declaration is permitted only in the global module. 2763 // The StringLiteral must specify a top - level external module name. 2764 // Relative external module names are not permitted 2765 2766 // NOTE: body of ambient module is always a module block, if it exists 2767 const body = (node as ModuleDeclaration).body as ModuleBlock; 2768 if (body) { 2769 for (const statement of body.statements) { 2770 collectModuleReferences(statement, /*inAmbientModule*/ true); 2771 } 2772 } 2773 } 2774 } 2775 } 2776 } 2777 2778 function collectDynamicImportOrRequireCalls(file: SourceFile) { 2779 const r = /import|require/g; 2780 while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null 2781 const node = getNodeAtPosition(file, r.lastIndex); 2782 if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) { 2783 setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here 2784 imports = append(imports, node.arguments[0]); 2785 } 2786 // 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. 2787 else if (isImportCall(node) && node.arguments.length >= 1 && isStringLiteralLike(node.arguments[0])) { 2788 setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here 2789 imports = append(imports, node.arguments[0]); 2790 } 2791 else if (isLiteralImportTypeNode(node)) { 2792 setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here 2793 imports = append(imports, node.argument.literal); 2794 } 2795 } 2796 } 2797 2798 /** Returns a token if position is in [start-of-leading-trivia, end), includes JSDoc only in JS files */ 2799 function getNodeAtPosition(sourceFile: SourceFile, position: number): Node { 2800 let current: Node = sourceFile; 2801 const getContainingChild = (child: Node) => { 2802 if (child.pos <= position && (position < child.end || (position === child.end && (child.kind === SyntaxKind.EndOfFileToken)))) { 2803 return child; 2804 } 2805 }; 2806 while (true) { 2807 const child = isJavaScriptFile && hasJSDocNodes(current) && forEach(current.jsDoc, getContainingChild) || forEachChild(current, getContainingChild); 2808 if (!child) { 2809 return current; 2810 } 2811 current = child; 2812 } 2813 } 2814 } 2815 2816 function getLibFileFromReference(ref: FileReference) { 2817 const libName = toFileNameLowerCase(ref.fileName); 2818 const libFileName = libMap.get(libName); 2819 if (libFileName) { 2820 return getSourceFile(pathForLibFile(libFileName)); 2821 } 2822 } 2823 2824 /** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ 2825 function getSourceFileFromReference(referencingFile: SourceFile | UnparsedSource, ref: FileReference): SourceFile | undefined { 2826 return getSourceFileFromReferenceWorker(resolveTripleslashReference(ref.fileName, referencingFile.fileName), getSourceFile); 2827 } 2828 2829 function getSourceFileFromReferenceWorker( 2830 fileName: string, 2831 getSourceFile: (fileName: string) => SourceFile | undefined, 2832 fail?: (diagnostic: DiagnosticMessage, ...argument: string[]) => void, 2833 reason?: FileIncludeReason): SourceFile | undefined { 2834 2835 if (hasExtension(fileName)) { 2836 const canonicalFileName = host.getCanonicalFileName(fileName); 2837 if (!options.allowNonTsExtensions && !forEach(flatten(supportedExtensionsWithJsonIfResolveJsonModule), extension => fileExtensionIs(canonicalFileName, extension)) && !fileExtensionIs(canonicalFileName, Extension.Ets)) { 2838 if (fail) { 2839 if (hasJSFileExtension(canonicalFileName)) { 2840 fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName); 2841 } 2842 else { 2843 fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'"); 2844 } 2845 } 2846 return undefined; 2847 } 2848 2849 const sourceFile = getSourceFile(fileName); 2850 if (fail) { 2851 if (!sourceFile) { 2852 const redirect = getProjectReferenceRedirect(fileName); 2853 if (redirect) { 2854 fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName); 2855 } 2856 else { 2857 fail(Diagnostics.File_0_not_found, fileName); 2858 } 2859 } 2860 else if (isReferencedFile(reason) && canonicalFileName === host.getCanonicalFileName(getSourceFileByPath(reason.file)!.fileName)) { 2861 fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); 2862 } 2863 } 2864 return sourceFile; 2865 } 2866 else { 2867 const sourceFileNoExtension = options.allowNonTsExtensions && getSourceFile(fileName); 2868 if (sourceFileNoExtension) return sourceFileNoExtension; 2869 2870 if (fail && options.allowNonTsExtensions) { 2871 fail(Diagnostics.File_0_not_found, fileName); 2872 return undefined; 2873 } 2874 2875 // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts) 2876 const sourceFileWithAddedExtension = forEach(supportedExtensions[0], extension => getSourceFile(fileName + extension)); 2877 if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'"); 2878 return sourceFileWithAddedExtension; 2879 } 2880 } 2881 2882 /** This has side effects through `findSourceFile`. */ 2883 function processSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, packageId: PackageId | undefined, reason: FileIncludeReason): void { 2884 getSourceFileFromReferenceWorker( 2885 fileName, 2886 fileName => findSourceFile(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId), // TODO: GH#18217 2887 (diagnostic, ...args) => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, diagnostic, args), 2888 reason 2889 ); 2890 } 2891 2892 function processProjectReferenceFile(fileName: string, reason: ProjectReferenceFile) { 2893 return processSourceFile(fileName, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, /*packageId*/ undefined, reason); 2894 } 2895 2896 function reportFileNamesDifferOnlyInCasingError(fileName: string, existingFile: SourceFile, reason: FileIncludeReason): void { 2897 const hasExistingReasonToReportErrorOn = !isReferencedFile(reason) && some(fileReasons.get(existingFile.path), isReferencedFile); 2898 if (hasExistingReasonToReportErrorOn) { 2899 addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.Already_included_file_name_0_differs_from_file_name_1_only_in_casing, [existingFile.fileName, fileName]); 2900 } 2901 else { 2902 addFilePreprocessingFileExplainingDiagnostic(existingFile, reason, Diagnostics.File_name_0_differs_from_already_included_file_name_1_only_in_casing, [fileName, existingFile.fileName]); 2903 } 2904 } 2905 2906 function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path, resolvedPath: Path, originalFileName: string, sourceFileOptions: CreateSourceFileOptions): SourceFile { 2907 const redirect: SourceFile = Object.create(redirectTarget); 2908 redirect.fileName = fileName; 2909 redirect.path = path; 2910 redirect.resolvedPath = resolvedPath; 2911 redirect.originalFileName = originalFileName; 2912 redirect.redirectInfo = { redirectTarget, unredirected }; 2913 redirect.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined; 2914 redirect.packageJsonScope = sourceFileOptions.packageJsonScope; 2915 sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); 2916 Object.defineProperties(redirect, { 2917 id: { 2918 get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; }, 2919 set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo!.redirectTarget.id = value; }, 2920 }, 2921 symbol: { 2922 get(this: SourceFile) { return this.redirectInfo!.redirectTarget.symbol; }, 2923 set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo!.redirectTarget.symbol = value; }, 2924 }, 2925 }); 2926 return redirect; 2927 } 2928 2929 // Get source file from normalized fileName 2930 function findSourceFile(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { 2931 tracing?.push(tracing.Phase.Program, "findSourceFile", { 2932 fileName, 2933 isDefaultLib: isDefaultLib || undefined, 2934 fileIncludeKind: (FileIncludeKind as any)[reason.kind], 2935 }); 2936 const result = findSourceFileWorker(fileName, isDefaultLib, ignoreNoDefaultLib, reason, packageId); 2937 tracing?.pop(); 2938 return result; 2939 } 2940 2941 function getCreateSourceFileOptions(fileName: string, moduleResolutionCache: ModuleResolutionCache | undefined, host: CompilerHost, options: CompilerOptions): CreateSourceFileOptions { 2942 // 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 2943 // 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 2944 // 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. 2945 const result = getImpliedNodeFormatForFileWorker(getNormalizedAbsolutePath(fileName, currentDirectory), moduleResolutionCache?.getPackageJsonInfoCache(), host, options); 2946 const languageVersion = getEmitScriptTarget(options); 2947 const setExternalModuleIndicator = getSetExternalModuleIndicator(options); 2948 return typeof result === "object" ? 2949 { ...result, languageVersion, setExternalModuleIndicator } : 2950 { languageVersion, impliedNodeFormat: result, setExternalModuleIndicator }; 2951 } 2952 2953 function findSourceFileWorker(fileName: string, isDefaultLib: boolean, ignoreNoDefaultLib: boolean, reason: FileIncludeReason, packageId: PackageId | undefined): SourceFile | undefined { 2954 const path = toPath(fileName); 2955 if (useSourceOfProjectReferenceRedirect) { 2956 let source = getSourceOfProjectReferenceRedirect(path); 2957 // If preserveSymlinks is true, module resolution wont jump the symlink 2958 // but the resolved real path may be the .d.ts from project reference 2959 // Note:: Currently we try the real path only if the 2960 // file is from node_modules or oh_modules to avoid having to run real path on all file paths 2961 const modulesPathPart: string = getModulePathPartByPMType(options.packageManagerType); 2962 if (!source && 2963 host.realpath && 2964 options.preserveSymlinks && 2965 isDeclarationFileName(fileName) && 2966 stringContains(fileName, modulesPathPart)) { 2967 const realPath = toPath(host.realpath(fileName)); 2968 if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath); 2969 } 2970 if (source) { 2971 const file = isString(source) ? 2972 findSourceFile(source, isDefaultLib, ignoreNoDefaultLib, reason, packageId) : 2973 undefined; 2974 if (file) addFileToFilesByName(file, path, /*redirectedPath*/ undefined); 2975 return file; 2976 } 2977 } 2978 const originalFileName = fileName; 2979 if (filesByName.has(path)) { 2980 const file = filesByName.get(path); 2981 addFileIncludeReason(file || undefined, reason); 2982 // try to check if we've already seen this file but with a different casing in path 2983 // NOTE: this only makes sense for case-insensitive file systems, and only on files which are not redirected 2984 if (file && options.forceConsistentCasingInFileNames) { 2985 const checkedName = file.fileName; 2986 const isRedirect = toPath(checkedName) !== toPath(fileName); 2987 if (isRedirect) { 2988 fileName = getProjectReferenceRedirect(fileName) || fileName; 2989 } 2990 // Check if it differs only in drive letters its ok to ignore that error: 2991 const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory); 2992 const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory); 2993 if (checkedAbsolutePath !== inputAbsolutePath) { 2994 reportFileNamesDifferOnlyInCasingError(fileName, file, reason); 2995 } 2996 } 2997 2998 // If the file was previously found via a node_modules or oh_modules search, but is now being processed as a root file, 2999 // then everything it sucks in may also be marked incorrectly, and needs to be checked again. 3000 if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth === 0) { 3001 sourceFilesFoundSearchingNodeModules.set(file.path, false); 3002 if (!options.noResolve) { 3003 processReferencedFiles(file, isDefaultLib); 3004 processTypeReferenceDirectives(file); 3005 } 3006 if (!options.noLib) { 3007 processLibReferenceDirectives(file); 3008 } 3009 3010 modulesWithElidedImports.set(file.path, false); 3011 processImportedModules(file); 3012 } 3013 // See if we need to reprocess the imports due to prior skipped imports 3014 else if (file && modulesWithElidedImports.get(file.path)) { 3015 if (currentNodeModulesDepth < maxNodeModuleJsDepth) { 3016 modulesWithElidedImports.set(file.path, false); 3017 processImportedModules(file); 3018 } 3019 } 3020 3021 return file || undefined; 3022 } 3023 3024 let redirectedPath: Path | undefined; 3025 if (isReferencedFile(reason) && !useSourceOfProjectReferenceRedirect) { 3026 const redirectProject = getProjectReferenceRedirectProject(fileName); 3027 if (redirectProject) { 3028 if (outFile(redirectProject.commandLine.options)) { 3029 // Shouldnt create many to 1 mapping file in --out scenario 3030 return undefined; 3031 } 3032 const redirect = getProjectReferenceOutputName(redirectProject, fileName); 3033 fileName = redirect; 3034 // Once we start redirecting to a file, we can potentially come back to it 3035 // via a back-reference from another file in the .d.ts folder. If that happens we'll 3036 // end up trying to add it to the program *again* because we were tracking it via its 3037 // original (un-redirected) name. So we have to map both the original path and the redirected path 3038 // to the source file we're about to find/create 3039 redirectedPath = toPath(redirect); 3040 } 3041 } 3042 3043 // We haven't looked for this file, do so now and cache result 3044 const sourceFileOptions = getCreateSourceFileOptions(fileName, moduleResolutionCache, host, options); 3045 const file = host.getSourceFile( 3046 fileName, 3047 sourceFileOptions, 3048 hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]), 3049 shouldCreateNewSourceFile || (oldProgram?.getSourceFileByPath(toPath(fileName))?.impliedNodeFormat !== sourceFileOptions.impliedNodeFormat), 3050 options 3051 ); 3052 3053 if (packageId) { 3054 const packageIdKey = packageIdToString(packageId); 3055 const fileFromPackageId = packageIdToSourceFile.get(packageIdKey); 3056 if (fileFromPackageId) { 3057 // Some other SourceFile already exists with this package name and version. 3058 // Instead of creating a duplicate, just redirect to the existing one. 3059 const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName, sourceFileOptions); 3060 redirectTargetsMap.add(fileFromPackageId.path, fileName); 3061 addFileToFilesByName(dupFile, path, redirectedPath); 3062 addFileIncludeReason(dupFile, reason); 3063 sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); 3064 processingOtherFiles!.push(dupFile); 3065 return dupFile; 3066 } 3067 else if (file) { 3068 // This is the first source file to have this packageId. 3069 packageIdToSourceFile.set(packageIdKey, file); 3070 sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); 3071 } 3072 } 3073 addFileToFilesByName(file, path, redirectedPath); 3074 3075 if (file) { 3076 sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); 3077 file.fileName = fileName; // Ensure that source file has same name as what we were looking for 3078 file.path = path; 3079 file.resolvedPath = toPath(fileName); 3080 file.originalFileName = originalFileName; 3081 file.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined; 3082 file.packageJsonScope = sourceFileOptions.packageJsonScope; 3083 addFileIncludeReason(file, reason); 3084 3085 if (host.useCaseSensitiveFileNames()) { 3086 const pathLowerCase = toFileNameLowerCase(path); 3087 // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case 3088 const existingFile = filesByNameIgnoreCase!.get(pathLowerCase); 3089 if (existingFile) { 3090 reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason); 3091 } 3092 else { 3093 filesByNameIgnoreCase!.set(pathLowerCase, file); 3094 } 3095 } 3096 3097 skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib); 3098 3099 if (!options.noResolve) { 3100 processReferencedFiles(file, isDefaultLib); 3101 processTypeReferenceDirectives(file); 3102 } 3103 if (!options.noLib) { 3104 processLibReferenceDirectives(file); 3105 } 3106 3107 3108 // always process imported modules to record module name resolutions 3109 processImportedModules(file); 3110 3111 if (isDefaultLib) { 3112 processingDefaultLibFiles!.push(file); 3113 } 3114 else { 3115 processingOtherFiles!.push(file); 3116 } 3117 } 3118 return file; 3119 } 3120 3121 function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) { 3122 if (file) fileReasons.add(file.path, reason); 3123 } 3124 3125 function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) { 3126 if (redirectedPath) { 3127 filesByName.set(redirectedPath, file); 3128 filesByName.set(path, file || false); 3129 } 3130 else { 3131 filesByName.set(path, file); 3132 } 3133 } 3134 3135 function getProjectReferenceRedirect(fileName: string): string | undefined { 3136 const referencedProject = getProjectReferenceRedirectProject(fileName); 3137 return referencedProject && getProjectReferenceOutputName(referencedProject, fileName); 3138 } 3139 3140 function getProjectReferenceRedirectProject(fileName: string) { 3141 // Ignore dts or any json files 3142 if (!resolvedProjectReferences || !resolvedProjectReferences.length || isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json)) { 3143 return undefined; 3144 } 3145 3146 // If this file is produced by a referenced project, we need to rewrite it to 3147 // look in the output folder of the referenced project rather than the input 3148 return getResolvedProjectReferenceToRedirect(fileName); 3149 } 3150 3151 3152 function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) { 3153 const out = outFile(referencedProject.commandLine.options); 3154 return out ? 3155 changeExtension(out, Extension.Dts) : 3156 getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames()); 3157 } 3158 3159 /** 3160 * Get the referenced project if the file is input file from that reference project 3161 */ 3162 function getResolvedProjectReferenceToRedirect(fileName: string) { 3163 if (mapFromFileToProjectReferenceRedirects === undefined) { 3164 mapFromFileToProjectReferenceRedirects = new Map(); 3165 forEachResolvedProjectReference(referencedProject => { 3166 // not input file from the referenced project, ignore 3167 if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) { 3168 referencedProject.commandLine.fileNames.forEach(f => 3169 mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path)); 3170 } 3171 }); 3172 } 3173 3174 const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName)); 3175 return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath); 3176 } 3177 3178 function forEachResolvedProjectReference<T>( 3179 cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined 3180 ): T | undefined { 3181 return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb); 3182 } 3183 3184 function getSourceOfProjectReferenceRedirect(path: Path) { 3185 if (!isDeclarationFileName(path)) return undefined; 3186 if (mapFromToProjectReferenceRedirectSource === undefined) { 3187 mapFromToProjectReferenceRedirectSource = new Map(); 3188 forEachResolvedProjectReference(resolvedRef => { 3189 const out = outFile(resolvedRef.commandLine.options); 3190 if (out) { 3191 // Dont know which source file it means so return true? 3192 const outputDts = changeExtension(out, Extension.Dts); 3193 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true); 3194 } 3195 else { 3196 const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames())); 3197 forEach(resolvedRef.commandLine.fileNames, fileName => { 3198 if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { 3199 const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory); 3200 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName); 3201 } 3202 }); 3203 } 3204 }); 3205 } 3206 return mapFromToProjectReferenceRedirectSource.get(path); 3207 } 3208 3209 function isSourceOfProjectReferenceRedirect(fileName: string) { 3210 return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName); 3211 } 3212 3213 function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined { 3214 if (!projectReferenceRedirects) { 3215 return undefined; 3216 } 3217 3218 return projectReferenceRedirects.get(projectReferencePath) || undefined; 3219 } 3220 3221 function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { 3222 forEach(file.referencedFiles, (ref, index) => { 3223 processSourceFile( 3224 resolveTripleslashReference(ref.fileName, file.fileName), 3225 isDefaultLib, 3226 /*ignoreNoDefaultLib*/ false, 3227 /*packageId*/ undefined, 3228 { kind: FileIncludeKind.ReferenceFile, file: file.path, index, } 3229 ); 3230 }); 3231 } 3232 3233 function processTypeReferenceDirectives(file: SourceFile) { 3234 const typeDirectives = file.typeReferenceDirectives; 3235 if (!typeDirectives) { 3236 return; 3237 } 3238 3239 const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file); 3240 for (let index = 0; index < typeDirectives.length; index++) { 3241 const ref = file.typeReferenceDirectives[index]; 3242 const resolvedTypeReferenceDirective = resolutions[index]; 3243 // store resolved type directive on the file 3244 const fileName = toFileNameLowerCase(ref.fileName); 3245 setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); 3246 const mode = ref.resolutionMode || file.impliedNodeFormat; 3247 if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) { 3248 programDiagnostics.add(createDiagnosticForRange(file, ref, Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext)); 3249 } 3250 processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }); 3251 } 3252 } 3253 3254 function processTypeReferenceDirective( 3255 typeReferenceDirective: string, 3256 mode: SourceFile["impliedNodeFormat"] | undefined, 3257 resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, 3258 reason: FileIncludeReason 3259 ): void { 3260 tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolvedTypeReferenceDirective, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); 3261 processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolvedTypeReferenceDirective, reason); 3262 tracing?.pop(); 3263 } 3264 3265 function processTypeReferenceDirectiveWorker( 3266 typeReferenceDirective: string, 3267 mode: SourceFile["impliedNodeFormat"] | undefined, 3268 resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, 3269 reason: FileIncludeReason 3270 ): void { 3271 3272 // If we already found this library as a primary reference - nothing to do 3273 const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode); 3274 if (previousResolution && previousResolution.primary) { 3275 return; 3276 } 3277 let saveResolution = true; 3278 if (resolvedTypeReferenceDirective) { 3279 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++; 3280 3281 if (resolvedTypeReferenceDirective.primary) { 3282 // resolved from the primary path 3283 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217 3284 } 3285 else { 3286 // If we already resolved to this file, it must have been a secondary reference. Check file contents 3287 // for sameness and possibly issue an error 3288 if (previousResolution) { 3289 // Don't bother reading the file again if it's the same file. 3290 if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) { 3291 const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!); 3292 const existingFile = getSourceFile(previousResolution.resolvedFileName!)!; 3293 if (otherFileText !== existingFile.text) { 3294 addFilePreprocessingFileExplainingDiagnostic( 3295 existingFile, 3296 reason, 3297 Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, 3298 [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName] 3299 ); 3300 } 3301 } 3302 // don't overwrite previous resolution result 3303 saveResolution = false; 3304 } 3305 else { 3306 // First resolution of this library 3307 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); 3308 } 3309 } 3310 3311 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; 3312 } 3313 else { 3314 addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]); 3315 } 3316 3317 if (saveResolution) { 3318 resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolvedTypeReferenceDirective); 3319 } 3320 } 3321 3322 function pathForLibFile(libFileName: string): string { 3323 // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and 3324 // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable 3325 // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown 3326 const components = libFileName.split("."); 3327 let path = components[1]; 3328 let i = 2; 3329 while (components[i] && components[i] !== "d") { 3330 path += (i === 2 ? "/" : "-") + components[i]; 3331 i++; 3332 } 3333 const modulePathType = getModuleByPMType(options.packageManagerType); 3334 const resolveFrom = combinePaths(currentDirectory, `__lib_${modulePathType}_lookup_${libFileName}__.ts`); 3335 const localOverrideModuleResult = resolveModuleName("@typescript/lib-" + path, resolveFrom, { moduleResolution: ModuleResolutionKind.NodeJs }, host, moduleResolutionCache); 3336 if (localOverrideModuleResult?.resolvedModule) { 3337 return localOverrideModuleResult.resolvedModule.resolvedFileName; 3338 } 3339 return combinePaths(defaultLibraryPath, libFileName); 3340 } 3341 3342 function processLibReferenceDirectives(file: SourceFile) { 3343 forEach(file.libReferenceDirectives, (libReference, index) => { 3344 const libName = toFileNameLowerCase(libReference.fileName); 3345 const libFileName = libMap.get(libName); 3346 if (libFileName) { 3347 // we ignore any 'no-default-lib' reference set on this file. 3348 processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }); 3349 } 3350 else { 3351 const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts"); 3352 const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity); 3353 const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; 3354 (fileProcessingDiagnostics ||= []).push({ 3355 kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic, 3356 reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, 3357 diagnostic, 3358 args: [libName, suggestion] 3359 }); 3360 } 3361 }); 3362 } 3363 3364 function getCanonicalFileName(fileName: string): string { 3365 return host.getCanonicalFileName(fileName); 3366 } 3367 3368 function processImportedModules(file: SourceFile) { 3369 collectExternalModuleReferences(file); 3370 if (file.imports.length || file.moduleAugmentations.length) { 3371 // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. 3372 const moduleNames = getModuleNames(file); 3373 const resolutions = resolveModuleNamesReusingOldState(moduleNames, file); 3374 Debug.assert(resolutions.length === moduleNames.length); 3375 const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options; 3376 for (let index = 0; index < moduleNames.length; index++) { 3377 const resolution = resolutions[index]; 3378 setResolvedModule(file, moduleNames[index], resolution, getModeForResolutionAtIndex(file, index)); 3379 3380 if (!resolution) { 3381 continue; 3382 } 3383 3384 const isFromNodeModulesSearch = resolution.isExternalLibraryImport; 3385 const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension); 3386 const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile; 3387 const resolvedFileName = resolution.resolvedFileName; 3388 3389 if (isFromNodeModulesSearch) { 3390 currentNodeModulesDepth++; 3391 } 3392 3393 // add file to program only if: 3394 // - resolution was successful 3395 // - noResolve is falsy 3396 // - module name comes from the list of imports 3397 // - it's not a top level JavaScript module that exceeded the search max 3398 const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth; 3399 // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs') 3400 // This may still end up being an untyped module -- the file won't be included but imports will be allowed. 3401 const shouldAddFile = resolvedFileName 3402 && !getResolutionDiagnostic(optionsForFile, resolution) 3403 && !optionsForFile.noResolve 3404 && index < file.imports.length 3405 && !elideImport 3406 && !(isJsFile && !getAllowJSCompilerOption(optionsForFile)) 3407 && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); 3408 3409 if (elideImport) { 3410 modulesWithElidedImports.set(file.path, true); 3411 } 3412 else if (shouldAddFile) { 3413 findSourceFile( 3414 resolvedFileName, 3415 /*isDefaultLib*/ false, 3416 /*ignoreNoDefaultLib*/ false, 3417 { kind: FileIncludeKind.Import, file: file.path, index, }, 3418 resolution.packageId, 3419 ); 3420 } 3421 3422 if (isFromNodeModulesSearch) { 3423 currentNodeModulesDepth--; 3424 } 3425 } 3426 } 3427 else { 3428 // no imports - drop cached module resolutions 3429 file.resolvedModules = undefined; 3430 } 3431 } 3432 3433 function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { 3434 let allFilesBelongToPath = true; 3435 const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); 3436 for (const sourceFile of sourceFiles) { 3437 if (!sourceFile.isDeclarationFile) { 3438 const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory)); 3439 if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) { 3440 addProgramDiagnosticExplainingFile( 3441 sourceFile, 3442 Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, 3443 [sourceFile.fileName, rootDirectory] 3444 ); 3445 allFilesBelongToPath = false; 3446 } 3447 } 3448 } 3449 3450 return allFilesBelongToPath; 3451 } 3452 3453 function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined { 3454 if (!projectReferenceRedirects) { 3455 projectReferenceRedirects = new Map(); 3456 } 3457 3458 // The actual filename (i.e. add "/tsconfig.json" if necessary) 3459 const refPath = resolveProjectReferencePath(ref); 3460 const sourceFilePath = toPath(refPath); 3461 const fromCache = projectReferenceRedirects.get(sourceFilePath); 3462 if (fromCache !== undefined) { 3463 return fromCache || undefined; 3464 } 3465 3466 let commandLine: ParsedCommandLine | undefined; 3467 let sourceFile: JsonSourceFile | undefined; 3468 if (host.getParsedCommandLine) { 3469 commandLine = host.getParsedCommandLine(refPath); 3470 if (!commandLine) { 3471 addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined); 3472 projectReferenceRedirects.set(sourceFilePath, false); 3473 return undefined; 3474 } 3475 sourceFile = Debug.checkDefined(commandLine.options.configFile); 3476 Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath); 3477 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined); 3478 } 3479 else { 3480 // An absolute path pointing to the containing directory of the config file 3481 const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); 3482 sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined; 3483 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined); 3484 if (sourceFile === undefined) { 3485 projectReferenceRedirects.set(sourceFilePath, false); 3486 return undefined; 3487 } 3488 commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath); 3489 } 3490 sourceFile.fileName = refPath; 3491 sourceFile.path = sourceFilePath; 3492 sourceFile.resolvedPath = sourceFilePath; 3493 sourceFile.originalFileName = refPath; 3494 3495 const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile }; 3496 projectReferenceRedirects.set(sourceFilePath, resolvedRef); 3497 if (commandLine.projectReferences) { 3498 resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile); 3499 } 3500 return resolvedRef; 3501 } 3502 3503 function verifyCompilerOptions() { 3504 if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) { 3505 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks"); 3506 } 3507 if (options.exactOptionalPropertyTypes && !getStrictOptionValue(options, "strictNullChecks")) { 3508 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "exactOptionalPropertyTypes", "strictNullChecks"); 3509 } 3510 3511 if (options.isolatedModules) { 3512 if (options.out) { 3513 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules"); 3514 } 3515 3516 if (options.outFile) { 3517 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules"); 3518 } 3519 } 3520 3521 if (options.inlineSourceMap) { 3522 if (options.sourceMap) { 3523 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap"); 3524 } 3525 if (options.mapRoot) { 3526 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"); 3527 } 3528 } 3529 3530 if (options.composite) { 3531 if (options.declaration === false) { 3532 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration"); 3533 } 3534 if (options.incremental === false) { 3535 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration"); 3536 } 3537 } 3538 3539 const outputFile = outFile(options); 3540 if (options.tsBuildInfoFile) { 3541 if (!isIncrementalCompilation(options)) { 3542 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite"); 3543 } 3544 } 3545 else if (options.incremental && !outputFile && !options.configFilePath) { 3546 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified)); 3547 } 3548 3549 verifyProjectReferences(); 3550 3551 // List of collected files is complete; validate exhautiveness if this is a project with a file list 3552 if (options.composite) { 3553 const rootPaths = new Set(rootNames.map(toPath)); 3554 for (const file of files) { 3555 // Ignore file that is not emitted 3556 if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) { 3557 addProgramDiagnosticExplainingFile( 3558 file, 3559 Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, 3560 [file.fileName, options.configFilePath || ""] 3561 ); 3562 } 3563 } 3564 } 3565 3566 if (options.paths) { 3567 for (const key in options.paths) { 3568 if (!hasProperty(options.paths, key)) { 3569 continue; 3570 } 3571 if (!hasZeroOrOneAsteriskCharacter(key)) { 3572 createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key); 3573 } 3574 if (isArray(options.paths[key])) { 3575 const len = options.paths[key].length; 3576 if (len === 0) { 3577 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key); 3578 } 3579 for (let i = 0; i < len; i++) { 3580 const subst = options.paths[key][i]; 3581 const typeOfSubst = typeof subst; 3582 if (typeOfSubst === "string") { 3583 if (!hasZeroOrOneAsteriskCharacter(subst)) { 3584 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key); 3585 } 3586 if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) { 3587 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash); 3588 } 3589 } 3590 else { 3591 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst); 3592 } 3593 } 3594 } 3595 else { 3596 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key); 3597 } 3598 } 3599 } 3600 3601 if (!options.sourceMap && !options.inlineSourceMap) { 3602 if (options.inlineSources) { 3603 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources"); 3604 } 3605 if (options.sourceRoot) { 3606 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot"); 3607 } 3608 } 3609 3610 if (options.out && options.outFile) { 3611 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile"); 3612 } 3613 3614 if (options.mapRoot && !(options.sourceMap || options.declarationMap)) { 3615 // Error to specify --mapRoot without --sourcemap 3616 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap"); 3617 } 3618 3619 if (options.declarationDir) { 3620 if (!getEmitDeclarations(options)) { 3621 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite"); 3622 } 3623 if (outputFile) { 3624 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile"); 3625 } 3626 } 3627 3628 if (options.declarationMap && !getEmitDeclarations(options)) { 3629 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite"); 3630 } 3631 3632 if (options.lib && options.noLib) { 3633 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib"); 3634 } 3635 3636 if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) { 3637 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict"); 3638 } 3639 3640 const languageVersion = getEmitScriptTarget(options); 3641 3642 const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile); 3643 if (options.isolatedModules) { 3644 if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) { 3645 createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target"); 3646 } 3647 3648 if (options.preserveConstEnums === false) { 3649 createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules"); 3650 } 3651 3652 for (const file of files) { 3653 if (!isExternalModule(file) && !isSourceFileJS(file) && !file.isDeclarationFile && file.scriptKind !== ScriptKind.JSON) { 3654 const span = getErrorSpanForNode(file, file); 3655 programDiagnostics.add(createFileDiagnostic(file, span.start, span.length, 3656 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))); 3657 } 3658 } 3659 } 3660 else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) { 3661 // We cannot use createDiagnosticFromNode because nodes do not have parents yet 3662 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, typeof firstNonAmbientExternalModuleSourceFile.externalModuleIndicator === "boolean" ? firstNonAmbientExternalModuleSourceFile : firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!); 3663 programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none)); 3664 } 3665 3666 // Cannot specify module gen that isn't amd or system with --out 3667 if (outputFile && !options.emitDeclarationOnly) { 3668 if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) { 3669 createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module"); 3670 } 3671 else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) { 3672 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, typeof firstNonAmbientExternalModuleSourceFile.externalModuleIndicator === "boolean" ? firstNonAmbientExternalModuleSourceFile : firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!); 3673 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")); 3674 } 3675 } 3676 3677 if (options.resolveJsonModule) { 3678 if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs && 3679 getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && 3680 getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) { 3681 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule"); 3682 } 3683 // Any emit other than common js, amd, es2015 or esnext is error 3684 else if (!hasJsonModuleEmitEnabled(options)) { 3685 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module"); 3686 } 3687 } 3688 3689 // there has to be common source directory if user specified --outdir || --rootDir || --sourceRoot 3690 // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted 3691 if (options.outDir || // there is --outDir specified 3692 options.rootDir || // there is --rootDir specified 3693 options.sourceRoot || // there is --sourceRoot specified 3694 options.mapRoot) { // there is --mapRoot specified 3695 3696 // Precalculate and cache the common source directory 3697 const dir = getCommonSourceDirectory(); 3698 3699 // 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 3700 if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) { 3701 createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir"); 3702 } 3703 } 3704 3705 if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) { 3706 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields"); 3707 } 3708 3709 if (options.checkJs && !getAllowJSCompilerOption(options)) { 3710 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs")); 3711 } 3712 3713 if (options.emitDeclarationOnly) { 3714 if (!getEmitDeclarations(options)) { 3715 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite"); 3716 } 3717 3718 if (options.noEmit) { 3719 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit"); 3720 } 3721 } 3722 3723 if (options.emitDecoratorMetadata && 3724 !options.experimentalDecorators) { 3725 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"); 3726 } 3727 3728 if (options.jsxFactory) { 3729 if (options.reactNamespace) { 3730 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"); 3731 } 3732 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3733 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx)); 3734 } 3735 if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) { 3736 createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory); 3737 } 3738 } 3739 else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) { 3740 createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace); 3741 } 3742 3743 if (options.jsxFragmentFactory) { 3744 if (!options.jsxFactory) { 3745 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory"); 3746 } 3747 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3748 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx)); 3749 } 3750 if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) { 3751 createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory); 3752 } 3753 } 3754 3755 if (options.reactNamespace) { 3756 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3757 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx)); 3758 } 3759 } 3760 3761 if (options.jsxImportSource) { 3762 if (options.jsx === JsxEmit.React) { 3763 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx)); 3764 } 3765 } 3766 3767 if (options.preserveValueImports && getEmitModuleKind(options) < ModuleKind.ES2015) { 3768 createOptionValueDiagnostic("importsNotUsedAsValues", Diagnostics.Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later); 3769 } 3770 3771 // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files 3772 if (!options.noEmit && !options.suppressOutputPathCheck) { 3773 const emitHost = getEmitHost(); 3774 const emitFilesSeen = new Set<string>(); 3775 forEachEmittedFile(emitHost, (emitFileNames) => { 3776 if (!options.emitDeclarationOnly) { 3777 verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen); 3778 } 3779 verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen); 3780 }); 3781 } 3782 3783 // Verify that all the emit files are unique and don't overwrite input files 3784 function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) { 3785 if (emitFileName) { 3786 const emitFilePath = toPath(emitFileName); 3787 // Report error if the output overwrites input file 3788 if (filesByName.has(emitFilePath)) { 3789 let chain: DiagnosticMessageChain | undefined; 3790 if (!options.configFilePath) { 3791 // The program is from either an inferred project or an external project 3792 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); 3793 } 3794 chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName); 3795 blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain)); 3796 } 3797 3798 const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath; 3799 // Report error if multiple files write into same file 3800 if (emitFilesSeen.has(emitFileKey)) { 3801 // Already seen the same emit file - report error 3802 blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName)); 3803 } 3804 else { 3805 emitFilesSeen.add(emitFileKey); 3806 } 3807 } 3808 } 3809 } 3810 3811 function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic { 3812 let fileIncludeReasons: DiagnosticMessageChain[] | undefined; 3813 let relatedInfo: Diagnostic[] | undefined; 3814 let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined; 3815 if (file) fileReasons.get(file.path)?.forEach(processReason); 3816 if (fileProcessingReason) processReason(fileProcessingReason); 3817 // If we have location and there is only one reason file is in which is the location, dont add details for file include 3818 if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined; 3819 const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason); 3820 const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon); 3821 const redirectInfo = file && explainIfFileIsRedirectAndImpliedFormat(file); 3822 const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray); 3823 return location && isReferenceFileLocation(location) ? 3824 createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) : 3825 createCompilerDiagnosticFromMessageChain(chain, relatedInfo); 3826 3827 function processReason(reason: FileIncludeReason) { 3828 (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason)); 3829 if (!locationReason && isReferencedFile(reason)) { 3830 // Report error at first reference file or file currently in processing and dont report in related information 3831 locationReason = reason; 3832 } 3833 else if (locationReason !== reason) { 3834 relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason)); 3835 } 3836 // Remove fileProcessingReason if its already included in fileReasons of the program 3837 if (reason === fileProcessingReason) fileProcessingReason = undefined; 3838 } 3839 } 3840 3841 function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { 3842 (fileProcessingDiagnostics ||= []).push({ 3843 kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic, 3844 file: file && file.path, 3845 fileProcessingReason, 3846 diagnostic, 3847 args 3848 }); 3849 } 3850 3851 function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { 3852 programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args)); 3853 } 3854 3855 function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined { 3856 if (isReferencedFile(reason)) { 3857 const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason); 3858 let message: DiagnosticMessage; 3859 switch (reason.kind) { 3860 case FileIncludeKind.Import: 3861 message = Diagnostics.File_is_included_via_import_here; 3862 break; 3863 case FileIncludeKind.ReferenceFile: 3864 message = Diagnostics.File_is_included_via_reference_here; 3865 break; 3866 case FileIncludeKind.TypeReferenceDirective: 3867 message = Diagnostics.File_is_included_via_type_library_reference_here; 3868 break; 3869 case FileIncludeKind.LibReferenceDirective: 3870 message = Diagnostics.File_is_included_via_library_reference_here; 3871 break; 3872 default: 3873 Debug.assertNever(reason); 3874 } 3875 return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic( 3876 referenceLocation.file, 3877 referenceLocation.pos, 3878 referenceLocation.end - referenceLocation.pos, 3879 message, 3880 ) : undefined; 3881 } 3882 3883 if (!options.configFile) return undefined; 3884 let configFileNode: Node | undefined; 3885 let message: DiagnosticMessage; 3886 switch (reason.kind) { 3887 case FileIncludeKind.RootFile: 3888 if (!options.configFile.configFileSpecs) return undefined; 3889 const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory); 3890 const matchedByFiles = getMatchedFileSpec(program, fileName); 3891 if (matchedByFiles) { 3892 configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles); 3893 message = Diagnostics.File_is_matched_by_files_list_specified_here; 3894 break; 3895 } 3896 const matchedByInclude = getMatchedIncludeSpec(program, fileName); 3897 // Could be additional files specified as roots 3898 if (!matchedByInclude || !isString(matchedByInclude)) return undefined; 3899 configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude); 3900 message = Diagnostics.File_is_matched_by_include_pattern_specified_here; 3901 break; 3902 case FileIncludeKind.SourceFromProjectReference: 3903 case FileIncludeKind.OutputFromProjectReference: 3904 const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]); 3905 const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => 3906 resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined 3907 ); 3908 if (!referenceInfo) return undefined; 3909 const { sourceFile, index } = referenceInfo; 3910 const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"), 3911 property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined); 3912 return referencesSyntax && referencesSyntax.elements.length > index ? 3913 createDiagnosticForNodeInSourceFile( 3914 sourceFile, 3915 referencesSyntax.elements[index], 3916 reason.kind === FileIncludeKind.OutputFromProjectReference ? 3917 Diagnostics.File_is_output_from_referenced_project_specified_here : 3918 Diagnostics.File_is_source_from_referenced_project_specified_here, 3919 ) : 3920 undefined; 3921 case FileIncludeKind.AutomaticTypeDirectiveFile: 3922 if (!options.types) return undefined; 3923 configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference); 3924 message = Diagnostics.File_is_entry_point_of_type_library_specified_here; 3925 break; 3926 case FileIncludeKind.LibFile: 3927 if (reason.index !== undefined) { 3928 configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]); 3929 message = Diagnostics.File_is_library_specified_here; 3930 break; 3931 } 3932 const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined); 3933 configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined; 3934 message = Diagnostics.File_is_default_library_for_target_specified_here; 3935 break; 3936 default: 3937 Debug.assertNever(reason); 3938 } 3939 return configFileNode && createDiagnosticForNodeInSourceFile( 3940 options.configFile, 3941 configFileNode, 3942 message, 3943 ); 3944 } 3945 3946 function verifyProjectReferences() { 3947 const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined; 3948 forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => { 3949 const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; 3950 const parentFile = parent && parent.sourceFile as JsonSourceFile; 3951 if (!resolvedRef) { 3952 createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path); 3953 return; 3954 } 3955 const options = resolvedRef.commandLine.options; 3956 if (!options.composite || options.noEmit) { 3957 // ok to not have composite if the current program is container only 3958 const inputs = parent ? parent.commandLine.fileNames : rootNames; 3959 if (inputs.length) { 3960 if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); 3961 if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path); 3962 } 3963 } 3964 if (ref.prepend) { 3965 const out = outFile(options); 3966 if (out) { 3967 if (!host.fileExists(out)) { 3968 createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path); 3969 } 3970 } 3971 else { 3972 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path); 3973 } 3974 } 3975 if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) { 3976 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path); 3977 hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true); 3978 } 3979 }); 3980 } 3981 3982 function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) { 3983 let needCompilerDiagnostic = true; 3984 const pathsSyntax = getOptionPathsSyntax(); 3985 for (const pathProp of pathsSyntax) { 3986 if (isObjectLiteralExpression(pathProp.initializer)) { 3987 for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) { 3988 const initializer = keyProps.initializer; 3989 if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) { 3990 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2)); 3991 needCompilerDiagnostic = false; 3992 } 3993 } 3994 } 3995 } 3996 3997 if (needCompilerDiagnostic) { 3998 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2)); 3999 } 4000 } 4001 4002 function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) { 4003 let needCompilerDiagnostic = true; 4004 const pathsSyntax = getOptionPathsSyntax(); 4005 for (const pathProp of pathsSyntax) { 4006 if (isObjectLiteralExpression(pathProp.initializer) && 4007 createOptionDiagnosticInObjectLiteralSyntax( 4008 pathProp.initializer, onKey, key, /*key2*/ undefined, 4009 message, arg0)) { 4010 needCompilerDiagnostic = false; 4011 } 4012 } 4013 if (needCompilerDiagnostic) { 4014 programDiagnostics.add(createCompilerDiagnostic(message, arg0)); 4015 } 4016 } 4017 4018 function getOptionsSyntaxByName(name: string) { 4019 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4020 return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); 4021 } 4022 4023 function getOptionPathsSyntax() { 4024 return getOptionsSyntaxByName("paths") || emptyArray; 4025 } 4026 4027 function getOptionsSyntaxByValue(name: string, value: string) { 4028 const syntaxByName = getOptionsSyntaxByName(name); 4029 return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined); 4030 } 4031 4032 function getOptionsSyntaxByArrayElementValue(name: string, value: string) { 4033 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4034 return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value); 4035 } 4036 4037 function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) { 4038 createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3); 4039 } 4040 4041 function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0?: string, arg1?: string) { 4042 createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0, arg1); 4043 } 4044 4045 function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) { 4046 const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"), 4047 property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined); 4048 if (referencesSyntax && referencesSyntax.elements.length > index) { 4049 programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1)); 4050 } 4051 else { 4052 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1)); 4053 } 4054 } 4055 4056 function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) { 4057 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4058 const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax || 4059 !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2); 4060 4061 if (needCompilerDiagnostic) { 4062 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2)); 4063 } 4064 } 4065 4066 function getCompilerOptionsObjectLiteralSyntax() { 4067 if (_compilerOptionsObjectLiteralSyntax === undefined) { 4068 _compilerOptionsObjectLiteralSyntax = false; 4069 const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); 4070 if (jsonObjectLiteral) { 4071 for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) { 4072 if (isObjectLiteralExpression(prop.initializer)) { 4073 _compilerOptionsObjectLiteralSyntax = prop.initializer; 4074 break; 4075 } 4076 } 4077 } 4078 } 4079 return _compilerOptionsObjectLiteralSyntax || undefined; 4080 } 4081 4082 function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean { 4083 const props = getPropertyAssignment(objectLiteral, key1, key2); 4084 for (const prop of props) { 4085 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2)); 4086 } 4087 return !!props.length; 4088 } 4089 4090 function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) { 4091 hasEmitBlockingDiagnostics.set(toPath(emitFileName), true); 4092 programDiagnostics.add(diag); 4093 } 4094 4095 function isEmittedFile(file: string): boolean { 4096 if (options.noEmit) { 4097 return false; 4098 } 4099 4100 // If this is source file, its not emitted file 4101 const filePath = toPath(file); 4102 if (getSourceFileByPath(filePath)) { 4103 return false; 4104 } 4105 4106 // If options have --outFile or --out just check that 4107 const out = outFile(options); 4108 if (out) { 4109 return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts); 4110 } 4111 4112 // If declarationDir is specified, return if its a file in that directory 4113 if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) { 4114 return true; 4115 } 4116 4117 // If --outDir, check if file is in that directory 4118 if (options.outDir) { 4119 return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames()); 4120 } 4121 4122 if (fileExtensionIsOneOf(filePath, supportedJSExtensionsFlat) || isDeclarationFileName(filePath)) { 4123 // Otherwise just check if sourceFile with the name exists 4124 const filePathWithoutExtension = removeFileExtension(filePath); 4125 return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) || 4126 !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path); 4127 } 4128 return false; 4129 } 4130 4131 function isSameFile(file1: string, file2: string) { 4132 return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo; 4133 } 4134 4135 function getSymlinkCache(): SymlinkCache { 4136 if (host.getSymlinkCache) { 4137 return host.getSymlinkCache(); 4138 } 4139 if (!symlinks) { 4140 symlinks = createSymlinkCache(currentDirectory, getCanonicalFileName, isOhpm(options.packageManagerType)); 4141 } 4142 if (files && resolvedTypeReferenceDirectives && !symlinks.hasProcessedResolutions()) { 4143 symlinks.setSymlinksFromResolutions(files, resolvedTypeReferenceDirectives); 4144 } 4145 return symlinks; 4146 } 4147 } 4148 4149 interface HostForUseSourceOfProjectReferenceRedirect { 4150 compilerHost: CompilerHost; 4151 getSymlinkCache: () => SymlinkCache; 4152 useSourceOfProjectReferenceRedirect: boolean; 4153 toPath(fileName: string): Path; 4154 getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined; 4155 getSourceOfProjectReferenceRedirect(path: Path): SourceOfProjectReferenceRedirect | undefined; 4156 forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; 4157 options?: CompilerOptions; 4158 } 4159 4160 function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) { 4161 let setOfDeclarationDirectories: Set<Path> | undefined; 4162 const originalFileExists = host.compilerHost.fileExists; 4163 const originalDirectoryExists = host.compilerHost.directoryExists; 4164 const originalGetDirectories = host.compilerHost.getDirectories; 4165 const originalRealpath = host.compilerHost.realpath; 4166 4167 if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists }; 4168 4169 host.compilerHost.fileExists = fileExists; 4170 4171 let directoryExists; 4172 if (originalDirectoryExists) { 4173 // This implementation of directoryExists checks if the directory being requested is 4174 // directory of .d.ts file for the referenced Project. 4175 // If it is it returns true irrespective of whether that directory exists on host 4176 directoryExists = host.compilerHost.directoryExists = path => { 4177 if (originalDirectoryExists.call(host.compilerHost, path)) { 4178 handleDirectoryCouldBeSymlink(path); 4179 return true; 4180 } 4181 4182 if (!host.getResolvedProjectReferences()) return false; 4183 4184 if (!setOfDeclarationDirectories) { 4185 setOfDeclarationDirectories = new Set(); 4186 host.forEachResolvedProjectReference(ref => { 4187 const out = outFile(ref.commandLine.options); 4188 if (out) { 4189 setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out))); 4190 } 4191 else { 4192 // Set declaration's in different locations only, if they are next to source the directory present doesnt change 4193 const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir; 4194 if (declarationDir) { 4195 setOfDeclarationDirectories!.add(host.toPath(declarationDir)); 4196 } 4197 } 4198 }); 4199 } 4200 4201 return fileOrDirectoryExistsUsingSource(path, /*isFile*/ false); 4202 }; 4203 } 4204 4205 if (originalGetDirectories) { 4206 // Call getDirectories only if directory actually present on the host 4207 // This is needed to ensure that we arent getting directories that we fake about presence for 4208 host.compilerHost.getDirectories = path => 4209 !host.getResolvedProjectReferences() || (originalDirectoryExists && originalDirectoryExists.call(host.compilerHost, path)) ? 4210 originalGetDirectories.call(host.compilerHost, path) : 4211 []; 4212 } 4213 4214 // This is something we keep for life time of the host 4215 if (originalRealpath) { 4216 host.compilerHost.realpath = s => 4217 host.getSymlinkCache().getSymlinkedFiles()?.get(host.toPath(s)) || 4218 originalRealpath.call(host.compilerHost, s); 4219 } 4220 4221 return { onProgramCreateComplete, fileExists, directoryExists }; 4222 4223 function onProgramCreateComplete() { 4224 host.compilerHost.fileExists = originalFileExists; 4225 host.compilerHost.directoryExists = originalDirectoryExists; 4226 host.compilerHost.getDirectories = originalGetDirectories; 4227 // DO not revert realpath as it could be used later 4228 } 4229 4230 // This implementation of fileExists checks if the file being requested is 4231 // .d.ts file for the referenced Project. 4232 // If it is it returns true irrespective of whether that file exists on host 4233 function fileExists(file: string) { 4234 if (originalFileExists.call(host.compilerHost, file)) return true; 4235 if (!host.getResolvedProjectReferences()) return false; 4236 if (!isDeclarationFileName(file)) return false; 4237 4238 // Project references go to source file instead of .d.ts file 4239 return fileOrDirectoryExistsUsingSource(file, /*isFile*/ true); 4240 } 4241 4242 function fileExistsIfProjectReferenceDts(file: string) { 4243 const source = host.getSourceOfProjectReferenceRedirect(host.toPath(file)); 4244 return source !== undefined ? 4245 isString(source) ? originalFileExists.call(host.compilerHost, source) as boolean : true : 4246 undefined; 4247 } 4248 4249 function directoryExistsIfProjectReferenceDeclDir(dir: string) { 4250 const dirPath = host.toPath(dir); 4251 const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`; 4252 return forEachKey( 4253 setOfDeclarationDirectories!, 4254 declDirPath => dirPath === declDirPath || 4255 // Any parent directory of declaration dir 4256 startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) || 4257 // Any directory inside declaration dir 4258 startsWith(dirPath, `${declDirPath}/`) 4259 ); 4260 } 4261 4262 function handleDirectoryCouldBeSymlink(directory: string) { 4263 if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return; 4264 4265 // Because we already watch node_modules or oh_modules, handle symlinks in there 4266 const modulesPathPart = getModulePathPartByPMType(host.options?.packageManagerType); 4267 if (!originalRealpath || !stringContains(directory, modulesPathPart)) return; 4268 const symlinkCache = host.getSymlinkCache(); 4269 const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory)); 4270 if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return; 4271 4272 const real = normalizePath(originalRealpath.call(host.compilerHost, directory)); 4273 let realPath: Path; 4274 if (real === directory || 4275 (realPath = ensureTrailingDirectorySeparator(host.toPath(real))) === directoryPath) { 4276 // not symlinked 4277 symlinkCache.setSymlinkedDirectory(directoryPath, false); 4278 return; 4279 } 4280 4281 symlinkCache.setSymlinkedDirectory(directory, { 4282 real: ensureTrailingDirectorySeparator(real), 4283 realPath 4284 }); 4285 } 4286 4287 function fileOrDirectoryExistsUsingSource(fileOrDirectory: string, isFile: boolean): boolean { 4288 const fileOrDirectoryExistsUsingSource = isFile ? 4289 (file: string) => fileExistsIfProjectReferenceDts(file) : 4290 (dir: string) => directoryExistsIfProjectReferenceDeclDir(dir); 4291 // Check current directory or file 4292 const result = fileOrDirectoryExistsUsingSource(fileOrDirectory); 4293 if (result !== undefined) return result; 4294 4295 const symlinkCache = host.getSymlinkCache(); 4296 const symlinkedDirectories = symlinkCache.getSymlinkedDirectories(); 4297 if (!symlinkedDirectories) return false; 4298 const fileOrDirectoryPath = host.toPath(fileOrDirectory); 4299 const modulesPathPart = getModulePathPartByPMType(host.options?.packageManagerType); 4300 if (!stringContains(fileOrDirectoryPath, modulesPathPart)) return false; 4301 if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true; 4302 4303 // If it contains node_modules or oh_modules check if its one of the symlinked path we know of 4304 return firstDefinedIterator( 4305 symlinkedDirectories.entries(), 4306 ([directoryPath, symlinkedDirectory]) => { 4307 if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined; 4308 const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath)); 4309 if (isFile && result) { 4310 // Store the real path for the file' 4311 const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory()); 4312 symlinkCache.setSymlinkedFile( 4313 fileOrDirectoryPath, 4314 `${symlinkedDirectory.real}${absolutePath.replace(new RegExp(directoryPath, "i"), "")}` 4315 ); 4316 } 4317 return result; 4318 } 4319 ) || false; 4320 } 4321 } 4322 4323 /*@internal*/ 4324 export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true }; 4325 4326 /*@internal*/ 4327 export function handleNoEmitOptions<T extends BuilderProgram>( 4328 program: Program | T, 4329 sourceFile: SourceFile | undefined, 4330 writeFile: WriteFileCallback | undefined, 4331 cancellationToken: CancellationToken | undefined 4332 ): EmitResult | undefined { 4333 const options = program.getCompilerOptions(); 4334 if (options.noEmit) { 4335 // Cache the semantic diagnostics 4336 program.getSemanticDiagnostics(sourceFile, cancellationToken); 4337 return sourceFile || outFile(options) ? 4338 emitSkippedWithNoDiagnostics : 4339 program.emitBuildInfo(writeFile, cancellationToken); 4340 } 4341 4342 // If the noEmitOnError flag is set, then check if we have any errors so far. If so, 4343 // immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we 4344 // get any preEmit diagnostics, not just the ones 4345 if (!options.noEmitOnError) return undefined; 4346 let diagnostics: readonly Diagnostic[] = [ 4347 ...program.getOptionsDiagnostics(cancellationToken), 4348 ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), 4349 ...program.getGlobalDiagnostics(cancellationToken), 4350 ...program.getSemanticDiagnostics(sourceFile, cancellationToken) 4351 ]; 4352 4353 if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) { 4354 diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken); 4355 } 4356 4357 if (!diagnostics.length) return undefined; 4358 let emittedFiles: string[] | undefined; 4359 if (!sourceFile && !outFile(options)) { 4360 const emitResult = program.emitBuildInfo(writeFile, cancellationToken); 4361 if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics]; 4362 emittedFiles = emitResult.emittedFiles; 4363 } 4364 return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true }; 4365 } 4366 4367 /*@internal*/ 4368 export function filterSemanticDiagnostics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] { 4369 return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]); 4370 } 4371 4372 /*@internal*/ 4373 interface CompilerHostLike { 4374 useCaseSensitiveFileNames(): boolean; 4375 getCurrentDirectory(): string; 4376 fileExists(fileName: string): boolean; 4377 readFile(fileName: string): string | undefined; 4378 readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; 4379 trace?(s: string): void; 4380 onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter; 4381 } 4382 4383 /* @internal */ 4384 export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost { 4385 return { 4386 fileExists: f => directoryStructureHost.fileExists(f), 4387 readDirectory(root, extensions, excludes, includes, depth) { 4388 Debug.assertIsDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'"); 4389 return directoryStructureHost.readDirectory(root, extensions, excludes, includes, depth); 4390 }, 4391 readFile: f => directoryStructureHost.readFile(f), 4392 useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), 4393 getCurrentDirectory: () => host.getCurrentDirectory(), 4394 onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined, 4395 trace: host.trace ? (s) => host.trace!(s) : undefined 4396 }; 4397 } 4398 4399 // For backward compatibility 4400 /** @deprecated */ export interface ResolveProjectReferencePathHost { 4401 fileExists(fileName: string): boolean; 4402 } 4403 4404 /* @internal */ 4405 export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) { 4406 if (!projectReferences) return emptyArray; 4407 let nodes: InputFiles[] | undefined; 4408 for (let i = 0; i < projectReferences.length; i++) { 4409 const ref = projectReferences[i]; 4410 const resolvedRefOpts = getCommandLine(ref, i); 4411 if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { 4412 const out = outFile(resolvedRefOpts.options); 4413 // Upstream project didn't have outFile set -- skip (error will have been issued earlier) 4414 if (!out) continue; 4415 4416 const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true); 4417 const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath); 4418 (nodes || (nodes = [])).push(node); 4419 } 4420 } 4421 return nodes || emptyArray; 4422 } 4423 /** 4424 * Returns the target config filename of a project reference. 4425 * Note: The file might not exist. 4426 */ 4427 export function resolveProjectReferencePath(ref: ProjectReference): ResolvedConfigFileName; 4428 /** @deprecated */ export function resolveProjectReferencePath(host: ResolveProjectReferencePathHost, ref: ProjectReference): ResolvedConfigFileName; 4429 export function resolveProjectReferencePath(hostOrRef: ResolveProjectReferencePathHost | ProjectReference, ref?: ProjectReference): ResolvedConfigFileName { 4430 const passedInRef = ref ? ref : hostOrRef as ProjectReference; 4431 return resolveConfigFileProjectName(passedInRef.path); 4432 } 4433 4434 /* @internal */ 4435 /** 4436 * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. 4437 * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to. 4438 * This returns a diagnostic even if the module will be an untyped module. 4439 */ 4440 export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined { 4441 switch (extension) { 4442 case Extension.Ts: 4443 case Extension.Dts: 4444 case Extension.Ets: 4445 case Extension.Dets: 4446 // These are always allowed. 4447 return undefined; 4448 case Extension.Tsx: 4449 return needJsx(); 4450 case Extension.Jsx: 4451 return needJsx() || needAllowJs(); 4452 case Extension.Js: 4453 return needAllowJs(); 4454 case Extension.Json: 4455 return needResolveJsonModule(); 4456 } 4457 4458 function needJsx() { 4459 return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set; 4460 } 4461 function needAllowJs() { 4462 return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type; 4463 } 4464 function needResolveJsonModule() { 4465 return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used; 4466 } 4467 } 4468 4469 function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] { 4470 const res = imports.map(i => i.text); 4471 for (const aug of moduleAugmentations) { 4472 if (aug.kind === SyntaxKind.StringLiteral) { 4473 res.push(aug.text); 4474 } 4475 // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. 4476 } 4477 return res; 4478 } 4479 4480 /* @internal */ 4481 export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFileImportsList, index: number): StringLiteralLike { 4482 if (index < imports.length) return imports[index]; 4483 let augIndex = imports.length; 4484 for (const aug of moduleAugmentations) { 4485 if (aug.kind === SyntaxKind.StringLiteral) { 4486 if (index === augIndex) return aug; 4487 augIndex++; 4488 } 4489 // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. 4490 } 4491 Debug.fail("should never ask for module name at index higher than possible module name"); 4492 } 4493} 4494