Skip to content

Commit 4445e11

Browse files
yacinehmitosheetalkamat
authored andcommitted
Fix isProgramUpToDate when changing rootFileNames (microsoft#36011)
1 parent f588c78 commit 4445e11

File tree

2 files changed

+207
-127
lines changed

2 files changed

+207
-127
lines changed

src/compiler/program.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,14 +571,14 @@ namespace ts {
571571
return false;
572572
}
573573

574-
// If number of files in the program do not match, it is not up-to-date
575-
if (program.getRootFileNames().length !== rootFileNames.length) {
574+
// If root file names don't match
575+
if (!arrayIsEqualTo(program.getRootFileNames(), rootFileNames)) {
576576
return false;
577577
}
578578

579579
let seenResolvedRefs: ResolvedProjectReference[] | undefined;
580580

581-
// If project references dont match
581+
// If project references don't match
582582
if (!arrayIsEqualTo(program.getProjectReferences(), projectReferences, projectReferenceUptoDate)) {
583583
return false;
584584
}

src/testRunner/unittests/reuseProgramStructure.ts

Lines changed: 204 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -905,20 +905,19 @@ namespace ts {
905905
import createTestSystem = TestFSWithWatch.createWatchedSystem;
906906
import libFile = TestFSWithWatch.libFile;
907907

908-
describe("unittests:: Reuse program structure:: isProgramUptoDate should return true when there is no change in compiler options and", () => {
909-
function verifyProgramIsUptoDate(
908+
describe("unittests:: Reuse program structure:: isProgramUptoDate", () => {
909+
function getWhetherProgramIsUptoDate(
910910
program: Program,
911911
newRootFileNames: string[],
912912
newOptions: CompilerOptions
913913
) {
914-
const actual = isProgramUptoDate(
914+
return isProgramUptoDate(
915915
program, newRootFileNames, newOptions,
916916
path => program.getSourceFileByPath(path)!.version, /*fileExists*/ returnFalse,
917917
/*hasInvalidatedResolution*/ returnFalse,
918918
/*hasChangedAutomaticTypeDirectiveNames*/ false,
919919
/*projectReferences*/ undefined
920920
);
921-
assert.isTrue(actual);
922921
}
923922

924923
function duplicate(options: CompilerOptions): CompilerOptions;
@@ -927,134 +926,215 @@ namespace ts {
927926
return JSON.parse(JSON.stringify(filesOrOptions));
928927
}
929928

930-
function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) {
931-
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
932-
verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options));
933-
}
929+
describe("should return true when there is no change in compiler options and", () => {
930+
function verifyProgramIsUptoDate(
931+
program: Program,
932+
newRootFileNames: string[],
933+
newOptions: CompilerOptions
934+
) {
935+
const actual = getWhetherProgramIsUptoDate(program, newRootFileNames, newOptions);
936+
assert.isTrue(actual);
937+
}
934938

935-
function verifyProgramWithConfigFile(system: System, configFileName: string) {
936-
const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, /*watchOptionsToExtend*/ undefined, system)).getCurrentProgram().getProgram();
937-
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, /*watchOptionsToExtend*/ undefined, system, notImplemented)!; // TODO: GH#18217
938-
verifyProgramIsUptoDate(program, fileNames, options);
939-
}
939+
function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) {
940+
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
941+
verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options));
942+
}
940943

941-
function verifyProgram(files: File[], rootFiles: string[], options: CompilerOptions, configFile: string) {
942-
const system = createTestSystem(files);
943-
verifyProgramWithoutConfigFile(system, rootFiles, options);
944-
verifyProgramWithConfigFile(system, configFile);
945-
}
944+
function verifyProgramWithConfigFile(system: System, configFileName: string) {
945+
const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, /*watchOptionsToExtend*/ undefined, system)).getCurrentProgram().getProgram();
946+
const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, /*watchOptionsToExtend*/ undefined, system, notImplemented)!; // TODO: GH#18217
947+
verifyProgramIsUptoDate(program, fileNames, options);
948+
}
946949

