diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts
index 4aa77d573863d..cc42d82f48247 100644
--- a/src/compiler/moduleSpecifiers.ts
+++ b/src/compiler/moduleSpecifiers.ts
@@ -47,7 +47,7 @@ namespace ts.moduleSpecifiers {
         host: ModuleSpecifierResolutionHost,
         oldImportSpecifier: string,
     ): string | undefined {
-        const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier));
+        const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier), {});
         if (res === oldImportSpecifier) return undefined;
         return res;
     }
@@ -59,9 +59,8 @@ namespace ts.moduleSpecifiers {
         importingSourceFileName: Path,
         toFileName: string,
         host: ModuleSpecifierResolutionHost,
-        preferences: UserPreferences = {},
     ): string {
-        return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferences(preferences, compilerOptions, importingSourceFile));
+        return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferences({}, compilerOptions, importingSourceFile), {});
     }
 
     export function getNodeModulesPackageName(
@@ -69,9 +68,10 @@ namespace ts.moduleSpecifiers {
         importingSourceFileName: Path,
         nodeModulesFileName: string,
         host: ModuleSpecifierResolutionHost,
+        preferences: UserPreferences,
     ): string | undefined {
         const info = getInfo(importingSourceFileName, host);
-        const modulePaths = getAllModulePaths(importingSourceFileName, nodeModulesFileName, host);
+        const modulePaths = getAllModulePaths(importingSourceFileName, nodeModulesFileName, host, preferences);
         return firstDefined(modulePaths,
             modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions, /*packageNameOnly*/ true));
     }
@@ -81,10 +81,11 @@ namespace ts.moduleSpecifiers {
         importingSourceFileName: Path,
         toFileName: string,
         host: ModuleSpecifierResolutionHost,
-        preferences: Preferences
+        preferences: Preferences,
+        userPreferences: UserPreferences,
     ): string {
         const info = getInfo(importingSourceFileName, host);
-        const modulePaths = getAllModulePaths(importingSourceFileName, toFileName, host);
+        const modulePaths = getAllModulePaths(importingSourceFileName, toFileName, host, userPreferences);
         return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions)) ||
             getLocalModuleSpecifier(toFileName, info, compilerOptions, host, preferences);
     }
@@ -106,9 +107,17 @@ namespace ts.moduleSpecifiers {
         if (!moduleSourceFile) {
             return [];
         }
-        const modulePaths = getAllModulePaths(importingSourceFile.path, moduleSourceFile.originalFileName, host);
-        const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
 
+        const cache = host.getModuleSpecifierCache?.();
+        const cached = cache?.get(importingSourceFile.path, moduleSourceFile.path, userPreferences);
+        let modulePaths;
+        if (cached) {
+            if (cached.moduleSpecifiers) return cached.moduleSpecifiers;
+            modulePaths = cached.modulePaths;
+        }
+
+        modulePaths ||= getAllModulePathsWorker(importingSourceFile.path, moduleSourceFile.originalFileName, host);
+        const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
         const existingSpecifier = forEach(modulePaths, modulePath => forEach(
             host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)),
             reason => {
@@ -120,7 +129,11 @@ namespace ts.moduleSpecifiers {
                     undefined;
             }
         ));
-        if (existingSpecifier) return [existingSpecifier];
+        if (existingSpecifier) {
+            const moduleSpecifiers = [existingSpecifier];
+            cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, moduleSpecifiers);
+            return moduleSpecifiers;
+        }
 
         const importedFileIsInNodeModules = some(modulePaths, p => p.isInNodeModules);
 
@@ -138,6 +151,7 @@ namespace ts.moduleSpecifiers {
             if (specifier && modulePath.isRedirect) {
                 // If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",
                 // not "@foo/bar/path/to/file"). No other specifier will be this good, so stop looking.
+                cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, nodeModulesSpecifiers!);
                 return nodeModulesSpecifiers!;
             }
 
@@ -161,9 +175,11 @@ namespace ts.moduleSpecifiers {
             }
         }
 
-        return pathsSpecifiers?.length ? pathsSpecifiers :
+        const moduleSpecifiers = pathsSpecifiers?.length ? pathsSpecifiers :
             nodeModulesSpecifiers?.length ? nodeModulesSpecifiers :
             Debug.checkDefined(relativeSpecifiers);
