diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index a1200b6a072..ee379787768 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -3199,9 +3199,12 @@
},
"Scoped package detected, looking in '{0}'": {
"category": "Message",
- "code": "6182"
+ "code": 6182
+ },
+ "Reusing module resolutions originating in '{0}' since this file was not modified.": {
+ "category": "Message",
+ "code": 6183
},
-
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 373ccb08d4d..2a9891e9d64 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -8,17 +8,17 @@ namespace ts {
const enum StructuralChangesFromOldProgram {
- None = 0,
- NoOldProgram = 1,
- ModuleResolutionOptions = 2,
- RootNames = 3,
- TypesOptions = 4,
- SourceFileRemoved = 5,
- HasNoDefaultLib = 6,
- TripleSlashReferences = 7,
- Imports = 8,
- ModuleAugmentations = 9,
- TripleSlashTypesReferences = 10,
+ None = 0,
+ NoOldProgram = 1 << 1,
+ ModuleResolutionOptions = 1 << 2,
+ RootNames = 1 << 3,
+ TypesOptions = 1 << 4,
+ SourceFileRemoved = 1 << 5,
+ HasNoDefaultLib = 1 << 6,
+ TripleSlashReferences = 1 << 7,
+ Imports = 1 << 8,
+ ModuleAugmentations = 1 << 9,
+ TripleSlashTypesReferences = 1 << 10,
CannotReuseResolution = NoOldProgram | ModuleResolutionOptions | RootNames | TypesOptions | SourceFileRemoved
}
@@ -520,6 +520,9 @@ namespace ts {
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
if (oldSourceFile === file) {
// `file` is unchanged from the old program, so we can reuse old module resolutions.
+ if (isTraceEnabled(options, host)) {
+ trace(host, Diagnostics.Reusing_module_resolutions_originating_in_0_since_this_file_was_not_modified, file.path);
+ }
const oldSourceFileResult: ResolvedModuleFull[] = [];
for (const moduleName of moduleNames) {
const resolvedModule = oldSourceFile.resolvedModules.get(moduleName);
@@ -710,10 +713,6 @@ namespace ts {
// tentatively approve the file
modifiedSourceFiles.push({ oldFile: oldSourceFile, newFile: newSourceFile });
}
- else {
- // file has no changes - use it as is
- newSourceFile = oldSourceFile;
- }
// if file has passed all checks it should be safe to reuse it
newSourceFiles.push(newSourceFile);
diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts
index 43014f25afb..e53aa0b4524 100644
--- a/src/harness/unittests/reuseProgramStructure.ts
+++ b/src/harness/unittests/reuseProgramStructure.ts
@@ -140,9 +140,7 @@ namespace ts {
useCaseSensitiveFileNames(): boolean {
return sys && sys.useCaseSensitiveFileNames;
},
- getNewLine(): string {
- return sys ? sys.newLine : newLine;
- },
+ getNewLine,
fileExists: fileName => files.has(fileName),
readFile: fileName => {
const file = files.get(fileName);
@@ -217,7 +215,7 @@ namespace ts {
describe("Reuse program structure", () => {
const target = ScriptTarget.Latest;
- const files = [
+ const files: NamedSourceText[] = [
{ name: "a.ts", text: SourceText.New(
`
///
@@ -468,8 +466,73 @@ namespace ts {
"File '/fs.jsx' does not exist.",
"======== Module name 'fs' was not resolved. ========",
], "should look for 'fs' again since node.d.ts was changed");
-
});
+
+ it("can reuse module resolutions from non-modified files", () => {
+ const files = [
+ { name: "a1.ts", text: SourceText.New("", "", "let x = 1;") },
+ { name: "a2.ts", text: SourceText.New("", "", "let x = 1;") },
+ { name: "b1.ts", text: SourceText.New("", "export class B { x: number; }", "") },
+ { name: "b2.ts", text: SourceText.New("", "export class B { x: number; }", "") },
+ { name: "node_modules/@types/typerefs1/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
+ { name: "node_modules/@types/typerefs2/index.d.ts", text: SourceText.New("", "", "declare let z: string;") },
+ {
+ name: "f1.ts",
+ text: SourceText.New(
+ `/// ${newLine}/// ${newLine}/// `,
+ `import { B } from './b1';${newLine}export let BB = B;`,
+ "declare module './b1' { interface B { y: string; } }")
+ },
+ {
+ name: "f2.ts",
+ text: SourceText.New(
+ `/// ${newLine}/// `,
+ `import { B } from './b2';${newLine}import { BB } from './f1';`,
+ "(new BB).x; (new BB).y;")
+ },
+ ];
+
+ const options = { target: ScriptTarget.ES2015, traceResolution: true };
+ const program_1 = newProgram(files, files.map(f => f.name), options);
+ assert.deepEqual(program_1.host.getTrace(),
+ [
+ "======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
+ "Resolving with primary search path 'node_modules/@types'.",
+ "File 'node_modules/@types/typerefs1/package.json' does not exist.",
+ "File 'node_modules/@types/typerefs1/index.d.ts' exist - use it as a name resolution result.",
+ "======== Type reference directive 'typerefs1' was successfully resolved to 'node_modules/@types/typerefs1/index.d.ts', primary: true. ========",
+ "======== Resolving module './b1' from 'f1.ts'. ========",
+ "Module resolution kind is not specified, using 'Classic'.",
+ "File 'b1.ts' exist - use it as a name resolution result.",
+ "======== Module name './b1' was successfully resolved to 'b1.ts'. ========",
+ "======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
+ "Resolving with primary search path 'node_modules/@types'.",
+ "File 'node_modules/@types/typerefs2/package.json' does not exist.",
+ "File 'node_modules/@types/typerefs2/index.d.ts' exist - use it as a name resolution result.",
+ "======== Type reference directive 'typerefs2' was successfully resolved to 'node_modules/@types/typerefs2/index.d.ts', primary: true. ========",
+ "======== Resolving module './b2' from 'f2.ts'. ========",
+ "Module resolution kind is not specified, using 'Classic'.",
+ "File 'b2.ts' exist - use it as a name resolution result.",
+ "======== Module name './b2' was successfully resolved to 'b2.ts'. ========",
+ "======== Resolving module './f1' from 'f2.ts'. ========",
+ "Module resolution kind is not specified, using 'Classic'.",
+ "File 'f1.ts' exist - use it as a name resolution result.",
+ "======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
+ ],
+ "First program should execute module reoslution normally.");
+
+ // Create project. Edit file-exists to track disk accesses. Edit f1.ts, update project.
+ // check that updating project "inherits" the fileexists implementation.
+ const indexOfF1 = 6;
+ function updateProgram_1(files: NamedSourceText[]): void {
+ files[indexOfF1].text = SourceText.New("","","");
+ }
+
+ const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, updateProgram_1);
+ assert.deepEqual(program_2.host.getTrace(), [
+ "Module 'fs' was resolved as ambient module declared in '/a/b/node.d.ts' since this file was not modified."
+ ], "should reuse module resolutions in f2 since it is unchanged");
+ });
});
describe("host is optional", () => {