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