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