+        cache?.set(importingSourceFile.path, moduleSourceFile.path, userPreferences, modulePaths, moduleSpecifiers);
+        return moduleSpecifiers;
     }
 
     interface Info {
@@ -329,13 +345,27 @@ namespace ts.moduleSpecifiers {
      * Looks for existing imports that use symlinks to this module.
      * Symlinks will be returned first so they are preferred over the real path.
      */
-    function getAllModulePaths(importingFileName: Path, importedFileName: string, host: ModuleSpecifierResolutionHost): readonly ModulePath[] {
+    function getAllModulePaths(
+        importingFilePath: Path,
+        importedFileName: string,
+        host: ModuleSpecifierResolutionHost,
+        preferences: UserPreferences,
+        importedFilePath = toPath(importedFileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host))
+    ) {
         const cache = host.getModuleSpecifierCache?.();
-        const getCanonicalFileName = hostGetCanonicalFileName(host);
         if (cache) {
-            const cached = cache.get(importingFileName, toPath(importedFileName, host.getCurrentDirectory(), getCanonicalFileName));
-            if (typeof cached === "object") return cached;
+            const cached = cache.get(importingFilePath, importedFilePath, preferences);
+            if (cached?.modulePaths) return cached.modulePaths;
+        }
+        const modulePaths = getAllModulePathsWorker(importingFilePath, importedFileName, host);
+        if (cache) {
+            cache.setModulePaths(importingFilePath, importedFilePath, preferences, modulePaths);
         }
+        return modulePaths;
+    }
+
+    function getAllModulePathsWorker(importingFileName: Path, importedFileName: string, host: ModuleSpecifierResolutionHost): readonly ModulePath[] {
+        const getCanonicalFileName = hostGetCanonicalFileName(host);
         const allFileNames = new Map<string, { path: string, isRedirect: boolean, isInNodeModules: boolean }>();
         let importedFileFromNodeModules = false;
         forEachFileNameOfModule(
@@ -381,9 +411,6 @@ namespace ts.moduleSpecifiers {
             sortedPaths.push(...remainingPaths);
         }
 
-        if (cache) {
-            cache.set(importingFileName, toPath(importedFileName, host.getCurrentDirectory(), getCanonicalFileName), sortedPaths);
-        }
         return sortedPaths;
     }
 
diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts
index 6a9cb08fa7cec..cc58530f46633 100644
--- a/src/compiler/transformers/declarations.ts
+++ b/src/compiler/transformers/declarations.ts
@@ -380,7 +380,6 @@ namespace ts {
                             toPath(outputFilePath, host.getCurrentDirectory(), host.getCanonicalFileName),
                             toPath(declFileName, host.getCurrentDirectory(), host.getCanonicalFileName),
                             host,
-                            /*preferences*/ undefined,
                         );
                         if (!pathIsRelative(specifier)) {
                             // If some compiler option/symlink/whatever allows access to the file containing the ambient module declaration
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 7df907413df06..c367e311a959d 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -8154,10 +8154,19 @@ namespace ts {
         isRedirect: boolean;
     }
 
+    /*@internal*/
+    export interface ResolvedModuleSpecifierInfo {
+        modulePaths: readonly ModulePath[] | undefined;
+        moduleSpecifiers: readonly string[] | undefined;
+        isAutoImportable: boolean | undefined;
+    }
+
     /* @internal */
     export interface ModuleSpecifierCache {
-        get(fromFileName: Path, toFileName: Path): boolean | readonly ModulePath[] | undefined;
-        set(fromFileName: Path, toFileName: Path, moduleSpecifiers: boolean | readonly ModulePath[]): void;
+        get(fromFileName: Path, toFileName: Path, preferences: UserPreferences): Readonly<ResolvedModuleSpecifierInfo> | undefined;
+        set(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[], moduleSpecifiers: readonly string[]): void;
+        setIsAutoImportable(fromFileName: Path, toFileName: Path, preferences: UserPreferences, isAutoImportable: boolean): void;
+        setModulePaths(fromFileName: Path, toFileName: Path, preferences: UserPreferences, modulePaths: readonly ModulePath[]): void;
         clear(): void;
         count(): number;
     }
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index d4c4277aba377..7378656c00115 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -589,8 +589,11 @@ namespace ts.server {
         );
     }
 
-    interface ScriptInfoInNodeModulesWatcher extends FileWatcher {
-        refCount: number;
+    interface NodeModulesWatcher extends FileWatcher {
+        /** How many watchers of this directory were for closed ScriptInfo */
+        refreshScriptInfoRefCount: number;
+        /** List of project names whose module specifier cache should be cleared when package.jsons change */
+        affectedModuleSpecifierCacheProjects?: Set<string>;
     }
 
     function getDetailWatchInfo(watchType: WatchType, project: Project | NormalizedPath | undefined) {
@@ -676,7 +679,7 @@ namespace ts.server {
          */
         /*@internal*/
         readonly filenameToScriptInfo = new Map<string, ScriptInfo>();
-        private readonly scriptInfoInNodeModulesWatchers = new Map<string, ScriptInfoInNodeModulesWatcher>();
+        private readonly nodeModulesWatchers = new Map<string, NodeModulesWatcher>();
         /**
          * Contains all the deleted script info's version information so that
          * it does not reset when creating script info again
@@ -2626,59 +2629,87 @@ namespace ts.server {
             }
         }
 
-        private watchClosedScriptInfoInNodeModules(dir: Path): ScriptInfoInNodeModulesWatcher {
-            // Watch only directory
-            const existing = this.scriptInfoInNodeModulesWatchers.get(dir);
-            if (existing) {
-                existing.refCount++;
-                return existing;
-            }
-
-            const watchDir = dir + "/node_modules" as Path;
+        private createNodeModulesWatcher(dir: Path) {
             const watcher = this.watchFactory.watchDirectory(
-                watchDir,
+                dir,
                 fileOrDirectory => {
                     const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
                     if (!fileOrDirectoryPath) return;
 
-                    // Has extension
-                    Debug.assert(result.refCount > 0);
-                    if (watchDir === fileOrDirectoryPath) {
-                        this.refreshScriptInfosInDirectory(watchDir);
+                    // Clear module specifier cache for any projects whose cache was affected by
+                    // dependency package.jsons in this node_modules directory
+                    const basename = getBaseFileName(fileOrDirectoryPath);
+                    if (result.affectedModuleSpecifierCacheProjects?.size && (
+                        basename === "package.json" || basename === "node_modules"
+                    )) {
+                        result.affectedModuleSpecifierCacheProjects.forEach(projectName => {
+                            this.findProject(projectName)?.getModuleSpecifierCache()?.clear();
+                        });
                     }
-                    else {
-                        const info = this.getScriptInfoForPath(fileOrDirectoryPath);
-                        if (info) {
-                            if (isScriptInfoWatchedFromNodeModules(info)) {
-                                this.refreshScriptInfo(info);
-                            }
+
+                    // Refresh closed script info after an npm install
+                    if (result.refreshScriptInfoRefCount) {
+                        if (dir === fileOrDirectoryPath) {
+                            this.refreshScriptInfosInDirectory(dir);
                         }
-                        // Folder
-                        else if (!hasExtension(fileOrDirectoryPath)) {
-                            this.refreshScriptInfosInDirectory(fileOrDirectoryPath);
+                        else {
+                            const info = this.getScriptInfoForPath(fileOrDirectoryPath);
+                            if (info) {
+                                if (isScriptInfoWatchedFromNodeModules(info)) {
+                                    this.refreshScriptInfo(info);
+                                }
+                            }
+                            // Folder
+                            else if (!hasExtension(fileOrDirectoryPath)) {
+                                this.refreshScriptInfosInDirectory(fileOrDirectoryPath);
+                            }
                         }
                     }
                 },
                 WatchDirectoryFlags.Recursive,
                 this.hostConfiguration.watchOptions,
-                WatchType.NodeModulesForClosedScriptInfo
+                WatchType.NodeModules
             );
-            const result: ScriptInfoInNodeModulesWatcher = {
+            const result: NodeModulesWatcher = {
+                refreshScriptInfoRefCount: 0,
+                affectedModuleSpecifierCacheProjects: undefined,
                 close: () => {
-                    if (result.refCount === 1) {
+                    if (!result.refreshScriptInfoRefCount && !result.affectedModuleSpecifierCacheProjects?.size) {
                         watcher.close();
-                        this.scriptInfoInNodeModulesWatchers.delete(dir);
-                    }
-                    else {
-                        result.refCount--;
+                        this.nodeModulesWatchers.delete(dir);
                     }
                 },
-                refCount: 1
             };
-            this.scriptInfoInNodeModulesWatchers.set(dir, result);
+            this.nodeModulesWatchers.set(dir, result);
             return result;
         }
 
+        /*@internal*/
+        watchPackageJsonsInNodeModules(dir: Path, project: Project): FileWatcher {
+            const watcher = this.nodeModulesWatchers.get(dir) || this.createNodeModulesWatcher(dir);
+            (watcher.affectedModuleSpecifierCacheProjects ||= new Set()).add(project.getProjectName());
+
+            return {
+                close: () => {
+                    watcher.affectedModuleSpecifierCacheProjects?.delete(project.getProjectName());
+                    watcher.close();
+                },
+            };
+        }
+
+        private watchClosedScriptInfoInNodeModules(dir: Path): FileWatcher {
+            const watchDir = dir + "/node_modules" as Path;
+            const watcher = this.nodeModulesWatchers.get(watchDir) || this.createNodeModulesWatcher(watchDir);
+            watcher.refreshScriptInfoRefCount++;
+
+            return {
+                close: () => {
+                    watcher.refreshScriptInfoRefCount--;
+                    watcher.close();
+                },
+            };
+        }
+
         private getModifiedTime(info: ScriptInfo) {
             return (this.host.getModifiedTime!(info.path) || missingFileModifiedTime).getTime();
         }
@@ -2954,7 +2985,11 @@ namespace ts.server {
                     this.logger.info("Format host information updated");
                 }
                 if (args.preferences) {
-                    const { lazyConfiguredProjectsFromExternalProject, includePackageJsonAutoImports } = this.hostConfiguration.preferences;
+                    const {
+                        lazyConfiguredProjectsFromExternalProject,
+                        includePackageJsonAutoImports,
+                    } = this.hostConfiguration.preferences;
+
                     this.hostConfiguration.preferences = { ...this.hostConfiguration.preferences, ...args.preferences };
                     if (lazyConfiguredProjectsFromExternalProject && !this.hostConfiguration.preferences.lazyConfiguredProjectsFromExternalProject) {
                         // Load configured projects for external projects that are pending reload
diff --git a/src/server/project.ts b/src/server/project.ts
index c1fb5cc3ca00d..a812d29eeac92 100644
--- a/src/server/project.ts
+++ b/src/server/project.ts
@@ -254,7 +254,7 @@ namespace ts.server {
         /*@internal*/
         private changedFilesForExportMapCache: Set<Path> | undefined;
         /*@internal*/
-        private moduleSpecifierCache = createModuleSpecifierCache();
+        private moduleSpecifierCache = createModuleSpecifierCache(this);
         /*@internal*/
         private symlinks: SymlinkCache | undefined;
         /*@internal*/
@@ -790,6 +790,7 @@ namespace ts.server {
             this.resolutionCache.clear();
             this.resolutionCache = undefined!;
             this.cachedUnresolvedImportsPerFile = undefined!;
+            this.moduleSpecifierCache = undefined!;
             this.directoryStructureHost = undefined!;
             this.projectErrors = undefined;
 
@@ -1394,6 +1395,7 @@ namespace ts.server {
                     this.cachedUnresolvedImportsPerFile.clear();
                     this.lastCachedUnresolvedImportsList = undefined;
                     this.resolutionCache.clear();
+                    this.moduleSpecifierCache.clear();
                 }
                 this.markAsDirty();
             }
@@ -1735,6 +1737,11 @@ namespace ts.server {
                 this.projectService.openFiles,
                 (_, fileName) => this.projectService.tryGetDefaultProjectForFile(toNormalizedPath(fileName)) === this);
         }
+
+        /*@internal*/
+        watchNodeModulesForPackageJsonChanges(directoryPath: string) {
+            return this.projectService.watchPackageJsonsInNodeModules(this.toPath(directoryPath), this);
+        }
     }
 
     function getUnresolvedImports(program: Program, cachedUnresolvedImportsPerFile: ESMap<Path, readonly string[]>): SortedReadonlyArray<string> {
diff --git a/src/server/watchType.ts b/src/server/watchType.ts
index 8cc5014b0e57b..2b9edafb09870 100644
--- a/src/server/watchType.ts
+++ b/src/server/watchType.ts
@@ -4,17 +4,18 @@ namespace ts {
     export interface WatchTypeRegistry {
         ClosedScriptInfo: "Closed Script info",
         ConfigFileForInferredRoot: "Config file for the inferred project root",
-        NodeModulesForClosedScriptInfo: "node_modules for closed script infos in them",
+        NodeModules: "node_modules for closed script infos and package.jsons affecting module specifier cache",
         MissingSourceMapFile: "Missing source map file",
         NoopConfigFileForInferredRoot: "Noop Config file for the inferred project root",
         MissingGeneratedFile: "Missing generated file",
-        PackageJsonFile: "package.json file for import suggestions"
+        PackageJsonFile: "package.json file for import suggestions",
+        NodeModulesForModuleSpecifierCache: "node_modules for module specifier cache invalidation",
     }
     WatchType.ClosedScriptInfo = "Closed Script info";
     WatchType.ConfigFileForInferredRoot = "Config file for the inferred project root";
-    WatchType.NodeModulesForClosedScriptInfo = "node_modules for closed script infos in them";
+    WatchType.NodeModules = "node_modules for closed script infos and package.jsons affecting module specifier cache";
     WatchType.MissingSourceMapFile = "Missing source map file";
     WatchType.NoopConfigFileForInferredRoot = "Noop Config file for the inferred project root";
     WatchType.MissingGeneratedFile = "Missing generated file";
     WatchType.PackageJsonFile = "package.json file for import suggestions";
-}
\ No newline at end of file
+}
diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts
index a1d5692324ee8..a76e70c7b9fb2 100644
--- a/src/services/codefixes/importFixes.ts
+++ b/src/services/codefixes/importFixes.ts
@@ -62,7 +62,7 @@ namespace ts.codefix {
             const symbolName = getNameForExportedSymbol(exportedSymbol, getEmitScriptTarget(compilerOptions));
             const checker = program.getTypeChecker();
             const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker));
-            const exportInfos = getAllReExportingModules(sourceFile, symbol, moduleSymbol, symbolName, host, program, useAutoImportProvider);
+            const exportInfos = getAllReExportingModules(sourceFile, symbol, moduleSymbol, symbolName, host, program, preferences, useAutoImportProvider);
             const preferTypeOnlyImport = !!usageIsTypeOnly && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error;
             const useRequire = shouldUseRequire(sourceFile, program);
             const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, /*position*/ undefined, preferTypeOnlyImport, useRequire, host, preferences);
@@ -202,7 +202,7 @@ namespace ts.codefix {
         const compilerOptions = program.getCompilerOptions();
         const exportInfos = pathIsBareSpecifier(stripQuotes(moduleSymbol.name))
             ? [getSymbolExportInfoForSymbol(exportedSymbol, moduleSymbol, program, host)]
-            : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true);
+            : getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, preferences, /*useAutoImportProvider*/ true);
         const useRequire = shouldUseRequire(sourceFile, program);
         const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position));
         const fix = Debug.checkDefined(getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences));
@@ -211,7 +211,7 @@ namespace ts.codefix {
 
     function getImportFixForSymbol(sourceFile: SourceFile, exportInfos: readonly SymbolExportInfo[], moduleSymbol: Symbol, symbolName: string, program: Program, position: number | undefined, preferTypeOnlyImport: boolean, useRequire: boolean, host: LanguageServiceHost, preferences: UserPreferences) {
         Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol), "Some exportInfo should match the specified moduleSymbol");
-        return getBestFix(getImportFixes(exportInfos, symbolName, position, preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences), sourceFile, host);
+        return getBestFix(getImportFixes(exportInfos, symbolName, position, preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences), sourceFile, host, preferences);
     }
 
     function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction {
@@ -239,7 +239,7 @@ namespace ts.codefix {
         }
     }
 
