From df345407f312d70911adae7f96a58a48d1ff05d3 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Mon, 4 Dec 2017 11:32:27 -0800 Subject: [PATCH] Use the old source file when re-resolving modules --- src/compiler/program.ts | 14 +++++++------- src/harness/unittests/reuseProgramStructure.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4088a6951a17e..c87cd31f052f4 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -704,7 +704,7 @@ namespace ts { interface OldProgramState { program: Program | undefined; - file: SourceFile; + oldSourceFile: SourceFile | undefined; /** The collection of paths modified *since* the old program. */ modifiedFilePaths: Path[]; } @@ -754,7 +754,6 @@ namespace ts { /** A transient placeholder used to mark predicted resolution in the result list. */ const predictedToResolveToAmbientModuleMarker: ResolvedModuleFull = {}; - for (let i = 0; i < moduleNames.length; i++) { const moduleName = moduleNames[i]; // If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions @@ -825,9 +824,10 @@ namespace ts { // If we change our policy of rechecking failed lookups on each program create, // we should adjust the value returned here. function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean { - const resolutionToFile = getResolvedModule(oldProgramState.file, moduleName); - if (resolutionToFile) { - // module used to be resolved to file - ignore it + const resolutionToFile = getResolvedModule(oldProgramState.oldSourceFile, moduleName); + const resolvedFile = resolutionToFile && oldProgramState.program && oldProgramState.program.getSourceFile(resolutionToFile.resolvedFileName); + if (resolutionToFile && !resolvedFile.externalModuleIndicator) { + // module used to be resolved to module - ignore it return false; } const ambientModule = oldProgramState.program && oldProgramState.program.getTypeChecker().tryFindAmbientModuleWithoutAugmentations(moduleName); @@ -1001,7 +1001,7 @@ namespace ts { const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.fileName, currentDirectory); if (resolveModuleNamesWorker) { const moduleNames = getModuleNames(newSourceFile); - const oldProgramState = { program: oldProgram, file: oldSourceFile, modifiedFilePaths }; + const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile, modifiedFilePaths }; const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState); // ensure that module resolution results are still correct const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo); @@ -1945,7 +1945,7 @@ namespace ts { if (file.imports.length || file.moduleAugmentations.length) { // Because global augmentation doesn't have string literal name, we can check for global augmentation as such. const moduleNames = getModuleNames(file); - const oldProgramState = { program: oldProgram, file, modifiedFilePaths }; + const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile: oldProgram && oldProgram.getSourceFile(file.fileName), modifiedFilePaths }; const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory), file, oldProgramState); Debug.assert(resolutions.length === moduleNames.length); for (let i = 0; i < moduleNames.length; i++) { diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 45fe7383c31d8..a7298c835cd38 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -389,6 +389,19 @@ namespace ts { checkResolvedModulesCache(program4, "a.ts", createMapFromTemplate({ b: createResolvedModule("b.ts"), c: undefined })); }); + it("set the resolvedImports after re-using an ambient external module declaration", () => { + const files = [ + { name: "/a.ts", text: SourceText.New("", "", 'import * as a from "a";') }, + { name: "/types/zzz/index.d.ts", text: SourceText.New("", "", 'declare module "a" { }') }, + ]; + const options: CompilerOptions = { target, typeRoots: ["/types"] }; + const program1 = newProgram(files, ["/a.ts"], options); + const program2 = updateProgram(program1, ["/a.ts"], options, files => { + files[0].text = files[0].text.updateProgram('import * as aa from "a";'); + }); + assert.isDefined(program2.getSourceFile("/a.ts").resolvedModules.get("a"), "'a' is not an unresolved module after re-use"); + }); + it("resolved type directives cache follows type directives", () => { const files = [ { name: "/a.ts", text: SourceText.New("/// ", "", "var x = $") },