Skip to content

Commit

Permalink
(feat) watch tsconfig and extended tsconfig (#1535)
Browse files Browse the repository at this point in the history
#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.
  • Loading branch information
jasonlyu123 committed Jun 26, 2022
1 parent e91eeba commit c2466bf
Show file tree
Hide file tree
Showing 9 changed files with 518 additions and 69 deletions.
Expand Up @@ -13,23 +13,28 @@ import {
} from './service';
import { GlobalSnapshotsManager, SnapshotManager } from './SnapshotManager';

export class LSAndTSDocResolver {
interface LSAndTSDocResolverOptions {
notifyExceedSizeLimit?: () => void;
/**
* True, if used in the context of svelte-check
*/
isSvelteCheck?: boolean;

/**
*
* @param docManager
* @param workspaceUris
* @param configManager
* @param notifyExceedSizeLimit
* @param isSvelteCheck True, if used in the context of svelte-check
* @param tsconfigPath This should only be set via svelte-check. Makes sure all documents are resolved to that tsconfig. Has to be absolute.
* This should only be set via svelte-check. Makes sure all documents are resolved to that tsconfig. Has to be absolute.
*/
tsconfigPath?: string;

onProjectReloaded?: () => void;
watchTsConfig?: boolean;
}

export class LSAndTSDocResolver {
constructor(
private readonly docManager: DocumentManager,
private readonly workspaceUris: string[],
private readonly configManager: LSConfigManager,
private readonly notifyExceedSizeLimit?: () => void,
private readonly isSvelteCheck = false,
private readonly tsconfigPath?: string
private readonly options?: LSAndTSDocResolverOptions
) {
const handleDocumentChange = (document: Document) => {
// This refreshes the document in the ts language service
Expand Down Expand Up @@ -65,15 +70,20 @@ export class LSAndTSDocResolver {
};

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

private get lsDocumentContext(): LanguageServiceDocumentContext {
return {
ambientTypesSource: this.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
ambientTypesSource: this.options?.isSvelteCheck ? 'svelte-check' : 'svelte2tsx',
createDocument: this.createDocument,
useNewTransformation: this.configManager.getConfig().svelte.useNewTransformation,
transformOnTemplateError: !this.isSvelteCheck,
transformOnTemplateError: !this.options?.isSvelteCheck,
globalSnapshotsManager: this.globalSnapshotsManager,
notifyExceedSizeLimit: this.notifyExceedSizeLimit
notifyExceedSizeLimit: this.options?.notifyExceedSizeLimit,
extendedConfigCache: this.extendedConfigCache,
onProjectReloaded: this.options?.onProjectReloaded,
watchTsConfig: !!this.options?.watchTsConfig,
tsSystem: ts.sys
};
}

Expand Down Expand Up @@ -157,8 +167,8 @@ export class LSAndTSDocResolver {
}

async getTSService(filePath?: string): Promise<LanguageServiceContainer> {
if (this.tsconfigPath) {
return getServiceForTsconfig(this.tsconfigPath, this.lsDocumentContext);
if (this.options?.tsconfigPath) {
return getServiceForTsconfig(this.options?.tsconfigPath, this.lsDocumentContext);
}
if (!filePath) {
throw new Error('Cannot call getTSService without filePath and without tsconfigPath');
Expand Down
39 changes: 26 additions & 13 deletions packages/language-server/src/plugins/typescript/SnapshotManager.ts
Expand Up @@ -5,6 +5,8 @@ import { TextDocumentContentChangeEvent } from 'vscode-languageserver';
import { normalizePath } from '../../utils';
import { EventEmitter } from 'events';

type SnapshotChangeHandler = (fileName: string, newDocument: DocumentSnapshot | undefined) => void;

/**
* Every snapshot corresponds to a unique file on disk.
* A snapshot can be part of multiple projects, but for a given file path
Expand Down Expand Up @@ -60,9 +62,13 @@ export class GlobalSnapshotsManager {
}
}

onChange(listener: (fileName: string, newDocument: DocumentSnapshot | undefined) => void) {
onChange(listener: SnapshotChangeHandler) {
this.emitter.on('change', listener);
}

removeChangeListener(listener: SnapshotChangeHandler) {
this.emitter.off('change', listener);
}
}

export interface TsFilesSpec {
Expand Down Expand Up @@ -92,18 +98,21 @@ export class SnapshotManager {
private fileSpec: TsFilesSpec,
private workspaceRoot: string
) {
this.globalSnapshotsManager.onChange((fileName, document) => {
// Only delete/update snapshots, don't add new ones,
// as they could be from another TS service and this
// snapshot manager can't reach this file.
// For these, instead wait on a `get` method invocation
// and set them "manually" in the set/update methods.
if (!document) {
this.documents.delete(fileName);
} else if (this.documents.has(fileName)) {
this.documents.set(fileName, document);
}
});
this.onSnapshotChange = this.onSnapshotChange.bind(this);
this.globalSnapshotsManager.onChange(this.onSnapshotChange);
}

private onSnapshotChange(fileName: string, document: DocumentSnapshot | undefined) {
// Only delete/update snapshots, don't add new ones,
// as they could be from another TS service and this
// snapshot manager can't reach this file.
// For these, instead wait on a `get` method invocation
// and set them "manually" in the set/update methods.
if (!document) {
this.documents.delete(fileName);
} else if (this.documents.has(fileName)) {
this.documents.set(fileName, document);
}
}

updateProjectFiles(): void {
Expand Down Expand Up @@ -191,6 +200,10 @@ export class SnapshotManager {
);
}
}

dispose() {
this.globalSnapshotsManager.removeChangeListener(this.onSnapshotChange);
}
}

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

0 comments on commit c2466bf

Please sign in to comment.