diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 3e68688de8885..e960a8cbb888a 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -3181,7 +3181,7 @@ namespace ts.projectSystem { const projectLocation = "/user/username/projects/project"; const file1: FileOrFolder = { path: `${projectLocation}/src/file1.ts`, - content: `import { y } from "./file1"; let x = 10;` + content: `import { y } from "./file2"; let x = 10;` }; const file2: FileOrFolder = { path: `${projectLocation}/src/file2.ts`, @@ -3259,6 +3259,53 @@ namespace ts.projectSystem { // Allow allowNonTsExtensions will be set to true for deferred extensions. assert.isTrue(configuredProject.getCompilerOptions().allowNonTsExtensions); }); + + it("Orphan source files are handled correctly on watch trigger", () => { + const projectLocation = "/user/username/projects/project"; + const file1: FileOrFolder = { + path: `${projectLocation}/src/file1.ts`, + content: `export let x = 10;` + }; + const file2: FileOrFolder = { + path: `${projectLocation}/src/file2.ts`, + content: "export let y = 10;" + }; + const configContent1 = JSON.stringify({ + files: ["src/file1.ts", "src/file2.ts"] + }); + const config: FileOrFolder = { + path: `${projectLocation}/tsconfig.json`, + content: configContent1 + }; + const files = [file1, file2, libFile, config]; + const host = createServerHost(files); + const service = createProjectService(host); + service.openClientFile(file1.path); + checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, file2.path, libFile.path, config.path]); + + const configContent2 = JSON.stringify({ + files: ["src/file1.ts"] + }); + config.content = configContent2; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + + checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, libFile.path, config.path]); + verifyFile2InfoIsOrphan(); + + file2.content += "export let z = 10;"; + host.reloadFS(files); + host.runQueuedTimeoutCallbacks(); + + checkProjectActualFiles(service.configuredProjects.get(config.path), [file1.path, libFile.path, config.path]); + verifyFile2InfoIsOrphan(); + + function verifyFile2InfoIsOrphan() { + const info = service.getScriptInfoForPath(file2.path as Path); + assert.isDefined(info); + assert.equal(info.containingProjects.length, 0); + } + }); }); describe("tsserverProjectSystem Proper errors", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index b835e566372c6..d66fe48682e63 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -594,10 +594,12 @@ namespace ts.server { } private delayUpdateProjectGraphs(projects: ReadonlyArray) { - for (const project of projects) { - this.delayUpdateProjectGraph(project); + if (projects.length) { + for (const project of projects) { + this.delayUpdateProjectGraph(project); + } + this.delayEnsureProjectForOpenFiles(); } - this.delayEnsureProjectForOpenFiles(); } setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void { @@ -708,7 +710,6 @@ namespace ts.server { this.handleDeletedFile(info); } else if (!info.isScriptOpen()) { - Debug.assert(info.containingProjects.length !== 0); // file has been changed which might affect the set of referenced files in projects that include // this file and set of inferred projects info.delayReloadNonMixedContentFile();