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 ); 3051 3052 if (packageId) { 3053 const packageIdKey = packageIdToString(packageId); 3054 const fileFromPackageId = packageIdToSourceFile.get(packageIdKey); 3055 if (fileFromPackageId) { 3056 // Some other SourceFile already exists with this package name and version. 3057 // Instead of creating a duplicate, just redirect to the existing one. 3058 const dupFile = createRedirectSourceFile(fileFromPackageId, file!, fileName, path, toPath(fileName), originalFileName, sourceFileOptions); 3059 redirectTargetsMap.add(fileFromPackageId.path, fileName); 3060 addFileToFilesByName(dupFile, path, redirectedPath); 3061 addFileIncludeReason(dupFile, reason); 3062 sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); 3063 processingOtherFiles!.push(dupFile); 3064 return dupFile; 3065 } 3066 else if (file) { 3067 // This is the first source file to have this packageId. 3068 packageIdToSourceFile.set(packageIdKey, file); 3069 sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); 3070 } 3071 } 3072 addFileToFilesByName(file, path, redirectedPath); 3073 3074 if (file) { 3075 sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); 3076 file.fileName = fileName; // Ensure that source file has same name as what we were looking for 3077 file.path = path; 3078 file.resolvedPath = toPath(fileName); 3079 file.originalFileName = originalFileName; 3080 file.packageJsonLocations = sourceFileOptions.packageJsonLocations?.length ? sourceFileOptions.packageJsonLocations : undefined; 3081 file.packageJsonScope = sourceFileOptions.packageJsonScope; 3082 addFileIncludeReason(file, reason); 3083 3084 if (host.useCaseSensitiveFileNames()) { 3085 const pathLowerCase = toFileNameLowerCase(path); 3086 // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case 3087 const existingFile = filesByNameIgnoreCase!.get(pathLowerCase); 3088 if (existingFile) { 3089 reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason); 3090 } 3091 else { 3092 filesByNameIgnoreCase!.set(pathLowerCase, file); 3093 } 3094 } 3095 3096 skipDefaultLib = skipDefaultLib || (file.hasNoDefaultLib && !ignoreNoDefaultLib); 3097 3098 if (!options.noResolve) { 3099 processReferencedFiles(file, isDefaultLib); 3100 processTypeReferenceDirectives(file); 3101 } 3102 if (!options.noLib) { 3103 processLibReferenceDirectives(file); 3104 } 3105 3106 3107 // always process imported modules to record module name resolutions 3108 processImportedModules(file); 3109 3110 if (isDefaultLib) { 3111 processingDefaultLibFiles!.push(file); 3112 } 3113 else { 3114 processingOtherFiles!.push(file); 3115 } 3116 } 3117 return file; 3118 } 3119 3120 function addFileIncludeReason(file: SourceFile | undefined, reason: FileIncludeReason) { 3121 if (file) fileReasons.add(file.path, reason); 3122 } 3123 3124 function addFileToFilesByName(file: SourceFile | undefined, path: Path, redirectedPath: Path | undefined) { 3125 if (redirectedPath) { 3126 filesByName.set(redirectedPath, file); 3127 filesByName.set(path, file || false); 3128 } 3129 else { 3130 filesByName.set(path, file); 3131 } 3132 } 3133 3134 function getProjectReferenceRedirect(fileName: string): string | undefined { 3135 const referencedProject = getProjectReferenceRedirectProject(fileName); 3136 return referencedProject && getProjectReferenceOutputName(referencedProject, fileName); 3137 } 3138 3139 function getProjectReferenceRedirectProject(fileName: string) { 3140 // Ignore dts or any json files 3141 if (!resolvedProjectReferences || !resolvedProjectReferences.length || isDeclarationFileName(fileName) || fileExtensionIs(fileName, Extension.Json)) { 3142 return undefined; 3143 } 3144 3145 // If this file is produced by a referenced project, we need to rewrite it to 3146 // look in the output folder of the referenced project rather than the input 3147 return getResolvedProjectReferenceToRedirect(fileName); 3148 } 3149 3150 3151 function getProjectReferenceOutputName(referencedProject: ResolvedProjectReference, fileName: string) { 3152 const out = outFile(referencedProject.commandLine.options); 3153 return out ? 3154 changeExtension(out, Extension.Dts) : 3155 getOutputDeclarationFileName(fileName, referencedProject.commandLine, !host.useCaseSensitiveFileNames()); 3156 } 3157 3158 /** 3159 * Get the referenced project if the file is input file from that reference project 3160 */ 3161 function getResolvedProjectReferenceToRedirect(fileName: string) { 3162 if (mapFromFileToProjectReferenceRedirects === undefined) { 3163 mapFromFileToProjectReferenceRedirects = new Map(); 3164 forEachResolvedProjectReference(referencedProject => { 3165 // not input file from the referenced project, ignore 3166 if (toPath(options.configFilePath!) !== referencedProject.sourceFile.path) { 3167 referencedProject.commandLine.fileNames.forEach(f => 3168 mapFromFileToProjectReferenceRedirects!.set(toPath(f), referencedProject.sourceFile.path)); 3169 } 3170 }); 3171 } 3172 3173 const referencedProjectPath = mapFromFileToProjectReferenceRedirects.get(toPath(fileName)); 3174 return referencedProjectPath && getResolvedProjectReferenceByPath(referencedProjectPath); 3175 } 3176 3177 function forEachResolvedProjectReference<T>( 3178 cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined 3179 ): T | undefined { 3180 return ts.forEachResolvedProjectReference(resolvedProjectReferences, cb); 3181 } 3182 3183 function getSourceOfProjectReferenceRedirect(path: Path) { 3184 if (!isDeclarationFileName(path)) return undefined; 3185 if (mapFromToProjectReferenceRedirectSource === undefined) { 3186 mapFromToProjectReferenceRedirectSource = new Map(); 3187 forEachResolvedProjectReference(resolvedRef => { 3188 const out = outFile(resolvedRef.commandLine.options); 3189 if (out) { 3190 // Dont know which source file it means so return true? 3191 const outputDts = changeExtension(out, Extension.Dts); 3192 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), true); 3193 } 3194 else { 3195 const getCommonSourceDirectory = memoize(() => getCommonSourceDirectoryOfConfig(resolvedRef.commandLine, !host.useCaseSensitiveFileNames())); 3196 forEach(resolvedRef.commandLine.fileNames, fileName => { 3197 if (!isDeclarationFileName(fileName) && !fileExtensionIs(fileName, Extension.Json)) { 3198 const outputDts = getOutputDeclarationFileName(fileName, resolvedRef.commandLine, !host.useCaseSensitiveFileNames(), getCommonSourceDirectory); 3199 mapFromToProjectReferenceRedirectSource!.set(toPath(outputDts), fileName); 3200 } 3201 }); 3202 } 3203 }); 3204 } 3205 return mapFromToProjectReferenceRedirectSource.get(path); 3206 } 3207 3208 function isSourceOfProjectReferenceRedirect(fileName: string) { 3209 return useSourceOfProjectReferenceRedirect && !!getResolvedProjectReferenceToRedirect(fileName); 3210 } 3211 3212 function getResolvedProjectReferenceByPath(projectReferencePath: Path): ResolvedProjectReference | undefined { 3213 if (!projectReferenceRedirects) { 3214 return undefined; 3215 } 3216 3217 return projectReferenceRedirects.get(projectReferencePath) || undefined; 3218 } 3219 3220 function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) { 3221 forEach(file.referencedFiles, (ref, index) => { 3222 processSourceFile( 3223 resolveTripleslashReference(ref.fileName, file.fileName), 3224 isDefaultLib, 3225 /*ignoreNoDefaultLib*/ false, 3226 /*packageId*/ undefined, 3227 { kind: FileIncludeKind.ReferenceFile, file: file.path, index, } 3228 ); 3229 }); 3230 } 3231 3232 function processTypeReferenceDirectives(file: SourceFile) { 3233 const typeDirectives = file.typeReferenceDirectives; 3234 if (!typeDirectives) { 3235 return; 3236 } 3237 3238 const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file); 3239 for (let index = 0; index < typeDirectives.length; index++) { 3240 const ref = file.typeReferenceDirectives[index]; 3241 const resolvedTypeReferenceDirective = resolutions[index]; 3242 // store resolved type directive on the file 3243 const fileName = toFileNameLowerCase(ref.fileName); 3244 setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); 3245 const mode = ref.resolutionMode || file.impliedNodeFormat; 3246 if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) { 3247 programDiagnostics.add(createDiagnosticForRange(file, ref, Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext)); 3248 } 3249 processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, }); 3250 } 3251 } 3252 3253 function processTypeReferenceDirective( 3254 typeReferenceDirective: string, 3255 mode: SourceFile["impliedNodeFormat"] | undefined, 3256 resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, 3257 reason: FileIncludeReason 3258 ): void { 3259 tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolvedTypeReferenceDirective, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined }); 3260 processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolvedTypeReferenceDirective, reason); 3261 tracing?.pop(); 3262 } 3263 3264 function processTypeReferenceDirectiveWorker( 3265 typeReferenceDirective: string, 3266 mode: SourceFile["impliedNodeFormat"] | undefined, 3267 resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined, 3268 reason: FileIncludeReason 3269 ): void { 3270 3271 // If we already found this library as a primary reference - nothing to do 3272 const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode); 3273 if (previousResolution && previousResolution.primary) { 3274 return; 3275 } 3276 let saveResolution = true; 3277 if (resolvedTypeReferenceDirective) { 3278 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth++; 3279 3280 if (resolvedTypeReferenceDirective.primary) { 3281 // resolved from the primary path 3282 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); // TODO: GH#18217 3283 } 3284 else { 3285 // If we already resolved to this file, it must have been a secondary reference. Check file contents 3286 // for sameness and possibly issue an error 3287 if (previousResolution) { 3288 // Don't bother reading the file again if it's the same file. 3289 if (resolvedTypeReferenceDirective.resolvedFileName !== previousResolution.resolvedFileName) { 3290 const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName!); 3291 const existingFile = getSourceFile(previousResolution.resolvedFileName!)!; 3292 if (otherFileText !== existingFile.text) { 3293 addFilePreprocessingFileExplainingDiagnostic( 3294 existingFile, 3295 reason, 3296 Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, 3297 [typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName] 3298 ); 3299 } 3300 } 3301 // don't overwrite previous resolution result 3302 saveResolution = false; 3303 } 3304 else { 3305 // First resolution of this library 3306 processSourceFile(resolvedTypeReferenceDirective.resolvedFileName!, /*isDefaultLib*/ false, /*ignoreNoDefaultLib*/ false, resolvedTypeReferenceDirective.packageId, reason); 3307 } 3308 } 3309 3310 if (resolvedTypeReferenceDirective.isExternalLibraryImport) currentNodeModulesDepth--; 3311 } 3312 else { 3313 addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_find_type_definition_file_for_0, [typeReferenceDirective]); 3314 } 3315 3316 if (saveResolution) { 3317 resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolvedTypeReferenceDirective); 3318 } 3319 } 3320 3321 function pathForLibFile(libFileName: string): string { 3322 // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and 3323 // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable 3324 // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown 3325 const components = libFileName.split("."); 3326 let path = components[1]; 3327 let i = 2; 3328 while (components[i] && components[i] !== "d") { 3329 path += (i === 2 ? "/" : "-") + components[i]; 3330 i++; 3331 } 3332 const modulePathType = getModuleByPMType(options.packageManagerType); 3333 const resolveFrom = combinePaths(currentDirectory, `__lib_${modulePathType}_lookup_${libFileName}__.ts`); 3334 const localOverrideModuleResult = resolveModuleName("@typescript/lib-" + path, resolveFrom, { moduleResolution: ModuleResolutionKind.NodeJs }, host, moduleResolutionCache); 3335 if (localOverrideModuleResult?.resolvedModule) { 3336 return localOverrideModuleResult.resolvedModule.resolvedFileName; 3337 } 3338 return combinePaths(defaultLibraryPath, libFileName); 3339 } 3340 3341 function processLibReferenceDirectives(file: SourceFile) { 3342 forEach(file.libReferenceDirectives, (libReference, index) => { 3343 const libName = toFileNameLowerCase(libReference.fileName); 3344 const libFileName = libMap.get(libName); 3345 if (libFileName) { 3346 // we ignore any 'no-default-lib' reference set on this file. 3347 processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }); 3348 } 3349 else { 3350 const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts"); 3351 const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity); 3352 const diagnostic = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; 3353 (fileProcessingDiagnostics ||= []).push({ 3354 kind: FilePreprocessingDiagnosticsKind.FilePreprocessingReferencedDiagnostic, 3355 reason: { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, }, 3356 diagnostic, 3357 args: [libName, suggestion] 3358 }); 3359 } 3360 }); 3361 } 3362 3363 function getCanonicalFileName(fileName: string): string { 3364 return host.getCanonicalFileName(fileName); 3365 } 3366 3367 function processImportedModules(file: SourceFile) { 3368 collectExternalModuleReferences(file); 3369 if (file.imports.length || file.moduleAugmentations.length) { 3370 // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. 3371 const moduleNames = getModuleNames(file); 3372 const resolutions = resolveModuleNamesReusingOldState(moduleNames, file); 3373 Debug.assert(resolutions.length === moduleNames.length); 3374 const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options; 3375 for (let index = 0; index < moduleNames.length; index++) { 3376 const resolution = resolutions[index]; 3377 setResolvedModule(file, moduleNames[index], resolution, getModeForResolutionAtIndex(file, index)); 3378 3379 if (!resolution) { 3380 continue; 3381 } 3382 3383 const isFromNodeModulesSearch = resolution.isExternalLibraryImport; 3384 const isJsFile = !resolutionExtensionIsTSOrJson(resolution.extension); 3385 const isJsFileFromNodeModules = isFromNodeModulesSearch && isJsFile; 3386 const resolvedFileName = resolution.resolvedFileName; 3387 3388 if (isFromNodeModulesSearch) { 3389 currentNodeModulesDepth++; 3390 } 3391 3392 // add file to program only if: 3393 // - resolution was successful 3394 // - noResolve is falsy 3395 // - module name comes from the list of imports 3396 // - it's not a top level JavaScript module that exceeded the search max 3397 const elideImport = isJsFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth; 3398 // Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs') 3399 // This may still end up being an untyped module -- the file won't be included but imports will be allowed. 3400 const shouldAddFile = resolvedFileName 3401 && !getResolutionDiagnostic(optionsForFile, resolution) 3402 && !optionsForFile.noResolve 3403 && index < file.imports.length 3404 && !elideImport 3405 && !(isJsFile && !getAllowJSCompilerOption(optionsForFile)) 3406 && (isInJSFile(file.imports[index]) || !(file.imports[index].flags & NodeFlags.JSDoc)); 3407 3408 if (elideImport) { 3409 modulesWithElidedImports.set(file.path, true); 3410 } 3411 else if (shouldAddFile) { 3412 findSourceFile( 3413 resolvedFileName, 3414 /*isDefaultLib*/ false, 3415 /*ignoreNoDefaultLib*/ false, 3416 { kind: FileIncludeKind.Import, file: file.path, index, }, 3417 resolution.packageId, 3418 ); 3419 } 3420 3421 if (isFromNodeModulesSearch) { 3422 currentNodeModulesDepth--; 3423 } 3424 } 3425 } 3426 else { 3427 // no imports - drop cached module resolutions 3428 file.resolvedModules = undefined; 3429 } 3430 } 3431 3432 function checkSourceFilesBelongToPath(sourceFiles: readonly SourceFile[], rootDirectory: string): boolean { 3433 let allFilesBelongToPath = true; 3434 const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory)); 3435 for (const sourceFile of sourceFiles) { 3436 if (!sourceFile.isDeclarationFile) { 3437 const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory)); 3438 if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) { 3439 addProgramDiagnosticExplainingFile( 3440 sourceFile, 3441 Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, 3442 [sourceFile.fileName, rootDirectory] 3443 ); 3444 allFilesBelongToPath = false; 3445 } 3446 } 3447 } 3448 3449 return allFilesBelongToPath; 3450 } 3451 3452 function parseProjectReferenceConfigFile(ref: ProjectReference): ResolvedProjectReference | undefined { 3453 if (!projectReferenceRedirects) { 3454 projectReferenceRedirects = new Map(); 3455 } 3456 3457 // The actual filename (i.e. add "/tsconfig.json" if necessary) 3458 const refPath = resolveProjectReferencePath(ref); 3459 const sourceFilePath = toPath(refPath); 3460 const fromCache = projectReferenceRedirects.get(sourceFilePath); 3461 if (fromCache !== undefined) { 3462 return fromCache || undefined; 3463 } 3464 3465 let commandLine: ParsedCommandLine | undefined; 3466 let sourceFile: JsonSourceFile | undefined; 3467 if (host.getParsedCommandLine) { 3468 commandLine = host.getParsedCommandLine(refPath); 3469 if (!commandLine) { 3470 addFileToFilesByName(/*sourceFile*/ undefined, sourceFilePath, /*redirectedPath*/ undefined); 3471 projectReferenceRedirects.set(sourceFilePath, false); 3472 return undefined; 3473 } 3474 sourceFile = Debug.checkDefined(commandLine.options.configFile); 3475 Debug.assert(!sourceFile.path || sourceFile.path === sourceFilePath); 3476 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined); 3477 } 3478 else { 3479 // An absolute path pointing to the containing directory of the config file 3480 const basePath = getNormalizedAbsolutePath(getDirectoryPath(refPath), host.getCurrentDirectory()); 3481 sourceFile = host.getSourceFile(refPath, ScriptTarget.JSON) as JsonSourceFile | undefined; 3482 addFileToFilesByName(sourceFile, sourceFilePath, /*redirectedPath*/ undefined); 3483 if (sourceFile === undefined) { 3484 projectReferenceRedirects.set(sourceFilePath, false); 3485 return undefined; 3486 } 3487 commandLine = parseJsonSourceFileConfigFileContent(sourceFile, configParsingHost, basePath, /*existingOptions*/ undefined, refPath); 3488 } 3489 sourceFile.fileName = refPath; 3490 sourceFile.path = sourceFilePath; 3491 sourceFile.resolvedPath = sourceFilePath; 3492 sourceFile.originalFileName = refPath; 3493 3494 const resolvedRef: ResolvedProjectReference = { commandLine, sourceFile }; 3495 projectReferenceRedirects.set(sourceFilePath, resolvedRef); 3496 if (commandLine.projectReferences) { 3497 resolvedRef.references = commandLine.projectReferences.map(parseProjectReferenceConfigFile); 3498 } 3499 return resolvedRef; 3500 } 3501 3502 function verifyCompilerOptions() { 3503 if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) { 3504 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks"); 3505 } 3506 if (options.exactOptionalPropertyTypes && !getStrictOptionValue(options, "strictNullChecks")) { 3507 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "exactOptionalPropertyTypes", "strictNullChecks"); 3508 } 3509 3510 if (options.isolatedModules) { 3511 if (options.out) { 3512 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "isolatedModules"); 3513 } 3514 3515 if (options.outFile) { 3516 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "outFile", "isolatedModules"); 3517 } 3518 } 3519 3520 if (options.inlineSourceMap) { 3521 if (options.sourceMap) { 3522 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap"); 3523 } 3524 if (options.mapRoot) { 3525 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap"); 3526 } 3527 } 3528 3529 if (options.composite) { 3530 if (options.declaration === false) { 3531 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration"); 3532 } 3533 if (options.incremental === false) { 3534 createDiagnosticForOptionName(Diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration"); 3535 } 3536 } 3537 3538 const outputFile = outFile(options); 3539 if (options.tsBuildInfoFile) { 3540 if (!isIncrementalCompilation(options)) { 3541 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "tsBuildInfoFile", "incremental", "composite"); 3542 } 3543 } 3544 else if (options.incremental && !outputFile && !options.configFilePath) { 3545 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_incremental_can_only_be_specified_using_tsconfig_emitting_to_single_file_or_when_option_tsBuildInfoFile_is_specified)); 3546 } 3547 3548 verifyProjectReferences(); 3549 3550 // List of collected files is complete; validate exhautiveness if this is a project with a file list 3551 if (options.composite) { 3552 const rootPaths = new Set(rootNames.map(toPath)); 3553 for (const file of files) { 3554 // Ignore file that is not emitted 3555 if (sourceFileMayBeEmitted(file, program) && !rootPaths.has(file.path)) { 3556 addProgramDiagnosticExplainingFile( 3557 file, 3558 Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, 3559 [file.fileName, options.configFilePath || ""] 3560 ); 3561 } 3562 } 3563 } 3564 3565 if (options.paths) { 3566 for (const key in options.paths) { 3567 if (!hasProperty(options.paths, key)) { 3568 continue; 3569 } 3570 if (!hasZeroOrOneAsteriskCharacter(key)) { 3571 createDiagnosticForOptionPaths(/*onKey*/ true, key, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key); 3572 } 3573 if (isArray(options.paths[key])) { 3574 const len = options.paths[key].length; 3575 if (len === 0) { 3576 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key); 3577 } 3578 for (let i = 0; i < len; i++) { 3579 const subst = options.paths[key][i]; 3580 const typeOfSubst = typeof subst; 3581 if (typeOfSubst === "string") { 3582 if (!hasZeroOrOneAsteriskCharacter(subst)) { 3583 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key); 3584 } 3585 if (!options.baseUrl && !pathIsRelative(subst) && !pathIsAbsolute(subst)) { 3586 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Non_relative_paths_are_not_allowed_when_baseUrl_is_not_set_Did_you_forget_a_leading_Slash); 3587 } 3588 } 3589 else { 3590 createDiagnosticForOptionPathKeyValue(key, i, Diagnostics.Substitution_0_for_pattern_1_has_incorrect_type_expected_string_got_2, subst, key, typeOfSubst); 3591 } 3592 } 3593 } 3594 else { 3595 createDiagnosticForOptionPaths(/*onKey*/ false, key, Diagnostics.Substitutions_for_pattern_0_should_be_an_array, key); 3596 } 3597 } 3598 } 3599 3600 if (!options.sourceMap && !options.inlineSourceMap) { 3601 if (options.inlineSources) { 3602 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources"); 3603 } 3604 if (options.sourceRoot) { 3605 createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot"); 3606 } 3607 } 3608 3609 if (options.out && options.outFile) { 3610 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile"); 3611 } 3612 3613 if (options.mapRoot && !(options.sourceMap || options.declarationMap)) { 3614 // Error to specify --mapRoot without --sourcemap 3615 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap"); 3616 } 3617 3618 if (options.declarationDir) { 3619 if (!getEmitDeclarations(options)) { 3620 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite"); 3621 } 3622 if (outputFile) { 3623 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "declarationDir", options.out ? "out" : "outFile"); 3624 } 3625 } 3626 3627 if (options.declarationMap && !getEmitDeclarations(options)) { 3628 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite"); 3629 } 3630 3631 if (options.lib && options.noLib) { 3632 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib"); 3633 } 3634 3635 if (options.noImplicitUseStrict && getStrictOptionValue(options, "alwaysStrict")) { 3636 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict"); 3637 } 3638 3639 const languageVersion = getEmitScriptTarget(options); 3640 3641 const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile); 3642 if (options.isolatedModules) { 3643 if (options.module === ModuleKind.None && languageVersion < ScriptTarget.ES2015) { 3644 createDiagnosticForOptionName(Diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target"); 3645 } 3646 3647 if (options.preserveConstEnums === false) { 3648 createDiagnosticForOptionName(Diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_isolatedModules_is_enabled, "preserveConstEnums", "isolatedModules"); 3649 } 3650 3651 for (const file of files) { 3652 if (!isExternalModule(file) && !isSourceFileJS(file) && !file.isDeclarationFile && file.scriptKind !== ScriptKind.JSON) { 3653 const span = getErrorSpanForNode(file, file); 3654 programDiagnostics.add(createFileDiagnostic(file, span.start, span.length, 3655 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))); 3656 } 3657 } 3658 } 3659 else if (firstNonAmbientExternalModuleSourceFile && languageVersion < ScriptTarget.ES2015 && options.module === ModuleKind.None) { 3660 // We cannot use createDiagnosticFromNode because nodes do not have parents yet 3661 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, typeof firstNonAmbientExternalModuleSourceFile.externalModuleIndicator === "boolean" ? firstNonAmbientExternalModuleSourceFile : firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!); 3662 programDiagnostics.add(createFileDiagnostic(firstNonAmbientExternalModuleSourceFile, span.start, span.length, Diagnostics.Cannot_use_imports_exports_or_module_augmentations_when_module_is_none)); 3663 } 3664 3665 // Cannot specify module gen that isn't amd or system with --out 3666 if (outputFile && !options.emitDeclarationOnly) { 3667 if (options.module && !(options.module === ModuleKind.AMD || options.module === ModuleKind.System)) { 3668 createDiagnosticForOptionName(Diagnostics.Only_amd_and_system_modules_are_supported_alongside_0, options.out ? "out" : "outFile", "module"); 3669 } 3670 else if (options.module === undefined && firstNonAmbientExternalModuleSourceFile) { 3671 const span = getErrorSpanForNode(firstNonAmbientExternalModuleSourceFile, typeof firstNonAmbientExternalModuleSourceFile.externalModuleIndicator === "boolean" ? firstNonAmbientExternalModuleSourceFile : firstNonAmbientExternalModuleSourceFile.externalModuleIndicator!); 3672 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")); 3673 } 3674 } 3675 3676 if (options.resolveJsonModule) { 3677 if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs && 3678 getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && 3679 getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) { 3680 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule"); 3681 } 3682 // Any emit other than common js, amd, es2015 or esnext is error 3683 else if (!hasJsonModuleEmitEnabled(options)) { 3684 createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_can_only_be_specified_when_module_code_generation_is_commonjs_amd_es2015_or_esNext, "resolveJsonModule", "module"); 3685 } 3686 } 3687 3688 // there has to be common source directory if user specified --outdir || --rootDir || --sourceRoot 3689 // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted 3690 if (options.outDir || // there is --outDir specified 3691 options.rootDir || // there is --rootDir specified 3692 options.sourceRoot || // there is --sourceRoot specified 3693 options.mapRoot) { // there is --mapRoot specified 3694 3695 // Precalculate and cache the common source directory 3696 const dir = getCommonSourceDirectory(); 3697 3698 // 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 3699 if (options.outDir && dir === "" && files.some(file => getRootLength(file.fileName) > 1)) { 3700 createDiagnosticForOptionName(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir"); 3701 } 3702 } 3703 3704 if (options.useDefineForClassFields && languageVersion === ScriptTarget.ES3) { 3705 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_target_is_ES3, "useDefineForClassFields"); 3706 } 3707 3708 if (options.checkJs && !getAllowJSCompilerOption(options)) { 3709 programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs")); 3710 } 3711 3712 if (options.emitDeclarationOnly) { 3713 if (!getEmitDeclarations(options)) { 3714 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite"); 3715 } 3716 3717 if (options.noEmit) { 3718 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationOnly", "noEmit"); 3719 } 3720 } 3721 3722 if (options.emitDecoratorMetadata && 3723 !options.experimentalDecorators) { 3724 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"); 3725 } 3726 3727 if (options.jsxFactory) { 3728 if (options.reactNamespace) { 3729 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"); 3730 } 3731 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3732 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", inverseJsxOptionMap.get("" + options.jsx)); 3733 } 3734 if (!parseIsolatedEntityName(options.jsxFactory, languageVersion)) { 3735 createOptionValueDiagnostic("jsxFactory", Diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFactory); 3736 } 3737 } 3738 else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) { 3739 createOptionValueDiagnostic("reactNamespace", Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace); 3740 } 3741 3742 if (options.jsxFragmentFactory) { 3743 if (!options.jsxFactory) { 3744 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory"); 3745 } 3746 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3747 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", inverseJsxOptionMap.get("" + options.jsx)); 3748 } 3749 if (!parseIsolatedEntityName(options.jsxFragmentFactory, languageVersion)) { 3750 createOptionValueDiagnostic("jsxFragmentFactory", Diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.jsxFragmentFactory); 3751 } 3752 } 3753 3754 if (options.reactNamespace) { 3755 if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) { 3756 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", inverseJsxOptionMap.get("" + options.jsx)); 3757 } 3758 } 3759 3760 if (options.jsxImportSource) { 3761 if (options.jsx === JsxEmit.React) { 3762 createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", inverseJsxOptionMap.get("" + options.jsx)); 3763 } 3764 } 3765 3766 if (options.preserveValueImports && getEmitModuleKind(options) < ModuleKind.ES2015) { 3767 createOptionValueDiagnostic("importsNotUsedAsValues", Diagnostics.Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later); 3768 } 3769 3770 // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files 3771 if (!options.noEmit && !options.suppressOutputPathCheck) { 3772 const emitHost = getEmitHost(); 3773 const emitFilesSeen = new Set<string>(); 3774 forEachEmittedFile(emitHost, (emitFileNames) => { 3775 if (!options.emitDeclarationOnly) { 3776 verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen); 3777 } 3778 verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen); 3779 }); 3780 } 3781 3782 // Verify that all the emit files are unique and don't overwrite input files 3783 function verifyEmitFilePath(emitFileName: string | undefined, emitFilesSeen: Set<string>) { 3784 if (emitFileName) { 3785 const emitFilePath = toPath(emitFileName); 3786 // Report error if the output overwrites input file 3787 if (filesByName.has(emitFilePath)) { 3788 let chain: DiagnosticMessageChain | undefined; 3789 if (!options.configFilePath) { 3790 // The program is from either an inferred project or an external project 3791 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); 3792 } 3793 chain = chainDiagnosticMessages(chain, Diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName); 3794 blockEmittingOfFile(emitFileName, createCompilerDiagnosticFromMessageChain(chain)); 3795 } 3796 3797 const emitFileKey = !host.useCaseSensitiveFileNames() ? toFileNameLowerCase(emitFilePath) : emitFilePath; 3798 // Report error if multiple files write into same file 3799 if (emitFilesSeen.has(emitFileKey)) { 3800 // Already seen the same emit file - report error 3801 blockEmittingOfFile(emitFileName, createCompilerDiagnostic(Diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName)); 3802 } 3803 else { 3804 emitFilesSeen.add(emitFileKey); 3805 } 3806 } 3807 } 3808 } 3809 3810 function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic { 3811 let fileIncludeReasons: DiagnosticMessageChain[] | undefined; 3812 let relatedInfo: Diagnostic[] | undefined; 3813 let locationReason = isReferencedFile(fileProcessingReason) ? fileProcessingReason : undefined; 3814 if (file) fileReasons.get(file.path)?.forEach(processReason); 3815 if (fileProcessingReason) processReason(fileProcessingReason); 3816 // If we have location and there is only one reason file is in which is the location, dont add details for file include 3817 if (locationReason && fileIncludeReasons?.length === 1) fileIncludeReasons = undefined; 3818 const location = locationReason && getReferencedFileLocation(getSourceFileByPath, locationReason); 3819 const fileIncludeReasonDetails = fileIncludeReasons && chainDiagnosticMessages(fileIncludeReasons, Diagnostics.The_file_is_in_the_program_because_Colon); 3820 const redirectInfo = file && explainIfFileIsRedirectAndImpliedFormat(file); 3821 const chain = chainDiagnosticMessages(redirectInfo ? fileIncludeReasonDetails ? [fileIncludeReasonDetails, ...redirectInfo] : redirectInfo : fileIncludeReasonDetails, diagnostic, ...args || emptyArray); 3822 return location && isReferenceFileLocation(location) ? 3823 createFileDiagnosticFromMessageChain(location.file, location.pos, location.end - location.pos, chain, relatedInfo) : 3824 createCompilerDiagnosticFromMessageChain(chain, relatedInfo); 3825 3826 function processReason(reason: FileIncludeReason) { 3827 (fileIncludeReasons ||= []).push(fileIncludeReasonToDiagnostics(program, reason)); 3828 if (!locationReason && isReferencedFile(reason)) { 3829 // Report error at first reference file or file currently in processing and dont report in related information 3830 locationReason = reason; 3831 } 3832 else if (locationReason !== reason) { 3833 relatedInfo = append(relatedInfo, fileIncludeReasonToRelatedInformation(reason)); 3834 } 3835 // Remove fileProcessingReason if its already included in fileReasons of the program 3836 if (reason === fileProcessingReason) fileProcessingReason = undefined; 3837 } 3838 } 3839 3840 function addFilePreprocessingFileExplainingDiagnostic(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { 3841 (fileProcessingDiagnostics ||= []).push({ 3842 kind: FilePreprocessingDiagnosticsKind.FilePreprocessingFileExplainingDiagnostic, 3843 file: file && file.path, 3844 fileProcessingReason, 3845 diagnostic, 3846 args 3847 }); 3848 } 3849 3850 function addProgramDiagnosticExplainingFile(file: SourceFile, diagnostic: DiagnosticMessage, args?: (string | number | undefined)[]) { 3851 programDiagnostics.add(createDiagnosticExplainingFile(file, /*fileProcessingReason*/ undefined, diagnostic, args)); 3852 } 3853 3854 function fileIncludeReasonToRelatedInformation(reason: FileIncludeReason): DiagnosticWithLocation | undefined { 3855 if (isReferencedFile(reason)) { 3856 const referenceLocation = getReferencedFileLocation(getSourceFileByPath, reason); 3857 let message: DiagnosticMessage; 3858 switch (reason.kind) { 3859 case FileIncludeKind.Import: 3860 message = Diagnostics.File_is_included_via_import_here; 3861 break; 3862 case FileIncludeKind.ReferenceFile: 3863 message = Diagnostics.File_is_included_via_reference_here; 3864 break; 3865 case FileIncludeKind.TypeReferenceDirective: 3866 message = Diagnostics.File_is_included_via_type_library_reference_here; 3867 break; 3868 case FileIncludeKind.LibReferenceDirective: 3869 message = Diagnostics.File_is_included_via_library_reference_here; 3870 break; 3871 default: 3872 Debug.assertNever(reason); 3873 } 3874 return isReferenceFileLocation(referenceLocation) ? createFileDiagnostic( 3875 referenceLocation.file, 3876 referenceLocation.pos, 3877 referenceLocation.end - referenceLocation.pos, 3878 message, 3879 ) : undefined; 3880 } 3881 3882 if (!options.configFile) return undefined; 3883 let configFileNode: Node | undefined; 3884 let message: DiagnosticMessage; 3885 switch (reason.kind) { 3886 case FileIncludeKind.RootFile: 3887 if (!options.configFile.configFileSpecs) return undefined; 3888 const fileName = getNormalizedAbsolutePath(rootNames[reason.index], currentDirectory); 3889 const matchedByFiles = getMatchedFileSpec(program, fileName); 3890 if (matchedByFiles) { 3891 configFileNode = getTsConfigPropArrayElementValue(options.configFile, "files", matchedByFiles); 3892 message = Diagnostics.File_is_matched_by_files_list_specified_here; 3893 break; 3894 } 3895 const matchedByInclude = getMatchedIncludeSpec(program, fileName); 3896 // Could be additional files specified as roots 3897 if (!matchedByInclude || !isString(matchedByInclude)) return undefined; 3898 configFileNode = getTsConfigPropArrayElementValue(options.configFile, "include", matchedByInclude); 3899 message = Diagnostics.File_is_matched_by_include_pattern_specified_here; 3900 break; 3901 case FileIncludeKind.SourceFromProjectReference: 3902 case FileIncludeKind.OutputFromProjectReference: 3903 const referencedResolvedRef = Debug.checkDefined(resolvedProjectReferences?.[reason.index]); 3904 const referenceInfo = forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => 3905 resolvedRef === referencedResolvedRef ? { sourceFile: parent?.sourceFile || options.configFile!, index } : undefined 3906 ); 3907 if (!referenceInfo) return undefined; 3908 const { sourceFile, index } = referenceInfo; 3909 const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile as TsConfigSourceFile, "references"), 3910 property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined); 3911 return referencesSyntax && referencesSyntax.elements.length > index ? 3912 createDiagnosticForNodeInSourceFile( 3913 sourceFile, 3914 referencesSyntax.elements[index], 3915 reason.kind === FileIncludeKind.OutputFromProjectReference ? 3916 Diagnostics.File_is_output_from_referenced_project_specified_here : 3917 Diagnostics.File_is_source_from_referenced_project_specified_here, 3918 ) : 3919 undefined; 3920 case FileIncludeKind.AutomaticTypeDirectiveFile: 3921 if (!options.types) return undefined; 3922 configFileNode = getOptionsSyntaxByArrayElementValue("types", reason.typeReference); 3923 message = Diagnostics.File_is_entry_point_of_type_library_specified_here; 3924 break; 3925 case FileIncludeKind.LibFile: 3926 if (reason.index !== undefined) { 3927 configFileNode = getOptionsSyntaxByArrayElementValue("lib", options.lib![reason.index]); 3928 message = Diagnostics.File_is_library_specified_here; 3929 break; 3930 } 3931 const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined); 3932 configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined; 3933 message = Diagnostics.File_is_default_library_for_target_specified_here; 3934 break; 3935 default: 3936 Debug.assertNever(reason); 3937 } 3938 return configFileNode && createDiagnosticForNodeInSourceFile( 3939 options.configFile, 3940 configFileNode, 3941 message, 3942 ); 3943 } 3944 3945 function verifyProjectReferences() { 3946 const buildInfoPath = !options.suppressOutputPathCheck ? getTsBuildInfoEmitOutputFilePath(options) : undefined; 3947 forEachProjectReference(projectReferences, resolvedProjectReferences, (resolvedRef, parent, index) => { 3948 const ref = (parent ? parent.commandLine.projectReferences : projectReferences)![index]; 3949 const parentFile = parent && parent.sourceFile as JsonSourceFile; 3950 if (!resolvedRef) { 3951 createDiagnosticForReference(parentFile, index, Diagnostics.File_0_not_found, ref.path); 3952 return; 3953 } 3954 const options = resolvedRef.commandLine.options; 3955 if (!options.composite || options.noEmit) { 3956 // ok to not have composite if the current program is container only 3957 const inputs = parent ? parent.commandLine.fileNames : rootNames; 3958 if (inputs.length) { 3959 if (!options.composite) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.path); 3960 if (options.noEmit) createDiagnosticForReference(parentFile, index, Diagnostics.Referenced_project_0_may_not_disable_emit, ref.path); 3961 } 3962 } 3963 if (ref.prepend) { 3964 const out = outFile(options); 3965 if (out) { 3966 if (!host.fileExists(out)) { 3967 createDiagnosticForReference(parentFile, index, Diagnostics.Output_file_0_from_project_1_does_not_exist, out, ref.path); 3968 } 3969 } 3970 else { 3971 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set, ref.path); 3972 } 3973 } 3974 if (!parent && buildInfoPath && buildInfoPath === getTsBuildInfoEmitOutputFilePath(options)) { 3975 createDiagnosticForReference(parentFile, index, Diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoPath, ref.path); 3976 hasEmitBlockingDiagnostics.set(toPath(buildInfoPath), true); 3977 } 3978 }); 3979 } 3980 3981 function createDiagnosticForOptionPathKeyValue(key: string, valueIndex: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) { 3982 let needCompilerDiagnostic = true; 3983 const pathsSyntax = getOptionPathsSyntax(); 3984 for (const pathProp of pathsSyntax) { 3985 if (isObjectLiteralExpression(pathProp.initializer)) { 3986 for (const keyProps of getPropertyAssignment(pathProp.initializer, key)) { 3987 const initializer = keyProps.initializer; 3988 if (isArrayLiteralExpression(initializer) && initializer.elements.length > valueIndex) { 3989 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, initializer.elements[valueIndex], message, arg0, arg1, arg2)); 3990 needCompilerDiagnostic = false; 3991 } 3992 } 3993 } 3994 } 3995 3996 if (needCompilerDiagnostic) { 3997 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2)); 3998 } 3999 } 4000 4001 function createDiagnosticForOptionPaths(onKey: boolean, key: string, message: DiagnosticMessage, arg0: string | number) { 4002 let needCompilerDiagnostic = true; 4003 const pathsSyntax = getOptionPathsSyntax(); 4004 for (const pathProp of pathsSyntax) { 4005 if (isObjectLiteralExpression(pathProp.initializer) && 4006 createOptionDiagnosticInObjectLiteralSyntax( 4007 pathProp.initializer, onKey, key, /*key2*/ undefined, 4008 message, arg0)) { 4009 needCompilerDiagnostic = false; 4010 } 4011 } 4012 if (needCompilerDiagnostic) { 4013 programDiagnostics.add(createCompilerDiagnostic(message, arg0)); 4014 } 4015 } 4016 4017 function getOptionsSyntaxByName(name: string) { 4018 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4019 return compilerOptionsObjectLiteralSyntax && getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name); 4020 } 4021 4022 function getOptionPathsSyntax() { 4023 return getOptionsSyntaxByName("paths") || emptyArray; 4024 } 4025 4026 function getOptionsSyntaxByValue(name: string, value: string) { 4027 const syntaxByName = getOptionsSyntaxByName(name); 4028 return syntaxByName && firstDefined(syntaxByName, property => isStringLiteral(property.initializer) && property.initializer.text === value ? property.initializer : undefined); 4029 } 4030 4031 function getOptionsSyntaxByArrayElementValue(name: string, value: string) { 4032 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4033 return compilerOptionsObjectLiteralSyntax && getPropertyArrayElementValue(compilerOptionsObjectLiteralSyntax, name, value); 4034 } 4035 4036 function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string, option3?: string) { 4037 createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3); 4038 } 4039 4040 function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0?: string, arg1?: string) { 4041 createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0, arg1); 4042 } 4043 4044 function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) { 4045 const referencesSyntax = firstDefined(getTsConfigPropArray(sourceFile || options.configFile, "references"), 4046 property => isArrayLiteralExpression(property.initializer) ? property.initializer : undefined); 4047 if (referencesSyntax && referencesSyntax.elements.length > index) { 4048 programDiagnostics.add(createDiagnosticForNodeInSourceFile(sourceFile || options.configFile!, referencesSyntax.elements[index], message, arg0, arg1)); 4049 } 4050 else { 4051 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1)); 4052 } 4053 } 4054 4055 function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number) { 4056 const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax(); 4057 const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax || 4058 !createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2); 4059 4060 if (needCompilerDiagnostic) { 4061 programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2)); 4062 } 4063 } 4064 4065 function getCompilerOptionsObjectLiteralSyntax() { 4066 if (_compilerOptionsObjectLiteralSyntax === undefined) { 4067 _compilerOptionsObjectLiteralSyntax = false; 4068 const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); 4069 if (jsonObjectLiteral) { 4070 for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) { 4071 if (isObjectLiteralExpression(prop.initializer)) { 4072 _compilerOptionsObjectLiteralSyntax = prop.initializer; 4073 break; 4074 } 4075 } 4076 } 4077 } 4078 return _compilerOptionsObjectLiteralSyntax || undefined; 4079 } 4080 4081 function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean { 4082 const props = getPropertyAssignment(objectLiteral, key1, key2); 4083 for (const prop of props) { 4084 programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2)); 4085 } 4086 return !!props.length; 4087 } 4088 4089 function blockEmittingOfFile(emitFileName: string, diag: Diagnostic) { 4090 hasEmitBlockingDiagnostics.set(toPath(emitFileName), true); 4091 programDiagnostics.add(diag); 4092 } 4093 4094 function isEmittedFile(file: string): boolean { 4095 if (options.noEmit) { 4096 return false; 4097 } 4098 4099 // If this is source file, its not emitted file 4100 const filePath = toPath(file); 4101 if (getSourceFileByPath(filePath)) { 4102 return false; 4103 } 4104 4105 // If options have --outFile or --out just check that 4106 const out = outFile(options); 4107 if (out) { 4108 return isSameFile(filePath, out) || isSameFile(filePath, removeFileExtension(out) + Extension.Dts); 4109 } 4110 4111 // If declarationDir is specified, return if its a file in that directory 4112 if (options.declarationDir && containsPath(options.declarationDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames())) { 4113 return true; 4114 } 4115 4116 // If --outDir, check if file is in that directory 4117 if (options.outDir) { 4118 return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames()); 4119 } 4120 4121 if (fileExtensionIsOneOf(filePath, supportedJSExtensionsFlat) || isDeclarationFileName(filePath)) { 4122 // Otherwise just check if sourceFile with the name exists 4123 const filePathWithoutExtension = removeFileExtension(filePath); 4124 return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) || 4125 !!getSourceFileByPath((filePathWithoutExtension + Extension.Tsx) as Path); 4126 } 4127 return false; 4128 } 4129 4130 function isSameFile(file1: string, file2: string) { 4131 return comparePaths(file1, file2, currentDirectory, !host.useCaseSensitiveFileNames()) === Comparison.EqualTo; 4132 } 4133 4134 function getSymlinkCache(): SymlinkCache { 4135 if (host.getSymlinkCache) { 4136 return host.getSymlinkCache(); 4137 } 4138 if (!symlinks) { 4139 symlinks = createSymlinkCache(currentDirectory, getCanonicalFileName, isOhpm(options.packageManagerType)); 4140 } 4141 if (files && resolvedTypeReferenceDirectives && !symlinks.hasProcessedResolutions()) { 4142 symlinks.setSymlinksFromResolutions(files, resolvedTypeReferenceDirectives); 4143 } 4144 return symlinks; 4145 } 4146 } 4147 4148 interface HostForUseSourceOfProjectReferenceRedirect { 4149 compilerHost: CompilerHost; 4150 getSymlinkCache: () => SymlinkCache; 4151 useSourceOfProjectReferenceRedirect: boolean; 4152 toPath(fileName: string): Path; 4153 getResolvedProjectReferences(): readonly (ResolvedProjectReference | undefined)[] | undefined; 4154 getSourceOfProjectReferenceRedirect(path: Path): SourceOfProjectReferenceRedirect | undefined; 4155 forEachResolvedProjectReference<T>(cb: (resolvedProjectReference: ResolvedProjectReference) => T | undefined): T | undefined; 4156 options?: CompilerOptions; 4157 } 4158 4159 function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSourceOfProjectReferenceRedirect) { 4160 let setOfDeclarationDirectories: Set<Path> | undefined; 4161 const originalFileExists = host.compilerHost.fileExists; 4162 const originalDirectoryExists = host.compilerHost.directoryExists; 4163 const originalGetDirectories = host.compilerHost.getDirectories; 4164 const originalRealpath = host.compilerHost.realpath; 4165 4166 if (!host.useSourceOfProjectReferenceRedirect) return { onProgramCreateComplete: noop, fileExists }; 4167 4168 host.compilerHost.fileExists = fileExists; 4169 4170 let directoryExists; 4171 if (originalDirectoryExists) { 4172 // This implementation of directoryExists checks if the directory being requested is 4173 // directory of .d.ts file for the referenced Project. 4174 // If it is it returns true irrespective of whether that directory exists on host 4175 directoryExists = host.compilerHost.directoryExists = path => { 4176 if (originalDirectoryExists.call(host.compilerHost, path)) { 4177 handleDirectoryCouldBeSymlink(path); 4178 return true; 4179 } 4180 4181 if (!host.getResolvedProjectReferences()) return false; 4182 4183 if (!setOfDeclarationDirectories) { 4184 setOfDeclarationDirectories = new Set(); 4185 host.forEachResolvedProjectReference(ref => { 4186 const out = outFile(ref.commandLine.options); 4187 if (out) { 4188 setOfDeclarationDirectories!.add(getDirectoryPath(host.toPath(out))); 4189 } 4190 else { 4191 // Set declaration's in different locations only, if they are next to source the directory present doesnt change 4192 const declarationDir = ref.commandLine.options.declarationDir || ref.commandLine.options.outDir; 4193 if (declarationDir) { 4194 setOfDeclarationDirectories!.add(host.toPath(declarationDir)); 4195 } 4196 } 4197 }); 4198 } 4199 4200 return fileOrDirectoryExistsUsingSource(path, /*isFile*/ false); 4201 }; 4202 } 4203 4204 if (originalGetDirectories) { 4205 // Call getDirectories only if directory actually present on the host 4206 // This is needed to ensure that we arent getting directories that we fake about presence for 4207 host.compilerHost.getDirectories = path => 4208 !host.getResolvedProjectReferences() || (originalDirectoryExists && originalDirectoryExists.call(host.compilerHost, path)) ? 4209 originalGetDirectories.call(host.compilerHost, path) : 4210 []; 4211 } 4212 4213 // This is something we keep for life time of the host 4214 if (originalRealpath) { 4215 host.compilerHost.realpath = s => 4216 host.getSymlinkCache().getSymlinkedFiles()?.get(host.toPath(s)) || 4217 originalRealpath.call(host.compilerHost, s); 4218 } 4219 4220 return { onProgramCreateComplete, fileExists, directoryExists }; 4221 4222 function onProgramCreateComplete() { 4223 host.compilerHost.fileExists = originalFileExists; 4224 host.compilerHost.directoryExists = originalDirectoryExists; 4225 host.compilerHost.getDirectories = originalGetDirectories; 4226 // DO not revert realpath as it could be used later 4227 } 4228 4229 // This implementation of fileExists checks if the file being requested is 4230 // .d.ts file for the referenced Project. 4231 // If it is it returns true irrespective of whether that file exists on host 4232 function fileExists(file: string) { 4233 if (originalFileExists.call(host.compilerHost, file)) return true; 4234 if (!host.getResolvedProjectReferences()) return false; 4235 if (!isDeclarationFileName(file)) return false; 4236 4237 // Project references go to source file instead of .d.ts file 4238 return fileOrDirectoryExistsUsingSource(file, /*isFile*/ true); 4239 } 4240 4241 function fileExistsIfProjectReferenceDts(file: string) { 4242 const source = host.getSourceOfProjectReferenceRedirect(host.toPath(file)); 4243 return source !== undefined ? 4244 isString(source) ? originalFileExists.call(host.compilerHost, source) as boolean : true : 4245 undefined; 4246 } 4247 4248 function directoryExistsIfProjectReferenceDeclDir(dir: string) { 4249 const dirPath = host.toPath(dir); 4250 const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`; 4251 return forEachKey( 4252 setOfDeclarationDirectories!, 4253 declDirPath => dirPath === declDirPath || 4254 // Any parent directory of declaration dir 4255 startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) || 4256 // Any directory inside declaration dir 4257 startsWith(dirPath, `${declDirPath}/`) 4258 ); 4259 } 4260 4261 function handleDirectoryCouldBeSymlink(directory: string) { 4262 if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return; 4263 4264 // Because we already watch node_modules or oh_modules, handle symlinks in there 4265 const modulesPathPart = getModulePathPartByPMType(host.options?.packageManagerType); 4266 if (!originalRealpath || !stringContains(directory, modulesPathPart)) return; 4267 const symlinkCache = host.getSymlinkCache(); 4268 const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory)); 4269 if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return; 4270 4271 const real = normalizePath(originalRealpath.call(host.compilerHost, directory)); 4272 let realPath: Path; 4273 if (real === directory || 4274 (realPath = ensureTrailingDirectorySeparator(host.toPath(real))) === directoryPath) { 4275 // not symlinked 4276 symlinkCache.setSymlinkedDirectory(directoryPath, false); 4277 return; 4278 } 4279 4280 symlinkCache.setSymlinkedDirectory(directory, { 4281 real: ensureTrailingDirectorySeparator(real), 4282 realPath 4283 }); 4284 } 4285 4286 function fileOrDirectoryExistsUsingSource(fileOrDirectory: string, isFile: boolean): boolean { 4287 const fileOrDirectoryExistsUsingSource = isFile ? 4288 (file: string) => fileExistsIfProjectReferenceDts(file) : 4289 (dir: string) => directoryExistsIfProjectReferenceDeclDir(dir); 4290 // Check current directory or file 4291 const result = fileOrDirectoryExistsUsingSource(fileOrDirectory); 4292 if (result !== undefined) return result; 4293 4294 const symlinkCache = host.getSymlinkCache(); 4295 const symlinkedDirectories = symlinkCache.getSymlinkedDirectories(); 4296 if (!symlinkedDirectories) return false; 4297 const fileOrDirectoryPath = host.toPath(fileOrDirectory); 4298 const modulesPathPart = getModulePathPartByPMType(host.options?.packageManagerType); 4299 if (!stringContains(fileOrDirectoryPath, modulesPathPart)) return false; 4300 if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true; 4301 4302 // If it contains node_modules or oh_modules check if its one of the symlinked path we know of 4303 return firstDefinedIterator( 4304 symlinkedDirectories.entries(), 4305 ([directoryPath, symlinkedDirectory]) => { 4306 if (!symlinkedDirectory || !startsWith(fileOrDirectoryPath, directoryPath)) return undefined; 4307 const result = fileOrDirectoryExistsUsingSource(fileOrDirectoryPath.replace(directoryPath, symlinkedDirectory.realPath)); 4308 if (isFile && result) { 4309 // Store the real path for the file' 4310 const absolutePath = getNormalizedAbsolutePath(fileOrDirectory, host.compilerHost.getCurrentDirectory()); 4311 symlinkCache.setSymlinkedFile( 4312 fileOrDirectoryPath, 4313 `${symlinkedDirectory.real}${absolutePath.replace(new RegExp(directoryPath, "i"), "")}` 4314 ); 4315 } 4316 return result; 4317 } 4318 ) || false; 4319 } 4320 } 4321 4322 /*@internal*/ 4323 export const emitSkippedWithNoDiagnostics: EmitResult = { diagnostics: emptyArray, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true }; 4324 4325 /*@internal*/ 4326 export function handleNoEmitOptions<T extends BuilderProgram>( 4327 program: Program | T, 4328 sourceFile: SourceFile | undefined, 4329 writeFile: WriteFileCallback | undefined, 4330 cancellationToken: CancellationToken | undefined 4331 ): EmitResult | undefined { 4332 const options = program.getCompilerOptions(); 4333 if (options.noEmit) { 4334 // Cache the semantic diagnostics 4335 program.getSemanticDiagnostics(sourceFile, cancellationToken); 4336 return sourceFile || outFile(options) ? 4337 emitSkippedWithNoDiagnostics : 4338 program.emitBuildInfo(writeFile, cancellationToken); 4339 } 4340 4341 // If the noEmitOnError flag is set, then check if we have any errors so far. If so, 4342 // immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we 4343 // get any preEmit diagnostics, not just the ones 4344 if (!options.noEmitOnError) return undefined; 4345 let diagnostics: readonly Diagnostic[] = [ 4346 ...program.getOptionsDiagnostics(cancellationToken), 4347 ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), 4348 ...program.getGlobalDiagnostics(cancellationToken), 4349 ...program.getSemanticDiagnostics(sourceFile, cancellationToken) 4350 ]; 4351 4352 if (diagnostics.length === 0 && getEmitDeclarations(program.getCompilerOptions())) { 4353 diagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken); 4354 } 4355 4356 if (!diagnostics.length) return undefined; 4357 let emittedFiles: string[] | undefined; 4358 if (!sourceFile && !outFile(options)) { 4359 const emitResult = program.emitBuildInfo(writeFile, cancellationToken); 4360 if (emitResult.diagnostics) diagnostics = [...diagnostics, ...emitResult.diagnostics]; 4361 emittedFiles = emitResult.emittedFiles; 4362 } 4363 return { diagnostics, sourceMaps: undefined, emittedFiles, emitSkipped: true }; 4364 } 4365 4366 /*@internal*/ 4367 export function filterSemanticDiagnostics(diagnostic: readonly Diagnostic[], option: CompilerOptions): readonly Diagnostic[] { 4368 return filter(diagnostic, d => !d.skippedOn || !option[d.skippedOn]); 4369 } 4370 4371 /*@internal*/ 4372 interface CompilerHostLike { 4373 useCaseSensitiveFileNames(): boolean; 4374 getCurrentDirectory(): string; 4375 fileExists(fileName: string): boolean; 4376 readFile(fileName: string): string | undefined; 4377 readDirectory?(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[], depth?: number): string[]; 4378 trace?(s: string): void; 4379 onUnRecoverableConfigFileDiagnostic?: DiagnosticReporter; 4380 } 4381 4382 /* @internal */ 4383 export function parseConfigHostFromCompilerHostLike(host: CompilerHostLike, directoryStructureHost: DirectoryStructureHost = host): ParseConfigFileHost { 4384 return { 4385 fileExists: f => directoryStructureHost.fileExists(f), 4386 readDirectory(root, extensions, excludes, includes, depth) { 4387 Debug.assertIsDefined(directoryStructureHost.readDirectory, "'CompilerHost.readDirectory' must be implemented to correctly process 'projectReferences'"); 4388 return directoryStructureHost.readDirectory(root, extensions, excludes, includes, depth); 4389 }, 4390 readFile: f => directoryStructureHost.readFile(f), 4391 useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), 4392 getCurrentDirectory: () => host.getCurrentDirectory(), 4393 onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic || returnUndefined, 4394 trace: host.trace ? (s) => host.trace!(s) : undefined 4395 }; 4396 } 4397 4398 // For backward compatibility 4399 /** @deprecated */ export interface ResolveProjectReferencePathHost { 4400 fileExists(fileName: string): boolean; 4401 } 4402 4403 /* @internal */ 4404 export function createPrependNodes(projectReferences: readonly ProjectReference[] | undefined, getCommandLine: (ref: ProjectReference, index: number) => ParsedCommandLine | undefined, readFile: (path: string) => string | undefined) { 4405 if (!projectReferences) return emptyArray; 4406 let nodes: InputFiles[] | undefined; 4407 for (let i = 0; i < projectReferences.length; i++) { 4408 const ref = projectReferences[i]; 4409 const resolvedRefOpts = getCommandLine(ref, i); 4410 if (ref.prepend && resolvedRefOpts && resolvedRefOpts.options) { 4411 const out = outFile(resolvedRefOpts.options); 4412 // Upstream project didn't have outFile set -- skip (error will have been issued earlier) 4413 if (!out) continue; 4414 4415 const { jsFilePath, sourceMapFilePath, declarationFilePath, declarationMapPath, buildInfoPath } = getOutputPathsForBundle(resolvedRefOpts.options, /*forceDtsPaths*/ true); 4416 const node = createInputFiles(readFile, jsFilePath!, sourceMapFilePath, declarationFilePath!, declarationMapPath, buildInfoPath); 4417 (nodes || (nodes = [])).push(node); 4418 } 4419 } 4420 return nodes || emptyArray; 4421 } 4422 /** 4423 * Returns the target config filename of a project reference. 4424 * Note: The file might not exist. 4425 */ 4426 export function resolveProjectReferencePath(ref: ProjectReference): ResolvedConfigFileName; 4427 /** @deprecated */ export function resolveProjectReferencePath(host: ResolveProjectReferencePathHost, ref: ProjectReference): ResolvedConfigFileName; 4428 export function resolveProjectReferencePath(hostOrRef: ResolveProjectReferencePathHost | ProjectReference, ref?: ProjectReference): ResolvedConfigFileName { 4429 const passedInRef = ref ? ref : hostOrRef as ProjectReference; 4430 return resolveConfigFileProjectName(passedInRef.path); 4431 } 4432 4433 /* @internal */ 4434 /** 4435 * Returns a DiagnosticMessage if we won't include a resolved module due to its extension. 4436 * The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to. 4437 * This returns a diagnostic even if the module will be an untyped module. 4438 */ 4439 export function getResolutionDiagnostic(options: CompilerOptions, { extension }: ResolvedModuleFull): DiagnosticMessage | undefined { 4440 switch (extension) { 4441 case Extension.Ts: 4442 case Extension.Dts: 4443 case Extension.Ets: 4444 case Extension.Dets: 4445 // These are always allowed. 4446 return undefined; 4447 case Extension.Tsx: 4448 return needJsx(); 4449 case Extension.Jsx: 4450 return needJsx() || needAllowJs(); 4451 case Extension.Js: 4452 return needAllowJs(); 4453 case Extension.Json: 4454 return needResolveJsonModule(); 4455 } 4456 4457 function needJsx() { 4458 return options.jsx ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set; 4459 } 4460 function needAllowJs() { 4461 return getAllowJSCompilerOption(options) || !getStrictOptionValue(options, "noImplicitAny") ? undefined : Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type; 4462 } 4463 function needResolveJsonModule() { 4464 return options.resolveJsonModule ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used; 4465 } 4466 } 4467 4468 function getModuleNames({ imports, moduleAugmentations }: SourceFile): string[] { 4469 const res = imports.map(i => i.text); 4470 for (const aug of moduleAugmentations) { 4471 if (aug.kind === SyntaxKind.StringLiteral) { 4472 res.push(aug.text); 4473 } 4474 // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. 4475 } 4476 return res; 4477 } 4478 4479 /* @internal */ 4480 export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFileImportsList, index: number): StringLiteralLike { 4481 if (index < imports.length) return imports[index]; 4482 let augIndex = imports.length; 4483 for (const aug of moduleAugmentations) { 4484 if (aug.kind === SyntaxKind.StringLiteral) { 4485 if (index === augIndex) return aug; 4486 augIndex++; 4487 } 4488 // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. 4489 } 4490 Debug.fail("should never ask for module name at index higher than possible module name"); 4491 } 4492} 4493