-    function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, useAutoImportProvider: boolean): readonly SymbolExportInfo[] {
+    function getAllReExportingModules(importingFile: SourceFile, exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, host: LanguageServiceHost, program: Program, preferences: UserPreferences, useAutoImportProvider: boolean): readonly SymbolExportInfo[] {
         const result: SymbolExportInfo[] = [];
         const compilerOptions = program.getCompilerOptions();
         const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
@@ -267,7 +267,7 @@ namespace ts.codefix {
         return result;
 
         function isImportable(program: Program, moduleFile: SourceFile | undefined, isFromPackageJson: boolean) {
-            return !moduleFile || isImportableFile(program, importingFile, moduleFile, /*packageJsonFilter*/ undefined, getModuleSpecifierResolutionHost(isFromPackageJson), host.getModuleSpecifierCache?.());
+            return !moduleFile || isImportableFile(program, importingFile, moduleFile, preferences, /*packageJsonFilter*/ undefined, getModuleSpecifierResolutionHost(isFromPackageJson), host.getModuleSpecifierCache?.());
         }
     }
 
@@ -277,7 +277,7 @@ namespace ts.codefix {
         host: LanguageServiceHost,
         preferences: UserPreferences
     ): { exportInfo?: SymbolExportInfo, moduleSpecifier: string } | undefined {
-        return getBestFix(getNewImportFixes(program, importingFile, /*position*/ undefined, /*preferTypeOnlyImport*/ false, /*useRequire*/ false, exportInfo, host, preferences), importingFile, host);
+        return getBestFix(getNewImportFixes(program, importingFile, /*position*/ undefined, /*preferTypeOnlyImport*/ false, /*useRequire*/ false, exportInfo, host, preferences), importingFile, host, preferences);
     }
 
     export function getSymbolToExportInfoMap(importingFile: SourceFile, host: LanguageServiceHost, program: Program) {
@@ -523,21 +523,21 @@ namespace ts.codefix {
         const info = errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
             ? getFixesInfoForUMDImport(context, symbolToken)
             : isIdentifier(symbolToken) ? getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider) : undefined;
-        return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.host) };
+        return info && { ...info, fixes: sortFixes(info.fixes, context.sourceFile, context.host, context.preferences) };
     }
 
