diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 80115dbd35dab..7017f138224e9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2829,14 +2829,14 @@ namespace ts { redirectTargetsMap.add(fileFromPackageId.path, fileName); addFileToFilesByName(dupFile, path, redirectedPath); addFileIncludeReason(dupFile, reason); - sourceFileToPackageName.set(path, packageId.name); + sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); processingOtherFiles!.push(dupFile); return dupFile; } else if (file) { // This is the first source file to have this packageId. packageIdToSourceFile.set(packageIdKey, file); - sourceFileToPackageName.set(path, packageId.name); + sourceFileToPackageName.set(path, packageIdToPackageName(packageId)); } } addFileToFilesByName(file, path, redirectedPath); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 691a965ee661d..ed8ddf17227a0 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -192,9 +192,12 @@ namespace ts { return a === b || !!a && !!b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version; } - export function packageIdToString({ name, subModuleName, version }: PackageId): string { - const fullName = subModuleName ? `${name}/${subModuleName}` : name; - return `${fullName}@${version}`; + export function packageIdToPackageName({ name, subModuleName }: PackageId): string { + return subModuleName ? `${name}/${subModuleName}` : name; + } + + export function packageIdToString(packageId: PackageId): string { + return `${packageIdToPackageName(packageId)}@${packageId.version}`; } export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean { diff --git a/src/testRunner/unittests/reuseProgramStructure.ts b/src/testRunner/unittests/reuseProgramStructure.ts index 6dc13de241d47..3e9e5f2313441 100644 --- a/src/testRunner/unittests/reuseProgramStructure.ts +++ b/src/testRunner/unittests/reuseProgramStructure.ts @@ -234,7 +234,7 @@ namespace ts { }); assert.equal(program2.structureIsReused, StructureIsReused.Completely); const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); - const program2Diagnostics = program2.getSemanticDiagnostics(program1.getSourceFile("a.ts")); + const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); assert.equal(program1Diagnostics.length, program2Diagnostics.length); }); @@ -245,7 +245,26 @@ namespace ts { }); assert.equal(program2.structureIsReused, StructureIsReused.Completely); const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); - const program2Diagnostics = program2.getSemanticDiagnostics(program1.getSourceFile("a.ts")); + const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); + assert.equal(program1Diagnostics.length, program2Diagnostics.length); + }); + + it("successful if change affects a single module of a package", () => { + const files = [ + { name: "/a.ts", text: SourceText.New("", "import {b} from 'b'", "var a = b;") }, + { name: "/node_modules/b/index.d.ts", text: SourceText.New("", "export * from './internal';", "") }, + { name: "/node_modules/b/internal.d.ts", text: SourceText.New("", "", "export const b = 1;") }, + { name: "/node_modules/b/package.json", text: SourceText.New("", "", JSON.stringify({ name: "b", version: "1.2.3" })) }, + ]; + + const options: CompilerOptions = { target, moduleResolution: ModuleResolutionKind.NodeJs }; + const program1 = newProgram(files, ["/a.ts"], options); + const program2 = updateProgram(program1, ["/a.ts"], options, files => { + files[2].text = files[2].text.updateProgram("export const b = 2;"); + }); + assert.equal(program2.structureIsReused, StructureIsReused.Completely); + const program1Diagnostics = program1.getSemanticDiagnostics(program1.getSourceFile("a.ts")); + const program2Diagnostics = program2.getSemanticDiagnostics(program2.getSourceFile("a.ts")); assert.equal(program1Diagnostics.length, program2Diagnostics.length); });