947-
it("has empty options", () => {
948-
const file1: File = {
949-
path: "/a/b/file1.ts",
950-
content: "let x = 1"
951-
};
952-
const file2: File = {
953-
path: "/a/b/file2.ts",
954-
content: "let y = 1"
955-
};
956-
const configFile: File = {
957-
path: "/a/b/tsconfig.json",
958-
content: "{}"
959-
};
960-
verifyProgram([file1, file2, libFile, configFile], [file1.path, file2.path], {}, configFile.path);
961-
});
950+
function verifyProgram(files: File[], rootFiles: string[], options: CompilerOptions, configFile: string) {
951+
const system = createTestSystem(files);
952+
verifyProgramWithoutConfigFile(system, rootFiles, options);
953+
verifyProgramWithConfigFile(system, configFile);
954+
}
962955

963-
it("has lib specified in the options", () => {
964-
const compilerOptions: CompilerOptions = { lib: ["es5", "es2015.promise"] };
965-
const app: File = {
966-
path: "/src/app.ts",
967-
content: "var x: Promise<string>;"
968-
};
969-
const configFile: File = {
970-
path: "/src/tsconfig.json",
971-
content: JSON.stringify({ compilerOptions })
972-
};
973-
const es5Lib: File = {
974-
path: "/compiler/lib.es5.d.ts",
975-
content: "declare const eval: any"
976-
};
977-
const es2015Promise: File = {
978-
path: "/compiler/lib.es2015.promise.d.ts",
979-
content: "declare class Promise<T> {}"
980-
};
956+
it("has empty options", () => {
957+
const file1: File = {
958+
path: "/a/b/file1.ts",
959+
content: "let x = 1"
960+
};
961+
const file2: File = {
962+
path: "/a/b/file2.ts",
963+
content: "let y = 1"
964+
};
965+
const configFile: File = {
966+
path: "/a/b/tsconfig.json",
967+
content: "{}"
968+
};
969+
verifyProgram([file1, file2, libFile, configFile], [file1.path, file2.path], {}, configFile.path);
970+
});
981971

982-
verifyProgram([app, configFile, es5Lib, es2015Promise], [app.path], compilerOptions, configFile.path);
983-
});
972+
it("has lib specified in the options", () => {
973+
const compilerOptions: CompilerOptions = { lib: ["es5", "es2015.promise"] };
974+
const app: File = {
975+
path: "/src/app.ts",
976+
content: "var x: Promise<string>;"
977+
};
978+
const configFile: File = {
979+
path: "/src/tsconfig.json",
980+
content: JSON.stringify({ compilerOptions })
981+
};
982+
const es5Lib: File = {
983+
path: "/compiler/lib.es5.d.ts",
984+
content: "declare const eval: any"
985+
};
986+
const es2015Promise: File = {
987+
path: "/compiler/lib.es2015.promise.d.ts",
988+
content: "declare class Promise<T> {}"
989+
};
990+
991+
verifyProgram([app, configFile, es5Lib, es2015Promise], [app.path], compilerOptions, configFile.path);
992+
});
984993