-    function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, host: LanguageServiceHost): readonly ImportFix[] {
-        const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, host);
+    function sortFixes(fixes: readonly ImportFix[], sourceFile: SourceFile, host: LanguageServiceHost, preferences: UserPreferences): readonly ImportFix[] {
+        const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host);
         return sort(fixes, (a, b) => compareValues(a.kind, b.kind) || compareModuleSpecifiers(a, b, allowsImportingSpecifier));
     }
 
-    function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost): T | undefined {
+    function getBestFix<T extends ImportFix>(fixes: readonly T[], sourceFile: SourceFile, host: LanguageServiceHost, preferences: UserPreferences): T | undefined {
         if (!some(fixes)) return;
         // These will always be placed first if available, and are better than other kinds
         if (fixes[0].kind === ImportFixKind.UseNamespace || fixes[0].kind === ImportFixKind.AddToExisting) {
             return fixes[0];
         }
-        const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, host);
+        const { allowsImportingSpecifier } = createPackageJsonImportFilter(sourceFile, preferences, host);
         return fixes.reduce((best, fix) =>
             compareModuleSpecifiers(fix, best, allowsImportingSpecifier) === Comparison.LessThan ? fix : best
         );
@@ -621,7 +621,7 @@ namespace ts.codefix {
 
         const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && isValidTypeOnlyAliasUseSite(symbolToken);
         const useRequire = shouldUseRequire(sourceFile, program);
-        const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host);
+        const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host, preferences);
         const fixes = arrayFrom(flatMapIterator(exportInfos.entries(), ([_, exportInfos]) =>
             getImportFixes(exportInfos, symbolName, symbolToken.getStart(sourceFile), preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences)));
         return { fixes, symbolName };
