1
- import { dirname } from 'path' ;
1
+ import { dirname , join } from 'path' ;
2
2
import ts from 'typescript' ;
3
3
import { TextDocumentContentChangeEvent } from 'vscode-languageserver' ;
4
4
import { Document , DocumentManager } from '../../lib/documents' ;
@@ -19,8 +19,10 @@ import {
19
19
LanguageServiceContainer ,
20
20
LanguageServiceDocumentContext
21
21
} from './service' ;
22
+ import { createProjectService } from './serviceCache' ;
22
23
import { GlobalSnapshotsManager , SnapshotManager } from './SnapshotManager' ;
23
24
import { isSubPath } from './utils' ;
25
+ import { FileMap } from '../../lib/documents/fileCollection' ;
24
26
25
27
interface LSAndTSDocResolverOptions {
26
28
notifyExceedSizeLimit ?: ( ) => void ;
@@ -71,6 +73,40 @@ export class LSAndTSDocResolver {
71
73
this . getCanonicalFileName = createGetCanonicalFileName (
72
74
( options ?. tsSystem ?? ts . sys ) . useCaseSensitiveFileNames
73
75
) ;
76
+
77
+ this . tsSystem = this . wrapWithPackageJsonMonitoring ( this . options ?. tsSystem ?? ts . sys ) ;
78
+ this . globalSnapshotsManager = new GlobalSnapshotsManager ( this . tsSystem ) ;
79
+ this . userPreferencesAccessor = { preferences : this . getTsUserPreferences ( ) } ;
80
+ const projectService = createProjectService ( this . tsSystem , this . userPreferencesAccessor ) ;
81
+
82
+ configManager . onChange ( ( ) => {
83
+ const newPreferences = this . getTsUserPreferences ( ) ;
84
+ const autoImportConfigChanged =
85
+ newPreferences . includePackageJsonAutoImports !==
86
+ this . userPreferencesAccessor . preferences . includePackageJsonAutoImports ;
87
+
88
+ this . userPreferencesAccessor . preferences = newPreferences ;
89
+
90
+ if ( autoImportConfigChanged ) {
91
+ forAllServices ( ( service ) => {
92
+ service . onAutoImportProviderSettingsChanged ( ) ;
93
+ } ) ;
94
+ }
95
+ } ) ;
96
+
97
+ this . watchers = new FileMap ( this . tsSystem . useCaseSensitiveFileNames ) ;
98
+ this . lsDocumentContext = {
99
+ ambientTypesSource : this . options ?. isSvelteCheck ? 'svelte-check' : 'svelte2tsx' ,
100
+ createDocument : this . createDocument ,
101
+ transformOnTemplateError : ! this . options ?. isSvelteCheck ,
102
+ globalSnapshotsManager : this . globalSnapshotsManager ,
103
+ notifyExceedSizeLimit : this . options ?. notifyExceedSizeLimit ,
104
+ extendedConfigCache : this . extendedConfigCache ,
105
+ onProjectReloaded : this . options ?. onProjectReloaded ,
106
+ watchTsConfig : ! ! this . options ?. watch ,
107
+ tsSystem : this . tsSystem ,
108
+ projectService : projectService
109
+ } ;
74
110
}
75
111
76
112
/**
@@ -89,26 +125,15 @@ export class LSAndTSDocResolver {
89
125
return document ;
90
126
} ;
91
127
92
- private globalSnapshotsManager = new GlobalSnapshotsManager (
93
- this . lsDocumentContext . tsSystem ,
94
- /* watchPackageJson */ ! ! this . options ?. watch
95
- ) ;
128
+ private tsSystem : ts . System ;
129
+ private globalSnapshotsManager : GlobalSnapshotsManager ;
96
130
private extendedConfigCache = new Map < string , ts . ExtendedConfigCacheEntry > ( ) ;
97
131
private getCanonicalFileName : GetCanonicalFileName ;
98
132
99
- private get lsDocumentContext ( ) : LanguageServiceDocumentContext {
100
- return {
101
- ambientTypesSource : this . options ?. isSvelteCheck ? 'svelte-check' : 'svelte2tsx' ,
102
- createDocument : this . createDocument ,
103
- transformOnTemplateError : ! this . options ?. isSvelteCheck ,
104
- globalSnapshotsManager : this . globalSnapshotsManager ,
105
- notifyExceedSizeLimit : this . options ?. notifyExceedSizeLimit ,
106
- extendedConfigCache : this . extendedConfigCache ,
107
- onProjectReloaded : this . options ?. onProjectReloaded ,
108
- watchTsConfig : ! ! this . options ?. watch ,
109
- tsSystem : this . options ?. tsSystem ?? ts . sys
110
- } ;
111
- }
133
+ private userPreferencesAccessor : { preferences : ts . UserPreferences } ;
134
+ private readonly watchers : FileMap < ts . FileWatcher > ;
135
+
136
+ private lsDocumentContext : LanguageServiceDocumentContext ;
112
137
113
138
async getLSForPath ( path : string ) {
114
139
return ( await this . getTSService ( path ) ) . getService ( ) ;
@@ -251,4 +276,73 @@ export class LSAndTSDocResolver {
251
276
nearestWorkspaceUri ? urlToPath ( nearestWorkspaceUri ) : null
252
277
) ;
253
278
}
279
+
280
+ private getTsUserPreferences ( ) {
281
+ return this . configManager . getTsUserPreferences ( 'typescript' , null ) ;
282
+ }
283
+
284
+ private wrapWithPackageJsonMonitoring ( sys : ts . System ) : ts . System {
285
+ if ( ! sys . watchFile || ! this . options ?. watch ) {
286
+ return sys ;
287
+ }
288
+
289
+ const watchFile = sys . watchFile ;
290
+ return {
291
+ ...sys ,
292
+ readFile : ( path , encoding ) => {
293
+ if ( path . endsWith ( 'package.json' ) && ! this . watchers . has ( path ) ) {
294
+ this . watchers . set (
295
+ path ,
296
+ watchFile ( path , this . onPackageJsonWatchChange . bind ( this ) , 3_000 )
297
+ ) ;
298
+ }
299
+
300
+ return sys . readFile ( path , encoding ) ;
301
+ }
302
+ } ;
303
+ }
304
+
305
+ private onPackageJsonWatchChange ( path : string , onWatchChange : ts . FileWatcherEventKind ) {
306
+ const dir = dirname ( path ) ;
307
+ const projectService = this . lsDocumentContext . projectService ;
308
+ const packageJsonCache = projectService ?. packageJsonCache ;
309
+ const normalizedPath = projectService ?. toPath ( path ) ;
310
+
311
+ if ( onWatchChange === ts . FileWatcherEventKind . Deleted ) {
312
+ this . watchers . get ( path ) ?. close ( ) ;
313
+ this . watchers . delete ( path ) ;
314
+ packageJsonCache ?. delete ( normalizedPath ) ;
315
+ } else {
316
+ packageJsonCache ?. addOrUpdate ( normalizedPath ) ;
317
+ }
318
+
319
+ forAllServices ( ( service ) => {
320
+ service . onPackageJsonChange ( path ) ;
321
+ } ) ;
322
+ if ( ! path . includes ( 'node_modules' ) ) {
323
+ return ;
324
+ }
325
+
326
+ setTimeout ( ( ) => {
327
+ this . updateSnapshotsInDirectory ( dir ) ;
328
+ const realPath =
329
+ this . tsSystem . realpath &&
330
+ this . getCanonicalFileName ( normalizePath ( this . tsSystem . realpath ?.( dir ) ) ) ;
331
+
332
+ // pnpm
333
+ if ( realPath && realPath !== dir ) {
334
+ this . updateSnapshotsInDirectory ( realPath ) ;
335
+ const realPkgPath = join ( realPath , 'package.json' ) ;
336
+ forAllServices ( ( service ) => {
337
+ service . onPackageJsonChange ( realPkgPath ) ;
338
+ } ) ;
339
+ }
340
+ } , 500 ) ;
341
+ }
342
+
343
+ private updateSnapshotsInDirectory ( dir : string ) {
344
+ this . globalSnapshotsManager . getByPrefix ( dir ) . forEach ( ( snapshot ) => {
345
+ this . globalSnapshotsManager . updateTsOrJsFile ( snapshot . filePath ) ;
346
+ } ) ;
347
+ }
254
348
}
0 commit comments