985-
it("has paths specified in the options", () => {
986-
const compilerOptions: CompilerOptions = {
987-
baseUrl: ".",
988-
paths: {
989-
"*": [
990-
"packages/mail/data/*",
991-
"packages/styles/*",
992-
"*"
993-
]
994-
}
995-
};
996-
const app: File = {
997-
path: "/src/packages/framework/app.ts",
998-
content: 'import classc from "module1/lib/file1";\
999-
import classD from "module3/file3";\
1000-
let x = new classc();\
1001-
let y = new classD();'
1002-
};
1003-
const module1: File = {
1004-
path: "/src/packages/mail/data/module1/lib/file1.ts",
1005-
content: 'import classc from "module2/file2";export default classc;',
1006-
};
1007-
const module2: File = {
1008-
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1009-
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1010-
};
1011-
const module3: File = {
1012-
path: "/src/packages/styles/module3/file3.ts",
1013-
content: "class classD { method() { return 10; } }\nexport default classD;"
1014-
};
1015-
const configFile: File = {
1016-
path: "/src/tsconfig.json",
1017-
content: JSON.stringify({ compilerOptions })
1018-
};
994+
it("has paths specified in the options", () => {
995+
const compilerOptions: CompilerOptions = {
996+
baseUrl: ".",
997+
paths: {
998+
"*": [
999+
"packages/mail/data/*",
1000+
"packages/styles/*",
1001+
"*"
1002+
]
1003+
}
1004+
};
1005+
const app: File = {
1006+
path: "/src/packages/framework/app.ts",
1007+
content: 'import classc from "module1/lib/file1";\
1008+
import classD from "module3/file3";\
1009+
let x = new classc();\
1010+
let y = new classD();'
1011+
};
1012+
const module1: File = {
1013+
path: "/src/packages/mail/data/module1/lib/file1.ts",
1014+
content: 'import classc from "module2/file2";export default classc;',
1015+
};
1016+
const module2: File = {
1017+
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1018+
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1019+
};
1020+
const module3: File = {
1021+
path: "/src/packages/styles/module3/file3.ts",
1022+
content: "class classD { method() { return 10; } }\nexport default classD;"
1023+
};
1024+
const configFile: File = {
1025+
path: "/src/tsconfig.json",
1026+
content: JSON.stringify({ compilerOptions })
1027+
};
1028+
1029+
verifyProgram([app, module1, module2, module3, libFile, configFile], [app.path], compilerOptions, configFile.path);
1030+
});
10191031

1020-
verifyProgram([app, module1, module2, module3, libFile, configFile], [app.path], compilerOptions, configFile.path);
1021-
});
1032+
it("has include paths specified in tsconfig file", () => {
1033+
const compilerOptions: CompilerOptions = {
1034+
baseUrl: ".",
1035+
paths: {
1036+
"*": [
1037+
"packages/mail/data/*",
1038+
"packages/styles/*",
1039+
"*"
1040+
]
1041+
}
1042+
};
1043+
const app: File = {
1044+
path: "/src/packages/framework/app.ts",
1045+
content: 'import classc from "module1/lib/file1";\
1046+
import classD from "module3/file3";\
1047+
let x = new classc();\
1048+
let y = new classD();'
1049+
};
1050+
const module1: File = {
1051+
path: "/src/packages/mail/data/module1/lib/file1.ts",
1052+
content: 'import classc from "module2/file2";export default classc;',
1053+
};
1054+
const module2: File = {
1055+
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1056+
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1057+
};
1058+
const module3: File = {
1059+
path: "/src/packages/styles/module3/file3.ts",
1060+
content: "class classD { method() { return 10; } }\nexport default classD;"
1061+
};
1062+
const configFile: File = {
1063+
path: "/src/tsconfig.json",
1064+
content: JSON.stringify({ compilerOptions, include: ["packages/**/*.ts"] })
1065+
};
1066+
verifyProgramWithConfigFile(createTestSystem([app, module1, module2, module3, libFile, configFile]), configFile.path);
1067+
});
1068+
it("has the same root file names", () => {
1069+
const module1: File = {
1070+
path: "/src/packages/mail/data/module1/lib/file1.ts",
1071+
content: 'import classc from "module2/file2";export default classc;',
1072+
};
1073+
const module2: File = {
1074+
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1075+
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1076+
};
1077+
const module3: File = {
1078+
path: "/src/packages/styles/module3/file3.ts",
1079+
content: "class classD { method() { return 10; } }\nexport default classD;"
1080+
};
1081+
const rootFiles = [module1.path, module2.path, module3.path];
1082+
const system = createTestSystem([module1, module2, module3]);
1083+
const options = {};
1084+
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
1085+
verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options));
1086+
});
10221087