@@ -646,19 +646,20 @@ namespace ts.codefix {
         fromFile: SourceFile,
         program: Program,
         useAutoImportProvider: boolean,
-        host: LanguageServiceHost
+        host: LanguageServiceHost,
+        preferences: UserPreferences,
     ): ReadonlyESMap<string, readonly SymbolExportInfo[]> {
         // For each original symbol, keep all re-exports of that symbol together so we can call `getCodeActionsForImport` on the whole group at once.
         // Maps symbol id to info for modules providing that symbol (original export + re-exports).
         const originalSymbolToExportInfos = createMultiMap<SymbolExportInfo>();
-        const packageJsonFilter = createPackageJsonImportFilter(fromFile, host);
+        const packageJsonFilter = createPackageJsonImportFilter(fromFile, preferences, host);
         const moduleSpecifierCache = host.getModuleSpecifierCache?.();
         const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => {
             return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host);
         });
         function addSymbol(moduleSymbol: Symbol, toFile: SourceFile | undefined, exportedSymbol: Symbol, exportKind: ExportKind, program: Program, isFromPackageJson: boolean): void {
             const moduleSpecifierResolutionHost = getModuleSpecifierResolutionHost(isFromPackageJson);
-            if (toFile && isImportableFile(program, fromFile, toFile, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) ||
+            if (toFile && isImportableFile(program, fromFile, toFile, preferences, packageJsonFilter, moduleSpecifierResolutionHost, moduleSpecifierCache) ||
                 !toFile && packageJsonFilter.allowsImportingAmbientModule(moduleSymbol, moduleSpecifierResolutionHost)
             ) {
                 const checker = program.getTypeChecker();
diff --git a/src/services/completions.ts b/src/services/completions.ts
index fb1fb0fcfad52..0bd9594b7b302 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1754,7 +1754,7 @@ namespace ts.Completions {
             const lowerCaseTokenText = previousToken && isIdentifier(previousToken) ? previousToken.text.toLowerCase() : "";
             const exportInfo = codefix.getSymbolToExportInfoMap(sourceFile, host, program);
             const packageJsonAutoImportProvider = host.getPackageJsonAutoImportProvider?.();
-            const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, host);
+            const packageJsonFilter = detailsEntryId ? undefined : createPackageJsonImportFilter(sourceFile, preferences, host);
             exportInfo.forEach((info, key) => {
                 const symbolName = key.substring(0, key.indexOf("|"));
                 if (!detailsEntryId && isStringANonContextualKeyword(symbolName)) return;
@@ -1803,6 +1803,7 @@ namespace ts.Completions {
                     info.isFromPackageJson ? packageJsonAutoImportProvider! : program,
                     sourceFile,
                     moduleFile,
+                    preferences,
                     packageJsonFilter,
                     getModuleSpecifierResolutionHost(info.isFromPackageJson),
                     moduleSpecifierCache);
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 2468ff0836fe5..70e3530b47c64 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -2894,7 +2894,7 @@ namespace ts {
         allowsImportingSpecifier: (moduleSpecifier: string) => boolean;
     }
 
-    export function createPackageJsonImportFilter(fromFile: SourceFile, host: LanguageServiceHost): PackageJsonImportFilter {
+    export function createPackageJsonImportFilter(fromFile: SourceFile, preferences: UserPreferences, host: LanguageServiceHost): PackageJsonImportFilter {
         const packageJsons = (
             (host.getPackageJsonsVisibleToFile && host.getPackageJsonsVisibleToFile(fromFile.fileName)) || getPackageJsonsVisibleToFile(fromFile.fileName, host)
           ).filter(p => p.parseable);
@@ -2980,6 +2980,7 @@ namespace ts {
                 fromFile.path,
                 importedFileName,
                 moduleSpecifierResolutionHost,
+                preferences,
             );
 
             if (!specifier) {
@@ -3275,47 +3276,112 @@ namespace ts {
         }
     }
 
-    export function createModuleSpecifierCache(): ModuleSpecifierCache {
-        let cache: ESMap<Path, boolean | readonly ModulePath[]> | undefined;
-        let importingFileName: Path | undefined;
-        const wrapped: ModuleSpecifierCache = {
-            get(fromFileName, toFileName) {
-                if (!cache || fromFileName !== importingFileName) return undefined;
+    export interface ModuleSpecifierResolutionCacheHost {
+        watchNodeModulesForPackageJsonChanges(directoryPath: string): FileWatcher;
+    }
+
+    export function createModuleSpecifierCache(host: ModuleSpecifierResolutionCacheHost): ModuleSpecifierCache {
+        let containedNodeModulesWatchers: ESMap<string, FileWatcher> | undefined;
+        let cache: ESMap<Path, ResolvedModuleSpecifierInfo> | undefined;
+        let currentKey: string | undefined;
+        const result: ModuleSpecifierCache = {
+            get(fromFileName, toFileName, preferences) {
+                if (!cache || currentKey !== key(fromFileName, preferences)) return undefined;
                 return cache.get(toFileName);
             },
-            set(fromFileName, toFileName, moduleSpecifiers) {
-                if (cache && fromFileName !== importingFileName) {
-                    cache.clear();
+            set(fromFileName, toFileName, preferences, modulePaths, moduleSpecifiers) {
+                ensureCache(fromFileName, preferences).set(toFileName, createInfo(modulePaths, moduleSpecifiers, /*isAutoImportable*/ true));
+
+                // If any module specifiers were generated based off paths in node_modules,
+                // a package.json file in that package was read and is an input to the cached.
+                // Instead of watching each individual package.json file, set up a wildcard
+                // directory watcher for any node_modules referenced and clear the cache when
+                // it sees any changes.
+                if (moduleSpecifiers) {
+                    for (const p of modulePaths) {
+                        if (p.isInNodeModules) {
+                            // No trailing slash
+                            const nodeModulesPath = p.path.substring(0, p.path.indexOf(nodeModulesPathPart) + nodeModulesPathPart.length - 1);
+                            if (!containedNodeModulesWatchers?.has(nodeModulesPath)) {
+                                (containedNodeModulesWatchers ||= new Map()).set(
+                                    nodeModulesPath,
+                                    host.watchNodeModulesForPackageJsonChanges(nodeModulesPath),
+                                );
+                            }
+                        }
+                    }
+                }
+            },
+            setModulePaths(fromFileName, toFileName, preferences, modulePaths) {
+                const cache = ensureCache(fromFileName, preferences);
+                const info = cache.get(toFileName);
+                if (info) {
+                    info.modulePaths = modulePaths;
+                }
+                else {
+                    cache.set(toFileName, createInfo(modulePaths, /*moduleSpecifiers*/ undefined, /*isAutoImportable*/ undefined));
+                }
+            },
+            setIsAutoImportable(fromFileName, toFileName, preferences, isAutoImportable) {
+                const cache = ensureCache(fromFileName, preferences);
+                const info = cache.get(toFileName);
+                if (info) {
+                    info.isAutoImportable = isAutoImportable;
+                }
+                else {
+                    cache.set(toFileName, createInfo(/*modulePaths*/ undefined, /*moduleSpecifiers*/ undefined, isAutoImportable));
                 }
-                importingFileName = fromFileName;
-                (cache ||= new Map()).set(toFileName, moduleSpecifiers);
             },
             clear() {
-                cache = undefined;
-                importingFileName = undefined;
+                containedNodeModulesWatchers?.forEach(watcher => watcher.close());
+                cache?.clear();
+                containedNodeModulesWatchers?.clear();
+                currentKey = undefined;
             },
             count() {
                 return cache ? cache.size : 0;
             }
         };
         if (Debug.isDebugging) {
-            Object.defineProperty(wrapped, "__cache", { get: () => cache });
+            Object.defineProperty(result, "__cache", { get: () => cache });
+        }
+        return result;
+
+        function ensureCache(fromFileName: Path, preferences: UserPreferences) {
+            const newKey = key(fromFileName, preferences);
+            if (cache && (currentKey !== newKey)) {
+                result.clear();
+            }
+            currentKey = newKey;
+            return cache ||= new Map();
+        }
+
+        function key(fromFileName: Path, preferences: UserPreferences) {
+            return `${fromFileName},${preferences.importModuleSpecifierEnding},${preferences.importModuleSpecifierPreference}`;
+        }
+
+        function createInfo(
+            modulePaths: readonly ModulePath[] | undefined,
+            moduleSpecifiers: readonly string[] | undefined,
+            isAutoImportable: boolean | undefined,
+        ): ResolvedModuleSpecifierInfo {
+            return { modulePaths, moduleSpecifiers, isAutoImportable };
         }
-        return wrapped;
     }
 
     export function isImportableFile(
         program: Program,
         from: SourceFile,
         to: SourceFile,
+        preferences: UserPreferences,
         packageJsonFilter: PackageJsonImportFilter | undefined,
         moduleSpecifierResolutionHost: ModuleSpecifierResolutionHost,
         moduleSpecifierCache: ModuleSpecifierCache | undefined,
     ): boolean {
         if (from === to) return false;
-        const cachedResult = moduleSpecifierCache?.get(from.path, to.path);
-        if (cachedResult !== undefined) {
-            return !!cachedResult;
+        const cachedResult = moduleSpecifierCache?.get(from.path, to.path, preferences);
+        if (cachedResult?.isAutoImportable !== undefined) {
+            return cachedResult.isAutoImportable;
         }
 
         const getCanonicalFileName = hostGetCanonicalFileName(moduleSpecifierResolutionHost);
@@ -3335,9 +3401,9 @@ namespace ts {
         );
 
         if (packageJsonFilter) {
-            const isImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost);
-            moduleSpecifierCache?.set(from.path, to.path, isImportable);
-            return isImportable;
+            const isAutoImportable = hasImportablePath && packageJsonFilter.allowsImportingSourceFile(to, moduleSpecifierResolutionHost);
+            moduleSpecifierCache?.setIsAutoImportable(from.path, to.path, preferences, isAutoImportable);
+            return isAutoImportable;
         }
 
         return hasImportablePath;
diff --git a/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts b/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts
index f109cab9d1f6e..fd57438a2691e 100644
--- a/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts
+++ b/src/testRunner/unittests/tsserver/moduleSpecifierCache.ts
@@ -4,23 +4,27 @@ namespace ts.projectSystem {
         content: `{ "dependencies": { "mobx": "*" } }`
     };
     const aTs: File = {
-        path: "/a.ts",
+        path: "/src/a.ts",
         content: "export const foo = 0;",
     };
     const bTs: File = {
-        path: "/b.ts",
+        path: "/src/b.ts",
         content: "foo",
     };
+    const cTs: File = {
+        path: "/src/c.ts",
+        content: "import ",
+    };
     const bSymlink: SymLink = {
-        path: "/b-link.ts",
+        path: "/src/b-link.ts",
         symLink: "./b.ts",
     };
     const tsconfig: File = {
         path: "/tsconfig.json",
-        content: "{}",
+        content: `{ "include": ["src"] }`,
     };
     const ambientDeclaration: File = {
-        path: "/ambient.d.ts",
+        path: "/src/ambient.d.ts",
         content: "declare module 'ambient' {}"
     };
     const mobxDts: File = {
@@ -31,50 +35,111 @@ namespace ts.projectSystem {
     describe("unittests:: tsserver:: moduleSpecifierCache", () => {
         it("caches importability within a file", () => {
             const { moduleSpecifierCache } = setup();
-            assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
+            assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable);
+        });
+
+        it("caches module specifiers within a file", () => {
+            const { moduleSpecifierCache, triggerCompletions } = setup();
+            // Completion at an import statement will calculate and cache module specifiers
+            triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 });
+            const mobxCache = moduleSpecifierCache.get(cTs.path as Path, mobxDts.path as Path, {});
+            assert.deepEqual(mobxCache, {
+                modulePaths: [{
+                    path: mobxDts.path,
+                    isInNodeModules: true,
+                    isRedirect: false
+                }],
+                moduleSpecifiers: ["mobx"],
+                isAutoImportable: true,
+            });
+        });
+
+        it("invalidates module specifiers when changes happen in contained node_modules directories", () => {
+            const { host, moduleSpecifierCache, triggerCompletions } = setup();
+            // Completion at an import statement will calculate and cache module specifiers
+            triggerCompletions({ file: cTs.path, line: 1, offset: cTs.content.length + 1 });
+            checkWatchedDirectories(host, ["/src", "/node_modules"], /*recursive*/ true);
+            host.writeFile("/node_modules/.staging/mobx-12345678/package.json", "{}");
+            host.runQueuedTimeoutCallbacks();
+            assert.equal(moduleSpecifierCache.count(), 0);
         });
 
         it("does not invalidate the cache when new files are added", () => {
             const { host, moduleSpecifierCache } = setup();
             host.writeFile("/src/a2.ts", aTs.content);
             host.runQueuedTimeoutCallbacks();
-            assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
+            assert.isTrue(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, {})?.isAutoImportable);
         });
 
         it("invalidates the cache when symlinks are added or removed", () => {
             const { host, moduleSpecifierCache } = setup();
-            host.renameFile(bSymlink.path, "/b-link2.ts");
+            host.renameFile(bSymlink.path, "/src/b-link2.ts");
             host.runQueuedTimeoutCallbacks();
             assert.equal(moduleSpecifierCache.count(), 0);
         });
 
-        it("invalidates the cache when package.json changes", () => {
+        it("invalidates the cache when local package.json changes", () => {
             const { host, moduleSpecifierCache } = setup();
             host.writeFile("/package.json", `{}`);
             host.runQueuedTimeoutCallbacks();
-            assert.isUndefined(moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path));
+            assert.equal(moduleSpecifierCache.count(), 0);
+        });
+
+        it("invalidates the cache when module resolution settings change", () => {
+            const { host, moduleSpecifierCache } = setup();
+            host.writeFile(tsconfig.path, `{ "compilerOptions": { "moduleResolution": "classic" }, "include": ["src"] }`);
+            host.runQueuedTimeoutCallbacks();
+            assert.equal(moduleSpecifierCache.count(), 0);
+        });
+
+        it("invalidates the cache when user preferences change", () => {
+            const { moduleSpecifierCache, session, triggerCompletions } = setup();
+            const preferences: UserPreferences = { importModuleSpecifierPreference: "project-relative" };
+
+            assert.ok(getWithPreferences({}));
+            executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, { preferences });
+            // Nothing changes yet
+            assert.ok(getWithPreferences({}));
+            assert.isUndefined(getWithPreferences(preferences));
+            // Completions will request (getting nothing) and set the cache with new preferences
+            triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
+            assert.isUndefined(getWithPreferences({}));
+            assert.ok(getWithPreferences(preferences));
+
+            // Test other affecting preference
+            executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, {
+                preferences: { importModuleSpecifierEnding: "js" },
+            });
+            triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
+            assert.isUndefined(getWithPreferences(preferences));
+
+            function getWithPreferences(preferences: UserPreferences) {
+                return moduleSpecifierCache.get(bTs.path as Path, aTs.path as Path, preferences);
+            }
         });
     });
 
     function setup() {
-        const host = createServerHost([aTs, bTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]);
+        const host = createServerHost([aTs, bTs, cTs, bSymlink, ambientDeclaration, tsconfig, packageJson, mobxDts]);
         const session = createSession(host);
-        openFilesForSession([aTs, bTs], session);
+        openFilesForSession([aTs, bTs, cTs], session);
         const projectService = session.getProjectService();
         const project = configuredProjectAt(projectService, 0);
-        triggerCompletions();
-        return { host, project, projectService, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
+        executeSessionRequest<protocol.ConfigureRequest, protocol.ConfigureResponse>(session, protocol.CommandTypes.Configure, {
+            preferences: {
+                includeCompletionsForImportStatements: true,
+                includeCompletionsForModuleExports: true,
+                includeCompletionsWithInsertText: true,
+                includeCompletionsWithSnippetText: true,
+            },
+        });
+        triggerCompletions({ file: bTs.path, line: 1, offset: 3 });
+
+        return { host, project, projectService, session, moduleSpecifierCache: project.getModuleSpecifierCache(), triggerCompletions };
 
-        function triggerCompletions() {
-            const requestLocation: protocol.FileLocationRequestArgs = {
-                file: bTs.path,
-                line: 1,
-                offset: 3,
-            };
+        function triggerCompletions(requestLocation: protocol.FileLocationRequestArgs) {
             executeSessionRequest<protocol.CompletionsRequest, protocol.CompletionInfoResponse>(session, protocol.CommandTypes.CompletionInfo, {
                 ...requestLocation,
-                includeExternalModuleExports: true,
-                prefix: "foo",
             });
         }
     }
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 98f85301f5c3c..d237b81c503e0 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -9954,7 +9954,7 @@ declare namespace ts.server {
         errors: Diagnostic[] | undefined;
     }
     export class ProjectService {
-        private readonly scriptInfoInNodeModulesWatchers;
+        private readonly nodeModulesWatchers;
         /**
          * Contains all the deleted script info's version information so that
          * it does not reset when creating script info again
@@ -10104,6 +10104,7 @@ declare namespace ts.server {
         private createInferredProject;
         getScriptInfo(uncheckedFileName: string): ScriptInfo | undefined;
         private watchClosedScriptInfo;
+        private createNodeModulesWatcher;
         private watchClosedScriptInfoInNodeModules;
         private getModifiedTime;
         private refreshScriptInfo;
diff --git a/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js b/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js
index 8cd0bada0c782..2bd9071b01162 100644
--- a/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js
+++ b/tests/baselines/reference/tsserver/projectErrors/correct-errors-when-resolution-resolves-to-file-that-has-same-ambient-module-and-is-also-module.js
@@ -18,8 +18,8 @@ DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/src 1 u
 Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/src 1 undefined Config: /users/username/projects/myproject/tsconfig.json WatchType: Wild card directory
 Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded
 Starting updateGraphWorker: Project: /users/username/projects/myproject/tsconfig.json
-DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
-Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
+DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined Project: /users/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations
 Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /users/username/projects/myproject/node_modules 1 undefined Project: /users/username/projects/myproject/tsconfig.json WatchType: Failed Lookup Locations
 FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info
diff --git a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js
index 61a0843e82382..419c68f410458 100644
--- a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js
+++ b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-after-installation.js
@@ -222,8 +222,8 @@ Scheduled: /user/username/projects/myproject/tsconfig.json, Cancelled earlier on
 Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
 Running: /user/username/projects/myproject/tsconfig.json
 Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json
-DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
-Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
+DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
 Project '/user/username/projects/myproject/tsconfig.json' (Configured)
 	Files (3)
diff --git a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js
index 8c22a7cd8e362..b17e03d859139 100644
--- a/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js
+++ b/tests/baselines/reference/tsserver/projectErrors/npm-install-when-timeout-occurs-inbetween-installation.js
@@ -245,8 +245,8 @@ Scheduled: /user/username/projects/myproject/tsconfig.json, Cancelled earlier on
 Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
 Running: /user/username/projects/myproject/tsconfig.json
 Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json
-DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
-Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos in them
+DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 3 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
 Project '/user/username/projects/myproject/tsconfig.json' (Configured)
 	Files (3)
diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js
index e7dda323106b0..c257c3c0f2e73 100644
--- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js
+++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built-with-disableSourceOfProjectReferenceRedirect.js
@@ -96,4 +96,6 @@ Open files:
 		Projects: /user/username/projects/myproject/app/src/program/tsconfig.json
 response:{"responseRequired":false}
 request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"}
+DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true}
\ No newline at end of file
diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js
index c76675540b864..6f7e0f4c7773c 100644
--- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js
+++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project-when-built.js
@@ -95,4 +95,6 @@ Open files:
 		Projects: /user/username/projects/myproject/app/src/program/tsconfig.json
 response:{"responseRequired":false}
 request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"}
+DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true}
\ No newline at end of file
diff --git a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js
index c76675540b864..6f7e0f4c7773c 100644
--- a/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js
+++ b/tests/baselines/reference/tsserver/projectReferences/auto-import-with-referenced-project.js
@@ -95,4 +95,6 @@ Open files:
 		Projects: /user/username/projects/myproject/app/src/program/tsconfig.json
 response:{"responseRequired":false}
 request:{"command":"getCodeFixes","arguments":{"file":"/user/username/projects/myproject/app/src/program/index.ts","startLine":1,"startOffset":1,"endLine":1,"endOffset":4,"errorCodes":[2304]},"seq":1,"type":"request"}
+DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 response:{"response":[{"fixName":"import","description":"Import 'foo' from module \"shared\"","changes":[{"fileName":"/user/username/projects/myproject/app/src/program/index.ts","textChanges":[{"start":{"line":1,"offset":1},"end":{"line":1,"offset":1},"newText":"import { foo } from \"shared\";\n\n"}]}]}],"responseRequired":true}
\ No newline at end of file
diff --git a/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js b/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js
index c9833a1189cd4..a8baf59905f81 100644
--- a/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js
+++ b/tests/baselines/reference/tsserver/resolutionCache/npm-install-@types-works.js
@@ -64,8 +64,8 @@ Elapsed:: *ms DirectoryWatcher:: Triggered with /a/b/projects/temp/node_modules/
 Running: /dev/null/inferredProject1*
 Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
 Starting updateGraphWorker: Project: /dev/null/inferredProject1*
-DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos in them
-Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos in them
+DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
+Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /a/b/projects/temp/node_modules 1 undefined WatchType: node_modules for closed script infos and package.jsons affecting module specifier cache
 Finishing updateGraphWorker: Project: /dev/null/inferredProject1* Version: 2 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms
 Project '/dev/null/inferredProject1*' (Inferred)
 	Files (3)