diff --git a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/node16/tsconfig.json b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/node16/tsconfig.json index cdd78a581..07cb31bf3 100644 --- a/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/node16/tsconfig.json +++ b/packages/language-server/test/plugins/typescript/features/diagnostics/fixtures/node16/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "outDir": "./dist", "allowJs": true, "module": "Node16", "moduleResolution": "Node16", diff --git a/packages/svelte-vscode/src/extension.ts b/packages/svelte-vscode/src/extension.ts index f7df8427c..87069dea4 100644 --- a/packages/svelte-vscode/src/extension.ts +++ b/packages/svelte-vscode/src/extension.ts @@ -159,6 +159,8 @@ export function activateSvelteLanguageServer(context: ExtensionContext) { } }; + watchGeneratedSvelteKitTsconfig(restartLS, context); + let ls = createLanguageServer(serverOptions, clientOptions); context.subscriptions.push(ls.start()); @@ -308,6 +310,66 @@ export function activateSvelteLanguageServer(context: ExtensionContext) { }; } +/** + * Watch `.svelte-kit/tsconfig.json` for changes and restart the server then. + * Reason: The user tsconfig extends that generated tsconfig and we don't have + * a general way yet to notice indirect changes of tsconfigs that extend other tsconfigs. + */ +function watchGeneratedSvelteKitTsconfig( + restartLS: (showNotification: boolean) => Promise, + context: ExtensionContext +) { + const configs = new Map(); + const hasConfigChanged = async (path: string) => { + try { + const newConfig = (await workspace.fs.readFile(Uri.parse(path))).toString(); + if (newConfig !== configs.get(path)) { + configs.set(path, newConfig); + return true; + } else { + return false; + } + } catch (e) { + return false; + } + }; + // best-guess init: + workspace.workspaceFolders?.forEach((folder) => + hasConfigChanged(folder.uri.path + '/.svelte-kit/tsconfig.json') + ); + + // Needs to be two watchers because of the limitation mentioned in the createFileSystemWatcher docs + const folderWatcher = workspace.createFileSystemWatcher('**/.svelte-kit', false, true, false); + const configWatcher = workspace.createFileSystemWatcher( + '**/.svelte-kit/tsconfig.json', + true, + false, + true + ); + // This only works with one folder getting deleted at the same time, but the chance + // of people deleting and recreating multiple .svelte-kit folders at once is near zero + let timeout: any; + folderWatcher.onDidCreate(async (dir: Uri) => { + clearTimeout(timeout); + if (await hasConfigChanged(dir.path + '/tsconfig.json')) { + restartLS(false); + } + }); + folderWatcher.onDidDelete((file: Uri) => { + timeout = setTimeout(() => { + // Only delete config, don't restart - create likely follows soon which will do this. + // Do this inside a timeout to ensure it's not a recreation of the folder. + configs.delete(file.path + '/tsconfig.json'); + }, 5000); + }); + configWatcher.onDidChange(async (file: Uri) => { + if (await hasConfigChanged(file.path)) { + restartLS(false); + } + }); + context.subscriptions.push(folderWatcher); +} + function addDidChangeTextDocumentListener(getLS: () => LanguageClient) { // Only Svelte file changes are automatically notified through the inbuilt LSP // because the extension says it's only responsible for Svelte files.