diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ad3121860ad4c..74bb8e1b1c817 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1134,7 +1134,7 @@ namespace ts { function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean { const resolutionToFile = getResolvedModule(oldSourceFile!, moduleName); const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName); - if (resolutionToFile && resolvedFile && !resolvedFile.externalModuleIndicator) { + if (resolutionToFile && resolvedFile) { // In the old program, we resolved to an ambient module that was in the same // place as we expected to find an actual module file. // We actually need to return 'false' here even though this seems like a 'true' case diff --git a/src/server/project.ts b/src/server/project.ts index 5a2c897e3863e..515340c94cc4c 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -958,6 +958,9 @@ namespace ts.server { ); const elapsed = timestamp() - start; this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasNewProgram} Elapsed: ${elapsed}ms`); + if (this.program !== oldProgram) { + this.print(); + } return hasNewProgram; } diff --git a/src/testRunner/unittests/tsserver/projectErrors.ts b/src/testRunner/unittests/tsserver/projectErrors.ts index 41201c365030c..10e6d3b972d46 100644 --- a/src/testRunner/unittests/tsserver/projectErrors.ts +++ b/src/testRunner/unittests/tsserver/projectErrors.ts @@ -479,6 +479,90 @@ namespace ts.projectSystem { session.clearMessages(); } }); + + it("Correct errors when resolution resolves to file that has same ambient module and is also module", () => { + const projectRootPath = "/users/username/projects/myproject"; + const aFile: File = { + path: `${projectRootPath}/src/a.ts`, + content: `import * as myModule from "@custom/plugin"; +function foo() { + // hello +}` + }; + const config: File = { + path: `${projectRootPath}/tsconfig.json`, + content: JSON.stringify({ include: ["src"] }) + }; + const plugin: File = { + path: `${projectRootPath}/node_modules/@custom/plugin/index.d.ts`, + content: `import './proposed'; +declare module '@custom/plugin' { + export const version: string; +}` + }; + const pluginProposed: File = { + path: `${projectRootPath}/node_modules/@custom/plugin/proposed.d.ts`, + content: `declare module '@custom/plugin' { + export const bar = 10; +}` + }; + const files = [libFile, aFile, config, plugin, pluginProposed]; + const host = createServerHost(files); + const session = createSession(host, { canUseEvents: true }); + const service = session.getProjectService(); + openFilesForSession([aFile], session); + + checkNumberOfProjects(service, { configuredProjects: 1 }); + session.clearMessages(); + checkErrors(); + + session.executeCommandSeq({ + command: protocol.CommandTypes.Change, + arguments: { + file: aFile.path, + line: 3, + offset: 8, + endLine: 3, + endOffset: 8, + insertString: "o" + } + }); + checkErrors(); + + function checkErrors() { + host.checkTimeoutQueueLength(0); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [aFile.path], + } + }); + + host.checkTimeoutQueueLengthAndRun(1); + + checkErrorMessage(session, "syntaxDiag", { file: aFile.path, diagnostics: [] }, /*isMostRecent*/ true); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "semanticDiag", { file: aFile.path, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "suggestionDiag", { + file: aFile.path, + diagnostics: [ + createDiagnostic({ line: 1, offset: 1 }, { line: 1, offset: 44 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["myModule"], "suggestion", /*reportsUnnecessary*/ true), + createDiagnostic({ line: 2, offset: 10 }, { line: 2, offset: 13 }, Diagnostics._0_is_declared_but_its_value_is_never_read, ["foo"], "suggestion", /*reportsUnnecessary*/ true) + ], + }); + checkCompleteEvent(session, 2, expectedSequenceId); + session.clearMessages(); + } + }); }); describe("unittests:: tsserver:: Project Errors for Configure file diagnostics events", () => {