@@ -589,8 +589,11 @@ namespace ts.server {
589
589
) ;
590
590
}
591
591
592
- interface ScriptInfoInNodeModulesWatcher extends FileWatcher {
593
- refCount : number ;
592
+ interface NodeModulesWatcher extends FileWatcher {
593
+ /** How many watchers of this directory were for closed ScriptInfo */
594
+ refreshScriptInfoRefCount : number ;
595
+ /** List of project names whose module specifier cache should be cleared when package.jsons change */
596
+ affectedModuleSpecifierCacheProjects : Set < string > ;
594
597
}
595
598
596
599
function getDetailWatchInfo ( watchType : WatchType , project : Project | NormalizedPath | undefined ) {
@@ -671,7 +674,7 @@ namespace ts.server {
671
674
*/
672
675
/*@internal */
673
676
readonly filenameToScriptInfo = new Map < string , ScriptInfo > ( ) ;
674
- private readonly scriptInfoInNodeModulesWatchers = new Map < string , ScriptInfoInNodeModulesWatcher > ( ) ;
677
+ private readonly nodeModulesWatchers = new Map < string , NodeModulesWatcher > ( ) ;
675
678
/**
676
679
* Contains all the deleted script info's version information so that
677
680
* it does not reset when creating script info again
@@ -2617,57 +2620,102 @@ namespace ts.server {
2617
2620
}
2618
2621
}
2619
2622
2620
- private watchClosedScriptInfoInNodeModules ( dir : Path ) : ScriptInfoInNodeModulesWatcher {
2621
- // Watch only directory
2622
- const existing = this . scriptInfoInNodeModulesWatchers . get ( dir ) ;
2623
- if ( existing ) {
2624
- existing . refCount ++ ;
2625
- return existing ;
2626
- }
2627
-
2628
- const watchDir = dir + "/node_modules" as Path ;
2623
+ private createNodeModulesWatcher ( dir : Path , affectedModuleSpecifierCacheProject ?: Project ) {
2629
2624
const watcher = this . watchFactory . watchDirectory (
2630
- watchDir ,
2625
+ dir ,
2631
2626
fileOrDirectory => {
2632
2627
const fileOrDirectoryPath = removeIgnoredPath ( this . toPath ( fileOrDirectory ) ) ;
2633
2628
if ( ! fileOrDirectoryPath ) return ;
2634
2629
2635
- // Has extension
2636
- Debug . assert ( result . refCount > 0 ) ;
2637
- if ( watchDir === fileOrDirectoryPath ) {
2638
- this . refreshScriptInfosInDirectory ( watchDir ) ;
2630
+ // Clear module specifier cache for any projects whose cache was affected by
2631
+ // dependency package.jsons in this node_modules directory
2632
+ const basename = getBaseFileName ( fileOrDirectoryPath ) ;
2633
+ if ( result . affectedModuleSpecifierCacheProjects . size && (
2634
+ basename === "package.json" || basename === "node_modules"
2635
+ ) ) {
2636
+ result . affectedModuleSpecifierCacheProjects . forEach ( projectName => {
2637
+ this . findProject ( projectName ) ?. getModuleSpecifierCache ( ) ?. clear ( ) ;
2638
+ } ) ;
2639
2639
}
2640
- else {
2641
- const info = this . getScriptInfoForPath ( fileOrDirectoryPath ) ;
2642
- if ( info ) {
2643
- if ( isScriptInfoWatchedFromNodeModules ( info ) ) {
2644
- this . refreshScriptInfo ( info ) ;
2645
- }
2640
+
2641
+ // Refresh closed script info after an npm install
2642
+ if ( result . refreshScriptInfoRefCount ) {
2643
+ if ( dir === fileOrDirectoryPath ) {
2644
+ this . refreshScriptInfosInDirectory ( dir ) ;
2646
2645
}
2647
- // Folder
2648
- else if ( ! hasExtension ( fileOrDirectoryPath ) ) {
2649
- this . refreshScriptInfosInDirectory ( fileOrDirectoryPath ) ;
2646
+ else {
2647
+ const info = this . getScriptInfoForPath ( fileOrDirectoryPath ) ;
2648
+ if ( info ) {
2649
+ if ( isScriptInfoWatchedFromNodeModules ( info ) ) {
2650
+ this . refreshScriptInfo ( info ) ;
2651
+ }
2652
+ }
2653
+ // Folder
2654
+ else if ( ! hasExtension ( fileOrDirectoryPath ) ) {
2655
+ this . refreshScriptInfosInDirectory ( fileOrDirectoryPath ) ;
2656
+ }
2650
2657
}
2651
2658
}
2652
2659
} ,
2653
2660
WatchDirectoryFlags . Recursive ,
2654
2661
this . hostConfiguration . watchOptions ,
2655
2662
WatchType . NodeModulesForClosedScriptInfo
2656
2663
) ;
2657
- const result : ScriptInfoInNodeModulesWatcher = {
2664
+ const result : NodeModulesWatcher = {
2665
+ refreshScriptInfoRefCount : affectedModuleSpecifierCacheProject ? 0 : 1 ,
2666
+ affectedModuleSpecifierCacheProjects : new Set (
2667
+ affectedModuleSpecifierCacheProject
2668
+ ? [ affectedModuleSpecifierCacheProject . getProjectName ( ) ]
2669
+ : emptyArray ) ,
2658
2670
close : ( ) => {
2659
- if ( result . refCount === 1 ) {
2671
+ watcher . close ( ) ;
2672
+ this . nodeModulesWatchers . delete ( dir ) ;
2673
+ } ,
2674
+ } ;
2675
+ this . nodeModulesWatchers . set ( dir , result ) ;
2676
+ return result ;
2677
+ }
2678
+
2679
+ /*@internal */
2680
+ watchPackageJsonsInNodeModules ( dir : Path , project : Project ) : FileWatcher {
2681
+ const existing = this . nodeModulesWatchers . get ( dir ) ;
2682
+ if ( existing ) {
2683
+ existing . affectedModuleSpecifierCacheProjects . add ( project . getProjectName ( ) ) ;
2684
+ return existing ;
2685
+ }
2686
+
2687
+ const watcher = this . createNodeModulesWatcher ( dir , project ) ;
2688
+ return {
2689
+ close : ( ) => {
2690
+ if ( watcher . refreshScriptInfoRefCount === 0 && watcher . affectedModuleSpecifierCacheProjects . size === 1 ) {
2660
2691
watcher . close ( ) ;
2661
- this . scriptInfoInNodeModulesWatchers . delete ( dir ) ;
2662
2692
}
2663
2693
else {
2664
- result . refCount -- ;
2694
+ watcher . affectedModuleSpecifierCacheProjects . delete ( project . getProjectName ( ) ) ;
2695
+ }
2696
+ } ,
2697
+ } ;
2698
+ }
2699
+
2700
+ private watchClosedScriptInfoInNodeModules ( dir : Path ) : FileWatcher {
2701
+ const watchDir = dir + "/node_modules" as Path ;
2702
+ const existing = this . nodeModulesWatchers . get ( watchDir ) ;
2703
+ if ( existing ) {
2704
+ existing . refreshScriptInfoRefCount ++ ;
2705
+ return existing ;
2706
+ }
2707
+
2708
+ const watcher = this . createNodeModulesWatcher ( watchDir ) ;
2709
+ return {
2710
+ close : ( ) => {
2711
+ if ( watcher . refreshScriptInfoRefCount === 1 && watcher . affectedModuleSpecifierCacheProjects . size === 0 ) {
2712
+ watcher . close ( ) ;
2713
+ }
2714
+ else {
2715
+ watcher . refreshScriptInfoRefCount -- ;
2665
2716
}
2666
2717
} ,
2667
- refCount : 1
2668
2718
} ;
2669
- this . scriptInfoInNodeModulesWatchers . set ( dir , result ) ;
2670
- return result ;
2671
2719
}
2672
2720
2673
2721
private getModifiedTime ( info : ScriptInfo ) {
0 commit comments