1/*@internal*/ 2namespace ts { 3 export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, 4 cancellationToken?: CancellationToken, customTransformers?: CustomTransformers, forceDtsEmit?: boolean): EmitOutput { 5 const outputFiles: OutputFile[] = []; 6 const { emitSkipped, diagnostics } = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers, forceDtsEmit); 7 return { outputFiles, emitSkipped, diagnostics }; 8 9 function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { 10 outputFiles.push({ name: fileName, writeByteOrderMark, text }); 11 } 12 } 13 export interface BuilderState { 14 /** 15 * Information of the file eg. its version, signature etc 16 */ 17 fileInfos: ESMap<Path, BuilderState.FileInfo>; 18 /** 19 * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled 20 * Otherwise undefined 21 * Thus non undefined value indicates, module emit 22 */ 23 readonly referencedMap?: BuilderState.ReadonlyManyToManyPathMap | undefined; 24 /** 25 * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled 26 * Otherwise undefined 27 * 28 * This is equivalent to referencedMap, but for the emitted .d.ts file. 29 */ 30 readonly exportedModulesMap?: BuilderState.ManyToManyPathMap | undefined; 31 32 /** 33 * true if file version is used as signature 34 * This helps in delaying the calculation of the d.ts hash as version for the file till reasonable time 35 */ 36 useFileVersionAsSignature?: boolean; 37 /** 38 * Map of files that have already called update signature. 39 * That means hence forth these files are assumed to have 40 * no change in their signature for this version of the program 41 */ 42 hasCalledUpdateShapeSignature?: Set<Path>; 43 /** 44 * Stores signatures before before the update till affected file is commited 45 */ 46 oldSignatures?: ESMap<Path, string | false>; 47 /** 48 * Stores exportedModulesMap before the update till affected file is commited 49 */ 50 oldExportedModulesMap?: ESMap<Path, ReadonlySet<Path> | false>; 51 /** 52 * Cache of all files excluding default library file for the current program 53 */ 54 allFilesExcludingDefaultLibraryFile?: readonly SourceFile[]; 55 /** 56 * Cache of all the file names 57 */ 58 allFileNames?: readonly string[]; 59 } 60 export namespace BuilderState { 61 /** 62 * Information about the source file: Its version and optional signature from last emit 63 */ 64 export interface FileInfo { 65 readonly version: string; 66 signature: string | undefined; 67 affectsGlobalScope: true | undefined; 68 impliedFormat: SourceFile["impliedNodeFormat"]; 69 } 70 71 export interface ReadonlyManyToManyPathMap { 72 getKeys(v: Path): ReadonlySet<Path> | undefined; 73 getValues(k: Path): ReadonlySet<Path> | undefined; 74 keys(): Iterator<Path>; 75 } 76 77 export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap { 78 deleteKey(k: Path): boolean; 79 set(k: Path, v: ReadonlySet<Path>): void; 80 } 81 82 export function createManyToManyPathMap(): ManyToManyPathMap { 83 function create(forward: ESMap<Path, ReadonlySet<Path>>, reverse: ESMap<Path, Set<Path>>, deleted: Set<Path> | undefined): ManyToManyPathMap { 84 const map: ManyToManyPathMap = { 85 getKeys: v => reverse.get(v), 86 getValues: k => forward.get(k), 87 keys: () => forward.keys(), 88 89 deleteKey: k => { 90 (deleted ||= new Set<Path>()).add(k); 91 92 const set = forward.get(k); 93 if (!set) { 94 return false; 95 } 96 97 set.forEach(v => deleteFromMultimap(reverse, v, k)); 98 forward.delete(k); 99 return true; 100 }, 101 set: (k, vSet) => { 102 deleted?.delete(k); 103 104 const existingVSet = forward.get(k); 105 forward.set(k, vSet); 106 107 existingVSet?.forEach(v => { 108 if (!vSet.has(v)) { 109 deleteFromMultimap(reverse, v, k); 110 } 111 }); 112 113 vSet.forEach(v => { 114 if (!existingVSet?.has(v)) { 115 addToMultimap(reverse, v, k); 116 } 117 }); 118 119 return map; 120 }, 121 }; 122 123 return map; 124 } 125 126 return create(new Map<Path, Set<Path>>(), new Map<Path, Set<Path>>(), /*deleted*/ undefined); 127 } 128 129 function addToMultimap<K, V>(map: ESMap<K, Set<V>>, k: K, v: V): void { 130 let set = map.get(k); 131 if (!set) { 132 set = new Set<V>(); 133 map.set(k, set); 134 } 135 set.add(v); 136 } 137 138 function deleteFromMultimap<K, V>(map: ESMap<K, Set<V>>, k: K, v: V): boolean { 139 const set = map.get(k); 140 141 if (set?.delete(v)) { 142 if (!set.size) { 143 map.delete(k); 144 } 145 return true; 146 } 147 148 return false; 149 } 150 151 /** 152 * Compute the hash to store the shape of the file 153 */ 154 export type ComputeHash = ((data: string) => string) | undefined; 155 156 function getReferencedFilesFromImportedModuleSymbol(symbol: Symbol): Path[] { 157 return mapDefined(symbol.declarations, declaration => getSourceFileOfNode(declaration)?.resolvedPath); 158 } 159 160 /** 161 * Get the module source file and all augmenting files from the import name node from file 162 */ 163 function getReferencedFilesFromImportLiteral(checker: TypeChecker, importName: StringLiteralLike): Path[] | undefined { 164 const symbol = checker.getSymbolAtLocation(importName); 165 return symbol && getReferencedFilesFromImportedModuleSymbol(symbol); 166 } 167 168 /** 169 * Gets the path to reference file from file name, it could be resolvedPath if present otherwise path 170 */ 171 function getReferencedFileFromFileName(program: Program, fileName: string, sourceFileDirectory: Path, getCanonicalFileName: GetCanonicalFileName): Path { 172 return toPath(program.getProjectReferenceRedirect(fileName) || fileName, sourceFileDirectory, getCanonicalFileName); 173 } 174 175 /** 176 * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true 177 */ 178 function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Set<Path> | undefined { 179 let referencedFiles: Set<Path> | undefined; 180 181 // We need to use a set here since the code can contain the same import twice, 182 // but that will only be one dependency. 183 // To avoid invernal conversion, the key of the referencedFiles map must be of type Path 184 if (sourceFile.imports && sourceFile.imports.length > 0) { 185 const checker: TypeChecker = program.getTypeChecker(); 186 for (const importName of sourceFile.imports) { 187 const declarationSourceFilePaths = getReferencedFilesFromImportLiteral(checker, importName); 188 declarationSourceFilePaths?.forEach(addReferencedFile); 189 } 190 } 191 192 const sourceFileDirectory = getDirectoryPath(sourceFile.resolvedPath); 193 // Handle triple slash references 194 if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { 195 for (const referencedFile of sourceFile.referencedFiles) { 196 const referencedPath = getReferencedFileFromFileName(program, referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); 197 addReferencedFile(referencedPath); 198 } 199 } 200 201 // Handle type reference directives 202 if (sourceFile.resolvedTypeReferenceDirectiveNames) { 203 sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { 204 if (!resolvedTypeReferenceDirective) { 205 return; 206 } 207 208 const fileName = resolvedTypeReferenceDirective.resolvedFileName!; // TODO: GH#18217 209 const typeFilePath = getReferencedFileFromFileName(program, fileName, sourceFileDirectory, getCanonicalFileName); 210 addReferencedFile(typeFilePath); 211 }); 212 } 213 214 // Add module augmentation as references 215 if (sourceFile.moduleAugmentations.length) { 216 const checker = program.getTypeChecker(); 217 for (const moduleName of sourceFile.moduleAugmentations) { 218 if (!isStringLiteral(moduleName)) continue; 219 const symbol = checker.getSymbolAtLocation(moduleName); 220 if (!symbol) continue; 221 222 // Add any file other than our own as reference 223 addReferenceFromAmbientModule(symbol); 224 } 225 } 226 227 // From ambient modules 228 for (const ambientModule of program.getTypeChecker().getAmbientModules()) { 229 if (ambientModule.declarations && ambientModule.declarations.length > 1) { 230 addReferenceFromAmbientModule(ambientModule); 231 } 232 } 233 234 return referencedFiles; 235 236 function addReferenceFromAmbientModule(symbol: Symbol) { 237 if (!symbol.declarations) { 238 return; 239 } 240 // Add any file other than our own as reference 241 for (const declaration of symbol.declarations) { 242 const declarationSourceFile = getSourceFileOfNode(declaration); 243 if (declarationSourceFile && 244 declarationSourceFile !== sourceFile) { 245 addReferencedFile(declarationSourceFile.resolvedPath); 246 } 247 } 248 } 249 250 function addReferencedFile(referencedPath: Path) { 251 (referencedFiles || (referencedFiles = new Set())).add(referencedPath); 252 } 253 } 254 255 /** 256 * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed 257 */ 258 export function canReuseOldState(newReferencedMap: ReadonlyManyToManyPathMap | undefined, oldState: BuilderState | undefined) { 259 return oldState && !oldState.referencedMap === !newReferencedMap; 260 } 261 262 /** 263 * Creates the state of file references and signature for the new program from oldState if it is safe 264 */ 265 export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly<BuilderState>, disableUseFileVersionAsSignature?: boolean): BuilderState { 266 const fileInfos = new Map<Path, FileInfo>(); 267 const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createManyToManyPathMap() : undefined; 268 const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined; 269 const useOldState = canReuseOldState(referencedMap, oldState); 270 271 // Ensure source files have parent pointers set 272 newProgram.getTypeChecker(); 273 274 // Create the reference map, and set the file infos 275 for (const sourceFile of newProgram.getSourceFiles()) { 276 const version = Debug.checkDefined(sourceFile.version, "Program intended to be used with Builder should have source files with versions set"); 277 const oldUncommittedSignature = useOldState ? oldState!.oldSignatures?.get(sourceFile.resolvedPath) : undefined; 278 const signature = oldUncommittedSignature === undefined ? 279 useOldState ? oldState!.fileInfos.get(sourceFile.resolvedPath)?.signature : undefined : 280 oldUncommittedSignature || undefined; 281 if (referencedMap) { 282 const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName); 283 if (newReferences) { 284 referencedMap.set(sourceFile.resolvedPath, newReferences); 285 } 286 // Copy old visible to outside files map 287 if (useOldState) { 288 const oldUncommittedExportedModules = oldState!.oldExportedModulesMap?.get(sourceFile.resolvedPath); 289 const exportedModules = oldUncommittedExportedModules === undefined ? 290 oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath) : 291 oldUncommittedExportedModules || undefined; 292 if (exportedModules) { 293 exportedModulesMap!.set(sourceFile.resolvedPath, exportedModules); 294 } 295 } 296 } 297 fileInfos.set(sourceFile.resolvedPath, { 298 version, 299 signature, 300 affectsGlobalScope: isFileAffectingGlobalScope(sourceFile) || undefined, 301 impliedFormat: sourceFile.impliedNodeFormat 302 }); 303 } 304 305 return { 306 fileInfos, 307 referencedMap, 308 exportedModulesMap, 309 useFileVersionAsSignature: !disableUseFileVersionAsSignature && !useOldState 310 }; 311 } 312 313 /** 314 * Releases needed properties 315 */ 316 export function releaseCache(state: BuilderState) { 317 state.allFilesExcludingDefaultLibraryFile = undefined; 318 state.allFileNames = undefined; 319 } 320 321 /** 322 * Gets the files affected by the path from the program 323 */ 324 export function getFilesAffectedBy( 325 state: BuilderState, 326 programOfThisState: Program, 327 path: Path, 328 cancellationToken: CancellationToken | undefined, 329 computeHash: ComputeHash, 330 getCanonicalFileName: GetCanonicalFileName, 331 ): readonly SourceFile[] { 332 const result = getFilesAffectedByWithOldState( 333 state, 334 programOfThisState, 335 path, 336 cancellationToken, 337 computeHash, 338 getCanonicalFileName, 339 ); 340 state.oldSignatures?.clear(); 341 state.oldExportedModulesMap?.clear(); 342 return result; 343 } 344 345 export function getFilesAffectedByWithOldState( 346 state: BuilderState, 347 programOfThisState: Program, 348 path: Path, 349 cancellationToken: CancellationToken | undefined, 350 computeHash: ComputeHash, 351 getCanonicalFileName: GetCanonicalFileName, 352 ): readonly SourceFile[] { 353 const sourceFile = programOfThisState.getSourceFileByPath(path); 354 if (!sourceFile) { 355 return emptyArray; 356 } 357 358 if (!updateShapeSignature(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName)) { 359 return [sourceFile]; 360 } 361 362 return (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, cancellationToken, computeHash, getCanonicalFileName); 363 } 364 365 export function updateSignatureOfFile(state: BuilderState, signature: string | undefined, path: Path) { 366 state.fileInfos.get(path)!.signature = signature; 367 (state.hasCalledUpdateShapeSignature ||= new Set()).add(path); 368 } 369 370 /** 371 * Returns if the shape of the signature has changed since last emit 372 */ 373 export function updateShapeSignature( 374 state: BuilderState, 375 programOfThisState: Program, 376 sourceFile: SourceFile, 377 cancellationToken: CancellationToken | undefined, 378 computeHash: ComputeHash, 379 getCanonicalFileName: GetCanonicalFileName, 380 useFileVersionAsSignature = state.useFileVersionAsSignature 381 ) { 382 // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate 383 if (state.hasCalledUpdateShapeSignature?.has(sourceFile.resolvedPath)) return false; 384 385 const info = state.fileInfos.get(sourceFile.resolvedPath)!; 386 const prevSignature = info.signature; 387 let latestSignature: string | undefined; 388 if (!sourceFile.isDeclarationFile && !useFileVersionAsSignature) { 389 programOfThisState.emit( 390 sourceFile, 391 (fileName, text, _writeByteOrderMark, _onError, sourceFiles, data) => { 392 Debug.assert(isDeclarationFileName(fileName), `File extension for signature expected to be dts: Got:: ${fileName}`); 393 latestSignature = computeSignatureWithDiagnostics( 394 sourceFile, 395 text, 396 computeHash, 397 getCanonicalFileName, 398 data, 399 ); 400 if (latestSignature !== prevSignature) { 401 updateExportedModules(state, sourceFile, sourceFiles![0].exportedModulesFromDeclarationEmit); 402 } 403 }, 404 cancellationToken, 405 /*emitOnlyDtsFiles*/ true, 406 /*customTransformers*/ undefined, 407 /*forceDtsEmit*/ true 408 ); 409 } 410 // Default is to use file version as signature 411 if (latestSignature === undefined) { 412 latestSignature = sourceFile.version; 413 if (state.exportedModulesMap && latestSignature !== prevSignature) { 414 (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false); 415 // All the references in this file are exported 416 const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined; 417 if (references) { 418 state.exportedModulesMap.set(sourceFile.resolvedPath, references); 419 } 420 else { 421 state.exportedModulesMap.deleteKey(sourceFile.resolvedPath); 422 } 423 } 424 } 425 (state.oldSignatures ||= new Map()).set(sourceFile.resolvedPath, prevSignature || false); 426 (state.hasCalledUpdateShapeSignature ||= new Set()).add(sourceFile.resolvedPath); 427 info.signature = latestSignature; 428 return latestSignature !== prevSignature; 429 } 430 431 /** 432 * Coverts the declaration emit result into exported modules map 433 */ 434 export function updateExportedModules(state: BuilderState, sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined) { 435 if (!state.exportedModulesMap) return; 436 (state.oldExportedModulesMap ||= new Map()).set(sourceFile.resolvedPath, state.exportedModulesMap.getValues(sourceFile.resolvedPath) || false); 437 if (!exportedModulesFromDeclarationEmit) { 438 state.exportedModulesMap.deleteKey(sourceFile.resolvedPath); 439 return; 440 } 441 442 let exportedModules: Set<Path> | undefined; 443 exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFilesFromImportedModuleSymbol(symbol))); 444 if (exportedModules) { 445 state.exportedModulesMap.set(sourceFile.resolvedPath, exportedModules); 446 } 447 else { 448 state.exportedModulesMap.deleteKey(sourceFile.resolvedPath); 449 } 450 451 function addExportedModule(exportedModulePaths: Path[] | undefined) { 452 if (exportedModulePaths?.length) { 453 if (!exportedModules) { 454 exportedModules = new Set(); 455 } 456 exportedModulePaths.forEach(path => exportedModules!.add(path)); 457 } 458 } 459 } 460 461 /** 462 * Get all the dependencies of the sourceFile 463 */ 464 export function getAllDependencies(state: BuilderState, programOfThisState: Program, sourceFile: SourceFile): readonly string[] { 465 const compilerOptions = programOfThisState.getCompilerOptions(); 466 // With --out or --outFile all outputs go into single file, all files depend on each other 467 if (outFile(compilerOptions)) { 468 return getAllFileNames(state, programOfThisState); 469 } 470 471 // If this is non module emit, or its a global file, it depends on all the source files 472 if (!state.referencedMap || isFileAffectingGlobalScope(sourceFile)) { 473 return getAllFileNames(state, programOfThisState); 474 } 475 476 // Get the references, traversing deep from the referenceMap 477 const seenMap = new Set<Path>(); 478 const queue = [sourceFile.resolvedPath]; 479 while (queue.length) { 480 const path = queue.pop()!; 481 if (!seenMap.has(path)) { 482 seenMap.add(path); 483 const references = state.referencedMap.getValues(path); 484 if (references) { 485 const iterator = references.keys(); 486 for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { 487 queue.push(iterResult.value); 488 } 489 } 490 } 491 } 492 493 return arrayFrom(mapDefinedIterator(seenMap.keys(), path => programOfThisState.getSourceFileByPath(path)?.fileName ?? path)); 494 } 495 496 /** 497 * Gets the names of all files from the program 498 */ 499 function getAllFileNames(state: BuilderState, programOfThisState: Program): readonly string[] { 500 if (!state.allFileNames) { 501 const sourceFiles = programOfThisState.getSourceFiles(); 502 state.allFileNames = sourceFiles === emptyArray ? emptyArray : sourceFiles.map(file => file.fileName); 503 } 504 return state.allFileNames; 505 } 506 507 /** 508 * Gets the files referenced by the the file path 509 */ 510 export function getReferencedByPaths(state: Readonly<BuilderState>, referencedFilePath: Path) { 511 const keys = state.referencedMap!.getKeys(referencedFilePath); 512 return keys ? arrayFrom(keys.keys()) : []; 513 } 514 515 /** 516 * For script files that contains only ambient external modules, although they are not actually external module files, 517 * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, 518 * there are no point to rebuild all script files if these special files have changed. However, if any statement 519 * in the file is not ambient external module, we treat it as a regular script file. 520 */ 521 function containsOnlyAmbientModules(sourceFile: SourceFile) { 522 for (const statement of sourceFile.statements) { 523 if (!isModuleWithStringLiteralName(statement)) { 524 return false; 525 } 526 } 527 return true; 528 } 529 530 /** 531 * Return true if file contains anything that augments to global scope we need to build them as if 532 * they are global files as well as module 533 */ 534 function containsGlobalScopeAugmentation(sourceFile: SourceFile) { 535 return some(sourceFile.moduleAugmentations, augmentation => isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration)); 536 } 537 538 /** 539 * Return true if the file will invalidate all files because it affectes global scope 540 */ 541 function isFileAffectingGlobalScope(sourceFile: SourceFile) { 542 return containsGlobalScopeAugmentation(sourceFile) || 543 !isExternalOrCommonJsModule(sourceFile) && !isJsonSourceFile(sourceFile) && !containsOnlyAmbientModules(sourceFile); 544 } 545 546 /** 547 * Gets all files of the program excluding the default library file 548 */ 549 export function getAllFilesExcludingDefaultLibraryFile(state: BuilderState, programOfThisState: Program, firstSourceFile: SourceFile | undefined): readonly SourceFile[] { 550 // Use cached result 551 if (state.allFilesExcludingDefaultLibraryFile) { 552 return state.allFilesExcludingDefaultLibraryFile; 553 } 554 555 let result: SourceFile[] | undefined; 556 if (firstSourceFile) addSourceFile(firstSourceFile); 557 for (const sourceFile of programOfThisState.getSourceFiles()) { 558 if (sourceFile !== firstSourceFile) { 559 addSourceFile(sourceFile); 560 } 561 } 562 state.allFilesExcludingDefaultLibraryFile = result || emptyArray; 563 return state.allFilesExcludingDefaultLibraryFile; 564 565 function addSourceFile(sourceFile: SourceFile) { 566 if (!programOfThisState.isSourceFileDefaultLibrary(sourceFile)) { 567 (result || (result = [])).push(sourceFile); 568 } 569 } 570 } 571 572 /** 573 * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed 574 */ 575 function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { 576 const compilerOptions = programOfThisState.getCompilerOptions(); 577 // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, 578 // so returning the file itself is good enough. 579 if (compilerOptions && outFile(compilerOptions)) { 580 return [sourceFileWithUpdatedShape]; 581 } 582 return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); 583 } 584 585 /** 586 * When program emits modular code, gets the files affected by the sourceFile whose shape has changed 587 */ 588 function getFilesAffectedByUpdatedShapeWhenModuleEmit( 589 state: BuilderState, 590 programOfThisState: Program, 591 sourceFileWithUpdatedShape: SourceFile, 592 cancellationToken: CancellationToken | undefined, 593 computeHash: ComputeHash, 594 getCanonicalFileName: GetCanonicalFileName, 595 ) { 596 if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { 597 return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); 598 } 599 600 const compilerOptions = programOfThisState.getCompilerOptions(); 601 if (compilerOptions && (compilerOptions.isolatedModules || outFile(compilerOptions))) { 602 return [sourceFileWithUpdatedShape]; 603 } 604 605 // Now we need to if each file in the referencedBy list has a shape change as well. 606 // Because if so, its own referencedBy files need to be saved as well to make the 607 // emitting result consistent with files on disk. 608 const seenFileNamesMap = new Map<Path, SourceFile>(); 609 610 // Start with the paths this file was referenced by 611 seenFileNamesMap.set(sourceFileWithUpdatedShape.resolvedPath, sourceFileWithUpdatedShape); 612 const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.resolvedPath); 613 while (queue.length > 0) { 614 const currentPath = queue.pop()!; 615 if (!seenFileNamesMap.has(currentPath)) { 616 const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath)!; 617 seenFileNamesMap.set(currentPath, currentSourceFile); 618 if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cancellationToken, computeHash, getCanonicalFileName)) { 619 queue.push(...getReferencedByPaths(state, currentSourceFile.resolvedPath)); 620 } 621 } 622 } 623 624 // Return array of values that needs emit 625 return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value)); 626 } 627 } 628} 629