diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index c7911bbd61a3b..2ae3128d54c4e 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -45,7 +45,7 @@ namespace ts { /** * Newly computed visible to outside referencedSet */ - currentAffectedFilesExportedModulesMap?: Readonly | undefined; + currentAffectedFilesExportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined; /** * True if the semantic diagnostics were copied from the old state */ @@ -113,8 +113,10 @@ namespace ts { currentAffectedFilesSignatures: ESMap | undefined; /** * Newly computed visible to outside referencedSet + * We need to store the updates separately in case the in-progress build is cancelled + * and we need to roll back. */ - currentAffectedFilesExportedModulesMap: BuilderState.ComputingExportedModulesMap | undefined; + currentAffectedFilesExportedModulesMap: BuilderState.ManyToManyPathMap | undefined; /** * Already seen affected files */ @@ -212,7 +214,7 @@ namespace ts { const copyLibFileDiagnostics = copyDeclarationFileDiagnostics && !compilerOptions.skipDefaultLibCheck === !oldCompilerOptions!.skipDefaultLibCheck; state.fileInfos.forEach((info, sourceFilePath) => { let oldInfo: Readonly | undefined; - let newReferences: BuilderState.ReferencedSet | undefined; + let newReferences: ReadonlySet | undefined; // if not using old state, every file is changed if (!useOldState || @@ -221,7 +223,7 @@ namespace ts { // versions dont match oldInfo.version !== info.version || // Referenced files changed - !hasSameKeys(newReferences = referencedMap && referencedMap.get(sourceFilePath), oldReferencedMap && oldReferencedMap.get(sourceFilePath)) || + !hasSameKeys(newReferences = referencedMap && referencedMap.getValues(sourceFilePath), oldReferencedMap && oldReferencedMap.getValues(sourceFilePath)) || // Referenced file was deleted in the new program newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState!.fileInfos.has(path))) { // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated @@ -311,7 +313,7 @@ namespace ts { newState.affectedFilesIndex = state.affectedFilesIndex; newState.currentChangedFilePath = state.currentChangedFilePath; newState.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures && new Map(state.currentAffectedFilesSignatures); - newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap && new Map(state.currentAffectedFilesExportedModulesMap); + newState.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap?.clone(); newState.seenAffectedFiles = state.seenAffectedFiles && new Set(state.seenAffectedFiles); newState.cleanedDiagnosticsOfLibFiles = state.cleanedDiagnosticsOfLibFiles; newState.semanticDiagnosticsFromOldState = state.semanticDiagnosticsFromOldState && new Set(state.semanticDiagnosticsFromOldState); @@ -384,7 +386,7 @@ namespace ts { // Get next batch of affected files if (!state.currentAffectedFilesSignatures) state.currentAffectedFilesSignatures = new Map(); if (state.exportedModulesMap) { - if (!state.currentAffectedFilesExportedModulesMap) state.currentAffectedFilesExportedModulesMap = new Map(); + state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap(); } state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap); state.currentChangedFilePath = nextKey.value; @@ -465,7 +467,7 @@ namespace ts { * Handle the dts may change, so they need to be added to pending emit if dts emit is enabled, * Also we need to make sure signature is updated for these files */ - function handleDtsMayChangeOf(state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash) { + function handleDtsMayChangeOf(state: BuilderProgramState, path: Path, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): void { removeSemanticDiagnosticsOf(state, path); if (!state.changedFilesSet.has(path)) { @@ -544,19 +546,19 @@ namespace ts { } Debug.assert(!!state.currentAffectedFilesExportedModulesMap); + const seenFileAndExportsOfFile = new Set(); // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected - forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => - exportedModules && - exportedModules.has(affectedFile.resolvedPath) && + state.currentAffectedFilesExportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn) ); // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected - forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) => - !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it - exportedModules.has(affectedFile.resolvedPath) && + state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => + // If the cache had an updated value, skip + !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) && + !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) && forEachFilesReferencingPath(state, exportedFromPath, seenFileAndExportsOfFile, fn) ); } @@ -564,16 +566,16 @@ namespace ts { /** * Iterate on files referencing referencedPath */ - function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void) { - forEachEntry(state.referencedMap!, (referencesInFile, filePath) => - referencesInFile.has(referencedPath) && forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn) + function forEachFilesReferencingPath(state: BuilderProgramState, referencedPath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void): void { + state.referencedMap!.getKeys(referencedPath)?.forEach(filePath => + forEachFileAndExportsOfFile(state, filePath, seenFileAndExportsOfFile, fn) ); } /** * fn on file and iterate on anything that exports this file */ - function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void) { + function forEachFileAndExportsOfFile(state: BuilderProgramState, filePath: Path, seenFileAndExportsOfFile: Set, fn: (state: BuilderProgramState, filePath: Path) => void): void { if (!tryAddToSet(seenFileAndExportsOfFile, filePath)) { return; } @@ -583,23 +585,20 @@ namespace ts { Debug.assert(!!state.currentAffectedFilesExportedModulesMap); // Go through exported modules from cache first // If exported modules has path, all files referencing file exported from are affected - forEachEntry(state.currentAffectedFilesExportedModulesMap, (exportedModules, exportedFromPath) => - exportedModules && - exportedModules.has(filePath) && + state.currentAffectedFilesExportedModulesMap.getKeys(filePath)?.forEach(exportedFromPath => forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) ); // If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected - forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) => - !state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it - exportedModules.has(filePath) && + state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath => + // If the cache had an updated value, skip + !state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) && + !state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) && forEachFileAndExportsOfFile(state, exportedFromPath, seenFileAndExportsOfFile, fn) ); // Remove diagnostics of files that import this file (without going to exports of referencing files) - - forEachEntry(state.referencedMap!, (referencesInFile, referencingFilePath) => - referencesInFile.has(filePath) && + state.referencedMap!.getKeys(filePath)?.forEach(referencingFilePath => !seenFileAndExportsOfFile.has(referencingFilePath) && // Not already removed diagnostic file fn(state, referencingFilePath) // Dont add to seen since this is not yet done with the export removal ); @@ -756,18 +755,26 @@ namespace ts { if (state.referencedMap) { referencedMap = arrayFrom(state.referencedMap.keys()).sort(compareStringsCaseSensitive).map(key => [ toFileId(key), - toFileIdListId(state.referencedMap!.get(key)!) + toFileIdListId(state.referencedMap!.getValues(key)!) ]); } let exportedModulesMap: ProgramBuildInfoReferencedMap | undefined; if (state.exportedModulesMap) { exportedModulesMap = mapDefined(arrayFrom(state.exportedModulesMap.keys()).sort(compareStringsCaseSensitive), key => { - const newValue = state.currentAffectedFilesExportedModulesMap && state.currentAffectedFilesExportedModulesMap.get(key); + if (state.currentAffectedFilesExportedModulesMap) { + if (state.currentAffectedFilesExportedModulesMap.deletedKeys()?.has(key)) { + return undefined; + } + + const newValue = state.currentAffectedFilesExportedModulesMap.getValues(key); + if (newValue) { + return [toFileId(key), toFileIdListId(newValue)]; + } + } + // Not in temporary cache, use existing value - if (newValue === undefined) return [toFileId(key), toFileIdListId(state.exportedModulesMap!.get(key)!)]; - // Value in cache and has updated value map, use that - else if (newValue) return [toFileId(key), toFileIdListId(newValue)]; + return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)]; }); } @@ -1251,8 +1258,8 @@ namespace ts { const state: ReusableBuilderProgramState = { fileInfos, compilerOptions: program.options ? convertToOptionsWithAbsolutePaths(program.options, toAbsolutePath) : {}, - referencedMap: toMapOfReferencedSet(program.referencedMap), - exportedModulesMap: toMapOfReferencedSet(program.exportedModulesMap), + referencedMap: toManyToManyPathMap(program.referencedMap), + exportedModulesMap: toManyToManyPathMap(program.exportedModulesMap), semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => toFilePath(isNumber(value) ? value : value[0]), value => isNumber(value) ? emptyArray : value[1]), hasReusableDiagnostic: true, affectedFilesPendingEmit: map(program.affectedFilesPendingEmit, value => toFilePath(value[0])), @@ -1300,8 +1307,16 @@ namespace ts { return filePathsSetList![fileIdsListId - 1]; } - function toMapOfReferencedSet(referenceMap: ProgramBuildInfoReferencedMap | undefined): ReadonlyESMap | undefined { - return referenceMap && arrayToMap(referenceMap, value => toFilePath(value[0]), value => toFilePathsSet(value[1])); + function toManyToManyPathMap(referenceMap: ProgramBuildInfoReferencedMap | undefined): BuilderState.ManyToManyPathMap | undefined { + if (!referenceMap) { + return undefined; + } + + const map = BuilderState.createManyToManyPathMap(); + referenceMap.forEach(([fileId, fileIdListId]) => + map.set(toFilePath(fileId), toFilePathsSet(fileIdListId)) + ); + return map; } } diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index c150098fff9fb..4194f1d21be35 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -21,12 +21,12 @@ namespace ts { * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap?: ReadonlyESMap | undefined; + readonly referencedMap?: BuilderState.ReadonlyManyToManyPathMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined */ - readonly exportedModulesMap?: ReadonlyESMap | undefined; + readonly exportedModulesMap?: BuilderState.ReadonlyManyToManyPathMap | undefined; } export interface BuilderState { @@ -39,12 +39,14 @@ namespace ts { * Otherwise undefined * Thus non undefined value indicates, module emit */ - readonly referencedMap: ReadonlyESMap | undefined; + readonly referencedMap: BuilderState.ReadonlyManyToManyPathMap | undefined; /** * Contains the map of exported modules ReferencedSet=exported module files from the file if module emit is enabled * Otherwise undefined + * + * This is equivalent to referencedMap, but for the emitted .d.ts file. */ - readonly exportedModulesMap: ESMap | undefined; + readonly exportedModulesMap: BuilderState.ManyToManyPathMap | undefined; /** * true if file version is used as signature @@ -76,21 +78,106 @@ namespace ts { signature: string | undefined; affectsGlobalScope: boolean | undefined; } - /** - * Referenced files with values for the keys as referenced file's path to be true - */ - export type ReferencedSet = ReadonlySet; + + export interface ReadonlyManyToManyPathMap { + clone(): ManyToManyPathMap; + forEach(action: (v: ReadonlySet, k: Path) => void): void; + getKeys(v: Path): ReadonlySet | undefined; + getValues(k: Path): ReadonlySet | undefined; + hasKey(k: Path): boolean; + keys(): Iterator; + + /** + * The set of arguments to {@link deleteKeys} which have not subsequently + * been arguments to {@link set}. Note that a key does not have to have + * ever been in the map to appear in this set. + */ + deletedKeys(): ReadonlySet | undefined; + } + + export interface ManyToManyPathMap extends ReadonlyManyToManyPathMap { + deleteKey(k: Path): boolean; + set(k: Path, v: ReadonlySet): void; + } + + export function createManyToManyPathMap(): ManyToManyPathMap { + function create(forward: ESMap>, reverse: ESMap>, deleted: Set | undefined): ManyToManyPathMap { + const map: ManyToManyPathMap = { + clone: () => create(new Map(forward), new Map(reverse), deleted && new Set(deleted)), + forEach: fn => forward.forEach(fn), + getKeys: v => reverse.get(v), + getValues: k => forward.get(k), + hasKey: k => forward.has(k), + keys: () => forward.keys(), + + deletedKeys: () => deleted, + deleteKey: k => { + (deleted ||= new Set()).add(k); + + const set = forward.get(k); + if (!set) { + return false; + } + + set.forEach(v => deleteFromMultimap(reverse, v, k)); + forward.delete(k); + return true; + }, + set: (k, vSet) => { + deleted?.delete(k); + + const existingVSet = forward.get(k); + forward.set(k, vSet); + + existingVSet?.forEach(v => { + if (!vSet.has(v)) { + deleteFromMultimap(reverse, v, k); + } + }); + + vSet.forEach(v => { + if (!existingVSet?.has(v)) { + addToMultimap(reverse, v, k); + } + }); + + return map; + }, + }; + + return map; + } + + return create(new Map>(), new Map>(), /*deleted*/ undefined); + } + + function addToMultimap(map: ESMap>, k: K, v: V): void { + let set = map.get(k); + if (!set) { + set = new Set(); + map.set(k, set); + } + set.add(v); + } + + function deleteFromMultimap(map: ESMap>, k: K, v: V, removeEmpty = true): boolean { + const set = map.get(k); + + if (set?.delete(v)) { + if (removeEmpty && !set.size) { + map.delete(k); + } + return true; + } + + return false; + } + /** * Compute the hash to store the shape of the file */ export type ComputeHash = ((data: string) => string) | undefined; - /** - * Exported modules to from declaration emit being computed. - * This can contain false in the affected file path to specify that there are no exported module(types from other modules) for this file - */ - export type ComputingExportedModulesMap = ESMap; - /** * Get the referencedFile from the imported module symbol */ @@ -201,7 +288,7 @@ namespace ts { /** * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed */ - export function canReuseOldState(newReferencedMap: ReadonlyESMap | undefined, oldState: Readonly | undefined) { + export function canReuseOldState(newReferencedMap: ReadonlyManyToManyPathMap | undefined, oldState: Readonly | undefined) { return oldState && !oldState.referencedMap === !newReferencedMap; } @@ -210,8 +297,8 @@ namespace ts { */ export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly, disableUseFileVersionAsSignature?: boolean): BuilderState { const fileInfos = new Map(); - const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? new Map() : undefined; - const exportedModulesMap = referencedMap ? new Map() : undefined; + const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createManyToManyPathMap() : undefined; + const exportedModulesMap = referencedMap ? createManyToManyPathMap() : undefined; const hasCalledUpdateShapeSignature = new Set(); const useOldState = canReuseOldState(referencedMap, oldState); @@ -229,7 +316,7 @@ namespace ts { } // Copy old visible to outside files map if (useOldState) { - const exportedModules = oldState!.exportedModulesMap!.get(sourceFile.resolvedPath); + const exportedModules = oldState!.exportedModulesMap!.getValues(sourceFile.resolvedPath); if (exportedModules) { exportedModulesMap!.set(sourceFile.resolvedPath, exportedModules); } @@ -262,8 +349,8 @@ namespace ts { // Dont need to backup allFiles info since its cache anyway return { fileInfos: new Map(state.fileInfos), - referencedMap: state.referencedMap && new Map(state.referencedMap), - exportedModulesMap: state.exportedModulesMap && new Map(state.exportedModulesMap), + referencedMap: state.referencedMap?.clone(), + exportedModulesMap: state.exportedModulesMap?.clone(), hasCalledUpdateShapeSignature: new Set(state.hasCalledUpdateShapeSignature), useFileVersionAsSignature: state.useFileVersionAsSignature, }; @@ -272,7 +359,7 @@ namespace ts { /** * Gets the files affected by the path from the program */ - export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: ESMap, exportedModulesMapCache?: ComputingExportedModulesMap): readonly SourceFile[] { + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: ESMap, exportedModulesMapCache?: ManyToManyPathMap): readonly SourceFile[] { // Since the operation could be cancelled, the signatures are always stored in the cache // They will be committed once it is safe to use them // eg when calling this api from tsserver, if there is no cancellation of the operation @@ -311,7 +398,7 @@ namespace ts { /** * Returns if the shape of the signature has changed since last emit */ - export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ComputingExportedModulesMap) { + export function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache?: ManyToManyPathMap) { Debug.assert(!!sourceFile); Debug.assert(!exportedModulesMapCache || !!state.exportedModulesMap, "Compute visible to outside map only if visibleToOutsideReferencedMap present in the state"); @@ -348,8 +435,13 @@ namespace ts { latestSignature = sourceFile.version; if (exportedModulesMapCache && latestSignature !== prevSignature) { // All the references in this file are exported - const references = state.referencedMap ? state.referencedMap.get(sourceFile.resolvedPath) : undefined; - exportedModulesMapCache.set(sourceFile.resolvedPath, references || false); + const references = state.referencedMap ? state.referencedMap.getValues(sourceFile.resolvedPath) : undefined; + if (references) { + exportedModulesMapCache.set(sourceFile.resolvedPath, references); + } + else { + exportedModulesMapCache.deleteKey(sourceFile.resolvedPath); + } } } cacheToUpdateSignature.set(sourceFile.resolvedPath, latestSignature); @@ -359,15 +451,20 @@ namespace ts { /** * Coverts the declaration emit result into exported modules map */ - function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ComputingExportedModulesMap) { + function updateExportedModules(sourceFile: SourceFile, exportedModulesFromDeclarationEmit: ExportedModulesFromDeclarationEmit | undefined, exportedModulesMapCache: ManyToManyPathMap) { if (!exportedModulesFromDeclarationEmit) { - exportedModulesMapCache.set(sourceFile.resolvedPath, false); + exportedModulesMapCache.deleteKey(sourceFile.resolvedPath); return; } let exportedModules: Set | undefined; exportedModulesFromDeclarationEmit.forEach(symbol => addExportedModule(getReferencedFileFromImportedModuleSymbol(symbol))); - exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules || false); + if (exportedModules) { + exportedModulesMapCache.set(sourceFile.resolvedPath, exportedModules); + } + else { + exportedModulesMapCache.deleteKey(sourceFile.resolvedPath); + } function addExportedModule(exportedModulePath: Path | undefined) { if (exportedModulePath) { @@ -383,17 +480,11 @@ namespace ts { * Updates the exported modules from cache into state's exported modules map * This should be called whenever it is safe to commit the state of the builder */ - export function updateExportedFilesMapFromCache(state: BuilderState, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + export function updateExportedFilesMapFromCache(state: BuilderState, exportedModulesMapCache: ManyToManyPathMap | undefined) { if (exportedModulesMapCache) { Debug.assert(!!state.exportedModulesMap); - exportedModulesMapCache.forEach((exportedModules, path) => { - if (exportedModules) { - state.exportedModulesMap!.set(path, exportedModules); - } - else { - state.exportedModulesMap!.delete(path); - } - }); + exportedModulesMapCache.deletedKeys()?.forEach(path => state.exportedModulesMap!.deleteKey(path)); + exportedModulesMapCache.forEach((exportedModules, path) => state.exportedModulesMap!.set(path, exportedModules)); } } @@ -419,7 +510,7 @@ namespace ts { const path = queue.pop()!; if (!seenMap.has(path)) { seenMap.add(path); - const references = state.referencedMap.get(path); + const references = state.referencedMap.getValues(path); if (references) { const iterator = references.keys(); for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { @@ -447,9 +538,8 @@ namespace ts { * Gets the files referenced by the the file path */ export function getReferencedByPaths(state: Readonly, referencedFilePath: Path) { - return arrayFrom(mapDefinedIterator(state.referencedMap!.entries(), ([filePath, referencesInFile]) => - referencesInFile.has(referencedFilePath) ? filePath : undefined - )); + const keys = state.referencedMap!.getKeys(referencedFilePath); + return keys ? arrayFrom(keys.keys()) : []; } /** @@ -525,7 +615,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache: ComputingExportedModulesMap | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: ESMap, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, exportedModulesMapCache: ManyToManyPathMap | undefined) { if (isFileAffectingGlobalScope(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); } diff --git a/src/testRunner/unittests/tscWatch/incremental.ts b/src/testRunner/unittests/tscWatch/incremental.ts index aaa58d6791493..147363447fd92 100644 --- a/src/testRunner/unittests/tscWatch/incremental.ts +++ b/src/testRunner/unittests/tscWatch/incremental.ts @@ -181,8 +181,8 @@ namespace ts.tscWatch { configFilePath: config.path }); - assert.equal(state.referencedMap!.size, 0); - assert.equal(state.exportedModulesMap!.size, 0); + assert.equal(arrayFrom(state.referencedMap!.keys()).length, 0); + assert.equal(arrayFrom(state.exportedModulesMap!.keys()).length, 0); assert.equal(state.semanticDiagnosticsPerFile!.size, 3); assert.deepEqual(state.semanticDiagnosticsPerFile!.get(libFile.path as Path), emptyArray);