Skip to content

Commit c2466bf

Browse files
authored
(feat) watch tsconfig and extended tsconfig (#1535)
#1529 Steps: 1. use a proxy of extendedConfigCache to monitor referenced extendedConfig 2. watch the tsconfig and the extended config file. 3. if tsconfig is updated, delete the service. 4. if extended tsconfig is updated, delete the services extending the config file 5. update diagnostic and restart service if the deleted one is still used.
1 parent e91eeba commit c2466bf

File tree

9 files changed

+518
-69
lines changed

9 files changed

+518
-69
lines changed

packages/language-server/src/plugins/typescript/LSAndTSDocResolver.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,28 @@ import {
1313
} from './service';
1414
import { GlobalSnapshotsManager, SnapshotManager } from './SnapshotManager';
1515

16-
export class LSAndTSDocResolver {
16+
interface LSAndTSDocResolverOptions {
17+
notifyExceedSizeLimit?: () => void;
18+
/**
19+
* True, if used in the context of svelte-check
20+
*/
21+
isSvelteCheck?: boolean;
22+
1723
/**
18-
*
19-
* @param docManager
20-
* @param workspaceUris
21-
* @param configManager
22-
* @param notifyExceedSizeLimit
23-
* @param isSvelteCheck True, if used in the context of svelte-check
24-
* @param tsconfigPath This should only be set via svelte-check. Makes sure all documents are resolved to that tsconfig. Has to be absolute.
24+
* This should only be set via svelte-check. Makes sure all documents are resolved to that tsconfig. Has to be absolute.
2525
*/
26+
tsconfigPath?: string;
27+
28+
onProjectReloaded?: () => void;
29+
watchTsConfig?: boolean;
30+
}
31+
32+
export class LSAndTSDocResolver {
2633
constructor(
2734
private readonly docManager: DocumentManager,
2835
private readonly workspaceUris: string[],
2936
private readonly configManager: LSConfigManager,
30-
private readonly notifyExceedSizeLimit?: () => void,
31-
private readonly isSvelteCheck = false,
32-
private readonly tsconfigPath?: string
37+
private readonly options?: LSAndTSDocResolverOptions
3338
) {
3439
const handleDocumentChange = (document: Document) => {
3540
// This refreshes the document in the ts language service
@@ -65,15 +70,20 @@ export class LSAndTSDocResolver {
6570
};
6671

6772
private globalSnapshotsManager = new GlobalSnapshotsManager();
73+
private extendedConfigCache = new Map<string, ts.ExtendedConfigCacheEntry>();
6874

6975
private get lsDocumentContext(): LanguageServiceDocumentContext {
7076
return {
71-
ambientTypesSource: this.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
77+
ambientTypesSource: this.options?.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
7278
createDocument: this.createDocument,
7379
useNewTransformation: this.configManager.getConfig().svelte.useNewTransformation,
74-
transformOnTemplateError: !this.isSvelteCheck,
80+
transformOnTemplateError: !this.options?.isSvelteCheck,
7581
globalSnapshotsManager: this.globalSnapshotsManager,
76-
notifyExceedSizeLimit: this.notifyExceedSizeLimit
82+
notifyExceedSizeLimit: this.options?.notifyExceedSizeLimit,
83+
extendedConfigCache: this.extendedConfigCache,
84+
onProjectReloaded: this.options?.onProjectReloaded,
85+
watchTsConfig: !!this.options?.watchTsConfig,
86+
tsSystem: ts.sys
7787
};
7888
}
7989

@@ -157,8 +167,8 @@ export class LSAndTSDocResolver {
157167
}
158168

159169
async getTSService(filePath?: string): Promise<LanguageServiceContainer> {
160-
if (this.tsconfigPath) {
161-
return getServiceForTsconfig(this.tsconfigPath, this.lsDocumentContext);
170+
if (this.options?.tsconfigPath) {
171+
return getServiceForTsconfig(this.options?.tsconfigPath, this.lsDocumentContext);
162172
}
163173
if (!filePath) {
164174
throw new Error('Cannot call getTSService without filePath and without tsconfigPath');

packages/language-server/src/plugins/typescript/SnapshotManager.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { TextDocumentContentChangeEvent } from 'vscode-languageserver';
55
import { normalizePath } from '../../utils';
66
import { EventEmitter } from 'events';
77

8+
type SnapshotChangeHandler = (fileName: string, newDocument: DocumentSnapshot | undefined) => void;
9+
810
/**
911
* Every snapshot corresponds to a unique file on disk.
1012
* A snapshot can be part of multiple projects, but for a given file path
@@ -60,9 +62,13 @@ export class GlobalSnapshotsManager {
6062
}
6163
}
6264

63-
onChange(listener: (fileName: string, newDocument: DocumentSnapshot | undefined) => void) {
65+
onChange(listener: SnapshotChangeHandler) {
6466
this.emitter.on('change', listener);
6567
}
68+
69+
removeChangeListener(listener: SnapshotChangeHandler) {
70+
this.emitter.off('change', listener);
71+
}
6672
}
6773

6874
export interface TsFilesSpec {
@@ -92,18 +98,21 @@ export class SnapshotManager {
9298
private fileSpec: TsFilesSpec,
9399
private workspaceRoot: string
94100
) {
95-
this.globalSnapshotsManager.onChange((fileName, document) => {
96-
// Only delete/update snapshots, don't add new ones,
97-
// as they could be from another TS service and this
98-
// snapshot manager can't reach this file.
99-
// For these, instead wait on a `get` method invocation
100-
// and set them "manually" in the set/update methods.
101-
if (!document) {
102-
this.documents.delete(fileName);
103-
} else if (this.documents.has(fileName)) {
104-
this.documents.set(fileName, document);
105-
}
106-
});
101+
this.onSnapshotChange = this.onSnapshotChange.bind(this);
102+
this.globalSnapshotsManager.onChange(this.onSnapshotChange);
103+
}
104+
105+
private onSnapshotChange(fileName: string, document: DocumentSnapshot | undefined) {
106+
// Only delete/update snapshots, don't add new ones,
107+
// as they could be from another TS service and this
108+
// snapshot manager can't reach this file.
109+
// For these, instead wait on a `get` method invocation
110+
// and set them "manually" in the set/update methods.
111+
if (!document) {
112+
this.documents.delete(fileName);
113+
} else if (this.documents.has(fileName)) {
114+
this.documents.set(fileName, document);
115+
}
107116
}
108117

109118
updateProjectFiles(): void {
@@ -191,6 +200,10 @@ export class SnapshotManager {
191200
);
192201
}
193202
}
203+
204+
dispose() {
205+
this.globalSnapshotsManager.removeChangeListener(this.onSnapshotChange);
206+
}
194207
}
195208

196209
export const ignoredBuildDirectories = ['__sapper__', '.svelte-kit'];

0 commit comments

Comments
 (0)