From 2648f6ab09e3176c7da2c07c54066d3a3433a298 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 6 Oct 2022 13:40:21 -0700 Subject: [PATCH] Plugins in project were adding up after every config file reload (#51087) * Add test where current plugins dont get reset when reloading config file * Reset loaded plugins when reloading configured project and closing project --- src/server/project.ts | 5 +- src/testRunner/unittests/tsserver/plugins.ts | 45 ++++ .../reference/api/tsserverlibrary.d.ts | 1 - ...-external-files-with-config-file-reload.js | 195 ++++++++++++++++++ 4 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js diff --git a/src/server/project.ts b/src/server/project.ts index e3dc2b77369d6..44ec0bff482c3 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -143,7 +143,8 @@ namespace ts.server { private missingFilesMap: ESMap | undefined; private generatedFilesMap: GeneratedFileWatcherMap | undefined; - private plugins: PluginModuleWithName[] = []; + /*@internal*/ + protected readonly plugins: PluginModuleWithName[] = []; /*@internal*/ /** @@ -845,6 +846,7 @@ namespace ts.server { this.directoryStructureHost = undefined!; this.exportMapCache = undefined; this.projectErrors = undefined; + this.plugins.length = 0; // Clean up file watchers waiting for missing files if (this.missingFilesMap) { @@ -2520,6 +2522,7 @@ namespace ts.server { /*@internal*/ enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: ESMap | undefined): void { + this.plugins.length = 0; if (!options.plugins?.length && !this.projectService.globalPlugins.length) return; const host = this.projectService.host; if (!host.require && !host.importPlugin) { diff --git a/src/testRunner/unittests/tsserver/plugins.ts b/src/testRunner/unittests/tsserver/plugins.ts index 5d46dd203f9db..0db19f7478b19 100644 --- a/src/testRunner/unittests/tsserver/plugins.ts +++ b/src/testRunner/unittests/tsserver/plugins.ts @@ -100,5 +100,50 @@ namespace ts.projectSystem { }; assert.deepEqual(resp, expectedResp); }); + + it("gets external files with config file reload", () => { + const aTs: File = { path: `${tscWatch.projectRoot}/a.ts`, content: `export const x = 10;` }; + const tsconfig: File = { + path: `${tscWatch.projectRoot}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { + plugins: [{ name: "some-plugin" }] + } + }) + }; + + const externalFiles: MapLike = { + "some-plugin": ["someFile.txt"], + "some-other-plugin": ["someOtherFile.txt"], + }; + + const host = createServerHost([aTs, tsconfig, libFile]); + host.require = (_initialPath, moduleName) => { + session.logger.logs.push(`Require:: ${moduleName}`); + return { + module: (): server.PluginModule => { + session.logger.logs.push(`PluginFactory Invoke`); + return { + create: Harness.LanguageService.makeDefaultProxy, + getExternalFiles: () => externalFiles[moduleName] + }; + }, + error: undefined + }; + }; + const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) }); + openFilesForSession([aTs], session); + session.logger.logs.push(`ExternalFiles:: ${JSON.stringify(session.getProjectService().configuredProjects.get(tsconfig.path)!.getExternalFiles())}`); + + host.writeFile(tsconfig.path, JSON.stringify({ + compilerOptions: { + plugins: [{ name: "some-other-plugin" }] + } + })); + host.runQueuedTimeoutCallbacks(); + session.logger.logs.push(`ExternalFiles:: ${JSON.stringify(session.getProjectService().configuredProjects.get(tsconfig.path)!.getExternalFiles())}`); + + baselineTsserverLogs("plugins", "gets external files with config file reload", session); + }); }); } \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4e1b115da8cb6..76ad8dbfe7ecb 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -10084,7 +10084,6 @@ declare namespace ts.server { private externalFiles; private missingFilesMap; private generatedFilesMap; - private plugins; protected languageService: LanguageService; languageServiceEnabled: boolean; readonly trace?: (s: string) => void; diff --git a/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js b/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js new file mode 100644 index 0000000000000..b71b682d1ea5a --- /dev/null +++ b/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js @@ -0,0 +1,195 @@ +Info 0 [00:00:21.000] Provided types map file "/a/lib/typesMap.json" doesn't exist +Info 1 [00:00:22.000] request: + { + "seq": 0, + "type": "request", + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/a.ts" + } + } +Before request +//// [/user/username/projects/myproject/a.ts] +export const x = 10; + +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"plugins":[{"name":"some-plugin"}]}} + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +PolledWatches:: + +FsWatches:: + +FsWatchesRecursive:: + +Info 2 [00:00:23.000] Search path: /user/username/projects/myproject +Info 3 [00:00:24.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info 4 [00:00:25.000] Creating configuration project /user/username/projects/myproject/tsconfig.json +Info 5 [00:00:26.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info 6 [00:00:27.000] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts" + ], + "options": { + "plugins": [ + { + "name": "some-plugin" + } + ], + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info 7 [00:00:28.000] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info 8 [00:00:29.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info 9 [00:00:30.000] Enabling plugin some-plugin from candidate paths: /a/lib/tsc.js/../../.. +Info 10 [00:00:31.000] Loading some-plugin from /a/lib/tsc.js/../../.. (resolved to /a/lib/tsc.js/../../../node_modules) +Require:: some-plugin +PluginFactory Invoke +Info 11 [00:00:32.000] Plugin validation succeeded +Info 12 [00:00:33.000] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info 13 [00:00:34.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info +Info 14 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/somefile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 15 [00:00:36.000] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info 16 [00:00:37.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info 17 [00:00:38.000] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 18 [00:00:39.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 19 [00:00:40.000] Files (2) + /a/lib/lib.d.ts + /user/username/projects/myproject/a.ts + + + ../../../../a/lib/lib.d.ts + Default library for target 'es3' + a.ts + Matched by default include pattern '**/*' + +Info 20 [00:00:41.000] ----------------------------------------------- +Info 21 [00:00:42.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 21 [00:00:43.000] Files (2) + +Info 21 [00:00:44.000] ----------------------------------------------- +Info 21 [00:00:45.000] Open files: +Info 21 [00:00:46.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 21 [00:00:47.000] Projects: /user/username/projects/myproject/tsconfig.json +After request + +PolledWatches:: +/user/username/projects/myproject/somefile.txt: + {"pollingInterval":500} +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Info 21 [00:00:48.000] response: + { + "responseRequired": false + } +ExternalFiles:: ["someFile.txt"] +Info 22 [00:00:52.000] FileWatcher:: Triggered with /user/username/projects/myproject/tsconfig.json 1:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info 23 [00:00:53.000] Scheduled: /user/username/projects/myproject/tsconfig.json +Info 24 [00:00:54.000] Scheduled: *ensureProjectForOpenFiles* +Info 25 [00:00:55.000] Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/tsconfig.json 1:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Before running timeout callbacks +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"plugins":[{"name":"some-other-plugin"}]}} + + +PolledWatches:: +/user/username/projects/myproject/somefile.txt: + {"pollingInterval":500} +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Info 26 [00:00:56.000] Running: /user/username/projects/myproject/tsconfig.json +Info 27 [00:00:57.000] Reloading configured project /user/username/projects/myproject/tsconfig.json +Info 28 [00:00:58.000] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts" + ], + "options": { + "plugins": [ + { + "name": "some-other-plugin" + } + ], + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info 29 [00:00:59.000] Enabling plugin some-other-plugin from candidate paths: /a/lib/tsc.js/../../.. +Info 30 [00:01:00.000] Loading some-other-plugin from /a/lib/tsc.js/../../.. (resolved to /a/lib/tsc.js/../../../node_modules) +Require:: some-other-plugin +PluginFactory Invoke +Info 31 [00:01:01.000] Plugin validation succeeded +Info 32 [00:01:02.000] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info 33 [00:01:03.000] FileWatcher:: Close:: WatchInfo: /user/username/projects/myproject/somefile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 34 [00:01:04.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/someotherfile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 35 [00:01:05.000] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 2 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 36 [00:01:06.000] Different program with same set of files +Info 37 [00:01:07.000] Running: *ensureProjectForOpenFiles* +Info 38 [00:01:08.000] Before ensureProjectForOpenFiles: +Info 39 [00:01:09.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 39 [00:01:10.000] Files (2) + +Info 39 [00:01:11.000] ----------------------------------------------- +Info 39 [00:01:12.000] Open files: +Info 39 [00:01:13.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 39 [00:01:14.000] Projects: /user/username/projects/myproject/tsconfig.json +Info 39 [00:01:15.000] After ensureProjectForOpenFiles: +Info 40 [00:01:16.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 40 [00:01:17.000] Files (2) + +Info 40 [00:01:18.000] ----------------------------------------------- +Info 40 [00:01:19.000] Open files: +Info 40 [00:01:20.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 40 [00:01:21.000] Projects: /user/username/projects/myproject/tsconfig.json +After running timeout callbacks + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} +/user/username/projects/myproject/someotherfile.txt: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +ExternalFiles:: ["someOtherFile.txt"] \ No newline at end of file