1023-
it("has include paths specified in tsconfig file", () => {
1024-
const compilerOptions: CompilerOptions = {
1025-
baseUrl: ".",
1026-
paths: {
1027-
"*": [
1028-
"packages/mail/data/*",
1029-
"packages/styles/*",
1030-
"*"
1031-
]
1032-
}
1033-
};
1034-
const app: File = {
1035-
path: "/src/packages/framework/app.ts",
1036-
content: 'import classc from "module1/lib/file1";\
1037-
import classD from "module3/file3";\
1038-
let x = new classc();\
1039-
let y = new classD();'
1040-
};
1041-
const module1: File = {
1042-
path: "/src/packages/mail/data/module1/lib/file1.ts",
1043-
content: 'import classc from "module2/file2";export default classc;',
1044-
};
1045-
const module2: File = {
1046-
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1047-
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1048-
};
1049-
const module3: File = {
1050-
path: "/src/packages/styles/module3/file3.ts",
1051-
content: "class classD { method() { return 10; } }\nexport default classD;"
1052-
};
1053-
const configFile: File = {
1054-
path: "/src/tsconfig.json",
1055-
content: JSON.stringify({ compilerOptions, include: ["packages/**/*.ts"] })
1056-
};
1057-
verifyProgramWithConfigFile(createTestSystem([app, module1, module2, module3, libFile, configFile]), configFile.path);
1088+
});
1089+
describe("should return false when there is no change in compiler options but", () => {
1090+
function verifyProgramIsNotUptoDate(
1091+
program: Program,
1092+
newRootFileNames: string[],
1093+
newOptions: CompilerOptions
1094+
) {
1095+
const actual = getWhetherProgramIsUptoDate(program, newRootFileNames, newOptions);
1096+
assert.isFalse(actual);
1097+
}
1098+
it("has more root file names", () => {
1099+
const module1: File = {
1100+
path: "/src/packages/mail/data/module1/lib/file1.ts",
1101+
content: 'import classc from "module2/file2";export default classc;',
1102+
};
1103+
const module2: File = {
1104+
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1105+
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1106+
};
1107+
const module3: File = {
1108+
path: "/src/packages/styles/module3/file3.ts",
1109+
content: "class classD { method() { return 10; } }\nexport default classD;"
1110+
};
1111+
const rootFiles = [module1.path, module2.path];
1112+
const newRootFiles = [module1.path, module2.path, module3.path];
1113+
const system = createTestSystem([module1, module2, module3]);
1114+
const options = {};
1115+
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
1116+
verifyProgramIsNotUptoDate(program, duplicate(newRootFiles), duplicate(options));
1117+
});
1118+
it("has one root file replaced by another", () => {
1119+
const module1: File = {
1120+
path: "/src/packages/mail/data/module1/lib/file1.ts",
1121+
content: 'import classc from "module2/file2";export default classc;',
1122+
};
1123+
const module2: File = {
1124+
path: "/src/packages/mail/data/module1/lib/module2/file2.ts",
1125+
content: 'class classc { method2() { return "hello"; } }\nexport default classc',
1126+
};
1127+
const module3: File = {
1128+
path: "/src/packages/styles/module3/file3.ts",
1129+
content: "class classD { method() { return 10; } }\nexport default classD;"
1130+
};
1131+
const rootFiles = [module1.path, module2.path];
1132+
const newRootFiles = [module2.path, module3.path];
1133+
const system = createTestSystem([module1, module2, module3]);
1134+
const options = {};
1135+
const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, /*watchOptions*/ undefined, system)).getCurrentProgram().getProgram();
1136+
verifyProgramIsNotUptoDate(program, duplicate(newRootFiles), duplicate(options));
1137+
});
10581138
});
10591139
});
10601140
}

0 commit comments

Comments
 (0)